• 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 <unordered_set>
20 
21 #include "base/geometry/axis.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/log/ace_trace.h"
25 #include "base/memory/ace_type.h"
26 #include "base/utils/time_util.h"
27 #include "base/utils/utils.h"
28 #include "core/components/common/layout/layout_param.h"
29 #include "core/components_ng/base/frame_node.h"
30 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
32 #include "core/components_ng/pattern/list/list_item_pattern.h"
33 #include "core/components_ng/pattern/list/list_layout_property.h"
34 #include "core/components_ng/pattern/list/list_pattern.h"
35 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
36 #include "core/components_ng/pattern/text_field/text_field_manager.h"
37 #include "core/components_ng/property/layout_constraint.h"
38 #include "core/components_ng/property/measure_property.h"
39 #include "core/components_ng/property/measure_utils.h"
40 #include "core/components_ng/property/property.h"
41 #include "core/components_v2/inspector/inspector_constants.h"
42 #include "core/components_v2/list/list_properties.h"
43 #include "core/pipeline_ng/pipeline_context.h"
44 
45 namespace OHOS::Ace::NG {
46 
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)47 void ListLayoutAlgorithm::UpdateListItemConstraint(
48     Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
49 {
50     contentConstraint.parentIdealSize = selfIdealSize;
51     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
52     auto crossSize = selfIdealSize.CrossSize(axis);
53     if (crossSize.has_value()) {
54         contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
55         contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
56     }
57 }
58 
Measure(LayoutWrapper * layoutWrapper)59 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
60 {
61     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
62     CHECK_NULL_VOID(listLayoutProperty);
63 
64     const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value();
65 
66     // calculate idealSize and set FrameSize
67     axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
68     auto startOffset = listLayoutProperty->GetContentStartOffset().value_or(0.0f);
69     contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0);
70     auto endOffset = listLayoutProperty->GetContentEndOffset().value_or(0.0f);
71     contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0);
72 
73     // calculate main size.
74     auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value();
75 
76     float expandHeight = ScrollableUtils::CheckHeightExpansion(listLayoutProperty, axis_);
77     contentEndOffset_ += expandHeight;
78     // expand contentSize
79     contentConstraint.MinusPadding(std::nullopt, std::nullopt, std::nullopt, -expandHeight);
80 
81     auto contentIdealSize = CreateIdealSize(
82         contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
83 
84     const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
85     paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
86     paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
87     contentMainSize_ = 0.0f;
88     totalItemCount_ = layoutWrapper->GetTotalChildCount();
89     if (!GetMainAxisSize(contentIdealSize, axis_)) {
90         if (totalItemCount_ == 0) {
91             contentMainSize_ = 0.0f;
92         } else {
93             // use parent max size first.
94             auto parentMaxSize = contentConstraint.maxSize;
95             contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_);
96             mainSizeIsDefined_ = false;
97         }
98     } else {
99         contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
100         mainSizeIsDefined_ = true;
101     }
102     if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, contentMainSize_)) {
103         contentStartOffset_ = 0;
104         contentEndOffset_ = 0;
105     }
106 
107     if (totalItemCount_ > 0) {
108         OnSurfaceChanged(layoutWrapper);
109 
110         stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
111         childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
112         auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
113         auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
114         spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
115         if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) {
116             spaceWidth_ = 0.0f;
117         }
118         if (listLayoutProperty->GetDivider().has_value()) {
119             auto divider = listLayoutProperty->GetDivider().value();
120             std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
121             if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
122                 dividerSpace.reset();
123             }
124             if (dividerSpace.has_value()) {
125                 spaceWidth_ = std::max(spaceWidth_, static_cast<float>(Round(dividerSpace.value())));
126             }
127         }
128         spaceWidth_ += chainInterval_;
129         CheckJumpToIndex();
130         currentOffset_ = currentDelta_;
131         startMainPos_ = currentOffset_;
132         endMainPos_ = currentOffset_ + contentMainSize_;
133         CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
134         listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
135         // calculate child layout constraint.
136         UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
137         MeasureList(layoutWrapper);
138     } else {
139         itemPosition_.clear();
140         layoutWrapper->RemoveAllChildInRenderTree();
141     }
142 
143     auto crossSize = contentIdealSize.CrossSize(axis_);
144     if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
145         contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
146         crossMatchChild_ = true;
147     }
148     contentIdealSize.SetMainSize(contentMainSize_, axis_);
149     AddPaddingToSize(padding, contentIdealSize);
150 
151     auto size = contentIdealSize.ConvertToSizeT();
152     // Cancel frame size expansion, only expand content size here.
153     // Frame expansion will be determined after Layout.
154     size.MinusHeight(expandHeight);
155     layoutWrapper->GetGeometryNode()->SetFrameSize(size);
156 
157     // set list cache info.
158     SetCacheCount(layoutWrapper, listLayoutProperty->GetCachedCountValue(1));
159 }
160 
SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)161 void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount)
162 {
163     layoutWrapper->SetCacheCount(cacheCount);
164 }
165 
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const166 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
167 {
168     if (GetItemPosition().empty()) {
169         return 0.0f;
170     }
171     float maxCrossSize = 0.0f;
172     float crossSize = 0.0f;
173     float prevPos = GetItemPosition().begin()->second.startPos;
174     for (const auto& pos : GetItemPosition()) {
175         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
176         if (!wrapper) {
177             continue;
178         }
179         auto getGeometryNode = wrapper->GetGeometryNode();
180         if (!getGeometryNode) {
181             continue;
182         }
183         if (NearEqual(prevPos, pos.second.startPos)) {
184             crossSize += getGeometryNode->GetMarginFrameSize().CrossSize(axis);
185         } else {
186             crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
187         }
188         prevPos = pos.second.startPos;
189         maxCrossSize = std::max(crossSize, maxCrossSize);
190     }
191     return maxCrossSize;
192 }
193 
ClearAllItemPosition(LayoutWrapper * layoutWrapper)194 void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper)
195 {
196     for (auto& pos : itemPosition_) {
197         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
198         if (!wrapper) {
199             continue;
200         }
201         auto algorithm = wrapper->GetLayoutAlgorithm(true);
202         if (!algorithm) {
203             continue;
204         }
205         auto groupAlgorithm = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithm->GetLayoutAlgorithm());
206         if (!groupAlgorithm) {
207             continue;
208         }
209         CHECK_NULL_VOID(groupAlgorithm);
210         groupAlgorithm->ClearItemPosition(&(*wrapper));
211     }
212     itemPosition_.clear();
213     layoutWrapper->RemoveAllChildInRenderTree();
214 }
215 
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)216 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
217 {
218     int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
219     LayoutForward(layoutWrapper, index, startPos);
220     if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
221         LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
222         if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
223             LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
224         }
225     }
226 }
227 
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)228 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
229 {
230     int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value());
231     LayoutBackward(layoutWrapper, index, startPos);
232     if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
233         LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
234         if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
235             LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
236         }
237     }
238 }
239 
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t & startIndex,int32_t & endIndex,float & startPos,float & endPos)240 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper,
241     int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos)
242 {
243     bool isSmoothJump = false;
244     int32_t jumpIndex = 0;
245     if (jumpIndex_.has_value()) {
246         jumpIndex = jumpIndex_.value();
247     } else {
248         jumpIndex = targetIndex_.value();
249         isSmoothJump = true;
250     }
251     int32_t tempStartIndex = startIndex;
252     int32_t tempEndIndex = endIndex;
253     if (GreatNotEqual(GetLanes(), 1)) {
254         jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
255         tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
256         tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
257     }
258     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
259     CHECK_NULL_VOID(wrapper);
260     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
261     if (!isGroup) {
262         if (jumpIndex >= tempEndIndex) {
263             scrollAutoType_ = ScrollAutoType::END;
264             if (!isSmoothJump) {
265                 jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value());
266                 startPos = contentMainSize_ - contentEndOffset_;
267                 BeginLayoutBackward(startPos, layoutWrapper);
268             }
269         } else if (jumpIndex <= tempStartIndex) {
270             scrollAutoType_ = ScrollAutoType::START;
271             if (!isSmoothJump) {
272                 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
273                 startPos = contentStartOffset_;
274                 BeginLayoutForward(startPos, layoutWrapper);
275             }
276         }
277     } else if (jumpIndexInGroup_) {
278         if (scrollAutoType_ == ScrollAutoType::START) {
279             scrollAlign_ = ScrollAlign::START;
280             HandleJumpStart(layoutWrapper);
281         } else if (scrollAutoType_ == ScrollAutoType::END) {
282             scrollAlign_ = ScrollAlign::END;
283             HandleJumpEnd(layoutWrapper);
284         }
285     } else if (jumpIndex <= tempStartIndex) {
286         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
287         SetListItemGroupParam(wrapper, jumpIndex, contentMainSize_, false, listLayoutProperty, false);
288         wrapper->Measure(childLayoutConstraint_);
289         float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
290         if (GreatNotEqual(contentMainSize_, mainLen)) {
291             scrollAutoType_ = ScrollAutoType::START;
292             if (!isSmoothJump) {
293                 startPos = contentStartOffset_;
294                 BeginLayoutForward(startPos, layoutWrapper);
295             }
296         } else {
297             scrollAutoType_ = ScrollAutoType::END;
298             if (!isSmoothJump) {
299                 startPos = contentMainSize_ - contentEndOffset_;
300                 BeginLayoutBackward(startPos, layoutWrapper);
301             }
302         }
303     } else if (jumpIndex >= tempEndIndex) {
304         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
305         SetListItemGroupParam(wrapper, jumpIndex, 0.0f, false, listLayoutProperty, false);
306         wrapper->Measure(childLayoutConstraint_);
307         float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
308         if (GreatOrEqual(mainLen, contentMainSize_)) {
309             scrollAutoType_ = ScrollAutoType::START;
310             if (!isSmoothJump) {
311                 startPos = contentStartOffset_;
312                 BeginLayoutForward(startPos, layoutWrapper);
313             }
314         } else {
315             scrollAutoType_ = ScrollAutoType::END;
316             if (!isSmoothJump) {
317                 startPos = contentMainSize_ - contentEndOffset_;
318                 BeginLayoutBackward(startPos, layoutWrapper);
319             }
320         }
321     }
322 }
323 
HandleJumpCenter(LayoutWrapper * layoutWrapper)324 void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper)
325 {
326     int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
327     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
328     bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
329     if (isGroup && jumpIndexInGroup_.has_value()) {
330         int32_t indexInGroup = jumpIndexInGroup_.value();
331         auto listLayoutProperty =
332             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
333         SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
334         wrapper->Measure(GetGroupLayoutConstraint());
335         itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
336         if (LessNotEqual(GetEndPosition(), endMainPos_)) {
337             LayoutForward(layoutWrapper, index + 1, GetEndPosition());
338         }
339     } else {
340         float mainLen = MeasureAndGetChildHeight(layoutWrapper, index);
341         float startPos = (contentMainSize_ - mainLen) / 2.0f;
342         if (LessNotEqual(startPos, endMainPos_)) {
343             LayoutForward(layoutWrapper, index, startPos);
344         }
345     }
346     if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
347         LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
348     }
349     if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_ - contentEndOffset_)) {
350         LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
351     }
352 }
353 
HandleJumpStart(LayoutWrapper * layoutWrapper)354 void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper)
355 {
356     int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
357     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
358     bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
359     if (isGroup && jumpIndexInGroup_.has_value()) {
360         int32_t indexInGroup = jumpIndexInGroup_.value();
361         auto listLayoutProperty =
362             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
363         SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
364         wrapper->Measure(GetGroupLayoutConstraint());
365         itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
366         if (LessNotEqual(GetEndPosition(), endMainPos_)) {
367             LayoutForward(layoutWrapper, index + 1, GetEndPosition());
368         }
369         if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
370             LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
371         }
372     } else {
373         BeginLayoutForward(contentStartOffset_, layoutWrapper);
374     }
375 }
376 
HandleJumpEnd(LayoutWrapper * layoutWrapper)377 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
378 {
379     int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value());
380     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
381     bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
382     if (isGroup && jumpIndexInGroup_.has_value()) {
383         int32_t indexInGroup = jumpIndexInGroup_.value();
384         auto listLayoutProperty =
385             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
386         SetListItemGroupParam(wrapper, index, contentMainSize_, true, listLayoutProperty, false);
387         wrapper->Measure(GetGroupLayoutConstraint());
388         itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
389         if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
390             LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
391         }
392         if (GetEndIndex() <= totalItemCount_ -1 && LessNotEqual(GetEndPosition(), endMainPos_)) {
393             LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
394         }
395     } else {
396         BeginLayoutBackward(contentMainSize_ - contentEndOffset_, layoutWrapper);
397     }
398 }
399 
CheckNoNeedJumpListItem(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex)400 bool ListLayoutAlgorithm::CheckNoNeedJumpListItem(LayoutWrapper* layoutWrapper,
401     float startPos, float endPos, int32_t startIndex, int32_t endIndex, int32_t jumpIndex)
402 {
403     int32_t tempJumpIndex = jumpIndex;
404     int32_t tempStartIndex = startIndex;
405     int32_t tempEndIndex = endIndex;
406     if (GreatNotEqual(GetLanes(), 1)) {
407         tempJumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
408         tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
409         tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
410     }
411     if (tempJumpIndex > tempStartIndex && tempJumpIndex < tempEndIndex) {
412         return true;
413     }
414     if (tempJumpIndex == tempStartIndex && tempJumpIndex == tempEndIndex) {
415         return true;
416     }
417     if ((tempJumpIndex == tempStartIndex) && GreatOrEqual(startPos, contentStartOffset_)) {
418         return true;
419     }
420     if ((tempJumpIndex == tempEndIndex) && LessOrEqual(endPos, contentMainSize_ - contentEndOffset_)) {
421         return true;
422     }
423     return false;
424 }
425 
CheckNoNeedJumpListItemGroup(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)426 bool ListLayoutAlgorithm::CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper,
427     int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
428 {
429     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
430     CHECK_NULL_RETURN(wrapper, true);
431     if (wrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
432         return true;
433     }
434     int32_t jumpIndexInGroup = 0;
435     if (jumpIndexInGroup_.has_value()) {
436         jumpIndexInGroup = jumpIndexInGroup_.value();
437     } else {
438         return false;
439     }
440 
441     auto layoutAlgorithm = wrapper->GetLayoutAlgorithm();
442     CHECK_NULL_RETURN(layoutAlgorithm, true);
443     auto groupLayoutAlgorithm =
444         AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithm->GetLayoutAlgorithm());
445     CHECK_NULL_RETURN(groupLayoutAlgorithm, true);
446     auto groupItemPosition = groupLayoutAlgorithm->GetItemPosition();
447     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
448     CHECK_NULL_RETURN(listLayoutProperty, false);
449 
450     if (jumpIndex >= startIndex && jumpIndex <= endIndex) {
451         auto it = groupItemPosition.find(jumpIndexInGroup);
452         if (it != groupItemPosition.end()) {
453             auto topPos = jumpIndexStartPos + it->second.first - contentStartOffset_;
454             auto bottomPos = jumpIndexStartPos + it->second.second + contentEndOffset_;
455             if (JudgeInOfScreenScrollAutoType(wrapper, listLayoutProperty, topPos, bottomPos)) {
456                 return true;
457             }
458         } else if (groupItemPosition.size() > 0) {
459             JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndexInGroup,
460                 groupItemPosition.begin()->first, groupItemPosition.rbegin()->first);
461         } else {
462             scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
463             return true;
464         }
465     } else  {
466         JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndex,
467             startIndex, endIndex);
468     }
469     return false;
470 }
471 
JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<ListLayoutProperty> & layoutProperty,float topPos,float bottomPos)472 bool ListLayoutAlgorithm::JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper,
473     const RefPtr<ListLayoutProperty>& layoutProperty, float topPos, float bottomPos)
474 {
475     auto stickyStyle = layoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
476 
477     auto groupNode = layoutWrapper->GetHostNode();
478     CHECK_NULL_RETURN(groupNode, true);
479     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
480     CHECK_NULL_RETURN(groupPattern, true);
481 
482     float headerMainSize = 0.0f;
483     float footerMainSize = 0.0f;
484     if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::HEADER) {
485         headerMainSize = groupPattern->GetHeaderMainSize();
486     }
487     if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::FOOTER) {
488         footerMainSize = groupPattern->GetFooterMainSize();
489     }
490 
491     if (GreatOrEqual(topPos, startMainPos_ + headerMainSize) &&
492         LessOrEqual(bottomPos, endMainPos_ - footerMainSize)) {
493         scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
494         return true;
495     } else if (NearEqual(topPos, startMainPos_ + headerMainSize) ||
496         NearEqual(bottomPos, endMainPos_ - footerMainSize)) {
497         scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
498         return true;
499     } else if (GreatOrEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
500         scrollAutoType_ = ScrollAutoType::END;
501     } else if (LessNotEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
502         scrollAutoType_ = ScrollAutoType::START;
503     }
504 
505     return false;
506 }
507 
JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,const RefPtr<ListLayoutProperty> & layoutProperty,int32_t indexInGroup,int32_t judgeIndex,int32_t startIndex,int32_t endIndex)508 void ListLayoutAlgorithm::JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
509     const RefPtr<ListLayoutProperty>& layoutProperty, int32_t indexInGroup, int32_t judgeIndex,
510     int32_t startIndex, int32_t endIndex)
511 {
512     SetListItemGroupParam(layoutWrapper, index, 0.0f, true, layoutProperty, false);
513     layoutWrapper->Measure(childLayoutConstraint_);
514     auto jumpItemHeight = GetListGroupItemHeight(layoutWrapper, indexInGroup);
515     jumpIndexInGroup_ = indexInGroup;
516 
517     if (judgeIndex < startIndex) {
518         if (jumpItemHeight > contentMainSize_) {
519             scrollAutoType_ = ScrollAutoType::END;
520         } else {
521             scrollAutoType_ = ScrollAutoType::START;
522         }
523     } else if (judgeIndex > endIndex) {
524         if (jumpItemHeight > contentMainSize_) {
525             scrollAutoType_ = ScrollAutoType::START;
526         } else {
527             scrollAutoType_ = ScrollAutoType::END;
528         }
529     }
530 }
531 
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)532 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
533     int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
534 {
535     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
536     CHECK_NULL_RETURN(wrapper, true);
537     if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG && jumpIndexInGroup_.has_value()) {
538         if (CheckNoNeedJumpListItemGroup(layoutWrapper, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
539             return true;
540         }
541     } else {
542         if (CheckNoNeedJumpListItem(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex)) {
543             return true;
544         }
545     }
546     return false;
547 }
548 
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)549 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
550 {
551     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
552     CHECK_NULL_RETURN(wrapper, 0.0f);
553     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
554     if (isGroup) {
555         auto listLayoutProperty =
556             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
557         // true: layout forward, true: layout all group items.
558         SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, true);
559     }
560     wrapper->Measure(childLayoutConstraint_);
561     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
562     return mainLen;
563 }
564 
CheckJumpToIndex()565 void ListLayoutAlgorithm::CheckJumpToIndex()
566 {
567     if (jumpIndex_.has_value() || !isNeedCheckOffset_) {
568         return;
569     }
570     if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
571         return;
572     }
573     for (const auto& pos : itemPosition_) {
574         if (pos.second.isGroup) {
575             return;
576         }
577     }
578     float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
579     float averageHeight = totalHeight / itemPosition_.size();
580     int32_t targetIndex = itemPosition_.begin()->first;
581     currentDelta_ -= itemPosition_.begin()->second.startPos;
582     if (NonNegative(currentDelta_)) {
583         int32_t items = currentDelta_ / averageHeight;
584         targetIndex += items;
585         currentDelta_ -= items * averageHeight;
586     } else {
587         int32_t items = -currentDelta_ / averageHeight;
588         targetIndex -= items;
589         currentDelta_ += items * averageHeight;
590         if (targetIndex <= 0) {
591             currentDelta_ = 0;
592         }
593     }
594     jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
595 }
596 
UpdateSnapCenterContentOffset(LayoutWrapper * layoutWrapper)597 void ListLayoutAlgorithm::UpdateSnapCenterContentOffset(LayoutWrapper* layoutWrapper)
598 {
599     if (IsScrollSnapAlignCenter(layoutWrapper) && !itemPosition_.empty()) {
600         float itemHeight = 0.0f;
601         if (GetStartIndex() == 0) {
602             itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
603             contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
604         }
605         if (GetEndIndex() == totalItemCount_ - 1) {
606             itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
607             contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
608         }
609     }
610 }
611 
CheckJumpValid(LayoutWrapper * layoutWrapper)612 bool ListLayoutAlgorithm::CheckJumpValid(LayoutWrapper* layoutWrapper)
613 {
614     if (jumpIndex_.value() == LAST_ITEM) {
615         jumpIndex_ = totalItemCount_ - 1;
616     } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
617         return false;
618     }
619     if (jumpIndex_ && jumpIndexInGroup_) {
620         auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
621         CHECK_NULL_RETURN(groupWrapper, false);
622         if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
623             return false;
624         }
625         auto groupNode = groupWrapper->GetHostNode();
626         CHECK_NULL_RETURN(groupNode, false);
627         auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
628         CHECK_NULL_RETURN(groupPattern, false);
629 
630         auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
631 
632         if (jumpIndexInGroup_.value() == LAST_ITEM) {
633             jumpIndexInGroup_ = groupItemCount - 1;
634         } else if ((jumpIndexInGroup_.value() < 0) || (jumpIndexInGroup_.value() >= groupItemCount)) {
635             return false;
636         }
637     }
638     return true;
639 }
640 
MeasureList(LayoutWrapper * layoutWrapper)641 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
642 {
643     int32_t startIndex = 0;
644     int32_t endIndex = 0;
645     int32_t midIndex = 0;
646     float midItemMidPos = contentMainSize_ / 2.0f;
647     float startPos = 0.0f;
648     float endPos = 0.0f;
649     float itemTotalSize = 0.0f;
650     float jumpIndexStartPos = 0.0f;
651     int32_t jumpIndex = 0;
652 
653     if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) {
654         if (jumpIndex_.has_value()) {
655             jumpIndex = jumpIndex_.value();
656         }
657         auto it = itemPosition_.find(jumpIndex);
658         if (it != itemPosition_.end()) {
659             jumpIndexStartPos = it->second.startPos;
660         }
661     }
662 
663     if (jumpIndex_) {
664         if (!CheckJumpValid(layoutWrapper)) {
665             jumpIndex_.reset();
666             jumpIndexInGroup_.reset();
667         } else {
668             if (jumpIndex_ && scrollAlign_ == ScrollAlign::CENTER) {
669                 ClearAllItemPosition(layoutWrapper);
670             }
671         }
672     }
673     if (targetIndex_) {
674         if (targetIndex_.value() == LAST_ITEM) {
675             targetIndex_ = totalItemCount_ - 1;
676         } else if ((targetIndex_.value() < 0) || (targetIndex_.value() >= totalItemCount_)) {
677             targetIndex_.reset();
678         }
679         targetIndexStaged_ = targetIndex_;
680     }
681     if (!itemPosition_.empty()) {
682         startPos = itemPosition_.begin()->second.startPos;
683         endPos = itemPosition_.rbegin()->second.endPos;
684         itemTotalSize = endPos - startPos;
685         startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
686         endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
687         if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
688             jumpIndex_ = totalItemCount_ - 1;
689             scrollAlign_ = ScrollAlign::END;
690         }
691         if (overScrollFeature_) {
692             UpdateSnapCenterContentOffset(layoutWrapper);
693         }
694         if (IsScrollSnapAlignCenter(layoutWrapper)) {
695             midIndex = GetMidIndex(layoutWrapper, true);
696             midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
697                 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
698             midIndex = std::min(midIndex, totalItemCount_ - 1);
699         }
700         OffScreenLayoutDirection();
701         itemPosition_.clear();
702         layoutWrapper->RemoveAllChildInRenderTree();
703     }
704     if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO &&
705         NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
706         jumpIndex_.reset();
707         jumpIndexInGroup_.reset();
708     }
709     if (jumpIndex_) {
710         switch (scrollAlign_) {
711             case ScrollAlign::START:
712             case ScrollAlign::NONE:
713                 HandleJumpStart(layoutWrapper);
714                 break;
715             case ScrollAlign::CENTER:
716                 HandleJumpCenter(layoutWrapper);
717                 break;
718             case ScrollAlign::END:
719                 HandleJumpEnd(layoutWrapper);
720                 break;
721             case ScrollAlign::AUTO:
722                 HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
723                 break;
724         }
725         needEstimateOffset_ = true;
726     } else if (targetIndex_.has_value()) {
727         if (LessOrEqual(startIndex, targetIndex_.value())) {
728             LayoutForward(layoutWrapper, startIndex, startPos);
729             if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
730                 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
731             }
732         } else if (GreatNotEqual(startIndex, targetIndex_.value())) {
733             LayoutBackward(layoutWrapper, endIndex, endPos);
734             if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
735                 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
736             }
737         }
738     } else {
739         jumpIndexInGroup_.reset();
740         bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos, startMainPos_ + contentStartOffset_);
741         float midItemHeight = 0.0f;
742         if (IsScrollSnapAlignCenter(layoutWrapper)) {
743             midItemHeight = MeasureAndGetChildHeight(layoutWrapper, midIndex);
744         }
745         if (NearZero(currentOffset_) || (!overScrollFeature_ && NonNegative(currentOffset_)) ||
746             (overScrollFeature_ && overScrollTop) ||
747             LessOrEqual(itemTotalSize, contentMainSize_ - contentStartOffset_ - contentEndOffset_)) {
748             if (overScrollTop && !canOverScroll_) {
749                 startPos = startMainPos_ + contentStartOffset_;
750             }
751             if (IsScrollSnapAlignCenter(layoutWrapper)) {
752                 midIndex = GetLanesFloor(layoutWrapper, midIndex);
753                 LayoutForward(layoutWrapper, midIndex, midItemMidPos - midItemHeight / 2.0f);
754             } else {
755                 startIndex = GetLanesFloor(layoutWrapper, startIndex);
756                 LayoutForward(layoutWrapper, startIndex, startPos);
757             }
758             if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
759                 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
760             }
761         } else {
762             if (overScrollFeature_ && !overScrollTop && !NearZero(prevContentMainSize_)) {
763                 endPos += contentMainSize_ - prevContentMainSize_;
764             }
765             if (IsScrollSnapAlignCenter(layoutWrapper)) {
766                 endIndex = midIndex;
767                 endPos = midItemMidPos + midItemHeight / 2.0f;
768             }
769             endIndex = GetLanesCeil(layoutWrapper, endIndex);
770             LayoutBackward(layoutWrapper, endIndex, endPos);
771             if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
772                 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
773             }
774         }
775     }
776 }
777 
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)778 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
779     int32_t& currentIndex, float startPos, float& endPos)
780 {
781     if (currentIndex + 1 >= totalItemCount_) {
782         return 0;
783     }
784     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
785     CHECK_NULL_RETURN(wrapper, 0);
786     int32_t id = wrapper->GetHostNode()->GetId();
787     ++currentIndex;
788     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
789     if (isGroup) {
790         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
791         SetListItemGroupParam(wrapper, currentIndex, startPos, true, listLayoutProperty, false);
792     }
793     {
794         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
795         wrapper->Measure(childLayoutConstraint_);
796     }
797     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
798     endPos = startPos + mainLen;
799     itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
800     OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
801     return 1;
802 }
803 
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)804 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
805     int32_t& currentIndex, float endPos, float& startPos)
806 {
807     if (currentIndex - 1 < 0) {
808         return 0;
809     }
810     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
811     CHECK_NULL_RETURN(wrapper, 0);
812     int32_t id = wrapper->GetHostNode()->GetId();
813     --currentIndex;
814     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
815     if (isGroup) {
816         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
817         SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
818     }
819     {
820         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
821         wrapper->Measure(childLayoutConstraint_);
822     }
823     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
824     startPos = endPos - mainLen;
825     itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
826     OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
827     return 1;
828 }
829 
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)830 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
831 {
832     float currentEndPos = startPos;
833     float currentStartPos = 0.0f;
834     float endMainPos = overScrollFeature_ ?
835         std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
836     if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
837         endMainPos = Infinity<float>();
838     }
839 
840     auto currentIndex = startIndex - 1;
841     auto chainOffset = 0.0f;
842     do {
843         currentStartPos = currentEndPos;
844         int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
845         if (count == 0) {
846             break;
847         }
848         if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
849             currentEndPos += spaceWidth_;
850         }
851         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
852         // reach the valid target index
853         if (forwardFeature_ && targetIndex_ && GreatNotEqual(currentIndex, targetIndex_.value())) {
854             endMainPos = GetEndPosition() + contentMainSize_;
855             targetIndex_.reset();
856         }
857     } while (LessNotEqual(currentEndPos + chainOffset, endMainPos));
858 
859     currentEndPos += chainOffset;
860     // adjust offset.
861     UpdateSnapCenterContentOffset(layoutWrapper);
862     if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
863         endMainPos_ = currentEndPos + contentEndOffset_;
864         startMainPos_ = endMainPos_ - contentMainSize_;
865         auto firstItemTop = itemPosition_.begin()->second.startPos;
866         if (itemPosition_.begin()->second.isGroup) {
867             AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), true);
868             firstItemTop = itemPosition_.begin()->second.startPos;
869         }
870         auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
871         if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
872             // all items size is less than list.
873             if (!canOverScroll_) {
874                 currentOffset_ = firstItemTop - contentStartOffset_;
875                 startMainPos_ = currentOffset_;
876                 endMainPos_ = startMainPos_ + contentMainSize_;
877             }
878             if (!mainSizeIsDefined_) {
879                 // adapt child size.
880                 contentMainSize_ = itemTotalSize;
881             }
882         } else {
883             // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
884             if (!canOverScroll_ || jumpIndex_.has_value()) {
885                 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
886             }
887         }
888     }
889     if (overScrollFeature_ && canOverScroll_) {
890         return;
891     }
892     // Mark inactive in wrapper.
893     for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
894         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
895         // Don't recycle When the head item is Visibility.None.
896         if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) ||
897             GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) {
898             if (pos->second.isGroup) {
899                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
900             }
901             break;
902         }
903         layoutWrapper->RemoveChildInRenderTree(pos->first);
904         itemPosition_.erase(pos++);
905     }
906 }
907 
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)908 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
909 {
910     float currentStartPos = endPos;
911     float currentEndPos = 0.0f;
912     float startMainPos = overScrollFeature_ ?
913         std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
914     if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
915         startMainPos = -Infinity<float>();
916     }
917     auto currentIndex = endIndex + 1;
918     auto chainOffset = 0.0f;
919     do {
920         currentEndPos = currentStartPos;
921         int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
922         if (count == 0) {
923             break;
924         }
925         if (currentIndex > 0) {
926             currentStartPos = currentStartPos - spaceWidth_;
927         }
928         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
929         // reach the valid target index
930         if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
931             startMainPos = GetStartPosition() - contentMainSize_;
932             targetIndex_.reset();
933         }
934     } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
935 
936     currentStartPos += chainOffset;
937     // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
938     UpdateSnapCenterContentOffset(layoutWrapper);
939     if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_)) {
940         auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
941         bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
942         if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
943             if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
944                 currentOffset_ += contentMainSize_ - prevContentMainSize_;
945             }
946             contentMainSize_ = itemTotalSize;
947         }
948         if (!canOverScroll_ || jumpIndex_.has_value()) {
949             currentOffset_ = currentStartPos - contentStartOffset_;
950         }
951         endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
952         startMainPos_ = currentStartPos - contentStartOffset_;
953         if (Positive(currentOffset_) && itemPosition_.rbegin()->second.isGroup) {
954             AdjustPostionForListItemGroup(layoutWrapper, axis_, GetEndIndex(), false);
955         }
956     }
957 
958     if (overScrollFeature_) {
959         return;
960     }
961 
962     // Mark inactive in wrapper.
963     std::list<int32_t> removeIndexes;
964     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
965         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
966         // Don't recycle When the tail item is Visibility.None.
967         if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) ||
968             LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) {
969             if (pos->second.isGroup) {
970                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
971             }
972             break;
973         }
974         layoutWrapper->RemoveChildInRenderTree(pos->first);
975         removeIndexes.emplace_back(pos->first);
976     }
977     for (const auto& index : removeIndexes) {
978         itemPosition_.erase(index);
979     }
980 }
981 
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)982 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
983 {
984     if (!predictSnapOffset_.has_value()) {
985         return;
986     }
987     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
988     if ((scrollSnapAlign != V2::ScrollSnapAlign::START) && (scrollSnapAlign != V2::ScrollSnapAlign::CENTER) &&
989         (scrollSnapAlign != V2::ScrollSnapAlign::END)) {
990         predictSnapOffset_.reset();
991         predictSnapEndPos_.reset();
992         return;
993     }
994 
995     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
996     int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign);
997     if (endIndex != -1) {
998         predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign);
999         predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_;
1000         predictSnapEndPos_.reset();
1001     } else {
1002         if (IsUniformHeightProbably()) {
1003             auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1004             if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1005                 FixPredictSnapOffsetAlignStart();
1006             } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1007                 FixPredictSnapOffsetAlignCenter();
1008             } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1009                 FixPredictSnapOffsetAlignEnd();
1010             }
1011         } else {
1012             predictSnapEndPos_ = predictEndPos;
1013         }
1014     }
1015 
1016     return;
1017 }
1018 
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1019 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1020 {
1021     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1022     CHECK_NULL_RETURN(listLayoutProperty, false);
1023     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1024     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1025         return true;
1026     }
1027 
1028     return false;
1029 }
1030 
FixPredictSnapOffsetAlignStart()1031 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1032 {
1033     if (itemPosition_.empty()) {
1034         return;
1035     }
1036     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1037     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1038     float startPos = contentStartOffset_;
1039     float endPos = contentMainSize_ - contentEndOffset_;
1040 
1041     if (LessNotEqual(predictEndPos, -startPos)) {
1042         if (isSpringEffect_) {
1043             return;
1044         }
1045         predictEndPos = -startPos;
1046     } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1047         if (isSpringEffect_) {
1048             return;
1049         }
1050         predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1051     } else {
1052         int32_t index;
1053         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1054             if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1055                 break;
1056             }
1057         }
1058         predictEndPos = index * itemHeight - startPos;
1059         if (LessNotEqual(predictEndPos, -startPos)) {
1060             predictEndPos = -startPos;
1061         } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1062             predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1063         }
1064     }
1065 
1066     predictSnapOffset_ = totalOffset_ - predictEndPos;
1067     predictSnapEndPos_ = predictEndPos;
1068 }
1069 
FixPredictSnapOffsetAlignCenter()1070 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1071 {
1072     if (itemPosition_.empty()) {
1073         return;
1074     }
1075     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1076     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1077 
1078     if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1079         if (isSpringEffect_) {
1080             return;
1081         }
1082         predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1083     } else if (GreatNotEqual(
1084         predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1085         if (isSpringEffect_) {
1086             return;
1087         }
1088         predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1089     } else {
1090         int32_t index;
1091         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1092             if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1093                 itemHeight / 2.0f) {
1094                 break;
1095             }
1096         }
1097         predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1098         if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1099             predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1100         } else if (GreatNotEqual(
1101             predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1102             predictEndPos =
1103                 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1104         }
1105     }
1106 
1107     predictSnapOffset_ = totalOffset_ - predictEndPos;
1108     predictSnapEndPos_ = predictEndPos;
1109 }
1110 
FixPredictSnapOffsetAlignEnd()1111 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1112 {
1113     if (itemPosition_.empty()) {
1114         return;
1115     }
1116     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1117     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1118     float startPos = contentStartOffset_;
1119     float endPos = contentMainSize_ - contentEndOffset_;
1120 
1121     if (LessNotEqual(predictEndPos, -startPos)) {
1122         if (isSpringEffect_) {
1123             return;
1124         }
1125         predictEndPos = -startPos;
1126     } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1127         if (isSpringEffect_) {
1128             return;
1129         }
1130         predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1131     } else {
1132         int32_t index;
1133         for (index = 0; index <= GetMaxListItemIndex(); index++) {
1134             if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1135                 break;
1136             }
1137         }
1138         predictEndPos = index * itemHeight - endPos - spaceWidth_;
1139         if (LessNotEqual(predictEndPos, -startPos)) {
1140             predictEndPos = -startPos;
1141         } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1142             predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1143         }
1144     }
1145 
1146     predictSnapOffset_ = totalOffset_ - predictEndPos;
1147     predictSnapEndPos_ = predictEndPos;
1148 }
1149 
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1150 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1151     int32_t& startIndex, float crossSize)
1152 {
1153     CHECK_NULL_VOID(wrapper);
1154     auto offset = paddingOffset_;
1155     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1156     float crossOffset = 0.0f;
1157     if (GetLanes() > 1) {
1158         int32_t laneIndex = 0;
1159         if (pos.isGroup) {
1160             startIndex = index + 1;
1161         } else {
1162             laneIndex = (index - startIndex) % GetLanes();
1163         }
1164 
1165         float laneGutter = GetLaneGutter();
1166         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
1167         crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1168     } else {
1169         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1170     }
1171     auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
1172     if (axis_ == Axis::VERTICAL) {
1173         offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
1174     } else {
1175         offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
1176     }
1177     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1178     SetListItemIndex(wrapper, index);
1179 }
1180 
Layout(LayoutWrapper * layoutWrapper)1181 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1182 {
1183     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1184     CHECK_NULL_VOID(listLayoutProperty);
1185     auto axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
1186     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1187     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1188     MinusPaddingToSize(padding, size);
1189     paddingOffset_ = padding.Offset();
1190     float crossSize = GetCrossAxisSize(size, axis_);
1191     totalItemCount_ = layoutWrapper->GetTotalChildCount();
1192     listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
1193     int32_t startIndex = GetStartIndex();
1194 
1195     totalOffset_ += currentOffset_;
1196     FixPredictSnapOffset(listLayoutProperty);
1197     // layout items.
1198     for (auto& pos : itemPosition_) {
1199         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1200         if (!wrapper) {
1201             continue;
1202         }
1203         pos.second.startPos -= currentOffset_;
1204         pos.second.endPos -= currentOffset_;
1205         LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1206         wrapper->Layout();
1207     }
1208     auto cacheCount = listLayoutProperty->GetCachedCountValue(1);
1209     if (!itemPosition_.empty() && cacheCount > 0) {
1210         auto items = LayoutCachedItem(layoutWrapper, cacheCount);
1211         if (!items.empty()) {
1212             PostIdleTask(layoutWrapper->GetHostNode(), { items, childLayoutConstraint_ });
1213         } else {
1214             auto host = layoutWrapper->GetHostNode();
1215             CHECK_NULL_VOID(host);
1216             auto pattern = host->GetPattern<ListPattern>();
1217             CHECK_NULL_VOID(pattern);
1218             pattern->SetPredictLayoutParam(std::nullopt);
1219         }
1220     }
1221 }
1222 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1223 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1224 {
1225     float delta = crossSize - GetLaneGutter() - childCrossSize;
1226     if (LessOrEqual(delta, 0)) {
1227         return 0.0f;
1228     }
1229     switch (listItemAlign_) {
1230         case OHOS::Ace::V2::ListItemAlign::START:
1231             return 0.0f;
1232         case OHOS::Ace::V2::ListItemAlign::CENTER:
1233             return delta / 2.0f;
1234         case OHOS::Ace::V2::ListItemAlign::END:
1235             return delta;
1236         default:
1237             return 0.0f;
1238     }
1239 }
1240 
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1241 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1242 {
1243     if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1244         return;
1245     }
1246     auto host = layoutWrapper->GetHostNode();
1247     CHECK_NULL_VOID(host);
1248     auto focusHub = host->GetFocusHub();
1249     CHECK_NULL_VOID(focusHub);
1250     // textField not in List
1251     if (!focusHub->IsCurrentFocus()) {
1252         return;
1253     }
1254     auto context = PipelineContext::GetCurrentContext();
1255     CHECK_NULL_VOID(context);
1256     auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1257     CHECK_NULL_VOID(textFieldManager);
1258     // only when textField is onFocus
1259     auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1260     CHECK_NULL_VOID(textField);
1261     auto textFieldHost = textField->GetHost();
1262     CHECK_NULL_VOID(textFieldHost);
1263     auto position = textFieldHost->GetTransformRelativeOffset().GetY() + textField->GetHostFrameSize()->Height();
1264     auto globalOffset = host->GetTransformRelativeOffset();
1265     auto offset = contentMainSize_ + globalOffset.GetY() - position;
1266     if (LessOrEqual(offset, 0.0)) {
1267         // negative offset to scroll down
1268         currentDelta_ -= static_cast<float>(offset);
1269     }
1270 }
1271 
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout)1272 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1273     float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout)
1274 {
1275     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1276     CHECK_NULL_VOID(layoutAlgorithmWrapper);
1277     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1278     CHECK_NULL_VOID(itemGroup);
1279     if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1280         referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1281     }
1282     if (jumpIndex_) {
1283         auto wrapper = layoutWrapper;
1284         itemGroup->ClearItemPosition(&(*wrapper));
1285     }
1286     itemGroup->SetListMainSize(startMainPos_, endMainPos_, referencePos, forwardLayout);
1287     itemGroup->SetListLayoutProperty(layoutProperty);
1288     itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
1289     if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1290         if (!jumpIndexInGroup_.has_value()) {
1291             if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1292                 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1293                 jumpIndexInGroup_ = 0;
1294             } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1295                 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1296                 jumpIndexInGroup_ = LAST_ITEM;
1297             }
1298         }
1299 
1300         if (jumpIndexInGroup_.has_value()) {
1301             itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1302             itemGroup->SetScrollAlign(scrollAlign_);
1303             jumpIndexInGroup_.reset();
1304         }
1305     }
1306 
1307     if (groupNeedAllLayout || targetIndex_) {
1308         auto groupItemPosition = itemGroup->GetItemPosition();
1309         int32_t groupTotalItemCount = layoutWrapper->GetTotalChildCount() - itemGroup->GetItemStartIndex();
1310         if (groupNeedAllLayout ||
1311             (targetIndex_ && targetIndex_.value() == index) ||
1312             (!(forwardLayout && !groupItemPosition.empty() &&
1313             groupItemPosition.rbegin()->first == groupTotalItemCount - 1) &&
1314             !(!forwardLayout && !groupItemPosition.empty() &&
1315             groupItemPosition.begin()->first == 0))) {
1316             itemGroup->SetNeedAllLayout();
1317         }
1318     }
1319     layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1320 }
1321 
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1322 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1323 {
1324     int32_t id = layoutWrapper->GetHostNode()->GetId();
1325     ListItemInfo pos = { id, 0, 0, true };
1326     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1327     CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
1328     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1329     CHECK_NULL_RETURN(itemGroup, pos);
1330     auto res = itemGroup->GetItemGroupPosition(index);
1331     return { id, res.first, res.second, true };
1332 }
1333 
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1334 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1335 {
1336     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1337     CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
1338     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1339     CHECK_NULL_RETURN(itemGroup, 0.0f);
1340     return itemGroup->GetItemHeight(index);
1341 }
1342 
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1343 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1344 {
1345     auto host = layoutWrapper->GetHostNode();
1346     CHECK_NULL_VOID(host);
1347     auto listItem = host->GetPattern<ListItemPattern>();
1348     if (listItem) {
1349         listItem->SetIndexInList(index);
1350         return;
1351     }
1352     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1353     CHECK_NULL_VOID(listItemGroup);
1354     listItemGroup->SetIndexInList(index);
1355 }
1356 
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1357 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1358     float referencePos, bool forwardLayout) const
1359 {
1360     if (targetIndex_.has_value()) {
1361         return;
1362     }
1363     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1364     CHECK_NULL_VOID(wrapper);
1365     auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1366     CHECK_NULL_VOID(algorithmWrapper);
1367     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1368     CHECK_NULL_VOID(itemGroup);
1369     itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1370 }
1371 
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1372 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
1373     bool forwardLayout)
1374 {
1375     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1376     CHECK_NULL_VOID(wrapper);
1377     auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1378     CHECK_NULL_VOID(algorithmWrapper);
1379     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1380     CHECK_NULL_VOID(itemGroup);
1381     if (forwardLayout) {
1382         itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, !forwardLayout);
1383     } else {
1384         itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, !forwardLayout);
1385     }
1386     itemGroup->SetScrollAlign(ScrollAlign::NONE);
1387     wrapper->Measure(GetGroupLayoutConstraint());
1388     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1389     auto& pos = itemPosition_[index];
1390     if (forwardLayout) {
1391         pos.startPos = pos.endPos - mainLen;
1392     } else {
1393         pos.endPos = pos.startPos + mainLen;
1394     }
1395 }
1396 
OffScreenLayoutDirection()1397 void ListLayoutAlgorithm::OffScreenLayoutDirection()
1398 {
1399     if (!targetIndex_ || itemPosition_.empty() || (itemPosition_.find(targetIndex_.value()) != itemPosition_.end())) {
1400         forwardFeature_ = false;
1401         backwardFeature_ = false;
1402         return;
1403     }
1404     if (GreatNotEqual(targetIndex_.value(), GetEndIndex())) {
1405         forwardFeature_ = true;
1406         backwardFeature_ = false;
1407     } else {
1408         forwardFeature_ = false;
1409         backwardFeature_ = true;
1410     }
1411 }
1412 
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1413 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1414 {
1415     float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1416     float midPos = contentSize / 2.0f;
1417     if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1418         GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
1419         midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
1420     } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1421         LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
1422         (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1423         midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
1424     }
1425     for (auto & pos : itemPosition_) {
1426         if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1427             return pos.first;
1428         }
1429     }
1430     return totalItemCount_ - 1;
1431 }
1432 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1433 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1434 {
1435     CHECK_NULL_VOID(wrapper);
1436     auto host = wrapper->GetHostNode();
1437     CHECK_NULL_VOID(host);
1438     host->ForceSyncGeometryNode();
1439 }
1440 
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1441 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1442 {
1443     std::list<int32_t> predictBuildList;
1444     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1445     float crossSize = GetCrossAxisSize(size, axis_);
1446 
1447     auto currIndex = itemPosition_.rbegin()->first + 1;
1448     auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1449     for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1450         int32_t index = currIndex + i;
1451         auto wrapper = layoutWrapper->GetChildByIndex(index);
1452         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1453             predictBuildList.emplace_back(index);
1454             continue;
1455         }
1456         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1457         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1458         auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1459         int32_t id = wrapper->GetHostNode()->GetId();
1460         ListItemInfo pos = { id, currPos, endPos, isGroup };
1461         currPos = endPos + spaceWidth_;
1462         auto startIndex = index;
1463         LayoutItem(wrapper, index, pos, startIndex, crossSize);
1464         SyncGeometry(wrapper);
1465         wrapper->SetActive(false);
1466     }
1467 
1468     currIndex = itemPosition_.begin()->first - 1;
1469     currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1470     for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1471         int32_t index = currIndex - i;
1472         auto wrapper = layoutWrapper->GetChildByIndex(index);
1473         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1474             predictBuildList.emplace_back(index);
1475             continue;
1476         }
1477         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1478         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1479         auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1480         int32_t id = wrapper->GetHostNode()->GetId();
1481         ListItemInfo pos = { id, startPos, currPos, isGroup };
1482         currPos = startPos - spaceWidth_;
1483         auto startIndex = index;
1484         LayoutItem(wrapper, index, pos, startIndex, crossSize);
1485         SyncGeometry(wrapper);
1486         wrapper->SetActive(false);
1487     }
1488     return predictBuildList;
1489 }
1490 
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1491 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1492 {
1493     CHECK_NULL_RETURN(wrapper, false);
1494     wrapper->SetActive(false);
1495     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1496     if (!isGroup) {
1497         auto frameNode = wrapper->GetHostNode();
1498         CHECK_NULL_RETURN(frameNode, false);
1499         frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1500         FrameNode::ProcessOffscreenNode(frameNode);
1501         return true;
1502     }
1503     return false;
1504 }
1505 
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1506 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1507 {
1508     CHECK_NULL_VOID(frameNode);
1509     auto pattern = frameNode->GetPattern<ListPattern>();
1510     CHECK_NULL_VOID(pattern);
1511     if (pattern->GetPredictLayoutParam()) {
1512         pattern->SetPredictLayoutParam(param);
1513         return;
1514     }
1515     pattern->SetPredictLayoutParam(param);
1516     auto context = PipelineContext::GetCurrentContext();
1517     CHECK_NULL_VOID(context);
1518     context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1519         ACE_SCOPED_TRACE("List predict");
1520         auto frameNode = weak.Upgrade();
1521         CHECK_NULL_VOID(frameNode);
1522         auto pattern = frameNode->GetPattern<ListPattern>();
1523         CHECK_NULL_VOID(pattern);
1524         if (!pattern->GetPredictLayoutParam().has_value()) {
1525             return;
1526         }
1527         bool needMarkDirty = false;
1528         auto param = pattern->GetPredictLayoutParam().value();
1529         for (auto it = param.items.begin(); it != param.items.end();) {
1530             if (GetSysTimestamp() > deadline) {
1531                 break;
1532             }
1533             auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false);
1534             if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
1535                 break;
1536             }
1537             needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty;
1538             param.items.erase(it++);
1539         }
1540         if (needMarkDirty) {
1541             frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1542         }
1543         pattern->SetPredictLayoutParam(std::nullopt);
1544         if (!param.items.empty()) {
1545             ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1546             pattern->SetPredictLayoutParam(param);
1547         }
1548     });
1549 }
1550 
GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign)1551 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign)
1552 {
1553     float stopOnScreen = 0;
1554     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1555         stopOnScreen = contentStartOffset_;
1556     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1557         stopOnScreen = contentMainSize_ / 2.0f;
1558     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1559         stopOnScreen = contentMainSize_ - contentEndOffset_;
1560     }
1561     return stopOnScreen;
1562 }
1563 
FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)1564 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
1565     float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign)
1566 {
1567     int32_t endIndex = -1;
1568     float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign);
1569     float startPos = 0.0f;
1570     float endPos = 0.0f;
1571     float itemHeight = 0.0f;
1572 
1573     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1574         for (const auto& positionInfo : itemPosition_) {
1575             startPos = totalOffset_ + positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
1576             itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
1577             endPos = totalOffset_ + positionInfo.second.startPos + itemHeight / 2.0f;
1578             if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1579                 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1580                 endIndex = positionInfo.first;
1581                 break;
1582             }
1583         }
1584     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1585         for (const auto& positionInfo : itemPosition_) {
1586             startPos = totalOffset_ + positionInfo.second.startPos - spaceWidth_ / 2.0f;
1587             endPos = totalOffset_ + positionInfo.second.endPos + spaceWidth_ / 2.0f;
1588             if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1589                 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1590                 endIndex = positionInfo.first;
1591                 break;
1592             }
1593         }
1594     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1595         for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1596             endPos = totalOffset_ + pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
1597             itemHeight = pos->second.endPos - pos->second.startPos;
1598             startPos = totalOffset_ + pos->second.endPos - itemHeight / 2.0f;
1599             if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1600                 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1601                 endIndex = pos->first;
1602                 break;
1603             }
1604         }
1605     }
1606     return endIndex;
1607 }
1608 
IsUniformHeightProbably()1609 bool ListLayoutAlgorithm::IsUniformHeightProbably()
1610 {
1611     bool isUniformHeightProbably = true;
1612     float itemHeight = 0.0f;
1613     float currentItemHeight = 0.0f;
1614     for (const auto& positionInfo : itemPosition_) {
1615         currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
1616         if (NearZero(itemHeight)) {
1617             itemHeight = currentItemHeight;
1618         } else if (!NearEqual(currentItemHeight, itemHeight)) {
1619             isUniformHeightProbably = false;
1620             break;
1621         }
1622     }
1623     return isUniformHeightProbably;
1624 }
1625 
CalculatePredictSnapEndPositionByIndex(uint32_t index,V2::ScrollSnapAlign scrollSnapAlign)1626 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(uint32_t index, V2::ScrollSnapAlign scrollSnapAlign)
1627 {
1628     float predictSnapEndPos = 0;
1629     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1630         predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
1631     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1632         float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
1633         predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1634     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1635         predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
1636     }
1637     return predictSnapEndPos;
1638 }
1639 
OnItemPositionAddOrUpdate(LayoutWrapper * layoutWrapper,uint32_t index)1640 void ListLayoutAlgorithm::OnItemPositionAddOrUpdate(LayoutWrapper* layoutWrapper, uint32_t index)
1641 {
1642     if (!predictSnapEndPos_.has_value()) {
1643         return;
1644     }
1645     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1646     CHECK_NULL_VOID(listLayoutProperty);
1647     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1648     float startPos = 0.0f;
1649     float endPos = 0.0f;
1650     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1651         startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_;
1652         endPos = totalOffset_ + itemPosition_[index].endPos;
1653     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1654         startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_ / 2.0f;
1655         endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_ / 2.0f;
1656     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1657         startPos = totalOffset_ + itemPosition_[index].startPos;
1658         endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_;
1659     } else {
1660         return;
1661     }
1662 
1663     float predictSnapEndPos = predictSnapEndPos_.value();
1664     float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign);
1665     if (GreatOrEqual(predictSnapEndPos + stopOnScreen, startPos) &&
1666         LessNotEqual(predictSnapEndPos + stopOnScreen, endPos)) {
1667         predictSnapEndPos = CalculatePredictSnapEndPositionByIndex(index, scrollSnapAlign);
1668     } else {
1669         return;
1670     }
1671 
1672     if (!NearEqual(predictSnapEndPos, predictSnapEndPos_.value())) {
1673         predictSnapEndPos_ = predictSnapEndPos;
1674     }
1675 }
1676 } // namespace OHOS::Ace::NG
1677