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