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