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