• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "base/log/event_report.h"
17 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
18 
19 #include "core/components/common/layout/grid_system_manager.h"
20 #include "core/components_ng/pattern/list/list_item_pattern.h"
21 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
22 #include "core/components_ng/property/measure_utils.h"
23 
24 namespace OHOS::Ace::NG {
25 
26 namespace {
27 constexpr uint32_t GRID_COUNTS_4 = 4;
28 constexpr uint32_t GRID_COUNTS_6 = 6;
29 constexpr uint32_t GRID_COUNTS_8 = 8;
30 constexpr uint32_t GRID_COUNTS_12 = 12;
31 
GetMaxGridCounts(int32_t currentColumns)32 uint32_t GetMaxGridCounts(int32_t currentColumns)
33 {
34     auto maxGridCounts = GRID_COUNTS_8;
35     switch (currentColumns) {
36         case GRID_COUNTS_4:
37             maxGridCounts = GRID_COUNTS_4;
38             break;
39         case GRID_COUNTS_8:
40             maxGridCounts = GRID_COUNTS_6;
41             break;
42         case GRID_COUNTS_12:
43             maxGridCounts = GRID_COUNTS_8;
44             break;
45         default:
46             break;
47     }
48     return maxGridCounts;
49 }
50 } // namespace
51 
Measure(LayoutWrapper * layoutWrapper)52 void ListItemGroupLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
53 {
54     auto totalChildCount = layoutWrapper->GetTotalChildCount();
55     totalItemCount_ = totalChildCount - itemStartIndex_ - footerCount_;
56     footerIndex_ = (footerCount_ > 0) ? (totalChildCount - footerCount_) : -1;
57     CHECK_NULL_VOID(listLayoutProperty_);
58     auto layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
59     CHECK_NULL_VOID(layoutProperty);
60     axis_ = listLayoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
61     layoutDirection_ = listLayoutProperty_->GetNonAutoLayoutDirection();
62     isStackFromEnd_ = listLayoutProperty_->GetStackFromEnd().value_or(false);
63     const auto& padding = layoutProperty->CreatePaddingAndBorder();
64     paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
65     paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
66     const auto& contentConstraintOps = layoutProperty->GetContentLayoutConstraint();
67     CHECK_NULL_VOID(contentConstraintOps);
68     auto contentConstraint = contentConstraintOps.value();
69     auto contentIdealSize = CreateIdealSize(
70         contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
71     auto listItemGroupLayoutProperty =
72         AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
73     CHECK_NULL_VOID(listItemGroupLayoutProperty);
74     auto layoutPolicy = listItemGroupLayoutProperty->GetLayoutPolicyProperty();
75     auto isCrossWrap =
76         layoutPolicy.has_value() && ((axis_ == Axis::VERTICAL && layoutPolicy.value().IsWidthWrap()) ||
77                                         (axis_ == Axis::HORIZONTAL && layoutPolicy.value().IsHeightWrap()));
78     auto isCrossFix =
79         layoutPolicy.has_value() && ((axis_ == Axis::VERTICAL && layoutPolicy.value().IsWidthFix()) ||
80                                         (axis_ == Axis::HORIZONTAL && layoutPolicy.value().IsHeightFix()));
81 
82     auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
83     auto space = layoutProperty->GetSpace().value_or(Dimension(0));
84 
85     const auto& layoutConstraintOps = layoutProperty->GetLayoutConstraint();
86     CHECK_NULL_VOID(layoutConstraintOps);
87     auto layoutConstraint = layoutConstraintOps.value();
88     CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
89     childLayoutConstraint_ = layoutProperty->CreateChildConstraint();
90     isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
91     if (isCardStyle_) {
92         UpdateListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty, contentIdealSize);
93         listLayoutProperty_->UpdateListItemAlign(V2::ListItemAlign::CENTER);
94     }
95     UpdateListItemConstraint(contentIdealSize, childLayoutConstraint_);
96     referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
97     totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
98     spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
99     if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, endPos_ - startPos_)) {
100         spaceWidth_ = 0.0f;
101     }
102     if (layoutProperty->GetDivider().has_value()) {
103         auto divider = layoutProperty->GetDivider().value();
104         std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
105         if (GreatOrEqual(dividerSpace.value(), endPos_ - startPos_)) {
106             dividerSpace.reset();
107         }
108         if (dividerSpace.has_value()) {
109             spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
110         }
111     }
112     if (IsRoundingMode(layoutWrapper)) {
113         spaceWidth_ = Round(spaceWidth_);
114     }
115     MeasureHeaderFooter(layoutWrapper);
116     totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
117     if (isStackFromEnd_) {
118         std::swap(headerMainSize_, footerMainSize_);
119     }
120     if (childrenSize_) {
121         posMap_->UpdateGroupPosMap(totalItemCount_, GetLanes(), spaceWidth_, childrenSize_,
122             headerMainSize_, footerMainSize_);
123         totalMainSize_ = posMap_->GetTotalHeight();
124     }
125 
126     if (isLayouted_) {
127         ReverseItemPosition(cachedItemPosition_, prevTotalItemCount_, prevTotalMainSize_);
128         ReverseItemPosition(itemPosition_, prevTotalItemCount_, prevTotalMainSize_);
129         ReverseLayoutedItemInfo(prevTotalItemCount_, prevTotalMainSize_);
130     } else {
131         recycledItemPosition_.insert(itemPosition_.begin(), itemPosition_.end());
132     }
133 
134     if (cacheParam_) {
135         MeasureCacheItem(layoutWrapper);
136     } else {
137         MeasureListItem(layoutWrapper, childLayoutConstraint_);
138         UpdateCachedItemPosition(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
139     }
140 
141     if (childrenSize_) {
142         AdjustByPosMap();
143     } else {
144         AdjustItemPosition();
145         UpdateLayoutedItemInfo();
146     }
147     UpdateRecycledItems();
148 
149     ReverseLayoutedItemInfo(totalItemCount_, totalMainSize_);
150     auto crossSize = contentIdealSize.CrossSize(axis_);
151     if ((crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) || isCrossFix) {
152         contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
153     } else if (isCrossWrap) {
154         contentIdealSize.SetCrossSize(
155             std::min(GetChildMaxCrossSize(layoutWrapper, axis_), crossSize.value_or(0.0f)), axis_);
156     }
157     contentIdealSize.SetMainSize(totalMainSize_, axis_);
158     AddPaddingToSize(padding, contentIdealSize);
159     layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
160     layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
161     isLayouted_ = false;
162 }
163 
UpdateRecycledItems()164 void ListItemGroupLayoutAlgorithm::UpdateRecycledItems()
165 {
166     if (isLayouted_) {
167         return;
168     }
169     for (const auto& item : itemPosition_) {
170         recycledItemPosition_.erase(item.first);
171     }
172 }
173 
UpdateCachedItemPosition(int32_t cacheCount)174 void ListItemGroupLayoutAlgorithm::UpdateCachedItemPosition(int32_t cacheCount)
175 {
176     if (!itemPosition_.empty()) {
177         auto iter = cachedItemPosition_.begin();
178         while (iter != cachedItemPosition_.end()) {
179             if ((iter->first >= GetStartIndex() && iter->first <= GetEndIndex()) ||
180                 iter->first < (GetStartIndex() - cacheCount) || iter->first > (GetEndIndex() + cacheCount)) {
181                 iter = cachedItemPosition_.erase(iter);
182             } else {
183                 iter++;
184             }
185         }
186     }
187 }
188 
UpdateListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty,OptionalSizeF & contentIdealSize)189 void ListItemGroupLayoutAlgorithm::UpdateListItemGroupMaxWidth(
190     const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty, OptionalSizeF& contentIdealSize)
191 {
192     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
193     CHECK_NULL_VOID(columnInfo);
194     auto columnParent = columnInfo->GetParent();
195     CHECK_NULL_VOID(columnParent);
196     columnParent->BuildColumnWidth();
197     auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnParent->GetColumns())));
198     float paddingWidth = layoutProperty->CreatePaddingAndBorder().Width();
199     const auto& parentIdealSizeCrossOps = parentIdealSize.CrossSize(axis_);
200     CHECK_NULL_VOID(parentIdealSizeCrossOps);
201     auto parentWidth = parentIdealSizeCrossOps.value() + paddingWidth;
202     auto maxWidth = std::min(parentWidth, maxGridWidth);
203     if (LessNotEqual(maxGridWidth, paddingWidth)) {
204         TAG_LOGI(AceLogTag::ACE_LIST,
205             "ListItemGroup reset to parentWidth since grid_col width:%{public}f, border:%{public}f",
206             maxGridWidth, paddingWidth);
207         maxWidth = parentWidth;
208     }
209     contentIdealSize.SetCrossSize(maxWidth - paddingWidth, axis_);
210 }
211 
Layout(LayoutWrapper * layoutWrapper)212 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
213 {
214     const auto& layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
215     CHECK_NULL_VOID(layoutProperty);
216     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
217     auto padding = layoutProperty->CreatePaddingAndBorder();
218     MinusPaddingToSize(padding, size);
219     auto left = padding.left.value_or(0.0f);
220     auto top = padding.top.value_or(0.0f);
221     auto paddingOffset = OffsetF(left, top);
222     float crossSize = GetCrossAxisSize(size, axis_);
223     CHECK_NULL_VOID(listLayoutProperty_);
224     itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
225     bool show = listLayoutProperty_->GetShowCachedItemsValue(false);
226     layoutProperty->UpdateListLanes(listLayoutProperty_->GetLanes(),
227         listLayoutProperty_->GetLaneMinLength(), listLayoutProperty_->GetLaneMaxLength());
228 
229     if (cacheParam_) {
230         LayoutCacheItem(layoutWrapper, paddingOffset, crossSize, show);
231         CheckUpdateGroupAndItemPos(layoutWrapper, paddingOffset, crossSize);
232     } else {
233         LayoutListItem(layoutWrapper, paddingOffset, crossSize);
234         ResetLayoutItem(layoutWrapper);
235     }
236     ReverseItemPosition(cachedItemPosition_, totalItemCount_, totalMainSize_);
237     ReverseItemPosition(itemPosition_, totalItemCount_, totalMainSize_);
238     if (isStackFromEnd_) {
239         std::swap(headerMainSize_, footerMainSize_);
240     }
241     SetActiveChildRange(layoutWrapper, listLayoutProperty_->GetCachedCountWithDefault(), show);
242     if (headerIndex_ >= 0 || footerIndex_ >= 0) {
243         if (layoutDirection_ == TextDirection::RTL && axis_ == Axis::HORIZONTAL) {
244             LayoutHeaderFooterRTL(layoutWrapper, paddingOffset, crossSize);
245         } else {
246             LayoutHeaderFooterLTR(layoutWrapper, paddingOffset, crossSize);
247         }
248     }
249     isLayouted_ = true;
250 }
251 
CheckUpdateGroupAndItemPos(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)252 void ListItemGroupLayoutAlgorithm::CheckUpdateGroupAndItemPos(LayoutWrapper* layoutWrapper,
253     const OffsetF& paddingOffset, float crossSize)
254 {
255     if (childrenSize_ || (NearZero(adjustReferenceDelta_) && NearZero(adjustTotalSize_))) {
256         return;
257     }
258     auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
259     if (axis_ == Axis::VERTICAL) {
260         offset = isStackFromEnd_ ? offset - OffsetF(0.0f, adjustTotalSize_ + adjustReferenceDelta_) :
261                                    offset + OffsetF(0.0f, adjustReferenceDelta_);
262     } else {
263         if (layoutDirection_ == TextDirection::RTL) {
264             offset = isStackFromEnd_ ? offset + OffsetF(adjustReferenceDelta_, 0.0f) :
265                                        offset - OffsetF(adjustTotalSize_ + adjustReferenceDelta_, 0.0f);
266         } else {
267             offset = isStackFromEnd_ ?  offset - OffsetF(adjustTotalSize_ + adjustReferenceDelta_, 0.0f) :
268                                         offset + OffsetF(adjustReferenceDelta_, 0.0f);
269         }
270     }
271     layoutWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
272     LayoutListItem(layoutWrapper, paddingOffset, crossSize);
273 }
274 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)275 void ListItemGroupLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
276 {
277     CHECK_NULL_VOID(wrapper);
278     auto host = wrapper->GetHostNode();
279     CHECK_NULL_VOID(host);
280     host->ForceSyncGeometryNode();
281     host->ResetLayoutAlgorithm();
282 }
283 
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const284 bool ListItemGroupLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
285 {
286     if (layoutWrapper->CheckNeedForceMeasureAndLayout() || layoutWrapper->IsIgnoreOptsValid()) {
287         return true;
288     }
289     auto geometryNode = layoutWrapper->GetGeometryNode();
290     CHECK_NULL_RETURN(geometryNode, true);
291     auto constraint = geometryNode->GetParentLayoutConstraint();
292     CHECK_NULL_RETURN(constraint, true);
293     return constraint.value() != childLayoutConstraint_;
294 }
295 
MeasureHeaderFooter(LayoutWrapper * layoutWrapper)296 void ListItemGroupLayoutAlgorithm::MeasureHeaderFooter(LayoutWrapper* layoutWrapper)
297 {
298     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
299     CHECK_NULL_VOID(layoutProperty);
300     auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
301     headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
302     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
303         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
304     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
305         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
306     if (headerWrapper) {
307         headerWrapper->Measure(headerFooterLayoutConstraint);
308         headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
309     }
310     if (footerWrapper) {
311         footerWrapper->Measure(headerFooterLayoutConstraint);
312         footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
313     }
314 }
315 
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)316 void ListItemGroupLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
317 {
318     if (measureInNextFrame_) {
319         return;
320     }
321     if (!itemPosition_.empty()) {
322         auto start = itemStartIndex_ + itemPosition_.begin()->first;
323         auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
324         int32_t cachedCountForward = cachedItemPosition_.empty() ? 0 :
325             std::max(itemStartIndex_ + cachedItemPosition_.rbegin()->first - end, 0);
326         int32_t cachedCountBackward = cachedItemPosition_.empty() ? 0 :
327             std::max(start - itemStartIndex_ - cachedItemPosition_.begin()->first, 0);
328         if (pauseMeasureCacheItem_ != -1 && pauseMeasureCacheItem_ < start) {
329             cachedCountBackward += 1;
330         } else if (pauseMeasureCacheItem_ != -1 && pauseMeasureCacheItem_ > end) {
331             cachedCountForward += 1;
332         }
333         layoutWrapper->SetActiveChildRange(start, end, cachedCountBackward, cachedCountForward, show);
334         return;
335     } else if (show && (!cachedItemPosition_.empty() || pauseMeasureCacheItem_ != -1)) {
336         int32_t start = cachedItemPosition_.empty() ? pauseMeasureCacheItem_ : cachedItemPosition_.begin()->first;
337         int32_t end = cachedItemPosition_.empty() ? pauseMeasureCacheItem_ : cachedItemPosition_.rbegin()->first;
338         int32_t count = end - start + 1;
339         if (start == 0) {
340             layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, count, show);
341         } else if (end == totalItemCount_ - 1) {
342             int32_t endLimit = end + itemStartIndex_ + 1;
343             layoutWrapper->SetActiveChildRange(endLimit, endLimit, count, 0, show);
344         }
345         return;
346     }
347     auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
348     auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
349     if (LessNotEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_))) {
350         int32_t index = totalItemCount_ + itemStartIndex_;
351         layoutWrapper->SetActiveChildRange(index, index, cacheCount * lanes_, 0);
352     } else {
353         layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount * lanes_);
354     }
355 }
356 
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)357 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
358     LayoutConstraintF& contentConstraint)
359 {
360     contentConstraint.parentIdealSize = selfIdealSize;
361     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
362     auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
363     if (crossSizeOptional.has_value()) {
364         float crossSize = crossSizeOptional.value();
365         if (lanes_ > 1) {
366             crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
367             crossSize = crossSize <= 0 ? 1 : crossSize;
368         }
369         if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
370             crossSize = maxLaneLength_.value();
371         }
372         contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
373         contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
374         contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
375         if (minLaneLength_.has_value()) {
376             contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
377         }
378     }
379 }
380 
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)381 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
382 {
383     float maxCrossSize = 0.0f;
384     for (const auto& pos : itemPosition_) {
385         auto wrapper = GetListItem(layoutWrapper, pos.first, false);
386         if (!wrapper) {
387             continue;
388         }
389         auto getGeometryNode = wrapper->GetGeometryNode();
390         if (!getGeometryNode) {
391             continue;
392         }
393         maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
394     }
395     for (const auto& pos : cachedItemPosition_) {
396         auto wrapper = GetListItem(layoutWrapper, pos.first, false);
397         if (!wrapper) {
398             continue;
399         }
400         auto getGeometryNode = wrapper->GetGeometryNode();
401         if (!getGeometryNode) {
402             continue;
403         }
404         maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
405     }
406     return maxCrossSize;
407 }
408 
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)409 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
410     RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
411 {
412     CHECK_NULL_RETURN(layoutProperty, referencePos);
413     const auto& padding = layoutProperty->CreatePaddingAndBorder();
414     const auto& margin = layoutProperty->CreateMargin();
415     auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
416     auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
417     offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
418     offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
419     forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
420     return referencePos;
421 }
422 
NeedMeasureItem(LayoutWrapper * layoutWrapper)423 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem(LayoutWrapper* layoutWrapper)
424 {
425     auto contentMainSize = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
426     if (NearZero(contentMainSize)) {
427         return true;
428     }
429     bool layoutedEntirely = layoutedItemInfo_ && layoutedItemInfo_.value().startIndex <= 0 &&
430         layoutedItemInfo_.value().endIndex >= totalItemCount_ - 1;
431     if (forwardLayout_) {
432         if (childrenSize_ && needAdjustRefPos_) {
433             referencePos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
434             refPos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
435         }
436         if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
437             return false;
438         }
439         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
440             auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
441             auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
442             bool belowList = GreatOrEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_));
443             if (belowList && totalItemCount_ > 0 && !layoutedEntirely) {
444                 return true;
445             }
446             return false;
447         }
448     } else {
449         if (childrenSize_ && needAdjustRefPos_) {
450             referencePos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
451             refPos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
452         }
453         if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
454             auto listPadding = GetMainAxisOffset(listLayoutProperty_->CreatePaddingAndBorder().Offset(), axis_);
455             auto offset = GetMainAxisOffset(layoutWrapper->GetGeometryNode()->GetMarginFrameOffset(), axis_);
456             auto groupBottom = offset + totalMainSize_;
457             auto aboveList = LessOrEqual(groupBottom, listPadding);
458             if (aboveList && totalItemCount_ > 0 && !layoutedEntirely) {
459                 return true;
460             }
461             return false;
462         }
463         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
464             return false;
465         }
466     }
467     return true;
468 }
469 
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)470 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
471     const LayoutConstraintF& layoutConstraint, float startPos)
472 {
473     int32_t currentIndex = -1;
474     float currentEndPos = startPos;
475     float currentStartPos = 0.0f;
476     while (currentIndex < totalItemCount_) {
477         currentStartPos = currentEndPos;
478         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
479             currentStartPos, currentEndPos);
480         if (count == 0) {
481             break;
482         }
483         if (currentIndex < (totalItemCount_ - 1)) {
484             currentEndPos += spaceWidth_;
485         }
486     }
487 }
488 
ClearItemPosition()489 void ListItemGroupLayoutAlgorithm::ClearItemPosition()
490 {
491     itemPosition_.clear();
492     cachedItemPosition_.clear();
493     layoutedItemInfo_.reset();
494     forwardCachedIndex_ = -1;
495     backwardCachedIndex_ = INT_MAX;
496 }
497 
CheckNeedAllLayout(const RefPtr<LayoutWrapper> & layoutWrapper,bool forwardLayout)498 void ListItemGroupLayoutAlgorithm::CheckNeedAllLayout(const RefPtr<LayoutWrapper>& layoutWrapper, bool forwardLayout)
499 {
500     if (itemPosition_.empty()) {
501         needAllLayout_ = true;
502         return;
503     }
504     int32_t totalItemCount = layoutWrapper->GetTotalChildCount() - itemStartIndex_ - footerCount_;
505     if (!(forwardLayout && itemPosition_.rbegin()->first == totalItemCount - 1) &&
506         !(!forwardLayout && itemPosition_.begin()->first == 0)) {
507         needAllLayout_ = true;
508     }
509 }
510 
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)511 void ListItemGroupLayoutAlgorithm::MeasureListItem(
512     LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
513 {
514     if (totalItemCount_ <= 0) {
515         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
516             adjustReferenceDelta_ = totalMainSize_ - (headerMainSize_ + footerMainSize_);
517         }
518         totalMainSize_ = headerMainSize_ + footerMainSize_;
519         itemPosition_.clear();
520         cachedItemPosition_.clear();
521         layoutedItemInfo_.reset();
522         return;
523     }
524     int32_t startIndex = 0;
525     int32_t endIndex = totalItemCount_ - 1;
526     float startPos = headerMainSize_;
527     float endPos = totalMainSize_ - footerMainSize_;
528     prevStartPos_ = startPos_;
529     prevEndPos_ = endPos_;
530     if (needAllLayout_) {
531         needAllLayout_ = false;
532         itemPosition_.clear();
533         cachedItemPosition_.clear();
534         LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
535         return;
536     }
537     if (targetIndex_) {
538         startPos_ = -Infinity<float>();
539         endPos_ = Infinity<float>();
540         targetIndex_ = isStackFromEnd_ ? totalItemCount_ - targetIndex_.value() - 1 : targetIndex_.value();
541     }
542     if (jumpIndex_.has_value()) {
543         if (jumpIndex_.value() == LAST_ITEM) {
544             jumpIndex_ = totalItemCount_ - 1;
545         }
546         auto jumpIndex = isStackFromEnd_ ? totalItemCount_ - jumpIndex_.value() - 1 : jumpIndex_.value();
547         if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
548             jumpIndex = 0;
549         }
550         if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
551             scrollAlign_ == ScrollAlign::AUTO) {
552             startIndex = jumpIndex;
553         } else if (scrollAlign_ == ScrollAlign::END) {
554             endIndex = jumpIndex;
555         } else if (forwardLayout_) {
556             startIndex = jumpIndex;
557         } else {
558             endIndex = jumpIndex;
559         }
560         itemPosition_.clear();
561         cachedItemPosition_.clear();
562         jumpIndex_.reset();
563         layoutedItemInfo_.reset();
564     } else if (!itemPosition_.empty()) {
565         if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
566             startPos = itemPosition_.begin()->second.startPos;
567         }
568         startIndex = GetStartIndex();
569         if (startIndex >= totalItemCount_) {
570             startIndex = totalItemCount_ - 1;
571             if (itemPosition_.begin()->first > 0) {
572                 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
573                                GetLanesFloor(startIndex) + headerMainSize_;
574             }
575         }
576         if (!isNeedMeasureFormLastItem_) {
577             endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
578             endPos = itemPosition_.rbegin()->second.endPos;
579         }
580         if (forwardLayout_) {
581             ModifyReferencePos(GetLanesFloor(startIndex), startPos);
582         } else {
583             ModifyReferencePos(GetLanesCeil(endIndex), endPos);
584         }
585         itemPosition_.clear();
586     } else if (!NeedMeasureItem(layoutWrapper)) {
587         itemPosition_.clear();
588         return;
589     }
590     if (scrollAlign_ == ScrollAlign::CENTER) {
591         startIndex = GetLanesFloor(startIndex);
592         MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
593     } else if (scrollAlign_ == ScrollAlign::START) {
594         startIndex = GetLanesFloor(startIndex);
595         MeasureStart(layoutWrapper, layoutConstraint, startIndex);
596     } else if (scrollAlign_ == ScrollAlign::END) {
597         endIndex = GetLanesCeil(endIndex);
598         MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
599     } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
600         startIndex = GetLanesFloor(startIndex);
601         MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
602     } else if (forwardLayout_) {
603         startIndex = GetLanesFloor(startIndex);
604         if (!CheckJumpForwardForBigOffset(startIndex, startPos)) {
605             startPos = childrenSize_ ? posMap_->GetPos(startIndex) : startPos;
606             MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
607         }
608     } else {
609         endIndex = GetLanesCeil(endIndex);
610         if (!CheckJumpBackwardForBigOffset(endIndex, endPos)) {
611             endPos = childrenSize_ ? posMap_->GetPos(endIndex) + posMap_->GetRowHeight(endIndex) : endPos;
612             MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
613         }
614     }
615 }
616 
GetItemGroupPosition(int32_t index)617 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
618 {
619     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
620     index = isStackFromEnd_ ? totalItemCount_ - index - 1 : index;
621     if (scrollAlign_ == ScrollAlign::CENTER) {
622         auto pos = itemPosition_.find(index);
623         if (pos != itemPosition_.end()) {
624             float refPos = (pos->second.endPos + pos->second.startPos) / 2 + paddingBeforeContent_; // 2:average
625             float delta = (startPos_ + endPos_) / 2 - refPos;
626             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
627         }
628     } else if (scrollAlign_ == ScrollAlign::START) {
629         auto pos = itemPosition_.find(index);
630         if (pos != itemPosition_.end()) {
631             float top = startPos_ + contentStartOffset_;
632             if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
633                 top += headerMainSize_;
634             }
635             float refPos = pos->second.startPos + paddingBeforeContent_;
636             float delta = top - refPos;
637             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
638         }
639     } else if (scrollAlign_ == ScrollAlign::END) {
640         auto pos = itemPosition_.find(index);
641         if (pos != itemPosition_.end()) {
642             float bottom = endPos_ - contentEndOffset_;
643             if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
644                 bottom -= footerMainSize_;
645             }
646             float refPos = pos->second.endPos + paddingBeforeContent_;
647             float delta = bottom - refPos;
648             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
649         }
650     }
651     return { 0.0f, 0.0f };
652 }
653 
GetItemHeight(int32_t index)654 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
655 {
656     auto it = itemPosition_.find(index);
657     if (it != itemPosition_.end()) {
658         return it->second.endPos - it->second.startPos;
659     }
660     return 0.0f;
661 }
662 
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)663 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
664     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
665 {
666     auto wrapper = GetListItem(layoutWrapper, currentIndex);
667     if (!wrapper) {
668         ReportGetChildError("MeasureALineAuto", currentIndex);
669         return 0;
670     }
671     if (CheckNeedMeasure(wrapper)) {
672         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
673         wrapper->Measure(layoutConstraint);
674     }
675     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
676     int32_t id = wrapper->GetHostNode()->GetId();
677     itemPosition_[currentIndex] = { id, 0.0f, mainLen };
678     return 1;
679 }
680 
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)681 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
682     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
683 {
684     float mainLen = 0;
685     int32_t cnt = 0;
686     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
687     for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
688         auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
689         if (!wrapper) {
690             ReportGetChildError("MeasureALineCenter", currentIndex + cnt);
691             break;
692         }
693         if (CheckNeedMeasure(wrapper)) {
694             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
695             wrapper->Measure(layoutConstraint);
696         }
697         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
698         cnt++;
699     }
700     if (cnt > 0) {
701         auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
702         auto endPos = startPos + mainLen; // 2:average
703         for (int32_t i = 0; i < cnt; i++) {
704             auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
705             int32_t id = wrapper->GetHostNode()->GetId();
706             itemPosition_[currentIndex + i] = { id, startPos, endPos };
707         }
708     }
709     return cnt;
710 }
711 
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)712 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
713     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
714 {
715     float mainLen = 0.0f;
716     int32_t cnt = 0;
717     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
718     if (currentIndex + 1 >= totalItemCount_) {
719         return cnt;
720     }
721     for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_ - 1; i++) {
722         auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
723         if (!wrapper) {
724             ReportGetChildError("MeasureALineForward", currentIndex + 1);
725             break;
726         }
727         cnt++;
728         ++currentIndex;
729         if (CheckNeedMeasure(wrapper)) {
730             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
731             wrapper->Measure(layoutConstraint);
732         }
733         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
734     }
735     if (cnt > 0) {
736         endPos = startPos + mainLen;
737         for (int32_t i = 0; i < cnt; i++) {
738             auto wrapper = GetListItem(layoutWrapper, currentIndex - i);
739             int32_t id = wrapper->GetHostNode()->GetId();
740             itemPosition_[currentIndex - i] = { id, startPos, endPos };
741         }
742     }
743     return cnt;
744 }
745 
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)746 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
747     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
748 {
749     float mainLen = 0.0f;
750     int32_t cnt = 0;
751     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
752     for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
753         auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
754         if (!wrapper) {
755             ReportGetChildError("MeasureALineBackward", currentIndex - 1);
756             break;
757         }
758         --currentIndex;
759         cnt++;
760         if (CheckNeedMeasure(wrapper)) {
761             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
762             wrapper->Measure(layoutConstraint);
763         }
764         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
765         if (currentIndex % lanes == 0) {
766             break;
767         }
768     }
769     if (cnt > 0) {
770         startPos = endPos - mainLen;
771         for (int32_t i = 0; i < cnt; i++) {
772             auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
773             int32_t id = wrapper->GetHostNode()->GetId();
774             itemPosition_[currentIndex + i] = { id, startPos, endPos };
775         }
776     }
777     return cnt;
778 }
779 
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)780 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
781     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
782 {
783     MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
784     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
785     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
786 
787     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
788     float currentStartPos = headerMainSize_;
789     int32_t i = 0;
790     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
791     for (auto& pos : itemPosition_) {
792         float len = pos.second.endPos - pos.second.startPos;
793         pos.second.startPos = currentStartPos;
794         pos.second.endPos = currentStartPos + len;
795         i++;
796         if (i % lanes == 0) {
797             currentStartPos = pos.second.endPos + spaceWidth_;
798         }
799     }
800 }
801 
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)802 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
803     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
804 {
805     if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
806         return;
807     }
808 
809     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
810 }
811 
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)812 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
813     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
814 {
815     float currentStartPos = startPos;
816     float currentEndPos = startPos;
817     int32_t currentIndex = startIndex - 1;
818     while (LessOrEqual(currentEndPos, endPos_)) {
819         if (ReachResponseDeadline(layoutWrapper)) {
820             measureInNextFrame_ = true;
821             return;
822         }
823         currentStartPos = currentEndPos;
824         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
825             currentStartPos, currentEndPos);
826         if (count == 0) {
827             break;
828         }
829         if (currentIndex < (totalItemCount_ - 1)) {
830             currentEndPos += spaceWidth_;
831         }
832     }
833 }
834 
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)835 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
836     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
837 {
838     float currentEndPos = endPos;
839     float currentStartPos = endPos;
840     int32_t currentIndex = endIndex + 1;
841     while (GreatOrEqual(currentStartPos, startPos_)) {
842         if (ReachResponseDeadline(layoutWrapper)) {
843             measureInNextFrame_ = true;
844             return;
845         }
846         currentEndPos = currentStartPos;
847         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
848             currentEndPos, currentStartPos);
849         if (count == 0) {
850             break;
851         }
852         if (currentIndex > 0) {
853             currentStartPos -= spaceWidth_;
854         }
855     }
856 }
857 
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)858 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
859     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
860 {
861     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
862     float currentStartPos = startPos_ + contentStartOffset_;
863     if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
864         currentStartPos += headerMainSize_;
865     }
866 
867     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
868     if (GreatNotEqual(currentStartPos, startPos_)) {
869         MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
870     }
871 
872     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
873     currentStartPos = headerMainSize_;
874     int32_t i = 0;
875     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
876     for (auto& pos : itemPosition_) {
877         float len = pos.second.endPos - pos.second.startPos;
878         pos.second.startPos = currentStartPos;
879         pos.second.endPos = currentStartPos + len;
880         i++;
881         if (i % lanes == 0) {
882             currentStartPos = pos.second.endPos + spaceWidth_;
883         }
884     }
885 }
886 
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)887 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
888     const LayoutConstraintF& layoutConstraint, int32_t endIndex)
889 {
890     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
891     float currentEndPos = endPos_ - contentEndOffset_;
892     if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
893         currentEndPos -= footerMainSize_;
894     }
895 
896     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
897     if (LessNotEqual(currentEndPos, endPos_)) {
898         MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
899     }
900 
901     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
902     float currentStartPos = headerMainSize_;
903     int32_t i = 0;
904     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
905     for (auto& pos : itemPosition_) {
906         float len = pos.second.endPos - pos.second.startPos;
907         pos.second.startPos = currentStartPos;
908         pos.second.endPos = currentStartPos + len;
909         i++;
910         if (i % lanes == 0) {
911             currentStartPos = pos.second.endPos + spaceWidth_;
912         }
913     }
914 }
915 
CheckJumpForwardForBigOffset(int32_t & startIndex,float & startPos)916 bool ListItemGroupLayoutAlgorithm::CheckJumpForwardForBigOffset(int32_t& startIndex, float& startPos)
917 {
918     if (!isNeedCheckOffset_ || childrenSize_) {
919         return false;
920     }
921     float th = startPos_ - (endPos_ - startPos_) - referencePos_;
922     if (GreatNotEqual(startPos, th)) {
923         return false;
924     }
925     float averageHeight = groupItemAverageHeight_;
926     if (layoutedItemInfo_.has_value()) {
927         const auto& itemInfo = layoutedItemInfo_.value();
928         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
929         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
930         averageHeight = totalHeight / itemCount;
931     }
932     if (Positive(averageHeight)) {
933         float distance = startPos_ - referencePos_ - startPos;
934         int32_t jumpCount = distance / averageHeight;
935         int32_t maxCount = totalItemCount_ - 1 - startIndex;
936         if (jumpCount > maxCount) {
937             float pos = startPos + maxCount * averageHeight;
938             itemPosition_[totalItemCount_ - 1] = { -1, pos, pos + averageHeight - spaceWidth_ };
939             return true;
940         }
941         startPos += jumpCount * averageHeight;
942         startIndex += jumpCount;
943     }
944     return false;
945 }
946 
CheckJumpBackwardForBigOffset(int32_t & endIndex,float & endPos)947 bool ListItemGroupLayoutAlgorithm::CheckJumpBackwardForBigOffset(int32_t& endIndex, float& endPos)
948 {
949     if (!isNeedCheckOffset_ || childrenSize_) {
950         return false;
951     }
952     float th = endPos_ + (endPos_ - startPos_) - (referencePos_ - totalMainSize_);
953     if (LessNotEqual(endPos, th)) {
954         return false;
955     }
956     float averageHeight = groupItemAverageHeight_;
957     if (layoutedItemInfo_.has_value()) {
958         const auto& itemInfo = layoutedItemInfo_.value();
959         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
960         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
961         averageHeight = totalHeight / itemCount;
962     }
963     if (Positive(averageHeight)) {
964         float distance = endPos - (endPos_ - (referencePos_ - totalMainSize_));
965         int32_t jumpCount = distance / averageHeight;
966         if (jumpCount > endIndex) {
967             float pos = endPos - endIndex * averageHeight;
968             itemPosition_[0] = { -1, pos - averageHeight + spaceWidth_, pos };
969             return true;
970         }
971         endPos -= jumpCount * averageHeight;
972         endIndex -= jumpCount;
973     }
974     return false;
975 }
976 
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)977 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
978     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
979 {
980     float currentEndPos = startPos;
981     float currentStartPos = 0.0f;
982     int32_t currentIndex = startIndex - 1;
983     while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
984         if (ReachResponseDeadline(layoutWrapper)) {
985             measureInNextFrame_ = true;
986             return;
987         }
988         currentStartPos = currentEndPos;
989         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
990             currentStartPos, currentEndPos);
991         if (count == 0) {
992             break;
993         }
994         if (currentIndex < (totalItemCount_ - 1)) {
995             currentEndPos += spaceWidth_;
996         }
997         if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
998             startPos_ = prevStartPos_;
999             endPos_ = prevEndPos_;
1000             targetIndex_.reset();
1001         }
1002     }
1003 
1004     currentStartPos = startPos - spaceWidth_;
1005     currentIndex = startIndex;
1006     float th = startPos_ - referencePos_;
1007     if (!prevMeasureBreak_) {
1008         th = std::max(th, headerMainSize_);
1009     }
1010     while (currentIndex > 0  && GreatNotEqual(currentStartPos, th)) {
1011         if (ReachResponseDeadline(layoutWrapper)) {
1012             measureInNextFrame_ = true;
1013             return;
1014         }
1015         currentEndPos = currentStartPos;
1016         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
1017             currentEndPos, currentStartPos);
1018         if (count == 0) {
1019             break;
1020         }
1021         if (currentIndex > 0) {
1022             currentStartPos = currentStartPos - spaceWidth_;
1023         }
1024     }
1025 }
1026 
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)1027 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
1028     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
1029 {
1030     float currentStartPos = endPos;
1031     float currentEndPos = 0.0f;
1032     auto currentIndex = endIndex + 1;
1033     while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
1034         if (ReachResponseDeadline(layoutWrapper)) {
1035             measureInNextFrame_ = true;
1036             return;
1037         }
1038         currentEndPos = currentStartPos;
1039         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
1040             currentEndPos, currentStartPos);
1041         if (count == 0) {
1042             break;
1043         }
1044         if (currentIndex > 0) {
1045             currentStartPos = currentStartPos - spaceWidth_;
1046         }
1047         if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
1048             startPos_ = prevStartPos_;
1049             endPos_ = prevEndPos_;
1050             targetIndex_.reset();
1051         }
1052     }
1053     currentIndex = endIndex;
1054     currentEndPos = endPos + spaceWidth_;
1055     while (childrenSize_ && LessOrEqual(currentEndPos, endPos_ - (referencePos_ - totalMainSize_))) {
1056         if (ReachResponseDeadline(layoutWrapper)) {
1057             measureInNextFrame_ = true;
1058             return;
1059         }
1060         currentStartPos = currentEndPos;
1061         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
1062             currentStartPos, currentEndPos);
1063         if (count == 0) {
1064             break;
1065         }
1066         if (currentIndex < (totalItemCount_ - 1)) {
1067             currentEndPos += spaceWidth_;
1068         }
1069     }
1070 }
1071 
ModifyReferencePos(int32_t index,float pos)1072 void ListItemGroupLayoutAlgorithm::ModifyReferencePos(int32_t index, float pos)
1073 {
1074     if (!childrenSize_ || !needAdjustRefPos_) {
1075         return;
1076     }
1077     if (forwardLayout_ && Negative(referencePos_)) {
1078         float offset = referencePos_ + pos;
1079         float newReferencePos = offset - posMap_->GetPos(index);
1080         refPos_ = refPos_ + newReferencePos - referencePos_;
1081         referencePos_ = newReferencePos;
1082     } else if (!forwardLayout_ && GreatNotEqual(referencePos_, prevContentMainSize_)) {
1083         float offset = referencePos_ - posMap_->GetPrevTotalHeight() + pos - prevContentMainSize_;
1084         float newReferencePos = offset + endPos_ - startPos_ + totalMainSize_ -
1085             (posMap_->GetPos(index) + posMap_->GetRowHeight(index));
1086         refPos_ = refPos_ + newReferencePos - referencePos_;
1087         referencePos_ = newReferencePos;
1088     }
1089 }
1090 
AdjustByPosMap()1091 void ListItemGroupLayoutAlgorithm::AdjustByPosMap()
1092 {
1093     totalMainSize_ = posMap_->GetTotalHeight();
1094     if (itemPosition_.empty()) {
1095         return;
1096     }
1097     float startPos = itemPosition_.begin()->second.startPos;
1098     float offset = posMap_->GetGroupLayoutOffset(GetStartIndex(), startPos);
1099     for (auto& pos : itemPosition_) {
1100         pos.second.startPos += offset;
1101         pos.second.endPos += offset;
1102     }
1103 }
1104 
AdjustItemPosition()1105 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
1106 {
1107     if (itemPosition_.empty()) {
1108         return;
1109     }
1110     float currentStartPos = GetStartPosition();
1111     if (currentStartPos < headerMainSize_) {
1112         auto delta = headerMainSize_ - currentStartPos;
1113         for (auto& pos : itemPosition_) {
1114             pos.second.startPos += delta;
1115             pos.second.endPos += delta;
1116         }
1117         for (auto& pos : cachedItemPosition_) {
1118             pos.second.startPos += delta;
1119             pos.second.endPos += delta;
1120         }
1121         totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
1122         adjustReferenceDelta_ = -delta;
1123     } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
1124         auto delta = currentStartPos - headerMainSize_;
1125         for (auto& pos : itemPosition_) {
1126             pos.second.startPos -= delta;
1127             pos.second.endPos -= delta;
1128         }
1129         for (auto& pos : cachedItemPosition_) {
1130             pos.second.startPos -= delta;
1131             pos.second.endPos -= delta;
1132         }
1133         totalMainSize_ -= delta;
1134         adjustReferenceDelta_ = delta;
1135     }
1136     if (GetEndIndex() == totalItemCount_ - 1) {
1137         totalMainSize_ = GetEndPosition() + footerMainSize_;
1138     } else {
1139         float endPos = GetCacheEndIndex() > GetEndIndex() ? GetCacheEndPosition() : GetEndPosition();
1140         totalMainSize_ = std::max(totalMainSize_, endPos + footerMainSize_);
1141     }
1142 }
1143 
UpdateLayoutedItemInfo()1144 void ListItemGroupLayoutAlgorithm::UpdateLayoutedItemInfo()
1145 {
1146     if (itemPosition_.empty()) {
1147         return;
1148     }
1149     int32_t startIndex = INT_MAX;
1150     int32_t endIndex = -1;
1151     float startPos = FLT_MAX;
1152     float endPos = -1.0f;
1153     if (!itemPosition_.empty()) {
1154         startIndex = itemPosition_.begin()->first;
1155         endIndex = itemPosition_.rbegin()->first;
1156         startPos = itemPosition_.begin()->second.startPos;
1157         endPos = itemPosition_.rbegin()->second.endPos;
1158     }
1159     if (layoutedItemInfo_.has_value()) {
1160         auto& itemInfo = layoutedItemInfo_.value();
1161         auto prevStartIndex = itemInfo.startIndex;
1162         if (startIndex <= itemInfo.startIndex || LessNotEqual(startPos, itemInfo.startPos) ||
1163             startIndex > itemInfo.endIndex) {
1164             itemInfo.startIndex = startIndex;
1165             itemInfo.startPos = startPos;
1166         }
1167         if (endIndex >= itemInfo.endIndex || GreatNotEqual(endPos, itemInfo.endPos) ||
1168             itemInfo.endIndex > totalItemCount_ - 1 || endIndex < prevStartIndex) {
1169             itemInfo.endIndex = endIndex;
1170             itemInfo.endPos = endPos;
1171         }
1172     } else {
1173         layoutedItemInfo_ = { startIndex, startPos, endIndex, endPos };
1174     }
1175 }
1176 
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)1177 void ListItemGroupLayoutAlgorithm::CheckRecycle(
1178     const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
1179 {
1180     referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
1181     // Mark inactive in wrapper.
1182     if (forwardLayout) {
1183         for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1184             if (GreatOrEqual(pos->second.endPos, startPos - referencePos)) {
1185                 break;
1186             }
1187             recycledItemPosition_.insert_or_assign(pos->first, pos->second);
1188             cachedItemPosition_.insert(*pos);
1189             pos = itemPosition_.erase(pos);
1190         }
1191         return;
1192     }
1193     std::list<int32_t> removeIndexes;
1194     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1195         if (LessOrEqual(pos->second.startPos, endPos - (referencePos - totalMainSize_))) {
1196             break;
1197         }
1198         recycledItemPosition_.insert_or_assign(pos->first, pos->second);
1199         cachedItemPosition_.insert(*pos);
1200         removeIndexes.emplace_back(pos->first);
1201     }
1202     for (const auto& index : removeIndexes) {
1203         itemPosition_.erase(index);
1204     }
1205 }
1206 
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1207 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
1208     const OffsetF& paddingOffset, float crossSize)
1209 {
1210     // layout items.
1211     for (auto& pos : itemPosition_) {
1212         auto wrapper = GetListItem(layoutWrapper, pos.first);
1213         if (!wrapper) {
1214             ReportGetChildError("LayoutListItem", pos.first);
1215             continue;
1216         }
1217 
1218         auto offset = paddingOffset;
1219         int32_t laneIndex = pos.first % lanes_;
1220         float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1221         float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
1222         auto startPos = !isStackFromEnd_ ? pos.second.startPos : totalMainSize_ - pos.second.endPos;
1223         auto endPos = !isStackFromEnd_ ? pos.second.endPos : totalMainSize_ - pos.second.startPos;
1224         if (layoutDirection_ == TextDirection::RTL) {
1225             if (axis_ == Axis::VERTICAL) {
1226                 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1227                 auto tmpX = crossSize - laneCrossOffset -
1228                     ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
1229                 offset = offset + OffsetF(tmpX, startPos);
1230             } else {
1231                 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
1232                 offset = offset + OffsetF(totalMainSize_ - endPos, tmpY);
1233             }
1234         } else {
1235             if (axis_ == Axis::VERTICAL) {
1236                 offset =
1237                     offset + OffsetF(0, startPos) + OffsetF(laneCrossOffset, 0) +
1238                     OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
1239             } else {
1240                 offset =
1241                     offset + OffsetF(startPos, 0) + OffsetF(0, laneCrossOffset) +
1242                     OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
1243             }
1244         }
1245         auto index = isStackFromEnd_ ? totalItemCount_ - pos.first - 1 : pos.first;
1246         SetListItemIndex(layoutWrapper, wrapper, index);
1247         wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1248         if (wrapper->CheckNeedForceMeasureAndLayout() || wrapper->IsIgnoreOptsValid()) {
1249             wrapper->Layout();
1250         } else {
1251             SyncGeometry(wrapper);
1252         }
1253         auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1254         if (frameNode) {
1255             frameNode->MarkAndCheckNewOpIncNode(axis_);
1256         }
1257     }
1258 }
1259 
UpdateZIndex(const RefPtr<LayoutWrapper> & layoutWrapper)1260 void ListItemGroupLayoutAlgorithm::UpdateZIndex(const RefPtr<LayoutWrapper>& layoutWrapper)
1261 {
1262     auto host = layoutWrapper->GetHostNode();
1263     CHECK_NULL_VOID(host);
1264     auto renderContext = host->GetRenderContext();
1265     CHECK_NULL_VOID(renderContext);
1266     renderContext->UpdateZIndex(1);
1267 }
1268 
LayoutHeaderFooterRTL(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1269 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterRTL(LayoutWrapper* layoutWrapper,
1270     const OffsetF& paddingOffset, float crossSize)
1271 {
1272     OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1273     selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1274     float mainPos = GetMainAxisOffset(selfOffset, axis_);
1275     float footerMainSize = 0.0f;
1276     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1277     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1278         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1279     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1280         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1281     if (footerWrapper) {
1282         UpdateZIndex(footerWrapper);
1283         footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1284         float footerPos = 0.0f;
1285         if (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER) {
1286             contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1287             float stickyPos = contentStartOffset_ - mainPos;
1288             stickyPos = std::min(stickyPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1289             footerPos = std::max(footerPos, stickyPos);
1290             footerPos = std::min(footerPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1291         }
1292         LayoutIndex(footerWrapper, paddingOffset, crossSize, footerPos);
1293         endFooterPos_ = endFooterPos_ > mainPos ? mainPos : endFooterPos_;
1294     }
1295 
1296     if (headerWrapper) {
1297         float headPos = totalMainSize_ - headerMainSize_;
1298         UpdateZIndex(headerWrapper);
1299         float const listMainSize = endPos_ - startPos_;
1300         if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER)) {
1301             auto headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1302             float stickyPos = listMainSize - contentEndOffset_ - mainPos - headerMainSize;
1303             if (stickyPos < footerMainSize) {
1304                 stickyPos = footerMainSize;
1305             }
1306             if (stickyPos < headPos) {
1307                 headPos = stickyPos;
1308             }
1309         }
1310         LayoutIndex(headerWrapper, paddingOffset, crossSize, headPos);
1311         startHeaderPos_ = mainPos + totalMainSize_ - headerMainSize_ - listMainSize;
1312     }
1313 }
1314 
LayoutHeaderFooterLTR(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1315 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterLTR(LayoutWrapper* layoutWrapper,
1316     const OffsetF& paddingOffset, float crossSize)
1317 {
1318     OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1319     selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1320     float mainPos = GetMainAxisOffset(selfOffset, axis_);
1321     float headerMainSize = 0.0f;
1322     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1323     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1324         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1325     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1326         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1327     if (headerWrapper) {
1328         UpdateZIndex(headerWrapper);
1329         headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1330         float headerPos = 0.0f;
1331         if (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) {
1332             contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1333             float stickyPos = contentStartOffset_ - mainPos;
1334             stickyPos = std::min(stickyPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1335             headerPos = std::max(headerPos, stickyPos);
1336         }
1337         LayoutIndex(headerWrapper, paddingOffset, crossSize, headerPos);
1338         startHeaderPos_ = startHeaderPos_ > mainPos ? mainPos : startHeaderPos_;
1339     }
1340 
1341     if (footerWrapper) {
1342         float endPos = totalMainSize_ - footerMainSize_;
1343         UpdateZIndex(footerWrapper);
1344         float const listMainSize = endPos_ - startPos_;
1345         if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
1346             auto footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1347             float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
1348             if (stickyPos < headerMainSize) {
1349                 stickyPos = headerMainSize;
1350             }
1351             if (stickyPos < endPos) {
1352                 endPos = stickyPos;
1353             }
1354         }
1355         LayoutIndex(footerWrapper, paddingOffset, crossSize, endPos);
1356         endFooterPos_ = mainPos + totalMainSize_ - footerMainSize_ - listMainSize;
1357     }
1358 }
1359 
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)1360 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
1361     float crossSize, float startPos)
1362 {
1363     CHECK_NULL_VOID(wrapper);
1364     auto offset = paddingOffset;
1365     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1366     float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1367     if (axis_ == Axis::VERTICAL) {
1368         if (layoutDirection_ == TextDirection::RTL) {
1369             auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1370             offset = offset + OffsetF(crossSize - laneCrossOffset - size.Width(), startPos);
1371         } else {
1372             offset = offset + OffsetF(laneCrossOffset, startPos);
1373         }
1374     } else {
1375         offset = offset + OffsetF(startPos, laneCrossOffset);
1376     }
1377     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1378     wrapper->Layout();
1379 }
1380 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1381 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1382 {
1383     float delta = crossSize - GetLaneGutter() - childCrossSize;
1384     if (LessOrEqual(delta, 0.0f)) {
1385         return 0.0f;
1386     }
1387     switch (itemAlign_) {
1388         case OHOS::Ace::V2::ListItemAlign::START:
1389             return 0.0f;
1390         case OHOS::Ace::V2::ListItemAlign::CENTER:
1391             return delta / 2; /* 2:average */
1392         case OHOS::Ace::V2::ListItemAlign::END:
1393             return delta;
1394         default:
1395             return 0.0f;
1396     }
1397 }
1398 
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)1399 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
1400     const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
1401 {
1402     int32_t lanes = layoutProperty->GetLanes().value_or(1);
1403     lanes = lanes > 1 ? lanes : 1;
1404     if (crossSizeOptional.has_value()) {
1405         if (layoutProperty->GetLaneMinLength().has_value()) {
1406             minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
1407                 layoutConstraint.scaleProperty, crossSizeOptional.value());
1408         }
1409         if (layoutProperty->GetLaneMaxLength().has_value()) {
1410             maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
1411                 layoutConstraint.scaleProperty, crossSizeOptional.value());
1412         }
1413         if (layoutProperty->GetLaneGutter().has_value()) {
1414             auto laneGutter = ConvertToPx(
1415                 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
1416             laneGutter_ = laneGutter.value_or(0.0f);
1417         }
1418     }
1419     lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
1420         minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
1421 }
1422 
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)1423 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
1424     const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
1425 {
1426     auto host = itemLayoutWrapper->GetHostNode();
1427     CHECK_NULL_VOID(host);
1428     auto listItem = host->GetPattern<ListItemPattern>();
1429     CHECK_NULL_VOID(listItem);
1430     listItem->SetIndexInListItemGroup(indexInGroup);
1431 
1432     host = groupLayoutWrapper->GetHostNode();
1433     CHECK_NULL_VOID(host);
1434     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1435     CHECK_NULL_VOID(listItemGroup);
1436     listItem->SetIndexInList(listItemGroup->GetIndexInList());
1437 }
1438 
GetLayoutInfo() const1439 ListItemGroupLayoutInfo ListItemGroupLayoutAlgorithm::GetLayoutInfo() const
1440 {
1441     ListItemGroupLayoutInfo info;
1442     info.headerSize = headerMainSize_;
1443     info.footerSize = footerMainSize_;
1444     info.spaceWidth = spaceWidth_;
1445     if (totalItemCount_ == 0 || childrenSize_) {
1446         info.atStart = true;
1447         info.atEnd = true;
1448         return info;
1449     }
1450     if (layoutedItemInfo_.has_value()) {
1451         const auto& itemInfo = layoutedItemInfo_.value();
1452         info.atStart = itemInfo.startIndex == 0;
1453         info.atEnd = itemInfo.endIndex >= totalItemCount_ - 1;
1454         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
1455         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
1456         info.averageHeight = totalHeight / itemCount;
1457     }
1458     return info;
1459 }
1460 
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)1461 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
1462 {
1463     auto host = groupLayoutWrapper->GetHostNode();
1464     CHECK_NULL_RETURN(host, false);
1465     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1466     CHECK_NULL_RETURN(listItemGroup, false);
1467     return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
1468 }
1469 
MeasureCacheForward(LayoutWrapper * layoutWrapper,ListItemGroupCacheParam & param)1470 void ListItemGroupLayoutAlgorithm::MeasureCacheForward(LayoutWrapper* layoutWrapper, ListItemGroupCacheParam& param)
1471 {
1472     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
1473     int32_t endIndex = itemPosition_.empty() ? -1 : GetEndIndex();
1474     if (endIndex >= totalItemCount_ - 1) {
1475         return;
1476     }
1477     int32_t limit = std::min(endIndex + param.cacheCountForward * lanes_, totalItemCount_ - 1);
1478     float startPos = itemPosition_.empty() ? headerMainSize_ : GetEndPosition();
1479     int32_t curIndex = GetLanesFloor(endIndex + 1);
1480     while (curIndex <= limit) {
1481         if (GetSysTimestamp() > param.deadline) {
1482             return;
1483         }
1484         float mainLen = 0.0f;
1485         int32_t cnt = 0;
1486         for (int32_t i = 0; i < lanes && curIndex + i < totalItemCount_; i++) {
1487             auto wrapper = GetListItem(layoutWrapper, curIndex + i, param.show, !param.show);
1488             if (!wrapper || !wrapper->GetHostNode()) {
1489                 return;
1490             }
1491             if (!wrapper->GetHostNode()->RenderCustomChild(param.deadline)) {
1492                 pauseMeasureCacheItem_ = curIndex + i;
1493                 return;
1494             }
1495             cnt++;
1496             if (CheckNeedMeasure(wrapper)) {
1497                 ACE_SCOPED_TRACE("ListItemGroupLayoutAlgorithm::MeasureCacheForward:%d", curIndex + i);
1498                 wrapper->Measure(childLayoutConstraint_);
1499             }
1500             mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
1501         }
1502         for (int32_t i = 0; i < cnt; i++) {
1503             cachedItemPosition_[curIndex + i] = { -1, startPos, startPos + mainLen };
1504         }
1505         curIndex += cnt;
1506         startPos += mainLen + spaceWidth_;
1507         param.forwardCachedIndex = curIndex - 1;
1508     }
1509 }
1510 
MeasureCacheBackward(LayoutWrapper * layoutWrapper,ListItemGroupCacheParam & param)1511 void ListItemGroupLayoutAlgorithm::MeasureCacheBackward(LayoutWrapper* layoutWrapper, ListItemGroupCacheParam& param)
1512 {
1513     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
1514     int32_t startIndex = itemPosition_.empty() ? totalItemCount_ : GetStartIndex();
1515     if (startIndex <= 0) {
1516         return;
1517     }
1518     int32_t limit = std::max(startIndex - param.cacheCountBackward * lanes_, 0);
1519     if (limit % lanes_ != 0) {
1520         limit += (lanes_ - limit % lanes_);
1521     }
1522     float endPos = itemPosition_.empty() ? totalMainSize_ - footerMainSize_ : GetStartPosition();
1523     int32_t curIndex = GetLanesCeil(startIndex - 1);
1524     while (curIndex >= limit) {
1525         if (GetSysTimestamp() > param.deadline) {
1526             return;
1527         }
1528         float mainLen = 0.0f;
1529         int32_t cnt = 0;
1530         for (int32_t i = 0; i < lanes && curIndex - i >= 0; i++) {
1531             auto wrapper = GetListItem(layoutWrapper, curIndex - i, param.show, !param.show);
1532             if (!wrapper || !wrapper->GetHostNode()) {
1533                 return;
1534             }
1535             if (!wrapper->GetHostNode()->RenderCustomChild(param.deadline)) {
1536                 pauseMeasureCacheItem_ = curIndex + i;
1537                 return;
1538             }
1539             cnt++;
1540             if (CheckNeedMeasure(wrapper)) {
1541                 ACE_SCOPED_TRACE("ListItemGroupLayoutAlgorithm::MeasureCacheForward:%d", curIndex - i);
1542                 wrapper->Measure(childLayoutConstraint_);
1543             }
1544             mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
1545             if (0 == (curIndex - i) % lanes) {
1546                 break;
1547             }
1548         }
1549         for (int32_t i = 0; i < cnt; i++) {
1550             cachedItemPosition_[curIndex - i] = { -1, endPos - mainLen, endPos };
1551         }
1552         curIndex -= cnt;
1553         endPos -= (mainLen + spaceWidth_);
1554         param.backwardCachedIndex = curIndex + 1;
1555     }
1556 }
1557 
MeasureCacheItem(LayoutWrapper * layoutWrapper)1558 void ListItemGroupLayoutAlgorithm::MeasureCacheItem(LayoutWrapper* layoutWrapper)
1559 {
1560     pauseMeasureCacheItem_ = -1;
1561     ListItemGroupCacheParam& param = cacheParam_.value();
1562     if (param.forward) {
1563         MeasureCacheForward(layoutWrapper, param);
1564     }
1565     if (param.backward) {
1566         MeasureCacheBackward(layoutWrapper, param);
1567     }
1568     if (cachedItemPosition_.empty()) {
1569         return;
1570     }
1571     float originHeight = totalMainSize_;
1572     float currentStartPos = GetCacheStartPosition();
1573     if (currentStartPos < headerMainSize_) {
1574         auto delta = headerMainSize_ - currentStartPos;
1575         for (auto& pos : itemPosition_) {
1576             pos.second.startPos += delta;
1577             pos.second.endPos += delta;
1578         }
1579         for (auto& pos : cachedItemPosition_) {
1580             pos.second.startPos += delta;
1581             pos.second.endPos += delta;
1582         }
1583         totalMainSize_ = std::max(totalMainSize_ + delta,
1584             std::max(GetCacheEndPosition(), GetEndPosition()) + footerMainSize_);
1585         adjustReferenceDelta_ = -delta;
1586     } else if (GetCacheStartIndex() == 0 && currentStartPos > headerMainSize_) {
1587         auto delta = currentStartPos - headerMainSize_;
1588         for (auto& pos : itemPosition_) {
1589             pos.second.startPos -= delta;
1590             pos.second.endPos -= delta;
1591         }
1592         for (auto& pos : cachedItemPosition_) {
1593             pos.second.startPos -= delta;
1594             pos.second.endPos -= delta;
1595         }
1596         totalMainSize_ -= delta;
1597         adjustReferenceDelta_ = delta;
1598     }
1599     if (GetCacheEndIndex() == totalItemCount_ - 1) {
1600         totalMainSize_ = GetCacheEndPosition() + footerMainSize_;
1601     } else {
1602         float endPos = GetCacheEndIndex() > GetEndIndex() || itemPosition_.empty() ?
1603             GetCacheEndPosition() : GetEndPosition();
1604         totalMainSize_ = std::max(totalMainSize_, endPos + footerMainSize_);
1605     }
1606     adjustTotalSize_ = totalMainSize_ - originHeight;
1607 }
1608 
LayoutCacheItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize,bool show)1609 void ListItemGroupLayoutAlgorithm::LayoutCacheItem(LayoutWrapper* layoutWrapper,
1610     const OffsetF& paddingOffset, float crossSize, bool show)
1611 {
1612     for (auto& pos : cachedItemPosition_) {
1613         auto wrapper = GetListItem(layoutWrapper, pos.first, show, !show);
1614         if (!wrapper) {
1615             continue;
1616         }
1617         auto offset = paddingOffset;
1618         int32_t laneIndex = pos.first % lanes_;
1619         float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1620         float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
1621         auto startPos = !isStackFromEnd_ ? pos.second.startPos : totalMainSize_ - pos.second.endPos;
1622         auto endPos = !isStackFromEnd_ ? pos.second.endPos : totalMainSize_ - pos.second.startPos;
1623         if (layoutDirection_ == TextDirection::RTL) {
1624             if (axis_ == Axis::VERTICAL) {
1625                 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1626                 auto tmpX = crossSize - laneCrossOffset -
1627                     ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
1628                 offset = offset + OffsetF(tmpX, startPos);
1629             } else {
1630                 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
1631                 offset = offset + OffsetF(totalMainSize_ - endPos, tmpY);
1632             }
1633         } else {
1634             if (axis_ == Axis::VERTICAL) {
1635                 offset =
1636                     offset + OffsetF(0, startPos) + OffsetF(laneCrossOffset, 0) +
1637                     OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
1638             } else {
1639                 offset =
1640                     offset + OffsetF(startPos, 0) + OffsetF(0, laneCrossOffset) +
1641                     OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
1642             }
1643         }
1644         auto index = isStackFromEnd_ ? totalItemCount_ - pos.first - 1 : pos.first;
1645         SetListItemIndex(layoutWrapper, wrapper, index);
1646         wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1647         auto host = wrapper->GetHostNode();
1648         if (wrapper->CheckNeedForceMeasureAndLayout() && host && !host->IsLayoutComplete()) {
1649             wrapper->Layout();
1650         } else {
1651             SyncGeometry(wrapper);
1652         }
1653     }
1654 }
1655 
ReverseItemPosition(ListItemGroupLayoutAlgorithm::PositionMap & itemPosition,int32_t totalItemCount,float mainSize)1656 void ListItemGroupLayoutAlgorithm::ReverseItemPosition(
1657     ListItemGroupLayoutAlgorithm::PositionMap& itemPosition, int32_t totalItemCount, float mainSize)
1658 {
1659     if (!isStackFromEnd_ || itemPosition.empty()) {
1660         return;
1661     }
1662     ListItemGroupLayoutAlgorithm::PositionMap posMap;
1663     for (auto pos : itemPosition) {
1664         auto startPos = mainSize - pos.second.endPos;
1665         auto endPos = mainSize - pos.second.startPos;
1666         pos.second.startPos = startPos;
1667         pos.second.endPos = endPos;
1668         posMap[totalItemCount - pos.first - 1] = pos.second;
1669     }
1670     itemPosition = std::move(posMap);
1671 }
1672 
ReverseLayoutedItemInfo(int32_t totalItemCount,float mainSize)1673 void ListItemGroupLayoutAlgorithm::ReverseLayoutedItemInfo(int32_t totalItemCount, float mainSize)
1674 {
1675     if (!isStackFromEnd_ || !layoutedItemInfo_.has_value()) {
1676         return;
1677     }
1678     auto& itemInfo = layoutedItemInfo_.value();
1679     auto startIndex = totalItemCount - itemInfo.endIndex - 1;
1680     auto startPos = mainSize - itemInfo.endPos;
1681     auto endIndex = totalItemCount - itemInfo.startIndex - 1;
1682     auto endPos = mainSize - itemInfo.startPos;
1683     layoutedItemInfo_ = { startIndex, startPos, endIndex, endPos };
1684 }
1685 
ReportGetChildError(const std::string & funcName,int32_t index) const1686 void ListItemGroupLayoutAlgorithm::ReportGetChildError(const std::string& funcName, int32_t index) const
1687 {
1688     if (index < 0 || index > totalItemCount_ - 1) {
1689         return;
1690     }
1691     std::string subErrorType = funcName + " get item: " + std::to_string(index) + " failed.";
1692     EventReport::ReportScrollableErrorEvent("ListItemGroup", ScrollableErrorType::GET_CHILD_FAILED, subErrorType);
1693 }
1694 
IsRoundingMode(LayoutWrapper * layoutWrapper)1695 bool ListItemGroupLayoutAlgorithm::IsRoundingMode(LayoutWrapper* layoutWrapper)
1696 {
1697     auto host = layoutWrapper->GetHostNode();
1698     CHECK_NULL_RETURN(host, false);
1699     auto pipeline = host->GetContext();
1700     CHECK_NULL_RETURN(pipeline, false);
1701     return pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE;
1702 }
1703 
ResetLayoutItem(LayoutWrapper * layoutWrapper)1704 void ListItemGroupLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1705 {
1706     for (auto& pos : recycledItemPosition_) {
1707         auto wrapper = GetListItem(layoutWrapper, pos.first);
1708         auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper);
1709         if (wrapperFrameNode) {
1710             wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1711         }
1712     }
1713 }
1714 } // namespace OHOS::Ace::NG
1715