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_item_group_layout_algorithm.h"
17
18 #include "base/utils/utils.h"
19 #include "core/components/common/layout/grid_system_manager.h"
20 #include "core/components_ng/pattern/list/list_item_group_layout_property.h"
21 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
22 #include "core/components_ng/pattern/list/list_item_pattern.h"
23 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
24 #include "core/components_ng/property/measure_utils.h"
25
26 namespace OHOS::Ace::NG {
27
28 namespace {
29 constexpr uint32_t GRID_COUNTS_4 = 4;
30 constexpr uint32_t GRID_COUNTS_6 = 6;
31 constexpr uint32_t GRID_COUNTS_8 = 8;
32 constexpr uint32_t GRID_COUNTS_12 = 12;
33
GetMaxGridCounts(const RefPtr<GridColumnInfo> & columnInfo)34 uint32_t GetMaxGridCounts(const RefPtr<GridColumnInfo>& columnInfo)
35 {
36 CHECK_NULL_RETURN(columnInfo, GRID_COUNTS_8);
37 auto currentColumns = columnInfo->GetParent()->GetColumns();
38 auto maxGridCounts = GRID_COUNTS_8;
39 switch (currentColumns) {
40 case GRID_COUNTS_4:
41 maxGridCounts = GRID_COUNTS_4;
42 break;
43 case GRID_COUNTS_8:
44 maxGridCounts = GRID_COUNTS_6;
45 break;
46 case GRID_COUNTS_12:
47 maxGridCounts = GRID_COUNTS_8;
48 break;
49 default:
50 break;
51 }
52 return maxGridCounts;
53 }
54 } // namespace
55
Measure(LayoutWrapper * layoutWrapper)56 void ListItemGroupLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
57 {
58 CHECK_NULL_VOID(listLayoutProperty_);
59 auto layoutProperty = AceType::DynamicCast<ListItemGroupLayoutProperty>(layoutWrapper->GetLayoutProperty());
60 CHECK_NULL_VOID(layoutProperty);
61 axis_ = listLayoutProperty_->GetListDirection().value_or(Axis::VERTICAL);
62 layoutDirection_ = listLayoutProperty_->GetNonAutoLayoutDirection();
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 auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
67 auto contentIdealSize = CreateIdealSize(
68 contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
69
70 auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
71 auto space = layoutProperty->GetSpace().value_or(Dimension(0));
72
73 auto layoutConstraint = layoutProperty->GetLayoutConstraint().value();
74 CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
75 childLayoutConstraint_ = layoutProperty->CreateChildConstraint();
76 isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
77 if (isCardStyle_) {
78 auto maxWidth = GetListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty) -
79 layoutProperty->CreatePaddingAndBorder().Width();
80 contentIdealSize.SetCrossSize(maxWidth, axis_);
81 }
82 UpdateListItemConstraint(contentIdealSize, childLayoutConstraint_);
83 referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
84 totalItemCount_ = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
85 totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
86 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
87 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, endPos_ - startPos_)) {
88 spaceWidth_ = 0.0f;
89 }
90 if (layoutProperty->GetDivider().has_value()) {
91 auto divider = layoutProperty->GetDivider().value();
92 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
93 if (GreatOrEqual(dividerSpace.value(), endPos_ - startPos_)) {
94 dividerSpace.reset();
95 }
96 if (dividerSpace.has_value()) {
97 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
98 }
99 }
100 MeasureHeaderFooter(layoutWrapper);
101 totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
102 if (childrenSize_) {
103 posMap_->UpdateGroupPosMap(totalItemCount_, GetLanes(), spaceWidth_, childrenSize_,
104 headerMainSize_, footerMainSize_);
105 totalMainSize_ = posMap_->GetTotalHeight();
106 }
107 if (cacheParam_) {
108 MeasureCacheItem(layoutWrapper);
109 } else {
110 MeasureListItem(layoutWrapper, childLayoutConstraint_);
111 }
112 childrenSize_ ? AdjustByPosMap() : AdjustItemPosition();
113
114 auto crossSize = contentIdealSize.CrossSize(axis_);
115 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
116 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
117 }
118 contentIdealSize.SetMainSize(totalMainSize_, axis_);
119 AddPaddingToSize(padding, contentIdealSize);
120 layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
121 layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountWithDefault() * lanes_);
122 }
123
GetListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty)124 float ListItemGroupLayoutAlgorithm::GetListItemGroupMaxWidth(
125 const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty)
126 {
127 RefPtr<GridColumnInfo> columnInfo;
128 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
129 columnInfo->GetParent()->BuildColumnWidth();
130 auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
131 auto parentWidth = parentIdealSize.CrossSize(axis_).value() + layoutProperty->CreatePaddingAndBorder().Width();
132 auto maxWidth = std::min(parentWidth, maxGridWidth);
133 if (LessNotEqual(maxGridWidth, layoutProperty->CreatePaddingAndBorder().Width())) {
134 TAG_LOGI(AceLogTag::ACE_LIST,
135 "ListItemGroup reset to parentWidth since grid_col width:%{public}f, border:%{public}f",
136 maxGridWidth, layoutProperty->CreatePaddingAndBorder().Width());
137 maxWidth = parentWidth;
138 }
139 return maxWidth;
140 }
141
Layout(LayoutWrapper * layoutWrapper)142 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
143 {
144 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
145 CHECK_NULL_VOID(layoutProperty);
146 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
147 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
148 MinusPaddingToSize(padding, size);
149 auto left = padding.left.value_or(0.0f);
150 auto top = padding.top.value_or(0.0f);
151 auto paddingOffset = OffsetF(left, top);
152 float crossSize = GetCrossAxisSize(size, axis_);
153 CHECK_NULL_VOID(listLayoutProperty_);
154 itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
155 SetActiveChildRange(layoutWrapper, listLayoutProperty_->GetCachedCountWithDefault());
156
157 if (headerIndex_ >= 0 || footerIndex_ >= 0) {
158 if (layoutDirection_ == TextDirection::RTL && axis_ == Axis::HORIZONTAL) {
159 LayoutHeaderFooterRTL(layoutWrapper, paddingOffset, crossSize);
160 } else {
161 LayoutHeaderFooterLTR(layoutWrapper, paddingOffset, crossSize);
162 }
163 }
164 if (cacheParam_) {
165 LayoutCacheItem(layoutWrapper);
166 return;
167 }
168 // layout items.
169 LayoutListItem(layoutWrapper, paddingOffset, crossSize);
170 }
171
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)172 void ListItemGroupLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
173 {
174 CHECK_NULL_VOID(wrapper);
175 auto host = wrapper->GetHostNode();
176 CHECK_NULL_VOID(host);
177 host->ForceSyncGeometryNode();
178 host->ResetLayoutAlgorithm();
179 }
180
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const181 bool ListItemGroupLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
182 {
183 if (layoutWrapper->CheckNeedForceMeasureAndLayout()) {
184 return true;
185 }
186 auto geometryNode = layoutWrapper->GetGeometryNode();
187 CHECK_NULL_RETURN(geometryNode, true);
188 auto constraint = geometryNode->GetParentLayoutConstraint();
189 CHECK_NULL_RETURN(constraint, true);
190 return constraint.value() != childLayoutConstraint_;
191 }
192
MeasureHeaderFooter(LayoutWrapper * layoutWrapper)193 void ListItemGroupLayoutAlgorithm::MeasureHeaderFooter(LayoutWrapper* layoutWrapper)
194 {
195 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
196 auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
197 headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
198 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
199 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
200 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
201 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
202 if (headerWrapper) {
203 headerWrapper->Measure(headerFooterLayoutConstraint);
204 headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
205 }
206 if (footerWrapper) {
207 footerWrapper->Measure(headerFooterLayoutConstraint);
208 footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
209 }
210 }
211
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheCount)212 void ListItemGroupLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper, int32_t cacheCount)
213 {
214 if (!itemPosition_.empty()) {
215 auto start = itemStartIndex_ + itemPosition_.begin()->first;
216 auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
217 layoutWrapper->SetActiveChildRange(start, end, cacheCount * lanes_, cacheCount * lanes_);
218 return;
219 }
220 auto listPadding = listLayoutProperty_->CreatePaddingAndBorder().Offset();
221 auto offset = layoutWrapper->GetGeometryNode()->GetMarginFrameOffset();
222 if (LessNotEqual(GetMainAxisOffset(offset, axis_), GetMainAxisOffset(listPadding, axis_))) {
223 int32_t index = totalItemCount_ + itemStartIndex_;
224 layoutWrapper->SetActiveChildRange(index, index, cacheCount * lanes_, 0);
225 } else {
226 layoutWrapper->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount * lanes_);
227 }
228 }
229
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)230 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
231 LayoutConstraintF& contentConstraint)
232 {
233 contentConstraint.parentIdealSize = selfIdealSize;
234 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
235 auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
236 if (crossSizeOptional.has_value()) {
237 float crossSize = crossSizeOptional.value();
238 if (lanes_ > 1) {
239 crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
240 crossSize = crossSize <= 0 ? 1 : crossSize;
241 }
242 if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
243 crossSize = maxLaneLength_.value();
244 }
245 contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
246 contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
247 contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
248 if (minLaneLength_.has_value()) {
249 contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
250 }
251 }
252 }
253
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)254 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
255 {
256 float maxCrossSize = 0.0f;
257 for (const auto& pos : itemPosition_) {
258 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
259 if (!wrapper) {
260 continue;
261 }
262 auto getGeometryNode = wrapper->GetGeometryNode();
263 if (!getGeometryNode) {
264 continue;
265 }
266 maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
267 }
268 return maxCrossSize;
269 }
270
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)271 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
272 RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
273 {
274 const auto& padding = layoutProperty->CreatePaddingAndBorder();
275 const auto& margin = layoutProperty->CreateMargin();
276 auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
277 auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
278 offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
279 offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
280 forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
281 return referencePos;
282 }
283
NeedMeasureItem(LayoutWrapper * layoutWrapper)284 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem(LayoutWrapper* layoutWrapper)
285 {
286 auto contentMainSize = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
287 if (NearZero(contentMainSize)) {
288 return true;
289 }
290 if (forwardLayout_) {
291 if (childrenSize_ && needAdjustRefPos_) {
292 referencePos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
293 refPos_ -= (totalMainSize_ - posMap_->GetPrevTotalHeight());
294 }
295 if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
296 return false;
297 }
298 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
299 if (totalItemCount_ > 0 &&
300 (!layoutedItemInfo_ || layoutedItemInfo_.value().endIndex < totalItemCount_ - 1)) {
301 return true;
302 } else {
303 return false;
304 }
305 }
306 } else {
307 if (childrenSize_ && needAdjustRefPos_) {
308 referencePos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
309 refPos_ += (totalMainSize_ - posMap_->GetPrevTotalHeight());
310 }
311 if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
312 return false;
313 }
314 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
315 return false;
316 }
317 }
318 return true;
319 }
320
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)321 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
322 const LayoutConstraintF& layoutConstraint, float startPos)
323 {
324 int32_t currentIndex = -1;
325 float currentEndPos = startPos;
326 float currentStartPos = 0.0f;
327 while (currentIndex < totalItemCount_) {
328 currentStartPos = currentEndPos;
329 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
330 currentStartPos, currentEndPos);
331 if (count == 0) {
332 break;
333 }
334 if (currentIndex < (totalItemCount_ - 1)) {
335 currentEndPos += spaceWidth_;
336 }
337 }
338 }
339
ClearItemPosition()340 void ListItemGroupLayoutAlgorithm::ClearItemPosition()
341 {
342 itemPosition_.clear();
343 }
344
CheckNeedAllLayout(const RefPtr<LayoutWrapper> & layoutWrapper,bool forwardLayout)345 void ListItemGroupLayoutAlgorithm::CheckNeedAllLayout(const RefPtr<LayoutWrapper>& layoutWrapper, bool forwardLayout)
346 {
347 if (itemPosition_.empty()) {
348 needAllLayout_ = true;
349 return;
350 }
351 int32_t totalItemCount = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
352 if (!(forwardLayout && itemPosition_.rbegin()->first == totalItemCount - 1) &&
353 !(!forwardLayout && itemPosition_.begin()->first == 0)) {
354 needAllLayout_ = true;
355 }
356 }
357
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)358 void ListItemGroupLayoutAlgorithm::MeasureListItem(
359 LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
360 {
361 if (totalItemCount_ <= 0) {
362 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
363 adjustReferenceDelta_ = totalMainSize_ - (headerMainSize_ + footerMainSize_);
364 }
365 totalMainSize_ = headerMainSize_ + footerMainSize_;
366 itemPosition_.clear();
367 layoutedItemInfo_.reset();
368 return;
369 }
370 int32_t startIndex = 0;
371 int32_t endIndex = totalItemCount_ - 1;
372 float startPos = headerMainSize_;
373 float endPos = totalMainSize_ - footerMainSize_;
374 prevStartPos_ = startPos_;
375 prevEndPos_ = endPos_;
376 if (needAllLayout_) {
377 needAllLayout_ = false;
378 itemPosition_.clear();
379 LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
380 return;
381 }
382 if (targetIndex_) {
383 startPos_ = -Infinity<float>();
384 endPos_ = Infinity<float>();
385 }
386 if (jumpIndex_.has_value()) {
387 if (jumpIndex_.value() == LAST_ITEM) {
388 jumpIndex_ = totalItemCount_ - 1;
389 }
390 auto jumpIndex = jumpIndex_.value();
391 if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
392 jumpIndex = 0;
393 }
394 if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
395 scrollAlign_ == ScrollAlign::AUTO) {
396 startIndex = jumpIndex;
397 } else if (scrollAlign_ == ScrollAlign::END) {
398 endIndex = jumpIndex;
399 } else if (forwardLayout_) {
400 startIndex = jumpIndex;
401 } else {
402 endIndex = jumpIndex;
403 }
404 itemPosition_.clear();
405 jumpIndex_.reset();
406 layoutedItemInfo_.reset();
407 } else if (!itemPosition_.empty()) {
408 if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
409 startPos = itemPosition_.begin()->second.startPos;
410 }
411 startIndex = GetStartIndex();
412 if (startIndex >= totalItemCount_) {
413 startIndex = totalItemCount_ - 1;
414 if (itemPosition_.begin()->first > 0) {
415 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
416 GetLanesFloor(startIndex) + headerMainSize_;
417 }
418 }
419 if (!isNeedMeasureFormLastItem_) {
420 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
421 endPos = itemPosition_.rbegin()->second.endPos;
422 }
423 if (forwardLayout_) {
424 ModifyReferencePos(GetLanesFloor(startIndex), startPos);
425 } else {
426 ModifyReferencePos(GetLanesCeil(endIndex), endPos);
427 }
428 itemPosition_.clear();
429 } else if (!NeedMeasureItem(layoutWrapper)) {
430 itemPosition_.clear();
431 return;
432 }
433 if (scrollAlign_ == ScrollAlign::CENTER) {
434 startIndex = GetLanesFloor(startIndex);
435 MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
436 } else if (scrollAlign_ == ScrollAlign::START) {
437 startIndex = GetLanesFloor(startIndex);
438 MeasureStart(layoutWrapper, layoutConstraint, startIndex);
439 } else if (scrollAlign_ == ScrollAlign::END) {
440 endIndex = GetLanesCeil(endIndex);
441 MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
442 } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
443 startIndex = GetLanesFloor(startIndex);
444 MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
445 } else if (forwardLayout_) {
446 startIndex = GetLanesFloor(startIndex);
447 CheckJumpForwardForBigOffset(startIndex, startPos);
448 startPos = childrenSize_ ? posMap_->GetPos(startIndex) : startPos;
449 MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
450 } else {
451 endIndex = GetLanesCeil(endIndex);
452 CheckJumpBackwardForBigOffset(endIndex, endPos);
453 endPos = childrenSize_ ? posMap_->GetPos(endIndex) + posMap_->GetRowHeight(endIndex) : endPos;
454 MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
455 }
456 }
457
GetItemGroupPosition(int32_t index)458 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
459 {
460 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
461 if (scrollAlign_ == ScrollAlign::CENTER) {
462 auto pos = itemPosition_.find(index);
463 if (pos != itemPosition_.end()) {
464 float refPos = (pos->second.endPos + pos->second.startPos) / 2 + paddingBeforeContent_; // 2:average
465 float delta = (startPos_ + endPos_) / 2 - refPos;
466 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
467 }
468 } else if (scrollAlign_ == ScrollAlign::START) {
469 auto pos = itemPosition_.find(index);
470 if (pos != itemPosition_.end()) {
471 float top = startPos_ + contentStartOffset_;
472 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
473 top += headerMainSize_;
474 }
475 float refPos = pos->second.startPos + paddingBeforeContent_;
476 float delta = top - refPos;
477 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
478 }
479 } else if (scrollAlign_ == ScrollAlign::END) {
480 auto pos = itemPosition_.find(index);
481 if (pos != itemPosition_.end()) {
482 float bottom = endPos_ - contentEndOffset_;
483 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
484 bottom -= footerMainSize_;
485 }
486 float refPos = pos->second.endPos + paddingBeforeContent_;
487 float delta = bottom - refPos;
488 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
489 }
490 }
491 return { 0.0f, 0.0f };
492 }
493
GetItemHeight(int32_t index)494 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
495 {
496 auto it = itemPosition_.find(index);
497 if (it != itemPosition_.end()) {
498 return it->second.endPos - it->second.startPos;
499 }
500 return 0.0f;
501 }
502
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)503 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
504 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
505 {
506 auto wrapper = GetListItem(layoutWrapper, currentIndex);
507 if (!wrapper) {
508 return 0;
509 }
510 if (CheckNeedMeasure(wrapper)) {
511 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
512 wrapper->Measure(layoutConstraint);
513 }
514 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
515 int32_t id = wrapper->GetHostNode()->GetId();
516 itemPosition_[currentIndex] = { id, 0.0f, mainLen };
517 return 1;
518 }
519
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)520 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
521 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
522 {
523 float mainLen = 0;
524 int32_t cnt = 0;
525 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
526 for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
527 auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
528 if (!wrapper) {
529 break;
530 }
531 if (CheckNeedMeasure(wrapper)) {
532 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
533 wrapper->Measure(layoutConstraint);
534 }
535 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
536 cnt++;
537 }
538 if (cnt > 0) {
539 auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
540 auto endPos = startPos + mainLen; // 2:average
541 for (int32_t i = 0; i < cnt; i++) {
542 auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
543 int32_t id = wrapper->GetHostNode()->GetId();
544 itemPosition_[currentIndex + i] = { id, startPos, endPos };
545 }
546 }
547 return cnt;
548 }
549
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)550 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
551 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
552 {
553 float mainLen = 0.0f;
554 int32_t cnt = 0;
555 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
556 for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_; i++) {
557 auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
558 if (!wrapper) {
559 break;
560 }
561 cnt++;
562 ++currentIndex;
563 if (CheckNeedMeasure(wrapper)) {
564 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
565 wrapper->Measure(layoutConstraint);
566 }
567 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
568 }
569 if (cnt > 0) {
570 endPos = startPos + mainLen;
571 for (int32_t i = 0; i < cnt; i++) {
572 auto wrapper = GetListItem(layoutWrapper, currentIndex - i);
573 int32_t id = wrapper->GetHostNode()->GetId();
574 itemPosition_[currentIndex - i] = { id, startPos, endPos };
575 }
576 }
577 return cnt;
578 }
579
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)580 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
581 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
582 {
583 float mainLen = 0.0f;
584 int32_t cnt = 0;
585 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
586 for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
587 auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
588 if (!wrapper) {
589 break;
590 }
591 --currentIndex;
592 cnt++;
593 if (CheckNeedMeasure(wrapper)) {
594 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
595 wrapper->Measure(layoutConstraint);
596 }
597 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
598 if (currentIndex % lanes == 0) {
599 break;
600 }
601 }
602 if (cnt > 0) {
603 startPos = endPos - mainLen;
604 for (int32_t i = 0; i < cnt; i++) {
605 auto wrapper = GetListItem(layoutWrapper, currentIndex + i);
606 int32_t id = wrapper->GetHostNode()->GetId();
607 itemPosition_[currentIndex + i] = { id, startPos, endPos };
608 }
609 }
610 return cnt;
611 }
612
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)613 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
614 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
615 {
616 MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
617 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
618 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
619
620 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
621 float currentStartPos = headerMainSize_;
622 int32_t i = 0;
623 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
624 for (auto& pos : itemPosition_) {
625 float len = pos.second.endPos - pos.second.startPos;
626 pos.second.startPos = currentStartPos;
627 pos.second.endPos = currentStartPos + len;
628 i++;
629 if (i % lanes == 0) {
630 currentStartPos = pos.second.endPos + spaceWidth_;
631 }
632 }
633 }
634
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)635 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
636 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
637 {
638 if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
639 return;
640 }
641
642 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
643 }
644
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)645 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
646 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
647 {
648 float currentStartPos = startPos;
649 float currentEndPos = startPos;
650 int32_t currentIndex = startIndex - 1;
651 while (LessOrEqual(currentEndPos, endPos_)) {
652 currentStartPos = currentEndPos;
653 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
654 currentStartPos, currentEndPos);
655 if (count == 0) {
656 break;
657 }
658 if (currentIndex < (totalItemCount_ - 1)) {
659 currentEndPos += spaceWidth_;
660 }
661 }
662 }
663
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)664 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
665 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
666 {
667 float currentEndPos = endPos;
668 float currentStartPos = endPos;
669 int32_t currentIndex = endIndex + 1;
670 while (GreatOrEqual(currentStartPos, startPos_)) {
671 currentEndPos = currentStartPos;
672 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
673 currentEndPos, currentStartPos);
674 if (count == 0) {
675 break;
676 }
677 if (currentIndex > 0) {
678 currentStartPos -= spaceWidth_;
679 }
680 }
681 }
682
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)683 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
684 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
685 {
686 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
687 float currentStartPos = startPos_ + contentStartOffset_;
688 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
689 currentStartPos += headerMainSize_;
690 }
691
692 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
693 if (GreatNotEqual(currentStartPos, startPos_)) {
694 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
695 }
696
697 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
698 currentStartPos = headerMainSize_;
699 int32_t i = 0;
700 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
701 for (auto& pos : itemPosition_) {
702 float len = pos.second.endPos - pos.second.startPos;
703 pos.second.startPos = currentStartPos;
704 pos.second.endPos = currentStartPos + len;
705 i++;
706 if (i % lanes == 0) {
707 currentStartPos = pos.second.endPos + spaceWidth_;
708 }
709 }
710 }
711
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)712 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
713 const LayoutConstraintF& layoutConstraint, int32_t endIndex)
714 {
715 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
716 float currentEndPos = endPos_ - contentEndOffset_;
717 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
718 currentEndPos -= footerMainSize_;
719 }
720
721 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
722 if (LessNotEqual(currentEndPos, endPos_)) {
723 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
724 }
725
726 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
727 float currentStartPos = headerMainSize_;
728 int32_t i = 0;
729 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
730 for (auto& pos : itemPosition_) {
731 float len = pos.second.endPos - pos.second.startPos;
732 pos.second.startPos = currentStartPos;
733 pos.second.endPos = currentStartPos + len;
734 i++;
735 if (i % lanes == 0) {
736 currentStartPos = pos.second.endPos + spaceWidth_;
737 }
738 }
739 }
740
CheckJumpForwardForBigOffset(int32_t & startIndex,float & startPos)741 void ListItemGroupLayoutAlgorithm::CheckJumpForwardForBigOffset(int32_t& startIndex, float& startPos)
742 {
743 if (!isNeedCheckOffset_ || childrenSize_ || !layoutedItemInfo_.has_value()) {
744 return;
745 }
746 float th = startPos_ - (endPos_ - startPos_) - referencePos_;
747 if (LessOrEqual(startPos, th)) {
748 const auto& itemInfo = layoutedItemInfo_.value();
749 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
750 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
751 float averageHeight = totalHeight / itemCount;
752 float distance = startPos_ - referencePos_ - startPos;
753 int32_t jumpCount = distance / averageHeight;
754 jumpCount = std::min(jumpCount, totalItemCount_ - 1 - startIndex);
755 startPos += jumpCount * averageHeight;
756 startIndex += jumpCount;
757 }
758 }
759
CheckJumpBackwardForBigOffset(int32_t & endIndex,float & endPos)760 void ListItemGroupLayoutAlgorithm::CheckJumpBackwardForBigOffset(int32_t& endIndex, float& endPos)
761 {
762 if (!isNeedCheckOffset_ || childrenSize_ || !layoutedItemInfo_.has_value()) {
763 return;
764 }
765 float th = endPos_ + (endPos_ - startPos_) - (referencePos_ - totalMainSize_);
766 if (GreatOrEqual(endPos, th)) {
767 const auto& itemInfo = layoutedItemInfo_.value();
768 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
769 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
770 float averageHeight = totalHeight / itemCount;
771 float distance = endPos - (endPos_ - (referencePos_ - totalMainSize_));
772 int32_t jumpCount = distance / averageHeight;
773 jumpCount = std::min(jumpCount, endIndex);
774 endPos -= jumpCount * averageHeight;
775 endIndex -= jumpCount;
776 }
777 }
778
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)779 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
780 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
781 {
782 float currentEndPos = startPos;
783 float currentStartPos = 0.0f;
784 int32_t currentIndex = startIndex - 1;
785 while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
786 currentStartPos = currentEndPos;
787 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
788 currentStartPos, currentEndPos);
789 if (count == 0) {
790 break;
791 }
792 if (currentIndex < (totalItemCount_ - 1)) {
793 currentEndPos += spaceWidth_;
794 }
795 if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
796 startPos_ = prevStartPos_;
797 endPos_ = prevEndPos_;
798 targetIndex_.reset();
799 }
800 }
801
802 currentStartPos = startPos - spaceWidth_;
803 currentIndex = startIndex;
804 float th = std::max(startPos_ - referencePos_, headerMainSize_);
805 while (currentIndex > 0 && GreatNotEqual(currentStartPos, th)) {
806 currentEndPos = currentStartPos;
807 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
808 currentEndPos, currentStartPos);
809 if (count == 0) {
810 break;
811 }
812 if (currentIndex > 0) {
813 currentStartPos = currentStartPos - spaceWidth_;
814 }
815 }
816 }
817
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)818 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
819 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
820 {
821 float currentStartPos = endPos;
822 float currentEndPos = 0.0f;
823 auto currentIndex = endIndex + 1;
824 while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
825 currentEndPos = currentStartPos;
826 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
827 currentEndPos, currentStartPos);
828 if (count == 0) {
829 break;
830 }
831 if (currentIndex > 0) {
832 currentStartPos = currentStartPos - spaceWidth_;
833 }
834 if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
835 startPos_ = prevStartPos_;
836 endPos_ = prevEndPos_;
837 targetIndex_.reset();
838 }
839 }
840 currentIndex = endIndex;
841 currentEndPos = endPos + spaceWidth_;
842 while (childrenSize_ && LessOrEqual(currentEndPos, endPos_ - (referencePos_ - totalMainSize_))) {
843 currentStartPos = currentEndPos;
844 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
845 currentStartPos, currentEndPos);
846 if (count == 0) {
847 break;
848 }
849 if (currentIndex < (totalItemCount_ - 1)) {
850 currentEndPos += spaceWidth_;
851 }
852 }
853 }
854
ModifyReferencePos(int32_t index,float pos)855 void ListItemGroupLayoutAlgorithm::ModifyReferencePos(int32_t index, float pos)
856 {
857 if (!childrenSize_ || !needAdjustRefPos_) {
858 return;
859 }
860 if (forwardLayout_ && Negative(referencePos_)) {
861 float offset = referencePos_ + pos;
862 float newReferencePos = offset - posMap_->GetPos(index);
863 refPos_ = refPos_ + newReferencePos - referencePos_;
864 referencePos_ = newReferencePos;
865 } else if (!forwardLayout_ && GreatNotEqual(referencePos_, prevContentMainSize_)) {
866 float offset = referencePos_ - posMap_->GetPrevTotalHeight() + pos - prevContentMainSize_;
867 float newReferencePos = offset + endPos_ - startPos_ + totalMainSize_ -
868 (posMap_->GetPos(index) + posMap_->GetRowHeight(index));
869 refPos_ = refPos_ + newReferencePos - referencePos_;
870 referencePos_ = newReferencePos;
871 }
872 }
873
AdjustByPosMap()874 void ListItemGroupLayoutAlgorithm::AdjustByPosMap()
875 {
876 totalMainSize_ = posMap_->GetTotalHeight();
877 if (itemPosition_.empty()) {
878 return;
879 }
880 float startPos = itemPosition_.begin()->second.startPos;
881 float offset = posMap_->GetGroupLayoutOffset(GetStartIndex(), startPos);
882 for (auto& pos : itemPosition_) {
883 pos.second.startPos += offset;
884 pos.second.endPos += offset;
885 }
886 }
887
AdjustItemPosition()888 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
889 {
890 if (itemPosition_.empty()) {
891 return;
892 }
893 float currentStartPos = GetStartPosition();
894 if (currentStartPos < headerMainSize_) {
895 auto delta = headerMainSize_ - currentStartPos;
896 for (auto& pos : itemPosition_) {
897 pos.second.startPos += delta;
898 pos.second.endPos += delta;
899 }
900 totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
901 adjustReferenceDelta_ = -delta;
902 } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
903 auto delta = currentStartPos - headerMainSize_;
904 for (auto& pos : itemPosition_) {
905 pos.second.startPos -= delta;
906 pos.second.endPos -= delta;
907 }
908 totalMainSize_ -= delta;
909 adjustReferenceDelta_ = delta;
910 }
911 if (GetEndIndex() == totalItemCount_ - 1) {
912 totalMainSize_ = GetEndPosition() + footerMainSize_;
913 } else {
914 totalMainSize_ = std::max(totalMainSize_, GetEndPosition() + footerMainSize_);
915 }
916 const auto& start = *itemPosition_.begin();
917 const auto& end = *itemPosition_.rbegin();
918 if (layoutedItemInfo_.has_value()) {
919 auto& itemInfo = layoutedItemInfo_.value();
920 auto prevStartIndex = itemInfo.startIndex;
921 if (start.first <= itemInfo.startIndex || LessNotEqual(start.second.startPos, itemInfo.startPos) ||
922 start.first > itemInfo.endIndex) {
923 itemInfo.startIndex = start.first;
924 itemInfo.startPos = start.second.startPos;
925 }
926 if (end.first >= itemInfo.endIndex || GreatNotEqual(end.second.endPos, itemInfo.endPos) ||
927 itemInfo.endIndex > totalItemCount_ - 1 || end.first < prevStartIndex) {
928 itemInfo.endIndex = end.first;
929 itemInfo.endPos = end.second.endPos;
930 }
931 } else {
932 layoutedItemInfo_ = { start.first, start.second.startPos, end.first, end.second.endPos };
933 }
934 }
935
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)936 void ListItemGroupLayoutAlgorithm::CheckRecycle(
937 const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
938 {
939 referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
940 // Mark inactive in wrapper.
941 if (forwardLayout) {
942 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
943 if (GreatOrEqual(pos->second.endPos, startPos - referencePos)) {
944 break;
945 }
946 itemPosition_.erase(pos++);
947 }
948 return;
949 }
950 std::list<int32_t> removeIndexes;
951 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
952 if (LessOrEqual(pos->second.startPos, endPos - (referencePos - totalMainSize_))) {
953 break;
954 }
955 removeIndexes.emplace_back(pos->first);
956 }
957 for (const auto& index : removeIndexes) {
958 itemPosition_.erase(index);
959 }
960 }
961
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)962 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
963 const OffsetF& paddingOffset, float crossSize)
964 {
965 // layout items.
966 for (auto& pos : itemPosition_) {
967 auto wrapper = GetListItem(layoutWrapper, pos.first);
968 if (!wrapper) {
969 LOGI("wrapper is out of boundary");
970 continue;
971 }
972
973 auto offset = paddingOffset;
974 int32_t laneIndex = pos.first % lanes_;
975 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
976 float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
977 if (layoutDirection_ == TextDirection::RTL) {
978 if (axis_ == Axis::VERTICAL) {
979 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
980 auto tmpX = crossSize - laneCrossOffset -
981 ((crossSize + laneGutter_) / lanes_) * laneIndex - size.Width();
982 offset = offset + OffsetF(tmpX, pos.second.startPos);
983 } else {
984 auto tmpY = laneCrossOffset + ((crossSize + laneGutter_) / lanes_) * laneIndex;
985 offset = offset + OffsetF(totalMainSize_ - pos.second.endPos, tmpY);
986 }
987 } else {
988 if (axis_ == Axis::VERTICAL) {
989 offset =
990 offset + OffsetF(0, pos.second.startPos) + OffsetF(laneCrossOffset, 0) +
991 OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
992 } else {
993 offset =
994 offset + OffsetF(pos.second.startPos, 0) + OffsetF(0, laneCrossOffset) +
995 OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
996 }
997 }
998 SetListItemIndex(layoutWrapper, wrapper, pos.first);
999 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1000 if (wrapper->CheckNeedForceMeasureAndLayout()) {
1001 wrapper->Layout();
1002 } else {
1003 SyncGeometry(wrapper);
1004 }
1005 }
1006 }
1007
UpdateZIndex(const RefPtr<LayoutWrapper> & layoutWrapper)1008 void ListItemGroupLayoutAlgorithm::UpdateZIndex(const RefPtr<LayoutWrapper>& layoutWrapper)
1009 {
1010 auto host = layoutWrapper->GetHostNode();
1011 CHECK_NULL_VOID(host);
1012 auto renderContext = host->GetRenderContext();
1013 CHECK_NULL_VOID(renderContext);
1014 renderContext->UpdateZIndex(1);
1015 }
1016
LayoutHeaderFooterRTL(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1017 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterRTL(LayoutWrapper* layoutWrapper,
1018 const OffsetF& paddingOffset, float crossSize)
1019 {
1020 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1021 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1022 float mainPos = GetMainAxisOffset(selfOffset, axis_);
1023 float footerMainSize = 0.0f;
1024 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1025 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1026 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1027 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1028 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1029 if (footerWrapper) {
1030 UpdateZIndex(footerWrapper);
1031 footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1032 float footerPos = 0.0f;
1033 if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER) && !itemPosition_.empty()) {
1034 contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1035 float stickyPos = contentStartOffset_ - mainPos;
1036 if (GetEndIndex() == totalItemCount_ - 1) {
1037 stickyPos = std::min(stickyPos, GetEndPosition() - footerMainSize);
1038 }
1039 footerPos = std::max(footerPos, stickyPos);
1040 footerPos = std::min(footerPos, totalMainSize_ - footerMainSize_ - headerMainSize_);
1041 }
1042 LayoutIndex(footerWrapper, paddingOffset, crossSize, footerPos);
1043 endFooterPos_ = endFooterPos_ > mainPos ? mainPos : endFooterPos_;
1044 }
1045
1046 if (headerWrapper) {
1047 float headPos = totalMainSize_ - headerMainSize_;
1048 UpdateZIndex(headerWrapper);
1049 float const listMainSize = endPos_ - startPos_;
1050 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER)) {
1051 auto headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1052 float stickyPos = listMainSize - contentEndOffset_ - mainPos - headerMainSize;
1053 if (stickyPos < footerMainSize) {
1054 stickyPos = footerMainSize;
1055 }
1056 if (stickyPos < headPos) {
1057 headPos = stickyPos;
1058 }
1059 }
1060 LayoutIndex(headerWrapper, paddingOffset, crossSize, headPos);
1061 startHeaderPos_ = mainPos + totalMainSize_ - headerMainSize_ - listMainSize;
1062 }
1063 }
1064
LayoutHeaderFooterLTR(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)1065 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooterLTR(LayoutWrapper* layoutWrapper,
1066 const OffsetF& paddingOffset, float crossSize)
1067 {
1068 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
1069 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
1070 float mainPos = GetMainAxisOffset(selfOffset, axis_);
1071 float headerMainSize = 0.0f;
1072 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1073 RefPtr<LayoutWrapper> headerWrapper = headerIndex_ >= 0 ?
1074 layoutWrapper->GetOrCreateChildByIndex(headerIndex_) : nullptr;
1075 RefPtr<LayoutWrapper> footerWrapper = footerIndex_ >= 0 ?
1076 layoutWrapper->GetOrCreateChildByIndex(footerIndex_) : nullptr;
1077 if (headerWrapper) {
1078 UpdateZIndex(headerWrapper);
1079 headerMainSize = headerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1080 float headerPos = 0.0f;
1081 if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) && !itemPosition_.empty()) {
1082 contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
1083 float stickyPos = contentStartOffset_ - mainPos;
1084 if (GetEndIndex() == totalItemCount_ - 1) {
1085 stickyPos = std::min(stickyPos, GetEndPosition() - headerMainSize);
1086 }
1087 headerPos = std::max(headerPos, stickyPos);
1088 }
1089 LayoutIndex(headerWrapper, paddingOffset, crossSize, headerPos);
1090 startHeaderPos_ = startHeaderPos_ > mainPos ? mainPos : startHeaderPos_;
1091 }
1092
1093 if (footerWrapper) {
1094 float endPos = totalMainSize_ - footerMainSize_;
1095 UpdateZIndex(footerWrapper);
1096 float const listMainSize = endPos_ - startPos_;
1097 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
1098 auto footerMainSize = footerWrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
1099 float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
1100 if (stickyPos < headerMainSize) {
1101 stickyPos = headerMainSize;
1102 }
1103 if (stickyPos < endPos) {
1104 endPos = stickyPos;
1105 }
1106 }
1107 LayoutIndex(footerWrapper, paddingOffset, crossSize, endPos);
1108 endFooterPos_ = mainPos + totalMainSize_ - footerMainSize_ - listMainSize;
1109 }
1110 }
1111
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)1112 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
1113 float crossSize, float startPos)
1114 {
1115 CHECK_NULL_VOID(wrapper);
1116 auto offset = paddingOffset;
1117 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1118 float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1119 if (axis_ == Axis::VERTICAL) {
1120 if (layoutDirection_ == TextDirection::RTL) {
1121 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1122 offset = offset + OffsetF(crossSize - laneCrossOffset - size.Width(), startPos);
1123 } else {
1124 offset = offset + OffsetF(laneCrossOffset, startPos);
1125 }
1126 } else {
1127 offset = offset + OffsetF(startPos, laneCrossOffset);
1128 }
1129 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1130 wrapper->Layout();
1131 }
1132
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1133 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1134 {
1135 float delta = crossSize - GetLaneGutter() - childCrossSize;
1136 if (LessOrEqual(delta, 0.0f)) {
1137 return 0.0f;
1138 }
1139 switch (itemAlign_) {
1140 case OHOS::Ace::V2::ListItemAlign::START:
1141 return 0.0f;
1142 case OHOS::Ace::V2::ListItemAlign::CENTER:
1143 return delta / 2; /* 2:average */
1144 case OHOS::Ace::V2::ListItemAlign::END:
1145 return delta;
1146 default:
1147 LOGW("Invalid ListItemAlign: %{public}d", itemAlign_);
1148 return 0.0f;
1149 }
1150 }
1151
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)1152 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
1153 const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
1154 {
1155 int32_t lanes = layoutProperty->GetLanes().value_or(1);
1156 lanes = lanes > 1 ? lanes : 1;
1157 if (crossSizeOptional.has_value()) {
1158 if (layoutProperty->GetLaneMinLength().has_value()) {
1159 minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
1160 layoutConstraint.scaleProperty, crossSizeOptional.value());
1161 }
1162 if (layoutProperty->GetLaneMaxLength().has_value()) {
1163 maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
1164 layoutConstraint.scaleProperty, crossSizeOptional.value());
1165 }
1166 if (layoutProperty->GetLaneGutter().has_value()) {
1167 auto laneGutter = ConvertToPx(
1168 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
1169 laneGutter_ = laneGutter.value();
1170 }
1171 }
1172 lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
1173 minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
1174 }
1175
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)1176 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
1177 const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
1178 {
1179 auto host = itemLayoutWrapper->GetHostNode();
1180 CHECK_NULL_VOID(host);
1181 auto listItem = host->GetPattern<ListItemPattern>();
1182 CHECK_NULL_VOID(listItem);
1183 listItem->SetIndexInListItemGroup(indexInGroup);
1184
1185 host = groupLayoutWrapper->GetHostNode();
1186 CHECK_NULL_VOID(host);
1187 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1188 CHECK_NULL_VOID(listItemGroup);
1189 listItem->SetIndexInList(listItemGroup->GetIndexInList());
1190 }
1191
GetLayoutInfo() const1192 ListItemGroupLayoutInfo ListItemGroupLayoutAlgorithm::GetLayoutInfo() const
1193 {
1194 ListItemGroupLayoutInfo info;
1195 if (totalItemCount_ == 0 || childrenSize_) {
1196 info.atStart = true;
1197 info.atEnd = true;
1198 return info;
1199 }
1200 if (layoutedItemInfo_.has_value()) {
1201 const auto& itemInfo = layoutedItemInfo_.value();
1202 info.atStart = itemInfo.startIndex == 0;
1203 info.atEnd = itemInfo.endIndex >= totalItemCount_ - 1;
1204 auto totalHeight = (itemInfo.endPos - itemInfo.startPos + spaceWidth_);
1205 auto itemCount = itemInfo.endIndex - itemInfo.startIndex + 1;
1206 info.averageHeight = totalHeight / itemCount;
1207 }
1208 return info;
1209 }
1210
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)1211 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
1212 {
1213 auto host = groupLayoutWrapper->GetHostNode();
1214 CHECK_NULL_RETURN(host, false);
1215 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1216 CHECK_NULL_RETURN(listItemGroup, false);
1217 return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
1218 }
1219
MeasureCacheItem(LayoutWrapper * layoutWrapper)1220 void ListItemGroupLayoutAlgorithm::MeasureCacheItem(LayoutWrapper* layoutWrapper)
1221 {
1222 ListItemGroupCacheParam& cacheParam = cacheParam_.value();
1223 if (cacheParam.forward) {
1224 int32_t endIndex = itemPosition_.empty() ? -1 : itemPosition_.rbegin()->first;
1225 int32_t limit = std::min(endIndex + cacheParam.cacheCount * lanes_, totalItemCount_ - 1);
1226 int32_t currentIndex = std::clamp(cacheParam.forwardCachedIndex, endIndex, limit) + 1;
1227 for (; currentIndex <= limit; currentIndex++) {
1228 auto item = layoutWrapper->GetOrCreateChildByIndex(currentIndex + itemStartIndex_, false, true);
1229 auto frameNode = AceType::DynamicCast<FrameNode>(item);
1230 if (!frameNode) {
1231 break;
1232 }
1233 if (!frameNode->CheckNeedForceMeasureAndLayout()) {
1234 continue;
1235 }
1236 if (!frameNode->GetHostNode()->RenderCustomChild(cacheParam.deadline)) {
1237 break;
1238 }
1239 item->Measure(childLayoutConstraint_);
1240 cachedItem_.push_back(currentIndex);
1241 }
1242 cacheParam.forwardCachedIndex = std::min(currentIndex - 1, limit);
1243 } else {
1244 int32_t startIndex = itemPosition_.empty() ? totalItemCount_ : itemPosition_.begin()->first;
1245 int32_t limit = std::max(startIndex - cacheParam.cacheCount * lanes_, 0);
1246 int32_t currentIndex = std::clamp(cacheParam.backwardCachedIndex, limit, startIndex) - 1;
1247 for (; currentIndex >= limit; currentIndex--) {
1248 auto item = layoutWrapper->GetOrCreateChildByIndex(currentIndex + itemStartIndex_, false, true);
1249 auto frameNode = AceType::DynamicCast<FrameNode>(item);
1250 if (!frameNode) {
1251 break;
1252 }
1253 if (!frameNode->CheckNeedForceMeasureAndLayout()) {
1254 continue;
1255 }
1256 if (!frameNode->GetHostNode()->RenderCustomChild(cacheParam.deadline)) {
1257 break;
1258 }
1259 item->Measure(childLayoutConstraint_);
1260 cachedItem_.push_back(currentIndex);
1261 }
1262 cacheParam.backwardCachedIndex = std::max(currentIndex + 1, limit);
1263 }
1264 }
1265
LayoutCacheItem(LayoutWrapper * layoutWrapper)1266 void ListItemGroupLayoutAlgorithm::LayoutCacheItem(LayoutWrapper* layoutWrapper)
1267 {
1268 for (auto index : cachedItem_) {
1269 auto item = layoutWrapper->GetOrCreateChildByIndex(index + itemStartIndex_, false, true);
1270 if (item) {
1271 item->SetActive(true);
1272 item->Layout();
1273 item->SetActive(false);
1274 }
1275 }
1276 }
1277 } // namespace OHOS::Ace::NG
1278