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