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 const auto& padding = layoutProperty->CreatePaddingAndBorder();
63 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
64 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
65 auto contentConstraint = layoutProperty->GetContentLayoutConstraint().value();
66 auto contentIdealSize = CreateIdealSize(
67 contentConstraint, axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
68
69 auto mainPercentRefer = GetMainAxisSize(contentConstraint.percentReference, axis_);
70 auto space = layoutProperty->GetSpace().value_or(Dimension(0));
71
72 auto layoutConstraint = layoutProperty->GetLayoutConstraint().value();
73 CalculateLanes(listLayoutProperty_, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
74 auto itemLayoutConstraint = layoutProperty->CreateChildConstraint();
75 isCardStyle_ = IsCardStyleForListItemGroup(layoutWrapper);
76 if (isCardStyle_) {
77 auto maxWidth = GetListItemGroupMaxWidth(contentConstraint.parentIdealSize, layoutProperty) -
78 layoutProperty->CreatePaddingAndBorder().Width();
79 contentIdealSize.SetCrossSize(maxWidth, axis_);
80 }
81 UpdateListItemConstraint(contentIdealSize, itemLayoutConstraint);
82 auto headerFooterLayoutConstraint = layoutProperty->CreateChildConstraint();
83 headerFooterLayoutConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
84 referencePos_ = UpdateReferencePos(layoutProperty, forwardLayout_, referencePos_);
85 totalItemCount_ = layoutWrapper->GetTotalChildCount() - itemStartIndex_;
86 totalMainSize_ = layoutWrapper->GetGeometryNode()->GetPaddingSize().MainSize(axis_);
87 if (headerIndex_ >= 0) {
88 auto headerWrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
89 headerWrapper->Measure(headerFooterLayoutConstraint);
90 headerMainSize_ = GetMainAxisSize(headerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
91 }
92 if (footerIndex_ >= 0) {
93 auto footerWrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
94 footerWrapper->Measure(headerFooterLayoutConstraint);
95 footerMainSize_ = GetMainAxisSize(footerWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
96 }
97 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
98 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, endPos_ - startPos_)) {
99 spaceWidth_ = 0.0f;
100 }
101 if (layoutProperty->GetDivider().has_value()) {
102 auto divider = layoutProperty->GetDivider().value();
103 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
104 if (GreatOrEqual(dividerSpace.value(), endPos_ - startPos_)) {
105 dividerSpace.reset();
106 }
107 if (dividerSpace.has_value()) {
108 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
109 }
110 }
111 totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_);
112 MeasureListItem(layoutWrapper, itemLayoutConstraint);
113 AdjustItemPosition();
114
115 auto crossSize = contentIdealSize.CrossSize(axis_);
116 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
117 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
118 }
119 contentIdealSize.SetMainSize(totalMainSize_, axis_);
120 AddPaddingToSize(padding, contentIdealSize);
121 layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
122 layoutWrapper->SetCacheCount(listLayoutProperty_->GetCachedCountValue(1) * lanes_);
123 }
124
GetListItemGroupMaxWidth(const OptionalSizeF & parentIdealSize,RefPtr<LayoutProperty> layoutProperty)125 float ListItemGroupLayoutAlgorithm::GetListItemGroupMaxWidth(
126 const OptionalSizeF& parentIdealSize, RefPtr<LayoutProperty> layoutProperty)
127 {
128 RefPtr<GridColumnInfo> columnInfo;
129 columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::LIST_CARD);
130 columnInfo->GetParent()->BuildColumnWidth();
131 auto maxGridWidth = static_cast<float>(columnInfo->GetWidth(GetMaxGridCounts(columnInfo)));
132 auto parentWidth = parentIdealSize.CrossSize(axis_).value() + layoutProperty->CreatePaddingAndBorder().Width();
133 auto maxWidth = std::min(parentWidth, maxGridWidth);
134 return maxWidth;
135 }
136
Layout(LayoutWrapper * layoutWrapper)137 void ListItemGroupLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
138 {
139 const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
140 CHECK_NULL_VOID(layoutProperty);
141 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
142 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
143 MinusPaddingToSize(padding, size);
144 auto left = padding.left.value_or(0.0f);
145 auto top = padding.top.value_or(0.0f);
146 auto paddingOffset = OffsetF(left, top);
147 float crossSize = GetCrossAxisSize(size, axis_);
148 CHECK_NULL_VOID(listLayoutProperty_);
149 itemAlign_ = listLayoutProperty_->GetListItemAlign().value_or(V2::ListItemAlign::START);
150
151 if (headerIndex_ >= 0 || footerIndex_ >= 0) {
152 LayoutHeaderFooter(layoutWrapper, paddingOffset, crossSize);
153 }
154 // layout items.
155 LayoutListItem(layoutWrapper, paddingOffset, crossSize);
156 }
157
UpdateListItemConstraint(const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)158 void ListItemGroupLayoutAlgorithm::UpdateListItemConstraint(const OptionalSizeF& selfIdealSize,
159 LayoutConstraintF& contentConstraint)
160 {
161 contentConstraint.parentIdealSize = selfIdealSize;
162 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis_);
163 auto crossSizeOptional = selfIdealSize.CrossSize(axis_);
164 if (crossSizeOptional.has_value()) {
165 float crossSize = crossSizeOptional.value();
166 if (lanes_ > 1) {
167 crossSize = (crossSize + laneGutter_) / lanes_ - laneGutter_;
168 crossSize = crossSize <= 0 ? 1 : crossSize;
169 }
170 if (maxLaneLength_.has_value() && maxLaneLength_.value() < crossSize) {
171 crossSize = maxLaneLength_.value();
172 }
173 contentConstraint.percentReference.SetCrossSize(crossSize, axis_);
174 contentConstraint.parentIdealSize.SetCrossSize(crossSize, axis_);
175 contentConstraint.maxSize.SetCrossSize(crossSize, axis_);
176 if (minLaneLength_.has_value()) {
177 contentConstraint.minSize.SetCrossSize(minLaneLength_.value(), axis_);
178 }
179 }
180 }
181
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis)182 float ListItemGroupLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis)
183 {
184 float maxCrossSize = 0.0f;
185 for (const auto& pos : itemPosition_) {
186 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
187 if (!wrapper) {
188 continue;
189 }
190 auto getGeometryNode = wrapper->GetGeometryNode();
191 if (!getGeometryNode) {
192 continue;
193 }
194 maxCrossSize = std::max(maxCrossSize, getGeometryNode->GetMarginFrameSize().CrossSize(axis));
195 }
196 return maxCrossSize;
197 }
198
UpdateReferencePos(RefPtr<LayoutProperty> layoutProperty,bool forwardLayout,float referencePos)199 float ListItemGroupLayoutAlgorithm::UpdateReferencePos(
200 RefPtr<LayoutProperty> layoutProperty, bool forwardLayout, float referencePos)
201 {
202 const auto& padding = layoutProperty->CreatePaddingAndBorder();
203 const auto& margin = layoutProperty->CreateMargin();
204 auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
205 auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
206 offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
207 offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
208 forwardLayout ? referencePos += offsetBeforeContent : referencePos -= offsetAfterContent;
209 return referencePos;
210 }
211
NeedMeasureItem() const212 bool ListItemGroupLayoutAlgorithm::NeedMeasureItem() const
213 {
214 if (forwardLayout_) {
215 if (GreatNotEqual(headerMainSize_, endPos_ - referencePos_)) {
216 return false;
217 }
218 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - referencePos_)) {
219 return false;
220 }
221 } else {
222 if (GreatNotEqual(headerMainSize_, endPos_ - (referencePos_ - totalMainSize_))) {
223 return false;
224 }
225 if (LessNotEqual(totalMainSize_ - footerMainSize_, startPos_ - (referencePos_ - totalMainSize_))) {
226 return false;
227 }
228 }
229 return true;
230 }
231
LayoutListItemAll(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,float startPos)232 void ListItemGroupLayoutAlgorithm::LayoutListItemAll(LayoutWrapper* layoutWrapper,
233 const LayoutConstraintF& layoutConstraint, float startPos)
234 {
235 int32_t currentIndex = -1;
236 float currentEndPos = startPos;
237 float currentStartPos = 0.0f;
238 while (currentIndex < totalItemCount_) {
239 currentStartPos = currentEndPos;
240 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
241 currentStartPos, currentEndPos);
242 if (count == 0) {
243 break;
244 }
245 if (currentIndex < (totalItemCount_ - 1)) {
246 currentEndPos += spaceWidth_;
247 }
248
249 }
250 }
251
ClearItemPosition(LayoutWrapper * layoutWrapper)252 void ListItemGroupLayoutAlgorithm::ClearItemPosition(LayoutWrapper* layoutWrapper)
253 {
254 itemPosition_.clear();
255 layoutWrapper->RemoveAllChildInRenderTree();
256 }
257
MeasureListItem(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint)258 void ListItemGroupLayoutAlgorithm::MeasureListItem(
259 LayoutWrapper* layoutWrapper, const LayoutConstraintF& layoutConstraint)
260 {
261 if (totalItemCount_ <= 0) {
262 totalMainSize_ = headerMainSize_ + footerMainSize_;
263 layoutWrapper->RemoveAllChildInRenderTree();
264 itemPosition_.clear();
265 return;
266 }
267 int32_t startIndex = 0;
268 int32_t endIndex = totalItemCount_ - 1;
269 float startPos = headerMainSize_;
270 float endPos = totalMainSize_ - footerMainSize_;
271 prevStartPos_ = startPos_;
272 prevEndPos_ = endPos_;
273 if (needAllLayout_) {
274 needAllLayout_ = false;
275 itemPosition_.clear();
276 layoutWrapper->RemoveAllChildInRenderTree();
277 LayoutListItemAll(layoutWrapper, layoutConstraint, startPos);
278 return;
279 }
280 if (targetIndex_) {
281 startPos_ = -Infinity<float>();
282 endPos_ = Infinity<float>();
283 }
284 if (jumpIndex_.has_value()) {
285 if (jumpIndex_.value() == LAST_ITEM) {
286 jumpIndex_ = totalItemCount_ - 1;
287 }
288 auto jumpIndex = jumpIndex_.value();
289 if (jumpIndex < 0 || jumpIndex >= totalItemCount_) {
290 jumpIndex = 0;
291 }
292 if (scrollAlign_ == ScrollAlign::CENTER || scrollAlign_ == ScrollAlign::START ||
293 scrollAlign_ == ScrollAlign::AUTO) {
294 startIndex = jumpIndex;
295 } else if (scrollAlign_ == ScrollAlign::END) {
296 endIndex = jumpIndex;
297 } else if (forwardLayout_) {
298 startIndex = jumpIndex;
299 } else {
300 endIndex = jumpIndex;
301 }
302 itemPosition_.clear();
303 layoutWrapper->RemoveAllChildInRenderTree();
304 jumpIndex_.reset();
305 } else if (!itemPosition_.empty()) {
306 if (itemPosition_.begin()->first > 0 || (forwardLayout_ && Negative(referencePos_))) {
307 startPos = itemPosition_.begin()->second.first;
308 }
309 endPos = itemPosition_.rbegin()->second.second;
310 startIndex = GetStartIndex();
311 if (startIndex >= totalItemCount_) {
312 startIndex = totalItemCount_ - 1;
313 if (itemPosition_.begin()->first > 0) {
314 startPos = ((startPos - headerMainSize_) / GetLanesFloor(itemPosition_.begin()->first)) *
315 GetLanesFloor(startIndex) + headerMainSize_;
316 }
317 }
318 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
319 itemPosition_.clear();
320 layoutWrapper->RemoveAllChildInRenderTree();
321 } else if (!NeedMeasureItem()) {
322 layoutWrapper->RemoveAllChildInRenderTree();
323 itemPosition_.clear();
324 return;
325 }
326 if (scrollAlign_ == ScrollAlign::CENTER) {
327 startIndex = GetLanesFloor(startIndex);
328 MeasureCenter(layoutWrapper, layoutConstraint, startIndex);
329 } else if (scrollAlign_ == ScrollAlign::START) {
330 startIndex = GetLanesFloor(startIndex);
331 MeasureStart(layoutWrapper, layoutConstraint, startIndex);
332 } else if (scrollAlign_ == ScrollAlign::END) {
333 endIndex = GetLanesCeil(endIndex);
334 MeasureEnd(layoutWrapper, layoutConstraint, endIndex);
335 } else if (jumpIndex_.has_value() && scrollAlign_ == ScrollAlign::AUTO) {
336 startIndex = GetLanesFloor(startIndex);
337 MeasureAuto(layoutWrapper, layoutConstraint, startIndex);
338 } else if (forwardLayout_) {
339 startIndex = GetLanesFloor(startIndex);
340 MeasureForward(layoutWrapper, layoutConstraint, startIndex, startPos);
341 } else {
342 endIndex = GetLanesCeil(endIndex);
343 MeasureBackward(layoutWrapper, layoutConstraint, endIndex, endPos);
344 }
345 }
346
GetItemGroupPosition(int32_t index)347 std::pair<float, float> ListItemGroupLayoutAlgorithm::GetItemGroupPosition(int32_t index)
348 {
349 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
350 if (scrollAlign_ == ScrollAlign::CENTER) {
351 float mainLen = 0;
352 float center = (startPos_ + endPos_) / 2; // 2:average
353 auto pos = itemPosition_.find(index);
354 if (pos != itemPosition_.end()) {
355 mainLen = pos->second.second - pos->second.first;
356 float refPos = (pos->second.second + pos->second.first) / 2 + paddingBeforeContent_; // 2:average
357 float delta = center - refPos;
358 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
359 }
360 } else if (scrollAlign_ == ScrollAlign::START) {
361 auto pos = itemPosition_.find(index);
362 if (pos != itemPosition_.end()) {
363 float top = startPos_ + contentStartOffset_;
364 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
365 top += headerMainSize_;
366 }
367 float refPos = pos->second.first + paddingBeforeContent_;
368 float delta = top - refPos;
369 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
370 }
371 } else if (scrollAlign_ == ScrollAlign::END) {
372 auto pos = itemPosition_.find(index);
373 if (pos != itemPosition_.end()) {
374 float bottom = endPos_ - contentEndOffset_;
375 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
376 bottom -= footerMainSize_;
377 }
378 float refPos = pos->second.second + paddingBeforeContent_;
379 float delta = bottom - refPos;
380 return { delta, totalMainSize_ + paddingBeforeContent_ + paddingAfterContent_ + delta };
381 }
382 }
383 return { 0.0f, 0.0f };
384 }
385
GetItemHeight(int32_t index)386 float ListItemGroupLayoutAlgorithm::GetItemHeight(int32_t index)
387 {
388 auto it = itemPosition_.find(index);
389 if (it != itemPosition_.end()) {
390 return it->second.second - it->second.first;
391 }
392 return 0.0f;
393 }
394
MeasureALineAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)395 int32_t ListItemGroupLayoutAlgorithm::MeasureALineAuto(LayoutWrapper* layoutWrapper,
396 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
397 {
398 auto wrapper = GetListItem(layoutWrapper, currentIndex);
399 if (!wrapper) {
400 return 0;
401 }
402 {
403 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
404 wrapper->Measure(layoutConstraint);
405 }
406 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
407 itemPosition_[currentIndex] = { 0.0f, mainLen };
408 return 1;
409 }
410
MeasureALineCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t currentIndex)411 int32_t ListItemGroupLayoutAlgorithm::MeasureALineCenter(LayoutWrapper* layoutWrapper,
412 const LayoutConstraintF& layoutConstraint, int32_t currentIndex)
413 {
414 float mainLen = 0;
415 int32_t cnt = 0;
416 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
417 for (int32_t i = 0; i < lanes && currentIndex + cnt < totalItemCount_; i++) {
418 auto wrapper = GetListItem(layoutWrapper, currentIndex + cnt);
419 if (!wrapper) {
420 break;
421 }
422 {
423 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex + cnt);
424 wrapper->Measure(layoutConstraint);
425 }
426 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
427 cnt++;
428 }
429 if (cnt > 0) {
430 auto startPos = (startPos_ + endPos_ - mainLen) / 2; // 2:average
431 auto endPos = startPos + mainLen; // 2:average
432 for (int32_t i = 0; i < cnt; i++) {
433 itemPosition_[currentIndex + i] = { startPos, endPos };
434 }
435 }
436 return cnt;
437 }
438
MeasureALineForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float startPos,float & endPos)439 int32_t ListItemGroupLayoutAlgorithm::MeasureALineForward(LayoutWrapper* layoutWrapper,
440 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float startPos, float& endPos)
441 {
442 float mainLen = 0.0f;
443 int32_t cnt = 0;
444 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
445 for (int32_t i = 0; i < lanes && currentIndex + 1 <= totalItemCount_; i++) {
446 auto wrapper = GetListItem(layoutWrapper, currentIndex + 1);
447 if (!wrapper) {
448 break;
449 }
450 cnt++;
451 ++currentIndex;
452 {
453 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
454 wrapper->Measure(layoutConstraint);
455 }
456 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
457 }
458 if (cnt > 0) {
459 endPos = startPos + mainLen;
460 for (int32_t i = 0; i < cnt; i++) {
461 itemPosition_[currentIndex - i] = { startPos, endPos };
462 }
463 }
464 return cnt;
465 }
466
MeasureALineBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t & currentIndex,float endPos,float & startPos)467 int32_t ListItemGroupLayoutAlgorithm::MeasureALineBackward(LayoutWrapper* layoutWrapper,
468 const LayoutConstraintF& layoutConstraint, int32_t& currentIndex, float endPos, float& startPos)
469 {
470 float mainLen = 0.0f;
471 int32_t cnt = 0;
472 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
473 for (int32_t i = 0; i < lanes && currentIndex - 1 >= 0; i++) {
474 auto wrapper = GetListItem(layoutWrapper, currentIndex - 1);
475 if (!wrapper) {
476 break;
477 }
478 --currentIndex;
479 cnt++;
480 {
481 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
482 wrapper->Measure(layoutConstraint);
483 }
484 mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_));
485 if (currentIndex % lanes == 0) {
486 break;
487 }
488 }
489 if (cnt > 0) {
490 startPos = endPos - mainLen;
491 for (int32_t i = 0; i < cnt; i++) {
492 itemPosition_[currentIndex + i] = { startPos, endPos };
493 }
494 }
495 return cnt;
496 }
497
MeasureCenter(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)498 void ListItemGroupLayoutAlgorithm::MeasureCenter(LayoutWrapper* layoutWrapper,
499 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
500 {
501 MeasureALineCenter(layoutWrapper, layoutConstraint, startIndex);
502 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, GetEndIndex() + 1, GetEndPosition());
503 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, GetStartIndex() - 1, GetStartPosition());
504
505 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
506 float currentStartPos = headerMainSize_;
507 int32_t i = 0;
508 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
509 for (auto& pos : itemPosition_) {
510 float len = pos.second.second - pos.second.first;
511 pos.second.first = currentStartPos;
512 pos.second.second = currentStartPos + len;
513 i++;
514 if (i % lanes == 0) {
515 currentStartPos = pos.second.second + spaceWidth_;
516 }
517 }
518 }
519
MeasureAuto(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)520 void ListItemGroupLayoutAlgorithm::MeasureAuto(LayoutWrapper* layoutWrapper,
521 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
522 {
523 if (MeasureALineAuto(layoutWrapper, layoutConstraint, startIndex) == 0) {
524 return;
525 }
526
527 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
528 }
529
MeasureJumpToItemForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)530 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemForward(LayoutWrapper* layoutWrapper,
531 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
532 {
533 float currentStartPos = startPos;
534 float currentEndPos = startPos;
535 int32_t currentIndex = startIndex - 1;
536 while (LessOrEqual(currentEndPos, endPos_)) {
537 currentStartPos = currentEndPos;
538 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
539 currentStartPos, currentEndPos);
540 if (count == 0) {
541 break;
542 }
543 if (currentIndex < (totalItemCount_ - 1)) {
544 currentEndPos += spaceWidth_;
545 }
546 }
547 }
548
MeasureJumpToItemBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)549 void ListItemGroupLayoutAlgorithm::MeasureJumpToItemBackward(LayoutWrapper* layoutWrapper,
550 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
551 {
552 float currentEndPos = endPos;
553 float currentStartPos = endPos;
554 int32_t currentIndex = endIndex + 1;
555 while (GreatOrEqual(currentStartPos, startPos_)) {
556 currentEndPos = currentStartPos;
557 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
558 currentEndPos, currentStartPos);
559 if (count == 0) {
560 break;
561 }
562 if (currentIndex > 0) {
563 currentStartPos -= spaceWidth_;
564 }
565 }
566 }
567
MeasureStart(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex)568 void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper,
569 const LayoutConstraintF& layoutConstraint, int32_t startIndex)
570 {
571 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
572 float currentStartPos = startPos_ + contentStartOffset_;
573 if (sticky == V2::StickyStyle::HEADER || sticky == V2::StickyStyle::BOTH) {
574 currentStartPos += headerMainSize_;
575 }
576
577 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos);
578 if (Positive(contentStartOffset_)) {
579 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos);
580 }
581
582 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
583 currentStartPos = headerMainSize_;
584 int32_t i = 0;
585 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
586 for (auto& pos : itemPosition_) {
587 float len = pos.second.second - pos.second.first;
588 pos.second.first = currentStartPos;
589 pos.second.second = currentStartPos + len;
590 i++;
591 if (i % lanes == 0) {
592 currentStartPos = pos.second.second + spaceWidth_;
593 }
594 }
595 }
596
MeasureEnd(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex)597 void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper,
598 const LayoutConstraintF& layoutConstraint, int32_t endIndex)
599 {
600 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
601 float currentEndPos = endPos_ - contentEndOffset_;
602 if (sticky == V2::StickyStyle::FOOTER || sticky == V2::StickyStyle::BOTH) {
603 currentEndPos -= footerMainSize_;
604 }
605
606 MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos);
607 if (Positive(contentEndOffset_)) {
608 MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos);
609 }
610
611 totalMainSize_ = GetEndPosition() - GetStartPosition() + headerMainSize_ + footerMainSize_;
612 float currentStartPos = headerMainSize_;
613 int32_t i = 0;
614 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
615 for (auto& pos : itemPosition_) {
616 float len = pos.second.second - pos.second.first;
617 pos.second.first = currentStartPos;
618 pos.second.second = currentStartPos + len;
619 i++;
620 if (i % lanes == 0) {
621 currentStartPos = pos.second.second + spaceWidth_;
622 }
623 }
624 }
625
MeasureForward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t startIndex,float startPos)626 void ListItemGroupLayoutAlgorithm::MeasureForward(LayoutWrapper* layoutWrapper,
627 const LayoutConstraintF& layoutConstraint, int32_t startIndex, float startPos)
628 {
629 float currentEndPos = startPos;
630 float currentStartPos = 0.0f;
631 int32_t currentIndex = startIndex - 1;
632 while (LessOrEqual(currentEndPos, endPos_ - referencePos_)) {
633 currentStartPos = currentEndPos;
634 int32_t count = MeasureALineForward(layoutWrapper, layoutConstraint, currentIndex,
635 currentStartPos, currentEndPos);
636 if (count == 0) {
637 break;
638 }
639 if (currentIndex < (totalItemCount_ - 1)) {
640 currentEndPos += spaceWidth_;
641 }
642 if (targetIndex_ && GreatOrEqual(startIndex, targetIndex_.value())) {
643 startPos_ = prevStartPos_;
644 endPos_ = prevEndPos_;
645 targetIndex_.reset();
646 }
647 }
648
649 currentStartPos = startPos - spaceWidth_;
650 currentIndex = startIndex;
651 float th = std::max(startPos_ - referencePos_, headerMainSize_);
652 while (currentIndex > 0 && GreatNotEqual(currentStartPos, th)) {
653 currentEndPos = currentStartPos;
654 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
655 currentEndPos, currentStartPos);
656 if (count == 0) {
657 break;
658 }
659 if (currentIndex > 0) {
660 currentStartPos = currentStartPos - spaceWidth_;
661 }
662 }
663 }
664
MeasureBackward(LayoutWrapper * layoutWrapper,const LayoutConstraintF & layoutConstraint,int32_t endIndex,float endPos)665 void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper,
666 const LayoutConstraintF& layoutConstraint, int32_t endIndex, float endPos)
667 {
668 float currentStartPos = endPos;
669 float currentEndPos = 0.0f;
670 auto currentIndex = endIndex + 1;
671 while (GreatOrEqual(currentStartPos, startPos_ - (referencePos_ - totalMainSize_))) {
672 currentEndPos = currentStartPos;
673 int32_t count = MeasureALineBackward(layoutWrapper, layoutConstraint, currentIndex,
674 currentEndPos, currentStartPos);
675 if (count == 0) {
676 break;
677 }
678 if (currentIndex > 0) {
679 currentStartPos = currentStartPos - spaceWidth_;
680 }
681 if (targetIndex_ && LessOrEqual(endIndex, targetIndex_.value())) {
682 startPos_ = prevStartPos_;
683 endPos_ = prevEndPos_;
684 targetIndex_.reset();
685 }
686 }
687 }
688
AdjustItemPosition()689 void ListItemGroupLayoutAlgorithm::AdjustItemPosition()
690 {
691 if (itemPosition_.empty()) {
692 return;
693 }
694 float currentStartPos = GetStartPosition();
695 if (currentStartPos < headerMainSize_) {
696 auto delta = headerMainSize_ - currentStartPos;
697 for (auto& pos : itemPosition_) {
698 pos.second.first += delta;
699 pos.second.second += delta;
700 }
701 totalMainSize_ = std::max(totalMainSize_ + delta, GetEndPosition() + footerMainSize_);
702 } else if (GetStartIndex() == 0 && currentStartPos > headerMainSize_) {
703 auto delta = currentStartPos - headerMainSize_;
704 for (auto& pos : itemPosition_) {
705 pos.second.first -= delta;
706 pos.second.second -= delta;
707 }
708 totalMainSize_ -= delta;
709 }
710 if (GetEndIndex() == totalItemCount_ - 1) {
711 totalMainSize_ = GetEndPosition() + footerMainSize_;
712 } else {
713 totalMainSize_ = std::max(totalMainSize_, GetEndPosition() + footerMainSize_);
714 }
715 const auto& start = *itemPosition_.begin();
716 const auto& end = *itemPosition_.rbegin();
717 if (layoutedItemInfo_.has_value()) {
718 auto& itemInfo = layoutedItemInfo_.value();
719 if (start.first <= itemInfo.startIndex || LessNotEqual(start.second.first, itemInfo.startPos)) {
720 itemInfo.startIndex = start.first;
721 itemInfo.startPos = start.second.first;
722 }
723 if (end.first >= itemInfo.endIndex || LessNotEqual(end.second.second, itemInfo.endPos)) {
724 itemInfo.endIndex = end.first;
725 itemInfo.endPos = end.second.second;
726 }
727 } else {
728 layoutedItemInfo_ = { start.first, start.second.first, end.first, end.second.second };
729 }
730 }
731
CheckRecycle(const RefPtr<LayoutWrapper> & layoutWrapper,float startPos,float endPos,float referencePos,bool forwardLayout)732 void ListItemGroupLayoutAlgorithm::CheckRecycle(
733 const RefPtr<LayoutWrapper>& layoutWrapper, float startPos, float endPos, float referencePos, bool forwardLayout)
734 {
735 referencePos = UpdateReferencePos(layoutWrapper->GetLayoutProperty(), forwardLayout, referencePos);
736 // Mark inactive in wrapper.
737 if (forwardLayout) {
738 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
739 if (GreatOrEqual(pos->second.second, startPos - referencePos)) {
740 break;
741 }
742 RecycleListItem(layoutWrapper, pos->first);
743 itemPosition_.erase(pos++);
744 }
745 return;
746 }
747 std::list<int32_t> removeIndexes;
748 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
749 if (LessOrEqual(pos->second.first, endPos - (referencePos - totalMainSize_))) {
750 break;
751 }
752 RecycleListItem(layoutWrapper, pos->first);
753 removeIndexes.emplace_back(pos->first);
754 }
755 for (const auto& index : removeIndexes) {
756 itemPosition_.erase(index);
757 }
758 }
759
LayoutListItem(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)760 void ListItemGroupLayoutAlgorithm::LayoutListItem(LayoutWrapper* layoutWrapper,
761 const OffsetF& paddingOffset, float crossSize)
762 {
763 // layout items.
764 for (auto& pos : itemPosition_) {
765 auto wrapper = GetListItem(layoutWrapper, pos.first);
766 if (!wrapper) {
767 continue;
768 }
769
770 auto offset = paddingOffset;
771 int32_t laneIndex = pos.first % lanes_;
772 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
773 float laneCrossOffset = CalculateLaneCrossOffset((crossSize + GetLaneGutter()) / lanes_, childCrossSize);
774 if (axis_ == Axis::VERTICAL) {
775 offset =
776 offset + OffsetF(0, pos.second.first) + OffsetF(laneCrossOffset, 0) +
777 OffsetF(((crossSize + laneGutter_) / lanes_) * laneIndex, 0);
778 } else {
779 offset =
780 offset + OffsetF(pos.second.first, 0) + OffsetF(0, laneCrossOffset) +
781 OffsetF(0, ((crossSize + laneGutter_) / lanes_) * laneIndex);
782 }
783 SetListItemIndex(layoutWrapper, wrapper, pos.first);
784 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
785 wrapper->Layout();
786 }
787 }
788
LayoutHeaderFooter(LayoutWrapper * layoutWrapper,const OffsetF & paddingOffset,float crossSize)789 void ListItemGroupLayoutAlgorithm::LayoutHeaderFooter(LayoutWrapper* layoutWrapper,
790 const OffsetF& paddingOffset, float crossSize)
791 {
792 OffsetF selfOffset = layoutWrapper->GetGeometryNode()->GetPaddingOffset();
793 selfOffset = selfOffset - listLayoutProperty_->CreatePaddingAndBorder().Offset();
794 float mainPos = GetMainAxisOffset(selfOffset, axis_);
795 float headerMainSize = 0.0f;
796 V2::StickyStyle sticky = listLayoutProperty_->GetStickyStyle().value_or(V2::StickyStyle::NONE);
797 if (headerIndex_ >= 0) {
798 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(headerIndex_);
799 CHECK_NULL_VOID(wrapper);
800 headerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
801 float headerPos = 0.0f;
802 if ((sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::HEADER) && !itemPosition_.empty()) {
803 contentStartOffset_ = std::max(contentStartOffset_, 0.0f);
804 float stickyPos = contentStartOffset_ - mainPos;
805 if (GetEndIndex() == totalItemCount_ - 1) {
806 stickyPos = std::min(stickyPos, GetEndPosition() - headerMainSize);
807 }
808 headerPos = std::max(headerPos, stickyPos);
809 }
810 LayoutIndex(wrapper, paddingOffset, crossSize, headerPos);
811 }
812
813 if (footerIndex_ >= 0) {
814 float endPos = totalMainSize_ - footerMainSize_;
815 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(footerIndex_);
816 CHECK_NULL_VOID(wrapper);
817 float const listMainSize = endPos_ - startPos_;
818 if (Positive(listMainSize) && (sticky == V2::StickyStyle::BOTH || sticky == V2::StickyStyle::FOOTER)) {
819 auto footerMainSize = wrapper->GetGeometryNode()->GetFrameSize().MainSize(axis_);
820 float stickyPos = listMainSize - contentEndOffset_ - mainPos - footerMainSize;
821 if (stickyPos < headerMainSize) {
822 stickyPos = headerMainSize;
823 }
824 if (stickyPos < endPos) {
825 endPos = stickyPos;
826 }
827 }
828 LayoutIndex(wrapper, paddingOffset, crossSize, endPos);
829 }
830 }
831
LayoutIndex(const RefPtr<LayoutWrapper> & wrapper,const OffsetF & paddingOffset,float crossSize,float startPos)832 void ListItemGroupLayoutAlgorithm::LayoutIndex(const RefPtr<LayoutWrapper>& wrapper, const OffsetF& paddingOffset,
833 float crossSize, float startPos)
834 {
835 CHECK_NULL_VOID(wrapper);
836 auto offset = paddingOffset;
837 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
838 float laneCrossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
839 if (axis_ == Axis::VERTICAL) {
840 offset = offset + OffsetF(laneCrossOffset, startPos);
841 } else {
842 offset = offset + OffsetF(startPos, laneCrossOffset);
843 }
844 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
845 wrapper->Layout();
846 }
847
CalculateLaneCrossOffset(float crossSize,float childCrossSize)848 float ListItemGroupLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
849 {
850 float delta = crossSize - GetLaneGutter() - childCrossSize;
851 if (LessOrEqual(delta, 0.0f)) {
852 return 0.0f;
853 }
854 switch (itemAlign_) {
855 case OHOS::Ace::V2::ListItemAlign::START:
856 return 0.0f;
857 case OHOS::Ace::V2::ListItemAlign::CENTER:
858 return delta / 2; /* 2:average */
859 case OHOS::Ace::V2::ListItemAlign::END:
860 return delta;
861 default:
862 return 0.0f;
863 }
864 }
865
CalculateLanes(const RefPtr<ListLayoutProperty> & layoutProperty,const LayoutConstraintF & layoutConstraint,std::optional<float> crossSizeOptional,Axis axis)866 void ListItemGroupLayoutAlgorithm::CalculateLanes(const RefPtr<ListLayoutProperty>& layoutProperty,
867 const LayoutConstraintF& layoutConstraint, std::optional<float> crossSizeOptional, Axis axis)
868 {
869 int32_t lanes = layoutProperty->GetLanes().value_or(1);
870 lanes = lanes > 1 ? lanes : 1;
871 if (crossSizeOptional.has_value()) {
872 if (layoutProperty->GetLaneMinLength().has_value()) {
873 minLaneLength_ = ConvertToPx(layoutProperty->GetLaneMinLength().value(),
874 layoutConstraint.scaleProperty, crossSizeOptional.value());
875 }
876 if (layoutProperty->GetLaneMaxLength().has_value()) {
877 maxLaneLength_ = ConvertToPx(layoutProperty->GetLaneMaxLength().value(),
878 layoutConstraint.scaleProperty, crossSizeOptional.value());
879 }
880 if (layoutProperty->GetLaneGutter().has_value()) {
881 auto laneGutter = ConvertToPx(
882 layoutProperty->GetLaneGutter().value(), layoutConstraint.scaleProperty, crossSizeOptional.value());
883 laneGutter_ = laneGutter.value();
884 }
885 }
886 lanes_ = ListLanesLayoutAlgorithm::CalculateLanesParam(
887 minLaneLength_, maxLaneLength_, lanes, crossSizeOptional, laneGutter_);
888 }
889
SetListItemIndex(const LayoutWrapper * groupLayoutWrapper,const RefPtr<LayoutWrapper> & itemLayoutWrapper,int32_t indexInGroup)890 void ListItemGroupLayoutAlgorithm::SetListItemIndex(const LayoutWrapper* groupLayoutWrapper,
891 const RefPtr<LayoutWrapper>& itemLayoutWrapper, int32_t indexInGroup)
892 {
893 auto host = itemLayoutWrapper->GetHostNode();
894 CHECK_NULL_VOID(host);
895 auto listItem = host->GetPattern<ListItemPattern>();
896 CHECK_NULL_VOID(listItem);
897 listItem->SetIndexInListItemGroup(indexInGroup);
898
899 host = groupLayoutWrapper->GetHostNode();
900 CHECK_NULL_VOID(host);
901 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
902 CHECK_NULL_VOID(listItemGroup);
903 listItem->SetIndexInList(listItemGroup->GetIndexInList());
904 }
905
IsCardStyleForListItemGroup(const LayoutWrapper * groupLayoutWrapper)906 bool ListItemGroupLayoutAlgorithm::IsCardStyleForListItemGroup(const LayoutWrapper* groupLayoutWrapper)
907 {
908 auto host = groupLayoutWrapper->GetHostNode();
909 CHECK_NULL_RETURN(host, false);
910 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
911 CHECK_NULL_RETURN(listItemGroup, false);
912 return listItemGroup->GetListItemGroupStyle() == V2::ListItemGroupStyle::CARD;
913 }
914 } // namespace OHOS::Ace::NG
915