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