• 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     const auto& padding = layoutProperty->CreatePaddingAndBorder();
63     paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
64     paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
65     auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
66     auto contentIdealSize = CreateIdealSize(
67         contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
68 
69     auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
70     auto space = layoutProperty->GetSpace().value_or(Dimension(0));
71 
72     auto layoutConstraint = layoutProperty->GetLayoutConstraint().value();
73     CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
74     auto itemLayoutConstraint = layoutProperty->CreateChildConstraint();
75     isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
76     if (isCardStyle_) {
77         auto maxWidth = GetListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty) -
78                         layoutProperty->CreatePaddingAndBorder().Width();
79         contentIdealSize.SetCrossSize(maxWidth, axis_);
80     }
81     UpdateListItemConstraint(contentIdealSize, itemLayoutConstraint);
82     auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
83     headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
84     referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
85     totalItemCount_ = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
86     totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
87     if (headerIndex_ >= 0) {
88         auto headerWrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
89         headerWrapper->Measure(headerFooterLayoutConstraint);
90         headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
91     }
92     if (footerIndex_ >= 0) {
93         auto footerWrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
94         footerWrapper->Measure(headerFooterLayoutConstraint);
95         footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
96     }
97     spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
98     if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, endPos_ - startPos_)) {
99         spaceWidth_ = 0.0f;
100     }
101     if (layoutProperty->GetDivider().has_value()) {
102         auto divider = layoutProperty->GetDivider().value();
103         std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
104         if (GreatOrEqual(dividerSpace.value(), endPos_ - startPos_)) {
105             dividerSpace.reset();
106         }
107         if (dividerSpace.has_value()) {
108             spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
109         }
110     }
111     totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
112     MeasureListItem(layoutWrapper, itemLayoutConstraint);
113     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_->GetCachedCountValue(1) * lanes_);
123 }
124 
GetListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty)125 float ListItemGroupLayoutAlgorithm::GetListItemGroupMaxWidth(
126     const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty)
127 {
128     RefPtr<GridColumnInfo> columnInfo;
129     columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
130     columnInfo->GetParent()->BuildColumnWidth();
131     auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
132     auto parentWidth = parentIdealSize.CrossSize(axis_).value() + layoutProperty->CreatePaddingAndBorder().Width();
133     auto maxWidth = std::min(parentWidth, maxGridWidth);
134     return maxWidth;
135 }
136 
Layout(LayoutWrapper * layoutWrapper)137 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
138 {
139     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
140     CHECK_NULL_VOID(layoutProperty);
141     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
142     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
143     MinusPaddingToSize(padding, size);
144     auto left = padding.left.value_or(0.0f);
145     auto top = padding.top.value_or(0.0f);
146     auto paddingOffset = OffsetF(left, top);
147     float crossSize = GetCrossAxisSize(size, axis_);
148     CHECK_NULL_VOID(listLayoutProperty_);
149     itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
150 
151     if (headerIndex_ >= 0 || footerIndex_ >= 0) {
152         LayoutHeaderFooter(layoutWrapper, paddingOffset, crossSize);
153     }
154     // layout items.
155     LayoutListItem(layoutWrapper, paddingOffset, crossSize);
156 }
157 
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)158 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
159     LayoutConstraintF& contentConstraint)
160 {
161     contentConstraint.parentIdealSize = selfIdealSize;
162     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
163     auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
164     if (crossSizeOptional.has_value()) {
165         float crossSize = crossSizeOptional.value();
166         if (lanes_ > 1) {
167             crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
168             crossSize = crossSize <= 0 ? 1 : crossSize;
169         }
170         if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
171             crossSize = maxLaneLength_.value();
172         }
173         contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
174         contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
175         contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
176         if (minLaneLength_.has_value()) {
177             contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
178         }
179     }
180 }
181 
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)182 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
183 {
184     float maxCrossSize = 0.0f;
185     for (const auto& pos : itemPosition_) {
186         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
187         if (!wrapper) {
188             continue;
189         }
190         auto getGeometryNode = wrapper->GetGeometryNode();
191         if (!getGeometryNode) {
192             continue;
193         }
194         maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
195     }
196     return maxCrossSize;
197 }
198 
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)199 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
200     RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
201 {
202     const auto& padding = layoutProperty->CreatePaddingAndBorder();
203     const auto& margin = layoutProperty->CreateMargin();
204     auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
205     auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
206     offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
207     offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
208     forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
209     return referencePos;
210 }
211 
NeedMeasureItem() const212 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem() const
213 {
214     if (forwardLayout_) {
215         if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
216             return false;
217         }
218         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
219             return false;
220         }
221     } else {
222         if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
223             return false;
224         }
225         if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
226             return false;
227         }
228     }
229     return true;
230 }
231 
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)232 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
233     const LayoutConstraintF& layoutConstraint, float startPos)
234 {
235     int32_t currentIndex = -1;
236     float currentEndPos = startPos;
237     float currentStartPos = 0.0f;
238     while (currentIndex < totalItemCount_) {
239         currentStartPos = currentEndPos;
240         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
241             currentStartPos, currentEndPos);
242         if (count == 0) {
243             break;
244         }
245         if (currentIndex < (totalItemCount_ - 1)) {
246             currentEndPos += spaceWidth_;
247         }
248 
249     }
250 }
251 
ClearItemPosition(LayoutWrapper * layoutWrapper)252 void ListItemGroupLayoutAlgorithm::ClearItemPosition(LayoutWrapper* layoutWrapper)
253 {
254     itemPosition_.clear();
255     layoutWrapper->RemoveAllChildInRenderTree();
256 }
257 
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)258 void ListItemGroupLayoutAlgorithm::MeasureListItem(
259     LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
260 {
261     if (totalItemCount_ <= 0) {
262         totalMainSize_ = headerMainSize_ + footerMainSize_;
263         layoutWrapper->RemoveAllChildInRenderTree();
264         itemPosition_.clear();
265         return;
266     }
267     int32_t startIndex = 0;
268     int32_t endIndex = totalItemCount_ - 1;
269     float startPos = headerMainSize_;
270     float endPos = totalMainSize_ - footerMainSize_;
271     prevStartPos_ = startPos_;
272     prevEndPos_ = endPos_;
273     if (needAllLayout_) {
274         needAllLayout_ = false;
275         itemPosition_.clear();
276         layoutWrapper->RemoveAllChildInRenderTree();
277         LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
278         return;
279     }
280     if (targetIndex_) {
281         startPos_ = -Infinity<float>();
282         endPos_ = Infinity<float>();
283     }
284     if (jumpIndex_.has_value()) {
285         if (jumpIndex_.value() == LAST_ITEM) {
286             jumpIndex_ = totalItemCount_ - 1;
287         }
288         auto jumpIndex = jumpIndex_.value();
289         if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
290             jumpIndex = 0;
291         }
292         if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
293             scrollAlign_ == ScrollAlign::AUTO) {
294             startIndex = jumpIndex;
295         } else if (scrollAlign_ == ScrollAlign::END) {
296             endIndex = jumpIndex;
297         } else if (forwardLayout_) {
298             startIndex = jumpIndex;
299         } else {
300             endIndex = jumpIndex;
301         }
302         itemPosition_.clear();
303         layoutWrapper->RemoveAllChildInRenderTree();
304         jumpIndex_.reset();
305     } else if (!itemPosition_.empty()) {
306         if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
307             startPos = itemPosition_.begin()->second.first;
308         }
309         endPos = itemPosition_.rbegin()->second.second;
310         startIndex = GetStartIndex();
311         if (startIndex >= totalItemCount_) {
312             startIndex = totalItemCount_ - 1;
313             if (itemPosition_.begin()->first > 0) {
314                 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
315                                GetLanesFloor(startIndex) + headerMainSize_;
316             }
317         }
318         endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
319         itemPosition_.clear();
320         layoutWrapper->RemoveAllChildInRenderTree();
321     } else if (!NeedMeasureItem()) {
322         layoutWrapper->RemoveAllChildInRenderTree();
323         itemPosition_.clear();
324         return;
325     }
326     if (scrollAlign_ == ScrollAlign::CENTER) {
327         startIndex = GetLanesFloor(startIndex);
328         MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
329     } else if (scrollAlign_ == ScrollAlign::START) {
330         startIndex = GetLanesFloor(startIndex);
331         MeasureStart(layoutWrapper, layoutConstraint, startIndex);
332     } else if (scrollAlign_ == ScrollAlign::END) {
333         endIndex = GetLanesCeil(endIndex);
334         MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
335     } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
336         startIndex = GetLanesFloor(startIndex);
337         MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
338     } else if (forwardLayout_) {
339         startIndex = GetLanesFloor(startIndex);
340         MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
341     } else {
342         endIndex = GetLanesCeil(endIndex);
343         MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
344     }
345 }
346 
GetItemGroupPosition(int32_t index)347 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
348 {
349     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
350     if (scrollAlign_ == ScrollAlign::CENTER) {
351         float mainLen = 0;
352         float center = (startPos_ + endPos_) / 2;  // 2:average
353         auto pos = itemPosition_.find(index);
354         if (pos != itemPosition_.end()) {
355             mainLen = pos->second.second - pos->second.first;
356             float refPos = (pos->second.second + pos->second.first) / 2 + paddingBeforeContent_; // 2:average
357             float delta = center - refPos;
358             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
359         }
360     } else if (scrollAlign_ == ScrollAlign::START) {
361         auto pos = itemPosition_.find(index);
362         if (pos != itemPosition_.end()) {
363             float top = startPos_ + contentStartOffset_;
364             if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
365                 top += headerMainSize_;
366             }
367             float refPos = pos->second.first + paddingBeforeContent_;
368             float delta = top - refPos;
369             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
370         }
371     } else if (scrollAlign_ == ScrollAlign::END) {
372         auto pos = itemPosition_.find(index);
373         if (pos != itemPosition_.end()) {
374             float bottom = endPos_ - contentEndOffset_;
375             if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
376                 bottom -= footerMainSize_;
377             }
378             float refPos = pos->second.second + paddingBeforeContent_;
379             float delta = bottom - refPos;
380             return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
381         }
382     }
383     return { 0.0f, 0.0f };
384 }
385 
GetItemHeight(int32_t index)386 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
387 {
388     auto it = itemPosition_.find(index);
389     if (it != itemPosition_.end()) {
390         return it->second.second - it->second.first;
391     }
392     return 0.0f;
393 }
394 
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)395 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
396     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
397 {
398     auto wrapper = GetListItem(layoutWrapper, currentIndex);
399     if (!wrapper) {
400         return 0;
401     }
402     {
403         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
404         wrapper->Measure(layoutConstraint);
405     }
406     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
407     itemPosition_[currentIndex] = { 0.0f, mainLen };
408     return 1;
409 }
410 
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)411 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
412     const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
413 {
414     float mainLen = 0;
415     int32_t cnt = 0;
416     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
417     for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
418         auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
419         if (!wrapper) {
420             break;
421         }
422         {
423             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
424             wrapper->Measure(layoutConstraint);
425         }
426         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
427         cnt++;
428     }
429     if (cnt > 0) {
430         auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
431         auto endPos = startPos + mainLen; // 2:average
432         for (int32_t i = 0; i < cnt; i++) {
433             itemPosition_[currentIndex + i] = { startPos, endPos };
434         }
435     }
436     return cnt;
437 }
438 
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)439 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
440     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
441 {
442     float mainLen = 0.0f;
443     int32_t cnt = 0;
444     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
445     for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_; i++) {
446         auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
447         if (!wrapper) {
448             break;
449         }
450         cnt++;
451         ++currentIndex;
452         {
453             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
454             wrapper->Measure(layoutConstraint);
455         }
456         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
457     }
458     if (cnt > 0) {
459         endPos = startPos + mainLen;
460         for (int32_t i = 0; i < cnt; i++) {
461             itemPosition_[currentIndex - i] = { startPos, endPos };
462         }
463     }
464     return cnt;
465 }
466 
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)467 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
468     const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
469 {
470     float mainLen = 0.0f;
471     int32_t cnt = 0;
472     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
473     for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
474         auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
475         if (!wrapper) {
476             break;
477         }
478         --currentIndex;
479         cnt++;
480         {
481             ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
482             wrapper->Measure(layoutConstraint);
483         }
484         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
485         if (currentIndex % lanes == 0) {
486             break;
487         }
488     }
489     if (cnt > 0) {
490         startPos = endPos - mainLen;
491         for (int32_t i = 0; i < cnt; i++) {
492             itemPosition_[currentIndex + i] = { startPos, endPos };
493         }
494     }
495     return cnt;
496 }
497 
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)498 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
499     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
500 {
501     MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
502     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
503     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
504 
505     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
506     float currentStartPos = headerMainSize_;
507     int32_t i = 0;
508     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
509     for (auto& pos : itemPosition_) {
510         float len = pos.second.second - pos.second.first;
511         pos.second.first = currentStartPos;
512         pos.second.second = currentStartPos + len;
513         i++;
514         if (i % lanes == 0) {
515             currentStartPos = pos.second.second + spaceWidth_;
516         }
517     }
518 }
519 
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)520 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
521     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
522 {
523     if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
524         return;
525     }
526 
527     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
528 }
529 
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)530 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
531     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
532 {
533     float currentStartPos = startPos;
534     float currentEndPos = startPos;
535     int32_t currentIndex = startIndex - 1;
536     while (LessOrEqual(currentEndPos, endPos_)) {
537         currentStartPos = currentEndPos;
538         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
539             currentStartPos, currentEndPos);
540         if (count == 0) {
541             break;
542         }
543         if (currentIndex < (totalItemCount_ - 1)) {
544             currentEndPos += spaceWidth_;
545         }
546     }
547 }
548 
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)549 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
550     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
551 {
552     float currentEndPos = endPos;
553     float currentStartPos = endPos;
554     int32_t currentIndex = endIndex + 1;
555     while (GreatOrEqual(currentStartPos, startPos_)) {
556         currentEndPos = currentStartPos;
557         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
558             currentEndPos, currentStartPos);
559         if (count == 0) {
560             break;
561         }
562         if (currentIndex > 0) {
563             currentStartPos -= spaceWidth_;
564         }
565     }
566 }
567 
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)568 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
569     const LayoutConstraintF& layoutConstraint, int32_t startIndex)
570 {
571     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
572     float currentStartPos = startPos_ + contentStartOffset_;
573     if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
574         currentStartPos += headerMainSize_;
575     }
576 
577     MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
578     if (Positive(contentStartOffset_)) {
579         MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
580     }
581 
582     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
583     currentStartPos = headerMainSize_;
584     int32_t i = 0;
585     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
586     for (auto& pos : itemPosition_) {
587         float len = pos.second.second - pos.second.first;
588         pos.second.first = currentStartPos;
589         pos.second.second = currentStartPos + len;
590         i++;
591         if (i % lanes == 0) {
592             currentStartPos = pos.second.second + spaceWidth_;
593         }
594     }
595 }
596 
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)597 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
598     const LayoutConstraintF& layoutConstraint, int32_t endIndex)
599 {
600     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
601     float currentEndPos = endPos_ - contentEndOffset_;
602     if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
603         currentEndPos -= footerMainSize_;
604     }
605 
606     MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
607     if (Positive(contentEndOffset_)) {
608         MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
609     }
610 
611     totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
612     float currentStartPos = headerMainSize_;
613     int32_t i = 0;
614     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
615     for (auto& pos : itemPosition_) {
616         float len = pos.second.second - pos.second.first;
617         pos.second.first = currentStartPos;
618         pos.second.second = currentStartPos + len;
619         i++;
620         if (i % lanes == 0) {
621             currentStartPos = pos.second.second + spaceWidth_;
622         }
623     }
624 }
625 
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)626 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
627     const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
628 {
629     float currentEndPos = startPos;
630     float currentStartPos = 0.0f;
631     int32_t currentIndex = startIndex - 1;
632     while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
633         currentStartPos = currentEndPos;
634         int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
635             currentStartPos, currentEndPos);
636         if (count == 0) {
637             break;
638         }
639         if (currentIndex < (totalItemCount_ - 1)) {
640             currentEndPos += spaceWidth_;
641         }
642         if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
643             startPos_ = prevStartPos_;
644             endPos_ = prevEndPos_;
645             targetIndex_.reset();
646         }
647     }
648 
649     currentStartPos = startPos - spaceWidth_;
650     currentIndex = startIndex;
651     float th = std::max(startPos_ - referencePos_, headerMainSize_);
652     while (currentIndex > 0  && GreatNotEqual(currentStartPos, th)) {
653         currentEndPos = currentStartPos;
654         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
655             currentEndPos, currentStartPos);
656         if (count == 0) {
657             break;
658         }
659         if (currentIndex > 0) {
660             currentStartPos = currentStartPos - spaceWidth_;
661         }
662     }
663 }
664 
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)665 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
666     const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
667 {
668     float currentStartPos = endPos;
669     float currentEndPos = 0.0f;
670     auto currentIndex = endIndex + 1;
671     while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
672         currentEndPos = currentStartPos;
673         int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
674             currentEndPos, currentStartPos);
675         if (count == 0) {
676             break;
677         }
678         if (currentIndex > 0) {
679             currentStartPos = currentStartPos - spaceWidth_;
680         }
681         if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
682             startPos_ = prevStartPos_;
683             endPos_ = prevEndPos_;
684             targetIndex_.reset();
685         }
686     }
687 }
688 
AdjustItemPosition()689 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
690 {
691     if (itemPosition_.empty()) {
692         return;
693     }
694     float currentStartPos = GetStartPosition();
695     if (currentStartPos < headerMainSize_) {
696         auto delta = headerMainSize_ - currentStartPos;
697         for (auto& pos : itemPosition_) {
698             pos.second.first += delta;
699             pos.second.second += delta;
700         }
701         totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
702     } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
703         auto delta = currentStartPos - headerMainSize_;
704         for (auto& pos : itemPosition_) {
705             pos.second.first -= delta;
706             pos.second.second -= delta;
707         }
708         totalMainSize_ -= delta;
709     }
710     if (GetEndIndex() == totalItemCount_ - 1) {
711         totalMainSize_ = GetEndPosition() + footerMainSize_;
712     } else {
713         totalMainSize_ = std::max(totalMainSize_, GetEndPosition() + footerMainSize_);
714     }
715     const auto& start = *itemPosition_.begin();
716     const auto& end = *itemPosition_.rbegin();
717     if (layoutedItemInfo_.has_value()) {
718         auto& itemInfo = layoutedItemInfo_.value();
719         if (start.first <= itemInfo.startIndex || LessNotEqual(start.second.first, itemInfo.startPos)) {
720             itemInfo.startIndex = start.first;
721             itemInfo.startPos = start.second.first;
722         }
723         if (end.first >= itemInfo.endIndex || LessNotEqual(end.second.second, itemInfo.endPos)) {
724             itemInfo.endIndex = end.first;
725             itemInfo.endPos = end.second.second;
726         }
727     } else {
728         layoutedItemInfo_ = { start.first, start.second.first, end.first, end.second.second };
729     }
730 }
731 
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)732 void ListItemGroupLayoutAlgorithm::CheckRecycle(
733     const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
734 {
735     referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
736     // Mark inactive in wrapper.
737     if (forwardLayout) {
738         for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
739             if (GreatOrEqual(pos->second.second, startPos - referencePos)) {
740                 break;
741             }
742             RecycleListItem(layoutWrapper, pos->first);
743             itemPosition_.erase(pos++);
744         }
745         return;
746     }
747     std::list<int32_t> removeIndexes;
748     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
749         if (LessOrEqual(pos->second.first, endPos - (referencePos - totalMainSize_))) {
750             break;
751         }
752         RecycleListItem(layoutWrapper, pos->first);
753         removeIndexes.emplace_back(pos->first);
754     }
755     for (const auto& index : removeIndexes) {
756         itemPosition_.erase(index);
757     }
758 }
759 
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)760 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
761     const OffsetF& paddingOffset, float crossSize)
762 {
763     // layout items.
764     for (auto& pos : itemPosition_) {
765         auto wrapper = GetListItem(layoutWrapper, pos.first);
766         if (!wrapper) {
767             continue;
768         }
769 
770         auto offset = paddingOffset;
771         int32_t laneIndex = pos.first % lanes_;
772         float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
773         float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
774         if (axis_ == Axis::VERTICAL) {
775             offset =
776                 offset + OffsetF(0, pos.second.first) + OffsetF(laneCrossOffset, 0) +
777                 OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
778         } else {
779             offset =
780                 offset + OffsetF(pos.second.first, 0) + OffsetF(0, laneCrossOffset) +
781                 OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
782         }
783         SetListItemIndex(layoutWrapper, wrapper, pos.first);
784         wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
785         wrapper->Layout();
786     }
787 }
788 
LayoutHeaderFooter(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)789 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooter(LayoutWrapper* layoutWrapper,
790     const OffsetF& paddingOffset, float crossSize)
791 {
792     OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
793     selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
794     float mainPos = GetMainAxisOffset(selfOffset, axis_);
795     float headerMainSize = 0.0f;
796     V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
797     if (headerIndex_ >= 0) {
798         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
799         CHECK_NULL_VOID(wrapper);
800         headerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
801         float headerPos = 0.0f;
802         if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) && !itemPosition_.empty()) {
803             contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
804             float stickyPos = contentStartOffset_ - mainPos;
805             if (GetEndIndex() == totalItemCount_ - 1) {
806                 stickyPos = std::min(stickyPos, GetEndPosition() - headerMainSize);
807             }
808             headerPos = std::max(headerPos, stickyPos);
809         }
810         LayoutIndex(wrapper, paddingOffset, crossSize, headerPos);
811     }
812 
813     if (footerIndex_ >= 0) {
814         float endPos = totalMainSize_ - footerMainSize_;
815         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
816         CHECK_NULL_VOID(wrapper);
817         float const listMainSize = endPos_ - startPos_;
818         if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
819             auto footerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
820             float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
821             if (stickyPos < headerMainSize) {
822                 stickyPos = headerMainSize;
823             }
824             if (stickyPos < endPos) {
825                 endPos = stickyPos;
826             }
827         }
828         LayoutIndex(wrapper, paddingOffset, crossSize, endPos);
829     }
830 }
831 
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)832 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
833     float crossSize, float startPos)
834 {
835     CHECK_NULL_VOID(wrapper);
836     auto offset = paddingOffset;
837     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
838     float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
839     if (axis_ == Axis::VERTICAL) {
840         offset = offset + OffsetF(laneCrossOffset, startPos);
841     } else {
842         offset = offset + OffsetF(startPos, laneCrossOffset);
843     }
844     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
845     wrapper->Layout();
846 }
847 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)848 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
849 {
850     float delta = crossSize - GetLaneGutter() - childCrossSize;
851     if (LessOrEqual(delta, 0.0f)) {
852         return 0.0f;
853     }
854     switch (itemAlign_) {
855         case OHOS::Ace::V2::ListItemAlign::START:
856             return 0.0f;
857         case OHOS::Ace::V2::ListItemAlign::CENTER:
858             return delta / 2; /* 2:average */
859         case OHOS::Ace::V2::ListItemAlign::END:
860             return delta;
861         default:
862             return 0.0f;
863     }
864 }
865 
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)866 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
867     const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
868 {
869     int32_t lanes = layoutProperty->GetLanes().value_or(1);
870     lanes = lanes > 1 ? lanes : 1;
871     if (crossSizeOptional.has_value()) {
872         if (layoutProperty->GetLaneMinLength().has_value()) {
873             minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
874                 layoutConstraint.scaleProperty, crossSizeOptional.value());
875         }
876         if (layoutProperty->GetLaneMaxLength().has_value()) {
877             maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
878                 layoutConstraint.scaleProperty, crossSizeOptional.value());
879         }
880         if (layoutProperty->GetLaneGutter().has_value()) {
881             auto laneGutter = ConvertToPx(
882                 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
883             laneGutter_ = laneGutter.value();
884         }
885     }
886     lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
887         minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
888 }
889 
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)890 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
891     const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
892 {
893     auto host = itemLayoutWrapper->GetHostNode();
894     CHECK_NULL_VOID(host);
895     auto listItem = host->GetPattern<ListItemPattern>();
896     CHECK_NULL_VOID(listItem);
897     listItem->SetIndexInListItemGroup(indexInGroup);
898 
899     host = groupLayoutWrapper->GetHostNode();
900     CHECK_NULL_VOID(host);
901     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
902     CHECK_NULL_VOID(listItemGroup);
903     listItem->SetIndexInList(listItemGroup->GetIndexInList());
904 }
905 
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)906 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
907 {
908     auto host = groupLayoutWrapper->GetHostNode();
909     CHECK_NULL_RETURN(host, false);
910     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
911     CHECK_NULL_RETURN(listItemGroup, false);
912     return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
913 }
914 } // namespace OHOS::Ace::NG
915