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