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 "base/log/event_report.h"
17 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
18
19 #include "core/components/common/layout/grid_system_manager.h"
20 #include "core/components_ng/pattern/list/list_item_pattern.h"
21 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
22 #include "core/components_ng/property/measure_utils.h"
23
24 namespace OHOS::Ace::NG {
25
26 namespace {
27 constexpr uint32_t GRID_COUNTS_4 = 4;
28 constexpr uint32_t GRID_COUNTS_6 = 6;
29 constexpr uint32_t GRID_COUNTS_8 = 8;
30 constexpr uint32_t GRID_COUNTS_12 = 12;
31
GetMaxGridCounts(int32_t currentColumns)32 uint32_t GetMaxGridCounts(int32_t currentColumns)
33 {
34 auto maxGridCounts = GRID_COUNTS_8;
35 switch (currentColumns) {
36 case GRID_COUNTS_4:
37 maxGridCounts = GRID_COUNTS_4;
38 break;
39 case GRID_COUNTS_8:
40 maxGridCounts = GRID_COUNTS_6;
41 break;
42 case GRID_COUNTS_12:
43 maxGridCounts = GRID_COUNTS_8;
44 break;
45 default:
46 break;
47 }
48 return maxGridCounts;
49 }
50 } // namespace
51
Measure(LayoutWrapper * layoutWrapper)52 void ListItemGroupLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
53 {
54 auto totalChildCount = layoutWrapper->GetTotalChildCount();
55 totalItemCount_ = totalChildCount - itemStartIndex_ - footerCount_;
56 footerIndex_ = (footerCount_ > 0) ? (totalChildCount - footerCount_) : -1;
57 CHECK_NULL_VOID(listLayoutProperty_);
58 auto layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
59 CHECK_NULL_VOID(layoutProperty);
60 axis_ = listLayoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
61 layoutDirection_ = listLayoutProperty_->GetNonAutoLayoutDirection();
62 isStackFromEnd_ = listLayoutProperty_->GetStackFromEnd().value_or(false);
63 const auto& padding = layoutProperty->CreatePaddingAndBorder();
64 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
65 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
66 const auto& contentConstraintOps = layoutProperty->GetContentLayoutConstraint();
67 CHECK_NULL_VOID(contentConstraintOps);
68 auto contentConstraint = contentConstraintOps.value();
69 auto contentIdealSize = CreateIdealSize(
70 contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
71 auto listItemGroupLayoutProperty =
72 AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
73 CHECK_NULL_VOID(listItemGroupLayoutProperty);
74 auto layoutPolicy = listItemGroupLayoutProperty->GetLayoutPolicyProperty();
75 auto isCrossWrap =
76 layoutPolicy.has_value() && ((axis_ == Axis::VERTICAL && layoutPolicy.value().IsWidthWrap()) ||
77 (axis_ == Axis::HORIZONTAL && layoutPolicy.value().IsHeightWrap()));
78 auto isCrossFix =
79 layoutPolicy.has_value() && ((axis_ == Axis::VERTICAL && layoutPolicy.value().IsWidthFix()) ||
80 (axis_ == Axis::HORIZONTAL && layoutPolicy.value().IsHeightFix()));
81
82 auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
83 auto space = layoutProperty->GetSpace().value_or(Dimension(0));
84
85 const auto& layoutConstraintOps = layoutProperty->GetLayoutConstraint();
86 CHECK_NULL_VOID(layoutConstraintOps);
87 auto layoutConstraint = layoutConstraintOps.value();
88 CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
89 childLayoutConstraint_ = layoutProperty->CreateChildConstraint();
90 isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
91 if (isCardStyle_) {
92 UpdateListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty, contentIdealSize);
93 listLayoutProperty_->UpdateListItemAlign(V2::ListItemAlign::CENTER);
94 }
95 UpdateListItemConstraint(contentIdealSize, childLayoutConstraint_);
96 referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
97 totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
98 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
99 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, endPos_ - startPos_)) {
100 spaceWidth_ = 0.0f;
101 }
102 if (layoutProperty->GetDivider().has_value()) {
103 auto divider = layoutProperty->GetDivider().value();
104 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
105 if (GreatOrEqual(dividerSpace.value(), endPos_ - startPos_)) {
106 dividerSpace.reset();
107 }
108 if (dividerSpace.has_value()) {
109 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
110 }
111 }
112 if (IsRoundingMode(layoutWrapper)) {
113 spaceWidth_ = Round(spaceWidth_);
114 }
115 MeasureHeaderFooter(layoutWrapper);
116 totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
117 if (isStackFromEnd_) {
118 std::swap(headerMainSize_, footerMainSize_);
119 }
120 if (childrenSize_) {
121 posMap_->UpdateGroupPosMap(totalItemCount_, GetLanes(), spaceWidth_, childrenSize_,
122 headerMainSize_, footerMainSize_);
123 totalMainSize_ = posMap_->GetTotalHeight();
124 }
125
126 if (isLayouted_) {
127 ReverseItemPosition(cachedItemPosition_, prevTotalItemCount_, prevTotalMainSize_);
128 ReverseItemPosition(itemPosition_, prevTotalItemCount_, prevTotalMainSize_);
129 ReverseLayoutedItemInfo(prevTotalItemCount_, prevTotalMainSize_);
130 } else {
131 recycledItemPosition_.insert(itemPosition_.begin(), itemPosition_.end());
132 }
133
134 if (cacheParam_) {
135 MeasureCacheItem(layoutWrapper);
136 } else {
137 MeasureListItem(layoutWrapper, childLayoutConstraint_);
138 UpdateCachedItemPosition(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
139 }
140
141 if (childrenSize_) {
142 AdjustByPosMap();
143 } else {
144 AdjustItemPosition();
145 UpdateLayoutedItemInfo();
146 }
147 UpdateRecycledItems();
148
149 ReverseLayoutedItemInfo(totalItemCount_, totalMainSize_);
150 auto crossSize = contentIdealSize.CrossSize(axis_);
151 if ((crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) || isCrossFix) {
152 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
153 } else if (isCrossWrap) {
154 contentIdealSize.SetCrossSize(
155 std::min(GetChildMaxCrossSize(layoutWrapper, axis_), crossSize.value_or(0.0f)), axis_);
156 }
157 contentIdealSize.SetMainSize(totalMainSize_, axis_);
158 AddPaddingToSize(padding, contentIdealSize);
159 layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
160 layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
161 isLayouted_ = false;
162 }
163
UpdateRecycledItems()164 void ListItemGroupLayoutAlgorithm::UpdateRecycledItems()
165 {
166 if (isLayouted_) {
167 return;
168 }
169 for (const auto& item : itemPosition_) {
170 recycledItemPosition_.erase(item.first);
171 }
172 }
173
UpdateCachedItemPosition(int32_t cacheCount)174 void ListItemGroupLayoutAlgorithm::UpdateCachedItemPosition(int32_t cacheCount)
175 {
176 if (!itemPosition_.empty()) {
177 auto iter = cachedItemPosition_.begin();
178 while (iter != cachedItemPosition_.end()) {
179 if ((iter->first >= GetStartIndex() && iter->first <= GetEndIndex()) ||
180 iter->first < (GetStartIndex() - cacheCount) || iter->first > (GetEndIndex() + cacheCount)) {
181 iter = cachedItemPosition_.erase(iter);
182 } else {
183 iter++;
184 }
185 }
186 }
187 }
188
UpdateListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty,OptionalSizeF & contentIdealSize)189 void ListItemGroupLayoutAlgorithm::UpdateListItemGroupMaxWidth(
190 const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty, OptionalSizeF& contentIdealSize)
191 {
192 RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
193 CHECK_NULL_VOID(columnInfo);
194 auto columnParent = columnInfo->GetParent();
195 CHECK_NULL_VOID(columnParent);
196 columnParent->BuildColumnWidth();
197 auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnParent->GetColumns())));
198 float paddingWidth = layoutProperty->CreatePaddingAndBorder().Width();
199 const auto& parentIdealSizeCrossOps = parentIdealSize.CrossSize(axis_);
200 CHECK_NULL_VOID(parentIdealSizeCrossOps);
201 auto parentWidth = parentIdealSizeCrossOps.value() + paddingWidth;
202 auto maxWidth = std::min(parentWidth, maxGridWidth);
203 if (LessNotEqual(maxGridWidth, paddingWidth)) {
204 TAG_LOGI(AceLogTag::ACE_LIST,
205 "ListItemGroup reset to parentWidth since grid_col width:%{public}f, border:%{public}f",
206 maxGridWidth, paddingWidth);
207 maxWidth = parentWidth;
208 }
209 contentIdealSize.SetCrossSize(maxWidth - paddingWidth, axis_);
210 }
211
Layout(LayoutWrapper * layoutWrapper)212 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
213 {
214 const auto& layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
215 CHECK_NULL_VOID(layoutProperty);
216 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
217 auto padding = layoutProperty->CreatePaddingAndBorder();
218 MinusPaddingToSize(padding, size);
219 auto left = padding.left.value_or(0.0f);
220 auto top = padding.top.value_or(0.0f);
221 auto paddingOffset = OffsetF(left, top);
222 float crossSize = GetCrossAxisSize(size, axis_);
223 CHECK_NULL_VOID(listLayoutProperty_);
224 itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
225 bool show = listLayoutProperty_->GetShowCachedItemsValue(false);
226 layoutProperty->UpdateListLanes(listLayoutProperty_->GetLanes(),
227 listLayoutProperty_->GetLaneMinLength(), listLayoutProperty_->GetLaneMaxLength());
228
229 if (cacheParam_) {
230 LayoutCacheItem(layoutWrapper, paddingOffset, crossSize, show);
231 CheckUpdateGroupAndItemPos(layoutWrapper, paddingOffset, crossSize);
232 } else {
233 LayoutListItem(layoutWrapper, paddingOffset, crossSize);
234 ResetLayoutItem(layoutWrapper);
235 }
236 ReverseItemPosition(cachedItemPosition_, totalItemCount_, totalMainSize_);
237 ReverseItemPosition(itemPosition_, totalItemCount_, totalMainSize_);
238 if (isStackFromEnd_) {
239 std::swap(headerMainSize_, footerMainSize_);
240 }
241 SetActiveChildRange(layoutWrapper, listLayoutProperty_->GetCachedCountWithDefault(), show);
242 if (headerIndex_ >= 0 || footerIndex_ >= 0) {
243 if (layoutDirection_ == TextDirection::RTL && axis_ == Axis::HORIZONTAL) {
244 LayoutHeaderFooterRTL(layoutWrapper, paddingOffset, crossSize);
245 } else {
246 LayoutHeaderFooterLTR(layoutWrapper, paddingOffset, crossSize);
247 }
248 }
249 isLayouted_ = true;
250 }
251
CheckUpdateGroupAndItemPos(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)252 void ListItemGroupLayoutAlgorithm::CheckUpdateGroupAndItemPos(LayoutWrapper* layoutWrapper,
253 const OffsetF& paddingOffset, float crossSize)
254 {
255 if (childrenSize_ || (NearZero(adjustReferenceDelta_) && NearZero(adjustTotalSize_))) {
256 return;
257 }
258 auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
259 if (axis_ == Axis::VERTICAL) {
260 offset = isStackFromEnd_ ? offset - OffsetF(0.0f, adjustTotalSize_ + adjustReferenceDelta_) :
261 offset + OffsetF(0.0f, adjustReferenceDelta_);
262 } else {
263 if (layoutDirection_ == TextDirection::RTL) {
264 offset = isStackFromEnd_ ? offset + OffsetF(adjustReferenceDelta_, 0.0f) :
265 offset - OffsetF(adjustTotalSize_ + adjustReferenceDelta_, 0.0f);
266 } else {
267 offset = isStackFromEnd_ ? offset - OffsetF(adjustTotalSize_ + adjustReferenceDelta_, 0.0f) :
268 offset + OffsetF(adjustReferenceDelta_, 0.0f);
269 }
270 }
271 layoutWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
272 LayoutListItem(layoutWrapper, paddingOffset, crossSize);
273 }
274
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)275 void ListItemGroupLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
276 {
277 CHECK_NULL_VOID(wrapper);
278 auto host = wrapper->GetHostNode();
279 CHECK_NULL_VOID(host);
280 host->ForceSyncGeometryNode();
281 host->ResetLayoutAlgorithm();
282 }
283
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const284 bool ListItemGroupLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
285 {
286 if (layoutWrapper->CheckNeedForceMeasureAndLayout() || layoutWrapper->IsIgnoreOptsValid()) {
287 return true;
288 }
289 auto geometryNode = layoutWrapper->GetGeometryNode();
290 CHECK_NULL_RETURN(geometryNode, true);
291 auto constraint = geometryNode->GetParentLayoutConstraint();
292 CHECK_NULL_RETURN(constraint, true);
293 return constraint.value() != childLayoutConstraint_;
294 }
295
MeasureHeaderFooter(LayoutWrapper * layoutWrapper)296 void ListItemGroupLayoutAlgorithm::MeasureHeaderFooter(LayoutWrapper* layoutWrapper)
297 {
298 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
299 CHECK_NULL_VOID(layoutProperty);
300 auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
301 headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
302 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
303 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
304 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
305 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
306 if (headerWrapper) {
307 headerWrapper->Measure(headerFooterLayoutConstraint);
308 headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
309 }
310 if (footerWrapper) {
311 footerWrapper->Measure(headerFooterLayoutConstraint);
312 footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
313 }
314 }
315
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)316 void ListItemGroupLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
317 {
318 if (measureInNextFrame_) {
319 return;
320 }
321 if (!itemPosition_.empty()) {
322 auto start = itemStartIndex_ + itemPosition_.begin()->first;
323 auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
324 int32_t cachedCountForward = cachedItemPosition_.empty() ? 0 :
325 std::max(itemStartIndex_ + cachedItemPosition_.rbegin()->first - end, 0);
326 int32_t cachedCountBackward = cachedItemPosition_.empty() ? 0 :
327 std::max(start - itemStartIndex_ - cachedItemPosition_.begin()->first, 0);
328 if (pauseMeasureCacheItem_ != -1 && pauseMeasureCacheItem_ < start) {
329 cachedCountBackward += 1;
330 } else if (pauseMeasureCacheItem_ != -1 && pauseMeasureCacheItem_ > end) {
331 cachedCountForward += 1;
332 }
333 layoutWrapper->SetActiveChildRange(start, end, cachedCountBackward, cachedCountForward, show);
334 return;
335 } else if (show && (!cachedItemPosition_.empty() || pauseMeasureCacheItem_ != -1)) {
336 int32_t start = cachedItemPosition_.empty() ? pauseMeasureCacheItem_ : cachedItemPosition_.begin()->first;
337 int32_t end = cachedItemPosition_.empty() ? pauseMeasureCacheItem_ : cachedItemPosition_.rbegin()->first;
338 int32_t count = end - start + 1;
339 if (start == 0) {
340 layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, count, show);
341 } else if (end == totalItemCount_ - 1) {
342 int32_t endLimit = end + itemStartIndex_ + 1;
343 layoutWrapper->SetActiveChildRange(endLimit, endLimit, count, 0, show);
344 }
345 return;
346 }
347 auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
348 auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
349 if (LessNotEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_))) {
350 int32_t index = totalItemCount_ + itemStartIndex_;
351 layoutWrapper->SetActiveChildRange(index, index, cacheCount * lanes_, 0);
352 } else {
353 layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount * lanes_);
354 }
355 }
356
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)357 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
358 LayoutConstraintF& contentConstraint)
359 {
360 contentConstraint.parentIdealSize = selfIdealSize;
361 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
362 auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
363 if (crossSizeOptional.has_value()) {
364 float crossSize = crossSizeOptional.value();
365 if (lanes_ > 1) {
366 crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
367 crossSize = crossSize <= 0 ? 1 : crossSize;
368 }
369 if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
370 crossSize = maxLaneLength_.value();
371 }
372 contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
373 contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
374 contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
375 if (minLaneLength_.has_value()) {
376 contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
377 }
378 }
379 }
380
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)381 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
382 {
383 float maxCrossSize = 0.0f;
384 for (const auto& pos : itemPosition_) {
385 auto wrapper = GetListItem(layoutWrapper, pos.first, false);
386 if (!wrapper) {
387 continue;
388 }
389 auto getGeometryNode = wrapper->GetGeometryNode();
390 if (!getGeometryNode) {
391 continue;
392 }
393 maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
394 }
395 for (const auto& pos : cachedItemPosition_) {
396 auto wrapper = GetListItem(layoutWrapper, pos.first, false);
397 if (!wrapper) {
398 continue;
399 }
400 auto getGeometryNode = wrapper->GetGeometryNode();
401 if (!getGeometryNode) {
402 continue;
403 }
404 maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
405 }
406 return maxCrossSize;
407 }
408
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)409 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
410 RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
411 {
412 CHECK_NULL_RETURN(layoutProperty, referencePos);
413 const auto& padding = layoutProperty->CreatePaddingAndBorder();
414 const auto& margin = layoutProperty->CreateMargin();
415 auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
416 auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
417 offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
418 offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
419 forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
420 return referencePos;
421 }
422
NeedMeasureItem(LayoutWrapper * layoutWrapper)423 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem(LayoutWrapper* layoutWrapper)
424 {
425 auto contentMainSize = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
426 if (NearZero(contentMainSize)) {
427 return true;
428 }
429 bool layoutedEntirely = layoutedItemInfo_ && layoutedItemInfo_.value().startIndex <= 0 &&
430 layoutedItemInfo_.value().endIndex >= totalItemCount_ - 1;
431 if (forwardLayout_) {
432 if (childrenSize_ && needAdjustRefPos_) {
433 referencePos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
434 refPos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
435 }
436 if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
437 return false;
438 }
439 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
440 auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
441 auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
442 bool belowList = GreatOrEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_));
443 if (belowList && totalItemCount_ > 0 && !layoutedEntirely) {
444 return true;
445 }
446 return false;
447 }
448 } else {
449 if (childrenSize_ && needAdjustRefPos_) {
450 referencePos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
451 refPos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
452 }
453 if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
454 auto listPadding = GetMainAxisOffset(listLayoutProperty_->CreatePaddingAndBorder().Offset(), axis_);
455 auto offset = GetMainAxisOffset(layoutWrapper->GetGeometryNode()->GetMarginFrameOffset(), axis_);
456 auto groupBottom = offset + totalMainSize_;
457 auto aboveList = LessOrEqual(groupBottom, listPadding);
458 if (aboveList && totalItemCount_ > 0 && !layoutedEntirely) {
459 return true;
460 }
461 return false;
462 }
463 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
464 return false;
465 }
466 }
467 return true;
468 }
469
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)470 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
471 const LayoutConstraintF& layoutConstraint, float startPos)
472 {
473 int32_t currentIndex = -1;
474 float currentEndPos = startPos;
475 float currentStartPos = 0.0f;
476 while (currentIndex < totalItemCount_) {
477 currentStartPos = currentEndPos;
478 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
479 currentStartPos, currentEndPos);
480 if (count == 0) {
481 break;
482 }
483 if (currentIndex < (totalItemCount_ - 1)) {
484 currentEndPos += spaceWidth_;
485 }
486 }
487 }
488
ClearItemPosition()489 void ListItemGroupLayoutAlgorithm::ClearItemPosition()
490 {
491 itemPosition_.clear();
492 cachedItemPosition_.clear();
493 layoutedItemInfo_.reset();
494 forwardCachedIndex_ = -1;
495 backwardCachedIndex_ = INT_MAX;
496 }
497
CheckNeedAllLayout(const RefPtr<LayoutWrapper> & layoutWrapper,bool forwardLayout)498 void ListItemGroupLayoutAlgorithm::CheckNeedAllLayout(const RefPtr<LayoutWrapper>& layoutWrapper, bool forwardLayout)
499 {
500 if (itemPosition_.empty()) {
501 needAllLayout_ = true;
502 return;
503 }
504 int32_t totalItemCount = layoutWrapper->GetTotalChildCount() - itemStartIndex_ - footerCount_;
505 if (!(forwardLayout && itemPosition_.rbegin()->first == totalItemCount - 1) &&
506 !(!forwardLayout && itemPosition_.begin()->first == 0)) {
507 needAllLayout_ = true;
508 }
509 }
510
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)511 void ListItemGroupLayoutAlgorithm::MeasureListItem(
512 LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
513 {
514 if (totalItemCount_ <= 0) {
515 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
516 adjustReferenceDelta_ = totalMainSize_ - (headerMainSize_ + footerMainSize_);
517 }
518 totalMainSize_ = headerMainSize_ + footerMainSize_;
519 itemPosition_.clear();
520 cachedItemPosition_.clear();
521 layoutedItemInfo_.reset();
522 return;
523 }
524 int32_t startIndex = 0;
525 int32_t endIndex = totalItemCount_ - 1;
526 float startPos = headerMainSize_;
527 float endPos = totalMainSize_ - footerMainSize_;
528 prevStartPos_ = startPos_;
529 prevEndPos_ = endPos_;
530 if (needAllLayout_) {
531 needAllLayout_ = false;
532 itemPosition_.clear();
533 cachedItemPosition_.clear();
534 LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
535 return;
536 }
537 if (targetIndex_) {
538 startPos_ = -Infinity<float>();
539 endPos_ = Infinity<float>();
540 targetIndex_ = isStackFromEnd_ ? totalItemCount_ - targetIndex_.value() - 1 : targetIndex_.value();
541 }
542 if (jumpIndex_.has_value()) {
543 if (jumpIndex_.value() == LAST_ITEM) {
544 jumpIndex_ = totalItemCount_ - 1;
545 }
546 auto jumpIndex = isStackFromEnd_ ? totalItemCount_ - jumpIndex_.value() - 1 : jumpIndex_.value();
547 if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
548 jumpIndex = 0;
549 }
550 if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
551 scrollAlign_ == ScrollAlign::AUTO) {
552 startIndex = jumpIndex;
553 } else if (scrollAlign_ == ScrollAlign::END) {
554 endIndex = jumpIndex;
555 } else if (forwardLayout_) {
556 startIndex = jumpIndex;
557 } else {
558 endIndex = jumpIndex;
559 }
560 itemPosition_.clear();
561 cachedItemPosition_.clear();
562 jumpIndex_.reset();
563 layoutedItemInfo_.reset();
564 } else if (!itemPosition_.empty()) {
565 if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
566 startPos = itemPosition_.begin()->second.startPos;
567 }
568 startIndex = GetStartIndex();
569 if (startIndex >= totalItemCount_) {
570 startIndex = totalItemCount_ - 1;
571 if (itemPosition_.begin()->first > 0) {
572 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
573 GetLanesFloor(startIndex) + headerMainSize_;
574 }
575 }
576 if (!isNeedMeasureFormLastItem_) {
577 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
578 endPos = itemPosition_.rbegin()->second.endPos;
579 }
580 if (forwardLayout_) {
581 ModifyReferencePos(GetLanesFloor(startIndex), startPos);
582 } else {
583 ModifyReferencePos(GetLanesCeil(endIndex), endPos);
584 }
585 itemPosition_.clear();
586 } else if (!NeedMeasureItem(layoutWrapper)) {
587 itemPosition_.clear();
588 return;
589 }
590 if (scrollAlign_ == ScrollAlign::CENTER) {
591 startIndex = GetLanesFloor(startIndex);
592 MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
593 } else if (scrollAlign_ == ScrollAlign::START) {
594 startIndex = GetLanesFloor(startIndex);
595 MeasureStart(layoutWrapper, layoutConstraint, startIndex);
596 } else if (scrollAlign_ == ScrollAlign::END) {
597 endIndex = GetLanesCeil(endIndex);
598 MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
599 } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
600 startIndex = GetLanesFloor(startIndex);
601 MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
602 } else if (forwardLayout_) {
603 startIndex = GetLanesFloor(startIndex);
604 if (!CheckJumpForwardForBigOffset(startIndex, startPos)) {
605 startPos = childrenSize_ ? posMap_->GetPos(startIndex) : startPos;
606 MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
607 }
608 } else {
609 endIndex = GetLanesCeil(endIndex);
610 if (!CheckJumpBackwardForBigOffset(endIndex, endPos)) {
611 endPos = childrenSize_ ? posMap_->GetPos(endIndex) + posMap_->GetRowHeight(endIndex) : endPos;
612 MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
613 }
614 }
615 }
616
GetItemGroupPosition(int32_t index)617 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
618 {
619 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
620 index = isStackFromEnd_ ? totalItemCount_ - index - 1 : index;
621 if (scrollAlign_ == ScrollAlign::CENTER) {
622 auto pos = itemPosition_.find(index);
623 if (pos != itemPosition_.end()) {
624 float refPos = (pos->second.endPos + pos->second.startPos) / 2 + paddingBeforeContent_; // 2:average
625 float delta = (startPos_ + endPos_) / 2 - refPos;
626 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
627 }
628 } else if (scrollAlign_ == ScrollAlign::START) {
629 auto pos = itemPosition_.find(index);
630 if (pos != itemPosition_.end()) {
631 float top = startPos_ + contentStartOffset_;
632 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
633 top += headerMainSize_;
634 }
635 float refPos = pos->second.startPos + paddingBeforeContent_;
636 float delta = top - refPos;
637 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
638 }
639 } else if (scrollAlign_ == ScrollAlign::END) {
640 auto pos = itemPosition_.find(index);
641 if (pos != itemPosition_.end()) {
642 float bottom = endPos_ - contentEndOffset_;
643 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
644 bottom -= footerMainSize_;
645 }
646 float refPos = pos->second.endPos + paddingBeforeContent_;
647 float delta = bottom - refPos;
648 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
649 }
650 }
651 return { 0.0f, 0.0f };
652 }
653
GetItemHeight(int32_t index)654 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
655 {
656 auto it = itemPosition_.find(index);
657 if (it != itemPosition_.end()) {
658 return it->second.endPos - it->second.startPos;
659 }
660 return 0.0f;
661 }
662
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)663 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
664 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
665 {
666 auto wrapper = GetListItem(layoutWrapper, currentIndex);
667 if (!wrapper) {
668 ReportGetChildError("MeasureALineAuto", currentIndex);
669 return 0;
670 }
671 if (CheckNeedMeasure(wrapper)) {
672 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
673 wrapper->Measure(layoutConstraint);
674 }
675 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
676 int32_t id = wrapper->GetHostNode()->GetId();
677 itemPosition_[currentIndex] = { id, 0.0f, mainLen };
678 return 1;
679 }
680
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)681 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
682 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
683 {
684 float mainLen = 0;
685 int32_t cnt = 0;
686 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
687 for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
688 auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
689 if (!wrapper) {
690 ReportGetChildError("MeasureALineCenter", currentIndex + cnt);
691 break;
692 }
693 if (CheckNeedMeasure(wrapper)) {
694 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
695 wrapper->Measure(layoutConstraint);
696 }
697 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
698 cnt++;
699 }
700 if (cnt > 0) {
701 auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
702 auto endPos = startPos + mainLen; // 2:average
703 for (int32_t i = 0; i < cnt; i++) {
704 auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
705 int32_t id = wrapper->GetHostNode()->GetId();
706 itemPosition_[currentIndex + i] = { id, startPos, endPos };
707 }
708 }
709 return cnt;
710 }
711
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)712 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
713 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
714 {
715 float mainLen = 0.0f;
716 int32_t cnt = 0;
717 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
718 if (currentIndex + 1 >= totalItemCount_) {
719 return cnt;
720 }
721 for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_ - 1; i++) {
722 auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
723 if (!wrapper) {
724 ReportGetChildError("MeasureALineForward", currentIndex + 1);
725 break;
726 }
727 cnt++;
728 ++currentIndex;
729 if (CheckNeedMeasure(wrapper)) {
730 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
731 wrapper->Measure(layoutConstraint);
732 }
733 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
734 }
735 if (cnt > 0) {
736 endPos = startPos + mainLen;
737 for (int32_t i = 0; i < cnt; i++) {
738 auto wrapper = GetListItem(layoutWrapper, currentIndex - i);
739 int32_t id = wrapper->GetHostNode()->GetId();
740 itemPosition_[currentIndex - i] = { id, startPos, endPos };
741 }
742 }
743 return cnt;
744 }
745
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)746 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
747 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
748 {
749 float mainLen = 0.0f;
750 int32_t cnt = 0;
751 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
752 for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
753 auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
754 if (!wrapper) {
755 ReportGetChildError("MeasureALineBackward", currentIndex - 1);
756 break;
757 }
758 --currentIndex;
759 cnt++;
760 if (CheckNeedMeasure(wrapper)) {
761 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
762 wrapper->Measure(layoutConstraint);
763 }
764 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
765 if (currentIndex % lanes == 0) {
766 break;
767 }
768 }
769 if (cnt > 0) {
770 startPos = endPos - mainLen;
771 for (int32_t i = 0; i < cnt; i++) {
772 auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
773 int32_t id = wrapper->GetHostNode()->GetId();
774 itemPosition_[currentIndex + i] = { id, startPos, endPos };
775 }
776 }
777 return cnt;
778 }
779
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)780 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
781 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
782 {
783 MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
784 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
785 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
786
787 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
788 float currentStartPos = headerMainSize_;
789 int32_t i = 0;
790 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
791 for (auto& pos : itemPosition_) {
792 float len = pos.second.endPos - pos.second.startPos;
793 pos.second.startPos = currentStartPos;
794 pos.second.endPos = currentStartPos + len;
795 i++;
796 if (i % lanes == 0) {
797 currentStartPos = pos.second.endPos + spaceWidth_;
798 }
799 }
800 }
801
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)802 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
803 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
804 {
805 if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
806 return;
807 }
808
809 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
810 }
811
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)812 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
813 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
814 {
815 float currentStartPos = startPos;
816 float currentEndPos = startPos;
817 int32_t currentIndex = startIndex - 1;
818 while (LessOrEqual(currentEndPos, endPos_)) {
819 if (ReachResponseDeadline(layoutWrapper)) {
820 measureInNextFrame_ = true;
821 return;
822 }
823 currentStartPos = currentEndPos;
824 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
825 currentStartPos, currentEndPos);
826 if (count == 0) {
827 break;
828 }
829 if (currentIndex < (totalItemCount_ - 1)) {
830 currentEndPos += spaceWidth_;
831 }
832 }
833 }
834
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)835 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
836 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
837 {
838 float currentEndPos = endPos;
839 float currentStartPos = endPos;
840 int32_t currentIndex = endIndex + 1;
841 while (GreatOrEqual(currentStartPos, startPos_)) {
842 if (ReachResponseDeadline(layoutWrapper)) {
843 measureInNextFrame_ = true;
844 return;
845 }
846 currentEndPos = currentStartPos;
847 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
848 currentEndPos, currentStartPos);
849 if (count == 0) {
850 break;
851 }
852 if (currentIndex > 0) {
853 currentStartPos -= spaceWidth_;
854 }
855 }
856 }
857
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)858 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
859 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
860 {
861 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
862 float currentStartPos = startPos_ + contentStartOffset_;
863 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
864 currentStartPos += headerMainSize_;
865 }
866
867 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
868 if (GreatNotEqual(currentStartPos, startPos_)) {
869 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
870 }
871
872 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
873 currentStartPos = headerMainSize_;
874 int32_t i = 0;
875 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
876 for (auto& pos : itemPosition_) {
877 float len = pos.second.endPos - pos.second.startPos;
878 pos.second.startPos = currentStartPos;
879 pos.second.endPos = currentStartPos + len;
880 i++;
881 if (i % lanes == 0) {
882 currentStartPos = pos.second.endPos + spaceWidth_;
883 }
884 }
885 }
886
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)887 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
888 const LayoutConstraintF& layoutConstraint, int32_t endIndex)
889 {
890 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
891 float currentEndPos = endPos_ - contentEndOffset_;
892 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
893 currentEndPos -= footerMainSize_;
894 }
895
896 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
897 if (LessNotEqual(currentEndPos, endPos_)) {
898 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
899 }
900
901 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
902 float currentStartPos = headerMainSize_;
903 int32_t i = 0;
904 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
905 for (auto& pos : itemPosition_) {
906 float len = pos.second.endPos - pos.second.startPos;
907 pos.second.startPos = currentStartPos;
908 pos.second.endPos = currentStartPos + len;
909 i++;
910 if (i % lanes == 0) {
911 currentStartPos = pos.second.endPos + spaceWidth_;
912 }
913 }
914 }
915
CheckJumpForwardForBigOffset(int32_t & startIndex,float & startPos)916 bool ListItemGroupLayoutAlgorithm::CheckJumpForwardForBigOffset(int32_t& startIndex, float& startPos)
917 {
918 if (!isNeedCheckOffset_ || childrenSize_) {
919 return false;
920 }
921 float th = startPos_ - (endPos_ - startPos_) - referencePos_;
922 if (GreatNotEqual(startPos, th)) {
923 return false;
924 }
925 float averageHeight = groupItemAverageHeight_;
926 if (layoutedItemInfo_.has_value()) {
927 const auto& itemInfo = layoutedItemInfo_.value();
928 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
929 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
930 averageHeight = totalHeight / itemCount;
931 }
932 if (Positive(averageHeight)) {
933 float distance = startPos_ - referencePos_ - startPos;
934 int32_t jumpCount = distance / averageHeight;
935 int32_t maxCount = totalItemCount_ - 1 - startIndex;
936 if (jumpCount > maxCount) {
937 float pos = startPos + maxCount * averageHeight;
938 itemPosition_[totalItemCount_ - 1] = { -1, pos, pos + averageHeight - spaceWidth_ };
939 return true;
940 }
941 startPos += jumpCount * averageHeight;
942 startIndex += jumpCount;
943 }
944 return false;
945 }
946
CheckJumpBackwardForBigOffset(int32_t & endIndex,float & endPos)947 bool ListItemGroupLayoutAlgorithm::CheckJumpBackwardForBigOffset(int32_t& endIndex, float& endPos)
948 {
949 if (!isNeedCheckOffset_ || childrenSize_) {
950 return false;
951 }
952 float th = endPos_ + (endPos_ - startPos_) - (referencePos_ - totalMainSize_);
953 if (LessNotEqual(endPos, th)) {
954 return false;
955 }
956 float averageHeight = groupItemAverageHeight_;
957 if (layoutedItemInfo_.has_value()) {
958 const auto& itemInfo = layoutedItemInfo_.value();
959 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
960 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
961 averageHeight = totalHeight / itemCount;
962 }
963 if (Positive(averageHeight)) {
964 float distance = endPos - (endPos_ - (referencePos_ - totalMainSize_));
965 int32_t jumpCount = distance / averageHeight;
966 if (jumpCount > endIndex) {
967 float pos = endPos - endIndex * averageHeight;
968 itemPosition_[0] = { -1, pos - averageHeight + spaceWidth_, pos };
969 return true;
970 }
971 endPos -= jumpCount * averageHeight;
972 endIndex -= jumpCount;
973 }
974 return false;
975 }
976
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)977 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
978 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
979 {
980 float currentEndPos = startPos;
981 float currentStartPos = 0.0f;
982 int32_t currentIndex = startIndex - 1;
983 while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
984 if (ReachResponseDeadline(layoutWrapper)) {
985 measureInNextFrame_ = true;
986 return;
987 }
988 currentStartPos = currentEndPos;
989 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
990 currentStartPos, currentEndPos);
991 if (count == 0) {
992 break;
993 }
994 if (currentIndex < (totalItemCount_ - 1)) {
995 currentEndPos += spaceWidth_;
996 }
997 if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
998 startPos_ = prevStartPos_;
999 endPos_ = prevEndPos_;
1000 targetIndex_.reset();
1001 }
1002 }
1003
1004 currentStartPos = startPos - spaceWidth_;
1005 currentIndex = startIndex;
1006 float th = startPos_ - referencePos_;
1007 if (!prevMeasureBreak_) {
1008 th = std::max(th, headerMainSize_);
1009 }
1010 while (currentIndex > 0 && GreatNotEqual(currentStartPos, th)) {
1011 if (ReachResponseDeadline(layoutWrapper)) {
1012 measureInNextFrame_ = true;
1013 return;
1014 }
1015 currentEndPos = currentStartPos;
1016 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
1017 currentEndPos, currentStartPos);
1018 if (count == 0) {
1019 break;
1020 }
1021 if (currentIndex > 0) {
1022 currentStartPos = currentStartPos - spaceWidth_;
1023 }
1024 }
1025 }
1026
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)1027 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
1028 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
1029 {
1030 float currentStartPos = endPos;
1031 float currentEndPos = 0.0f;
1032 auto currentIndex = endIndex + 1;
1033 while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
1034 if (ReachResponseDeadline(layoutWrapper)) {
1035 measureInNextFrame_ = true;
1036 return;
1037 }
1038 currentEndPos = currentStartPos;
1039 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
1040 currentEndPos, currentStartPos);
1041 if (count == 0) {
1042 break;
1043 }
1044 if (currentIndex > 0) {
1045 currentStartPos = currentStartPos - spaceWidth_;
1046 }
1047 if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
1048 startPos_ = prevStartPos_;
1049 endPos_ = prevEndPos_;
1050 targetIndex_.reset();
1051 }
1052 }
1053 currentIndex = endIndex;
1054 currentEndPos = endPos + spaceWidth_;
1055 while (childrenSize_ && LessOrEqual(currentEndPos, endPos_ - (referencePos_ - totalMainSize_))) {
1056 if (ReachResponseDeadline(layoutWrapper)) {
1057 measureInNextFrame_ = true;
1058 return;
1059 }
1060 currentStartPos = currentEndPos;
1061 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
1062 currentStartPos, currentEndPos);
1063 if (count == 0) {
1064 break;
1065 }
1066 if (currentIndex < (totalItemCount_ - 1)) {
1067 currentEndPos += spaceWidth_;
1068 }
1069 }
1070 }
1071
ModifyReferencePos(int32_t index,float pos)1072 void ListItemGroupLayoutAlgorithm::ModifyReferencePos(int32_t index, float pos)
1073 {
1074 if (!childrenSize_ || !needAdjustRefPos_) {
1075 return;
1076 }
1077 if (forwardLayout_ && Negative(referencePos_)) {
1078 float offset = referencePos_ + pos;
1079 float newReferencePos = offset - posMap_->GetPos(index);
1080 refPos_ = refPos_ + newReferencePos - referencePos_;
1081 referencePos_ = newReferencePos;
1082 } else if (!forwardLayout_ && GreatNotEqual(referencePos_, prevContentMainSize_)) {
1083 float offset = referencePos_ - posMap_->GetPrevTotalHeight() + pos - prevContentMainSize_;
1084 float newReferencePos = offset + endPos_ - startPos_ + totalMainSize_ -
1085 (posMap_->GetPos(index) + posMap_->GetRowHeight(index));
1086 refPos_ = refPos_ + newReferencePos - referencePos_;
1087 referencePos_ = newReferencePos;
1088 }
1089 }
1090
AdjustByPosMap()1091 void ListItemGroupLayoutAlgorithm::AdjustByPosMap()
1092 {
1093 totalMainSize_ = posMap_->GetTotalHeight();
1094 if (itemPosition_.empty()) {
1095 return;
1096 }
1097 float startPos = itemPosition_.begin()->second.startPos;
1098 float offset = posMap_->GetGroupLayoutOffset(GetStartIndex(), startPos);
1099 for (auto& pos : itemPosition_) {
1100 pos.second.startPos += offset;
1101 pos.second.endPos += offset;
1102 }
1103 }
1104
AdjustItemPosition()1105 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
1106 {
1107 if (itemPosition_.empty()) {
1108 return;
1109 }
1110 float currentStartPos = GetStartPosition();
1111 if (currentStartPos < headerMainSize_) {
1112 auto delta = headerMainSize_ - currentStartPos;
1113 for (auto& pos : itemPosition_) {
1114 pos.second.startPos += delta;
1115 pos.second.endPos += delta;
1116 }
1117 for (auto& pos : cachedItemPosition_) {
1118 pos.second.startPos += delta;
1119 pos.second.endPos += delta;
1120 }
1121 totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
1122 adjustReferenceDelta_ = -delta;
1123 } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
1124 auto delta = currentStartPos - headerMainSize_;
1125 for (auto& pos : itemPosition_) {
1126 pos.second.startPos -= delta;
1127 pos.second.endPos -= delta;
1128 }
1129 for (auto& pos : cachedItemPosition_) {
1130 pos.second.startPos -= delta;
1131 pos.second.endPos -= delta;
1132 }
1133 totalMainSize_ -= delta;
1134 adjustReferenceDelta_ = delta;
1135 }
1136 if (GetEndIndex() == totalItemCount_ - 1) {
1137 totalMainSize_ = GetEndPosition() + footerMainSize_;
1138 } else {
1139 float endPos = GetCacheEndIndex() > GetEndIndex() ? GetCacheEndPosition() : GetEndPosition();
1140 totalMainSize_ = std::max(totalMainSize_, endPos + footerMainSize_);
1141 }
1142 }
1143
UpdateLayoutedItemInfo()1144 void ListItemGroupLayoutAlgorithm::UpdateLayoutedItemInfo()
1145 {
1146 if (itemPosition_.empty()) {
1147 return;
1148 }
1149 int32_t startIndex = INT_MAX;
1150 int32_t endIndex = -1;
1151 float startPos = FLT_MAX;
1152 float endPos = -1.0f;
1153 if (!itemPosition_.empty()) {
1154 startIndex = itemPosition_.begin()->first;
1155 endIndex = itemPosition_.rbegin()->first;
1156 startPos = itemPosition_.begin()->second.startPos;
1157 endPos = itemPosition_.rbegin()->second.endPos;
1158 }
1159 if (layoutedItemInfo_.has_value()) {
1160 auto& itemInfo = layoutedItemInfo_.value();
1161 auto prevStartIndex = itemInfo.startIndex;
1162 if (startIndex <= itemInfo.startIndex || LessNotEqual(startPos, itemInfo.startPos) ||
1163 startIndex > itemInfo.endIndex) {
1164 itemInfo.startIndex = startIndex;
1165 itemInfo.startPos = startPos;
1166 }
1167 if (endIndex >= itemInfo.endIndex || GreatNotEqual(endPos, itemInfo.endPos) ||
1168 itemInfo.endIndex > totalItemCount_ - 1 || endIndex < prevStartIndex) {
1169 itemInfo.endIndex = endIndex;
1170 itemInfo.endPos = endPos;
1171 }
1172 } else {
1173 layoutedItemInfo_ = { startIndex, startPos, endIndex, endPos };
1174 }
1175 }
1176
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)1177 void ListItemGroupLayoutAlgorithm::CheckRecycle(
1178 const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
1179 {
1180 referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
1181 // Mark inactive in wrapper.
1182 if (forwardLayout) {
1183 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1184 if (GreatOrEqual(pos->second.endPos, startPos - referencePos)) {
1185 break;
1186 }
1187 recycledItemPosition_.insert_or_assign(pos->first, pos->second);
1188 cachedItemPosition_.insert(*pos);
1189 pos = itemPosition_.erase(pos);
1190 }
1191 return;
1192 }
1193 std::list<int32_t> removeIndexes;
1194 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1195 if (LessOrEqual(pos->second.startPos, endPos - (referencePos - totalMainSize_))) {
1196 break;
1197 }
1198 recycledItemPosition_.insert_or_assign(pos->first, pos->second);
1199 cachedItemPosition_.insert(*pos);
1200 removeIndexes.emplace_back(pos->first);
1201 }
1202 for (const auto& index : removeIndexes) {
1203 itemPosition_.erase(index);
1204 }
1205 }
1206
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1207 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
1208 const OffsetF& paddingOffset, float crossSize)
1209 {
1210 // layout items.
1211 for (auto& pos : itemPosition_) {
1212 auto wrapper = GetListItem(layoutWrapper, pos.first);
1213 if (!wrapper) {
1214 ReportGetChildError("LayoutListItem", pos.first);
1215 continue;
1216 }
1217
1218 auto offset = paddingOffset;
1219 int32_t laneIndex = pos.first % lanes_;
1220 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1221 float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
1222 auto startPos = !isStackFromEnd_ ? pos.second.startPos : totalMainSize_ - pos.second.endPos;
1223 auto endPos = !isStackFromEnd_ ? pos.second.endPos : totalMainSize_ - pos.second.startPos;
1224 if (layoutDirection_ == TextDirection::RTL) {
1225 if (axis_ == Axis::VERTICAL) {
1226 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1227 auto tmpX = crossSize - laneCrossOffset -
1228 ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
1229 offset = offset + OffsetF(tmpX, startPos);
1230 } else {
1231 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
1232 offset = offset + OffsetF(totalMainSize_ - endPos, tmpY);
1233 }
1234 } else {
1235 if (axis_ == Axis::VERTICAL) {
1236 offset =
1237 offset + OffsetF(0, startPos) + OffsetF(laneCrossOffset, 0) +
1238 OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
1239 } else {
1240 offset =
1241 offset + OffsetF(startPos, 0) + OffsetF(0, laneCrossOffset) +
1242 OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
1243 }
1244 }
1245 auto index = isStackFromEnd_ ? totalItemCount_ - pos.first - 1 : pos.first;
1246 SetListItemIndex(layoutWrapper, wrapper, index);
1247 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1248 if (wrapper->CheckNeedForceMeasureAndLayout() || wrapper->IsIgnoreOptsValid()) {
1249 wrapper->Layout();
1250 } else {
1251 SyncGeometry(wrapper);
1252 }
1253 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1254 if (frameNode) {
1255 frameNode->MarkAndCheckNewOpIncNode(axis_);
1256 }
1257 }
1258 }
1259
UpdateZIndex(const RefPtr<LayoutWrapper> & layoutWrapper)1260 void ListItemGroupLayoutAlgorithm::UpdateZIndex(const RefPtr<LayoutWrapper>& layoutWrapper)
1261 {
1262 auto host = layoutWrapper->GetHostNode();
1263 CHECK_NULL_VOID(host);
1264 auto renderContext = host->GetRenderContext();
1265 CHECK_NULL_VOID(renderContext);
1266 renderContext->UpdateZIndex(1);
1267 }
1268
LayoutHeaderFooterRTL(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1269 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterRTL(LayoutWrapper* layoutWrapper,
1270 const OffsetF& paddingOffset, float crossSize)
1271 {
1272 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1273 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1274 float mainPos = GetMainAxisOffset(selfOffset, axis_);
1275 float footerMainSize = 0.0f;
1276 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1277 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1278 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1279 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1280 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1281 if (footerWrapper) {
1282 UpdateZIndex(footerWrapper);
1283 footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1284 float footerPos = 0.0f;
1285 if (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER) {
1286 contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1287 float stickyPos = contentStartOffset_ - mainPos;
1288 stickyPos = std::min(stickyPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1289 footerPos = std::max(footerPos, stickyPos);
1290 footerPos = std::min(footerPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1291 }
1292 LayoutIndex(footerWrapper, paddingOffset, crossSize, footerPos);
1293 endFooterPos_ = endFooterPos_ > mainPos ? mainPos : endFooterPos_;
1294 }
1295
1296 if (headerWrapper) {
1297 float headPos = totalMainSize_ - headerMainSize_;
1298 UpdateZIndex(headerWrapper);
1299 float const listMainSize = endPos_ - startPos_;
1300 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER)) {
1301 auto headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1302 float stickyPos = listMainSize - contentEndOffset_ - mainPos - headerMainSize;
1303 if (stickyPos < footerMainSize) {
1304 stickyPos = footerMainSize;
1305 }
1306 if (stickyPos < headPos) {
1307 headPos = stickyPos;
1308 }
1309 }
1310 LayoutIndex(headerWrapper, paddingOffset, crossSize, headPos);
1311 startHeaderPos_ = mainPos + totalMainSize_ - headerMainSize_ - listMainSize;
1312 }
1313 }
1314
LayoutHeaderFooterLTR(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1315 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterLTR(LayoutWrapper* layoutWrapper,
1316 const OffsetF& paddingOffset, float crossSize)
1317 {
1318 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1319 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1320 float mainPos = GetMainAxisOffset(selfOffset, axis_);
1321 float headerMainSize = 0.0f;
1322 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1323 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1324 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1325 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1326 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1327 if (headerWrapper) {
1328 UpdateZIndex(headerWrapper);
1329 headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1330 float headerPos = 0.0f;
1331 if (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) {
1332 contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1333 float stickyPos = contentStartOffset_ - mainPos;
1334 stickyPos = std::min(stickyPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1335 headerPos = std::max(headerPos, stickyPos);
1336 }
1337 LayoutIndex(headerWrapper, paddingOffset, crossSize, headerPos);
1338 startHeaderPos_ = startHeaderPos_ > mainPos ? mainPos : startHeaderPos_;
1339 }
1340
1341 if (footerWrapper) {
1342 float endPos = totalMainSize_ - footerMainSize_;
1343 UpdateZIndex(footerWrapper);
1344 float const listMainSize = endPos_ - startPos_;
1345 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
1346 auto footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1347 float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
1348 if (stickyPos < headerMainSize) {
1349 stickyPos = headerMainSize;
1350 }
1351 if (stickyPos < endPos) {
1352 endPos = stickyPos;
1353 }
1354 }
1355 LayoutIndex(footerWrapper, paddingOffset, crossSize, endPos);
1356 endFooterPos_ = mainPos + totalMainSize_ - footerMainSize_ - listMainSize;
1357 }
1358 }
1359
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)1360 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
1361 float crossSize, float startPos)
1362 {
1363 CHECK_NULL_VOID(wrapper);
1364 auto offset = paddingOffset;
1365 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1366 float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1367 if (axis_ == Axis::VERTICAL) {
1368 if (layoutDirection_ == TextDirection::RTL) {
1369 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1370 offset = offset + OffsetF(crossSize - laneCrossOffset - size.Width(), startPos);
1371 } else {
1372 offset = offset + OffsetF(laneCrossOffset, startPos);
1373 }
1374 } else {
1375 offset = offset + OffsetF(startPos, laneCrossOffset);
1376 }
1377 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1378 wrapper->Layout();
1379 }
1380
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1381 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1382 {
1383 float delta = crossSize - GetLaneGutter() - childCrossSize;
1384 if (LessOrEqual(delta, 0.0f)) {
1385 return 0.0f;
1386 }
1387 switch (itemAlign_) {
1388 case OHOS::Ace::V2::ListItemAlign::START:
1389 return 0.0f;
1390 case OHOS::Ace::V2::ListItemAlign::CENTER:
1391 return delta / 2; /* 2:average */
1392 case OHOS::Ace::V2::ListItemAlign::END:
1393 return delta;
1394 default:
1395 return 0.0f;
1396 }
1397 }
1398
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)1399 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
1400 const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
1401 {
1402 int32_t lanes = layoutProperty->GetLanes().value_or(1);
1403 lanes = lanes > 1 ? lanes : 1;
1404 if (crossSizeOptional.has_value()) {
1405 if (layoutProperty->GetLaneMinLength().has_value()) {
1406 minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
1407 layoutConstraint.scaleProperty, crossSizeOptional.value());
1408 }
1409 if (layoutProperty->GetLaneMaxLength().has_value()) {
1410 maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
1411 layoutConstraint.scaleProperty, crossSizeOptional.value());
1412 }
1413 if (layoutProperty->GetLaneGutter().has_value()) {
1414 auto laneGutter = ConvertToPx(
1415 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
1416 laneGutter_ = laneGutter.value_or(0.0f);
1417 }
1418 }
1419 lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
1420 minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
1421 }
1422
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)1423 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
1424 const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
1425 {
1426 auto host = itemLayoutWrapper->GetHostNode();
1427 CHECK_NULL_VOID(host);
1428 auto listItem = host->GetPattern<ListItemPattern>();
1429 CHECK_NULL_VOID(listItem);
1430 listItem->SetIndexInListItemGroup(indexInGroup);
1431
1432 host = groupLayoutWrapper->GetHostNode();
1433 CHECK_NULL_VOID(host);
1434 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1435 CHECK_NULL_VOID(listItemGroup);
1436 listItem->SetIndexInList(listItemGroup->GetIndexInList());
1437 }
1438
GetLayoutInfo() const1439 ListItemGroupLayoutInfo ListItemGroupLayoutAlgorithm::GetLayoutInfo() const
1440 {
1441 ListItemGroupLayoutInfo info;
1442 info.headerSize = headerMainSize_;
1443 info.footerSize = footerMainSize_;
1444 info.spaceWidth = spaceWidth_;
1445 if (totalItemCount_ == 0 || childrenSize_) {
1446 info.atStart = true;
1447 info.atEnd = true;
1448 return info;
1449 }
1450 if (layoutedItemInfo_.has_value()) {
1451 const auto& itemInfo = layoutedItemInfo_.value();
1452 info.atStart = itemInfo.startIndex == 0;
1453 info.atEnd = itemInfo.endIndex >= totalItemCount_ - 1;
1454 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
1455 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
1456 info.averageHeight = totalHeight / itemCount;
1457 }
1458 return info;
1459 }
1460
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)1461 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
1462 {
1463 auto host = groupLayoutWrapper->GetHostNode();
1464 CHECK_NULL_RETURN(host, false);
1465 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1466 CHECK_NULL_RETURN(listItemGroup, false);
1467 return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
1468 }
1469
MeasureCacheForward(LayoutWrapper * layoutWrapper,ListItemGroupCacheParam & param)1470 void ListItemGroupLayoutAlgorithm::MeasureCacheForward(LayoutWrapper* layoutWrapper, ListItemGroupCacheParam& param)
1471 {
1472 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
1473 int32_t endIndex = itemPosition_.empty() ? -1 : GetEndIndex();
1474 if (endIndex >= totalItemCount_ - 1) {
1475 return;
1476 }
1477 int32_t limit = std::min(endIndex + param.cacheCountForward * lanes_, totalItemCount_ - 1);
1478 float startPos = itemPosition_.empty() ? headerMainSize_ : GetEndPosition();
1479 int32_t curIndex = GetLanesFloor(endIndex + 1);
1480 while (curIndex <= limit) {
1481 if (GetSysTimestamp() > param.deadline) {
1482 return;
1483 }
1484 float mainLen = 0.0f;
1485 int32_t cnt = 0;
1486 for (int32_t i = 0; i < lanes && curIndex + i < totalItemCount_; i++) {
1487 auto wrapper = GetListItem(layoutWrapper, curIndex + i, param.show, !param.show);
1488 if (!wrapper || !wrapper->GetHostNode()) {
1489 return;
1490 }
1491 if (!wrapper->GetHostNode()->RenderCustomChild(param.deadline)) {
1492 pauseMeasureCacheItem_ = curIndex + i;
1493 return;
1494 }
1495 cnt++;
1496 if (CheckNeedMeasure(wrapper)) {
1497 ACE_SCOPED_TRACE("ListItemGroupLayoutAlgorithm::MeasureCacheForward:%d", curIndex + i);
1498 wrapper->Measure(childLayoutConstraint_);
1499 }
1500 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
1501 }
1502 for (int32_t i = 0; i < cnt; i++) {
1503 cachedItemPosition_[curIndex + i] = { -1, startPos, startPos + mainLen };
1504 }
1505 curIndex += cnt;
1506 startPos += mainLen + spaceWidth_;
1507 param.forwardCachedIndex = curIndex - 1;
1508 }
1509 }
1510
MeasureCacheBackward(LayoutWrapper * layoutWrapper,ListItemGroupCacheParam & param)1511 void ListItemGroupLayoutAlgorithm::MeasureCacheBackward(LayoutWrapper* layoutWrapper, ListItemGroupCacheParam& param)
1512 {
1513 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
1514 int32_t startIndex = itemPosition_.empty() ? totalItemCount_ : GetStartIndex();
1515 if (startIndex <= 0) {
1516 return;
1517 }
1518 int32_t limit = std::max(startIndex - param.cacheCountBackward * lanes_, 0);
1519 if (limit % lanes_ != 0) {
1520 limit += (lanes_ - limit % lanes_);
1521 }
1522 float endPos = itemPosition_.empty() ? totalMainSize_ - footerMainSize_ : GetStartPosition();
1523 int32_t curIndex = GetLanesCeil(startIndex - 1);
1524 while (curIndex >= limit) {
1525 if (GetSysTimestamp() > param.deadline) {
1526 return;
1527 }
1528 float mainLen = 0.0f;
1529 int32_t cnt = 0;
1530 for (int32_t i = 0; i < lanes && curIndex - i >= 0; i++) {
1531 auto wrapper = GetListItem(layoutWrapper, curIndex - i, param.show, !param.show);
1532 if (!wrapper || !wrapper->GetHostNode()) {
1533 return;
1534 }
1535 if (!wrapper->GetHostNode()->RenderCustomChild(param.deadline)) {
1536 pauseMeasureCacheItem_ = curIndex + i;
1537 return;
1538 }
1539 cnt++;
1540 if (CheckNeedMeasure(wrapper)) {
1541 ACE_SCOPED_TRACE("ListItemGroupLayoutAlgorithm::MeasureCacheForward:%d", curIndex - i);
1542 wrapper->Measure(childLayoutConstraint_);
1543 }
1544 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
1545 if (0 == (curIndex - i) % lanes) {
1546 break;
1547 }
1548 }
1549 for (int32_t i = 0; i < cnt; i++) {
1550 cachedItemPosition_[curIndex - i] = { -1, endPos - mainLen, endPos };
1551 }
1552 curIndex -= cnt;
1553 endPos -= (mainLen + spaceWidth_);
1554 param.backwardCachedIndex = curIndex + 1;
1555 }
1556 }
1557
MeasureCacheItem(LayoutWrapper * layoutWrapper)1558 void ListItemGroupLayoutAlgorithm::MeasureCacheItem(LayoutWrapper* layoutWrapper)
1559 {
1560 pauseMeasureCacheItem_ = -1;
1561 ListItemGroupCacheParam& param = cacheParam_.value();
1562 if (param.forward) {
1563 MeasureCacheForward(layoutWrapper, param);
1564 }
1565 if (param.backward) {
1566 MeasureCacheBackward(layoutWrapper, param);
1567 }
1568 if (cachedItemPosition_.empty()) {
1569 return;
1570 }
1571 float originHeight = totalMainSize_;
1572 float currentStartPos = GetCacheStartPosition();
1573 if (currentStartPos < headerMainSize_) {
1574 auto delta = headerMainSize_ - currentStartPos;
1575 for (auto& pos : itemPosition_) {
1576 pos.second.startPos += delta;
1577 pos.second.endPos += delta;
1578 }
1579 for (auto& pos : cachedItemPosition_) {
1580 pos.second.startPos += delta;
1581 pos.second.endPos += delta;
1582 }
1583 totalMainSize_ = std::max(totalMainSize_ + delta,
1584 std::max(GetCacheEndPosition(), GetEndPosition()) + footerMainSize_);
1585 adjustReferenceDelta_ = -delta;
1586 } else if (GetCacheStartIndex() == 0 && currentStartPos > headerMainSize_) {
1587 auto delta = currentStartPos - headerMainSize_;
1588 for (auto& pos : itemPosition_) {
1589 pos.second.startPos -= delta;
1590 pos.second.endPos -= delta;
1591 }
1592 for (auto& pos : cachedItemPosition_) {
1593 pos.second.startPos -= delta;
1594 pos.second.endPos -= delta;
1595 }
1596 totalMainSize_ -= delta;
1597 adjustReferenceDelta_ = delta;
1598 }
1599 if (GetCacheEndIndex() == totalItemCount_ - 1) {
1600 totalMainSize_ = GetCacheEndPosition() + footerMainSize_;
1601 } else {
1602 float endPos = GetCacheEndIndex() > GetEndIndex() || itemPosition_.empty() ?
1603 GetCacheEndPosition() : GetEndPosition();
1604 totalMainSize_ = std::max(totalMainSize_, endPos + footerMainSize_);
1605 }
1606 adjustTotalSize_ = totalMainSize_ - originHeight;
1607 }
1608
LayoutCacheItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize,bool show)1609 void ListItemGroupLayoutAlgorithm::LayoutCacheItem(LayoutWrapper* layoutWrapper,
1610 const OffsetF& paddingOffset, float crossSize, bool show)
1611 {
1612 for (auto& pos : cachedItemPosition_) {
1613 auto wrapper = GetListItem(layoutWrapper, pos.first, show, !show);
1614 if (!wrapper) {
1615 continue;
1616 }
1617 auto offset = paddingOffset;
1618 int32_t laneIndex = pos.first % lanes_;
1619 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1620 float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
1621 auto startPos = !isStackFromEnd_ ? pos.second.startPos : totalMainSize_ - pos.second.endPos;
1622 auto endPos = !isStackFromEnd_ ? pos.second.endPos : totalMainSize_ - pos.second.startPos;
1623 if (layoutDirection_ == TextDirection::RTL) {
1624 if (axis_ == Axis::VERTICAL) {
1625 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1626 auto tmpX = crossSize - laneCrossOffset -
1627 ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
1628 offset = offset + OffsetF(tmpX, startPos);
1629 } else {
1630 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
1631 offset = offset + OffsetF(totalMainSize_ - endPos, tmpY);
1632 }
1633 } else {
1634 if (axis_ == Axis::VERTICAL) {
1635 offset =
1636 offset + OffsetF(0, startPos) + OffsetF(laneCrossOffset, 0) +
1637 OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
1638 } else {
1639 offset =
1640 offset + OffsetF(startPos, 0) + OffsetF(0, laneCrossOffset) +
1641 OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
1642 }
1643 }
1644 auto index = isStackFromEnd_ ? totalItemCount_ - pos.first - 1 : pos.first;
1645 SetListItemIndex(layoutWrapper, wrapper, index);
1646 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1647 auto host = wrapper->GetHostNode();
1648 if (wrapper->CheckNeedForceMeasureAndLayout() && host && !host->IsLayoutComplete()) {
1649 wrapper->Layout();
1650 } else {
1651 SyncGeometry(wrapper);
1652 }
1653 }
1654 }
1655
ReverseItemPosition(ListItemGroupLayoutAlgorithm::PositionMap & itemPosition,int32_t totalItemCount,float mainSize)1656 void ListItemGroupLayoutAlgorithm::ReverseItemPosition(
1657 ListItemGroupLayoutAlgorithm::PositionMap& itemPosition, int32_t totalItemCount, float mainSize)
1658 {
1659 if (!isStackFromEnd_ || itemPosition.empty()) {
1660 return;
1661 }
1662 ListItemGroupLayoutAlgorithm::PositionMap posMap;
1663 for (auto pos : itemPosition) {
1664 auto startPos = mainSize - pos.second.endPos;
1665 auto endPos = mainSize - pos.second.startPos;
1666 pos.second.startPos = startPos;
1667 pos.second.endPos = endPos;
1668 posMap[totalItemCount - pos.first - 1] = pos.second;
1669 }
1670 itemPosition = std::move(posMap);
1671 }
1672
ReverseLayoutedItemInfo(int32_t totalItemCount,float mainSize)1673 void ListItemGroupLayoutAlgorithm::ReverseLayoutedItemInfo(int32_t totalItemCount, float mainSize)
1674 {
1675 if (!isStackFromEnd_ || !layoutedItemInfo_.has_value()) {
1676 return;
1677 }
1678 auto& itemInfo = layoutedItemInfo_.value();
1679 auto startIndex = totalItemCount - itemInfo.endIndex - 1;
1680 auto startPos = mainSize - itemInfo.endPos;
1681 auto endIndex = totalItemCount - itemInfo.startIndex - 1;
1682 auto endPos = mainSize - itemInfo.startPos;
1683 layoutedItemInfo_ = { startIndex, startPos, endIndex, endPos };
1684 }
1685
ReportGetChildError(const std::string & funcName,int32_t index) const1686 void ListItemGroupLayoutAlgorithm::ReportGetChildError(const std::string& funcName, int32_t index) const
1687 {
1688 if (index < 0 || index > totalItemCount_ - 1) {
1689 return;
1690 }
1691 std::string subErrorType = funcName + " get item: " + std::to_string(index) + " failed.";
1692 EventReport::ReportScrollableErrorEvent("ListItemGroup", ScrollableErrorType::GET_CHILD_FAILED, subErrorType);
1693 }
1694
IsRoundingMode(LayoutWrapper * layoutWrapper)1695 bool ListItemGroupLayoutAlgorithm::IsRoundingMode(LayoutWrapper* layoutWrapper)
1696 {
1697 auto host = layoutWrapper->GetHostNode();
1698 CHECK_NULL_RETURN(host, false);
1699 auto pipeline = host->GetContext();
1700 CHECK_NULL_RETURN(pipeline, false);
1701 return pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE;
1702 }
1703
ResetLayoutItem(LayoutWrapper * layoutWrapper)1704 void ListItemGroupLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1705 {
1706 for (auto& pos : recycledItemPosition_) {
1707 auto wrapper = GetListItem(layoutWrapper, pos.first);
1708 auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper);
1709 if (wrapperFrameNode) {
1710 wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1711 }
1712 }
1713 }
1714 } // namespace OHOS::Ace::NG
1715