1 /*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
17
18 #include <utility>
19
20 #include "base/log/ace_trace.h"
21 #include "base/log/event_report.h"
22 #include "base/memory/ace_type.h"
23 #include "base/utils/feature_param.h"
24 #include "base/utils/time_util.h"
25 #include "base/utils/utils.h"
26 #include "core/components/common/layout/layout_param.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
29 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
30 #include "core/components_ng/pattern/list/list_item_pattern.h"
31 #include "core/components_ng/pattern/list/list_layout_property.h"
32 #include "core/components_ng/pattern/list/list_pattern.h"
33 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
34 #include "core/components_ng/pattern/text/text_base.h"
35 #include "core/components_ng/pattern/text_field/text_field_manager.h"
36 #include "core/components_ng/property/layout_constraint.h"
37 #include "core/components_ng/property/measure_property.h"
38 #include "core/components_ng/property/measure_utils.h"
39 #include "core/components_ng/property/property.h"
40 #include "core/components_v2/inspector/inspector_constants.h"
41 #include "core/components_v2/list/list_properties.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43
44 namespace OHOS::Ace::NG {
45
46 namespace {
47 constexpr Dimension RESERVE_BOTTOM_HEIGHT = 24.0_vp;
48 constexpr float SCROLL_SNAP_VELOCITY_TH = 780;
49 } // namespace
50
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)51 void ListLayoutAlgorithm::UpdateListItemConstraint(
52 Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
53 {
54 contentConstraint.parentIdealSize = selfIdealSize;
55 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
56 auto crossSize = selfIdealSize.CrossSize(axis);
57 if (crossSize.has_value()) {
58 contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
59 contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
60 }
61 }
62
ReviseSpace(const RefPtr<ListLayoutProperty> & listLayoutProperty)63 void ListLayoutAlgorithm::ReviseSpace(const RefPtr<ListLayoutProperty>& listLayoutProperty)
64 {
65 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) {
66 spaceWidth_ = 0.0f;
67 }
68 if (listLayoutProperty->GetDivider().has_value()) {
69 auto divider = listLayoutProperty->GetDivider().value();
70 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
71 if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
72 dividerSpace.reset();
73 }
74 if (dividerSpace.has_value()) {
75 spaceWidth_ = std::max(spaceWidth_, static_cast<float>(Round(dividerSpace.value())));
76 }
77 }
78 if (isRoundingMode_) {
79 spaceWidth_ = Round(spaceWidth_);
80 }
81 spaceWidth_ += chainInterval_;
82 }
83
Measure(LayoutWrapper * layoutWrapper)84 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
85 {
86 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
87 CHECK_NULL_VOID(listLayoutProperty);
88 listLayoutProperty_ = listLayoutProperty;
89
90 axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
91 isStackFromEnd_ = listLayoutProperty->GetStackFromEnd().value_or(false);
92 // Pre-recycle
93 ScrollableUtils::RecycleItemsOutOfBoundary(axis_, -currentDelta_, GetStartIndex(), GetEndIndex(), layoutWrapper);
94
95 const auto& layoutConstraintOps = listLayoutProperty->GetLayoutConstraint();
96 CHECK_NULL_VOID(layoutConstraintOps);
97 const auto& layoutConstraint = layoutConstraintOps.value();
98
99 // calculate idealSize and set FrameSize
100 CalcContentOffset(listLayoutProperty);
101
102 // calculate main size.
103 const auto& contentConstraintOps = listLayoutProperty->GetContentLayoutConstraint();
104 CHECK_NULL_VOID(contentConstraintOps);
105 auto contentConstraint = contentConstraintOps.value();
106
107 float expandHeight = ScrollableUtils::CheckHeightExpansion(listLayoutProperty, axis_);
108 contentEndOffset_ += expandHeight;
109 // expand contentSize
110 contentConstraint.MinusPadding(std::nullopt, std::nullopt, std::nullopt, -expandHeight);
111 auto&& safeAreaOpts = listLayoutProperty->GetSafeAreaExpandOpts();
112 expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive();
113
114 auto contentIdealSize = CreateIdealSize(
115 contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
116
117 auto layoutPolicy = listLayoutProperty->GetLayoutPolicyProperty();
118 auto isCrossWrap = false;
119 auto isCrossFix = false;
120 auto isMainFix = false;
121 if (layoutPolicy.has_value()) {
122 auto isVertical = axis_ == Axis::VERTICAL;
123 auto widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
124 auto heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
125 isMainFix = (isVertical ? heightLayoutPolicy : widthLayoutPolicy) == LayoutCalPolicy::FIX_AT_IDEAL_SIZE;
126 isCrossWrap = (isVertical ? widthLayoutPolicy : heightLayoutPolicy) == LayoutCalPolicy::WRAP_CONTENT;
127 isCrossFix = (isVertical ? widthLayoutPolicy : heightLayoutPolicy) == LayoutCalPolicy::FIX_AT_IDEAL_SIZE;
128 auto layoutPolicySize =
129 ConstrainIdealSizeByLayoutPolicy(layoutConstraint, widthLayoutPolicy, heightLayoutPolicy, axis_);
130 contentIdealSize.UpdateIllegalSizeWithCheck(layoutPolicySize);
131 }
132
133 const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
134 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
135 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
136 contentMainSize_ = 0.0f;
137 CalculateTotalCountByRepeat(layoutWrapper);
138 scrollSnapAlign_ = listLayoutProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
139 if (childrenSize_) {
140 childrenSize_->ResizeChildrenSize(totalItemCount_);
141 }
142 if (!GetMainAxisSize(contentIdealSize, axis_)) {
143 if (totalItemCount_ == 0) {
144 contentMainSize_ = 0.0f;
145 } else {
146 // use parent max size first.
147 auto parentMaxSize = contentConstraint.maxSize;
148 contentMainSize_ = isMainFix ? Infinity<float>() : GetMainAxisSize(parentMaxSize, axis_);
149 mainSizeIsDefined_ = false;
150 }
151 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
152 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_));
153 }
154 } else {
155 contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
156 mainSizeIsDefined_ = true;
157 }
158 syncLoad_ = IsNeedSyncLoad(listLayoutProperty);
159 if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, contentMainSize_) ||
160 IsScrollSnapAlignCenter(layoutWrapper)) {
161 contentStartOffset_ = 0;
162 contentEndOffset_ = 0;
163 }
164 MeasureHeader(layoutWrapper);
165 if (totalItemCount_ > 0) {
166 OnSurfaceChanged(layoutWrapper);
167
168 stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
169 childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
170 auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
171 auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
172 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
173 ReviseSpace(listLayoutProperty);
174 CheckJumpToIndex();
175 CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
176 if (childrenSize_ && posMap_) {
177 posMap_->UpdatePosMap(layoutWrapper, GetLanes(), spaceWidth_, childrenSize_);
178 }
179 ProcessStackFromEnd();
180 currentOffset_ = currentDelta_;
181 startMainPos_ = currentOffset_;
182 endMainPos_ = currentOffset_ + contentMainSize_;
183 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
184 // calculate child layout constraint.
185 UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
186 MeasureList(layoutWrapper);
187 } else {
188 itemPosition_.clear();
189 if (childrenSize_ && posMap_) {
190 posMap_->ClearPosMap();
191 }
192 }
193 // In the secondary layout scenario, the previous contentMainSize_ is used as the next prevContentMainSize_.
194 prevContentMainSize_ = contentMainSize_;
195
196 auto crossSize = contentIdealSize.CrossSize(axis_);
197 if ((crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) || isCrossFix) {
198 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
199 crossMatchChild_ = true;
200 } else if (isCrossWrap) {
201 contentIdealSize.SetCrossSize(
202 std::min(GetChildMaxCrossSize(layoutWrapper, axis_), crossSize.value_or(0.0f)), axis_);
203 crossMatchChild_ = true;
204 }
205 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && !mainSizeIsDefined_ && !isMainFix) {
206 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_));
207 }
208 contentIdealSize.SetMainSize(contentMainSize_, axis_);
209 AddPaddingToSize(padding, contentIdealSize);
210
211 auto size = contentIdealSize.ConvertToSizeT();
212 // Cancel frame size expansion, only expand content size here.
213 // Frame expansion will be determined after Layout.
214 size.MinusHeight(expandHeight);
215 layoutWrapper->GetGeometryNode()->SetFrameSize(size);
216
217 if (listLayoutProperty->HasCacheRange()) {
218 ScrollableUtils::DisableLazyForEachBuildCache(layoutWrapper->GetHostNode());
219 }
220
221 // set list cache info.
222 SetCacheCount(layoutWrapper, listLayoutProperty->GetCachedCountWithDefault());
223 isLayouted_ = false;
224 }
225
CalcContentOffset(const RefPtr<ListLayoutProperty> & property)226 void ListLayoutAlgorithm::CalcContentOffset(const RefPtr<ListLayoutProperty>& property)
227 {
228 CHECK_NULL_VOID(property);
229 auto startOffset = property->GetContentStartOffset().value_or(0.0f);
230 contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0);
231 auto endOffset = property->GetContentEndOffset().value_or(0.0f);
232 contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0);
233 }
234
SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)235 void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount)
236 {
237 layoutWrapper->SetCacheCount(cacheCount);
238 }
239
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheStart,int32_t cacheEnd,bool show)240 void ListLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper,
241 int32_t cacheStart, int32_t cacheEnd, bool show)
242 {
243 if (measureInNextFrame_) {
244 return;
245 }
246 if (itemPosition_.empty()) {
247 layoutWrapper->SetActiveChildRange(-1, -1);
248 return;
249 }
250 auto start = !isStackFromEnd_ ? itemStartIndex_ + itemPosition_.begin()->first :
251 itemStartIndex_ + totalItemCount_ - itemPosition_.rbegin()->first - 1;
252 auto end = !isStackFromEnd_ ? itemStartIndex_ + itemPosition_.rbegin()->first :
253 itemStartIndex_ + totalItemCount_ - itemPosition_.begin()->first - 1;
254 if (draggingIndex_ >= 0) {
255 start = std::min(start, draggingIndex_);
256 end = std::max(end, draggingIndex_);
257 }
258 LostChildFocusToSelf(layoutWrapper, start - cacheStart, end + cacheEnd);
259 layoutWrapper->SetActiveChildRange(start, end, cacheStart, cacheEnd, show);
260 }
261
LostChildFocusToSelf(LayoutWrapper * layoutWrapper,int32_t start,int32_t end)262 void ListLayoutAlgorithm::LostChildFocusToSelf(LayoutWrapper* layoutWrapper, int32_t start, int32_t end)
263 {
264 CHECK_NULL_VOID(layoutWrapper);
265 auto host = layoutWrapper->GetHostNode();
266 CHECK_NULL_VOID(host);
267 auto focusHub = host->GetFocusHub();
268 CHECK_NULL_VOID(focusHub);
269 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
270 auto listPattern = host->GetPattern<ListPattern>();
271 CHECK_NULL_VOID(listPattern);
272 auto focusIndex = listPattern->GetFocusIndex();
273 CHECK_NULL_VOID(focusIndex.has_value());
274 if (focusIndex.value() >= start - itemStartIndex_ && focusIndex.value() <= end) {
275 return;
276 }
277 int32_t indexInList = -1;
278 auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
279 CHECK_NULL_VOID(childFocusHub);
280 auto focusNode = childFocusHub->GetFrameNode();
281 CHECK_NULL_VOID(focusNode);
282 auto childItemPattern = focusNode->GetPattern<ListItemPattern>();
283 if (!childItemPattern) {
284 auto listItemGroupPattern = focusNode->GetPattern<ListItemGroupPattern>();
285 CHECK_NULL_VOID(listItemGroupPattern);
286 indexInList = listItemGroupPattern->GetIndexInList();
287 } else {
288 indexInList = childItemPattern->GetIndexInList();
289 }
290 if (indexInList == focusIndex && childFocusHub->IsCurrentFocus()) {
291 focusHub->LostChildFocusToSelf();
292 }
293 }
294
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const295 bool ListLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
296 {
297 if (layoutWrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(layoutWrapper) ||
298 layoutWrapper->IsIgnoreOptsValid()) {
299 return true;
300 }
301 return CheckLayoutConstraintChanged(layoutWrapper);
302 }
303
CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper> & layoutWrapper) const304 bool ListLayoutAlgorithm::CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper>& layoutWrapper) const
305 {
306 auto geometryNode = layoutWrapper->GetGeometryNode();
307 CHECK_NULL_RETURN(geometryNode, true);
308 auto constraint = geometryNode->GetParentLayoutConstraint();
309 CHECK_NULL_RETURN(constraint, true);
310 bool isGroup = layoutWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
311 return isGroup ? constraint.value() != GetGroupLayoutConstraint() : constraint.value() != childLayoutConstraint_;
312 }
313
IsListLanesEqual(const RefPtr<LayoutWrapper> & wrapper) const314 bool ListLayoutAlgorithm::IsListLanesEqual(const RefPtr<LayoutWrapper>& wrapper) const
315 {
316 CHECK_NULL_RETURN(listLayoutProperty_, true);
317 auto groupProps = AceType::DynamicCast<ListItemGroupLayoutProperty>(wrapper->GetLayoutProperty());
318 CHECK_NULL_RETURN(groupProps, true);
319 return groupProps->IsListLanesEqual(listLayoutProperty_->GetLanes(),
320 listLayoutProperty_->GetLaneMinLength(), listLayoutProperty_->GetLaneMaxLength());
321 }
322
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const323 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
324 {
325 if (GetItemPosition().empty()) {
326 return 0.0f;
327 }
328 float maxCrossSize = 0.0f;
329 float crossSize = -laneGutter_;
330 float prevPos = GetItemPosition().begin()->second.startPos;
331 for (const auto& pos : GetItemPosition()) {
332 auto wrapper = GetListItem(layoutWrapper, pos.first, false);
333 if (!wrapper) {
334 continue;
335 }
336 auto getGeometryNode = wrapper->GetGeometryNode();
337 if (!getGeometryNode) {
338 continue;
339 }
340 if (NearEqual(prevPos, pos.second.startPos)) {
341 crossSize = crossSize + getGeometryNode->GetMarginFrameSize().CrossSize(axis) + laneGutter_;
342 } else {
343 crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
344 }
345 prevPos = pos.second.startPos;
346 maxCrossSize = std::max(crossSize, maxCrossSize);
347 }
348 return maxCrossSize;
349 }
350
ClearAllItemPosition(LayoutWrapper * layoutWrapper)351 void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper)
352 {
353 for (auto& pos : itemPosition_) {
354 auto wrapper = GetListItem(layoutWrapper, pos.first);
355 if (!wrapper) {
356 ReportGetChildError("ClearAllItemPosition", pos.first);
357 continue;
358 }
359 auto node = wrapper->GetHostNode();
360 if (!node) {
361 continue;
362 }
363 auto listItemGroup = node->GetPattern<ListItemGroupPattern>();
364 if (!listItemGroup) {
365 continue;
366 }
367 listItemGroup->ClearItemPosition();
368 listItemGroup->ClearCachedItemPosition();
369 }
370 itemPosition_.clear();
371 }
372
GetStartPositionWithChainOffset() const373 float ListLayoutAlgorithm::GetStartPositionWithChainOffset() const
374 {
375 if (itemPosition_.empty()) {
376 return 0.0f;
377 }
378 int32_t startIndex = itemPosition_.begin()->first;
379 float chainOffset = GetChainOffset(startIndex);
380 if (startIndex == 0) {
381 return itemPosition_.begin()->second.startPos + chainOffset;
382 }
383 return itemPosition_.begin()->second.startPos + chainOffset - spaceWidth_;
384 }
385
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)386 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
387 {
388 if (!jumpIndex_.has_value()) {
389 return;
390 }
391 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
392 LayoutForward(layoutWrapper, jumpIndex_.value(), startPos);
393 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
394 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
395 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
396 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
397 }
398 }
399 }
400
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)401 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
402 {
403 if (!jumpIndex_.has_value()) {
404 return;
405 }
406 jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value());
407 LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos);
408 if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
409 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
410 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
411 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
412 }
413 }
414 }
415
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex)416 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex)
417 {
418 int32_t jumpIndex = jumpIndex_.has_value() ? jumpIndex_.value() : targetIndex_.value();
419 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
420 startIndex = GetLanesFloor(layoutWrapper, startIndex);
421 endIndex = GetLanesFloor(layoutWrapper, endIndex);
422 float contentStartOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_;
423 float contentEndOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_;
424 auto wrapper = GetListItem(layoutWrapper, jumpIndex);
425 if (!wrapper) {
426 ReportGetChildError("HandleJumpAuto", jumpIndex);
427 return;
428 }
429 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
430 if (isGroup && jumpIndexInGroup_) {
431 if (scrollAutoType_ == ScrollAutoType::START) {
432 scrollAlign_ = ScrollAlign::START;
433 HandleJumpStart(layoutWrapper);
434 } else if (scrollAutoType_ == ScrollAutoType::END) {
435 scrollAlign_ = ScrollAlign::END;
436 HandleJumpEnd(layoutWrapper);
437 }
438 } else if (jumpIndex <= startIndex) {
439 float mainLen = childrenSize_ ?
440 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false);
441 if (GreatNotEqual(contentMainSize_ - contentStartOffset - contentEndOffset, mainLen)) {
442 scrollAutoType_ = ScrollAutoType::START;
443 BeginLayoutForward(contentStartOffset, layoutWrapper);
444 } else {
445 scrollAutoType_ = ScrollAutoType::END;
446 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper);
447 }
448 } else if (jumpIndex >= endIndex) {
449 float mainLen = childrenSize_ ?
450 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false);
451 if (GreatOrEqual(mainLen, contentMainSize_ - contentStartOffset - contentEndOffset)) {
452 scrollAutoType_ = ScrollAutoType::START;
453 BeginLayoutForward(contentStartOffset, layoutWrapper);
454 } else {
455 scrollAutoType_ = ScrollAutoType::END;
456 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper);
457 }
458 }
459 }
460
HandleJumpCenter(LayoutWrapper * layoutWrapper)461 void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper)
462 {
463 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
464 auto wrapper = GetListItem(layoutWrapper, index);
465 if (!wrapper) {
466 ReportGetChildError("HandleJumpCenter", index);
467 }
468 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
469 if (isGroup && jumpIndexInGroup_.has_value()) {
470 int32_t indexInGroup = jumpIndexInGroup_.value();
471 auto listLayoutProperty =
472 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
473 SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
474 wrapper->Measure(GetGroupLayoutConstraint());
475 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
476 CheckGroupMeasureBreak(wrapper);
477 if (!measureInNextFrame_ && LessNotEqual(GetEndPosition(), endMainPos_)) {
478 LayoutForward(layoutWrapper, index + 1, GetEndPosition());
479 }
480 } else {
481 float mainLen = childrenSize_ ?
482 GetChildHeight(layoutWrapper, index) : MeasureAndGetChildHeight(layoutWrapper, index);
483 float startPos = (contentMainSize_ - mainLen) / 2.0f;
484 if (LessNotEqual(startPos, endMainPos_)) {
485 LayoutForward(layoutWrapper, index, startPos);
486 }
487 }
488 if (!measureInNextFrame_ && GreatNotEqual(GetStartPosition(), startMainPos_)) {
489 LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
490 }
491 if (!measureInNextFrame_ && (GetEndIndex() < totalItemCount_ - 1) &&
492 LessNotEqual(GetEndPosition(), endMainPos_ - contentEndOffset_)) {
493 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
494 }
495 }
496
HandleJumpStart(LayoutWrapper * layoutWrapper)497 void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper)
498 {
499 auto wrapper = GetListItem(layoutWrapper, jumpIndex_.value());
500 if (!wrapper) {
501 ReportGetChildError("HandleJumpStart", jumpIndex_.value());
502 }
503 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
504 if (isGroup && jumpIndexInGroup_.has_value()) {
505 int32_t indexInGroup = jumpIndexInGroup_.value();
506 auto listLayoutProperty =
507 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
508 SetListItemGroupParam(wrapper, jumpIndex_.value(), 0.0f, true, listLayoutProperty, false);
509 wrapper->Measure(GetGroupLayoutConstraint());
510 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup);
511 CheckGroupMeasureBreak(wrapper);
512 if (!measureInNextFrame_ && LessNotEqual(GetEndPosition(), endMainPos_)) {
513 LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition());
514 }
515 if (!measureInNextFrame_ && GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
516 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
517 }
518 } else {
519 BeginLayoutForward(IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_, layoutWrapper);
520 }
521 }
522
HandleJumpEnd(LayoutWrapper * layoutWrapper)523 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
524 {
525 auto wrapper = GetListItem(layoutWrapper, jumpIndex_.value());
526 if (!wrapper) {
527 ReportGetChildError("HandleJumpEnd", jumpIndex_.value());
528 }
529 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
530 if (isGroup && jumpIndexInGroup_.has_value()) {
531 int32_t indexInGroup = jumpIndexInGroup_.value();
532 auto listLayoutProperty =
533 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
534 SetListItemGroupParam(wrapper, jumpIndex_.value(), contentMainSize_, true, listLayoutProperty, false);
535 wrapper->Measure(GetGroupLayoutConstraint());
536 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup);
537 CheckGroupMeasureBreak(wrapper);
538 if (!measureInNextFrame_ && GreatNotEqual(GetStartPosition(), startMainPos_)) {
539 LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition());
540 }
541 if (!measureInNextFrame_ && GetEndIndex() <= totalItemCount_ -1 &&
542 LessNotEqual(GetEndPosition(), endMainPos_)) {
543 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
544 }
545 } else {
546 BeginLayoutBackward(contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_),
547 layoutWrapper);
548 }
549 }
550
CheckNoNeedJumpListItem(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex)551 bool ListLayoutAlgorithm::CheckNoNeedJumpListItem(LayoutWrapper* layoutWrapper,
552 float startPos, float endPos, int32_t startIndex, int32_t endIndex, int32_t jumpIndex)
553 {
554 int32_t tempJumpIndex = jumpIndex;
555 int32_t tempStartIndex = startIndex;
556 int32_t tempEndIndex = endIndex;
557 if (GreatNotEqual(GetLanes(), 1)) {
558 tempJumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
559 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
560 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
561 }
562 if (tempJumpIndex > tempStartIndex && tempJumpIndex < tempEndIndex) {
563 return true;
564 }
565 if (tempJumpIndex == tempStartIndex && tempJumpIndex == tempEndIndex) {
566 return true;
567 }
568 if ((tempJumpIndex == tempStartIndex) &&
569 GreatOrEqual(startPos, IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_)) {
570 return true;
571 }
572 if ((tempJumpIndex == tempEndIndex) &&
573 LessOrEqual(endPos, contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_))) {
574 return true;
575 }
576 return false;
577 }
578
CheckNoNeedJumpListItemGroup(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)579 bool ListLayoutAlgorithm::CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper,
580 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
581 {
582 auto wrapper = GetListItem(layoutWrapper, jumpIndex);
583 if (!wrapper) {
584 ReportGetChildError("CheckNoNeedJumpListItemGroup", jumpIndex);
585 return true;
586 }
587 if (wrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
588 return true;
589 }
590 int32_t jumpIndexInGroup = 0;
591 if (jumpIndexInGroup_.has_value()) {
592 jumpIndexInGroup = jumpIndexInGroup_.value();
593 } else {
594 return false;
595 }
596
597 auto layoutAlgorithm = wrapper->GetLayoutAlgorithm();
598 CHECK_NULL_RETURN(layoutAlgorithm, true);
599 auto groupLayoutAlgorithm =
600 AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithm->GetLayoutAlgorithm());
601 CHECK_NULL_RETURN(groupLayoutAlgorithm, true);
602 auto groupItemPosition = groupLayoutAlgorithm->GetItemPosition();
603 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
604 CHECK_NULL_RETURN(listLayoutProperty, false);
605
606 if (jumpIndex >= startIndex && jumpIndex <= endIndex) {
607 auto it = groupItemPosition.find(jumpIndexInGroup);
608 if (it != groupItemPosition.end()) {
609 auto topPos = jumpIndexStartPos + it->second.startPos -
610 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_);
611 auto bottomPos = jumpIndexStartPos + it->second.endPos +
612 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_);
613 if (JudgeInOfScreenScrollAutoType(wrapper, listLayoutProperty, topPos, bottomPos)) {
614 return true;
615 }
616 } else if (groupItemPosition.size() > 0) {
617 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndexInGroup,
618 groupItemPosition.begin()->first, groupItemPosition.rbegin()->first);
619 } else {
620 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
621 return true;
622 }
623 } else {
624 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndex,
625 startIndex, endIndex);
626 }
627 return false;
628 }
629
JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<ListLayoutProperty> & layoutProperty,float topPos,float bottomPos)630 bool ListLayoutAlgorithm::JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper,
631 const RefPtr<ListLayoutProperty>& layoutProperty, float topPos, float bottomPos)
632 {
633 auto stickyStyle = layoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
634
635 auto groupNode = layoutWrapper->GetHostNode();
636 CHECK_NULL_RETURN(groupNode, true);
637 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
638 CHECK_NULL_RETURN(groupPattern, true);
639
640 float headerMainSize = 0.0f;
641 float footerMainSize = 0.0f;
642 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::HEADER) {
643 headerMainSize = groupPattern->GetHeaderMainSize();
644 }
645 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::FOOTER) {
646 footerMainSize = groupPattern->GetFooterMainSize();
647 }
648
649 if (GreatOrEqual(topPos, startMainPos_ + headerMainSize) &&
650 LessOrEqual(bottomPos, endMainPos_ - footerMainSize)) {
651 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
652 return true;
653 } else if (NearEqual(topPos, startMainPos_ + headerMainSize) ||
654 NearEqual(bottomPos, endMainPos_ - footerMainSize)) {
655 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
656 return true;
657 } else if (GreatOrEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
658 scrollAutoType_ = ScrollAutoType::END;
659 } else if (LessNotEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
660 scrollAutoType_ = ScrollAutoType::START;
661 }
662
663 return false;
664 }
665
JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,const RefPtr<ListLayoutProperty> & layoutProperty,int32_t indexInGroup,int32_t judgeIndex,int32_t startIndex,int32_t endIndex)666 void ListLayoutAlgorithm::JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
667 const RefPtr<ListLayoutProperty>& layoutProperty, int32_t indexInGroup, int32_t judgeIndex,
668 int32_t startIndex, int32_t endIndex)
669 {
670 SetListItemGroupParam(layoutWrapper, index, 0.0f, true, layoutProperty, false);
671 layoutWrapper->Measure(childLayoutConstraint_);
672 auto jumpItemHeight = GetListGroupItemHeight(layoutWrapper, indexInGroup);
673 jumpIndexInGroup_ = indexInGroup;
674
675 if (judgeIndex < startIndex) {
676 if (jumpItemHeight > contentMainSize_) {
677 scrollAutoType_ = ScrollAutoType::END;
678 } else {
679 scrollAutoType_ = ScrollAutoType::START;
680 }
681 } else if (judgeIndex > endIndex) {
682 if (jumpItemHeight > contentMainSize_) {
683 scrollAutoType_ = ScrollAutoType::START;
684 } else {
685 scrollAutoType_ = ScrollAutoType::END;
686 }
687 }
688 }
689
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)690 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
691 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
692 {
693 auto wrapper = GetListItem(layoutWrapper, jumpIndex);
694 if (!wrapper) {
695 ReportGetChildError("NoNeedJump", jumpIndex);
696 return true;
697 }
698 if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG && jumpIndexInGroup_.has_value()) {
699 if (CheckNoNeedJumpListItemGroup(layoutWrapper, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
700 return true;
701 }
702 } else {
703 if (CheckNoNeedJumpListItem(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex)) {
704 return true;
705 }
706 }
707 return false;
708 }
709
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex,bool groupLayoutAll)710 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex,
711 bool groupLayoutAll)
712 {
713 auto wrapper = GetListItem(layoutWrapper, childIndex);
714 if (!wrapper) {
715 ReportGetChildError("MeasureAndGetChildHeight", childIndex);
716 return 0.0f;
717 }
718 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
719 if (isGroup) {
720 auto listLayoutProperty =
721 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
722 // true: layout forward, 0.0f: layout start position.
723 SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, groupLayoutAll);
724 }
725 wrapper->Measure(childLayoutConstraint_);
726 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
727 if (isGroup) {
728 CheckGroupMeasureBreak(wrapper);
729 }
730 return mainLen;
731 }
732
FindIndexAndDeltaInPosMap(float delta) const733 std::pair<int32_t, float> ListLayoutAlgorithm::FindIndexAndDeltaInPosMap(float delta) const
734 {
735 // If cannot find or inappropriate, return index -1 and delta 0.f.
736 // If a suitable location can be found based on posMap_, return the corresponding index and delta.
737 // The delta and index in the return value satisfies the following constraints:
738 // 1) delta >= -spaceWidth_;
739 // 2) delta < posMap[index].mainSize.
740 if (!posMap_) {
741 return { -1, 0.f };
742 }
743 // itemPosition_ has been checked nonNull before the func is called.
744 int32_t curIndex = itemPosition_.begin()->first;
745 float startPos = itemPosition_.begin()->second.startPos;
746 // Consume a portion of delta to align item with the top of the List.
747 if (Negative(delta) && LessOrEqual(startPos, delta)) {
748 return { curIndex, delta - startPos };
749 }
750 delta -= startPos;
751 float curPos = posMap_->GetPositionInfo(curIndex).mainPos;
752 float curSize = posMap_->GetPositionInfo(curIndex).mainSize;
753 // The abs value of the input param of the func is greater than 2 * contentMainSize_, so
754 // the delta here must not be 0.f
755 int32_t step = Negative(delta) ? -1 : 1;
756 while (!Negative(curPos)) { // if curIndex
757 if (LessOrEqual(-spaceWidth_, delta) && LessNotEqual(delta, curSize)) {
758 return { curIndex, delta };
759 }
760 curIndex += step;
761 curPos = posMap_->GetPositionInfo(curIndex).mainPos;
762 curSize = posMap_->GetPositionInfo(curIndex).mainSize;
763 float gap = curSize + spaceWidth_;
764 delta -= (step > 0 ? gap : -gap);
765 }
766 return { -1, 0.f };
767 }
768
CanUseInfoInPosMap(int32_t index,float delta) const769 bool ListLayoutAlgorithm::CanUseInfoInPosMap(int32_t index, float delta) const
770 {
771 if (index < 0 || index > totalItemCount_ - 1 || !posMap_) {
772 return false;
773 }
774 const auto& info = posMap_->GetPositionInfo(index);
775 if (info.isGroup && GreatNotEqual(info.mainSize, contentMainSize_ * 2.0f)) {
776 return false;
777 }
778 return true;
779 }
780
CheckJumpToIndex()781 void ListLayoutAlgorithm::CheckJumpToIndex()
782 {
783 if (jumpIndex_.has_value() || !isNeedCheckOffset_ || childrenSize_) {
784 return;
785 }
786 if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
787 return;
788 }
789 auto [index, delta] = FindIndexAndDeltaInPosMap(currentDelta_);
790 if (CanUseInfoInPosMap(index, delta)) {
791 jumpIndex_ = index;
792 currentDelta_ = delta;
793 isNeedCheckOffset_ = false;
794 return;
795 }
796 for (const auto& pos : itemPosition_) {
797 if (pos.second.isGroup) {
798 if (pos.second.groupInfo) {
799 groupItemAverageHeight_ = pos.second.groupInfo.value().averageHeight;
800 }
801 return;
802 }
803 }
804 float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
805 float averageHeight = totalHeight / itemPosition_.size();
806 int32_t targetIndex = itemPosition_.begin()->first;
807 currentDelta_ -= itemPosition_.begin()->second.startPos;
808 if (NonNegative(currentDelta_)) {
809 int32_t items = currentDelta_ / averageHeight;
810 targetIndex += items;
811 currentDelta_ -= items * averageHeight;
812 } else {
813 int32_t items = -currentDelta_ / averageHeight;
814 targetIndex -= items;
815 currentDelta_ += items * averageHeight;
816 if (targetIndex <= 0) {
817 currentDelta_ = 0;
818 }
819 }
820 jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
821 }
822
UpdateSnapCenterContentOffset(LayoutWrapper * layoutWrapper)823 void ListLayoutAlgorithm::UpdateSnapCenterContentOffset(LayoutWrapper* layoutWrapper)
824 {
825 if (IsScrollSnapAlignCenter(layoutWrapper) && !itemPosition_.empty()) {
826 float itemHeight = 0.0f;
827 if (GetStartIndex() == 0) {
828 itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
829 contentStartOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f);
830 }
831 if (GetEndIndex() == totalItemCount_ - 1) {
832 itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
833 contentEndOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f);
834 }
835 }
836 }
837
CheckJumpValid(LayoutWrapper * layoutWrapper)838 bool ListLayoutAlgorithm::CheckJumpValid(LayoutWrapper* layoutWrapper)
839 {
840 if (jumpIndex_.value() == LAST_ITEM) {
841 jumpIndex_ = totalItemCount_ - 1;
842 } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
843 return false;
844 }
845 if (jumpIndex_ && jumpIndexInGroup_) {
846 auto groupWrapper = GetListItem(layoutWrapper, jumpIndex_.value());
847 if (!groupWrapper) {
848 ReportGetChildError("CheckJumpValid", jumpIndex_.value());
849 return false;
850 }
851 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
852 return false;
853 }
854 auto groupNode = groupWrapper->GetHostNode();
855 CHECK_NULL_RETURN(groupNode, false);
856 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
857 CHECK_NULL_RETURN(groupPattern, false);
858
859 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
860
861 if (jumpIndexInGroup_.value() == LAST_ITEM) {
862 jumpIndexInGroup_ = groupItemCount - 1;
863 } else if ((jumpIndexInGroup_.value() < 0) || (jumpIndexInGroup_.value() >= groupItemCount)) {
864 return false;
865 }
866 }
867 return true;
868 }
869
CheckAndMeasureStartItem(LayoutWrapper * layoutWrapper,int32_t startIndex,float & startPos,bool isGroup,bool forwardLayout)870 void ListLayoutAlgorithm::CheckAndMeasureStartItem(LayoutWrapper* layoutWrapper, int32_t startIndex,
871 float& startPos, bool isGroup, bool forwardLayout)
872 {
873 if (!isGroup || IsScrollSnapAlignCenter(layoutWrapper) ||
874 (forwardLayout && NonNegative(startPos)) || (!forwardLayout && LessOrEqual(startPos, prevContentMainSize_))) {
875 return;
876 }
877 auto wrapper = GetListItem(layoutWrapper, startIndex);
878 if (!wrapper) {
879 ReportGetChildError("CheckAndMeasureStartItem", startIndex);
880 return;
881 }
882 int32_t id = wrapper->GetHostNode()->GetId();
883 isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
884 if (!isGroup) {
885 return;
886 }
887 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
888 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d", startIndex);
889 SetListItemGroupParam(wrapper, startIndex, startPos, forwardLayout, listLayoutProperty, false, true);
890 wrapper->Measure(GetGroupLayoutConstraint());
891 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
892 CHECK_NULL_VOID(algorithmWrapper);
893 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
894 CHECK_NULL_VOID(itemGroup);
895 measureInNextFrame_ = itemGroup->GroupMeasureInNextFrame();
896 startPos = itemGroup->GetRefPos();
897 ListItemInfo itemInfo;
898 if (forwardLayout) {
899 itemInfo = { id, startPos, startPos + childrenSize_->GetChildSize(startIndex, isStackFromEnd_), isGroup };
900 } else {
901 itemInfo = { id, startPos - childrenSize_->GetChildSize(startIndex, isStackFromEnd_), startPos, isGroup };
902 }
903 firstItemInfo_ = std::make_pair(startIndex, itemInfo);
904 }
905
GetStartIndexInfo(int32_t & index,float & pos,bool & isGroup)906 void ListLayoutAlgorithm::GetStartIndexInfo(int32_t& index, float& pos, bool& isGroup)
907 {
908 auto it = itemPosition_.begin();
909 if (!overScrollFeature_) {
910 auto nextIt = it;
911 ++nextIt;
912 while (nextIt != itemPosition_.end() &&
913 LessNotEqual(it->second.endPos + GetChainOffset(it->first), startMainPos_)) {
914 recycledItemPosition_.emplace(it->first, it->second);
915 it = nextIt;
916 ++nextIt;
917 }
918 }
919 index = std::min(it->first, totalItemCount_ - 1);
920 pos = it->second.startPos;
921 isGroup = it->second.isGroup;
922 }
923
GetEndIndexInfo(int32_t & index,float & pos,bool & isGroup)924 void ListLayoutAlgorithm::GetEndIndexInfo(int32_t& index, float& pos, bool& isGroup)
925 {
926 auto it = itemPosition_.rbegin();
927 if (!overScrollFeature_ && NearEqual(contentMainSize_, prevContentMainSize_)) {
928 auto nextIt = it;
929 ++nextIt;
930 while (nextIt != itemPosition_.rend() &&
931 GreatNotEqual(it->second.startPos + GetChainOffset(it->first), endMainPos_)) {
932 recycledItemPosition_.emplace(it->first, it->second);
933 it = nextIt;
934 ++nextIt;
935 }
936 }
937 index = std::min(it->first, totalItemCount_ - 1);
938 pos = it->second.endPos;
939 isGroup = it->second.isGroup;
940 }
941
MeasureList(LayoutWrapper * layoutWrapper)942 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
943 {
944 bool startItemIsGroup = false;
945 bool endItemIsGroup = false;
946 int32_t startIndex = 0;
947 int32_t endIndex = 0;
948 int32_t midIndex = 0;
949 float midItemMidPos = contentMainSize_ / 2.0f;
950 float startPos = contentStartOffset_;
951 float endPos = 0.0f;
952 float itemTotalSize = 0.0f;
953 float jumpIndexStartPos = 0.0f;
954 bool needLayoutBackward = false;
955 auto host = layoutWrapper->GetHostNode();
956 CHECK_NULL_VOID(host);
957 auto pattern = host->GetPattern<ListPattern>();
958 CHECK_NULL_VOID(pattern);
959 if (!isLayouted_) {
960 noLayoutedItems_ = std::move(itemPosition_);
961 itemPosition_ = pattern->GetItemPosition();
962 }
963 auto prevTotalItemCount = pattern->GetMaxListItemIndex() + 1;
964 ReverseItemPosition(itemPosition_, prevTotalItemCount, prevContentMainSize_);
965 ReverseItemPosition(cachedItemPosition_, prevTotalItemCount, prevContentMainSize_);
966 preStartIndex_ = isStackFromEnd_ ? prevTotalItemCount - pattern->GetEndIndexInItemPosition() - 1:
967 pattern->GetStartIndexInItemPosition();
968 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) {
969 auto it = itemPosition_.find(jumpIndex_.value());
970 if (it != itemPosition_.end()) {
971 jumpIndexStartPos = it->second.startPos;
972 }
973 }
974
975 if (jumpIndex_) {
976 if (!CheckJumpValid(layoutWrapper)) {
977 jumpIndex_.reset();
978 jumpIndexInGroup_.reset();
979 } else {
980 if (jumpIndex_ && scrollAlign_ != ScrollAlign::AUTO) {
981 ClearAllItemPosition(layoutWrapper);
982 }
983 jumpIndex_ = isStackFromEnd_ ? totalItemCount_ - jumpIndex_.value() - 1 : jumpIndex_.value();
984 }
985 }
986 if (targetIndex_) {
987 if (targetIndex_.value() == LAST_ITEM) {
988 targetIndex_ = totalItemCount_ - 1;
989 }
990 targetIndex_ = isStackFromEnd_ ? totalItemCount_ - targetIndex_.value() - 1 : targetIndex_.value();
991 if ((targetIndex_.value() < 0) || (targetIndex_.value() >= totalItemCount_)) {
992 targetIndex_.reset();
993 }
994 targetIndexStaged_ = targetIndex_;
995 }
996 if (!itemPosition_.empty()) {
997 GetStartIndexInfo(startIndex, startPos, startItemIsGroup);
998 GetEndIndexInfo(endIndex, endPos, endItemIsGroup);
999 itemTotalSize = GetEndPosition() - GetStartPosition();
1000 if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
1001 jumpIndex_ = totalItemCount_ - 1;
1002 scrollAlign_ = ScrollAlign::END;
1003 }
1004 UpdateSnapCenterContentOffset(layoutWrapper);
1005 if (IsScrollSnapAlignCenter(layoutWrapper) && draggingIndex_ < 0) {
1006 midIndex = GetMidIndex(layoutWrapper, true);
1007 midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
1008 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
1009 midIndex = std::min(midIndex, totalItemCount_ - 1);
1010 } else if (scrollSnapAlign_ == ScrollSnapAlign::START && pattern->GetScrollState() == ScrollState::IDLE) {
1011 auto res = GetSnapStartIndexAndPos();
1012 startIndex = res.first;
1013 startPos = res.second;
1014 } else if (scrollSnapAlign_ == ScrollSnapAlign::END && pattern->GetScrollState() == ScrollState::IDLE) {
1015 auto res = GetSnapEndIndexAndPos();
1016 needLayoutBackward = res.first != -1;
1017 endIndex = needLayoutBackward ? res.first : endIndex;
1018 endPos = needLayoutBackward ? res.second : endPos;
1019 }
1020 needLayoutBackward = needLayoutBackward || (draggingIndex_ >= 0 &&
1021 revertIndex(draggingIndex_) <= startIndex && NearEqual(prevContentMainSize_, contentMainSize_));
1022 OffScreenLayoutDirection(layoutWrapper);
1023 itemPosition_.clear();
1024 }
1025 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO &&
1026 NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex_.value(), jumpIndexStartPos)) {
1027 jumpIndex_.reset();
1028 jumpIndexInGroup_.reset();
1029 }
1030 if (jumpIndex_) {
1031 switch (scrollAlign_) {
1032 case ScrollAlign::START:
1033 case ScrollAlign::NONE:
1034 HandleJumpStart(layoutWrapper);
1035 break;
1036 case ScrollAlign::CENTER:
1037 HandleJumpCenter(layoutWrapper);
1038 break;
1039 case ScrollAlign::END:
1040 HandleJumpEnd(layoutWrapper);
1041 break;
1042 case ScrollAlign::AUTO:
1043 HandleJumpAuto(layoutWrapper, startIndex, endIndex);
1044 break;
1045 }
1046 needEstimateOffset_ = true;
1047 } else if (targetIndex_.has_value()) {
1048 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
1049 if (layoutDirection == LayoutDirection::BACKWARD) {
1050 LayoutBackward(layoutWrapper, endIndex, endPos);
1051 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
1052 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
1053 }
1054 } else {
1055 LayoutForward(layoutWrapper, startIndex, startPos);
1056 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
1057 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
1058 }
1059 }
1060 } else {
1061 jumpIndexInGroup_.reset();
1062 bool overScrollTop =
1063 startIndex == 0 && GreatNotEqual(startPos + GetChainOffset(0), startMainPos_ + contentStartOffset_);
1064 bool overScrollBottom = (endIndex == totalItemCount_ - 1) &&
1065 LessNotEqual(endPos + GetChainOffset(totalItemCount_ - 1), prevContentMainSize_ - prevContentEndOffset_);
1066 float midItemHeight = 0.0f;
1067 if (IsScrollSnapAlignCenter(layoutWrapper) && draggingIndex_ < 0) {
1068 midItemHeight = childrenSize_ ?
1069 GetChildHeight(layoutWrapper, midIndex) : MeasureAndGetChildHeight(layoutWrapper, midIndex);
1070 startIndex = midIndex;
1071 endIndex = midIndex;
1072 }
1073 if ((NonNegative(currentOffset_) || overScrollFeature_) && !needLayoutBackward) {
1074 startIndex = GetLanesFloor(layoutWrapper, startIndex);
1075 if (overScrollTop && !canOverScrollStart_) {
1076 startPos = startMainPos_ + contentStartOffset_;
1077 }
1078 if (IsScrollSnapAlignCenter(layoutWrapper) && draggingIndex_ < 0) {
1079 startPos = midItemMidPos - midItemHeight / 2.0f;
1080 }
1081 if (overScrollBottom && GreatNotEqual(contentMainSize_, prevContentMainSize_) &&
1082 GreatNotEqual(itemTotalSize, contentMainSize_)) {
1083 startPos += contentMainSize_ - prevContentMainSize_;
1084 }
1085 if (childrenSize_) {
1086 CheckAndMeasureStartItem(layoutWrapper, startIndex, startPos, startItemIsGroup, true);
1087 posMap_->OptimizeBeforeMeasure(startIndex, startPos, currentOffset_, contentMainSize_);
1088 }
1089 LayoutForward(layoutWrapper, startIndex, startPos);
1090 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPositionWithChainOffset(), startMainPos_)) {
1091 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
1092 }
1093 } else {
1094 endIndex = GetLanesCeil(layoutWrapper, endIndex);
1095 if (needLayoutBackward) {
1096 endPos += contentMainSize_ - prevContentMainSize_;
1097 }
1098 if (IsScrollSnapAlignCenter(layoutWrapper) && draggingIndex_ < 0) {
1099 endPos = midItemMidPos + midItemHeight / 2.0f;
1100 }
1101 if (childrenSize_) {
1102 CheckAndMeasureStartItem(layoutWrapper, endIndex, endPos, endItemIsGroup, false);
1103 posMap_->OptimizeBeforeMeasure(endIndex, endPos, currentOffset_, contentMainSize_);
1104 }
1105 LayoutBackward(layoutWrapper, endIndex, endPos);
1106 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
1107 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
1108 }
1109 }
1110 }
1111 RecycleGroupItem(layoutWrapper);
1112 UpdateNoLayoutedItems();
1113 }
1114
UpdateNoLayoutedItems()1115 void ListLayoutAlgorithm::UpdateNoLayoutedItems()
1116 {
1117 if (isLayouted_) {
1118 return;
1119 }
1120 for (const auto& item : itemPosition_) {
1121 noLayoutedItems_.erase(item.first);
1122 }
1123 for (const auto& item : recycledItemPosition_) {
1124 noLayoutedItems_.erase(item.first);
1125 }
1126 }
1127
LayoutDirectionForTargetIndex(LayoutWrapper * layoutWrapper,int startIndex)1128 LayoutDirection ListLayoutAlgorithm::LayoutDirectionForTargetIndex(LayoutWrapper* layoutWrapper, int startIndex)
1129 {
1130 CHECK_NULL_RETURN(targetIndex_, LayoutDirection::NONE);
1131 if (startIndex < targetIndex_.value()) {
1132 return LayoutDirection::FORWARD;
1133 } else if (startIndex > targetIndex_.value()) {
1134 return LayoutDirection::BACKWARD;
1135 } else if (targetIndexInGroup_.has_value()) {
1136 auto groupWrapper = GetListItem(layoutWrapper, targetIndex_.value());
1137 if (!groupWrapper) {
1138 ReportGetChildError("LayoutDirectionForTargetIndex", targetIndex_.value());
1139 return LayoutDirection::NONE;
1140 }
1141 auto groupHost = groupWrapper->GetHostNode();
1142 CHECK_NULL_RETURN(groupHost, LayoutDirection::NONE);
1143 auto groupPattern = groupHost->GetPattern<ListItemGroupPattern>();
1144 CHECK_NULL_RETURN(groupPattern, LayoutDirection::NONE);
1145 auto startIndexInGroup = groupPattern->GetDisplayStartIndexInGroup();
1146 auto endIndexInGroup = groupPattern->GetDisplayEndIndexInGroup();
1147 auto isTargetGroupEmpty = groupPattern->GetItemPosition().empty();
1148 auto targetGroupPosition = itemPosition_[targetIndex_.value()].startPos;
1149 if (targetIndexInGroup_.value() < startIndexInGroup || (isTargetGroupEmpty && Negative(targetGroupPosition))) {
1150 return LayoutDirection::BACKWARD;
1151 } else if (targetIndexInGroup_.value() > endIndexInGroup ||
1152 (isTargetGroupEmpty && !Negative(targetGroupPosition))) {
1153 return LayoutDirection::FORWARD;
1154 }
1155 }
1156 return LayoutDirection::NONE;
1157 }
1158
RecycleGroupItem(LayoutWrapper * layoutWrapper) const1159 void ListLayoutAlgorithm::RecycleGroupItem(LayoutWrapper* layoutWrapper) const
1160 {
1161 if (scrollSnapAlign_ == ScrollSnapAlign::NONE || childrenSize_) {
1162 return;
1163 }
1164 auto startChild = itemPosition_.begin();
1165 auto endChild = itemPosition_.rbegin();
1166 if (startChild != itemPosition_.end() && startChild->second.isGroup) {
1167 float chainOffset = GetChainOffset(startChild->first);
1168 CheckListItemGroupRecycle(layoutWrapper, startChild->first, startChild->second.startPos + chainOffset, true);
1169 }
1170 if (endChild != itemPosition_.rend() && endChild->second.isGroup) {
1171 float chainOffset = GetChainOffset(endChild->first);
1172 CheckListItemGroupRecycle(layoutWrapper, endChild->first, endChild->second.endPos + chainOffset, false);
1173 }
1174 }
1175
AdjustStartPosition(const RefPtr<LayoutWrapper> & layoutWrapper,float & startPos)1176 void ListLayoutAlgorithm::AdjustStartPosition(const RefPtr<LayoutWrapper>& layoutWrapper, float& startPos)
1177 {
1178 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1179 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1180 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1181 CHECK_NULL_VOID(itemGroup);
1182 startPos += itemGroup->GetAdjustReferenceDelta();
1183 }
1184
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)1185 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
1186 int32_t& currentIndex, float startPos, float& endPos)
1187 {
1188 if (currentIndex + 1 >= totalItemCount_) {
1189 return 0;
1190 }
1191 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex + 1) {
1192 auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
1193 if (!wrapper) {
1194 ReportGetChildError("LayoutALineForward", currentIndex + 1);
1195 return 0;
1196 }
1197 int32_t id = wrapper->GetHostNode()->GetId();
1198 ++currentIndex;
1199 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1200 if (isGroup) {
1201 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1202 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, startPos);
1203 SetListItemGroupParam(wrapper, currentIndex, startPos, true, listLayoutProperty, false);
1204 wrapper->Measure(childLayoutConstraint_);
1205 if (LessOrEqual(startPos, 0.0f) || prevMeasureBreak_) {
1206 AdjustStartPosition(wrapper, startPos);
1207 }
1208 CheckGroupMeasureBreak(wrapper);
1209 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
1210 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, startPos);
1211 wrapper->Measure(childLayoutConstraint_);
1212 }
1213 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex, isStackFromEnd_) :
1214 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1215 endPos = startPos + mainLen;
1216 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
1217 } else {
1218 ++currentIndex;
1219 itemPosition_[currentIndex] = firstItemInfo_.value().second;
1220 endPos = itemPosition_[currentIndex].endPos;
1221 }
1222 if (firstItemInfo_) {
1223 firstItemInfo_.reset();
1224 }
1225 return 1;
1226 }
1227
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)1228 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
1229 int32_t& currentIndex, float endPos, float& startPos)
1230 {
1231 if (currentIndex - 1 < 0) {
1232 return 0;
1233 }
1234 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex - 1) {
1235 auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
1236 if (!wrapper) {
1237 ReportGetChildError("LayoutALineBackward", currentIndex - 1);
1238 return 0;
1239 }
1240 int32_t id = wrapper->GetHostNode()->GetId();
1241 --currentIndex;
1242 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1243 if (isGroup) {
1244 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1245 SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
1246 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, endPos);
1247 wrapper->Measure(childLayoutConstraint_);
1248 CheckGroupMeasureBreak(wrapper);
1249 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
1250 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, endPos);
1251 wrapper->Measure(childLayoutConstraint_);
1252 }
1253 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex, isStackFromEnd_) :
1254 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1255 startPos = endPos - mainLen;
1256 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
1257 } else {
1258 --currentIndex;
1259 itemPosition_[currentIndex] = firstItemInfo_.value().second;
1260 startPos = itemPosition_[currentIndex].startPos;
1261 }
1262 if (firstItemInfo_) {
1263 firstItemInfo_.reset();
1264 }
1265 return 1;
1266 }
1267
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)1268 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
1269 {
1270 float currentEndPos = startPos;
1271 float currentStartPos = 0.0f;
1272 float endMainPos = (overScrollFeature_ && startIndex == 0) ?
1273 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
1274 layoutEndMainPos_ = endMainPos;
1275 float endFixPos = GetLayoutFixOffset();
1276 auto currentIndex = startIndex - 1;
1277 auto chainOffset = 0.0f;
1278 do {
1279 if (!itemPosition_.empty() && !syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1280 measureInNextFrame_ = true;
1281 return;
1282 }
1283 currentStartPos = currentEndPos;
1284 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
1285 if (count == 0) {
1286 break;
1287 }
1288 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
1289 currentEndPos += spaceWidth_;
1290 }
1291 chainOffset = GetChainOffset(currentIndex);
1292 // reach the valid target index
1293 if (forwardFeature_ && targetIndex_ && currentIndex >= targetIndex_.value()) {
1294 endMainPos = layoutEndMainPos_.value_or(endMainPos_);
1295 forwardFeature_ = false;
1296 }
1297 } while (LessOrEqual(currentEndPos + chainOffset, endMainPos + endFixPos) || forwardFeature_);
1298 currentEndPos += chainOffset;
1299
1300 while (itemPosition_.size() > 1 && !targetIndex_) {
1301 auto pos = itemPosition_.rbegin();
1302 float chainDelta = GetChainOffset(pos->first);
1303 if (GreatNotEqual(pos->second.endPos + chainDelta, endMainPos) &&
1304 GreatOrEqual(pos->second.startPos + chainDelta, endMainPos)) {
1305 recycledItemPosition_.emplace(pos->first, pos->second);
1306 itemPosition_.erase(pos->first);
1307 } else {
1308 break;
1309 }
1310 }
1311 // adjust offset.
1312 UpdateSnapCenterContentOffset(layoutWrapper);
1313 if (LessNotEqual(currentEndPos, endMainPos - contentEndOffset_) && !itemPosition_.empty()) {
1314 endMainPos_ = currentEndPos + contentEndOffset_;
1315 startMainPos_ = endMainPos_ - contentMainSize_;
1316 ReMeasureListItemGroup(layoutWrapper, true);
1317 auto firstItemTop = itemPosition_.begin()->second.startPos;
1318 auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
1319 if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
1320 // all items size is less than list.
1321 if ((GreatOrEqual(firstItemTop, currentOffset_ + contentStartOffset_) && !canOverScrollStart_) ||
1322 (LessOrEqual(firstItemTop, currentOffset_ + contentStartOffset_) && !canOverScrollEnd_)) {
1323 currentOffset_ = firstItemTop - contentStartOffset_;
1324 startMainPos_ = currentOffset_;
1325 endMainPos_ = startMainPos_ + contentMainSize_;
1326 }
1327 if (!mainSizeIsDefined_) {
1328 // adapt child size.
1329 contentMainSize_ = itemTotalSize;
1330 }
1331 } else {
1332 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1333 if (!canOverScrollEnd_ || jumpIndex_.has_value()) {
1334 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
1335 }
1336 }
1337 }
1338 if ((overScrollFeature_ && (canOverScrollStart_ || canOverScrollEnd_)) || targetIndex_) {
1339 return;
1340 }
1341 // Mark inactive in wrapper.
1342 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1343 chainOffset = GetChainOffset(pos->first);
1344 // Don't recycle When the head item is Visibility.None.
1345 if (GreatNotEqual(pos->second.endPos + chainOffset + endFixPos, startMainPos_) ||
1346 GreatOrEqual(pos->second.startPos + chainOffset + endFixPos, startMainPos_)) {
1347 if (pos->second.isGroup) {
1348 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
1349 }
1350 break;
1351 }
1352 recycledItemPosition_.emplace(pos->first, pos->second);
1353 pos = itemPosition_.erase(pos);
1354 }
1355 }
1356
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)1357 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
1358 {
1359 float currentStartPos = endPos;
1360 float currentEndPos = 0.0f;
1361 float startMainPos = (overScrollFeature_ && endIndex == totalItemCount_ - 1) ?
1362 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
1363 layoutStartMainPos_ = startMainPos;
1364 float startFixPos = GetLayoutFixOffset();
1365 auto currentIndex = endIndex + 1;
1366 auto chainOffset = 0.0f;
1367 do {
1368 if (!itemPosition_.empty() && !syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1369 measureInNextFrame_ = true;
1370 return;
1371 }
1372 currentEndPos = currentStartPos;
1373 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
1374 if (count == 0) {
1375 break;
1376 }
1377 if (currentIndex > 0) {
1378 currentStartPos = currentStartPos - spaceWidth_;
1379 }
1380 chainOffset = GetChainOffset(currentIndex);
1381 // reach the valid target index
1382 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
1383 startMainPos = layoutStartMainPos_.value_or(startMainPos_);
1384 backwardFeature_ = false;
1385 }
1386 } while (GreatNotEqual(currentStartPos + chainOffset + startFixPos, startMainPos) || backwardFeature_);
1387
1388 currentStartPos += chainOffset;
1389 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1390 UpdateSnapCenterContentOffset(layoutWrapper);
1391 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_) && !itemPosition_.empty()) {
1392 auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
1393 bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
1394 if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
1395 if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
1396 currentOffset_ += contentMainSize_ - prevContentMainSize_;
1397 }
1398 contentMainSize_ = itemTotalSize;
1399 }
1400 if ((!canOverScrollStart_ && GreatOrEqual(currentStartPos, contentStartOffset_ + currentOffset_)) ||
1401 jumpIndex_.has_value()) {
1402 currentOffset_ = currentStartPos - contentStartOffset_;
1403 }
1404 endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
1405 startMainPos_ = currentStartPos - contentStartOffset_;
1406 ReMeasureListItemGroup(layoutWrapper, false);
1407 }
1408
1409 if (overScrollFeature_ || targetIndex_) {
1410 return;
1411 }
1412 // Mark inactive in wrapper.
1413 std::list<int32_t> removeIndexes;
1414 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1415 chainOffset = GetChainOffset(pos->first);
1416 // Don't recycle When the tail item is Visibility.None.
1417 if (LessNotEqual(pos->second.startPos + chainOffset - startFixPos, endMainPos_) ||
1418 LessOrEqual(pos->second.endPos + chainOffset - startFixPos, endMainPos_)) {
1419 if (pos->second.isGroup) {
1420 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
1421 }
1422 break;
1423 }
1424 recycledItemPosition_.emplace(pos->first, pos->second);
1425 removeIndexes.emplace_back(pos->first);
1426 }
1427 for (const auto& index : removeIndexes) {
1428 itemPosition_.erase(index);
1429 }
1430 }
1431
ReMeasureListItemGroup(LayoutWrapper * layoutWrapper,bool forwardLayout)1432 void ListLayoutAlgorithm::ReMeasureListItemGroup(LayoutWrapper* layoutWrapper, bool forwardLayout)
1433 {
1434 if (forwardFeature_ || backwardFeature_) {
1435 return;
1436 }
1437 if (!forwardLayout) {
1438 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end(); pos++) {
1439 float chainOffset = GetChainOffset(pos->first);
1440 if (GreatOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
1441 break;
1442 } else if (!pos->second.isGroup) {
1443 continue;
1444 }
1445 AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout);
1446 }
1447 return;
1448 }
1449 if (LessNotEqual(itemPosition_.begin()->second.endPos, currentOffset_)) {
1450 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); pos++) {
1451 float chainOffset = GetChainOffset(pos->first);
1452 if (LessOrEqual(pos->second.endPos + chainOffset, startMainPos_)) {
1453 break;
1454 } else if (!pos->second.isGroup) {
1455 continue;
1456 }
1457 AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout);
1458 }
1459 return;
1460 }
1461 if (itemPosition_.begin()->second.isGroup) {
1462 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), forwardLayout);
1463 }
1464 }
1465
FixPredictSnapPos()1466 void ListLayoutAlgorithm::FixPredictSnapPos()
1467 {
1468 if (!predictSnapEndPos_.has_value() || itemPosition_.empty()) {
1469 return;
1470 }
1471 float predictEndPos = predictSnapEndPos_.value();
1472 int32_t predictIndex = -1;
1473 int32_t curIndex = -1; // here invalid.
1474 switch (scrollSnapAlign_) {
1475 case ScrollSnapAlign::START:
1476 FindPredictSnapIndexInItemPositionsStart(predictEndPos, predictIndex, curIndex);
1477 break;
1478 case ScrollSnapAlign::CENTER:
1479 FindPredictSnapIndexInItemPositionsCenter(predictEndPos, predictIndex, curIndex);
1480 break;
1481 case ScrollSnapAlign::END:
1482 FindPredictSnapIndexInItemPositionsEnd(predictEndPos, predictIndex, curIndex);
1483 break;
1484 default:
1485 return;
1486 }
1487 if (GetStartIndex() <= predictIndex && predictIndex <= GetEndIndex()) {
1488 predictEndPos = CalculatePredictSnapEndPositionByIndex(predictIndex, scrollSnapAlign_);
1489 }
1490 if (!NearEqual(predictEndPos, predictSnapEndPos_.value())) {
1491 predictSnapEndPos_ = predictEndPos;
1492 }
1493 }
1494
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)1495 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
1496 {
1497 if (!predictSnapOffset_.has_value() || itemPosition_.empty()) {
1498 return;
1499 }
1500 if (scrollSnapAlign_ == ScrollSnapAlign::NONE) {
1501 predictSnapOffset_.reset();
1502 predictSnapEndPos_.reset();
1503 return;
1504 }
1505
1506 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1507 int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign_);
1508 if (GetStartIndex() <= endIndex && endIndex <= GetEndIndex()) {
1509 predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign_);
1510 predictSnapOffset_ = totalOffset_ - predictEndPos;
1511 predictSnapEndPos_ = predictEndPos;
1512 } else {
1513 if (IsUniformHeightProbably()) {
1514 if (scrollSnapAlign_ == ScrollSnapAlign::START) {
1515 FixPredictSnapOffsetAlignStart();
1516 } else if (scrollSnapAlign_ == ScrollSnapAlign::CENTER) {
1517 FixPredictSnapOffsetAlignCenter();
1518 } else if (scrollSnapAlign_ == ScrollSnapAlign::END) {
1519 FixPredictSnapOffsetAlignEnd();
1520 }
1521 } else {
1522 predictSnapEndPos_ = predictEndPos;
1523 }
1524 }
1525
1526 return;
1527 }
1528
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1529 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1530 {
1531 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1532 CHECK_NULL_RETURN(listLayoutProperty, false);
1533 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
1534 if (scrollSnapAlign == ScrollSnapAlign::CENTER) {
1535 return true;
1536 }
1537
1538 return false;
1539 }
1540
FixPredictSnapOffsetAlignStart()1541 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1542 {
1543 if (itemPosition_.empty()) {
1544 return;
1545 }
1546 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1547 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1548 float startPos = contentStartOffset_;
1549 float endPos = contentMainSize_ - contentEndOffset_;
1550 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1551
1552 if (LessNotEqual(predictEndPos, -startPos)) {
1553 if (isSpringEffect_) {
1554 return;
1555 }
1556 predictEndPos = -startPos;
1557 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1558 if (isSpringEffect_) {
1559 return;
1560 }
1561 predictEndPos = maxPos;
1562 } else {
1563 int32_t index;
1564 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1565 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1566 break;
1567 }
1568 }
1569 predictEndPos = index * itemHeight - startPos;
1570 if (LessNotEqual(predictEndPos, -startPos)) {
1571 predictEndPos = -startPos;
1572 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1573 predictEndPos = maxPos;
1574 }
1575 }
1576
1577 predictSnapOffset_ = totalOffset_ - predictEndPos;
1578 predictSnapEndPos_ = predictEndPos;
1579 }
1580
FixPredictSnapOffsetAlignCenter()1581 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1582 {
1583 if (itemPosition_.empty()) {
1584 return;
1585 }
1586 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1587 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1588 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1589 if (isSpringEffect_) {
1590 return;
1591 }
1592 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1593 } else if (GreatNotEqual(
1594 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1595 if (isSpringEffect_) {
1596 return;
1597 }
1598 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1599 } else {
1600 int32_t index;
1601 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1602 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1603 itemHeight / 2.0f) {
1604 break;
1605 }
1606 }
1607 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1608 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1609 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1610 } else if (GreatNotEqual(
1611 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1612 predictEndPos =
1613 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1614 }
1615 }
1616
1617 predictSnapOffset_ = totalOffset_ - predictEndPos;
1618 predictSnapEndPos_ = predictEndPos;
1619 }
1620
FixPredictSnapOffsetAlignEnd()1621 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1622 {
1623 if (itemPosition_.empty()) {
1624 return;
1625 }
1626 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1627 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1628 float startPos = contentStartOffset_;
1629 float endPos = contentMainSize_ - contentEndOffset_;
1630 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1631
1632 if (LessNotEqual(predictEndPos, -startPos)) {
1633 if (isSpringEffect_) {
1634 return;
1635 }
1636 predictEndPos = -startPos;
1637 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1638 if (isSpringEffect_) {
1639 return;
1640 }
1641 predictEndPos = maxPos;
1642 } else {
1643 int32_t index;
1644 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1645 if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1646 break;
1647 }
1648 }
1649 predictEndPos = index * itemHeight - endPos - spaceWidth_;
1650 if (LessNotEqual(predictEndPos, -startPos)) {
1651 predictEndPos = -startPos;
1652 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1653 predictEndPos = maxPos;
1654 }
1655 }
1656
1657 predictSnapOffset_ = totalOffset_ - predictEndPos;
1658 predictSnapEndPos_ = predictEndPos;
1659 }
1660
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1661 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1662 int32_t& startIndex, float crossSize)
1663 {
1664 CHECK_NULL_VOID(wrapper);
1665 auto offset = paddingOffset_;
1666 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1667 float crossOffset = 0.0f;
1668 if (GetLanes() > 1) {
1669 int32_t laneIndex = 0;
1670 int32_t lanes = 1;
1671 if (pos.isGroup) {
1672 startIndex = index + 1;
1673 } else {
1674 laneIndex = (index - startIndex) % GetLanes();
1675 lanes = GetLanes();
1676 }
1677
1678 float laneGutter = GetLaneGutter();
1679 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * lanes, pos.isGroup);
1680 crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1681 } else {
1682 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize, pos.isGroup);
1683 }
1684 auto chainOffset = GetChainOffset(index);
1685 auto startPos = !isStackFromEnd_ ? pos.startPos + chainOffset : contentMainSize_ - pos.endPos - chainOffset;
1686 auto endPos = !isStackFromEnd_ ? pos.endPos + chainOffset : contentMainSize_ - pos.startPos - chainOffset;
1687 if (isReverse_) {
1688 if (axis_ == Axis::VERTICAL) {
1689 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1690 offset = offset + OffsetF(crossSize - crossOffset - size.Width(), startPos);
1691 } else {
1692 offset = offset + OffsetF(contentMainSize_ - endPos, crossOffset);
1693 }
1694 } else {
1695 if (axis_ == Axis::VERTICAL) {
1696 offset = offset + OffsetF(crossOffset, startPos);
1697 } else {
1698 offset = offset + OffsetF(startPos, crossOffset);
1699 }
1700 }
1701 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1702 index = isStackFromEnd_ ? totalItemCount_ - index - 1 : index;
1703 SetListItemIndex(wrapper, index);
1704 }
1705
ProcessCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)1706 void ListLayoutAlgorithm::ProcessCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
1707 {
1708 if (!itemPosition_.empty() && cacheCount > 0) {
1709 auto items = LayoutCachedItemV2(layoutWrapper, cacheCount, show);
1710 auto host = layoutWrapper->GetHostNode();
1711 CHECK_NULL_VOID(host);
1712 if (!items.empty()) {
1713 ListMainSizeValues value = { startMainPos_, endMainPos_, jumpIndexInGroup_, prevContentMainSize_,
1714 scrollAlign_, layoutStartMainPos_, layoutEndMainPos_ };
1715 if (scrollSnapAlign_ != ScrollSnapAlign::CENTER) {
1716 value.contentStartOffset = contentStartOffset_;
1717 value.contentEndOffset = contentEndOffset_;
1718 }
1719 PostIdleTaskV2(host, { items, childLayoutConstraint_, GetGroupLayoutConstraint() }, value, show);
1720 } else {
1721 auto pattern = host->GetPattern<ListPattern>();
1722 CHECK_NULL_VOID(pattern);
1723 pattern->SetPredictLayoutParamV2(std::nullopt);
1724 }
1725 } else {
1726 ResetLayoutItem(layoutWrapper);
1727 SetActiveChildRange(layoutWrapper, cacheCount, cacheCount, show);
1728 }
1729 }
1730
GetListItemGroupLayoutInfo(const RefPtr<LayoutWrapper> & wrapper) const1731 std::optional<ListItemGroupLayoutInfo> ListLayoutAlgorithm::GetListItemGroupLayoutInfo(
1732 const RefPtr<LayoutWrapper>& wrapper) const
1733 {
1734 CHECK_NULL_RETURN(wrapper, std::nullopt);
1735 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1736 CHECK_NULL_RETURN(layoutAlgorithmWrapper, std::nullopt);
1737 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1738 CHECK_NULL_RETURN(itemGroup, std::nullopt);
1739 return itemGroup->GetLayoutInfo();
1740 }
1741
GetListItemGroupItemCount(const RefPtr<LayoutWrapper> & wrapper) const1742 int32_t ListLayoutAlgorithm::GetListItemGroupItemCount(const RefPtr<LayoutWrapper>& wrapper) const
1743 {
1744 CHECK_NULL_RETURN(wrapper, 0);
1745 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1746 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
1747 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1748 CHECK_NULL_RETURN(itemGroup, 0);
1749 int32_t itemCount = itemGroup->GetListItemCount();
1750 return itemCount == 0 ? 1 : itemCount;
1751 }
1752
IsNeedSyncLoad(const RefPtr<ListLayoutProperty> & property) const1753 bool ListLayoutAlgorithm::IsNeedSyncLoad(const RefPtr<ListLayoutProperty>& property) const
1754 {
1755 bool syncLoad = property->GetSyncLoad().value_or(!FeatureParam::IsSyncLoadEnabled());
1756 return !(!syncLoad && NearZero(currentDelta_) && !targetIndex_.has_value() && mainSizeIsDefined_);
1757 }
1758
CheckGroupMeasureBreak(const RefPtr<LayoutWrapper> & wrapper)1759 void ListLayoutAlgorithm::CheckGroupMeasureBreak(const RefPtr<LayoutWrapper>& wrapper)
1760 {
1761 if (syncLoad_) {
1762 return;
1763 }
1764 CHECK_NULL_VOID(wrapper);
1765 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1766 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1767 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1768 CHECK_NULL_VOID(itemGroup);
1769 if (itemGroup->GroupMeasureInNextFrame()) {
1770 measureInNextFrame_ = true;
1771 }
1772 }
1773
ResetLayoutItem(LayoutWrapper * layoutWrapper)1774 void ListLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1775 {
1776 ResetUnLayoutedItems(layoutWrapper, recycledItemPosition_);
1777 ResetUnLayoutedItems(layoutWrapper, noLayoutedItems_);
1778 }
1779
ResetUnLayoutedItems(LayoutWrapper * layoutWrapper,PositionMap & positionMap)1780 void ListLayoutAlgorithm::ResetUnLayoutedItems(LayoutWrapper* layoutWrapper, PositionMap& positionMap)
1781 {
1782 for (auto& pos : positionMap) {
1783 auto wrapper = GetListItem(layoutWrapper, pos.first);
1784 if (!wrapper) {
1785 ReportGetChildError("ResetLayoutItem", pos.first);
1786 }
1787 ResetUnLayoutedItem(wrapper, pos.second);
1788 }
1789 }
1790
ResetUnLayoutedItem(const RefPtr<LayoutWrapper> & layoutWrapper,ListItemInfo & info)1791 void ListLayoutAlgorithm::ResetUnLayoutedItem(const RefPtr<LayoutWrapper>& layoutWrapper, ListItemInfo& info)
1792 {
1793 info.startPos -= currentOffset_;
1794 info.endPos -= currentOffset_;
1795 if (info.isGroup) {
1796 if (layoutWrapper && layoutWrapper->GetHostNode()) {
1797 auto host = layoutWrapper->GetHostNode();
1798 if (host->HasLayoutAlgorithm()) {
1799 info.groupInfo = GetListItemGroupLayoutInfo(layoutWrapper);
1800 }
1801 if (host->GetPattern<ListItemGroupPattern>()) {
1802 auto groupPattern = host->GetPattern<ListItemGroupPattern>();
1803 groupPattern->ClearItemPosition();
1804 }
1805 }
1806 } else {
1807 info.groupInfo.reset();
1808 }
1809 auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(layoutWrapper);
1810 if (wrapperFrameNode) {
1811 wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1812 }
1813 }
1814
Layout(LayoutWrapper * layoutWrapper)1815 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1816 {
1817 auto listProps = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1818 CHECK_NULL_VOID(listProps);
1819 auto axis_ = listProps->GetListDirection().value_or(Axis::VERTICAL);
1820 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1821 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1822 MinusPaddingToSize(padding, size);
1823 paddingOffset_ = padding.Offset();
1824 float crossSize = GetCrossAxisSize(size, axis_);
1825 CalculateTotalCountByRepeat(layoutWrapper);
1826 listItemAlign_ = listProps->GetListItemAlign().value_or(V2::ListItemAlign::START);
1827 int32_t startIndex = GetStartIndex();
1828 isReverse_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
1829
1830 // layout items.
1831 int32_t itemCount = 0;
1832 for (auto& pos : itemPosition_) {
1833 auto wrapper = GetListItem(layoutWrapper, pos.first);
1834 if (!wrapper) {
1835 ReportGetChildError("Layout", pos.first);
1836 continue;
1837 }
1838 pos.second.startPos -= currentOffset_;
1839 pos.second.endPos -= currentOffset_;
1840 if (pos.second.isGroup) {
1841 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1842 itemCount += GetListItemGroupItemCount(wrapper);
1843 } else {
1844 pos.second.groupInfo.reset();
1845 itemCount++;
1846 }
1847 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1848 auto childLayoutProperty = wrapper->GetLayoutProperty();
1849 if (expandSafeArea_ || wrapper->CheckNeedForceMeasureAndLayout() || wrapper->IsIgnoreOptsValid()) {
1850 wrapper->Layout();
1851 } else {
1852 SyncGeometry(wrapper);
1853 }
1854 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1855 if (frameNode) {
1856 auto childNode = layoutWrapper->GetHostNode();
1857 if (pos.second.isGroup && childNode && childNode->GetSuggestOpIncActivatedOnce()) {
1858 frameNode->SetSuggestOpIncActivatedOnce();
1859 } else if (!pos.second.isGroup) {
1860 frameNode->MarkAndCheckNewOpIncNode(axis_);
1861 }
1862 }
1863 }
1864 auto cacheCount = listProps->GetCachedCountWithDefault();
1865 if (!listProps->HasCachedCount()) {
1866 int32_t newCacheCount = UpdateDefaultCachedCount(cacheCount, itemCount);
1867 listProps->SetDefaultCachedCount(newCacheCount);
1868 }
1869 ProcessCacheCount(layoutWrapper, cacheCount, listProps->GetShowCachedItemsValue(false));
1870 FixItemLayoutOffset(layoutWrapper);
1871 LayoutHeader(layoutWrapper, paddingOffset_, crossSize);
1872 UpdateOverlay(layoutWrapper);
1873 ProcessStackFromEnd();
1874 ReverseItemPosition(itemPosition_, totalItemCount_, contentMainSize_);
1875 ReverseItemPosition(cachedItemPosition_, totalItemCount_, contentMainSize_);
1876 ReverseItemPosition(recycledItemPosition_, totalItemCount_, contentMainSize_);
1877 totalOffset_ += currentOffset_;
1878 FixPredictSnapPos();
1879 FixPredictSnapOffset(listProps);
1880 isLayouted_ = true;
1881 }
1882
UpdateOverlay(LayoutWrapper * layoutWrapper)1883 void ListLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper)
1884 {
1885 auto frameNode = layoutWrapper->GetHostNode();
1886 CHECK_NULL_VOID(frameNode);
1887 auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>();
1888 CHECK_NULL_VOID(paintProperty);
1889 bool defaultFadingEdge = paintProperty->GetDefaultFadingEdge().value_or(false);
1890 if (!paintProperty->GetFadingEdge().value_or(defaultFadingEdge)) {
1891 return;
1892 }
1893 auto overlayNode = frameNode->GetOverlayNode();
1894 CHECK_NULL_VOID(overlayNode);
1895 auto geometryNode = frameNode->GetGeometryNode();
1896 CHECK_NULL_VOID(geometryNode);
1897 auto listFrameSize = geometryNode->GetFrameSize(true);
1898 auto overlayGeometryNode = overlayNode->GetGeometryNode();
1899 CHECK_NULL_VOID(overlayGeometryNode);
1900 overlayGeometryNode->SetFrameSize(listFrameSize);
1901 }
1902
CalculateLaneCrossOffset(float crossSize,float childCrossSize,bool isGroup)1903 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize, bool isGroup)
1904 {
1905 float delta = isGroup ? crossSize - childCrossSize : crossSize - GetLaneGutter() - childCrossSize;
1906 if (LessOrEqual(delta, 0)) {
1907 return 0.0f;
1908 }
1909 switch (listItemAlign_) {
1910 case OHOS::Ace::V2::ListItemAlign::START:
1911 return 0.0f;
1912 case OHOS::Ace::V2::ListItemAlign::CENTER:
1913 return delta / 2.0f;
1914 case OHOS::Ace::V2::ListItemAlign::END:
1915 return delta;
1916 default:
1917 return 0.0f;
1918 }
1919 }
1920
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1921 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1922 {
1923 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1924 return;
1925 }
1926 auto host = layoutWrapper->GetHostNode();
1927 CHECK_NULL_VOID(host);
1928 auto focusHub = host->GetFocusHub();
1929 CHECK_NULL_VOID(focusHub);
1930 // textField not in List
1931 if (!focusHub->IsCurrentFocus()) {
1932 return;
1933 }
1934 auto context = host->GetContext();
1935 CHECK_NULL_VOID(context);
1936 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1937 CHECK_NULL_VOID(textFieldManager);
1938 // only when textField is onFocus
1939 auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1940 CHECK_NULL_VOID(textField);
1941 auto textFieldHost = textField->GetHost();
1942 CHECK_NULL_VOID(textFieldHost);
1943 auto textBase = DynamicCast<TextBase>(textField);
1944 CHECK_NULL_VOID(textBase);
1945 auto caretPos = textFieldHost->GetTransformRelativeOffset().GetY() + textBase->GetCaretRect().Bottom();
1946 auto globalOffset = host->GetTransformRelativeOffset();
1947 auto offset = contentMainSize_ + globalOffset.GetY() - caretPos - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
1948 if (LessOrEqual(offset, 0.0)) {
1949 // negative offset to scroll down
1950 currentDelta_ -= static_cast<float>(offset);
1951 }
1952 }
1953
SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm> & itemGroup,bool forwardLayout,int32_t index)1954 void ListLayoutAlgorithm::SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm>& itemGroup,
1955 bool forwardLayout, int32_t index)
1956 {
1957 if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1958 if (!jumpIndexInGroup_.has_value()) {
1959 if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1960 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1961 jumpIndexInGroup_ = isStackFromEnd_ ? LAST_ITEM : 0;
1962 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1963 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1964 jumpIndexInGroup_ = isStackFromEnd_ ? 0 : LAST_ITEM;
1965 }
1966 }
1967
1968 if (jumpIndexInGroup_.has_value()) {
1969 itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1970 itemGroup->SetScrollAlign(scrollAlign_);
1971 jumpIndexInGroup_.reset();
1972 }
1973 }
1974 }
1975
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout,bool needAdjustRefPos)1976 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1977 float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout,
1978 bool needAdjustRefPos)
1979 {
1980 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1981 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1982 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1983 CHECK_NULL_VOID(itemGroup);
1984 if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1985 referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1986 }
1987 if (jumpIndex_) {
1988 itemGroup->ClearItemPosition();
1989 }
1990 if (forwardLayout) {
1991 float endPos = layoutEndMainPos_.value_or(endMainPos_);
1992 float startPos = endPos - contentMainSize_;
1993 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1994 } else {
1995 float startPos = layoutStartMainPos_.value_or(startMainPos_);
1996 float endPos = startPos + contentMainSize_;
1997 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1998 }
1999 bool needMeasureFormLastItem = index < preStartIndex_;
2000 itemGroup->SetNeedMeasureFormLastItem(needMeasureFormLastItem);
2001 itemGroup->SetNeedAdjustRefPos(needAdjustRefPos);
2002 itemGroup->SetListLayoutProperty(layoutProperty);
2003 itemGroup->SetNeedCheckOffset(isNeedCheckOffset_, groupItemAverageHeight_);
2004 itemGroup->SetNeedSyncLoad(syncLoad_);
2005 if (scrollSnapAlign_ != ScrollSnapAlign::CENTER) {
2006 itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
2007 }
2008 SetListItemGroupJumpIndex(itemGroup, forwardLayout, index);
2009
2010 if (groupNeedAllLayout || (targetIndex_ && targetIndex_.value() == index) ||
2011 (scrollSnapAlign_ != ScrollSnapAlign::NONE && !childrenSize_)) {
2012 itemGroup->SetNeedAllLayout();
2013 } else if (forwardFeature_ || backwardFeature_) {
2014 itemGroup->CheckNeedAllLayout(layoutWrapper, forwardLayout);
2015 }
2016 if (CheckNeedMeasure(layoutWrapper)) {
2017 itemGroup->ResetCachedItemPosition();
2018 itemGroup->ResetCachedIndex();
2019 if (layoutWrapper->GetHostNode() && layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
2020 auto groupPattern = layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
2021 groupPattern->SetRecache(true);
2022 }
2023 }
2024 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
2025 }
2026
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)2027 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
2028 {
2029 int32_t id = layoutWrapper->GetHostNode()->GetId();
2030 ListItemInfo pos = { id, 0, 0, true };
2031 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
2032 CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
2033 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
2034 CHECK_NULL_RETURN(itemGroup, pos);
2035 auto res = itemGroup->GetItemGroupPosition(index);
2036 measureInNextFrame_ = itemGroup->GroupMeasureInNextFrame();
2037 return { id, res.first, res.second, true };
2038 }
2039
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)2040 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
2041 {
2042 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
2043 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
2044 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
2045 CHECK_NULL_RETURN(itemGroup, 0.0f);
2046 return itemGroup->GetItemHeight(index);
2047 }
2048
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)2049 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
2050 {
2051 auto host = layoutWrapper->GetHostNode();
2052 CHECK_NULL_VOID(host);
2053 auto listItem = host->GetPattern<ListItemPattern>();
2054 if (listItem) {
2055 listItem->SetIndexInList(index);
2056 return;
2057 }
2058 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
2059 CHECK_NULL_VOID(listItemGroup);
2060 listItemGroup->SetIndexInList(index);
2061 }
2062
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const2063 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
2064 float referencePos, bool forwardLayout) const
2065 {
2066 if (targetIndex_.has_value()) {
2067 return;
2068 }
2069 auto wrapper = GetListItem(layoutWrapper, index);
2070 if (!wrapper) {
2071 ReportGetChildError("CheckListItemGroupRecycle", index);
2072 return;
2073 }
2074 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
2075 CHECK_NULL_VOID(algorithmWrapper);
2076 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
2077 CHECK_NULL_VOID(itemGroup);
2078 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
2079 }
2080
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)2081 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
2082 bool forwardLayout)
2083 {
2084 auto wrapper = GetListItem(layoutWrapper, index);
2085 if (!wrapper) {
2086 ReportGetChildError("AdjustPostionForListItemGroup", index);
2087 return;
2088 }
2089 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(true);
2090 CHECK_NULL_VOID(algorithmWrapper);
2091 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
2092 CHECK_NULL_VOID(itemGroup);
2093 if (forwardLayout) {
2094 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, prevContentMainSize_,
2095 !forwardLayout);
2096 } else {
2097 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, prevContentMainSize_,
2098 !forwardLayout);
2099 }
2100 itemGroup->SetScrollAlign(ScrollAlign::NONE);
2101 wrapper->Measure(GetGroupLayoutConstraint());
2102 measureInNextFrame_ = itemGroup->GroupMeasureInNextFrame();
2103 if (childrenSize_) {
2104 return;
2105 }
2106 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
2107 auto& pos = itemPosition_[index];
2108 if (forwardLayout) {
2109 pos.startPos = pos.endPos - mainLen;
2110 } else {
2111 pos.endPos = pos.startPos + mainLen;
2112 }
2113 }
2114
OffScreenLayoutDirection(LayoutWrapper * layoutWrapper)2115 void ListLayoutAlgorithm::OffScreenLayoutDirection(LayoutWrapper* layoutWrapper)
2116 {
2117 if (!targetIndex_ || itemPosition_.empty()) {
2118 forwardFeature_ = false;
2119 backwardFeature_ = false;
2120 return;
2121 }
2122 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
2123 if (layoutDirection == LayoutDirection::BACKWARD) {
2124 forwardFeature_ = false;
2125 backwardFeature_ = true;
2126 } else {
2127 forwardFeature_ = true;
2128 backwardFeature_ = false;
2129 }
2130 }
2131
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)2132 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
2133 {
2134 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
2135 float midPos = contentSize / 2.0f;
2136 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
2137 GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
2138 midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
2139 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
2140 LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
2141 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
2142 midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
2143 }
2144 for (auto& pos : itemPosition_) {
2145 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2146 return pos.first;
2147 }
2148 }
2149 return totalItemCount_ - 1;
2150 }
2151
SyncGeometry(RefPtr<LayoutWrapper> & wrapper,bool isDirty)2152 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper, bool isDirty)
2153 {
2154 CHECK_NULL_VOID(wrapper);
2155 auto host = wrapper->GetHostNode();
2156 CHECK_NULL_VOID(host);
2157 if (!(isDirty && host->IsGeometrySizeChange() && !host->IsActive())) {
2158 host->ForceSyncGeometryNode();
2159 }
2160 host->ResetLayoutAlgorithm();
2161 host->RebuildRenderContextTree();
2162 }
2163
2164 // return current CachedCount and max CacheCount
GetLayoutGroupCachedCount(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & wrapper,int32_t forwardCache,int32_t backwardCache,int32_t index,bool outOfView)2165 CachedIndexInfo ListLayoutAlgorithm::GetLayoutGroupCachedCount(LayoutWrapper* layoutWrapper,
2166 const RefPtr<LayoutWrapper>& wrapper, int32_t forwardCache, int32_t backwardCache, int32_t index, bool outOfView)
2167 {
2168 CachedIndexInfo res;
2169 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
2170 CHECK_NULL_RETURN(groupNode, res);
2171 auto group = groupNode->GetPattern<ListItemGroupPattern>();
2172 CHECK_NULL_RETURN(group, res);
2173 const auto& itemPos = group->GetItemPosition();
2174 bool reCache = false;
2175 if (outOfView && recycledItemPosition_.count(index) == 0) {
2176 reCache = CheckNeedMeasure(wrapper);
2177 } else if (outOfView) {
2178 wrapper->SetActive(true);
2179 wrapper->Layout();
2180 group->SyncItemsToCachedItemPosition();
2181 recycledItemPosition_.erase(index);
2182 }
2183 bool forward = forwardCache > -1;
2184 bool backward = backwardCache > -1;
2185 if (forward && backward && itemPos.empty()) {
2186 forward = group->NeedCacheForward(layoutWrapper);
2187 backward = !forward;
2188 forwardCache = forward ? forwardCache : -1;
2189 backwardCache = backward ? backwardCache : -1;
2190 }
2191 res = group->UpdateCachedIndex(outOfView, reCache, forwardCache, backwardCache);
2192 if ((group->GetTotalItemCount() == 0 && outOfView) || !group->IsVisible()) {
2193 if (groupNode->CheckNeedForceMeasureAndLayout()) {
2194 res = {0, 0, 1, 1};
2195 } else {
2196 res = {1, 1, 1, 1};
2197 }
2198 }
2199 ACE_SCOPED_TRACE("GetLayoutGroupCachedCount forward:%d, %d, backward:%d, %d",
2200 res.forwardCachedCount, res.forwardCacheMax, res.backwardCachedCount, res.backwardCacheMax);
2201 return res;
2202 }
2203
GetLayoutCrossAxisSize(LayoutWrapper * layoutWrapper)2204 float ListLayoutAlgorithm::GetLayoutCrossAxisSize(LayoutWrapper* layoutWrapper)
2205 {
2206 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
2207 auto layoutProperty = layoutWrapper->GetLayoutProperty();
2208 if (layoutProperty) {
2209 auto padding = layoutProperty->CreatePaddingAndBorder();
2210 MinusPaddingToSize(padding, size);
2211 }
2212 return GetCrossAxisSize(size, axis_);
2213 }
2214
LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2215 int32_t ListLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper,
2216 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2217 {
2218 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2219 curIndex = itemPosition_.rbegin()->first + 1;
2220 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
2221 while (cachedCount < cacheCount && curIndex < totalItemCount_) {
2222 auto wrapper = GetChildByIndex(layoutWrapper, curIndex + itemStartIndex_, !show);
2223 if (!wrapper) {
2224 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2225 return curIndex - 1;
2226 }
2227 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2228 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2229 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper)) && !wrapper->CheckHasPreMeasured()) {
2230 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2231 }
2232 if (!isGroup && isDirty && !wrapper->GetHostNode()->IsLayoutComplete() && !wrapper->CheckHasPreMeasured()) {
2233 return curIndex - 1;
2234 }
2235 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2236 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
2237 int32_t id = wrapper->GetHostNode()->GetId();
2238 ListItemInfo pos = { id, currPos, endPos, isGroup };
2239 currPos = endPos + spaceWidth_;
2240 auto startIndex = curIndex;
2241 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2242 cachedItemPosition_[curIndex] = pos;
2243 if (isGroup) {
2244 auto res = GetLayoutGroupCachedCount(
2245 layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true);
2246 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) {
2247 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2248 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1;
2249 }
2250 cachedCount += std::max(res.forwardCacheMax, 1);
2251 } else {
2252 cachedCount++;
2253 }
2254 ExpandWithSafeAreaPadding(wrapper);
2255 SyncGeometry(wrapper, isDirty);
2256 wrapper->SetActive(false);
2257 curIndex++;
2258 }
2259 return curIndex - 1;
2260 }
2261
LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2262 int32_t ListLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper,
2263 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2264 {
2265 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2266 curIndex = itemPosition_.begin()->first - 1;
2267 auto currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
2268 while (cachedCount < cacheCount && curIndex >= 0) {
2269 auto wrapper = GetChildByIndex(layoutWrapper, curIndex + itemStartIndex_, !show);
2270 if (!wrapper) {
2271 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2272 return curIndex + 1;
2273 }
2274 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2275 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2276 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper)) && !wrapper->CheckHasPreMeasured()) {
2277 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2278 }
2279 if (!isGroup && isDirty && !wrapper->GetHostNode()->IsLayoutComplete() && !wrapper->CheckHasPreMeasured()) {
2280 return curIndex + 1;
2281 }
2282 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2283 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
2284 int32_t id = wrapper->GetHostNode()->GetId();
2285 ListItemInfo pos = { id, startPos, currPos, isGroup };
2286 currPos = startPos - spaceWidth_;
2287 auto startIndex = curIndex;
2288 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2289 cachedItemPosition_[curIndex] = pos;
2290 if (isGroup) {
2291 auto res = GetLayoutGroupCachedCount(
2292 layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true);
2293 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) {
2294 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2295 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1;
2296 }
2297 cachedCount += std::max(res.backwardCacheMax, 1);
2298 } else {
2299 cachedCount++;
2300 }
2301 ExpandWithSafeAreaPadding(wrapper);
2302 SyncGeometry(wrapper, isDirty);
2303 wrapper->SetActive(false);
2304 curIndex--;
2305 }
2306 return curIndex + 1;
2307 }
2308
ExpandWithSafeAreaPadding(const RefPtr<LayoutWrapper> & layoutWrapper)2309 void ListLayoutAlgorithm::ExpandWithSafeAreaPadding(const RefPtr<LayoutWrapper>& layoutWrapper)
2310 {
2311 IgnoreLayoutSafeAreaOpts options = { .type = NG::LAYOUT_SAFE_AREA_TYPE_NONE,
2312 .edges = NG::LAYOUT_SAFE_AREA_EDGE_NONE };
2313 auto layoutProperty = layoutWrapper->GetLayoutProperty();
2314 if (layoutProperty) {
2315 auto&& nodeOpts = layoutWrapper->GetLayoutProperty()->GetIgnoreLayoutSafeAreaOpts();
2316 if (nodeOpts) {
2317 options = *nodeOpts;
2318 }
2319 }
2320
2321 auto geometryNode = layoutWrapper->GetGeometryNode();
2322 if (geometryNode) {
2323 auto offset = geometryNode->GetMarginFrameOffset();
2324 auto ignoreAdjust = geometryNode->GetIgnoreAdjust();
2325 offset -= ignoreAdjust;
2326 geometryNode->SetMarginFrameOffset(offset);
2327 }
2328 }
2329
LayoutCachedItemInEdgeGroup(LayoutWrapper * layoutWrapper,int32_t cacheCount,std::list<PredictLayoutItem> & predictList)2330 std::tuple<int32_t, int32_t, int32_t, int32_t> ListLayoutAlgorithm::LayoutCachedItemInEdgeGroup(
2331 LayoutWrapper* layoutWrapper, int32_t cacheCount, std::list<PredictLayoutItem>& predictList)
2332 {
2333 int32_t startIndex = GetStartIndex();
2334 int32_t endIndex = GetEndIndex();
2335 int32_t cachedForward = 0;
2336 int32_t cachedBackward = 0;
2337 if (startIndex == endIndex && itemPosition_.begin()->second.isGroup) {
2338 auto wrapper = GetChildByIndex(layoutWrapper, startIndex);
2339 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, cacheCount, startIndex, false);
2340 if ((res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) ||
2341 (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount)) {
2342 int32_t forwardCached = res.forwardCacheMax > 0 ? cachedForward : -1;
2343 int32_t backwardCached = res.backwardCacheMax > 0 ? cachedBackward : -1;
2344 predictList.emplace_back(PredictLayoutItem { startIndex, forwardCached, backwardCached });
2345 }
2346 cachedForward += res.forwardCacheMax;
2347 cachedBackward += res.backwardCacheMax;
2348 } else {
2349 if (itemPosition_.rbegin()->second.isGroup) {
2350 auto wrapper = GetChildByIndex(layoutWrapper, endIndex);
2351 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, -1, endIndex, false);
2352 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) {
2353 predictList.emplace_back(PredictLayoutItem { endIndex, cachedForward, -1 });
2354 }
2355 cachedForward += res.forwardCacheMax;
2356 }
2357 if (itemPosition_.begin()->second.isGroup) {
2358 auto wrapper = GetChildByIndex(layoutWrapper, startIndex);
2359 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount, startIndex, false);
2360 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount) {
2361 predictList.emplace_back(PredictLayoutItem { startIndex, -1, cachedBackward });
2362 }
2363 cachedBackward += res.backwardCacheMax;
2364 }
2365 }
2366 return { startIndex, endIndex, cachedForward, cachedBackward };
2367 }
2368
CheckMinCacheRange(LayoutWrapper * layoutWrapper,std::list<PredictLayoutItem> & list,int32_t cacheCount,int32_t start,int32_t end)2369 void CheckMinCacheRange(LayoutWrapper* layoutWrapper, std::list<PredictLayoutItem>& list,
2370 int32_t cacheCount, int32_t start, int32_t end)
2371 {
2372 CHECK_NULL_VOID(layoutWrapper);
2373 auto prop = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
2374 CHECK_NULL_VOID(prop);
2375 int32_t minCacheCount = prop->GetMinCacheCount();
2376 if (minCacheCount >= cacheCount) {
2377 return;
2378 }
2379 for (auto it = list.begin(); it != list.end();) {
2380 if (it->index < start - minCacheCount || it->index > end + minCacheCount) {
2381 it = list.erase(it);
2382 } else {
2383 ++it;
2384 }
2385 }
2386 }
2387
LayoutCachedItemV2(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)2388 std::list<PredictLayoutItem> ListLayoutAlgorithm::LayoutCachedItemV2(LayoutWrapper* layoutWrapper, int32_t cacheCount,
2389 bool show)
2390 {
2391 ACE_SCOPED_TRACE("LayoutCachedItemV2");
2392 std::list<PredictLayoutItem> predictBuildList;
2393 auto [startIndex, endIndex, cachedForward, cachedBackward] =
2394 LayoutCachedItemInEdgeGroup(layoutWrapper, cacheCount, predictBuildList);
2395 if (cachedForward < cacheCount && endIndex < totalItemCount_ - 1) {
2396 endIndex = LayoutCachedForward(layoutWrapper, cacheCount, cachedForward, endIndex, predictBuildList, show);
2397 }
2398 if (cachedBackward < cacheCount && startIndex > 0) {
2399 startIndex =
2400 LayoutCachedBackward(layoutWrapper, cacheCount, cachedBackward, startIndex, predictBuildList, show);
2401 }
2402 CheckMinCacheRange(layoutWrapper, predictBuildList, cacheCount,
2403 itemPosition_.begin()->first, itemPosition_.rbegin()->first);
2404 int32_t cacheStart = itemPosition_.begin()->first - startIndex;
2405 int32_t cacheEnd = endIndex - itemPosition_.rbegin()->first;
2406 if (isStackFromEnd_) {
2407 std::swap(cacheStart, cacheEnd);
2408 }
2409 ResetLayoutItem(layoutWrapper);
2410 SetActiveChildRange(layoutWrapper, cacheStart, cacheEnd, show);
2411 return predictBuildList;
2412 }
2413
PredictBuildGroup(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,const ListMainSizeValues & listMainSizeValues)2414 bool ListLayoutAlgorithm::PredictBuildGroup(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint,
2415 int64_t deadline, int32_t forwardCached, int32_t backwardCached, const ListMainSizeValues& listMainSizeValues)
2416 {
2417 CHECK_NULL_RETURN(wrapper, false);
2418 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
2419 CHECK_NULL_RETURN(groupNode, false);
2420 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
2421 CHECK_NULL_RETURN(groupPattern, false);
2422 float referencePos = 0.0f;
2423 if (listMainSizeValues.jumpIndexInGroup.has_value() && listMainSizeValues.scrollAlign == ScrollAlign::CENTER) {
2424 referencePos = (listMainSizeValues.startPos + listMainSizeValues.endPos) / 2; // 2:average
2425 }
2426 float endPos = 0.0f;
2427 float startPos = 0.0f;
2428 if (listMainSizeValues.forward) {
2429 startPos = listMainSizeValues.startPos;
2430 endPos = listMainSizeValues.layoutEndMainPos.value_or(listMainSizeValues.endPos);
2431 } else {
2432 startPos = listMainSizeValues.layoutStartMainPos.value_or(listMainSizeValues.startPos);
2433 endPos = listMainSizeValues.endPos;
2434 }
2435 ListMainSizeValues values;
2436 values.startPos = startPos;
2437 values.endPos = endPos;
2438 values.referencePos = referencePos;
2439 values.prevContentMainSize = listMainSizeValues.prevContentMainSize;
2440 values.forward = listMainSizeValues.forward;
2441 values.backward = listMainSizeValues.backward;
2442 values.contentStartOffset = listMainSizeValues.contentStartOffset;
2443 values.contentEndOffset = listMainSizeValues.contentEndOffset;
2444 groupPattern->LayoutCache(constraint, deadline, forwardCached, backwardCached, values);
2445 return true;
2446 }
2447
PredictBuildV2(RefPtr<FrameNode> frameNode,int64_t deadline,ListMainSizeValues listMainSizeValues,bool show)2448 void ListLayoutAlgorithm::PredictBuildV2(
2449 RefPtr<FrameNode> frameNode, int64_t deadline, ListMainSizeValues listMainSizeValues, bool show)
2450 {
2451 ACE_SCOPED_TRACE("List predict v2");
2452 CHECK_NULL_VOID(frameNode);
2453 auto pattern = frameNode->GetPattern<ListPattern>();
2454 CHECK_NULL_VOID(pattern);
2455 if (!pattern->GetPredictLayoutParamV2().has_value()) {
2456 return;
2457 }
2458 bool needMarkDirty = false;
2459 auto param = pattern->GetPredictLayoutParamV2().value();
2460 for (auto it = param.items.begin(); it != param.items.end();) {
2461 if (GetSysTimestamp() > deadline) {
2462 break;
2463 }
2464 ACE_SCOPED_TRACE("predict Item:%d", (*it).index);
2465 auto index = !pattern->IsStackFromEnd() ? (*it).index : frameNode->GetTotalChildCount() - (*it).index - 1;
2466 auto wrapper = frameNode->GetOrCreateChildByIndex(index + pattern->GetItemStartIndex(), show, true);
2467 if (!wrapper) {
2468 it = param.items.erase(it);
2469 continue;
2470 }
2471 if (wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
2472 break;
2473 }
2474 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2475 if (!isGroup) {
2476 auto frameNode = wrapper->GetHostNode();
2477 CHECK_NULL_VOID(frameNode);
2478 frameNode->GetGeometryNode()->SetParentLayoutConstraint(param.layoutConstraint);
2479 FrameNode::ProcessOffscreenNode(frameNode);
2480 } else {
2481 listMainSizeValues.forward = (*it).forwardCacheCount > -1;
2482 listMainSizeValues.backward = (*it).backwardCacheCount > -1;
2483 PredictBuildGroup(wrapper, param.groupLayoutConstraint, deadline, (*it).forwardCacheCount,
2484 (*it).backwardCacheCount, listMainSizeValues);
2485 }
2486 needMarkDirty = true;
2487 it = param.items.erase(it);
2488 }
2489 if (needMarkDirty) {
2490 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
2491 }
2492 pattern->SetPredictLayoutParamV2(std::nullopt);
2493 if (!param.items.empty()) {
2494 ListLayoutAlgorithm::PostIdleTaskV2(frameNode, param, listMainSizeValues, show);
2495 }
2496 }
2497
PostIdleTaskV2(RefPtr<FrameNode> frameNode,const ListPredictLayoutParamV2 & param,ListMainSizeValues listMainSizeValues,bool show)2498 void ListLayoutAlgorithm::PostIdleTaskV2(RefPtr<FrameNode> frameNode,
2499 const ListPredictLayoutParamV2& param, ListMainSizeValues listMainSizeValues, bool show)
2500 {
2501 ACE_SCOPED_TRACE("PostIdleTaskV2");
2502 CHECK_NULL_VOID(frameNode);
2503 auto pattern = frameNode->GetPattern<ListPattern>();
2504 CHECK_NULL_VOID(pattern);
2505 if (pattern->GetPredictLayoutParamV2()) {
2506 pattern->SetPredictLayoutParamV2(param);
2507 return;
2508 }
2509 pattern->SetPredictLayoutParamV2(param);
2510 auto context = frameNode->GetContext();
2511 CHECK_NULL_VOID(context);
2512 if (!context->IsWindowSizeDragging()) {
2513 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode)), listMainSizeValues, show]
2514 (int64_t deadline, bool canUseLongPredictTask) {
2515 ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, listMainSizeValues, show);
2516 }
2517 );
2518 return;
2519 }
2520 context->AddWindowSizeDragEndCallback([weak = WeakClaim(RawPtr(frameNode)), listMainSizeValues, show]() {
2521 auto frameNode = weak.Upgrade();
2522 CHECK_NULL_VOID(frameNode);
2523 auto pattern = frameNode->GetPattern<ListPattern>();
2524 CHECK_NULL_VOID(pattern);
2525 if (!pattern->GetPredictLayoutParamV2().has_value()) {
2526 return;
2527 }
2528 auto context = frameNode->GetContext();
2529 CHECK_NULL_VOID(context);
2530 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode)), listMainSizeValues, show]
2531 (int64_t deadline, bool canUseLongPredictTask) {
2532 ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, listMainSizeValues, show);
2533 }
2534 );
2535 });
2536 }
2537
GetStopOnScreenOffset(ScrollSnapAlign scrollSnapAlign) const2538 float ListLayoutAlgorithm::GetStopOnScreenOffset(ScrollSnapAlign scrollSnapAlign) const
2539 {
2540 float stopOnScreen = 0;
2541 if (scrollSnapAlign == ScrollSnapAlign::START) {
2542 stopOnScreen = contentStartOffset_;
2543 } else if (scrollSnapAlign == ScrollSnapAlign::CENTER) {
2544 stopOnScreen = contentMainSize_ / 2.0f;
2545 } else if (scrollSnapAlign == ScrollSnapAlign::END) {
2546 stopOnScreen = contentMainSize_ - contentEndOffset_;
2547 }
2548 return stopOnScreen;
2549 }
2550
FindPredictSnapIndexInItemPositionsStart(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2551 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsStart(
2552 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2553 {
2554 float stopOnScreen = GetStopOnScreenOffset(ScrollSnapAlign::START);
2555 float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
2556 for (const auto& positionInfo : itemPosition_) {
2557 auto startPos = positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
2558 itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2559 auto endPos = positionInfo.second.startPos + itemHeight / 2.0f;
2560 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2561 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2562 endIndex = positionInfo.first;
2563 }
2564 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2565 currIndex = positionInfo.first;
2566 }
2567 if (endIndex >= 0 && currIndex >= 0) {
2568 break;
2569 }
2570 }
2571 }
2572
FindPredictSnapIndexInItemPositionsCenter(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2573 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsCenter(
2574 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2575 {
2576 float stopOnScreen = GetStopOnScreenOffset(ScrollSnapAlign::CENTER);
2577 for (const auto& positionInfo : itemPosition_) {
2578 auto startPos = positionInfo.second.startPos - spaceWidth_ / 2.0f;
2579 auto endPos = positionInfo.second.endPos + spaceWidth_ / 2.0f;
2580 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2581 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2582 endIndex = positionInfo.first;
2583 }
2584 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2585 currIndex = positionInfo.first;
2586 }
2587 if (endIndex >= 0 && currIndex >= 0) {
2588 break;
2589 }
2590 }
2591 }
2592
FindPredictSnapIndexInItemPositionsEnd(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2593 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsEnd(
2594 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2595 {
2596 float stopOnScreen = GetStopOnScreenOffset(ScrollSnapAlign::END);
2597 float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
2598 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2599 auto endPos = pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
2600 itemHeight = pos->second.endPos - pos->second.startPos;
2601 auto startPos = pos->second.endPos - itemHeight / 2.0f;
2602 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2603 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2604 endIndex = pos->first;
2605 }
2606 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2607 currIndex = pos->first;
2608 }
2609 if (endIndex >= 0 && currIndex >= 0) {
2610 break;
2611 }
2612 }
2613 }
2614
FindPredictSnapEndIndexInItemPositions(float predictEndPos,ScrollSnapAlign scrollSnapAlign)2615 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
2616 float predictEndPos, ScrollSnapAlign scrollSnapAlign)
2617 {
2618 int32_t endIndex = -1;
2619 int32_t currIndex = -1;
2620
2621 if (scrollSnapAlign == ScrollSnapAlign::START) {
2622 FindPredictSnapIndexInItemPositionsStart(predictEndPos, endIndex, currIndex);
2623 } else if (scrollSnapAlign == ScrollSnapAlign::CENTER) {
2624 FindPredictSnapIndexInItemPositionsCenter(predictEndPos, endIndex, currIndex);
2625 } else if (scrollSnapAlign == ScrollSnapAlign::END) {
2626 FindPredictSnapIndexInItemPositionsEnd(predictEndPos, endIndex, currIndex);
2627 }
2628 if (endIndex == currIndex && currIndex >= 0) {
2629 if (scrollSnapVelocity_ < -SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2630 endIndex = std::min(GetEndIndex(), endIndex + 1);
2631 } else if (scrollSnapVelocity_ > SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2632 endIndex = std::max(GetStartIndex(), endIndex - 1);
2633 }
2634 }
2635 return endIndex;
2636 }
2637
IsUniformHeightProbably()2638 bool ListLayoutAlgorithm::IsUniformHeightProbably()
2639 {
2640 bool isUniformHeightProbably = true;
2641 float itemHeight = 0.0f;
2642 float currentItemHeight = 0.0f;
2643 for (const auto& positionInfo : itemPosition_) {
2644 currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2645 if (NearZero(itemHeight)) {
2646 itemHeight = currentItemHeight;
2647 } else if (!NearEqual(currentItemHeight, itemHeight)) {
2648 isUniformHeightProbably = false;
2649 break;
2650 }
2651 }
2652 return isUniformHeightProbably;
2653 }
2654
CalculatePredictSnapEndPositionByIndex(int32_t index,ScrollSnapAlign scrollSnapAlign)2655 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(int32_t index, ScrollSnapAlign scrollSnapAlign)
2656 {
2657 float predictSnapEndPos = 0;
2658 if (scrollSnapAlign == ScrollSnapAlign::START) {
2659 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
2660 float endPos = GetEndPosition();
2661 float itemTotalSize = endPos - GetStartPosition();
2662 float contentSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
2663 if ((GetEndIndex() == totalItemCount_ - 1) && GreatNotEqual(itemTotalSize, contentSize) &&
2664 GreatNotEqual(predictSnapEndPos + contentMainSize_ - contentEndOffset_, totalOffset_ + endPos)) {
2665 predictSnapEndPos = totalOffset_ + endPos - contentMainSize_ + contentEndOffset_;
2666 }
2667 } else if (scrollSnapAlign == ScrollSnapAlign::CENTER) {
2668 float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
2669 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
2670 } else if (scrollSnapAlign == ScrollSnapAlign::END) {
2671 predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
2672 if (GetStartIndex() == 0 && LessNotEqual(predictSnapEndPos, totalOffset_ + GetStartPosition())) {
2673 predictSnapEndPos = totalOffset_ + GetStartPosition() - contentStartOffset_;
2674 }
2675 }
2676 return predictSnapEndPos;
2677 }
2678
GetSnapStartIndexAndPos()2679 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapStartIndexAndPos()
2680 {
2681 int32_t startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
2682 float startPos = itemPosition_.begin()->second.startPos;
2683 for (auto& pos : itemPosition_) {
2684 if (NearEqual(pos.second.startPos, prevContentStartOffset_)) {
2685 startIndex = pos.first;
2686 startPos = itemPosition_[startIndex].startPos + contentStartOffset_ - prevContentStartOffset_;
2687 break;
2688 } else if (GreatNotEqual(pos.second.startPos, prevContentStartOffset_)) {
2689 if ((GetEndIndex() == totalItemCount_ - 1) &&
2690 NearEqual(GetEndPosition(), prevContentMainSize_ - prevContentEndOffset_) && !canOverScrollStart_) {
2691 startIndex = pos.first;
2692 startPos = contentStartOffset_;
2693 adjustOffset_ = pos.second.startPos - prevContentStartOffset_;
2694 }
2695 break;
2696 }
2697 }
2698 return std::make_pair(std::min(startIndex, totalItemCount_ -1), startPos);
2699 }
2700
GetSnapEndIndexAndPos()2701 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapEndIndexAndPos()
2702 {
2703 int32_t endIndex = -1;
2704 float endPos = 0.0f;
2705 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2706 if (NearEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2707 endIndex = pos->first;
2708 endPos = itemPosition_[endIndex].endPos - contentEndOffset_ + prevContentEndOffset_;
2709 break;
2710 } else if (GreatNotEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2711 if ((GetStartIndex() == 0) && NearEqual(GetStartPosition(), prevContentStartOffset_) &&
2712 !canOverScrollEnd_) {
2713 endIndex = pos->first;
2714 endPos = prevContentMainSize_ - contentEndOffset_;
2715 adjustOffset_ = pos->second.endPos + prevContentEndOffset_ - prevContentMainSize_;
2716 }
2717 break;
2718 }
2719 }
2720 return std::make_pair(std::min(endIndex, totalItemCount_ - 1), endPos);
2721 }
2722
ReverseItemPosition(ListLayoutAlgorithm::PositionMap & itemPosition,int32_t totalItemCount,float mainSize)2723 void ListLayoutAlgorithm::ReverseItemPosition(
2724 ListLayoutAlgorithm::PositionMap& itemPosition, int32_t totalItemCount, float mainSize)
2725 {
2726 if (!isStackFromEnd_ || itemPosition.empty()) {
2727 return;
2728 }
2729 ListLayoutAlgorithm::PositionMap posMap;
2730 for (auto pos : itemPosition) {
2731 auto startPos = mainSize - pos.second.endPos;
2732 auto endPos = mainSize - pos.second.startPos;
2733 pos.second.startPos = startPos;
2734 pos.second.endPos = endPos;
2735 posMap[totalItemCount - pos.first - 1] = pos.second;
2736 }
2737 itemPosition = std::move(posMap);
2738 }
2739
ProcessStackFromEnd()2740 void ListLayoutAlgorithm::ProcessStackFromEnd()
2741 {
2742 if (isStackFromEnd_ && totalItemCount_ > 0) {
2743 currentDelta_ = -currentDelta_;
2744 currentOffset_ = -currentOffset_;
2745 std::swap(canOverScrollStart_, canOverScrollEnd_);
2746 std::swap(contentStartOffset_, contentEndOffset_);
2747 if (scrollAlign_ == ScrollAlign::START) {
2748 scrollAlign_ = ScrollAlign::END;
2749 } else if (scrollAlign_ == ScrollAlign::END) {
2750 scrollAlign_ = ScrollAlign::START;
2751 }
2752 if (childrenSize_ && posMap_) {
2753 posMap_->ReversePosMap();
2754 }
2755 }
2756 }
2757
UpdateDefaultCachedCount(const int32_t oldCacheCount,const int32_t itemCount)2758 int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount)
2759 {
2760 if (itemCount <= 0) {
2761 return oldCacheCount;
2762 }
2763 thread_local float pageCount = SystemProperties::GetPageCount();
2764 if (pageCount <= 0.0f) {
2765 return oldCacheCount;
2766 }
2767 constexpr int32_t MAX_DEFAULT_CACHED_COUNT = 16;
2768 int32_t newCachedCount = static_cast<int32_t>(ceil(pageCount * itemCount));
2769 if (newCachedCount > MAX_DEFAULT_CACHED_COUNT) {
2770 TAG_LOGI(AceLogTag::ACE_LIST, "Default cachedCount exceed 16");
2771 return MAX_DEFAULT_CACHED_COUNT;
2772 } else {
2773 return std::max(newCachedCount, oldCacheCount);
2774 }
2775 }
2776
ReportGetChildError(const std::string & funcName,int32_t index) const2777 void ListLayoutAlgorithm::ReportGetChildError(const std::string& funcName, int32_t index) const
2778 {
2779 if (index < 0 || index > totalItemCount_ - 1) {
2780 return;
2781 }
2782 std::string subErrorType = funcName + " get item: " + std::to_string(index) + " failed.";
2783 EventReport::ReportScrollableErrorEvent("List", ScrollableErrorType::GET_CHILD_FAILED, subErrorType);
2784 }
2785
CalculateTotalCountByRepeat(LayoutWrapper * layoutWrapper)2786 void ListLayoutAlgorithm::CalculateTotalCountByRepeat(LayoutWrapper* layoutWrapper)
2787 {
2788 auto host = layoutWrapper->GetHostNode();
2789 CHECK_NULL_VOID(host);
2790 auto pattern = host->GetPattern<ListPattern>();
2791 CHECK_NULL_VOID(pattern);
2792 auto repeatDifference = 0;
2793 firstRepeatCount_ = 0;
2794 totalItemCount_ = 0;
2795 pattern->GetRepeatCountInfo(host, repeatDifference, firstRepeatCount_, totalItemCount_);
2796 pattern->SetRepeatDifference(repeatDifference);
2797 totalItemCount_ = (repeatDifference > 0 ? firstRepeatCount_ : totalItemCount_) - itemStartIndex_;
2798 }
2799 } // namespace OHOS::Ace::NG
2800