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