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 auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
63 auto contentIdealSize = CreateIdealSize(
64 contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
65
66 auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
67 auto space = layoutProperty->GetSpace().value_or(Dimension(0));
68
69 auto layoutConstraint = layoutProperty->GetLayoutConstraint().value();
70 CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
71 auto itemLayoutConstraint = layoutProperty->CreateChildConstraint();
72 isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
73 if (isCardStyle_) {
74 auto maxWidth = GetListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty) -
75 layoutProperty->CreatePaddingAndBorder().Width();
76 contentIdealSize.SetCrossSize(maxWidth, axis_);
77 }
78 UpdateListItemConstraint(contentIdealSize, itemLayoutConstraint);
79 auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
80 headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
81 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
82 if (layoutProperty->GetDivider().has_value()) {
83 auto divider = layoutProperty->GetDivider().value();
84 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
85 if (dividerSpace.has_value()) {
86 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
87 }
88 }
89 UpdateReferencePos(layoutProperty);
90 totalItemCount_ = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
91
92 totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
93 if (headerIndex_ >= 0) {
94 auto headerWrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
95 headerWrapper->Measure(headerFooterLayoutConstraint);
96 headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
97 }
98 if (footerIndex_ >= 0) {
99 auto footerWrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
100 footerWrapper->Measure(headerFooterLayoutConstraint);
101 footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
102 }
103 totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
104 MeasureListItem(layoutWrapper, itemLayoutConstraint);
105 if (!itemPosition_.empty()) {
106 if (GetEndIndex() == totalItemCount_ - 1) {
107 totalMainSize_ = GetEndPosition() + footerMainSize_;
108 } else {
109 totalMainSize_ = std::max(totalMainSize_, GetEndPosition() + footerMainSize_);
110 }
111 }
112
113 auto crossSize = contentIdealSize.CrossSize(axis_);
114 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
115 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
116 }
117 contentIdealSize.SetMainSize(totalMainSize_, axis_);
118 const auto& padding = layoutProperty->CreatePaddingAndBorder();
119 AddPaddingToSize(padding, contentIdealSize);
120 layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
121 layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountValue(1) * 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 return maxWidth;
134 }
135
Layout(LayoutWrapper * layoutWrapper)136 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
137 {
138 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
139 CHECK_NULL_VOID(layoutProperty);
140 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
141 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
142 MinusPaddingToSize(padding, size);
143 auto left = padding.left.value_or(0.0f);
144 auto top = padding.top.value_or(0.0f);
145 auto paddingOffset = OffsetF(left, top);
146 float crossSize = GetCrossAxisSize(size, axis_);
147 CHECK_NULL_VOID(listLayoutProperty_);
148 itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
149
150 if (headerIndex_ >= 0 || footerIndex_ >= 0) {
151 LayoutHeaderFooter(layoutWrapper, paddingOffset, crossSize);
152 }
153 // layout items.
154 LayoutListItem(layoutWrapper, paddingOffset, crossSize);
155 }
156
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)157 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
158 LayoutConstraintF& contentConstraint)
159 {
160 contentConstraint.parentIdealSize = selfIdealSize;
161 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
162 auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
163 if (crossSizeOptional.has_value()) {
164 float crossSize = crossSizeOptional.value();
165 if (lanes_ > 1) {
166 crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
167 crossSize = crossSize <= 0 ? 1 : crossSize;
168 }
169 if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
170 crossSize = maxLaneLength_.value();
171 }
172 contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
173 contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
174 contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
175 if (minLaneLength_.has_value()) {
176 contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
177 }
178 }
179 }
180
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)181 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
182 {
183 float maxCrossSize = 0.0f;
184 for (const auto& pos : itemPosition_) {
185 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
186 if (!wrapper) {
187 continue;
188 }
189 auto getGeometryNode = wrapper->GetGeometryNode();
190 if (!getGeometryNode) {
191 continue;
192 }
193 maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
194 }
195 return maxCrossSize;
196 }
197
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty)198 void ListItemGroupLayoutAlgorithm::UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty)
199 {
200 const auto& padding = layoutProperty->CreatePaddingAndBorder();
201 const auto& margin = layoutProperty->CreateMargin();
202 auto offsetBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
203 auto offsetAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
204 offsetBeforeContent_ += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
205 offsetAfterContent_ += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
206 forwardLayout_ ? referencePos_ += offsetBeforeContent_ : referencePos_ -= offsetAfterContent_;
207 }
208
NeedMeasureItem() const209 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem() const
210 {
211 if (forwardLayout_ && headerIndex_ >= 0) {
212 if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
213 return false;
214 }
215 }
216 if (forwardLayout_ && footerIndex_ >= 0) {
217 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
218 return false;
219 }
220 }
221 if (!forwardLayout_ && headerIndex_ >= 0) {
222 if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
223 return false;
224 }
225 }
226 if (!forwardLayout_ && footerIndex_ >= 0) {
227 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
228 return false;
229 }
230 }
231 return true;
232 }
233
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)234 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
235 const LayoutConstraintF& layoutConstraint, float startPos)
236 {
237 int32_t currentIndex = -1;
238 float currentEndPos = startPos;
239 float currentStartPos = 0.0f;
240 while (currentIndex < totalItemCount_) {
241 currentStartPos = currentEndPos;
242 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
243 currentStartPos, currentEndPos);
244 if (count == 0) {
245 break;
246 }
247 if (currentIndex < (totalItemCount_ - 1)) {
248 currentEndPos += spaceWidth_;
249 }
250
251 }
252 }
253
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)254 void ListItemGroupLayoutAlgorithm::MeasureListItem(
255 LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
256 {
257 if (totalItemCount_ <= 0) {
258 totalMainSize_ = headerMainSize_ + footerMainSize_;
259 layoutWrapper->RemoveAllChildInRenderTree();
260 itemPosition_.clear();
261 return;
262 }
263 int32_t startIndex = 0;
264 int32_t endIndex = totalItemCount_ - 1;
265 float startPos = headerMainSize_;
266 float endPos = totalMainSize_ - footerMainSize_;
267 prevStartPos_ = startPos_;
268 prevEndPos_ = endPos_;
269 if (needAllLayout_) {
270 needAllLayout_ = false;
271 itemPosition_.clear();
272 layoutWrapper->RemoveAllChildInRenderTree();
273 LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
274 return;
275 }
276 if (targetIndex_) {
277 startPos_ = -Infinity<float>();
278 endPos_ = Infinity<float>();
279 }
280 if (jumpIndex_.has_value()) {
281 if (jumpIndex_.value() == LAST_ITEM) {
282 jumpIndex_ = totalItemCount_ - 1;
283 }
284 auto jumpIndex = jumpIndex_.value();
285 if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
286 jumpIndex = 0;
287 }
288 if (forwardLayout_) {
289 startIndex = jumpIndex;
290 } else {
291 endIndex = jumpIndex;
292 }
293 itemPosition_.clear();
294 layoutWrapper->RemoveAllChildInRenderTree();
295 jumpIndex_.reset();
296 } else if (!itemPosition_.empty()) {
297 if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
298 startPos = itemPosition_.begin()->second.first;
299 }
300 endPos = itemPosition_.rbegin()->second.second;
301 startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
302 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
303 itemPosition_.clear();
304 layoutWrapper->RemoveAllChildInRenderTree();
305 } else if (!NeedMeasureItem()) {
306 layoutWrapper->RemoveAllChildInRenderTree();
307 itemPosition_.clear();
308 return;
309 }
310 LOGD("referencePos_ is %{public}f, startPos_: %{public}f, endPos_: %{public}f, forward:%{public}d",
311 referencePos_, startPos_, endPos_, forwardLayout_);
312 if (forwardLayout_) {
313 startIndex = GetLanesFloor(startIndex);
314 LOGD("startIndex:%{public}d, startPos:%{public}f", startIndex, startPos);
315 MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
316 } else {
317 endIndex = (lanes_ <= 1) ? endIndex : (endIndex - endIndex % lanes_ + lanes_ - 1);
318 endIndex = endIndex >= totalItemCount_ ? totalItemCount_ - 1 : endIndex;
319 LOGD("endIndex:%{public}d, endPos:%{public}f", endIndex, endPos);
320 MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
321 }
322 }
323
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)324 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
325 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
326 {
327 float mainLen = 0.0f;
328 int32_t cnt = 0;
329 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
330 for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_; i++) {
331 auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
332 if (!wrapper) {
333 break;
334 }
335 cnt++;
336 ++currentIndex;
337 {
338 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
339 wrapper->Measure(layoutConstraint);
340 }
341 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
342 }
343 if (cnt > 0) {
344 endPos = startPos + mainLen;
345 for (int32_t i = 0; i < cnt; i++) {
346 itemPosition_[currentIndex - i] = { startPos, endPos };
347 }
348 }
349 return cnt;
350 }
351
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)352 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
353 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
354 {
355 float mainLen = 0.0f;
356 int32_t cnt = 0;
357 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
358 for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
359 auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
360 if (!wrapper) {
361 break;
362 }
363 --currentIndex;
364 cnt++;
365 {
366 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
367 wrapper->Measure(layoutConstraint);
368 }
369 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
370 if (currentIndex % lanes == 0) {
371 break;
372 }
373 }
374 if (cnt > 0) {
375 startPos = endPos - mainLen;
376 for (int32_t i = 0; i < cnt; i++) {
377 itemPosition_[currentIndex + i] = { startPos, endPos };
378 }
379 }
380 return cnt;
381 }
382
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)383 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
384 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
385 {
386 float currentEndPos = startPos;
387 float currentStartPos = 0.0f;
388 int32_t currentIndex = startIndex - 1;
389 while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
390 currentStartPos = currentEndPos;
391 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
392 currentStartPos, currentEndPos);
393 if (count == 0) {
394 break;
395 }
396 if (currentIndex < (totalItemCount_ - 1)) {
397 currentEndPos += spaceWidth_;
398 }
399 LOGD("LayoutForward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
400 currentStartPos, currentEndPos);
401 if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
402 startPos_ = prevStartPos_;
403 endPos_ = prevEndPos_;
404 targetIndex_.reset();
405 }
406 }
407
408 currentStartPos = startPos - spaceWidth_;
409 currentIndex = startIndex;
410 while (currentIndex > 0 && GreatOrEqual(currentStartPos, startPos_ - referencePos_)) {
411 currentEndPos = currentStartPos;
412 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
413 currentEndPos, currentStartPos);
414 if (count == 0) {
415 break;
416 }
417 if (currentIndex > 0) {
418 currentStartPos = currentStartPos - spaceWidth_;
419 }
420 }
421 }
422
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)423 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
424 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
425 {
426 float currentStartPos = endPos;
427 float currentEndPos = 0.0f;
428 auto currentIndex = endIndex + 1;
429 while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
430 currentEndPos = currentStartPos;
431 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
432 currentEndPos, currentStartPos);
433 if (count == 0) {
434 break;
435 }
436 if (currentIndex > 0) {
437 currentStartPos = currentStartPos - spaceWidth_;
438 }
439 LOGD("LayoutBackward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
440 currentStartPos, currentEndPos);
441 if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
442 startPos_ = prevStartPos_;
443 endPos_ = prevEndPos_;
444 targetIndex_.reset();
445 }
446 }
447
448 if (itemPosition_.empty()) {
449 return;
450 }
451
452 if (currentStartPos < headerMainSize_) {
453 auto delta = headerMainSize_ - currentStartPos;
454 for (auto& pos : itemPosition_) {
455 pos.second.first += delta;
456 pos.second.second += delta;
457 }
458 totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
459 } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
460 auto delta = currentStartPos - headerMainSize_;
461 for (auto& pos : itemPosition_) {
462 pos.second.first -= delta;
463 pos.second.second -= delta;
464 }
465 totalMainSize_ -= delta;
466 }
467 }
468
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)469 void ListItemGroupLayoutAlgorithm::CheckRecycle(
470 const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
471 {
472 // Mark inactive in wrapper.
473 if (forwardLayout) {
474 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
475 if (GreatOrEqual(pos->second.second, startPos - referencePos)) {
476 break;
477 }
478 LOGI("recycle item:%{public}d", pos->first);
479 layoutWrapper->RemoveChildInRenderTree(pos->first);
480 itemPosition_.erase(pos++);
481 }
482 return;
483 }
484 std::list<int32_t> removeIndexes;
485 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
486 if (LessOrEqual(pos->second.first, endPos - (referencePos - totalMainSize_))) {
487 break;
488 }
489 layoutWrapper->RemoveChildInRenderTree(pos->first);
490 removeIndexes.emplace_back(pos->first);
491 }
492 for (const auto& index : removeIndexes) {
493 itemPosition_.erase(index);
494 }
495 }
496
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)497 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
498 const OffsetF& paddingOffset, float crossSize)
499 {
500 // layout items.
501 for (auto& pos : itemPosition_) {
502 auto wrapper = GetListItem(layoutWrapper, pos.first);
503 if (!wrapper) {
504 LOGI("wrapper is out of boundary");
505 continue;
506 }
507
508 auto offset = paddingOffset;
509 int32_t laneIndex = pos.first % lanes_;
510 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
511 float laneCrossOffset = CalculateLaneCrossOffset(crossSize / lanes_, childCrossSize);
512 if (axis_ == Axis::VERTICAL) {
513 offset =
514 offset + OffsetF(0, pos.second.first) + OffsetF(laneCrossOffset, 0) +
515 OffsetF(((crossSize + laneGutter_) / lanes_ - laneGutter_) * laneIndex + laneGutter_ * laneIndex, 0);
516 } else {
517 offset =
518 offset + OffsetF(pos.second.first, 0) + OffsetF(0, laneCrossOffset) +
519 OffsetF(0, ((crossSize + laneGutter_) / lanes_ - laneGutter_) * laneIndex + laneGutter_ * laneIndex);
520 }
521 SetListItemIndex(layoutWrapper, wrapper, pos.first);
522 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
523 wrapper->Layout();
524 }
525 }
526
LayoutHeaderFooter(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)527 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooter(LayoutWrapper* layoutWrapper,
528 const OffsetF& paddingOffset, float crossSize)
529 {
530 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
531 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
532 float mainPos = GetMainAxisOffset(selfOffset, axis_);
533 float headerMainSize = 0.0f;
534 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
535 if (headerIndex_ >= 0) {
536 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
537 CHECK_NULL_VOID(wrapper);
538 headerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
539 float headerPos = 0.0f;
540 if (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) {
541 float endPos = itemPosition_.empty() ? headerMainSize : itemPosition_.rbegin()->second.second;
542 float stickyPos = -mainPos;
543 if (stickyPos + headerMainSize > endPos) {
544 stickyPos = endPos - headerMainSize;
545 }
546 if (stickyPos > headerPos) {
547 headerPos = stickyPos;
548 }
549 }
550 LayoutIndex(wrapper, paddingOffset, crossSize, headerPos);
551 }
552
553 if (footerIndex_ >= 0) {
554 float endPos = totalMainSize_ - footerMainSize_;
555 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
556 CHECK_NULL_VOID(wrapper);
557 float const listMainSize = endPos_ - startPos_;
558 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
559 auto footerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
560 float stickyPos = listMainSize - mainPos - footerMainSize;
561 if (stickyPos < headerMainSize) {
562 stickyPos = headerMainSize;
563 }
564 if (stickyPos < endPos) {
565 endPos = stickyPos;
566 }
567 }
568 LayoutIndex(wrapper, paddingOffset, crossSize, endPos);
569 }
570 }
571
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)572 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
573 float crossSize, float startPos)
574 {
575 CHECK_NULL_VOID_NOLOG(wrapper);
576 auto offset = paddingOffset;
577 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
578 float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
579 if (axis_ == Axis::VERTICAL) {
580 offset = offset + OffsetF(laneCrossOffset, startPos);
581 } else {
582 offset = offset + OffsetF(startPos, laneCrossOffset);
583 }
584 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
585 wrapper->Layout();
586 }
587
CalculateLaneCrossOffset(float crossSize,float childCrossSize)588 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
589 {
590 float delta = crossSize - childCrossSize;
591 if (LessOrEqual(delta, 0.0f)) {
592 return 0.0f;
593 }
594 switch (itemAlign_) {
595 case OHOS::Ace::V2::ListItemAlign::START:
596 return 0.0f;
597 case OHOS::Ace::V2::ListItemAlign::CENTER:
598 return delta / 2; /* 2:average */
599 case OHOS::Ace::V2::ListItemAlign::END:
600 return delta;
601 default:
602 LOGW("Invalid ListItemAlign: %{public}d", itemAlign_);
603 return 0.0f;
604 }
605 }
606
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)607 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
608 const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
609 {
610 int32_t lanes = layoutProperty->GetLanes().value_or(1);
611 lanes = lanes > 1 ? lanes : 1;
612 if (crossSizeOptional.has_value()) {
613 if (layoutProperty->GetLaneMinLength().has_value()) {
614 minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
615 layoutConstraint.scaleProperty, crossSizeOptional.value());
616 }
617 if (layoutProperty->GetLaneMaxLength().has_value()) {
618 maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
619 layoutConstraint.scaleProperty, crossSizeOptional.value());
620 }
621 if (layoutProperty->GetLaneGutter().has_value()) {
622 auto laneGutter = ConvertToPx(
623 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
624 laneGutter_ = laneGutter.value();
625 }
626 }
627 lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
628 minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
629 }
630
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)631 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
632 const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
633 {
634 auto host = itemLayoutWrapper->GetHostNode();
635 CHECK_NULL_VOID_NOLOG(host);
636 auto listItem = host->GetPattern<ListItemPattern>();
637 CHECK_NULL_VOID_NOLOG(listItem);
638 listItem->SetIndexInListItemGroup(indexInGroup);
639
640 host = groupLayoutWrapper->GetHostNode();
641 CHECK_NULL_VOID_NOLOG(host);
642 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
643 CHECK_NULL_VOID_NOLOG(listItemGroup);
644 listItem->SetIndexInList(listItemGroup->GetIndexInList());
645 }
646
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)647 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
648 {
649 auto host = groupLayoutWrapper->GetHostNode();
650 CHECK_NULL_RETURN(host, false);
651 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
652 CHECK_NULL_RETURN(listItemGroup, false);
653 return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
654 }
655 } // namespace OHOS::Ace::NG
656