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_lanes_layout_algorithm.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/components_ng/syntax/lazy_for_each_node.h"
21 #include "core/components_v2/inspector/inspector_constants.h"
22 
23 namespace OHOS::Ace::NG {
24 
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)25 void ListLanesLayoutAlgorithm::UpdateListItemConstraint(
26     Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
27 {
28     contentConstraint.parentIdealSize = selfIdealSize;
29     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
30     groupLayoutConstraint_ = contentConstraint;
31     auto crossSizeOptional = selfIdealSize.CrossSize(axis);
32     if (crossSizeOptional.has_value()) {
33         float crossSize = crossSizeOptional.value();
34         groupLayoutConstraint_.maxSize.SetCrossSize(crossSize, axis);
35         if (lanes_ > 1) {
36             float laneGutter = GetLaneGutter();
37             crossSize = (crossSize + laneGutter) / lanes_ - laneGutter;
38             crossSize = crossSize <= 0 ? 1 : crossSize;
39         }
40         if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
41             crossSize = maxLaneLength_.value();
42         }
43         contentConstraint.percentReference.SetCrossSize(crossSize, axis);
44         contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis);
45         contentConstraint.maxSize.SetCrossSize(crossSize, axis);
46         if (minLaneLength_.has_value()) {
47             contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis);
48         }
49     }
50 }
51 
GetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)52 float ListLanesLayoutAlgorithm::GetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
53 {
54     CHECK_NULL_RETURN(childrenSize_, 0.0f);
55     float mainLen = 0.0f;
56     int32_t laneCeil = GetLanesCeil(layoutWrapper, childIndex);
57     for (int32_t index = GetLanesFloor(layoutWrapper, childIndex); index <= laneCeil; index++) {
58         mainLen = std::max(mainLen, childrenSize_->GetChildSize(index));
59     }
60     return mainLen;
61 }
62 
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex,bool groupLayoutAll)63 float ListLanesLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex,
64     bool groupLayoutAll)
65 {
66     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
67     CHECK_NULL_RETURN(wrapper, 0.0f);
68     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
69     float mainLen = 0.0f;
70     if (isGroup) {
71         auto listLayoutProperty =
72             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
73         // true: layout forward, true: layout all group items.
74         SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, true);
75         wrapper->Measure(groupLayoutConstraint_);
76         mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
77     } else {
78         auto laneCeil = GetLanesCeil(layoutWrapper, childIndex);
79         for (int32_t i = GetLanesFloor(layoutWrapper, childIndex); i <= laneCeil; i++) {
80             auto wrapper = layoutWrapper->GetOrCreateChildByIndex(i);
81             wrapper->Measure(childLayoutConstraint_);
82             mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
83         }
84     }
85     return mainLen;
86 }
87 
MeasureGroup(LayoutWrapper * listWrapper,const RefPtr<LayoutWrapper> & groupWrapper,int32_t index,float & pos,bool forward)88 void ListLanesLayoutAlgorithm::MeasureGroup(LayoutWrapper* listWrapper, const RefPtr<LayoutWrapper>& groupWrapper,
89     int32_t index, float& pos, bool forward)
90 {
91     CHECK_NULL_VOID(groupWrapper);
92     auto host = groupWrapper->GetHostNode();
93     const char* direction = forward ? "Forward" : "Backward";
94     if (host) {
95         ACE_SCOPED_TRACE("[Measure%sListItemGroup:%d][self:%d][parent:%d]", direction, index, host->GetId(),
96             host->GetParent() ? host->GetParent()->GetId() : 0);
97     }
98     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(listWrapper->GetLayoutProperty());
99     SetListItemGroupParam(groupWrapper, index, pos, forward, listLayoutProperty, false);
100     groupWrapper->Measure(groupLayoutConstraint_);
101     if (forward && LessOrEqual(pos, 0.0f)) {
102         AdjustStartPosition(groupWrapper, pos);
103     }
104 }
105 
MeasureItem(const RefPtr<LayoutWrapper> & itemWrapper,int32_t index,bool forward)106 void ListLanesLayoutAlgorithm::MeasureItem(const RefPtr<LayoutWrapper>& itemWrapper, int32_t index, bool forward)
107 {
108     CHECK_NULL_VOID(itemWrapper);
109     auto host = itemWrapper->GetHostNode();
110     const char* direction = forward ? "Forward" : "Backward";
111     if (host) {
112         ACE_SCOPED_TRACE("[Measure%sListItem:%d][self:%d][parent:%d]", direction, index, host->GetId(),
113             host->GetParent() ? host->GetParent()->GetId() : 0);
114     }
115     itemWrapper->Measure(childLayoutConstraint_);
116 }
117 
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)118 int32_t ListLanesLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
119     int32_t& currentIndex, float startPos, float& endPos)
120 {
121     float mainLen = 0.0f;
122     bool isGroup = false;
123     int32_t cnt = 0;
124     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
125     if (firstItemInfo_ && firstItemInfo_.value().first == currentIndex + 1) {
126         ++currentIndex;
127         endPos = firstItemInfo_.value().second.endPos;
128         SetItemInfo(currentIndex, std::move(firstItemInfo_.value().second));
129         OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
130         firstItemInfo_.reset();
131         return 1;
132     } else if (firstItemInfo_) {
133         firstItemInfo_.reset();
134     }
135     for (int32_t i = 0; i < lanes && currentIndex + 1 <= GetMaxListItemIndex() && !isGroup; i++) {
136         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
137         if (!wrapper) {
138             break;
139         }
140         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
141         if (isGroup && cnt > 0) {
142             wrapper->SetActive(false);
143             isGroup = false;
144             break;
145         }
146         cnt++;
147         ++currentIndex;
148         if (isGroup) {
149             MeasureGroup(layoutWrapper, wrapper, currentIndex, startPos, true);
150         } else if (CheckNeedMeasure(wrapper)) {
151             MeasureItem(wrapper, currentIndex, true);
152         }
153         mainLen = std::max(mainLen, childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
154             GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
155     }
156     if (cnt > 0) {
157         endPos = startPos + mainLen;
158         for (int32_t i = 0; i < cnt; i++) {
159             auto wrap = layoutWrapper->GetOrCreateChildByIndex(currentIndex - i);
160             int32_t id = wrap->GetHostNode()->GetId();
161             SetItemInfo(currentIndex - i, { id, startPos, endPos, isGroup });
162         }
163     }
164     OnItemPositionAddOrUpdate(layoutWrapper, GetLanesFloor(layoutWrapper, currentIndex));
165     return cnt;
166 }
167 
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)168 int32_t ListLanesLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
169     int32_t& currentIndex, float endPos, float& startPos)
170 {
171     float mainLen = 0.0f;
172     bool isGroup = false;
173     int32_t cnt = 0;
174     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
175     if (firstItemInfo_ && firstItemInfo_.value().first == currentIndex - 1) {
176         --currentIndex;
177         startPos = firstItemInfo_.value().second.startPos;
178         SetItemInfo(currentIndex, std::move(firstItemInfo_.value().second));
179         OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
180         firstItemInfo_.reset();
181         return 1;
182     } else if (firstItemInfo_) {
183         firstItemInfo_.reset();
184     }
185     for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
186         if (currentIndex > GetMaxListItemIndex() + 1) {
187             --currentIndex;
188             continue;
189         }
190         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
191         if (!wrapper) {
192             break;
193         }
194         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
195         if (isGroup && cnt > 0) {
196             wrapper->SetActive(false);
197             isGroup = false;
198             break;
199         }
200         --currentIndex;
201         cnt++;
202         if (isGroup) {
203             MeasureGroup(layoutWrapper, wrapper, currentIndex, endPos, false);
204         } else if (CheckNeedMeasure(wrapper)) {
205             MeasureItem(wrapper, currentIndex, false);
206         }
207         mainLen = std::max(mainLen, childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
208             GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
209         if (CheckCurRowMeasureFinished(layoutWrapper, currentIndex, isGroup)) {
210             break;
211         }
212     }
213     if (cnt > 0) {
214         startPos = endPos - mainLen;
215         for (int32_t i = 0; i < cnt; i++) {
216             auto wrap = layoutWrapper->GetOrCreateChildByIndex(currentIndex + i);
217             int32_t id = wrap->GetHostNode()->GetId();
218             SetItemInfo(currentIndex + i, { id, startPos, endPos, isGroup });
219         }
220     }
221     OnItemPositionAddOrUpdate(layoutWrapper, GetLanesFloor(layoutWrapper, currentIndex));
222     return cnt;
223 }
224 
CheckCurRowMeasureFinished(LayoutWrapper * layoutWrapper,int32_t curIndex,bool isGroup)225 bool ListLanesLayoutAlgorithm::CheckCurRowMeasureFinished(LayoutWrapper* layoutWrapper, int32_t curIndex, bool isGroup)
226 {
227     if (childrenSize_) {
228         return isGroup || posMap_->GetRowStartIndex(curIndex) == curIndex;
229     }
230     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
231     return isGroup || (curIndex - FindLanesStartIndex(layoutWrapper, curIndex)) % lanes == 0;
232 }
233 
SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)234 void ListLanesLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount)
235 {
236     bool hasGroup = false;
237     auto& itemPosition = GetItemPosition();
238     for (auto &pos : itemPosition) {
239         if (pos.second.isGroup) {
240             hasGroup = true;
241             break;
242         }
243     }
244     int32_t count = hasGroup ? cacheCount : cacheCount * lanes_;
245     layoutWrapper->SetCacheCount(count);
246 }
247 
CalculateLanesParam(std::optional<float> & minLaneLength,std::optional<float> & maxLaneLength,int32_t lanes,std::optional<float> crossSizeOptional,float laneGutter)248 int32_t ListLanesLayoutAlgorithm::CalculateLanesParam(std::optional<float>& minLaneLength,
249     std::optional<float>& maxLaneLength, int32_t lanes, std::optional<float> crossSizeOptional, float laneGutter)
250 {
251     if (lanes < 1) {
252         return 1;
253     }
254     // Case 1: lane length constrain is not set
255     //      1.1: use [lanes] set by user if [lanes] is set
256     //      1.2: set [lanes] to 1 if [lanes] is not set
257     if (!crossSizeOptional.has_value() || GreaterOrEqualToInfinity(crossSizeOptional.value()) ||
258         !minLaneLength.has_value() || !maxLaneLength.has_value()) {
259         maxLaneLength.reset();
260         minLaneLength.reset();
261         return lanes;
262     }
263     // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
264     // We agreed on such rules (assuming we have a vertical list here):
265     // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
266     //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
267     //         the [lanes_] is 3 rather than 2.
268     // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for
269     // e.g.,
270     //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
271     //         according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
272     //         its [minLaneLength_].
273     auto crossSize = crossSizeOptional.value();
274     ModifyLaneLength(minLaneLength, maxLaneLength, crossSize);
275 
276     // if minLaneLength is 40, maxLaneLength is 60
277     // when list's width is 120, lanes_ = 3
278     // when list's width is 80, lanes_ = 2
279     // when list's width is 70, lanes_ = 1
280     float maxLanes = (crossSize + laneGutter) / (minLaneLength.value() + laneGutter);
281     float minLanes = (crossSize + laneGutter) / (maxLaneLength.value() + laneGutter);
282     // let's considerate scenarios when maxCrossSize > 0
283     // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
284     // there are 3 scenarios:
285     // 1. 1 > maxLanes >= minLanes > 0
286     // 2. maxLanes >= 1 >= minLanes > 0
287     // 3. maxLanes >= minLanes > 1
288     // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
289     if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
290         lanes = 1;
291         minLaneLength = crossSize;
292         maxLaneLength = crossSize;
293         return lanes;
294     }
295     // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
296     if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
297         lanes = std::floor(maxLanes);
298         maxLaneLength = crossSize;
299         return lanes;
300     }
301     // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
302     if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
303         lanes = std::floor(maxLanes);
304         return lanes;
305     }
306     lanes = 1;
307     return lanes;
308 }
309 
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)310 void ListLanesLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
311     const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
312 {
313     auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
314     auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis);
315     int32_t lanes = layoutProperty->GetLanes().value_or(1);
316     lanes = lanes > 1 ? lanes : 1;
317     if (layoutProperty->GetLaneMinLength().has_value()) {
318         minLaneLength_ =
319             ConvertToPx(layoutProperty->GetLaneMinLength().value(), layoutConstraint.scaleProperty, mainPercentRefer);
320     }
321     if (layoutProperty->GetLaneMaxLength().has_value()) {
322         maxLaneLength_ =
323             ConvertToPx(layoutProperty->GetLaneMaxLength().value(), layoutConstraint.scaleProperty, mainPercentRefer);
324     }
325     float laneGutter = 0.0f;
326     if (layoutProperty->GetLaneGutter().has_value()) {
327         laneGutter = ConvertToPx(layoutProperty->GetLaneGutter().value(),
328             layoutConstraint.scaleProperty, crossSizeOptional.value_or(0.0)).value();
329         SetLaneGutter(laneGutter);
330     }
331     lanes_ = CalculateLanesParam(minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter);
332 }
333 
ModifyLaneLength(std::optional<float> & minLaneLength,std::optional<float> & maxLaneLength,float crossSize)334 void ListLanesLayoutAlgorithm::ModifyLaneLength(
335     std::optional<float>& minLaneLength, std::optional<float>& maxLaneLength, float crossSize)
336 {
337     if (GreatNotEqual(minLaneLength.value(), maxLaneLength.value())) {
338         maxLaneLength = minLaneLength;
339     }
340 }
341 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)342 float ListLanesLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
343 {
344     if (lanes_ <= 0) {
345         return 0.0f;
346     }
347     return ListLayoutAlgorithm::CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_,
348         childCrossSize / lanes_);
349 }
350 
GetLazyForEachIndex(const RefPtr<FrameNode> & host)351 int32_t ListLanesLayoutAlgorithm::GetLazyForEachIndex(const RefPtr<FrameNode>& host)
352 {
353     CHECK_NULL_RETURN(host, -1);
354     auto parent = host->GetParent();
355     auto lazyForEach = AceType::DynamicCast<LazyForEachNode>(parent);
356     if (lazyForEach) {
357         return lazyForEach->GetIndexByUINode(host);
358     }
359     while (parent && !AceType::InstanceOf<FrameNode>(parent)) {
360         if (AceType::InstanceOf<RepeatVirtualScrollNode>(parent)) {
361             return parent->GetFrameNodeIndex(host);
362         }
363         parent = parent->GetParent();
364     }
365     return -1;
366 }
367 
FindLanesStartIndex(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t index)368 int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t index)
369 {
370     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
371     CHECK_NULL_RETURN(wrapper, index);
372     if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
373         return index;
374     }
375     auto lazyIndex = GetLazyForEachIndex(wrapper->GetHostNode());
376     if (lazyIndex > 0) {
377         index -= lazyIndex;
378     }
379     for (int32_t idx = index; idx > startIndex; idx--) {
380         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(idx - 1);
381         CHECK_NULL_RETURN(wrapper, idx);
382         if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
383             return idx;
384         }
385     }
386     if (startIndex == 0) {
387         return 0;
388     }
389     return -1;
390 }
391 
FindLanesStartIndex(LayoutWrapper * layoutWrapper,int32_t index)392 int32_t ListLanesLayoutAlgorithm::FindLanesStartIndex(LayoutWrapper* layoutWrapper, int32_t index)
393 {
394     if (lanes_ == 1) {
395         return 0;
396     }
397     auto it = lanesItemRange_.upper_bound(index);
398     if (it == lanesItemRange_.begin()) {
399         int32_t startIdx = FindLanesStartIndex(layoutWrapper, 0, index);
400         lanesItemRange_[startIdx] = index;
401         return startIdx;
402     }
403     it--;
404     if (it->second >= index) {
405         return it->first;
406     }
407     int32_t startIdx = FindLanesStartIndex(layoutWrapper, it->second, index);
408     if (startIdx >= 0) {
409         lanesItemRange_[startIdx] = index;
410         return startIdx;
411     }
412     it->second = index;
413     return it->first;
414 }
415 
GetLanesFloor(LayoutWrapper * layoutWrapper,int32_t index)416 int32_t ListLanesLayoutAlgorithm::GetLanesFloor(LayoutWrapper* layoutWrapper, int32_t index)
417 {
418     if (lanes_ > 1) {
419         if (childrenSize_) {
420             return posMap_->GetRowStartIndex(index);
421         }
422         int32_t startIndex = FindLanesStartIndex(layoutWrapper, index);
423         return index - (index - startIndex) % lanes_;
424     }
425     return index;
426 }
427 
GetLanesCeil(LayoutWrapper * layoutWrapper,int32_t index)428 int32_t ListLanesLayoutAlgorithm::GetLanesCeil(LayoutWrapper* layoutWrapper, int32_t index)
429 {
430     if (lanes_ > 1) {
431         if (childrenSize_) {
432             return posMap_->GetRowEndIndex(index);
433         }
434         int32_t startIndex = GetLanesFloor(layoutWrapper, index);
435         while (startIndex == GetLanesFloor(layoutWrapper, index + 1)) {
436             index++;
437         }
438     }
439     return index;
440 }
441 
LayoutCachedALine(LayoutWrapper * layoutWrapper,std::pair<const int,ListItemInfo> & pos,int32_t startIndex,float crossSize)442 void ListLanesLayoutAlgorithm::LayoutCachedALine(LayoutWrapper* layoutWrapper,
443     std::pair<const int, ListItemInfo>& pos, int32_t startIndex, float crossSize)
444 {
445     auto wrapper = layoutWrapper->GetChildByIndex(pos.first, true);
446     CHECK_NULL_VOID(wrapper);
447     LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
448     SyncGeometry(wrapper);
449     wrapper->SetActive(false);
450     SetCachedItemInfo(pos.first, std::move(pos.second));
451 }
452 
LayoutCachedALineForward(LayoutWrapper * layoutWrapper,int32_t & index,float & startPos,float crossSize)453 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedALineForward(LayoutWrapper* layoutWrapper,
454     int32_t& index, float& startPos, float crossSize)
455 {
456     std::list<int32_t> predictBuildList;
457     ListLayoutAlgorithm::PositionMap posMap;
458     float mainLen = 0.0f;
459     bool isGroup = false;
460     int32_t cnt = 0;
461     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
462     for (int32_t i = 0; i < lanes && index + i <= GetMaxListItemIndex(); i++) {
463         auto wrapper = layoutWrapper->GetChildByIndex(index + i, true);
464         if (!wrapper) {
465             predictBuildList.emplace_back(index + i);
466             break;
467         }
468         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
469         if (isGroup && cnt > 0) {
470             isGroup = false;
471             break;
472         }
473         if (CheckNeedMeasure(wrapper)) {
474             if (!isGroup) {
475                 predictBuildList.emplace_back(index + i);
476                 continue;
477             }
478             break;
479         }
480         cnt++;
481         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
482         auto id = wrapper->GetHostNode()->GetId();
483         posMap[index + i] = { id, startPos, startPos + mainLen, isGroup };
484         if (isGroup) {
485             break;
486         }
487     }
488     if (cnt > 0) {
489         auto endPos = startPos + mainLen;
490         startPos = endPos + GetSpaceWidth();
491         auto startIndex = index;
492         for (auto& pos: posMap) {
493             pos.second.endPos = endPos;
494             LayoutCachedALine(layoutWrapper, pos, startIndex, crossSize);
495         }
496     }
497     index += cnt + static_cast<int32_t>(predictBuildList.size());
498     return predictBuildList;
499 }
500 
LayoutCachedALineBackward(LayoutWrapper * layoutWrapper,int32_t & index,float & endPos,float crossSize)501 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedALineBackward(LayoutWrapper* layoutWrapper,
502     int32_t& index, float& endPos, float crossSize)
503 {
504     std::list<int32_t> predictBuildList;
505     ListLayoutAlgorithm::PositionMap posMap;
506     float mainLen = 0.0f;
507     bool isGroup = false;
508     int32_t cnt = 0;
509     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
510     for (int32_t i = 0; i < lanes && index >= 0; i++) {
511         auto idx = index - i;
512         auto wrapper = layoutWrapper->GetChildByIndex(idx, true);
513         if (!wrapper) {
514             predictBuildList.emplace_back(idx);
515             break;
516         }
517         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
518         if (isGroup && cnt > 0) {
519             isGroup = false;
520             break;
521         }
522         if (CheckNeedMeasure(wrapper)) {
523             if (!isGroup) {
524                 predictBuildList.emplace_back(idx);
525                 continue;
526             }
527             break;
528         }
529         cnt++;
530         mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
531         auto id = wrapper->GetHostNode()->GetId();
532         posMap[idx] = { id, endPos - mainLen, endPos, isGroup };
533         if (CheckCurRowMeasureFinished(layoutWrapper, idx, isGroup)) {
534             break;
535         }
536     }
537     if (cnt > 0) {
538         auto startPos = endPos - mainLen;
539         endPos = startPos - GetSpaceWidth();
540         auto startIndex = index - cnt + 1;
541         for (auto& pos: posMap) {
542             pos.second.startPos = startPos;
543             LayoutCachedALine(layoutWrapper, pos, startIndex, crossSize);
544         }
545     }
546     index -= cnt + static_cast<int32_t>(predictBuildList.size());
547     return predictBuildList;
548 }
549 
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)550 std::list<int32_t> ListLanesLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
551 {
552     std::list<int32_t> predictBuildList;
553     float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
554 
555     auto& itemPosition = GetItemPosition();
556     auto curIndex = itemPosition.rbegin()->first + 1;
557     auto currPos = itemPosition.rbegin()->second.endPos + GetSpaceWidth();
558     for (int32_t i = 0; i < cacheCount && curIndex <= GetMaxListItemIndex(); i++) {
559         auto tmpList = LayoutCachedALineForward(layoutWrapper, curIndex, currPos, crossSize);
560         predictBuildList.merge(tmpList);
561     }
562     curIndex = itemPosition.begin()->first - 1;
563     currPos = itemPosition.begin()->second.startPos - GetSpaceWidth();
564     for (int32_t i = 0; i < cacheCount && curIndex >= 0; i++) {
565         auto tmpList = LayoutCachedALineBackward(layoutWrapper, curIndex, currPos, crossSize);
566         predictBuildList.merge(tmpList);
567     }
568     return predictBuildList;
569 }
570 
CheckACachedItem(const RefPtr<LayoutWrapper> & wrapper,int32_t cnt,bool & isGroup) const571 std::pair<bool, bool> ListLanesLayoutAlgorithm::CheckACachedItem(
572     const RefPtr<LayoutWrapper>& wrapper, int32_t cnt, bool& isGroup) const
573 {
574     if (!wrapper) {
575         return std::make_pair(true, true);
576     }
577     isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
578     if (isGroup && cnt > 0) {
579         isGroup = false;
580         return std::make_pair(true, false);
581     }
582     bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
583     if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
584         if (isDirty) {
585             return std::make_pair(true, true);
586         }
587         return std::make_pair(false, true);
588     }
589     return std::make_pair(false, false);
590 }
591 
LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)592 int32_t ListLanesLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper,
593     int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
594 {
595     float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
596     RefPtr<LayoutWrapper> wrapper;
597     curIndex = GetItemPosition().rbegin()->first + 1;
598     auto startPos = GetItemPosition().rbegin()->second.endPos + GetSpaceWidth();
599     while (cachedCount < cacheCount && curIndex <= GetMaxListItemIndex()) {
600         ListLayoutAlgorithm::PositionMap posMap;
601         float mainLen = 0.0f;
602         bool isGroup = false;
603         int32_t cnt = 0;
604         for (int32_t i = 0; i < lanes_ && curIndex + i <= GetMaxListItemIndex() && !isGroup; i++) {
605             wrapper = layoutWrapper->GetChildByIndex(curIndex + i, !show);
606             auto [needBreak, needPredict] = CheckACachedItem(wrapper, cnt, isGroup);
607             if (needPredict) {
608                 predictList.emplace_back(PredictLayoutItem { curIndex + i, cachedCount, -1 });
609             }
610             if (needBreak) {
611                 break;
612             }
613             cnt++;
614             mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
615             posMap[curIndex + i] = { wrapper->GetHostNode()->GetId(), startPos, startPos + mainLen, isGroup };
616         }
617         auto startIndex = curIndex;
618         if (isGroup) {
619             auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true);
620             if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) {
621                 LayoutItem(wrapper, posMap.begin()->first, posMap.begin()->second, startIndex, crossSize);
622                 predictList.emplace_back(PredictLayoutItem { posMap.begin()->first, cachedCount, -1 });
623                 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1;
624             }
625             cachedCount += std::max(res.forwardCacheMax, 1);
626         } else if (cnt > 0) {
627             cachedCount++;
628         } else {
629             break;
630         }
631         for (auto& pos : posMap) {
632             pos.second.endPos = startPos + mainLen;
633             LayoutCachedALine(layoutWrapper, pos, startIndex, crossSize);
634         }
635         startPos = startPos + mainLen + GetSpaceWidth();
636         curIndex += cnt;
637     }
638     return curIndex - 1;
639 }
640 
LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)641 int32_t ListLanesLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper,
642     int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
643 {
644     float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
645     RefPtr<LayoutWrapper> wrapper;
646     curIndex = GetItemPosition().begin()->first - 1;
647     auto endPos = GetItemPosition().begin()->second.startPos - GetSpaceWidth();
648     while (cachedCount < cacheCount && curIndex >= 0) {
649         ListLayoutAlgorithm::PositionMap posMap;
650         float mainLen = 0.0f;
651         bool isGroup = false;
652         int32_t cnt = 0;
653         for (int32_t i = 0; i < lanes_ && curIndex - i >= 0; i++) {
654             auto idx = curIndex - i;
655             wrapper = layoutWrapper->GetChildByIndex(idx, !show);
656             auto [needBreak, needPredict] = CheckACachedItem(wrapper, cnt, isGroup);
657             if (needPredict) {
658                 predictList.emplace_back(PredictLayoutItem { idx, -1, cachedCount });
659             }
660             if (needBreak) {
661                 break;
662             }
663             cnt++;
664             mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
665             posMap[idx] = { wrapper->GetHostNode()->GetId(), endPos - mainLen, endPos, isGroup };
666             if (CheckCurRowMeasureFinished(layoutWrapper, idx, isGroup)) {
667                 break;
668             }
669         }
670         auto startIndex = curIndex - cnt + 1;
671         if (isGroup) {
672             auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true);
673             if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) {
674                 LayoutItem(wrapper, posMap.begin()->first, posMap.begin()->second, startIndex, crossSize);
675                 predictList.emplace_back(PredictLayoutItem { posMap.begin()->first, -1, cachedCount });
676                 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1;
677             }
678             cachedCount += std::max(res.backwardCacheMax, 1);
679         } else if (cnt > 0) {
680             cachedCount++;
681         } else {
682             break;
683         }
684         for (auto& pos: posMap) {
685             pos.second.startPos = endPos - mainLen;
686             LayoutCachedALine(layoutWrapper, pos, startIndex, crossSize);
687         }
688         endPos = endPos - mainLen - GetSpaceWidth();
689         curIndex -= cnt;
690     }
691     return curIndex + 1;
692 }
693 } // namespace OHOS::Ace::NG
694