• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/list/list_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     CHECK_NULL_VOID(listLayoutProperty_);
59     auto layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
60     CHECK_NULL_VOID(layoutProperty);
61     axis_ = listLayoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
62     layoutDirection_ = listLayoutProperty_->GetNonAutoLayoutDirection();
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     auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
67     auto contentIdealSize = CreateIdealSize(
68         contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
69 
70     auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
71     auto space = layoutProperty->GetSpace().value_or(Dimension(0));
72 
73     auto layoutConstraint = layoutProperty->GetLayoutConstraint().value();
74     CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
75     childLayoutConstraint_ = layoutProperty->CreateChildConstraint();
76     isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
77     if (isCardStyle_) {
78         auto maxWidth = GetListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty) -
79                         layoutProperty->CreatePaddingAndBorder().Width();
80         contentIdealSize.SetCrossSize(maxWidth, axis_);
81     }
82     UpdateListItemConstraint(contentIdealSize, childLayoutConstraint_);
83     referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
84     totalItemCount_ = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
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     }
112     childrenSize_ ? AdjustByPosMap() : AdjustItemPosition();
113 
114     auto crossSize = contentIdealSize.CrossSize(axis_);
115     if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
116         contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
117     }
118     contentIdealSize.SetMainSize(totalMainSize_, axis_);
119     AddPaddingToSize(padding, contentIdealSize);
120     layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
121     layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
122 }
123 
GetListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty)124 float ListItemGroupLayoutAlgorithm::GetListItemGroupMaxWidth(
125     const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty)
126 {
127     RefPtr<GridColumnInfo> columnInfo;
128     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
129     columnInfo->GetParent()->BuildColumnWidth();
130     auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
131     auto parentWidth = parentIdealSize.CrossSize(axis_).value() + layoutProperty->CreatePaddingAndBorder().Width();
132     auto maxWidth = std::min(parentWidth, maxGridWidth);
133     if (LessNotEqual(maxGridWidth, layoutProperty->CreatePaddingAndBorder().Width())) {
134         TAG_LOGI(AceLogTag::ACE_LIST,
135             "ListItemGroup reset to parentWidth since grid_col width:%{public}f, border:%{public}f",
136             maxGridWidth, layoutProperty->CreatePaddingAndBorder().Width());
137         maxWidth = parentWidth;
138     }
139     return maxWidth;
140 }
141 
Layout(LayoutWrapper * layoutWrapper)142 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
143 {
144     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
145     CHECK_NULL_VOID(layoutProperty);
146     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
147     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
148     MinusPaddingToSize(padding, size);
149     auto left = padding.left.value_or(0.0f);
150     auto top = padding.top.value_or(0.0f);
151     auto paddingOffset = OffsetF(left, top);
152     float crossSize = GetCrossAxisSize(size, axis_);
153     CHECK_NULL_VOID(listLayoutProperty_);
154     itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
155     SetActiveChildRange(layoutWrapper, listLayoutProperty_->GetCachedCountWithDefault());
156 
157     if (headerIndex_ >= 0 || footerIndex_ >= 0) {
158         if (layoutDirection_ == TextDirection::RTL && axis_ == Axis::HORIZONTAL) {
159             LayoutHeaderFooterRTL(layoutWrapper, paddingOffset, crossSize);
160         } else {
161             LayoutHeaderFooterLTR(layoutWrapper, paddingOffset, crossSize);
162         }
163     }
164     if (cacheParam_) {
165         LayoutCacheItem(layoutWrapper);
166         return;
167     }
168     // layout items.
169     LayoutListItem(layoutWrapper, paddingOffset, crossSize);
170 }
171 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)172 void ListItemGroupLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
173 {
174     CHECK_NULL_VOID(wrapper);
175     auto host = wrapper->GetHostNode();
176     CHECK_NULL_VOID(host);
177     host->ForceSyncGeometryNode();
178     host->ResetLayoutAlgorithm();
179 }
180 
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const181 bool ListItemGroupLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
182 {
183     if (layoutWrapper->CheckNeedForceMeasureAndLayout()) {
184         return true;
185     }
186     auto geometryNode = layoutWrapper->GetGeometryNode();
187     CHECK_NULL_RETURN(geometryNode, true);
188     auto constraint = geometryNode->GetParentLayoutConstraint();
189     CHECK_NULL_RETURN(constraint, true);
190     return constraint.value() != childLayoutConstraint_;
191 }
192 
MeasureHeaderFooter(LayoutWrapper * layoutWrapper)193 void ListItemGroupLayoutAlgorithm::MeasureHeaderFooter(LayoutWrapper* layoutWrapper)
194 {
195     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
196     auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
197     headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
198     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
199         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
200     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
201         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
202     if (headerWrapper) {
203         headerWrapper->Measure(headerFooterLayoutConstraint);
204         headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
205     }
206     if (footerWrapper) {
207         footerWrapper->Measure(headerFooterLayoutConstraint);
208         footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
209     }
210 }
211 
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheCount)212 void ListItemGroupLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper, int32_t cacheCount)
213 {
214     if (!itemPosition_.empty()) {
215         auto start = itemStartIndex_ + itemPosition_.begin()->first;
216         auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
217         layoutWrapper->SetActiveChildRange(start, end, cacheCount * lanes_, cacheCount * lanes_);
218         return;
219     }
220     auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
221     auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
222     if (LessNotEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_))) {
223         int32_t index = totalItemCount_ + itemStartIndex_;
224         layoutWrapper->SetActiveChildRange(index, index, cacheCount * lanes_, 0);
225     } else {
226         layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount * lanes_);
227     }
228 }
229 
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)230 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
231     LayoutConstraintF& contentConstraint)
232 {
233     contentConstraint.parentIdealSize = selfIdealSize;
234     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
235     auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
236     if (crossSizeOptional.has_value()) {
237         float crossSize = crossSizeOptional.value();
238         if (lanes_ > 1) {
239             crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
240             crossSize = crossSize <= 0 ? 1 : crossSize;
241         }
242         if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
243             crossSize = maxLaneLength_.value();
244         }
245         contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
246         contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
247         contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
248         if (minLaneLength_.has_value()) {
249             contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
250         }
251     }
252 }
253 
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)254 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
255 {
256     float maxCrossSize = 0.0f;
257     for (const auto& pos : itemPosition_) {
258         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
259         if (!wrapper) {
260             continue;
261         }
262         auto getGeometryNode = wrapper->GetGeometryNode();
263         if (!getGeometryNode) {
264             continue;
265         }
266         maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
267     }
268     return maxCrossSize;
269 }
270 
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)271 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
272     RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
273 {
274     const auto& padding = layoutProperty->CreatePaddingAndBorder();
275     const auto& margin = layoutProperty->CreateMargin();
276     auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
277     auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
278     offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
279     offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
280     forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
281     return referencePos;
282 }
283 
NeedMeasureItem(LayoutWrapper * layoutWrapper)284 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem(LayoutWrapper* layoutWrapper)
285 {
286     auto contentMainSize = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
287     if (NearZero(contentMainSize)) {
288         return true;
289     }
290     if (forwardLayout_) {
291         if (childrenSize_ && needAdjustRefPos_) {
292             referencePos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
293             refPos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
294         }
295         if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
296             return false;
297         }
298         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
299             if (totalItemCount_ > 0 &&
300                 (!layoutedItemInfo_ || layoutedItemInfo_.value().endIndex < totalItemCount_ - 1)) {
301                 return true;
302             } else {
303                 return false;
304             }
305         }
306     } else {
307         if (childrenSize_ && needAdjustRefPos_) {
308             referencePos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
309             refPos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
310         }
311         if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
312             return false;
313         }
314         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
315             return false;
316         }
317     }
318     return true;
319 }
320 
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)321 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
322     const LayoutConstraintF& layoutConstraint, float startPos)
323 {
324     int32_t currentIndex = -1;
325     float currentEndPos = startPos;
326     float currentStartPos = 0.0f;
327     while (currentIndex < totalItemCount_) {
328         currentStartPos = currentEndPos;
329         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
330             currentStartPos, currentEndPos);
331         if (count == 0) {
332             break;
333         }
334         if (currentIndex < (totalItemCount_ - 1)) {
335             currentEndPos += spaceWidth_;
336         }
337     }
338 }
339 
ClearItemPosition()340 void ListItemGroupLayoutAlgorithm::ClearItemPosition()
341 {
342     itemPosition_.clear();
343 }
344 
CheckNeedAllLayout(const RefPtr<LayoutWrapper> & layoutWrapper,bool forwardLayout)345 void ListItemGroupLayoutAlgorithm::CheckNeedAllLayout(const RefPtr<LayoutWrapper>& layoutWrapper, bool forwardLayout)
346 {
347     if (itemPosition_.empty()) {
348         needAllLayout_ = true;
349         return;
350     }
351     int32_t totalItemCount = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
352     if (!(forwardLayout && itemPosition_.rbegin()->first == totalItemCount - 1) &&
353         !(!forwardLayout && itemPosition_.begin()->first == 0)) {
354         needAllLayout_ = true;
355     }
356 }
357 
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)358 void ListItemGroupLayoutAlgorithm::MeasureListItem(
359     LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
360 {
361     if (totalItemCount_ <= 0) {
362         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
363             adjustReferenceDelta_ = totalMainSize_ - (headerMainSize_ + footerMainSize_);
364         }
365         totalMainSize_ = headerMainSize_ + footerMainSize_;
366         itemPosition_.clear();
367         layoutedItemInfo_.reset();
368         return;
369     }
370     int32_t startIndex = 0;
371     int32_t endIndex = totalItemCount_ - 1;
372     float startPos = headerMainSize_;
373     float endPos = totalMainSize_ - footerMainSize_;
374     prevStartPos_ = startPos_;
375     prevEndPos_ = endPos_;
376     if (needAllLayout_) {
377         needAllLayout_ = false;
378         itemPosition_.clear();
379         LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
380         return;
381     }
382     if (targetIndex_) {
383         startPos_ = -Infinity<float>();
384         endPos_ = Infinity<float>();
385     }
386     if (jumpIndex_.has_value()) {
387         if (jumpIndex_.value() == LAST_ITEM) {
388             jumpIndex_ = totalItemCount_ - 1;
389         }
390         auto jumpIndex = jumpIndex_.value();
391         if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
392             jumpIndex = 0;
393         }
394         if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
395             scrollAlign_ == ScrollAlign::AUTO) {
396             startIndex = jumpIndex;
397         } else if (scrollAlign_ == ScrollAlign::END) {
398             endIndex = jumpIndex;
399         } else if (forwardLayout_) {
400             startIndex = jumpIndex;
401         } else {
402             endIndex = jumpIndex;
403         }
404         itemPosition_.clear();
405         jumpIndex_.reset();
406         layoutedItemInfo_.reset();
407     } else if (!itemPosition_.empty()) {
408         if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
409             startPos = itemPosition_.begin()->second.startPos;
410         }
411         startIndex = GetStartIndex();
412         if (startIndex >= totalItemCount_) {
413             startIndex = totalItemCount_ - 1;
414             if (itemPosition_.begin()->first > 0) {
415                 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
416                                GetLanesFloor(startIndex) + headerMainSize_;
417             }
418         }
419         if (!isNeedMeasureFormLastItem_) {
420             endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
421             endPos = itemPosition_.rbegin()->second.endPos;
422         }
423         if (forwardLayout_) {
424             ModifyReferencePos(GetLanesFloor(startIndex), startPos);
425         } else {
426             ModifyReferencePos(GetLanesCeil(endIndex), endPos);
427         }
428         itemPosition_.clear();
429     } else if (!NeedMeasureItem(layoutWrapper)) {
430         itemPosition_.clear();
431         return;
432     }
433     if (scrollAlign_ == ScrollAlign::CENTER) {
434         startIndex = GetLanesFloor(startIndex);
435         MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
436     } else if (scrollAlign_ == ScrollAlign::START) {
437         startIndex = GetLanesFloor(startIndex);
438         MeasureStart(layoutWrapper, layoutConstraint, startIndex);
439     } else if (scrollAlign_ == ScrollAlign::END) {
440         endIndex = GetLanesCeil(endIndex);
441         MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
442     } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
443         startIndex = GetLanesFloor(startIndex);
444         MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
445     } else if (forwardLayout_) {
446         startIndex = GetLanesFloor(startIndex);
447         CheckJumpForwardForBigOffset(startIndex, startPos);
448         startPos = childrenSize_ ? posMap_->GetPos(startIndex) : startPos;
449         MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
450     } else {
451         endIndex = GetLanesCeil(endIndex);
452         CheckJumpBackwardForBigOffset(endIndex, endPos);
453         endPos = childrenSize_ ? posMap_->GetPos(endIndex) + posMap_->GetRowHeight(endIndex) : endPos;
454         MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
455     }
456 }
457 
GetItemGroupPosition(int32_t index)458 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
459 {
460     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
461     if (scrollAlign_ == ScrollAlign::CENTER) {
462         auto pos = itemPosition_.find(index);
463         if (pos != itemPosition_.end()) {
464             float refPos = (pos->second.endPos + pos->second.startPos) / 2 + paddingBeforeContent_; // 2:average
465             float delta = (startPos_ + endPos_) / 2 - refPos;
466             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
467         }
468     } else if (scrollAlign_ == ScrollAlign::START) {
469         auto pos = itemPosition_.find(index);
470         if (pos != itemPosition_.end()) {
471             float top = startPos_ + contentStartOffset_;
472             if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
473                 top += headerMainSize_;
474             }
475             float refPos = pos->second.startPos + paddingBeforeContent_;
476             float delta = top - refPos;
477             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
478         }
479     } else if (scrollAlign_ == ScrollAlign::END) {
480         auto pos = itemPosition_.find(index);
481         if (pos != itemPosition_.end()) {
482             float bottom = endPos_ - contentEndOffset_;
483             if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
484                 bottom -= footerMainSize_;
485             }
486             float refPos = pos->second.endPos + paddingBeforeContent_;
487             float delta = bottom - refPos;
488             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
489         }
490     }
491     return { 0.0f, 0.0f };
492 }
493 
GetItemHeight(int32_t index)494 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
495 {
496     auto it = itemPosition_.find(index);
497     if (it != itemPosition_.end()) {
498         return it->second.endPos - it->second.startPos;
499     }
500     return 0.0f;
501 }
502 
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)503 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
504     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
505 {
506     auto wrapper = GetListItem(layoutWrapper, currentIndex);
507     if (!wrapper) {
508         return 0;
509     }
510     if (CheckNeedMeasure(wrapper)) {
511         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
512         wrapper->Measure(layoutConstraint);
513     }
514     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
515     int32_t id = wrapper->GetHostNode()->GetId();
516     itemPosition_[currentIndex] = { id, 0.0f, mainLen };
517     return 1;
518 }
519 
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)520 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
521     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
522 {
523     float mainLen = 0;
524     int32_t cnt = 0;
525     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
526     for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
527         auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
528         if (!wrapper) {
529             break;
530         }
531         if (CheckNeedMeasure(wrapper)) {
532             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
533             wrapper->Measure(layoutConstraint);
534         }
535         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
536         cnt++;
537     }
538     if (cnt > 0) {
539         auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
540         auto endPos = startPos + mainLen; // 2:average
541         for (int32_t i = 0; i < cnt; i++) {
542             auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
543             int32_t id = wrapper->GetHostNode()->GetId();
544             itemPosition_[currentIndex + i] = { id, startPos, endPos };
545         }
546     }
547     return cnt;
548 }
549 
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)550 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
551     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
552 {
553     float mainLen = 0.0f;
554     int32_t cnt = 0;
555     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
556     for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_; i++) {
557         auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
558         if (!wrapper) {
559             break;
560         }
561         cnt++;
562         ++currentIndex;
563         if (CheckNeedMeasure(wrapper)) {
564             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
565             wrapper->Measure(layoutConstraint);
566         }
567         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
568     }
569     if (cnt > 0) {
570         endPos = startPos + mainLen;
571         for (int32_t i = 0; i < cnt; i++) {
572             auto wrapper = GetListItem(layoutWrapper, currentIndex - i);
573             int32_t id = wrapper->GetHostNode()->GetId();
574             itemPosition_[currentIndex - i] = { id, startPos, endPos };
575         }
576     }
577     return cnt;
578 }
579 
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)580 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
581     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
582 {
583     float mainLen = 0.0f;
584     int32_t cnt = 0;
585     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
586     for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
587         auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
588         if (!wrapper) {
589             break;
590         }
591         --currentIndex;
592         cnt++;
593         if (CheckNeedMeasure(wrapper)) {
594             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
595             wrapper->Measure(layoutConstraint);
596         }
597         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
598         if (currentIndex % lanes == 0) {
599             break;
600         }
601     }
602     if (cnt > 0) {
603         startPos = endPos - mainLen;
604         for (int32_t i = 0; i < cnt; i++) {
605             auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
606             int32_t id = wrapper->GetHostNode()->GetId();
607             itemPosition_[currentIndex + i] = { id, startPos, endPos };
608         }
609     }
610     return cnt;
611 }
612 
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)613 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
614     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
615 {
616     MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
617     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
618     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
619 
620     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
621     float currentStartPos = headerMainSize_;
622     int32_t i = 0;
623     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
624     for (auto& pos : itemPosition_) {
625         float len = pos.second.endPos - pos.second.startPos;
626         pos.second.startPos = currentStartPos;
627         pos.second.endPos = currentStartPos + len;
628         i++;
629         if (i % lanes == 0) {
630             currentStartPos = pos.second.endPos + spaceWidth_;
631         }
632     }
633 }
634 
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)635 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
636     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
637 {
638     if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
639         return;
640     }
641 
642     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
643 }
644 
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)645 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
646     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
647 {
648     float currentStartPos = startPos;
649     float currentEndPos = startPos;
650     int32_t currentIndex = startIndex - 1;
651     while (LessOrEqual(currentEndPos, endPos_)) {
652         currentStartPos = currentEndPos;
653         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
654             currentStartPos, currentEndPos);
655         if (count == 0) {
656             break;
657         }
658         if (currentIndex < (totalItemCount_ - 1)) {
659             currentEndPos += spaceWidth_;
660         }
661     }
662 }
663 
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)664 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
665     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
666 {
667     float currentEndPos = endPos;
668     float currentStartPos = endPos;
669     int32_t currentIndex = endIndex + 1;
670     while (GreatOrEqual(currentStartPos, startPos_)) {
671         currentEndPos = currentStartPos;
672         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
673             currentEndPos, currentStartPos);
674         if (count == 0) {
675             break;
676         }
677         if (currentIndex > 0) {
678             currentStartPos -= spaceWidth_;
679         }
680     }
681 }
682 
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)683 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
684     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
685 {
686     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
687     float currentStartPos = startPos_ + contentStartOffset_;
688     if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
689         currentStartPos += headerMainSize_;
690     }
691 
692     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
693     if (GreatNotEqual(currentStartPos, startPos_)) {
694         MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
695     }
696 
697     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
698     currentStartPos = headerMainSize_;
699     int32_t i = 0;
700     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
701     for (auto& pos : itemPosition_) {
702         float len = pos.second.endPos - pos.second.startPos;
703         pos.second.startPos = currentStartPos;
704         pos.second.endPos = currentStartPos + len;
705         i++;
706         if (i % lanes == 0) {
707             currentStartPos = pos.second.endPos + spaceWidth_;
708         }
709     }
710 }
711 
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)712 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
713     const LayoutConstraintF& layoutConstraint, int32_t endIndex)
714 {
715     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
716     float currentEndPos = endPos_ - contentEndOffset_;
717     if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
718         currentEndPos -= footerMainSize_;
719     }
720 
721     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
722     if (LessNotEqual(currentEndPos, endPos_)) {
723         MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
724     }
725 
726     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
727     float currentStartPos = headerMainSize_;
728     int32_t i = 0;
729     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
730     for (auto& pos : itemPosition_) {
731         float len = pos.second.endPos - pos.second.startPos;
732         pos.second.startPos = currentStartPos;
733         pos.second.endPos = currentStartPos + len;
734         i++;
735         if (i % lanes == 0) {
736             currentStartPos = pos.second.endPos + spaceWidth_;
737         }
738     }
739 }
740 
CheckJumpForwardForBigOffset(int32_t & startIndex,float & startPos)741 void ListItemGroupLayoutAlgorithm::CheckJumpForwardForBigOffset(int32_t& startIndex, float& startPos)
742 {
743     if (!isNeedCheckOffset_ || childrenSize_ || !layoutedItemInfo_.has_value()) {
744         return;
745     }
746     float th = startPos_ - (endPos_ - startPos_) - referencePos_;
747     if (LessOrEqual(startPos, th)) {
748         const auto& itemInfo = layoutedItemInfo_.value();
749         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
750         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
751         float averageHeight = totalHeight / itemCount;
752         float distance = startPos_ - referencePos_ - startPos;
753         int32_t jumpCount = distance / averageHeight;
754         jumpCount = std::min(jumpCount, totalItemCount_ - 1 - startIndex);
755         startPos += jumpCount * averageHeight;
756         startIndex += jumpCount;
757     }
758 }
759 
CheckJumpBackwardForBigOffset(int32_t & endIndex,float & endPos)760 void ListItemGroupLayoutAlgorithm::CheckJumpBackwardForBigOffset(int32_t& endIndex, float& endPos)
761 {
762     if (!isNeedCheckOffset_ || childrenSize_ || !layoutedItemInfo_.has_value()) {
763         return;
764     }
765     float th = endPos_ + (endPos_ - startPos_) - (referencePos_ - totalMainSize_);
766     if (GreatOrEqual(endPos, th)) {
767         const auto& itemInfo = layoutedItemInfo_.value();
768         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
769         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
770         float averageHeight = totalHeight / itemCount;
771         float distance = endPos - (endPos_ - (referencePos_ - totalMainSize_));
772         int32_t jumpCount = distance / averageHeight;
773         jumpCount = std::min(jumpCount, endIndex);
774         endPos -= jumpCount * averageHeight;
775         endIndex -= jumpCount;
776     }
777 }
778 
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)779 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
780     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
781 {
782     float currentEndPos = startPos;
783     float currentStartPos = 0.0f;
784     int32_t currentIndex = startIndex - 1;
785     while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
786         currentStartPos = currentEndPos;
787         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
788             currentStartPos, currentEndPos);
789         if (count == 0) {
790             break;
791         }
792         if (currentIndex < (totalItemCount_ - 1)) {
793             currentEndPos += spaceWidth_;
794         }
795         if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
796             startPos_ = prevStartPos_;
797             endPos_ = prevEndPos_;
798             targetIndex_.reset();
799         }
800     }
801 
802     currentStartPos = startPos - spaceWidth_;
803     currentIndex = startIndex;
804     float th = std::max(startPos_ - referencePos_, headerMainSize_);
805     while (currentIndex > 0  && GreatNotEqual(currentStartPos, th)) {
806         currentEndPos = currentStartPos;
807         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
808             currentEndPos, currentStartPos);
809         if (count == 0) {
810             break;
811         }
812         if (currentIndex > 0) {
813             currentStartPos = currentStartPos - spaceWidth_;
814         }
815     }
816 }
817 
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)818 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
819     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
820 {
821     float currentStartPos = endPos;
822     float currentEndPos = 0.0f;
823     auto currentIndex = endIndex + 1;
824     while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
825         currentEndPos = currentStartPos;
826         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
827             currentEndPos, currentStartPos);
828         if (count == 0) {
829             break;
830         }
831         if (currentIndex > 0) {
832             currentStartPos = currentStartPos - spaceWidth_;
833         }
834         if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
835             startPos_ = prevStartPos_;
836             endPos_ = prevEndPos_;
837             targetIndex_.reset();
838         }
839     }
840     currentIndex = endIndex;
841     currentEndPos = endPos + spaceWidth_;
842     while (childrenSize_ && LessOrEqual(currentEndPos, endPos_ - (referencePos_ - totalMainSize_))) {
843         currentStartPos = currentEndPos;
844         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
845             currentStartPos, currentEndPos);
846         if (count == 0) {
847             break;
848         }
849         if (currentIndex < (totalItemCount_ - 1)) {
850             currentEndPos += spaceWidth_;
851         }
852     }
853 }
854 
ModifyReferencePos(int32_t index,float pos)855 void ListItemGroupLayoutAlgorithm::ModifyReferencePos(int32_t index, float pos)
856 {
857     if (!childrenSize_ || !needAdjustRefPos_) {
858         return;
859     }
860     if (forwardLayout_ && Negative(referencePos_)) {
861         float offset = referencePos_ + pos;
862         float newReferencePos = offset - posMap_->GetPos(index);
863         refPos_ = refPos_ + newReferencePos - referencePos_;
864         referencePos_ = newReferencePos;
865     } else if (!forwardLayout_ && GreatNotEqual(referencePos_, prevContentMainSize_)) {
866         float offset = referencePos_ - posMap_->GetPrevTotalHeight() + pos - prevContentMainSize_;
867         float newReferencePos = offset + endPos_ - startPos_ + totalMainSize_ -
868             (posMap_->GetPos(index) + posMap_->GetRowHeight(index));
869         refPos_ = refPos_ + newReferencePos - referencePos_;
870         referencePos_ = newReferencePos;
871     }
872 }
873 
AdjustByPosMap()874 void ListItemGroupLayoutAlgorithm::AdjustByPosMap()
875 {
876     totalMainSize_ = posMap_->GetTotalHeight();
877     if (itemPosition_.empty()) {
878         return;
879     }
880     float startPos = itemPosition_.begin()->second.startPos;
881     float offset = posMap_->GetGroupLayoutOffset(GetStartIndex(), startPos);
882     for (auto& pos : itemPosition_) {
883         pos.second.startPos += offset;
884         pos.second.endPos += offset;
885     }
886 }
887 
AdjustItemPosition()888 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
889 {
890     if (itemPosition_.empty()) {
891         return;
892     }
893     float currentStartPos = GetStartPosition();
894     if (currentStartPos < headerMainSize_) {
895         auto delta = headerMainSize_ - currentStartPos;
896         for (auto& pos : itemPosition_) {
897             pos.second.startPos += delta;
898             pos.second.endPos += delta;
899         }
900         totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
901         adjustReferenceDelta_ = -delta;
902     } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
903         auto delta = currentStartPos - headerMainSize_;
904         for (auto& pos : itemPosition_) {
905             pos.second.startPos -= delta;
906             pos.second.endPos -= delta;
907         }
908         totalMainSize_ -= delta;
909         adjustReferenceDelta_ = delta;
910     }
911     if (GetEndIndex() == totalItemCount_ - 1) {
912         totalMainSize_ = GetEndPosition() + footerMainSize_;
913     } else {
914         totalMainSize_ = std::max(totalMainSize_, GetEndPosition() + footerMainSize_);
915     }
916     const auto& start = *itemPosition_.begin();
917     const auto& end = *itemPosition_.rbegin();
918     if (layoutedItemInfo_.has_value()) {
919         auto& itemInfo = layoutedItemInfo_.value();
920         auto prevStartIndex = itemInfo.startIndex;
921         if (start.first <= itemInfo.startIndex || LessNotEqual(start.second.startPos, itemInfo.startPos) ||
922             start.first > itemInfo.endIndex) {
923             itemInfo.startIndex = start.first;
924             itemInfo.startPos = start.second.startPos;
925         }
926         if (end.first >= itemInfo.endIndex || GreatNotEqual(end.second.endPos, itemInfo.endPos) ||
927             itemInfo.endIndex > totalItemCount_ - 1 || end.first < prevStartIndex) {
928             itemInfo.endIndex = end.first;
929             itemInfo.endPos = end.second.endPos;
930         }
931     } else {
932         layoutedItemInfo_ = { start.first, start.second.startPos, end.first, end.second.endPos };
933     }
934 }
935 
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)936 void ListItemGroupLayoutAlgorithm::CheckRecycle(
937     const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
938 {
939     referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
940     // Mark inactive in wrapper.
941     if (forwardLayout) {
942         for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
943             if (GreatOrEqual(pos->second.endPos, startPos - referencePos)) {
944                 break;
945             }
946             itemPosition_.erase(pos++);
947         }
948         return;
949     }
950     std::list<int32_t> removeIndexes;
951     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
952         if (LessOrEqual(pos->second.startPos, endPos - (referencePos - totalMainSize_))) {
953             break;
954         }
955         removeIndexes.emplace_back(pos->first);
956     }
957     for (const auto& index : removeIndexes) {
958         itemPosition_.erase(index);
959     }
960 }
961 
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)962 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
963     const OffsetF& paddingOffset, float crossSize)
964 {
965     // layout items.
966     for (auto& pos : itemPosition_) {
967         auto wrapper = GetListItem(layoutWrapper, pos.first);
968         if (!wrapper) {
969             LOGI("wrapper is out of boundary");
970             continue;
971         }
972 
973         auto offset = paddingOffset;
974         int32_t laneIndex = pos.first % lanes_;
975         float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
976         float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
977         if (layoutDirection_ == TextDirection::RTL) {
978             if (axis_ == Axis::VERTICAL) {
979                 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
980                 auto tmpX = crossSize - laneCrossOffset -
981                     ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
982                 offset = offset + OffsetF(tmpX, pos.second.startPos);
983             } else {
984                 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
985                 offset = offset + OffsetF(totalMainSize_ - pos.second.endPos, tmpY);
986             }
987         } else {
988             if (axis_ == Axis::VERTICAL) {
989                 offset =
990                     offset + OffsetF(0, pos.second.startPos) + OffsetF(laneCrossOffset, 0) +
991                     OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
992             } else {
993                 offset =
994                     offset + OffsetF(pos.second.startPos, 0) + OffsetF(0, laneCrossOffset) +
995                     OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
996             }
997         }
998         SetListItemIndex(layoutWrapper, wrapper, pos.first);
999         wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1000         if (wrapper->CheckNeedForceMeasureAndLayout()) {
1001             wrapper->Layout();
1002         } else {
1003             SyncGeometry(wrapper);
1004         }
1005     }
1006 }
1007 
UpdateZIndex(const RefPtr<LayoutWrapper> & layoutWrapper)1008 void ListItemGroupLayoutAlgorithm::UpdateZIndex(const RefPtr<LayoutWrapper>& layoutWrapper)
1009 {
1010     auto host = layoutWrapper->GetHostNode();
1011     CHECK_NULL_VOID(host);
1012     auto renderContext = host->GetRenderContext();
1013     CHECK_NULL_VOID(renderContext);
1014     renderContext->UpdateZIndex(1);
1015 }
1016 
LayoutHeaderFooterRTL(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1017 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterRTL(LayoutWrapper* layoutWrapper,
1018     const OffsetF& paddingOffset, float crossSize)
1019 {
1020     OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1021     selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1022     float mainPos = GetMainAxisOffset(selfOffset, axis_);
1023     float footerMainSize = 0.0f;
1024     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1025     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1026         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1027     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1028         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1029     if (footerWrapper) {
1030         UpdateZIndex(footerWrapper);
1031         footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1032         float footerPos = 0.0f;
1033         if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER) && !itemPosition_.empty()) {
1034             contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1035             float stickyPos = contentStartOffset_ - mainPos;
1036             if (GetEndIndex() == totalItemCount_ - 1) {
1037                 stickyPos = std::min(stickyPos, GetEndPosition() - footerMainSize);
1038             }
1039             footerPos = std::max(footerPos, stickyPos);
1040             footerPos = std::min(footerPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1041         }
1042         LayoutIndex(footerWrapper, paddingOffset, crossSize, footerPos);
1043         endFooterPos_ = endFooterPos_ > mainPos ? mainPos : endFooterPos_;
1044     }
1045 
1046     if (headerWrapper) {
1047         float headPos = totalMainSize_ - headerMainSize_;
1048         UpdateZIndex(headerWrapper);
1049         float const listMainSize = endPos_ - startPos_;
1050         if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER)) {
1051             auto headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1052             float stickyPos = listMainSize - contentEndOffset_ - mainPos - headerMainSize;
1053             if (stickyPos < footerMainSize) {
1054                 stickyPos = footerMainSize;
1055             }
1056             if (stickyPos < headPos) {
1057                 headPos = stickyPos;
1058             }
1059         }
1060         LayoutIndex(headerWrapper, paddingOffset, crossSize, headPos);
1061         startHeaderPos_ = mainPos + totalMainSize_ - headerMainSize_ - listMainSize;
1062     }
1063 }
1064 
LayoutHeaderFooterLTR(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1065 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterLTR(LayoutWrapper* layoutWrapper,
1066     const OffsetF& paddingOffset, float crossSize)
1067 {
1068     OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1069     selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1070     float mainPos = GetMainAxisOffset(selfOffset, axis_);
1071     float headerMainSize = 0.0f;
1072     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1073     RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1074         layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1075     RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1076         layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1077     if (headerWrapper) {
1078         UpdateZIndex(headerWrapper);
1079         headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1080         float headerPos = 0.0f;
1081         if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) && !itemPosition_.empty()) {
1082             contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1083             float stickyPos = contentStartOffset_ - mainPos;
1084             if (GetEndIndex() == totalItemCount_ - 1) {
1085                 stickyPos = std::min(stickyPos, GetEndPosition() - headerMainSize);
1086             }
1087             headerPos = std::max(headerPos, stickyPos);
1088         }
1089         LayoutIndex(headerWrapper, paddingOffset, crossSize, headerPos);
1090         startHeaderPos_ = startHeaderPos_ > mainPos ? mainPos : startHeaderPos_;
1091     }
1092 
1093     if (footerWrapper) {
1094         float endPos = totalMainSize_ - footerMainSize_;
1095         UpdateZIndex(footerWrapper);
1096         float const listMainSize = endPos_ - startPos_;
1097         if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
1098             auto footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1099             float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
1100             if (stickyPos < headerMainSize) {
1101                 stickyPos = headerMainSize;
1102             }
1103             if (stickyPos < endPos) {
1104                 endPos = stickyPos;
1105             }
1106         }
1107         LayoutIndex(footerWrapper, paddingOffset, crossSize, endPos);
1108         endFooterPos_ = mainPos + totalMainSize_ - footerMainSize_ - listMainSize;
1109     }
1110 }
1111 
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)1112 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
1113     float crossSize, float startPos)
1114 {
1115     CHECK_NULL_VOID(wrapper);
1116     auto offset = paddingOffset;
1117     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1118     float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1119     if (axis_ == Axis::VERTICAL) {
1120         if (layoutDirection_ == TextDirection::RTL) {
1121             auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1122             offset = offset + OffsetF(crossSize - laneCrossOffset - size.Width(), startPos);
1123         } else {
1124             offset = offset + OffsetF(laneCrossOffset, startPos);
1125         }
1126     } else {
1127         offset = offset + OffsetF(startPos, laneCrossOffset);
1128     }
1129     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1130     wrapper->Layout();
1131 }
1132 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1133 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1134 {
1135     float delta = crossSize - GetLaneGutter() - childCrossSize;
1136     if (LessOrEqual(delta, 0.0f)) {
1137         return 0.0f;
1138     }
1139     switch (itemAlign_) {
1140         case OHOS::Ace::V2::ListItemAlign::START:
1141             return 0.0f;
1142         case OHOS::Ace::V2::ListItemAlign::CENTER:
1143             return delta / 2; /* 2:average */
1144         case OHOS::Ace::V2::ListItemAlign::END:
1145             return delta;
1146         default:
1147             LOGW("Invalid ListItemAlign: %{public}d", itemAlign_);
1148             return 0.0f;
1149     }
1150 }
1151 
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)1152 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
1153     const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
1154 {
1155     int32_t lanes = layoutProperty->GetLanes().value_or(1);
1156     lanes = lanes > 1 ? lanes : 1;
1157     if (crossSizeOptional.has_value()) {
1158         if (layoutProperty->GetLaneMinLength().has_value()) {
1159             minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
1160                 layoutConstraint.scaleProperty, crossSizeOptional.value());
1161         }
1162         if (layoutProperty->GetLaneMaxLength().has_value()) {
1163             maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
1164                 layoutConstraint.scaleProperty, crossSizeOptional.value());
1165         }
1166         if (layoutProperty->GetLaneGutter().has_value()) {
1167             auto laneGutter = ConvertToPx(
1168                 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
1169             laneGutter_ = laneGutter.value();
1170         }
1171     }
1172     lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
1173         minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
1174 }
1175 
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)1176 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
1177     const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
1178 {
1179     auto host = itemLayoutWrapper->GetHostNode();
1180     CHECK_NULL_VOID(host);
1181     auto listItem = host->GetPattern<ListItemPattern>();
1182     CHECK_NULL_VOID(listItem);
1183     listItem->SetIndexInListItemGroup(indexInGroup);
1184 
1185     host = groupLayoutWrapper->GetHostNode();
1186     CHECK_NULL_VOID(host);
1187     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1188     CHECK_NULL_VOID(listItemGroup);
1189     listItem->SetIndexInList(listItemGroup->GetIndexInList());
1190 }
1191 
GetLayoutInfo() const1192 ListItemGroupLayoutInfo ListItemGroupLayoutAlgorithm::GetLayoutInfo() const
1193 {
1194     ListItemGroupLayoutInfo info;
1195     if (totalItemCount_ == 0 || childrenSize_) {
1196         info.atStart = true;
1197         info.atEnd = true;
1198         return info;
1199     }
1200     if (layoutedItemInfo_.has_value()) {
1201         const auto& itemInfo = layoutedItemInfo_.value();
1202         info.atStart = itemInfo.startIndex == 0;
1203         info.atEnd = itemInfo.endIndex >= totalItemCount_ - 1;
1204         auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
1205         auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
1206         info.averageHeight = totalHeight / itemCount;
1207     }
1208     return info;
1209 }
1210 
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)1211 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
1212 {
1213     auto host = groupLayoutWrapper->GetHostNode();
1214     CHECK_NULL_RETURN(host, false);
1215     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1216     CHECK_NULL_RETURN(listItemGroup, false);
1217     return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
1218 }
1219 
MeasureCacheItem(LayoutWrapper * layoutWrapper)1220 void ListItemGroupLayoutAlgorithm::MeasureCacheItem(LayoutWrapper* layoutWrapper)
1221 {
1222     ListItemGroupCacheParam& cacheParam = cacheParam_.value();
1223     if (cacheParam.forward) {
1224         int32_t endIndex = itemPosition_.empty() ? -1 : itemPosition_.rbegin()->first;
1225         int32_t limit = std::min(endIndex + cacheParam.cacheCount * lanes_, totalItemCount_ - 1);
1226         int32_t currentIndex = std::clamp(cacheParam.forwardCachedIndex, endIndex, limit) + 1;
1227         for (; currentIndex <= limit; currentIndex++) {
1228             auto item = layoutWrapper->GetOrCreateChildByIndex(currentIndex + itemStartIndex_, false, true);
1229             auto frameNode = AceType::DynamicCast<FrameNode>(item);
1230             if (!frameNode) {
1231                 break;
1232             }
1233             if (!frameNode->CheckNeedForceMeasureAndLayout()) {
1234                 continue;
1235             }
1236             if (!frameNode->GetHostNode()->RenderCustomChild(cacheParam.deadline)) {
1237                 break;
1238             }
1239             item->Measure(childLayoutConstraint_);
1240             cachedItem_.push_back(currentIndex);
1241         }
1242         cacheParam.forwardCachedIndex = std::min(currentIndex - 1, limit);
1243     } else {
1244         int32_t startIndex = itemPosition_.empty() ? totalItemCount_ : itemPosition_.begin()->first;
1245         int32_t limit = std::max(startIndex - cacheParam.cacheCount * lanes_, 0);
1246         int32_t currentIndex = std::clamp(cacheParam.backwardCachedIndex, limit, startIndex) - 1;
1247         for (; currentIndex >= limit; currentIndex--) {
1248             auto item = layoutWrapper->GetOrCreateChildByIndex(currentIndex + itemStartIndex_, false, true);
1249             auto frameNode = AceType::DynamicCast<FrameNode>(item);
1250             if (!frameNode) {
1251                 break;
1252             }
1253             if (!frameNode->CheckNeedForceMeasureAndLayout()) {
1254                 continue;
1255             }
1256             if (!frameNode->GetHostNode()->RenderCustomChild(cacheParam.deadline)) {
1257                 break;
1258             }
1259             item->Measure(childLayoutConstraint_);
1260             cachedItem_.push_back(currentIndex);
1261         }
1262         cacheParam.backwardCachedIndex = std::max(currentIndex + 1, limit);
1263     }
1264 }
1265 
LayoutCacheItem(LayoutWrapper * layoutWrapper)1266 void ListItemGroupLayoutAlgorithm::LayoutCacheItem(LayoutWrapper* layoutWrapper)
1267 {
1268     for (auto index : cachedItem_) {
1269         auto item = layoutWrapper->GetOrCreateChildByIndex(index + itemStartIndex_, false, true);
1270         if (item) {
1271             item->SetActive(true);
1272             item->Layout();
1273             item->SetActive(false);
1274         }
1275     }
1276 }
1277 } // namespace OHOS::Ace::NG
1278