• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     return 1;
992 }
993 
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)994 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
995     int32_t& currentIndex, float endPos, float& startPos)
996 {
997     if (currentIndex - 1 < 0) {
998         return 0;
999     }
1000     if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex - 1) {
1001         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
1002         CHECK_NULL_RETURN(wrapper, 0);
1003         int32_t id = wrapper->GetHostNode()->GetId();
1004         --currentIndex;
1005         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1006         if (isGroup) {
1007             auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1008             SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
1009             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, endPos);
1010             wrapper->Measure(childLayoutConstraint_);
1011         } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
1012             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, endPos);
1013             wrapper->Measure(childLayoutConstraint_);
1014         }
1015         float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
1016             GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1017         startPos = endPos - mainLen;
1018         itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
1019     } else {
1020         --currentIndex;
1021         itemPosition_[currentIndex] = firstItemInfo_.value().second;
1022         startPos = itemPosition_[currentIndex].startPos;
1023     }
1024     if (firstItemInfo_) {
1025         firstItemInfo_.reset();
1026     }
1027     return 1;
1028 }
1029 
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)1030 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
1031 {
1032     float currentEndPos = startPos;
1033     float currentStartPos = 0.0f;
1034     float endMainPos = (overScrollFeature_ && startIndex == 0) ?
1035         std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
1036     layoutEndMainPos_ = endMainPos;
1037     if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1038         endMainPos = Infinity<float>();
1039     }
1040 
1041     auto currentIndex = startIndex - 1;
1042     auto chainOffset = 0.0f;
1043     do {
1044         currentStartPos = currentEndPos;
1045         int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
1046         if (count == 0) {
1047             break;
1048         }
1049         if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
1050             currentEndPos += spaceWidth_;
1051         }
1052         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1053         // reach the valid target index
1054         if (forwardFeature_ && targetIndex_ && currentIndex >= targetIndex_.value()) {
1055             endMainPos = layoutEndMainPos_.value_or(endMainPos_);
1056             forwardFeature_ = false;
1057         }
1058     } while (LessOrEqual(currentEndPos + chainOffset, endMainPos));
1059     currentEndPos += chainOffset;
1060 
1061     while (itemPosition_.size() > 1 && !targetIndex_) {
1062         auto pos = itemPosition_.rbegin();
1063         float chainDelta = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1064         if ((GreatNotEqual(pos->second.endPos + chainDelta, endMainPos) &&
1065             GreatOrEqual(pos->second.startPos + chainDelta, endMainPos))) {
1066             recycledItemPosition_.emplace(pos->first, pos->second);
1067             itemPosition_.erase(pos->first);
1068         } else {
1069             break;
1070         }
1071     }
1072     // adjust offset.
1073     UpdateSnapCenterContentOffset(layoutWrapper);
1074     if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
1075         endMainPos_ = currentEndPos + contentEndOffset_;
1076         startMainPos_ = endMainPos_ - contentMainSize_;
1077         ReMeasureListItemGroup(layoutWrapper, true);
1078         auto firstItemTop = itemPosition_.begin()->second.startPos;
1079         auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
1080         if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
1081             // all items size is less than list.
1082             if (!canOverScroll_) {
1083                 currentOffset_ = firstItemTop - contentStartOffset_;
1084                 startMainPos_ = currentOffset_;
1085                 endMainPos_ = startMainPos_ + contentMainSize_;
1086             }
1087             if (!mainSizeIsDefined_) {
1088                 // adapt child size.
1089                 contentMainSize_ = itemTotalSize;
1090             }
1091         } else {
1092             // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1093             if (!canOverScroll_ || jumpIndex_.has_value()) {
1094                 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
1095             }
1096         }
1097     }
1098     if (overScrollFeature_ && canOverScroll_) {
1099         return;
1100     }
1101     // Mark inactive in wrapper.
1102     for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1103         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1104         // Don't recycle When the head item is Visibility.None.
1105         if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) ||
1106             GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) {
1107             if (pos->second.isGroup) {
1108                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
1109             }
1110             break;
1111         }
1112         recycledItemPosition_.emplace(pos->first, pos->second);
1113         pos = itemPosition_.erase(pos);
1114     }
1115 }
1116 
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)1117 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
1118 {
1119     float currentStartPos = endPos;
1120     float currentEndPos = 0.0f;
1121     float startMainPos = (overScrollFeature_ && endIndex == totalItemCount_ - 1) ?
1122         std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
1123     layoutStartMainPos_ = startMainPos;
1124     if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1125         startMainPos = -Infinity<float>();
1126     }
1127     auto currentIndex = endIndex + 1;
1128     auto chainOffset = 0.0f;
1129     do {
1130         currentEndPos = currentStartPos;
1131         int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
1132         if (count == 0) {
1133             break;
1134         }
1135         if (currentIndex > 0) {
1136             currentStartPos = currentStartPos - spaceWidth_;
1137         }
1138         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1139         // reach the valid target index
1140         if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
1141             startMainPos = layoutStartMainPos_.value_or(startMainPos_);
1142             backwardFeature_ = false;
1143         }
1144     } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
1145 
1146     currentStartPos += chainOffset;
1147     // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1148     UpdateSnapCenterContentOffset(layoutWrapper);
1149     if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_) && !itemPosition_.empty()) {
1150         auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
1151         bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
1152         if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
1153             if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
1154                 currentOffset_ += contentMainSize_ - prevContentMainSize_;
1155             }
1156             contentMainSize_ = itemTotalSize;
1157         }
1158         if (!canOverScroll_ || jumpIndex_.has_value()) {
1159             currentOffset_ = currentStartPos - contentStartOffset_;
1160         }
1161         endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
1162         startMainPos_ = currentStartPos - contentStartOffset_;
1163         ReMeasureListItemGroup(layoutWrapper, false);
1164     }
1165 
1166     if (overScrollFeature_) {
1167         return;
1168     }
1169 
1170     // Mark inactive in wrapper.
1171     std::list<int32_t> removeIndexes;
1172     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1173         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1174         // Don't recycle When the tail item is Visibility.None.
1175         if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) ||
1176             LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) {
1177             if (pos->second.isGroup) {
1178                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
1179             }
1180             break;
1181         }
1182         recycledItemPosition_.emplace(pos->first, pos->second);
1183         removeIndexes.emplace_back(pos->first);
1184     }
1185     for (const auto& index : removeIndexes) {
1186         itemPosition_.erase(index);
1187     }
1188 }
1189 
ReMeasureListItemGroup(LayoutWrapper * layoutWrapper,bool forwardLayout)1190 void ListLayoutAlgorithm::ReMeasureListItemGroup(LayoutWrapper* layoutWrapper, bool forwardLayout)
1191 {
1192     if (forwardFeature_ || backwardFeature_) {
1193         return;
1194     }
1195     if (forwardLayout) {
1196         if (itemPosition_.begin()->second.isGroup) {
1197             AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), forwardLayout);
1198         }
1199         return;
1200     }
1201     for (auto pos = itemPosition_.begin(); pos != itemPosition_.end(); pos++) {
1202         float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1203         if (GreatOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
1204             break;
1205         } else if (!pos->second.isGroup) {
1206             continue;
1207         }
1208         AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout);
1209     }
1210 }
1211 
FixPredictSnapPos()1212 void ListLayoutAlgorithm::FixPredictSnapPos()
1213 {
1214     if (!predictSnapEndPos_.has_value() || itemPosition_.empty()) {
1215         return;
1216     }
1217     float predictEndPos = predictSnapEndPos_.value();
1218     int32_t predictIndex = -1;
1219     int32_t curIndex = -1; // here invalid.
1220     switch (scrollSnapAlign_) {
1221         case V2::ScrollSnapAlign::START:
1222             FindPredictSnapIndexInItemPositionsStart(predictEndPos + currentOffset_, predictIndex, curIndex);
1223             break;
1224         case V2::ScrollSnapAlign::CENTER:
1225             FindPredictSnapIndexInItemPositionsCenter(predictEndPos + currentOffset_, predictIndex, curIndex);
1226             break;
1227         case V2::ScrollSnapAlign::END:
1228             FindPredictSnapIndexInItemPositionsEnd(predictEndPos + currentOffset_, predictIndex, curIndex);
1229             break;
1230         default:
1231             return;
1232     }
1233     if (GetStartIndex() <= predictIndex && predictIndex <= GetEndIndex()) {
1234         predictEndPos = CalculatePredictSnapEndPositionByIndex(predictIndex, scrollSnapAlign_);
1235         predictEndPos -= currentOffset_;
1236     }
1237     if (!NearEqual(predictEndPos, predictSnapEndPos_.value())) {
1238         predictSnapEndPos_ = predictEndPos;
1239     }
1240 }
1241 
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)1242 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
1243 {
1244     if (!predictSnapOffset_.has_value() || itemPosition_.empty()) {
1245         return;
1246     }
1247     if (scrollSnapAlign_ == V2::ScrollSnapAlign::NONE) {
1248         predictSnapOffset_.reset();
1249         predictSnapEndPos_.reset();
1250         return;
1251     }
1252 
1253     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1254     int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign_);
1255     if (GetStartIndex() <= endIndex && endIndex <= GetEndIndex()) {
1256         predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign_);
1257         predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_;
1258         predictSnapEndPos_.reset();
1259     } else {
1260         if (IsUniformHeightProbably()) {
1261             if (scrollSnapAlign_ == V2::ScrollSnapAlign::START) {
1262                 FixPredictSnapOffsetAlignStart();
1263             } else if (scrollSnapAlign_ == V2::ScrollSnapAlign::CENTER) {
1264                 FixPredictSnapOffsetAlignCenter();
1265             } else if (scrollSnapAlign_ == V2::ScrollSnapAlign::END) {
1266                 FixPredictSnapOffsetAlignEnd();
1267             }
1268         } else {
1269             predictSnapEndPos_ = predictEndPos;
1270         }
1271     }
1272 
1273     return;
1274 }
1275 
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1276 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1277 {
1278     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1279     CHECK_NULL_RETURN(listLayoutProperty, false);
1280     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1281     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1282         return true;
1283     }
1284 
1285     return false;
1286 }
1287 
FixPredictSnapOffsetAlignStart()1288 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1289 {
1290     if (itemPosition_.empty()) {
1291         return;
1292     }
1293     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1294     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1295     float startPos = contentStartOffset_;
1296     float endPos = contentMainSize_ - contentEndOffset_;
1297     float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1298 
1299     if (LessNotEqual(predictEndPos, -startPos)) {
1300         if (isSpringEffect_) {
1301             return;
1302         }
1303         predictEndPos = -startPos;
1304     } else if (GreatNotEqual(predictEndPos, maxPos)) {
1305         if (isSpringEffect_) {
1306             return;
1307         }
1308         predictEndPos = maxPos;
1309     } else {
1310         int32_t index;
1311         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1312             if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1313                 break;
1314             }
1315         }
1316         predictEndPos = index * itemHeight - startPos;
1317         if (LessNotEqual(predictEndPos, -startPos)) {
1318             predictEndPos = -startPos;
1319         } else if (GreatNotEqual(predictEndPos, maxPos)) {
1320             predictEndPos = maxPos;
1321         }
1322     }
1323 
1324     predictSnapOffset_ = totalOffset_ - predictEndPos;
1325     predictSnapEndPos_ = predictEndPos;
1326 }
1327 
FixPredictSnapOffsetAlignCenter()1328 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1329 {
1330     if (itemPosition_.empty()) {
1331         return;
1332     }
1333     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1334     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1335     if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1336         if (isSpringEffect_) {
1337             return;
1338         }
1339         predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1340     } else if (GreatNotEqual(
1341         predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1342         if (isSpringEffect_) {
1343             return;
1344         }
1345         predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1346     } else {
1347         int32_t index;
1348         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1349             if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1350                 itemHeight / 2.0f) {
1351                 break;
1352             }
1353         }
1354         predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1355         if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1356             predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1357         } else if (GreatNotEqual(
1358             predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1359             predictEndPos =
1360                 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1361         }
1362     }
1363 
1364     predictSnapOffset_ = totalOffset_ - predictEndPos;
1365     predictSnapEndPos_ = predictEndPos;
1366 }
1367 
FixPredictSnapOffsetAlignEnd()1368 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1369 {
1370     if (itemPosition_.empty()) {
1371         return;
1372     }
1373     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1374     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1375     float startPos = contentStartOffset_;
1376     float endPos = contentMainSize_ - contentEndOffset_;
1377     float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1378 
1379     if (LessNotEqual(predictEndPos, -startPos)) {
1380         if (isSpringEffect_) {
1381             return;
1382         }
1383         predictEndPos = -startPos;
1384     } else if (GreatNotEqual(predictEndPos, maxPos)) {
1385         if (isSpringEffect_) {
1386             return;
1387         }
1388         predictEndPos = maxPos;
1389     } else {
1390         int32_t index;
1391         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1392             if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1393                 break;
1394             }
1395         }
1396         predictEndPos = index * itemHeight - endPos - spaceWidth_;
1397         if (LessNotEqual(predictEndPos, -startPos)) {
1398             predictEndPos = -startPos;
1399         } else if (GreatNotEqual(predictEndPos, maxPos)) {
1400             predictEndPos = maxPos;
1401         }
1402     }
1403 
1404     predictSnapOffset_ = totalOffset_ - predictEndPos;
1405     predictSnapEndPos_ = predictEndPos;
1406 }
1407 
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1408 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1409     int32_t& startIndex, float crossSize)
1410 {
1411     CHECK_NULL_VOID(wrapper);
1412     auto offset = paddingOffset_;
1413     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1414     float crossOffset = 0.0f;
1415     if (GetLanes() > 1) {
1416         int32_t laneIndex = 0;
1417         if (pos.isGroup) {
1418             startIndex = index + 1;
1419         } else {
1420             laneIndex = (index - startIndex) % GetLanes();
1421         }
1422 
1423         float laneGutter = GetLaneGutter();
1424         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
1425         crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1426     } else {
1427         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1428     }
1429     auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
1430     if (isReverse_) {
1431         if (axis_ == Axis::VERTICAL) {
1432             auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1433             offset = offset + OffsetF(crossSize - crossOffset - size.Width(), pos.startPos + chainOffset);
1434         } else {
1435             offset = offset + OffsetF(contentMainSize_ - pos.endPos - chainOffset, crossOffset);
1436         }
1437     } else {
1438         if (axis_ == Axis::VERTICAL) {
1439             offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
1440         } else {
1441             offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
1442         }
1443     }
1444     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1445     SetListItemIndex(wrapper, index);
1446 }
1447 
ProcessCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)1448 void ListLayoutAlgorithm::ProcessCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
1449 {
1450     if (!itemPosition_.empty() && cacheCount > 0) {
1451         auto items = LayoutCachedItemV2(layoutWrapper, cacheCount, show);
1452         auto host = layoutWrapper->GetHostNode();
1453         CHECK_NULL_VOID(host);
1454         if (!items.empty()) {
1455             ListMainSizeValues value = { startMainPos_, endMainPos_, jumpIndexInGroup_, prevContentMainSize_,
1456                 scrollAlign_, layoutStartMainPos_, layoutEndMainPos_ };
1457             PostIdleTaskV2(host, { items, childLayoutConstraint_, GetGroupLayoutConstraint() }, value, show);
1458         } else {
1459             auto pattern = host->GetPattern<ListPattern>();
1460             CHECK_NULL_VOID(pattern);
1461             pattern->SetPredictLayoutParamV2(std::nullopt);
1462         }
1463     } else {
1464         ResetLayoutItem(layoutWrapper);
1465         SetActiveChildRange(layoutWrapper, cacheCount, cacheCount, show);
1466     }
1467 }
1468 
GetListItemGroupLayoutInfo(const RefPtr<LayoutWrapper> & wrapper) const1469 std::optional<ListItemGroupLayoutInfo> ListLayoutAlgorithm::GetListItemGroupLayoutInfo(
1470     const RefPtr<LayoutWrapper>& wrapper) const
1471 {
1472     CHECK_NULL_RETURN(wrapper, std::nullopt);
1473     auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1474     CHECK_NULL_RETURN(layoutAlgorithmWrapper, std::nullopt);
1475     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1476     CHECK_NULL_RETURN(itemGroup, std::nullopt);
1477     return itemGroup->GetLayoutInfo();
1478 }
1479 
GetListItemGroupItemCount(const RefPtr<LayoutWrapper> & wrapper) const1480 int32_t ListLayoutAlgorithm::GetListItemGroupItemCount(const RefPtr<LayoutWrapper>& wrapper) const
1481 {
1482     CHECK_NULL_RETURN(wrapper, 0);
1483     auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1484     CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
1485     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1486     CHECK_NULL_RETURN(itemGroup, 0);
1487     int32_t itemCount = itemGroup->GetListItemCount();
1488     return itemCount == 0 ? 1 : itemCount;
1489 }
1490 
ResetLayoutItem(LayoutWrapper * layoutWrapper)1491 void ListLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1492 {
1493     for (auto& pos : recycledItemPosition_) {
1494         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1495         pos.second.startPos -= currentOffset_;
1496         pos.second.endPos -= currentOffset_;
1497         if (pos.second.isGroup) {
1498             pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1499             if (wrapper && wrapper->GetHostNode() && wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1500                 auto groupPattern = wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1501                 groupPattern->ClearItemPosition();
1502             }
1503         } else {
1504             pos.second.groupInfo.reset();
1505         }
1506         auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper);
1507         if (wrapperFrameNode) {
1508             wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1509         }
1510     }
1511 }
1512 
Layout(LayoutWrapper * layoutWrapper)1513 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1514 {
1515     auto listProps = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1516     CHECK_NULL_VOID(listProps);
1517     auto axis_ = listProps->GetListDirection().value_or(Axis::VERTICAL);
1518     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1519     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1520     MinusPaddingToSize(padding, size);
1521     paddingOffset_ = padding.Offset();
1522     float crossSize = GetCrossAxisSize(size, axis_);
1523     totalItemCount_ = layoutWrapper->GetTotalChildCount();
1524     listItemAlign_ = listProps->GetListItemAlign().value_or(V2::ListItemAlign::START);
1525     int32_t startIndex = GetStartIndex();
1526     isReverse_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
1527 
1528     totalOffset_ += currentOffset_;
1529     FixPredictSnapPos();
1530     FixPredictSnapOffset(listProps);
1531     // layout items.
1532     int32_t itemCount = 0;
1533     for (auto& pos : itemPosition_) {
1534         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1535         if (!wrapper) {
1536             LOGI("wrapper is out of boundary");
1537             continue;
1538         }
1539         pos.second.startPos -= currentOffset_;
1540         pos.second.endPos -= currentOffset_;
1541         if (pos.second.isGroup) {
1542             pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1543             itemCount += GetListItemGroupItemCount(wrapper);
1544         } else {
1545             pos.second.groupInfo.reset();
1546             itemCount++;
1547         }
1548         LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1549         if (expandSafeArea_ || wrapper->CheckNeedForceMeasureAndLayout()) {
1550             wrapper->Layout();
1551         } else {
1552             SyncGeometry(wrapper);
1553         }
1554         auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1555         if (frameNode) {
1556             frameNode->MarkAndCheckNewOpIncNode();
1557         }
1558     }
1559     auto cacheCount = listProps->GetCachedCountWithDefault();
1560     if (!listProps->HasCachedCount()) {
1561         int32_t newCacheCount = UpdateDefaultCachedCount(cacheCount, itemCount);
1562         listProps->SetDefaultCachedCount(newCacheCount);
1563     }
1564     ProcessCacheCount(layoutWrapper, cacheCount, listProps->GetShowCachedItemsValue(false));
1565     isLayouted_ = true;
1566     UpdateOverlay(layoutWrapper);
1567 }
1568 
UpdateOverlay(LayoutWrapper * layoutWrapper)1569 void ListLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper)
1570 {
1571     auto frameNode = layoutWrapper->GetHostNode();
1572     CHECK_NULL_VOID(frameNode);
1573     auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>();
1574     CHECK_NULL_VOID(paintProperty);
1575     if (!paintProperty->GetFadingEdge().value_or(false)) {
1576         return;
1577     }
1578     auto overlayNode = frameNode->GetOverlayNode();
1579     CHECK_NULL_VOID(overlayNode);
1580     auto geometryNode = frameNode->GetGeometryNode();
1581     CHECK_NULL_VOID(geometryNode);
1582     auto listFrameSize = geometryNode->GetFrameSize();
1583     auto overlayGeometryNode = overlayNode->GetGeometryNode();
1584     CHECK_NULL_VOID(overlayGeometryNode);
1585     overlayGeometryNode->SetFrameSize(listFrameSize);
1586 }
1587 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1588 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1589 {
1590     float delta = crossSize - GetLaneGutter() - childCrossSize;
1591     if (LessOrEqual(delta, 0)) {
1592         return 0.0f;
1593     }
1594     switch (listItemAlign_) {
1595         case OHOS::Ace::V2::ListItemAlign::START:
1596             return 0.0f;
1597         case OHOS::Ace::V2::ListItemAlign::CENTER:
1598             return delta / 2.0f;
1599         case OHOS::Ace::V2::ListItemAlign::END:
1600             return delta;
1601         default:
1602             LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_);
1603             return 0.0f;
1604     }
1605 }
1606 
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1607 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1608 {
1609     if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1610         return;
1611     }
1612     auto host = layoutWrapper->GetHostNode();
1613     CHECK_NULL_VOID(host);
1614     auto focusHub = host->GetFocusHub();
1615     CHECK_NULL_VOID(focusHub);
1616     // textField not in List
1617     if (!focusHub->IsCurrentFocus()) {
1618         return;
1619     }
1620     auto context = PipelineContext::GetCurrentContext();
1621     CHECK_NULL_VOID(context);
1622     auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1623     CHECK_NULL_VOID(textFieldManager);
1624     // only when textField is onFocus
1625     auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1626     CHECK_NULL_VOID(textField);
1627     auto textFieldHost = textField->GetHost();
1628     CHECK_NULL_VOID(textFieldHost);
1629     auto textBase = DynamicCast<TextBase>(textField);
1630     CHECK_NULL_VOID(textBase);
1631     auto caretPos = textFieldHost->GetTransformRelativeOffset().GetY() + textBase->GetCaretRect().Bottom();
1632     auto globalOffset = host->GetTransformRelativeOffset();
1633     auto offset = contentMainSize_ + globalOffset.GetY() - caretPos - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
1634     if (LessOrEqual(offset, 0.0)) {
1635         // negative offset to scroll down
1636         currentDelta_ -= static_cast<float>(offset);
1637     }
1638 }
1639 
SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm> & itemGroup,bool forwardLayout,int32_t index)1640 void ListLayoutAlgorithm::SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm>& itemGroup,
1641     bool forwardLayout, int32_t index)
1642 {
1643     if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1644         if (!jumpIndexInGroup_.has_value()) {
1645             if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1646                 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1647                 jumpIndexInGroup_ = 0;
1648             } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1649                 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1650                 jumpIndexInGroup_ = LAST_ITEM;
1651             }
1652         }
1653 
1654         if (jumpIndexInGroup_.has_value()) {
1655             itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1656             itemGroup->SetScrollAlign(scrollAlign_);
1657             jumpIndexInGroup_.reset();
1658         }
1659     }
1660 }
1661 
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout,bool needAdjustRefPos)1662 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1663     float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout,
1664     bool needAdjustRefPos)
1665 {
1666     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1667     CHECK_NULL_VOID(layoutAlgorithmWrapper);
1668     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1669     CHECK_NULL_VOID(itemGroup);
1670     if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1671         referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1672     }
1673     if (jumpIndex_) {
1674         itemGroup->ClearItemPosition();
1675     }
1676     if (forwardLayout) {
1677         float endPos = layoutEndMainPos_.value_or(endMainPos_);
1678         float startPos = endPos - contentMainSize_;
1679         itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1680     } else {
1681         float startPos = layoutStartMainPos_.value_or(startMainPos_);
1682         float endPos = startPos + contentMainSize_;
1683         itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1684     }
1685     bool needMeasureFormLastItem = index < preStartIndex_;
1686     itemGroup->SetNeedMeasureFormLastItem(needMeasureFormLastItem);
1687     itemGroup->SetNeedAdjustRefPos(needAdjustRefPos);
1688     itemGroup->SetListLayoutProperty(layoutProperty);
1689     itemGroup->SetNeedCheckOffset(isNeedCheckOffset_);
1690     if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER) {
1691         itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
1692     }
1693     SetListItemGroupJumpIndex(itemGroup, forwardLayout, index);
1694 
1695     if (groupNeedAllLayout || (targetIndex_ && targetIndex_.value() == index) ||
1696         (scrollSnapAlign_ != V2::ScrollSnapAlign::NONE && !childrenSize_)) {
1697         itemGroup->SetNeedAllLayout();
1698     } else if (forwardFeature_ || backwardFeature_) {
1699         itemGroup->CheckNeedAllLayout(layoutWrapper, forwardLayout);
1700     }
1701     if (CheckNeedMeasure(layoutWrapper)) {
1702         itemGroup->ResetCachedItemPosition();
1703         itemGroup->ResetCachedIndex();
1704         if (layoutWrapper->GetHostNode() && layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1705             auto groupPattern = layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1706             groupPattern->SetRecache(true);
1707         }
1708     }
1709     layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1710 }
1711 
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1712 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1713 {
1714     int32_t id = layoutWrapper->GetHostNode()->GetId();
1715     ListItemInfo pos = { id, 0, 0, true };
1716     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1717     CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
1718     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1719     CHECK_NULL_RETURN(itemGroup, pos);
1720     auto res = itemGroup->GetItemGroupPosition(index);
1721     return { id, res.first, res.second, true };
1722 }
1723 
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1724 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1725 {
1726     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1727     CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
1728     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1729     CHECK_NULL_RETURN(itemGroup, 0.0f);
1730     return itemGroup->GetItemHeight(index);
1731 }
1732 
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1733 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1734 {
1735     auto host = layoutWrapper->GetHostNode();
1736     CHECK_NULL_VOID(host);
1737     auto listItem = host->GetPattern<ListItemPattern>();
1738     if (listItem) {
1739         listItem->SetIndexInList(index);
1740         return;
1741     }
1742     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1743     CHECK_NULL_VOID(listItemGroup);
1744     listItemGroup->SetIndexInList(index);
1745 }
1746 
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1747 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1748     float referencePos, bool forwardLayout) const
1749 {
1750     if (targetIndex_.has_value()) {
1751         return;
1752     }
1753     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1754     CHECK_NULL_VOID(wrapper);
1755     auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1756     CHECK_NULL_VOID(algorithmWrapper);
1757     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1758     CHECK_NULL_VOID(itemGroup);
1759     itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1760 }
1761 
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1762 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
1763     bool forwardLayout)
1764 {
1765     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1766     CHECK_NULL_VOID(wrapper);
1767     auto algorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1768     CHECK_NULL_VOID(algorithmWrapper);
1769     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1770     CHECK_NULL_VOID(itemGroup);
1771     if (forwardLayout) {
1772         itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, prevContentMainSize_,
1773             !forwardLayout);
1774     } else {
1775         itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, prevContentMainSize_,
1776             !forwardLayout);
1777     }
1778     itemGroup->SetScrollAlign(ScrollAlign::NONE);
1779     wrapper->Measure(GetGroupLayoutConstraint());
1780     if (childrenSize_) {
1781         return;
1782     }
1783     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1784     auto& pos = itemPosition_[index];
1785     if (forwardLayout) {
1786         pos.startPos = pos.endPos - mainLen;
1787     } else {
1788         pos.endPos = pos.startPos + mainLen;
1789     }
1790 }
1791 
OffScreenLayoutDirection(LayoutWrapper * layoutWrapper)1792 void ListLayoutAlgorithm::OffScreenLayoutDirection(LayoutWrapper* layoutWrapper)
1793 {
1794     if (!targetIndex_ || itemPosition_.empty()) {
1795         forwardFeature_ = false;
1796         backwardFeature_ = false;
1797         return;
1798     }
1799     auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
1800     if (layoutDirection == LayoutDirection::BACKWARD) {
1801         forwardFeature_ = false;
1802         backwardFeature_ = true;
1803     } else {
1804         forwardFeature_ = true;
1805         backwardFeature_ = false;
1806     }
1807 }
1808 
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1809 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1810 {
1811     float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1812     float midPos = contentSize / 2.0f;
1813     if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1814         GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
1815         midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
1816     } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1817         LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
1818         (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1819         midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
1820     }
1821     for (auto& pos : itemPosition_) {
1822         if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1823             return pos.first;
1824         }
1825     }
1826     return totalItemCount_ - 1;
1827 }
1828 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1829 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1830 {
1831     CHECK_NULL_VOID(wrapper);
1832     auto host = wrapper->GetHostNode();
1833     CHECK_NULL_VOID(host);
1834     host->ForceSyncGeometryNode();
1835     host->ResetLayoutAlgorithm();
1836     host->RebuildRenderContextTree();
1837 }
1838 
LayoutCachedALine(LayoutWrapper * layoutWrapper,int32_t index,bool forward,float & currPos,float crossSize)1839 bool ListLayoutAlgorithm::LayoutCachedALine(LayoutWrapper* layoutWrapper, int32_t index,
1840     bool forward, float &currPos, float crossSize)
1841 {
1842     auto wrapper = layoutWrapper->GetChildByIndex(index, true);
1843     if (!wrapper) {
1844         return true;
1845     }
1846     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1847     if (CheckNeedMeasure(wrapper)) {
1848         return !isGroup;
1849     }
1850     auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1851     int32_t id = wrapper->GetHostNode()->GetId();
1852     ListItemInfo pos;
1853     if (forward) {
1854         auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1855         pos = { id, currPos, endPos, isGroup };
1856         currPos = endPos + spaceWidth_;
1857     } else {
1858         auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1859         pos = { id, startPos, currPos, isGroup };
1860         currPos = startPos - spaceWidth_;
1861     }
1862     auto startIndex = index;
1863     LayoutItem(wrapper, index, pos, startIndex, crossSize);
1864     SyncGeometry(wrapper);
1865     wrapper->SetActive(false);
1866     return false;
1867 }
1868 
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1869 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1870 {
1871     std::list<int32_t> predictBuildList;
1872     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1873     float crossSize = GetCrossAxisSize(size, axis_);
1874 
1875     auto currIndex = itemPosition_.rbegin()->first + 1;
1876     auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1877     for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1878         int32_t index = currIndex + i;
1879         if (LayoutCachedALine(layoutWrapper, index, true, currPos, crossSize)) {
1880             predictBuildList.emplace_back(index);
1881         }
1882     }
1883 
1884     currIndex = itemPosition_.begin()->first - 1;
1885     currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1886     for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1887         int32_t index = currIndex - i;
1888         if (LayoutCachedALine(layoutWrapper, index, false, currPos, crossSize)) {
1889             predictBuildList.emplace_back(index);
1890         }
1891     }
1892     return predictBuildList;
1893 }
1894 
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1895 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1896 {
1897     CHECK_NULL_RETURN(wrapper, false);
1898     wrapper->SetActive(false);
1899     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1900     if (!isGroup) {
1901         auto frameNode = wrapper->GetHostNode();
1902         CHECK_NULL_RETURN(frameNode, false);
1903         frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1904         FrameNode::ProcessOffscreenNode(frameNode);
1905         return true;
1906     }
1907     return false;
1908 }
1909 
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1910 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1911 {
1912     CHECK_NULL_VOID(frameNode);
1913     auto pattern = frameNode->GetPattern<ListPattern>();
1914     CHECK_NULL_VOID(pattern);
1915     if (pattern->GetPredictLayoutParam()) {
1916         pattern->SetPredictLayoutParam(param);
1917         return;
1918     }
1919     pattern->SetPredictLayoutParam(param);
1920     auto context = PipelineContext::GetCurrentContext();
1921     CHECK_NULL_VOID(context);
1922     context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1923         ACE_SCOPED_TRACE("List predict");
1924         auto frameNode = weak.Upgrade();
1925         CHECK_NULL_VOID(frameNode);
1926         auto pattern = frameNode->GetPattern<ListPattern>();
1927         CHECK_NULL_VOID(pattern);
1928         if (!pattern->GetPredictLayoutParam().has_value()) {
1929             return;
1930         }
1931         bool needMarkDirty = false;
1932         auto param = pattern->GetPredictLayoutParam().value();
1933         for (auto it = param.items.begin(); it != param.items.end();) {
1934             if (GetSysTimestamp() > deadline) {
1935                 break;
1936             }
1937             auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false, true);
1938             if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
1939                 break;
1940             }
1941             needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty;
1942             it = param.items.erase(it);
1943         }
1944         if (needMarkDirty) {
1945             frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1946         }
1947         pattern->SetPredictLayoutParam(std::nullopt);
1948         if (!param.items.empty()) {
1949             ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1950             pattern->SetPredictLayoutParam(param);
1951         }
1952     });
1953 }
1954 
1955 // return current CachedCount and max CacheCount
GetLayoutGroupCachedCount(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & wrapper,int32_t forwardCache,int32_t backwardCache,int32_t index,bool outOfView)1956 CachedIndexInfo ListLayoutAlgorithm::GetLayoutGroupCachedCount(LayoutWrapper* layoutWrapper,
1957     const RefPtr<LayoutWrapper>& wrapper, int32_t forwardCache, int32_t backwardCache, int32_t index, bool outOfView)
1958 {
1959     CachedIndexInfo res;
1960     auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
1961     CHECK_NULL_RETURN(groupNode, res);
1962     auto group = groupNode->GetPattern<ListItemGroupPattern>();
1963     CHECK_NULL_RETURN(group, res);
1964     const auto& itemPos = group->GetItemPosition();
1965     bool reCache = false;
1966     if (outOfView && recycledItemPosition_.count(index) == 0) {
1967         reCache = CheckNeedMeasure(wrapper);
1968     } else if (outOfView) {
1969         wrapper->SetActive(true);
1970         wrapper->Layout();
1971         group->SyncItemsToCachedItemPosition();
1972     }
1973     bool forward = forwardCache > -1;
1974     bool backward = backwardCache > -1;
1975     if (forward && backward && itemPos.empty()) {
1976         forward = group->NeedCacheForward(layoutWrapper);
1977         backward = !forward;
1978         forwardCache = forward ? forwardCache : -1;
1979         backwardCache = backward ? backwardCache : -1;
1980     }
1981     res = group->UpdateCachedIndex(outOfView, reCache, forwardCache, backwardCache);
1982     if ((group->GetTotalItemCount() == 0 && outOfView) || !group->IsVisible()) {
1983         if (groupNode->CheckNeedForceMeasureAndLayout()) {
1984             res = {0, 0, 1, 1};
1985         } else {
1986             res = {1, 1, 1, 1};
1987         }
1988     }
1989     ACE_SCOPED_TRACE("GetLayoutGroupCachedCount forward:%d, %d, backward:%d, %d",
1990         res.forwardCachedCount, res.forwardCacheMax, res.backwardCachedCount, res.backwardCacheMax);
1991     return res;
1992 }
1993 
GetLayoutCrossAxisSize(LayoutWrapper * layoutWrapper)1994 float ListLayoutAlgorithm::GetLayoutCrossAxisSize(LayoutWrapper* layoutWrapper)
1995 {
1996     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1997     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1998     MinusPaddingToSize(padding, size);
1999     return GetCrossAxisSize(size, axis_);
2000 }
2001 
LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2002 int32_t ListLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper,
2003     int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2004 {
2005     float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2006     curIndex = itemPosition_.rbegin()->first + 1;
2007     auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
2008     while (cachedCount < cacheCount && curIndex < totalItemCount_) {
2009         auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
2010         if (!wrapper) {
2011             predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2012             return curIndex - 1;
2013         }
2014         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2015         bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2016         if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
2017             predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2018         }
2019         if (!isGroup && isDirty) {
2020             return curIndex - 1;
2021         }
2022         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2023         auto endPos = currPos + GetMainAxisSize(childSize, axis_);
2024         int32_t id = wrapper->GetHostNode()->GetId();
2025         ListItemInfo pos = { id, currPos, endPos, isGroup };
2026         currPos = endPos + spaceWidth_;
2027         auto startIndex = curIndex;
2028         LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2029         cachedItemPosition_[curIndex] = pos;
2030         if (isGroup) {
2031             auto res = GetLayoutGroupCachedCount(
2032                 layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true);
2033             if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) {
2034                 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2035                 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1;
2036             }
2037             cachedCount += std::max(res.forwardCacheMax, 1);
2038         } else {
2039             cachedCount++;
2040         }
2041         SyncGeometry(wrapper);
2042         wrapper->SetActive(false);
2043         curIndex++;
2044     }
2045     return curIndex - 1;
2046 }
2047 
LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2048 int32_t ListLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper,
2049     int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2050 {
2051     float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2052     curIndex = itemPosition_.begin()->first - 1;
2053     auto currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
2054     while (cachedCount < cacheCount && curIndex >= 0) {
2055         auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
2056         if (!wrapper) {
2057             predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2058             return curIndex + 1;
2059         }
2060         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2061         bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2062         if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
2063             predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2064         }
2065         if (!isGroup && isDirty) {
2066             return curIndex + 1;
2067         }
2068         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2069         auto startPos = currPos - GetMainAxisSize(childSize, axis_);
2070         int32_t id = wrapper->GetHostNode()->GetId();
2071         ListItemInfo pos = { id, startPos, currPos, isGroup };
2072         currPos = startPos - spaceWidth_;
2073         auto startIndex = curIndex;
2074         LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2075         cachedItemPosition_[curIndex] = pos;
2076         if (isGroup) {
2077             auto res = GetLayoutGroupCachedCount(
2078                 layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true);
2079             if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) {
2080                 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2081                 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1;
2082             }
2083             cachedCount += std::max(res.backwardCacheMax, 1);
2084         } else {
2085             cachedCount++;
2086         }
2087         SyncGeometry(wrapper);
2088         wrapper->SetActive(false);
2089         curIndex--;
2090     }
2091     return curIndex + 1;
2092 }
2093 
LayoutCachedItemInEdgeGroup(LayoutWrapper * layoutWrapper,int32_t cacheCount,std::list<PredictLayoutItem> & predictList)2094 std::tuple<int32_t, int32_t, int32_t, int32_t> ListLayoutAlgorithm::LayoutCachedItemInEdgeGroup(
2095     LayoutWrapper* layoutWrapper, int32_t cacheCount, std::list<PredictLayoutItem>& predictList)
2096 {
2097     int32_t startIndex = GetStartIndex();
2098     int32_t endIndex = GetEndIndex();
2099     int32_t cachedForward = 0;
2100     int32_t cachedBackward = 0;
2101     if (startIndex == endIndex && itemPosition_.begin()->second.isGroup) {
2102         auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2103         auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, cacheCount, startIndex, false);
2104         if ((res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) ||
2105             (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount)) {
2106             int32_t forwardCached = res.forwardCacheMax > 0 ? cachedForward : -1;
2107             int32_t backwardCached = res.backwardCacheMax > 0 ? cachedBackward : -1;
2108             predictList.emplace_back(PredictLayoutItem { startIndex, forwardCached, backwardCached });
2109         }
2110         cachedForward += res.forwardCacheMax;
2111         cachedBackward += res.backwardCacheMax;
2112     } else {
2113         if (itemPosition_.rbegin()->second.isGroup) {
2114             auto wrapper = layoutWrapper->GetChildByIndex(endIndex);
2115             auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, -1, endIndex, false);
2116             if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) {
2117                 predictList.emplace_back(PredictLayoutItem { endIndex, cachedForward, -1 });
2118             }
2119             cachedForward += res.forwardCacheMax;
2120         }
2121         if (itemPosition_.begin()->second.isGroup) {
2122             auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2123             auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount, startIndex, false);
2124             if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount) {
2125                 predictList.emplace_back(PredictLayoutItem { startIndex, -1, cachedBackward });
2126             }
2127             cachedBackward += res.backwardCacheMax;
2128         }
2129     }
2130     return { startIndex, endIndex, cachedForward, cachedBackward };
2131 }
2132 
LayoutCachedItemV2(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)2133 std::list<PredictLayoutItem> ListLayoutAlgorithm::LayoutCachedItemV2(LayoutWrapper* layoutWrapper, int32_t cacheCount,
2134     bool show)
2135 {
2136     ACE_SCOPED_TRACE("LayoutCachedItemV2");
2137     std::list<PredictLayoutItem> predictBuildList;
2138     auto [startIndex, endIndex, cachedForward, cachedBackward] =
2139         LayoutCachedItemInEdgeGroup(layoutWrapper, cacheCount, predictBuildList);
2140     if (cachedForward < cacheCount && endIndex < totalItemCount_ - 1) {
2141         endIndex = LayoutCachedForward(layoutWrapper, cacheCount, cachedForward, endIndex, predictBuildList, show);
2142     }
2143     if (cachedBackward < cacheCount && startIndex > 0) {
2144         startIndex =
2145             LayoutCachedBackward(layoutWrapper, cacheCount, cachedBackward, startIndex, predictBuildList, show);
2146     }
2147     int32_t cacheStart = itemPosition_.begin()->first - startIndex;
2148     int32_t cacheEnd = endIndex - itemPosition_.rbegin()->first;
2149     ResetLayoutItem(layoutWrapper);
2150     SetActiveChildRange(layoutWrapper, cacheStart, cacheEnd, show);
2151     return predictBuildList;
2152 }
2153 
PredictBuildGroup(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,const ListMainSizeValues & listMainSizeValues)2154 bool ListLayoutAlgorithm::PredictBuildGroup(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint,
2155     int64_t deadline, int32_t forwardCached, int32_t backwardCached, const ListMainSizeValues& listMainSizeValues)
2156 {
2157     CHECK_NULL_RETURN(wrapper, false);
2158     auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
2159     CHECK_NULL_RETURN(groupNode, false);
2160     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
2161     CHECK_NULL_RETURN(groupPattern, false);
2162     float referencePos = 0.0f;
2163     if (listMainSizeValues.jumpIndexInGroup.has_value() && listMainSizeValues.scrollAlign == ScrollAlign::CENTER) {
2164         referencePos = (listMainSizeValues.startPos + listMainSizeValues.endPos) / 2; // 2:average
2165     }
2166     float endPos = 0.0f;
2167     float startPos = 0.0f;
2168     if (listMainSizeValues.forward) {
2169         startPos = listMainSizeValues.startPos;
2170         endPos = listMainSizeValues.layoutEndMainPos.value_or(listMainSizeValues.endPos);
2171     } else {
2172         startPos = listMainSizeValues.layoutStartMainPos.value_or(listMainSizeValues.startPos);
2173         endPos = listMainSizeValues.endPos;
2174     }
2175     ListMainSizeValues values;
2176     values.startPos = startPos;
2177     values.endPos = endPos;
2178     values.referencePos = referencePos;
2179     values.prevContentMainSize = listMainSizeValues.prevContentMainSize;
2180     values.forward = listMainSizeValues.forward;
2181     values.backward = listMainSizeValues.backward;
2182     groupPattern->LayoutCache(constraint, deadline, forwardCached, backwardCached, values);
2183     return true;
2184 }
2185 
PredictBuildV2(RefPtr<FrameNode> frameNode,int64_t deadline,ListMainSizeValues listMainSizeValues,bool show)2186 void ListLayoutAlgorithm::PredictBuildV2(
2187     RefPtr<FrameNode> frameNode, int64_t deadline, ListMainSizeValues listMainSizeValues, bool show)
2188 {
2189     ACE_SCOPED_TRACE("List predict v2");
2190     CHECK_NULL_VOID(frameNode);
2191     auto pattern = frameNode->GetPattern<ListPattern>();
2192     CHECK_NULL_VOID(pattern);
2193     if (!pattern->GetPredictLayoutParamV2().has_value()) {
2194         return;
2195     }
2196     bool needMarkDirty = false;
2197     auto param = pattern->GetPredictLayoutParamV2().value();
2198     for (auto it = param.items.begin(); it != param.items.end();) {
2199         if (GetSysTimestamp() > deadline) {
2200             break;
2201         }
2202         ACE_SCOPED_TRACE("predict Item:%d", (*it).index);
2203         auto wrapper = frameNode->GetOrCreateChildByIndex((*it).index, show, true);
2204         if (!wrapper) {
2205             it = param.items.erase(it);
2206             continue;
2207         }
2208         if (wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
2209             break;
2210         }
2211         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2212         if (!isGroup) {
2213             auto frameNode = wrapper->GetHostNode();
2214             CHECK_NULL_VOID(frameNode);
2215             frameNode->GetGeometryNode()->SetParentLayoutConstraint(param.layoutConstraint);
2216             FrameNode::ProcessOffscreenNode(frameNode);
2217         } else {
2218             listMainSizeValues.forward = (*it).forwardCacheCount > -1;
2219             listMainSizeValues.backward = (*it).backwardCacheCount > -1;
2220             PredictBuildGroup(wrapper, param.groupLayoutConstraint, deadline, (*it).forwardCacheCount,
2221                 (*it).backwardCacheCount, listMainSizeValues);
2222         }
2223         needMarkDirty = true;
2224         it = param.items.erase(it);
2225     }
2226     if (needMarkDirty) {
2227         frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
2228     }
2229     pattern->SetPredictLayoutParamV2(std::nullopt);
2230     if (!param.items.empty()) {
2231         ListLayoutAlgorithm::PostIdleTaskV2(frameNode, param, listMainSizeValues, show);
2232     }
2233 }
2234 
PostIdleTaskV2(RefPtr<FrameNode> frameNode,const ListPredictLayoutParamV2 & param,ListMainSizeValues listMainSizeValues,bool show)2235 void ListLayoutAlgorithm::PostIdleTaskV2(RefPtr<FrameNode> frameNode,
2236     const ListPredictLayoutParamV2& param, ListMainSizeValues listMainSizeValues, bool show)
2237 {
2238     ACE_SCOPED_TRACE("PostIdleTaskV2");
2239     CHECK_NULL_VOID(frameNode);
2240     auto pattern = frameNode->GetPattern<ListPattern>();
2241     CHECK_NULL_VOID(pattern);
2242     if (pattern->GetPredictLayoutParamV2()) {
2243         pattern->SetPredictLayoutParamV2(param);
2244         return;
2245     }
2246     pattern->SetPredictLayoutParamV2(param);
2247     auto context = PipelineContext::GetCurrentContext();
2248     CHECK_NULL_VOID(context);
2249     context->AddPredictTask(
2250         [weak = WeakClaim(RawPtr(frameNode)), value = listMainSizeValues, show = show](int64_t deadline,
2251         bool canUseLongPredictTask) { ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, value, show); });
2252 }
2253 
GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const2254 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const
2255 {
2256     float stopOnScreen = 0;
2257     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2258         stopOnScreen = contentStartOffset_;
2259     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2260         stopOnScreen = contentMainSize_ / 2.0f;
2261     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2262         stopOnScreen = contentMainSize_ - contentEndOffset_;
2263     }
2264     return stopOnScreen;
2265 }
2266 
FindPredictSnapIndexInItemPositionsStart(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2267 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsStart(
2268     float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2269 {
2270     float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::START);
2271     float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
2272     for (const auto& positionInfo : itemPosition_) {
2273         auto startPos = positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
2274         itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2275         auto endPos = positionInfo.second.startPos + itemHeight / 2.0f;
2276         if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2277             LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2278             endIndex = positionInfo.first;
2279         }
2280         if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2281             currIndex = positionInfo.first;
2282         }
2283         if (endIndex >= 0 && currIndex >= 0) {
2284             break;
2285         }
2286     }
2287 }
2288 
FindPredictSnapIndexInItemPositionsCenter(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2289 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsCenter(
2290     float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2291 {
2292     float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::CENTER);
2293     for (const auto& positionInfo : itemPosition_) {
2294         auto startPos = positionInfo.second.startPos - spaceWidth_ / 2.0f;
2295         auto endPos = positionInfo.second.endPos + spaceWidth_ / 2.0f;
2296         if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2297             LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2298             endIndex = positionInfo.first;
2299         }
2300         if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2301             currIndex = positionInfo.first;
2302         }
2303         if (endIndex >= 0 && currIndex >= 0) {
2304             break;
2305         }
2306     }
2307 }
2308 
FindPredictSnapIndexInItemPositionsEnd(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2309 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsEnd(
2310     float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2311 {
2312     float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::END);
2313     float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
2314     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2315         auto endPos = pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
2316         itemHeight = pos->second.endPos - pos->second.startPos;
2317         auto startPos = pos->second.endPos - itemHeight / 2.0f;
2318         if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2319             LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2320             endIndex = pos->first;
2321         }
2322         if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2323             currIndex = pos->first;
2324         }
2325         if (endIndex >= 0 && currIndex >= 0) {
2326             break;
2327         }
2328     }
2329 }
2330 
FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)2331 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
2332     float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign)
2333 {
2334     int32_t endIndex = -1;
2335     int32_t currIndex = -1;
2336 
2337     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2338         FindPredictSnapIndexInItemPositionsStart(predictEndPos, endIndex, currIndex);
2339     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2340         FindPredictSnapIndexInItemPositionsCenter(predictEndPos, endIndex, currIndex);
2341     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2342         FindPredictSnapIndexInItemPositionsEnd(predictEndPos, endIndex, currIndex);
2343     }
2344     if (endIndex == currIndex && currIndex >= 0) {
2345         if (scrollSnapVelocity_ < -SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2346             endIndex = std::min(GetEndIndex(), endIndex + 1);
2347         } else if (scrollSnapVelocity_ > SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2348             endIndex = std::min(GetStartIndex(), endIndex - 1);
2349         }
2350     }
2351     return endIndex;
2352 }
2353 
IsUniformHeightProbably()2354 bool ListLayoutAlgorithm::IsUniformHeightProbably()
2355 {
2356     bool isUniformHeightProbably = true;
2357     float itemHeight = 0.0f;
2358     float currentItemHeight = 0.0f;
2359     for (const auto& positionInfo : itemPosition_) {
2360         currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2361         if (NearZero(itemHeight)) {
2362             itemHeight = currentItemHeight;
2363         } else if (!NearEqual(currentItemHeight, itemHeight)) {
2364             isUniformHeightProbably = false;
2365             break;
2366         }
2367     }
2368     return isUniformHeightProbably;
2369 }
2370 
CalculatePredictSnapEndPositionByIndex(int32_t index,V2::ScrollSnapAlign scrollSnapAlign)2371 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(int32_t index, V2::ScrollSnapAlign scrollSnapAlign)
2372 {
2373     float predictSnapEndPos = 0;
2374     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2375         predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
2376         float endPos = GetEndPosition();
2377         float itemTotalSize = endPos - GetStartPosition();
2378         float contentSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
2379         if ((GetEndIndex() == totalItemCount_ - 1) && GreatNotEqual(itemTotalSize, contentSize) &&
2380             GreatNotEqual(predictSnapEndPos + contentMainSize_ - contentEndOffset_, totalOffset_ + endPos)) {
2381             predictSnapEndPos = totalOffset_ + endPos - contentMainSize_ + contentEndOffset_;
2382         }
2383     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2384         float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
2385         predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
2386     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2387         predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
2388         if (GetStartIndex() == 0 && LessNotEqual(predictSnapEndPos, totalOffset_ + GetStartPosition())) {
2389             predictSnapEndPos = totalOffset_ + GetStartPosition() - contentStartOffset_;
2390         }
2391     }
2392     return predictSnapEndPos;
2393 }
2394 
GetSnapStartIndexAndPos()2395 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapStartIndexAndPos()
2396 {
2397     int32_t startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
2398     float startPos = itemPosition_.begin()->second.startPos;
2399     for (auto& pos : itemPosition_) {
2400         if (NearEqual(pos.second.startPos, prevContentStartOffset_)) {
2401             startIndex = pos.first;
2402             startPos = itemPosition_[startIndex].startPos + contentStartOffset_ - prevContentStartOffset_;
2403             break;
2404         } else if (GreatNotEqual(pos.second.startPos, prevContentStartOffset_)) {
2405             if ((GetEndIndex() == totalItemCount_ - 1) &&
2406                 NearEqual(GetEndPosition(), prevContentMainSize_ - prevContentEndOffset_) && !canOverScroll_) {
2407                 startIndex = pos.first;
2408                 startPos = contentStartOffset_;
2409                 adjustOffset_ = pos.second.startPos - prevContentStartOffset_;
2410             }
2411             break;
2412         }
2413     }
2414     return std::make_pair(std::min(startIndex, totalItemCount_ -1), startPos);
2415 }
2416 
GetSnapEndIndexAndPos()2417 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapEndIndexAndPos()
2418 {
2419     int32_t endIndex = -1;
2420     float endPos = 0.0f;
2421     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2422         if (NearEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2423             endIndex = pos->first;
2424             endPos = itemPosition_[endIndex].endPos - contentEndOffset_ + prevContentEndOffset_;
2425             break;
2426         } else if (GreatNotEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2427             if ((GetStartIndex() == 0) && NearEqual(GetStartPosition(), prevContentStartOffset_) && !canOverScroll_) {
2428                 endIndex = pos->first;
2429                 endPos = prevContentMainSize_ - contentEndOffset_;
2430                 adjustOffset_ = pos->second.endPos + prevContentEndOffset_ - prevContentMainSize_;
2431             }
2432             break;
2433         }
2434     }
2435     return std::make_pair(std::min(endIndex, totalItemCount_ -1), endPos);
2436 }
2437 
UpdateDefaultCachedCount(const int32_t oldCacheCount,const int32_t itemCount)2438 int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount)
2439 {
2440     if (itemCount <= 0) {
2441         return oldCacheCount;
2442     }
2443     static float pageCount = SystemProperties::GetPageCount();
2444     if (pageCount <= 0.0f) {
2445         return oldCacheCount;
2446     }
2447     constexpr int32_t MAX_DEFAULT_CACHED_COUNT = 16;
2448     int32_t newCachedCount = static_cast<int32_t>(ceil(pageCount * itemCount));
2449     if (newCachedCount > MAX_DEFAULT_CACHED_COUNT) {
2450         TAG_LOGI(AceLogTag::ACE_LIST, "Default cachedCount exceed 16");
2451         return MAX_DEFAULT_CACHED_COUNT;
2452     } else {
2453         return std::max(newCachedCount, oldCacheCount);
2454     }
2455 }
2456 } // namespace OHOS::Ace::NG
2457