• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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