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_layout_algorithm.h"
17
18 #include <algorithm>
19 #include <cstdint>
20 #include <unordered_set>
21 #include <utility>
22
23 #include "base/geometry/axis.h"
24 #include "base/geometry/ng/offset_t.h"
25 #include "base/geometry/ng/size_t.h"
26 #include "base/log/ace_trace.h"
27 #include "base/memory/ace_type.h"
28 #include "base/utils/time_util.h"
29 #include "base/utils/utils.h"
30 #include "core/components/common/layout/layout_param.h"
31 #include "core/components_ng/base/frame_node.h"
32 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
33 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
34 #include "core/components_ng/pattern/list/list_item_pattern.h"
35 #include "core/components_ng/pattern/list/list_layout_property.h"
36 #include "core/components_ng/pattern/list/list_pattern.h"
37 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
38 #include "core/components_ng/pattern/text/text_base.h"
39 #include "core/components_ng/pattern/text_field/text_field_manager.h"
40 #include "core/components_ng/property/layout_constraint.h"
41 #include "core/components_ng/property/measure_property.h"
42 #include "core/components_ng/property/measure_utils.h"
43 #include "core/components_ng/property/property.h"
44 #include "core/components_v2/inspector/inspector_constants.h"
45 #include "core/components_v2/list/list_properties.h"
46 #include "core/pipeline_ng/pipeline_context.h"
47
48 namespace OHOS::Ace::NG {
49
50 namespace {
51 constexpr Dimension RESERVE_BOTTOM_HEIGHT = 24.0_vp;
52 constexpr float SCROLL_SNAP_VELOCITY_TH = 780;
53 } // namespace
54
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)55 void ListLayoutAlgorithm::UpdateListItemConstraint(
56 Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
57 {
58 contentConstraint.parentIdealSize = selfIdealSize;
59 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
60 auto crossSize = selfIdealSize.CrossSize(axis);
61 if (crossSize.has_value()) {
62 contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
63 contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
64 }
65 }
66
ReviseSpace(const RefPtr<ListLayoutProperty> & listLayoutProperty)67 void ListLayoutAlgorithm::ReviseSpace(const RefPtr<ListLayoutProperty>& listLayoutProperty)
68 {
69 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) {
70 spaceWidth_ = 0.0f;
71 }
72 if (listLayoutProperty->GetDivider().has_value()) {
73 auto divider = listLayoutProperty->GetDivider().value();
74 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
75 if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
76 dividerSpace.reset();
77 }
78 if (dividerSpace.has_value()) {
79 spaceWidth_ = std::max(spaceWidth_, static_cast<float>(Round(dividerSpace.value())));
80 }
81 }
82 spaceWidth_ += chainInterval_;
83 }
84
Measure(LayoutWrapper * layoutWrapper)85 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
86 {
87 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
88 CHECK_NULL_VOID(listLayoutProperty);
89 listLayoutProperty_ = listLayoutProperty;
90
91 axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
92 // Pre-recycle
93 ScrollableUtils::RecycleItemsOutOfBoundary(axis_, -currentDelta_, GetStartIndex(), GetEndIndex(), layoutWrapper);
94
95 const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value();
96
97 // calculate idealSize and set FrameSize
98 auto startOffset = listLayoutProperty->GetContentStartOffset().value_or(0.0f);
99 contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0);
100 auto endOffset = listLayoutProperty->GetContentEndOffset().value_or(0.0f);
101 contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0);
102
103 // calculate main size.
104 auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value();
105
106 float expandHeight = ScrollableUtils::CheckHeightExpansion(listLayoutProperty, axis_);
107 contentEndOffset_ += expandHeight;
108 // expand contentSize
109 contentConstraint.MinusPadding(std::nullopt, std::nullopt, std::nullopt, -expandHeight);
110 auto&& safeAreaOpts = listLayoutProperty->GetSafeAreaExpandOpts();
111 expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive();
112
113 auto contentIdealSize = CreateIdealSize(
114 contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
115
116 const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
117 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
118 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
119 contentMainSize_ = 0.0f;
120 totalItemCount_ = layoutWrapper->GetTotalChildCount();
121 scrollSnapAlign_ = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
122 if (childrenSize_) {
123 childrenSize_->ResizeChildrenSize(totalItemCount_);
124 }
125 if (!GetMainAxisSize(contentIdealSize, axis_)) {
126 if (totalItemCount_ == 0) {
127 contentMainSize_ = 0.0f;
128 } else {
129 // use parent max size first.
130 auto parentMaxSize = contentConstraint.maxSize;
131 contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_);
132 mainSizeIsDefined_ = false;
133 }
134 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
135 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_));
136 }
137 } else {
138 contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
139 mainSizeIsDefined_ = true;
140 }
141 if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, contentMainSize_) ||
142 IsScrollSnapAlignCenter(layoutWrapper)) {
143 contentStartOffset_ = 0;
144 contentEndOffset_ = 0;
145 }
146
147 if (totalItemCount_ > 0) {
148 OnSurfaceChanged(layoutWrapper);
149
150 stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
151 childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
152 auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
153 auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
154 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
155 ReviseSpace(listLayoutProperty);
156 CheckJumpToIndex();
157 currentOffset_ = currentDelta_;
158 startMainPos_ = currentOffset_;
159 endMainPos_ = currentOffset_ + contentMainSize_;
160 CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
161 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
162 // calculate child layout constraint.
163 UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
164 if (posMap_) {
165 posMap_->UpdatePosMap(layoutWrapper, GetLanes(), spaceWidth_, childrenSize_);
166 }
167 MeasureList(layoutWrapper);
168 } else {
169 itemPosition_.clear();
170 if (posMap_) {
171 posMap_->ClearPosMap();
172 }
173 }
174
175 // In the secondary layout scenario, the previous contentMainSize_ is used as the next prevContentMainSize_.
176 prevContentMainSize_ = contentMainSize_;
177
178 auto crossSize = contentIdealSize.CrossSize(axis_);
179 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
180 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
181 crossMatchChild_ = true;
182 }
183 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && !mainSizeIsDefined_) {
184 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_));
185 }
186 contentIdealSize.SetMainSize(contentMainSize_, axis_);
187 AddPaddingToSize(padding, contentIdealSize);
188
189 auto size = contentIdealSize.ConvertToSizeT();
190 // Cancel frame size expansion, only expand content size here.
191 // Frame expansion will be determined after Layout.
192 size.MinusHeight(expandHeight);
193 layoutWrapper->GetGeometryNode()->SetFrameSize(size);
194
195 // set list cache info.
196 SetCacheCount(layoutWrapper, listLayoutProperty->GetCachedCountWithDefault());
197 isLayouted_ = false;
198 }
199
SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)200 void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount)
201 {
202 layoutWrapper->SetCacheCount(cacheCount);
203 }
204
SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheStart,int32_t cacheEnd,bool show)205 void ListLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper,
206 int32_t cacheStart, int32_t cacheEnd, bool show)
207 {
208 if (itemPosition_.empty()) {
209 layoutWrapper->SetActiveChildRange(-1, -1);
210 return;
211 }
212 layoutWrapper->SetActiveChildRange(
213 itemPosition_.begin()->first, itemPosition_.rbegin()->first, cacheStart, cacheEnd, show);
214 }
215
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const216 bool ListLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const
217 {
218 if (layoutWrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(layoutWrapper)) {
219 return true;
220 }
221 return CheckLayoutConstraintChanged(layoutWrapper);
222 }
223
CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper> & layoutWrapper) const224 bool ListLayoutAlgorithm::CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper>& layoutWrapper) const
225 {
226 auto geometryNode = layoutWrapper->GetGeometryNode();
227 CHECK_NULL_RETURN(geometryNode, true);
228 auto constraint = geometryNode->GetParentLayoutConstraint();
229 CHECK_NULL_RETURN(constraint, true);
230 bool isGroup = layoutWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
231 return isGroup ? constraint.value() != GetGroupLayoutConstraint() : constraint.value() != childLayoutConstraint_;
232 }
233
IsListLanesEqual(const RefPtr<LayoutWrapper> & wrapper) const234 bool ListLayoutAlgorithm::IsListLanesEqual(const RefPtr<LayoutWrapper>& wrapper) const
235 {
236 CHECK_NULL_RETURN(listLayoutProperty_, true);
237 auto groupProps = AceType::DynamicCast<ListItemGroupLayoutProperty>(wrapper->GetLayoutProperty());
238 CHECK_NULL_RETURN(groupProps, true);
239 return groupProps->IsListLanesEqual(listLayoutProperty_->GetLanes(),
240 listLayoutProperty_->GetLaneMinLength(), listLayoutProperty_->GetLaneMaxLength());
241 }
242
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const243 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
244 {
245 if (GetItemPosition().empty()) {
246 return 0.0f;
247 }
248 float maxCrossSize = 0.0f;
249 float crossSize = -laneGutter_;
250 float prevPos = GetItemPosition().begin()->second.startPos;
251 for (const auto& pos : GetItemPosition()) {
252 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
253 if (!wrapper) {
254 continue;
255 }
256 auto getGeometryNode = wrapper->GetGeometryNode();
257 if (!getGeometryNode) {
258 continue;
259 }
260 if (NearEqual(prevPos, pos.second.startPos)) {
261 crossSize = crossSize + getGeometryNode->GetMarginFrameSize().CrossSize(axis) + laneGutter_;
262 } else {
263 crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
264 }
265 prevPos = pos.second.startPos;
266 maxCrossSize = std::max(crossSize, maxCrossSize);
267 }
268 return maxCrossSize;
269 }
270
ClearAllItemPosition(LayoutWrapper * layoutWrapper)271 void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper)
272 {
273 for (auto& pos : itemPosition_) {
274 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
275 if (!wrapper) {
276 continue;
277 }
278 auto node = wrapper->GetHostNode();
279 if (!node) {
280 continue;
281 }
282 auto listItemGroup = node->GetPattern<ListItemGroupPattern>();
283 if (!listItemGroup) {
284 continue;
285 }
286 listItemGroup->ClearItemPosition();
287 listItemGroup->ClearCachedItemPosition();
288 }
289 itemPosition_.clear();
290 }
291
GetStartPositionWithChainOffset() const292 float ListLayoutAlgorithm::GetStartPositionWithChainOffset() const
293 {
294 if (itemPosition_.empty()) {
295 return 0.0f;
296 }
297 int32_t startIndex = itemPosition_.begin()->first;
298 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(startIndex) : 0.0f;
299 if (startIndex == 0) {
300 return itemPosition_.begin()->second.startPos + chainOffset;
301 }
302 return itemPosition_.begin()->second.startPos + chainOffset - spaceWidth_;
303 }
304
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)305 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
306 {
307 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
308 LayoutForward(layoutWrapper, jumpIndex_.value(), startPos);
309 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
310 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
311 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
312 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
313 }
314 }
315 }
316
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)317 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
318 {
319 jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value());
320 LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos);
321 if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
322 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
323 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
324 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
325 }
326 }
327 }
328
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex)329 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex)
330 {
331 int32_t jumpIndex = jumpIndex_.has_value() ? jumpIndex_.value() : targetIndex_.value();
332 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
333 startIndex = GetLanesFloor(layoutWrapper, startIndex);
334 endIndex = GetLanesFloor(layoutWrapper, endIndex);
335 float contentStartOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_;
336 float contentEndOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_;
337 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
338 CHECK_NULL_VOID(wrapper);
339 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
340 if (isGroup && jumpIndexInGroup_) {
341 if (scrollAutoType_ == ScrollAutoType::START) {
342 scrollAlign_ = ScrollAlign::START;
343 HandleJumpStart(layoutWrapper);
344 } else if (scrollAutoType_ == ScrollAutoType::END) {
345 scrollAlign_ = ScrollAlign::END;
346 HandleJumpEnd(layoutWrapper);
347 }
348 } else if (jumpIndex <= startIndex) {
349 float mainLen = childrenSize_ ?
350 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false);
351 if (GreatNotEqual(contentMainSize_ - contentStartOffset - contentEndOffset, mainLen)) {
352 scrollAutoType_ = ScrollAutoType::START;
353 if (jumpIndex_.has_value()) {
354 BeginLayoutForward(contentStartOffset, layoutWrapper);
355 }
356 } else {
357 scrollAutoType_ = ScrollAutoType::END;
358 if (jumpIndex_.has_value()) {
359 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper);
360 }
361 }
362 } else if (jumpIndex >= endIndex) {
363 float mainLen = childrenSize_ ?
364 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false);
365 if (GreatOrEqual(mainLen, contentMainSize_ - contentStartOffset - contentEndOffset)) {
366 scrollAutoType_ = ScrollAutoType::START;
367 if (jumpIndex_.has_value()) {
368 BeginLayoutForward(contentStartOffset, layoutWrapper);
369 }
370 } else {
371 scrollAutoType_ = ScrollAutoType::END;
372 if (jumpIndex_.has_value()) {
373 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper);
374 }
375 }
376 }
377 }
378
HandleJumpCenter(LayoutWrapper * layoutWrapper)379 void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper)
380 {
381 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
382 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
383 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
384 if (isGroup && jumpIndexInGroup_.has_value()) {
385 int32_t indexInGroup = jumpIndexInGroup_.value();
386 auto listLayoutProperty =
387 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
388 SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
389 wrapper->Measure(GetGroupLayoutConstraint());
390 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
391 if (LessNotEqual(GetEndPosition(), endMainPos_)) {
392 LayoutForward(layoutWrapper, index + 1, GetEndPosition());
393 }
394 } else {
395 float mainLen = childrenSize_ ?
396 GetChildHeight(layoutWrapper, index) : MeasureAndGetChildHeight(layoutWrapper, index);
397 float startPos = (contentMainSize_ - mainLen) / 2.0f;
398 if (LessNotEqual(startPos, endMainPos_)) {
399 LayoutForward(layoutWrapper, index, startPos);
400 }
401 }
402 if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
403 LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
404 }
405 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_ - contentEndOffset_)) {
406 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
407 }
408 }
409
HandleJumpStart(LayoutWrapper * layoutWrapper)410 void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper)
411 {
412 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
413 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
414 if (isGroup && jumpIndexInGroup_.has_value()) {
415 int32_t indexInGroup = jumpIndexInGroup_.value();
416 auto listLayoutProperty =
417 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
418 SetListItemGroupParam(wrapper, jumpIndex_.value(), 0.0f, true, listLayoutProperty, false);
419 wrapper->Measure(GetGroupLayoutConstraint());
420 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup);
421 if (LessNotEqual(GetEndPosition(), endMainPos_)) {
422 LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition());
423 }
424 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
425 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
426 }
427 } else {
428 BeginLayoutForward(IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_, layoutWrapper);
429 }
430 }
431
HandleJumpEnd(LayoutWrapper * layoutWrapper)432 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
433 {
434 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
435 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
436 if (isGroup && jumpIndexInGroup_.has_value()) {
437 int32_t indexInGroup = jumpIndexInGroup_.value();
438 auto listLayoutProperty =
439 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
440 SetListItemGroupParam(wrapper, jumpIndex_.value(), contentMainSize_, true, listLayoutProperty, false);
441 wrapper->Measure(GetGroupLayoutConstraint());
442 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup);
443 if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
444 LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition());
445 }
446 if (GetEndIndex() <= totalItemCount_ -1 && LessNotEqual(GetEndPosition(), endMainPos_)) {
447 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
448 }
449 } else {
450 BeginLayoutBackward(contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_),
451 layoutWrapper);
452 }
453 }
454
CheckNoNeedJumpListItem(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex)455 bool ListLayoutAlgorithm::CheckNoNeedJumpListItem(LayoutWrapper* layoutWrapper,
456 float startPos, float endPos, int32_t startIndex, int32_t endIndex, int32_t jumpIndex)
457 {
458 int32_t tempJumpIndex = jumpIndex;
459 int32_t tempStartIndex = startIndex;
460 int32_t tempEndIndex = endIndex;
461 if (GreatNotEqual(GetLanes(), 1)) {
462 tempJumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
463 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
464 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
465 }
466 if (tempJumpIndex > tempStartIndex && tempJumpIndex < tempEndIndex) {
467 return true;
468 }
469 if (tempJumpIndex == tempStartIndex && tempJumpIndex == tempEndIndex) {
470 return true;
471 }
472 if ((tempJumpIndex == tempStartIndex) &&
473 GreatOrEqual(startPos, IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_)) {
474 return true;
475 }
476 if ((tempJumpIndex == tempEndIndex) &&
477 LessOrEqual(endPos, contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_))) {
478 return true;
479 }
480 return false;
481 }
482
CheckNoNeedJumpListItemGroup(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)483 bool ListLayoutAlgorithm::CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper,
484 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
485 {
486 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
487 CHECK_NULL_RETURN(wrapper, true);
488 if (wrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
489 return true;
490 }
491 int32_t jumpIndexInGroup = 0;
492 if (jumpIndexInGroup_.has_value()) {
493 jumpIndexInGroup = jumpIndexInGroup_.value();
494 } else {
495 return false;
496 }
497
498 auto layoutAlgorithm = wrapper->GetLayoutAlgorithm();
499 CHECK_NULL_RETURN(layoutAlgorithm, true);
500 auto groupLayoutAlgorithm =
501 AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithm->GetLayoutAlgorithm());
502 CHECK_NULL_RETURN(groupLayoutAlgorithm, true);
503 auto groupItemPosition = groupLayoutAlgorithm->GetItemPosition();
504 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
505 CHECK_NULL_RETURN(listLayoutProperty, false);
506
507 if (jumpIndex >= startIndex && jumpIndex <= endIndex) {
508 auto it = groupItemPosition.find(jumpIndexInGroup);
509 if (it != groupItemPosition.end()) {
510 auto topPos = jumpIndexStartPos + it->second.startPos -
511 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_);
512 auto bottomPos = jumpIndexStartPos + it->second.endPos +
513 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_);
514 if (JudgeInOfScreenScrollAutoType(wrapper, listLayoutProperty, topPos, bottomPos)) {
515 return true;
516 }
517 } else if (groupItemPosition.size() > 0) {
518 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndexInGroup,
519 groupItemPosition.begin()->first, groupItemPosition.rbegin()->first);
520 } else {
521 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
522 return true;
523 }
524 } else {
525 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndex,
526 startIndex, endIndex);
527 }
528 return false;
529 }
530
JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<ListLayoutProperty> & layoutProperty,float topPos,float bottomPos)531 bool ListLayoutAlgorithm::JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper,
532 const RefPtr<ListLayoutProperty>& layoutProperty, float topPos, float bottomPos)
533 {
534 auto stickyStyle = layoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
535
536 auto groupNode = layoutWrapper->GetHostNode();
537 CHECK_NULL_RETURN(groupNode, true);
538 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
539 CHECK_NULL_RETURN(groupPattern, true);
540
541 float headerMainSize = 0.0f;
542 float footerMainSize = 0.0f;
543 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::HEADER) {
544 headerMainSize = groupPattern->GetHeaderMainSize();
545 }
546 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::FOOTER) {
547 footerMainSize = groupPattern->GetFooterMainSize();
548 }
549
550 if (GreatOrEqual(topPos, startMainPos_ + headerMainSize) &&
551 LessOrEqual(bottomPos, endMainPos_ - footerMainSize)) {
552 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
553 return true;
554 } else if (NearEqual(topPos, startMainPos_ + headerMainSize) ||
555 NearEqual(bottomPos, endMainPos_ - footerMainSize)) {
556 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
557 return true;
558 } else if (GreatOrEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
559 scrollAutoType_ = ScrollAutoType::END;
560 } else if (LessNotEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
561 scrollAutoType_ = ScrollAutoType::START;
562 }
563
564 return false;
565 }
566
JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,const RefPtr<ListLayoutProperty> & layoutProperty,int32_t indexInGroup,int32_t judgeIndex,int32_t startIndex,int32_t endIndex)567 void ListLayoutAlgorithm::JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
568 const RefPtr<ListLayoutProperty>& layoutProperty, int32_t indexInGroup, int32_t judgeIndex,
569 int32_t startIndex, int32_t endIndex)
570 {
571 SetListItemGroupParam(layoutWrapper, index, 0.0f, true, layoutProperty, false);
572 layoutWrapper->Measure(childLayoutConstraint_);
573 auto jumpItemHeight = GetListGroupItemHeight(layoutWrapper, indexInGroup);
574 jumpIndexInGroup_ = indexInGroup;
575
576 if (judgeIndex < startIndex) {
577 if (jumpItemHeight > contentMainSize_) {
578 scrollAutoType_ = ScrollAutoType::END;
579 } else {
580 scrollAutoType_ = ScrollAutoType::START;
581 }
582 } else if (judgeIndex > endIndex) {
583 if (jumpItemHeight > contentMainSize_) {
584 scrollAutoType_ = ScrollAutoType::START;
585 } else {
586 scrollAutoType_ = ScrollAutoType::END;
587 }
588 }
589 }
590
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)591 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
592 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
593 {
594 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
595 CHECK_NULL_RETURN(wrapper, true);
596 if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG && jumpIndexInGroup_.has_value()) {
597 if (CheckNoNeedJumpListItemGroup(layoutWrapper, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
598 return true;
599 }
600 } else {
601 if (CheckNoNeedJumpListItem(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex)) {
602 return true;
603 }
604 }
605 return false;
606 }
607
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex,bool groupLayoutAll)608 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex,
609 bool groupLayoutAll)
610 {
611 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
612 CHECK_NULL_RETURN(wrapper, 0.0f);
613 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
614 if (isGroup) {
615 auto listLayoutProperty =
616 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
617 // true: layout forward, 0.0f: layout start position.
618 SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, groupLayoutAll);
619 }
620 wrapper->Measure(childLayoutConstraint_);
621 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
622 return mainLen;
623 }
624
CheckJumpToIndex()625 void ListLayoutAlgorithm::CheckJumpToIndex()
626 {
627 if (jumpIndex_.has_value() || !isNeedCheckOffset_ || childrenSize_) {
628 return;
629 }
630 if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
631 return;
632 }
633 for (const auto& pos : itemPosition_) {
634 if (pos.second.isGroup) {
635 return;
636 }
637 }
638 float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
639 float averageHeight = totalHeight / itemPosition_.size();
640 int32_t targetIndex = itemPosition_.begin()->first;
641 currentDelta_ -= itemPosition_.begin()->second.startPos;
642 if (NonNegative(currentDelta_)) {
643 int32_t items = currentDelta_ / averageHeight;
644 targetIndex += items;
645 currentDelta_ -= items * averageHeight;
646 } else {
647 int32_t items = -currentDelta_ / averageHeight;
648 targetIndex -= items;
649 currentDelta_ += items * averageHeight;
650 if (targetIndex <= 0) {
651 currentDelta_ = 0;
652 }
653 }
654 jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
655 }
656
UpdateSnapCenterContentOffset(LayoutWrapper * layoutWrapper)657 void ListLayoutAlgorithm::UpdateSnapCenterContentOffset(LayoutWrapper* layoutWrapper)
658 {
659 if (IsScrollSnapAlignCenter(layoutWrapper) && !itemPosition_.empty()) {
660 float itemHeight = 0.0f;
661 if (GetStartIndex() == 0) {
662 itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
663 contentStartOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f);
664 }
665 if (GetEndIndex() == totalItemCount_ - 1) {
666 itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
667 contentEndOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f);
668 }
669 }
670 }
671
CheckJumpValid(LayoutWrapper * layoutWrapper)672 bool ListLayoutAlgorithm::CheckJumpValid(LayoutWrapper* layoutWrapper)
673 {
674 if (jumpIndex_.value() == LAST_ITEM) {
675 jumpIndex_ = totalItemCount_ - 1;
676 } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
677 return false;
678 }
679 if (jumpIndex_ && jumpIndexInGroup_) {
680 auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
681 CHECK_NULL_RETURN(groupWrapper, false);
682 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
683 return false;
684 }
685 auto groupNode = groupWrapper->GetHostNode();
686 CHECK_NULL_RETURN(groupNode, false);
687 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
688 CHECK_NULL_RETURN(groupPattern, false);
689
690 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
691
692 if (jumpIndexInGroup_.value() == LAST_ITEM) {
693 jumpIndexInGroup_ = groupItemCount - 1;
694 } else if ((jumpIndexInGroup_.value() < 0) || (jumpIndexInGroup_.value() >= groupItemCount)) {
695 return false;
696 }
697 }
698 return true;
699 }
700
CheckAndMeasureStartItem(LayoutWrapper * layoutWrapper,int32_t startIndex,float & startPos,bool isGroup,bool forwardLayout)701 void ListLayoutAlgorithm::CheckAndMeasureStartItem(LayoutWrapper* layoutWrapper, int32_t startIndex,
702 float& startPos, bool isGroup, bool forwardLayout)
703 {
704 if (!isGroup || IsScrollSnapAlignCenter(layoutWrapper) ||
705 (forwardLayout && NonNegative(startPos)) || (!forwardLayout && LessOrEqual(startPos, prevContentMainSize_))) {
706 return;
707 }
708 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(startIndex);
709 CHECK_NULL_VOID(wrapper);
710 int32_t id = wrapper->GetHostNode()->GetId();
711 isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
712 if (!isGroup) {
713 return;
714 }
715 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
716 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d", startIndex);
717 SetListItemGroupParam(wrapper, startIndex, startPos, forwardLayout, listLayoutProperty, false, true);
718 wrapper->Measure(GetGroupLayoutConstraint());
719 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
720 CHECK_NULL_VOID(algorithmWrapper);
721 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
722 CHECK_NULL_VOID(itemGroup);
723 startPos = itemGroup->GetRefPos();
724 ListItemInfo itemInfo;
725 if (forwardLayout) {
726 itemInfo = { id, startPos, startPos + childrenSize_->GetChildSize(startIndex), isGroup };
727 } else {
728 itemInfo = { id, startPos - childrenSize_->GetChildSize(startIndex), startPos, isGroup };
729 }
730 firstItemInfo_ = std::make_pair(startIndex, itemInfo);
731 }
732
MeasureList(LayoutWrapper * layoutWrapper)733 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
734 {
735 bool startItemIsGroup = false;
736 bool endItemIsGroup = false;
737 int32_t startIndex = 0;
738 int32_t endIndex = 0;
739 int32_t midIndex = 0;
740 float midItemMidPos = contentMainSize_ / 2.0f;
741 float startPos = contentStartOffset_;
742 float endPos = 0.0f;
743 float itemTotalSize = 0.0f;
744 float jumpIndexStartPos = 0.0f;
745 bool needLayoutBackward = false;
746 auto host = layoutWrapper->GetHostNode();
747 CHECK_NULL_VOID(host);
748 auto pattern = host->GetPattern<ListPattern>();
749 CHECK_NULL_VOID(pattern);
750 if (!isLayouted_) {
751 itemPosition_ = pattern->GetItemPosition();
752 }
753 preStartIndex_ = pattern->GetStartIndex();
754 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) {
755 auto it = itemPosition_.find(jumpIndex_.value());
756 if (it != itemPosition_.end()) {
757 jumpIndexStartPos = it->second.startPos;
758 }
759 }
760
761 if (jumpIndex_) {
762 if (!CheckJumpValid(layoutWrapper)) {
763 jumpIndex_.reset();
764 jumpIndexInGroup_.reset();
765 } else {
766 if (jumpIndex_ && scrollAlign_ != ScrollAlign::AUTO) {
767 ClearAllItemPosition(layoutWrapper);
768 }
769 }
770 }
771 if (targetIndex_) {
772 if (targetIndex_.value() == LAST_ITEM) {
773 targetIndex_ = totalItemCount_ - 1;
774 } else if ((targetIndex_.value() < 0) || (targetIndex_.value() >= totalItemCount_)) {
775 targetIndex_.reset();
776 }
777 targetIndexStaged_ = targetIndex_;
778 }
779 if (!itemPosition_.empty()) {
780 startItemIsGroup = itemPosition_.begin()->second.isGroup;
781 endItemIsGroup = itemPosition_.rbegin()->second.isGroup;
782 startPos = itemPosition_.begin()->second.startPos;
783 endPos = itemPosition_.rbegin()->second.endPos;
784 itemTotalSize = GetEndPosition() - GetStartPosition();
785 startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
786 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
787 if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
788 jumpIndex_ = totalItemCount_ - 1;
789 scrollAlign_ = ScrollAlign::END;
790 }
791 UpdateSnapCenterContentOffset(layoutWrapper);
792 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
793 CHECK_NULL_VOID(listLayoutProperty);
794 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
795 if (IsScrollSnapAlignCenter(layoutWrapper)) {
796 midIndex = GetMidIndex(layoutWrapper, true);
797 midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
798 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
799 midIndex = std::min(midIndex, totalItemCount_ - 1);
800 } else if (scrollSnapAlign == V2::ScrollSnapAlign::START && pattern->GetScrollState() == ScrollState::IDLE) {
801 auto res = GetSnapStartIndexAndPos();
802 startIndex = res.first;
803 startPos = res.second;
804 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END && pattern->GetScrollState() == ScrollState::IDLE) {
805 auto res = GetSnapEndIndexAndPos();
806 needLayoutBackward = res.first != -1;
807 endIndex = needLayoutBackward ? res.first : endIndex;
808 endPos = needLayoutBackward ? res.second : endPos;
809 }
810 OffScreenLayoutDirection(layoutWrapper);
811 itemPosition_.clear();
812 }
813 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO &&
814 NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex_.value(), jumpIndexStartPos)) {
815 jumpIndex_.reset();
816 jumpIndexInGroup_.reset();
817 }
818 if (jumpIndex_) {
819 switch (scrollAlign_) {
820 case ScrollAlign::START:
821 case ScrollAlign::NONE:
822 HandleJumpStart(layoutWrapper);
823 break;
824 case ScrollAlign::CENTER:
825 HandleJumpCenter(layoutWrapper);
826 break;
827 case ScrollAlign::END:
828 HandleJumpEnd(layoutWrapper);
829 break;
830 case ScrollAlign::AUTO:
831 HandleJumpAuto(layoutWrapper, startIndex, endIndex);
832 break;
833 }
834 needEstimateOffset_ = true;
835 } else if (targetIndex_.has_value()) {
836 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
837 if (layoutDirection == LayoutDirection::BACKWARD) {
838 LayoutBackward(layoutWrapper, endIndex, endPos);
839 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
840 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
841 }
842 } else {
843 LayoutForward(layoutWrapper, startIndex, startPos);
844 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
845 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
846 }
847 }
848 } else {
849 jumpIndexInGroup_.reset();
850 bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos + GetChainOffset(0), contentStartOffset_);
851 float midItemHeight = 0.0f;
852 if (IsScrollSnapAlignCenter(layoutWrapper)) {
853 midItemHeight = childrenSize_ ?
854 GetChildHeight(layoutWrapper, midIndex) : MeasureAndGetChildHeight(layoutWrapper, midIndex);
855 startIndex = midIndex;
856 endIndex = midIndex;
857 }
858 if ((NonNegative(currentOffset_) || overScrollFeature_ || (canOverScroll_ &&
859 LessOrEqual(itemTotalSize, contentMainSize_ - contentStartOffset_ - contentEndOffset_))) &&
860 !needLayoutBackward) {
861 startIndex = GetLanesFloor(layoutWrapper, startIndex);
862 if (overScrollTop && !canOverScroll_ && !overScrollFeature_) {
863 startPos = startMainPos_ + contentStartOffset_;
864 }
865 if (IsScrollSnapAlignCenter(layoutWrapper)) {
866 startPos = midItemMidPos - midItemHeight / 2.0f;
867 }
868 if (overScrollFeature_ && !overScrollTop && GreatNotEqual(contentMainSize_, prevContentMainSize_) &&
869 GreatNotEqual(itemTotalSize, contentMainSize_)) {
870 startPos += contentMainSize_ - prevContentMainSize_;
871 }
872 if (childrenSize_) {
873 CheckAndMeasureStartItem(layoutWrapper, startIndex, startPos, startItemIsGroup, true);
874 posMap_->OptimizeBeforeMeasure(startIndex, startPos, currentOffset_, contentMainSize_);
875 }
876 LayoutForward(layoutWrapper, startIndex, startPos);
877 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPositionWithChainOffset(), startMainPos_)) {
878 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
879 }
880 } else {
881 endIndex = GetLanesCeil(layoutWrapper, endIndex);
882 if (needLayoutBackward) {
883 endPos += contentMainSize_ - prevContentMainSize_;
884 }
885 if (IsScrollSnapAlignCenter(layoutWrapper)) {
886 endPos = midItemMidPos + midItemHeight / 2.0f;
887 }
888 if (childrenSize_) {
889 CheckAndMeasureStartItem(layoutWrapper, endIndex, endPos, endItemIsGroup, false);
890 posMap_->OptimizeBeforeMeasure(endIndex, endPos, currentOffset_, contentMainSize_);
891 }
892 LayoutBackward(layoutWrapper, endIndex, endPos);
893 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
894 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
895 }
896 }
897 }
898 RecycleGroupItem(layoutWrapper);
899 }
900
LayoutDirectionForTargetIndex(LayoutWrapper * layoutWrapper,int startIndex)901 LayoutDirection ListLayoutAlgorithm::LayoutDirectionForTargetIndex(LayoutWrapper* layoutWrapper, int startIndex)
902 {
903 CHECK_NULL_RETURN(targetIndex_, LayoutDirection::NONE);
904 if (startIndex < targetIndex_.value()) {
905 return LayoutDirection::FORWARD;
906 } else if (startIndex > targetIndex_.value()) {
907 return LayoutDirection::BACKWARD;
908 } else if (targetIndexInGroup_.has_value()) {
909 auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(targetIndex_.value());
910 CHECK_NULL_RETURN(groupWrapper, LayoutDirection::NONE);
911 auto groupHost = groupWrapper->GetHostNode();
912 CHECK_NULL_RETURN(groupHost, LayoutDirection::NONE);
913 auto groupPattern = groupHost->GetPattern<ListItemGroupPattern>();
914 CHECK_NULL_RETURN(groupPattern, LayoutDirection::NONE);
915 auto startIndexInGroup = groupPattern->GetDisplayStartIndexInGroup();
916 auto endIndexInGroup = groupPattern->GetDisplayEndIndexInGroup();
917 auto isTargetGroupEmpty = groupPattern->GetItemPosition().empty();
918 auto targetGroupPosition = itemPosition_[targetIndex_.value()].startPos;
919 if (targetIndexInGroup_.value() < startIndexInGroup || (isTargetGroupEmpty && Negative(targetGroupPosition))) {
920 return LayoutDirection::BACKWARD;
921 } else if (targetIndexInGroup_.value() > endIndexInGroup ||
922 (isTargetGroupEmpty && !Negative(targetGroupPosition))) {
923 return LayoutDirection::FORWARD;
924 }
925 }
926 return LayoutDirection::NONE;
927 }
928
RecycleGroupItem(LayoutWrapper * layoutWrapper) const929 void ListLayoutAlgorithm::RecycleGroupItem(LayoutWrapper* layoutWrapper) const
930 {
931 if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER || childrenSize_) {
932 return;
933 }
934 auto startChild = itemPosition_.begin();
935 auto endChild = itemPosition_.rbegin();
936 if (startChild != itemPosition_.end() && startChild->second.isGroup) {
937 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(startChild->first) : 0.0f;
938 CheckListItemGroupRecycle(layoutWrapper, startChild->first, startChild->second.startPos + chainOffset, true);
939 }
940 if (endChild != itemPosition_.rend() && endChild->second.isGroup) {
941 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(endChild->first) : 0.0f;
942 CheckListItemGroupRecycle(layoutWrapper, endChild->first, endChild->second.endPos + chainOffset, false);
943 }
944 }
945
AdjustStartPosition(const RefPtr<LayoutWrapper> & layoutWrapper,float & startPos)946 void ListLayoutAlgorithm::AdjustStartPosition(const RefPtr<LayoutWrapper>& layoutWrapper, float& startPos)
947 {
948 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
949 CHECK_NULL_VOID(layoutAlgorithmWrapper);
950 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
951 CHECK_NULL_VOID(itemGroup);
952 startPos += itemGroup->GetAdjustReferenceDelta();
953 }
954
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)955 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
956 int32_t& currentIndex, float startPos, float& endPos)
957 {
958 if (currentIndex + 1 >= totalItemCount_) {
959 return 0;
960 }
961 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex + 1) {
962 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
963 CHECK_NULL_RETURN(wrapper, 0);
964 int32_t id = wrapper->GetHostNode()->GetId();
965 ++currentIndex;
966 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
967 if (isGroup) {
968 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
969 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, startPos);
970 SetListItemGroupParam(wrapper, currentIndex, startPos, true, listLayoutProperty, false);
971 wrapper->Measure(childLayoutConstraint_);
972 if (LessOrEqual(startPos, 0.0f)) {
973 AdjustStartPosition(wrapper, startPos);
974 }
975 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
976 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, startPos);
977 wrapper->Measure(childLayoutConstraint_);
978 }
979 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
980 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
981 endPos = startPos + mainLen;
982 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
983 } else {
984 ++currentIndex;
985 itemPosition_[currentIndex] = firstItemInfo_.value().second;
986 endPos = itemPosition_[currentIndex].endPos;
987 }
988 if (firstItemInfo_) {
989 firstItemInfo_.reset();
990 }
991 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
992 return 1;
993 }
994
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)995 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
996 int32_t& currentIndex, float endPos, float& startPos)
997 {
998 if (currentIndex - 1 < 0) {
999 return 0;
1000 }
1001 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex - 1) {
1002 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
1003 CHECK_NULL_RETURN(wrapper, 0);
1004 int32_t id = wrapper->GetHostNode()->GetId();
1005 --currentIndex;
1006 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1007 if (isGroup) {
1008 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1009 SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
1010 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, endPos);
1011 wrapper->Measure(childLayoutConstraint_);
1012 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
1013 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, endPos);
1014 wrapper->Measure(childLayoutConstraint_);
1015 }
1016 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
1017 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1018 startPos = endPos - mainLen;
1019 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
1020 } else {
1021 --currentIndex;
1022 itemPosition_[currentIndex] = firstItemInfo_.value().second;
1023 startPos = itemPosition_[currentIndex].startPos;
1024 }
1025 if (firstItemInfo_) {
1026 firstItemInfo_.reset();
1027 }
1028 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
1029 return 1;
1030 }
1031
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)1032 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
1033 {
1034 float currentEndPos = startPos;
1035 float currentStartPos = 0.0f;
1036 float endMainPos = (overScrollFeature_ && startIndex == 0) ?
1037 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
1038 layoutEndMainPos_ = endMainPos;
1039 if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1040 endMainPos = Infinity<float>();
1041 }
1042
1043 auto currentIndex = startIndex - 1;
1044 auto chainOffset = 0.0f;
1045 do {
1046 currentStartPos = currentEndPos;
1047 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
1048 if (count == 0) {
1049 break;
1050 }
1051 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
1052 currentEndPos += spaceWidth_;
1053 }
1054 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1055 // reach the valid target index
1056 if (forwardFeature_ && targetIndex_ && currentIndex >= targetIndex_.value()) {
1057 endMainPos = layoutEndMainPos_.value_or(endMainPos_);
1058 forwardFeature_ = false;
1059 }
1060 } while (LessOrEqual(currentEndPos + chainOffset, endMainPos));
1061 currentEndPos += chainOffset;
1062
1063 while (itemPosition_.size() > 1 && !targetIndex_) {
1064 auto pos = itemPosition_.rbegin();
1065 float chainDelta = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1066 if ((GreatNotEqual(pos->second.endPos + chainDelta, endMainPos) &&
1067 GreatOrEqual(pos->second.startPos + chainDelta, endMainPos))) {
1068 recycledItemPosition_.emplace(pos->first, pos->second);
1069 itemPosition_.erase(pos->first);
1070 } else {
1071 break;
1072 }
1073 }
1074 // adjust offset.
1075 UpdateSnapCenterContentOffset(layoutWrapper);
1076 if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
1077 endMainPos_ = currentEndPos + contentEndOffset_;
1078 startMainPos_ = endMainPos_ - contentMainSize_;
1079 ReMeasureListItemGroup(layoutWrapper, true);
1080 auto firstItemTop = itemPosition_.begin()->second.startPos;
1081 auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
1082 if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
1083 // all items size is less than list.
1084 if (!canOverScroll_) {
1085 currentOffset_ = firstItemTop - contentStartOffset_;
1086 startMainPos_ = currentOffset_;
1087 endMainPos_ = startMainPos_ + contentMainSize_;
1088 }
1089 if (!mainSizeIsDefined_) {
1090 // adapt child size.
1091 contentMainSize_ = itemTotalSize;
1092 }
1093 } else {
1094 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1095 if (!canOverScroll_ || jumpIndex_.has_value()) {
1096 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
1097 }
1098 }
1099 }
1100 if (overScrollFeature_ && canOverScroll_) {
1101 return;
1102 }
1103 // Mark inactive in wrapper.
1104 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1105 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1106 // Don't recycle When the head item is Visibility.None.
1107 if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) ||
1108 GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) {
1109 if (pos->second.isGroup) {
1110 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
1111 }
1112 break;
1113 }
1114 recycledItemPosition_.emplace(pos->first, pos->second);
1115 pos = itemPosition_.erase(pos);
1116 }
1117 }
1118
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)1119 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
1120 {
1121 float currentStartPos = endPos;
1122 float currentEndPos = 0.0f;
1123 float startMainPos = (overScrollFeature_ && endIndex == totalItemCount_ - 1) ?
1124 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
1125 layoutStartMainPos_ = startMainPos;
1126 if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1127 startMainPos = -Infinity<float>();
1128 }
1129 auto currentIndex = endIndex + 1;
1130 auto chainOffset = 0.0f;
1131 do {
1132 currentEndPos = currentStartPos;
1133 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
1134 if (count == 0) {
1135 break;
1136 }
1137 if (currentIndex > 0) {
1138 currentStartPos = currentStartPos - spaceWidth_;
1139 }
1140 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1141 // reach the valid target index
1142 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
1143 startMainPos = layoutStartMainPos_.value_or(startMainPos_);
1144 backwardFeature_ = false;
1145 }
1146 } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
1147
1148 currentStartPos += chainOffset;
1149 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1150 UpdateSnapCenterContentOffset(layoutWrapper);
1151 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_) && !itemPosition_.empty()) {
1152 auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
1153 bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
1154 if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
1155 if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
1156 currentOffset_ += contentMainSize_ - prevContentMainSize_;
1157 }
1158 contentMainSize_ = itemTotalSize;
1159 }
1160 if (!canOverScroll_ || jumpIndex_.has_value()) {
1161 currentOffset_ = currentStartPos - contentStartOffset_;
1162 }
1163 endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
1164 startMainPos_ = currentStartPos - contentStartOffset_;
1165 ReMeasureListItemGroup(layoutWrapper, false);
1166 }
1167
1168 if (overScrollFeature_) {
1169 return;
1170 }
1171
1172 // Mark inactive in wrapper.
1173 std::list<int32_t> removeIndexes;
1174 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1175 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1176 // Don't recycle When the tail item is Visibility.None.
1177 if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) ||
1178 LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) {
1179 if (pos->second.isGroup) {
1180 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
1181 }
1182 break;
1183 }
1184 recycledItemPosition_.emplace(pos->first, pos->second);
1185 removeIndexes.emplace_back(pos->first);
1186 }
1187 for (const auto& index : removeIndexes) {
1188 itemPosition_.erase(index);
1189 }
1190 }
1191
ReMeasureListItemGroup(LayoutWrapper * layoutWrapper,bool forwardLayout)1192 void ListLayoutAlgorithm::ReMeasureListItemGroup(LayoutWrapper* layoutWrapper, bool forwardLayout)
1193 {
1194 if (forwardFeature_ || backwardFeature_) {
1195 return;
1196 }
1197 if (forwardLayout) {
1198 if (itemPosition_.begin()->second.isGroup) {
1199 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), forwardLayout);
1200 }
1201 return;
1202 }
1203 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end(); pos++) {
1204 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1205 if (GreatOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
1206 break;
1207 } else if (!pos->second.isGroup) {
1208 continue;
1209 }
1210 AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout);
1211 }
1212 }
1213
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)1214 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
1215 {
1216 if (!predictSnapOffset_.has_value() || itemPosition_.empty()) {
1217 return;
1218 }
1219 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1220 if ((scrollSnapAlign != V2::ScrollSnapAlign::START) && (scrollSnapAlign != V2::ScrollSnapAlign::CENTER) &&
1221 (scrollSnapAlign != V2::ScrollSnapAlign::END)) {
1222 predictSnapOffset_.reset();
1223 predictSnapEndPos_.reset();
1224 return;
1225 }
1226
1227 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1228 int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign);
1229 if (GetStartIndex() <= endIndex && endIndex <= GetEndIndex()) {
1230 predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign);
1231 predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_;
1232 predictSnapEndPos_.reset();
1233 } else {
1234 if (IsUniformHeightProbably()) {
1235 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1236 FixPredictSnapOffsetAlignStart();
1237 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1238 FixPredictSnapOffsetAlignCenter();
1239 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1240 FixPredictSnapOffsetAlignEnd();
1241 }
1242 } else {
1243 predictSnapEndPos_ = predictEndPos;
1244 }
1245 }
1246
1247 return;
1248 }
1249
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1250 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1251 {
1252 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1253 CHECK_NULL_RETURN(listLayoutProperty, false);
1254 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1255 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1256 return true;
1257 }
1258
1259 return false;
1260 }
1261
FixPredictSnapOffsetAlignStart()1262 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1263 {
1264 if (itemPosition_.empty()) {
1265 return;
1266 }
1267 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1268 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1269 float startPos = contentStartOffset_;
1270 float endPos = contentMainSize_ - contentEndOffset_;
1271 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1272
1273 if (LessNotEqual(predictEndPos, -startPos)) {
1274 if (isSpringEffect_) {
1275 return;
1276 }
1277 predictEndPos = -startPos;
1278 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1279 if (isSpringEffect_) {
1280 return;
1281 }
1282 predictEndPos = maxPos;
1283 } else {
1284 int32_t index;
1285 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1286 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1287 break;
1288 }
1289 }
1290 predictEndPos = index * itemHeight - startPos;
1291 if (LessNotEqual(predictEndPos, -startPos)) {
1292 predictEndPos = -startPos;
1293 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1294 predictEndPos = maxPos;
1295 }
1296 }
1297
1298 predictSnapOffset_ = totalOffset_ - predictEndPos;
1299 predictSnapEndPos_ = predictEndPos;
1300 }
1301
FixPredictSnapOffsetAlignCenter()1302 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1303 {
1304 if (itemPosition_.empty()) {
1305 return;
1306 }
1307 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1308 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1309 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1310 if (isSpringEffect_) {
1311 return;
1312 }
1313 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1314 } else if (GreatNotEqual(
1315 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1316 if (isSpringEffect_) {
1317 return;
1318 }
1319 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1320 } else {
1321 int32_t index;
1322 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1323 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1324 itemHeight / 2.0f) {
1325 break;
1326 }
1327 }
1328 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1329 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1330 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1331 } else if (GreatNotEqual(
1332 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1333 predictEndPos =
1334 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1335 }
1336 }
1337
1338 predictSnapOffset_ = totalOffset_ - predictEndPos;
1339 predictSnapEndPos_ = predictEndPos;
1340 }
1341
FixPredictSnapOffsetAlignEnd()1342 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1343 {
1344 if (itemPosition_.empty()) {
1345 return;
1346 }
1347 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1348 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1349 float startPos = contentStartOffset_;
1350 float endPos = contentMainSize_ - contentEndOffset_;
1351 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1352
1353 if (LessNotEqual(predictEndPos, -startPos)) {
1354 if (isSpringEffect_) {
1355 return;
1356 }
1357 predictEndPos = -startPos;
1358 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1359 if (isSpringEffect_) {
1360 return;
1361 }
1362 predictEndPos = maxPos;
1363 } else {
1364 int32_t index;
1365 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1366 if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1367 break;
1368 }
1369 }
1370 predictEndPos = index * itemHeight - endPos - spaceWidth_;
1371 if (LessNotEqual(predictEndPos, -startPos)) {
1372 predictEndPos = -startPos;
1373 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1374 predictEndPos = maxPos;
1375 }
1376 }
1377
1378 predictSnapOffset_ = totalOffset_ - predictEndPos;
1379 predictSnapEndPos_ = predictEndPos;
1380 }
1381
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1382 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1383 int32_t& startIndex, float crossSize)
1384 {
1385 CHECK_NULL_VOID(wrapper);
1386 auto offset = paddingOffset_;
1387 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1388 float crossOffset = 0.0f;
1389 if (GetLanes() > 1) {
1390 int32_t laneIndex = 0;
1391 if (pos.isGroup) {
1392 startIndex = index + 1;
1393 } else {
1394 laneIndex = (index - startIndex) % GetLanes();
1395 }
1396
1397 float laneGutter = GetLaneGutter();
1398 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
1399 crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1400 } else {
1401 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1402 }
1403 auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
1404 if (isReverse_) {
1405 if (axis_ == Axis::VERTICAL) {
1406 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1407 offset = offset + OffsetF(crossSize - crossOffset - size.Width(), pos.startPos + chainOffset);
1408 } else {
1409 offset = offset + OffsetF(contentMainSize_ - pos.endPos - chainOffset, crossOffset);
1410 }
1411 } else {
1412 if (axis_ == Axis::VERTICAL) {
1413 offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
1414 } else {
1415 offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
1416 }
1417 }
1418 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1419 SetListItemIndex(wrapper, index);
1420 }
1421
ProcessCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)1422 void ListLayoutAlgorithm::ProcessCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
1423 {
1424 if (!itemPosition_.empty() && cacheCount > 0) {
1425 auto items = LayoutCachedItemV2(layoutWrapper, cacheCount, show);
1426 auto host = layoutWrapper->GetHostNode();
1427 CHECK_NULL_VOID(host);
1428 if (!items.empty()) {
1429 ListMainSizeValues value = { startMainPos_, endMainPos_, jumpIndexInGroup_, prevContentMainSize_,
1430 scrollAlign_, layoutStartMainPos_, layoutEndMainPos_ };
1431 PostIdleTaskV2(host, { items, childLayoutConstraint_, GetGroupLayoutConstraint() }, value, show);
1432 } else {
1433 auto pattern = host->GetPattern<ListPattern>();
1434 CHECK_NULL_VOID(pattern);
1435 pattern->SetPredictLayoutParamV2(std::nullopt);
1436 }
1437 } else {
1438 ResetLayoutItem(layoutWrapper);
1439 SetActiveChildRange(layoutWrapper, cacheCount, cacheCount, show);
1440 }
1441 }
1442
GetListItemGroupLayoutInfo(const RefPtr<LayoutWrapper> & wrapper) const1443 std::optional<ListItemGroupLayoutInfo> ListLayoutAlgorithm::GetListItemGroupLayoutInfo(
1444 const RefPtr<LayoutWrapper>& wrapper) const
1445 {
1446 CHECK_NULL_RETURN(wrapper, std::nullopt);
1447 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1448 CHECK_NULL_RETURN(layoutAlgorithmWrapper, std::nullopt);
1449 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1450 CHECK_NULL_RETURN(itemGroup, std::nullopt);
1451 return itemGroup->GetLayoutInfo();
1452 }
1453
GetListItemGroupItemCount(const RefPtr<LayoutWrapper> & wrapper) const1454 int32_t ListLayoutAlgorithm::GetListItemGroupItemCount(const RefPtr<LayoutWrapper>& wrapper) const
1455 {
1456 CHECK_NULL_RETURN(wrapper, 0);
1457 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1458 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
1459 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1460 CHECK_NULL_RETURN(itemGroup, 0);
1461 int32_t itemCount = itemGroup->GetListItemCount();
1462 return itemCount == 0 ? 1 : itemCount;
1463 }
1464
ResetLayoutItem(LayoutWrapper * layoutWrapper)1465 void ListLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1466 {
1467 for (auto& pos : recycledItemPosition_) {
1468 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1469 pos.second.startPos -= currentOffset_;
1470 pos.second.endPos -= currentOffset_;
1471 if (pos.second.isGroup) {
1472 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1473 if (wrapper && wrapper->GetHostNode() && wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1474 auto groupPattern = wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1475 groupPattern->ClearItemPosition();
1476 }
1477 } else {
1478 pos.second.groupInfo.reset();
1479 }
1480 auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper);
1481 if (wrapperFrameNode) {
1482 wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1483 }
1484 }
1485 }
1486
Layout(LayoutWrapper * layoutWrapper)1487 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1488 {
1489 auto listProps = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1490 CHECK_NULL_VOID(listProps);
1491 auto axis_ = listProps->GetListDirection().value_or(Axis::VERTICAL);
1492 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1493 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1494 MinusPaddingToSize(padding, size);
1495 paddingOffset_ = padding.Offset();
1496 float crossSize = GetCrossAxisSize(size, axis_);
1497 totalItemCount_ = layoutWrapper->GetTotalChildCount();
1498 listItemAlign_ = listProps->GetListItemAlign().value_or(V2::ListItemAlign::START);
1499 int32_t startIndex = GetStartIndex();
1500 isReverse_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
1501
1502 totalOffset_ += currentOffset_;
1503 FixPredictSnapOffset(listProps);
1504 // layout items.
1505 int32_t itemCount = 0;
1506 for (auto& pos : itemPosition_) {
1507 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1508 if (!wrapper) {
1509 LOGI("wrapper is out of boundary");
1510 continue;
1511 }
1512 pos.second.startPos -= currentOffset_;
1513 pos.second.endPos -= currentOffset_;
1514 if (pos.second.isGroup) {
1515 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1516 itemCount += GetListItemGroupItemCount(wrapper);
1517 } else {
1518 pos.second.groupInfo.reset();
1519 itemCount++;
1520 }
1521 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1522 if (expandSafeArea_ || wrapper->CheckNeedForceMeasureAndLayout()) {
1523 wrapper->Layout();
1524 } else {
1525 SyncGeometry(wrapper);
1526 }
1527 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1528 if (frameNode) {
1529 frameNode->MarkAndCheckNewOpIncNode();
1530 }
1531 }
1532 auto cacheCount = listProps->GetCachedCountWithDefault();
1533 if (!listProps->HasCachedCount()) {
1534 int32_t newCacheCount = UpdateDefaultCachedCount(cacheCount, itemCount);
1535 listProps->SetDefaultCachedCount(newCacheCount);
1536 }
1537 ProcessCacheCount(layoutWrapper, cacheCount, listProps->GetShowCachedItemsValue(false));
1538 isLayouted_ = true;
1539 UpdateOverlay(layoutWrapper);
1540 }
1541
UpdateOverlay(LayoutWrapper * layoutWrapper)1542 void ListLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper)
1543 {
1544 auto frameNode = layoutWrapper->GetHostNode();
1545 CHECK_NULL_VOID(frameNode);
1546 auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>();
1547 CHECK_NULL_VOID(paintProperty);
1548 if (!paintProperty->GetFadingEdge().value_or(false)) {
1549 return;
1550 }
1551 auto overlayNode = frameNode->GetOverlayNode();
1552 CHECK_NULL_VOID(overlayNode);
1553 auto geometryNode = frameNode->GetGeometryNode();
1554 CHECK_NULL_VOID(geometryNode);
1555 auto listFrameSize = geometryNode->GetFrameSize();
1556 auto overlayGeometryNode = overlayNode->GetGeometryNode();
1557 CHECK_NULL_VOID(overlayGeometryNode);
1558 overlayGeometryNode->SetFrameSize(listFrameSize);
1559 }
1560
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1561 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1562 {
1563 float delta = crossSize - GetLaneGutter() - childCrossSize;
1564 if (LessOrEqual(delta, 0)) {
1565 return 0.0f;
1566 }
1567 switch (listItemAlign_) {
1568 case OHOS::Ace::V2::ListItemAlign::START:
1569 return 0.0f;
1570 case OHOS::Ace::V2::ListItemAlign::CENTER:
1571 return delta / 2.0f;
1572 case OHOS::Ace::V2::ListItemAlign::END:
1573 return delta;
1574 default:
1575 LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_);
1576 return 0.0f;
1577 }
1578 }
1579
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1580 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1581 {
1582 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1583 return;
1584 }
1585 auto host = layoutWrapper->GetHostNode();
1586 CHECK_NULL_VOID(host);
1587 auto focusHub = host->GetFocusHub();
1588 CHECK_NULL_VOID(focusHub);
1589 // textField not in List
1590 if (!focusHub->IsCurrentFocus()) {
1591 return;
1592 }
1593 auto context = PipelineContext::GetCurrentContext();
1594 CHECK_NULL_VOID(context);
1595 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1596 CHECK_NULL_VOID(textFieldManager);
1597 // only when textField is onFocus
1598 auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1599 CHECK_NULL_VOID(textField);
1600 auto textFieldHost = textField->GetHost();
1601 CHECK_NULL_VOID(textFieldHost);
1602 auto textBase = DynamicCast<TextBase>(textField);
1603 CHECK_NULL_VOID(textBase);
1604 auto caretPos = textFieldHost->GetTransformRelativeOffset().GetY() + textBase->GetCaretRect().Bottom();
1605 auto globalOffset = host->GetTransformRelativeOffset();
1606 auto offset = contentMainSize_ + globalOffset.GetY() - caretPos - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
1607 if (LessOrEqual(offset, 0.0)) {
1608 // negative offset to scroll down
1609 currentDelta_ -= static_cast<float>(offset);
1610 }
1611 }
1612
SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm> & itemGroup,bool forwardLayout,int32_t index)1613 void ListLayoutAlgorithm::SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm>& itemGroup,
1614 bool forwardLayout, int32_t index)
1615 {
1616 if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1617 if (!jumpIndexInGroup_.has_value()) {
1618 if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1619 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1620 jumpIndexInGroup_ = 0;
1621 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1622 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1623 jumpIndexInGroup_ = LAST_ITEM;
1624 }
1625 }
1626
1627 if (jumpIndexInGroup_.has_value()) {
1628 itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1629 itemGroup->SetScrollAlign(scrollAlign_);
1630 jumpIndexInGroup_.reset();
1631 }
1632 }
1633 }
1634
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout,bool needAdjustRefPos)1635 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1636 float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout,
1637 bool needAdjustRefPos)
1638 {
1639 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1640 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1641 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1642 CHECK_NULL_VOID(itemGroup);
1643 if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1644 referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1645 }
1646 if (jumpIndex_) {
1647 itemGroup->ClearItemPosition();
1648 }
1649 if (forwardLayout) {
1650 float endPos = layoutEndMainPos_.value_or(endMainPos_);
1651 float startPos = endPos - contentMainSize_;
1652 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1653 } else {
1654 float startPos = layoutStartMainPos_.value_or(startMainPos_);
1655 float endPos = startPos + contentMainSize_;
1656 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1657 }
1658 bool needMeasureFormLastItem = index < preStartIndex_;
1659 itemGroup->SetNeedMeasureFormLastItem(needMeasureFormLastItem);
1660 itemGroup->SetNeedAdjustRefPos(needAdjustRefPos);
1661 itemGroup->SetListLayoutProperty(layoutProperty);
1662 itemGroup->SetNeedCheckOffset(isNeedCheckOffset_);
1663 if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER) {
1664 itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
1665 }
1666 SetListItemGroupJumpIndex(itemGroup, forwardLayout, index);
1667
1668 if (groupNeedAllLayout || (targetIndex_ && targetIndex_.value() == index) ||
1669 (scrollSnapAlign_ != V2::ScrollSnapAlign::NONE && !childrenSize_)) {
1670 itemGroup->SetNeedAllLayout();
1671 } else if (forwardFeature_ || backwardFeature_) {
1672 itemGroup->CheckNeedAllLayout(layoutWrapper, forwardLayout);
1673 }
1674 if (CheckNeedMeasure(layoutWrapper)) {
1675 itemGroup->ResetCachedItemPosition();
1676 itemGroup->ResetCachedIndex();
1677 if (layoutWrapper->GetHostNode() && layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1678 auto groupPattern = layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1679 groupPattern->SetRecache(true);
1680 }
1681 }
1682 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1683 }
1684
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1685 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1686 {
1687 int32_t id = layoutWrapper->GetHostNode()->GetId();
1688 ListItemInfo pos = { id, 0, 0, true };
1689 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1690 CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
1691 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1692 CHECK_NULL_RETURN(itemGroup, pos);
1693 auto res = itemGroup->GetItemGroupPosition(index);
1694 return { id, res.first, res.second, true };
1695 }
1696
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1697 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1698 {
1699 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1700 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
1701 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1702 CHECK_NULL_RETURN(itemGroup, 0.0f);
1703 return itemGroup->GetItemHeight(index);
1704 }
1705
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1706 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1707 {
1708 auto host = layoutWrapper->GetHostNode();
1709 CHECK_NULL_VOID(host);
1710 auto listItem = host->GetPattern<ListItemPattern>();
1711 if (listItem) {
1712 listItem->SetIndexInList(index);
1713 return;
1714 }
1715 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1716 CHECK_NULL_VOID(listItemGroup);
1717 listItemGroup->SetIndexInList(index);
1718 }
1719
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1720 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1721 float referencePos, bool forwardLayout) const
1722 {
1723 if (targetIndex_.has_value()) {
1724 return;
1725 }
1726 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1727 CHECK_NULL_VOID(wrapper);
1728 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1729 CHECK_NULL_VOID(algorithmWrapper);
1730 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1731 CHECK_NULL_VOID(itemGroup);
1732 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1733 }
1734
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1735 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
1736 bool forwardLayout)
1737 {
1738 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1739 CHECK_NULL_VOID(wrapper);
1740 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1741 CHECK_NULL_VOID(algorithmWrapper);
1742 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1743 CHECK_NULL_VOID(itemGroup);
1744 if (forwardLayout) {
1745 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, prevContentMainSize_,
1746 !forwardLayout);
1747 } else {
1748 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, prevContentMainSize_,
1749 !forwardLayout);
1750 }
1751 itemGroup->SetScrollAlign(ScrollAlign::NONE);
1752 wrapper->Measure(GetGroupLayoutConstraint());
1753 if (childrenSize_) {
1754 return;
1755 }
1756 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1757 auto& pos = itemPosition_[index];
1758 if (forwardLayout) {
1759 pos.startPos = pos.endPos - mainLen;
1760 } else {
1761 pos.endPos = pos.startPos + mainLen;
1762 }
1763 }
1764
OffScreenLayoutDirection(LayoutWrapper * layoutWrapper)1765 void ListLayoutAlgorithm::OffScreenLayoutDirection(LayoutWrapper* layoutWrapper)
1766 {
1767 if (!targetIndex_ || itemPosition_.empty()) {
1768 forwardFeature_ = false;
1769 backwardFeature_ = false;
1770 return;
1771 }
1772 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
1773 if (layoutDirection == LayoutDirection::BACKWARD) {
1774 forwardFeature_ = false;
1775 backwardFeature_ = true;
1776 } else {
1777 forwardFeature_ = true;
1778 backwardFeature_ = false;
1779 }
1780 }
1781
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1782 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1783 {
1784 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1785 float midPos = contentSize / 2.0f;
1786 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1787 GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
1788 midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
1789 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1790 LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
1791 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1792 midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
1793 }
1794 for (auto& pos : itemPosition_) {
1795 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1796 return pos.first;
1797 }
1798 }
1799 return totalItemCount_ - 1;
1800 }
1801
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1802 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1803 {
1804 CHECK_NULL_VOID(wrapper);
1805 auto host = wrapper->GetHostNode();
1806 CHECK_NULL_VOID(host);
1807 host->ForceSyncGeometryNode();
1808 host->ResetLayoutAlgorithm();
1809 host->RebuildRenderContextTree();
1810 }
1811
LayoutCachedALine(LayoutWrapper * layoutWrapper,int32_t index,bool forward,float & currPos,float crossSize)1812 bool ListLayoutAlgorithm::LayoutCachedALine(LayoutWrapper* layoutWrapper, int32_t index,
1813 bool forward, float &currPos, float crossSize)
1814 {
1815 auto wrapper = layoutWrapper->GetChildByIndex(index, true);
1816 if (!wrapper) {
1817 return true;
1818 }
1819 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1820 if (CheckNeedMeasure(wrapper)) {
1821 return !isGroup;
1822 }
1823 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1824 int32_t id = wrapper->GetHostNode()->GetId();
1825 ListItemInfo pos;
1826 if (forward) {
1827 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1828 pos = { id, currPos, endPos, isGroup };
1829 currPos = endPos + spaceWidth_;
1830 } else {
1831 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1832 pos = { id, startPos, currPos, isGroup };
1833 currPos = startPos - spaceWidth_;
1834 }
1835 auto startIndex = index;
1836 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1837 SyncGeometry(wrapper);
1838 wrapper->SetActive(false);
1839 return false;
1840 }
1841
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1842 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1843 {
1844 std::list<int32_t> predictBuildList;
1845 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1846 float crossSize = GetCrossAxisSize(size, axis_);
1847
1848 auto currIndex = itemPosition_.rbegin()->first + 1;
1849 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1850 for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1851 int32_t index = currIndex + i;
1852 if (LayoutCachedALine(layoutWrapper, index, true, currPos, crossSize)) {
1853 predictBuildList.emplace_back(index);
1854 }
1855 }
1856
1857 currIndex = itemPosition_.begin()->first - 1;
1858 currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1859 for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1860 int32_t index = currIndex - i;
1861 if (LayoutCachedALine(layoutWrapper, index, false, currPos, crossSize)) {
1862 predictBuildList.emplace_back(index);
1863 }
1864 }
1865 return predictBuildList;
1866 }
1867
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1868 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1869 {
1870 CHECK_NULL_RETURN(wrapper, false);
1871 wrapper->SetActive(false);
1872 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1873 if (!isGroup) {
1874 auto frameNode = wrapper->GetHostNode();
1875 CHECK_NULL_RETURN(frameNode, false);
1876 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1877 FrameNode::ProcessOffscreenNode(frameNode);
1878 return true;
1879 }
1880 return false;
1881 }
1882
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1883 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1884 {
1885 CHECK_NULL_VOID(frameNode);
1886 auto pattern = frameNode->GetPattern<ListPattern>();
1887 CHECK_NULL_VOID(pattern);
1888 if (pattern->GetPredictLayoutParam()) {
1889 pattern->SetPredictLayoutParam(param);
1890 return;
1891 }
1892 pattern->SetPredictLayoutParam(param);
1893 auto context = PipelineContext::GetCurrentContext();
1894 CHECK_NULL_VOID(context);
1895 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1896 ACE_SCOPED_TRACE("List predict");
1897 auto frameNode = weak.Upgrade();
1898 CHECK_NULL_VOID(frameNode);
1899 auto pattern = frameNode->GetPattern<ListPattern>();
1900 CHECK_NULL_VOID(pattern);
1901 if (!pattern->GetPredictLayoutParam().has_value()) {
1902 return;
1903 }
1904 bool needMarkDirty = false;
1905 auto param = pattern->GetPredictLayoutParam().value();
1906 for (auto it = param.items.begin(); it != param.items.end();) {
1907 if (GetSysTimestamp() > deadline) {
1908 break;
1909 }
1910 auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false, true);
1911 if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
1912 break;
1913 }
1914 needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty;
1915 it = param.items.erase(it);
1916 }
1917 if (needMarkDirty) {
1918 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1919 }
1920 pattern->SetPredictLayoutParam(std::nullopt);
1921 if (!param.items.empty()) {
1922 ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1923 pattern->SetPredictLayoutParam(param);
1924 }
1925 });
1926 }
1927
1928 // return current CachedCount and max CacheCount
GetLayoutGroupCachedCount(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & wrapper,int32_t forwardCache,int32_t backwardCache,int32_t index,bool outOfView)1929 CachedIndexInfo ListLayoutAlgorithm::GetLayoutGroupCachedCount(LayoutWrapper* layoutWrapper,
1930 const RefPtr<LayoutWrapper>& wrapper, int32_t forwardCache, int32_t backwardCache, int32_t index, bool outOfView)
1931 {
1932 CachedIndexInfo res;
1933 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
1934 CHECK_NULL_RETURN(groupNode, res);
1935 auto group = groupNode->GetPattern<ListItemGroupPattern>();
1936 CHECK_NULL_RETURN(group, res);
1937 const auto& itemPos = group->GetItemPosition();
1938 bool reCache = false;
1939 if (outOfView && recycledItemPosition_.count(index) == 0) {
1940 reCache = CheckNeedMeasure(wrapper);
1941 } else if (outOfView) {
1942 wrapper->SetActive(true);
1943 wrapper->Layout();
1944 group->SyncItemsToCachedItemPosition();
1945 }
1946 bool forward = forwardCache > -1;
1947 bool backward = backwardCache > -1;
1948 if (forward && backward && itemPos.empty()) {
1949 forward = group->NeedCacheForward(layoutWrapper);
1950 backward = !forward;
1951 forwardCache = forward ? forwardCache : -1;
1952 backwardCache = backward ? backwardCache : -1;
1953 }
1954 res = group->UpdateCachedIndex(outOfView, reCache, forwardCache, backwardCache);
1955 if (group->GetTotalItemCount() == 0 && outOfView) {
1956 if (groupNode->CheckNeedForceMeasureAndLayout()) {
1957 res = {0, 0, 1, 1};
1958 } else {
1959 res = {1, 1, 1, 1};
1960 }
1961 }
1962 ACE_SCOPED_TRACE("GetLayoutGroupCachedCount forward:%d, %d, backward:%d, %d",
1963 res.forwardCachedCount, res.forwardCacheMax, res.backwardCachedCount, res.backwardCacheMax);
1964 return res;
1965 }
1966
GetLayoutCrossAxisSize(LayoutWrapper * layoutWrapper)1967 float ListLayoutAlgorithm::GetLayoutCrossAxisSize(LayoutWrapper* layoutWrapper)
1968 {
1969 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1970 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1971 MinusPaddingToSize(padding, size);
1972 return GetCrossAxisSize(size, axis_);
1973 }
1974
LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)1975 int32_t ListLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper,
1976 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
1977 {
1978 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
1979 curIndex = itemPosition_.rbegin()->first + 1;
1980 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1981 while (cachedCount < cacheCount && curIndex < totalItemCount_) {
1982 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
1983 if (!wrapper) {
1984 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
1985 return curIndex - 1;
1986 }
1987 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1988 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
1989 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
1990 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
1991 }
1992 if (!isGroup && isDirty) {
1993 return curIndex - 1;
1994 }
1995 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1996 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1997 int32_t id = wrapper->GetHostNode()->GetId();
1998 ListItemInfo pos = { id, currPos, endPos, isGroup };
1999 currPos = endPos + spaceWidth_;
2000 auto startIndex = curIndex;
2001 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2002 cachedItemPosition_[curIndex] = pos;
2003 if (isGroup) {
2004 auto res = GetLayoutGroupCachedCount(
2005 layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true);
2006 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) {
2007 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2008 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1;
2009 }
2010 cachedCount += std::max(res.forwardCacheMax, 1);
2011 } else {
2012 cachedCount++;
2013 }
2014 SyncGeometry(wrapper);
2015 wrapper->SetActive(false);
2016 curIndex++;
2017 }
2018 return curIndex - 1;
2019 }
2020
LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2021 int32_t ListLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper,
2022 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2023 {
2024 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2025 curIndex = itemPosition_.begin()->first - 1;
2026 auto currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
2027 while (cachedCount < cacheCount && curIndex >= 0) {
2028 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
2029 if (!wrapper) {
2030 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2031 return curIndex + 1;
2032 }
2033 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2034 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2035 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
2036 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2037 }
2038 if (!isGroup && isDirty) {
2039 return curIndex + 1;
2040 }
2041 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2042 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
2043 int32_t id = wrapper->GetHostNode()->GetId();
2044 ListItemInfo pos = { id, startPos, currPos, isGroup };
2045 currPos = startPos - spaceWidth_;
2046 auto startIndex = curIndex;
2047 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2048 cachedItemPosition_[curIndex] = pos;
2049 if (isGroup) {
2050 auto res = GetLayoutGroupCachedCount(
2051 layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true);
2052 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) {
2053 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2054 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1;
2055 }
2056 cachedCount += std::max(res.backwardCacheMax, 1);
2057 } else {
2058 cachedCount++;
2059 }
2060 SyncGeometry(wrapper);
2061 wrapper->SetActive(false);
2062 curIndex--;
2063 }
2064 return curIndex + 1;
2065 }
2066
LayoutCachedItemInEdgeGroup(LayoutWrapper * layoutWrapper,int32_t cacheCount,std::list<PredictLayoutItem> & predictList)2067 std::tuple<int32_t, int32_t, int32_t, int32_t> ListLayoutAlgorithm::LayoutCachedItemInEdgeGroup(
2068 LayoutWrapper* layoutWrapper, int32_t cacheCount, std::list<PredictLayoutItem>& predictList)
2069 {
2070 int32_t startIndex = GetStartIndex();
2071 int32_t endIndex = GetEndIndex();
2072 int32_t cachedForward = 0;
2073 int32_t cachedBackward = 0;
2074 if (startIndex == endIndex && itemPosition_.begin()->second.isGroup) {
2075 auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2076 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, cacheCount, startIndex, false);
2077 if ((res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) ||
2078 (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount)) {
2079 int32_t forwardCached = res.forwardCacheMax > 0 ? cachedForward : -1;
2080 int32_t backwardCached = res.backwardCacheMax > 0 ? cachedBackward : -1;
2081 predictList.emplace_back(PredictLayoutItem { startIndex, forwardCached, backwardCached });
2082 }
2083 cachedForward += res.forwardCacheMax;
2084 cachedBackward += res.backwardCacheMax;
2085 } else {
2086 if (itemPosition_.rbegin()->second.isGroup) {
2087 auto wrapper = layoutWrapper->GetChildByIndex(endIndex);
2088 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, -1, endIndex, false);
2089 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) {
2090 predictList.emplace_back(PredictLayoutItem { endIndex, cachedForward, -1 });
2091 }
2092 cachedForward += res.forwardCacheMax;
2093 }
2094 if (itemPosition_.begin()->second.isGroup) {
2095 auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2096 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount, startIndex, false);
2097 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount) {
2098 predictList.emplace_back(PredictLayoutItem { startIndex, -1, cachedBackward });
2099 }
2100 cachedBackward += res.backwardCacheMax;
2101 }
2102 }
2103 return { startIndex, endIndex, cachedForward, cachedBackward };
2104 }
2105
LayoutCachedItemV2(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)2106 std::list<PredictLayoutItem> ListLayoutAlgorithm::LayoutCachedItemV2(LayoutWrapper* layoutWrapper, int32_t cacheCount,
2107 bool show)
2108 {
2109 ACE_SCOPED_TRACE("LayoutCachedItemV2");
2110 std::list<PredictLayoutItem> predictBuildList;
2111 auto [startIndex, endIndex, cachedForward, cachedBackward] =
2112 LayoutCachedItemInEdgeGroup(layoutWrapper, cacheCount, predictBuildList);
2113 if (cachedForward < cacheCount && endIndex < totalItemCount_ - 1) {
2114 endIndex = LayoutCachedForward(layoutWrapper, cacheCount, cachedForward, endIndex, predictBuildList, show);
2115 }
2116 if (cachedBackward < cacheCount && startIndex > 0) {
2117 startIndex =
2118 LayoutCachedBackward(layoutWrapper, cacheCount, cachedBackward, startIndex, predictBuildList, show);
2119 }
2120 int32_t cacheStart = itemPosition_.begin()->first - startIndex;
2121 int32_t cacheEnd = endIndex - itemPosition_.rbegin()->first;
2122 ResetLayoutItem(layoutWrapper);
2123 SetActiveChildRange(layoutWrapper, cacheStart, cacheEnd, show);
2124 return predictBuildList;
2125 }
2126
PredictBuildGroup(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,const ListMainSizeValues & listMainSizeValues)2127 bool ListLayoutAlgorithm::PredictBuildGroup(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint,
2128 int64_t deadline, int32_t forwardCached, int32_t backwardCached, const ListMainSizeValues& listMainSizeValues)
2129 {
2130 CHECK_NULL_RETURN(wrapper, false);
2131 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
2132 CHECK_NULL_RETURN(groupNode, false);
2133 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
2134 CHECK_NULL_RETURN(groupPattern, false);
2135 float referencePos = 0.0f;
2136 if (listMainSizeValues.jumpIndexInGroup.has_value() && listMainSizeValues.scrollAlign == ScrollAlign::CENTER) {
2137 referencePos = (listMainSizeValues.startPos + listMainSizeValues.endPos) / 2; // 2:average
2138 }
2139 float endPos = 0.0f;
2140 float startPos = 0.0f;
2141 if (listMainSizeValues.forward) {
2142 startPos = listMainSizeValues.startPos;
2143 endPos = listMainSizeValues.layoutEndMainPos.value_or(listMainSizeValues.endPos);
2144 } else {
2145 startPos = listMainSizeValues.layoutStartMainPos.value_or(listMainSizeValues.startPos);
2146 endPos = listMainSizeValues.endPos;
2147 }
2148 ListMainSizeValues values;
2149 values.startPos = startPos;
2150 values.endPos = endPos;
2151 values.referencePos = referencePos;
2152 values.prevContentMainSize = listMainSizeValues.prevContentMainSize;
2153 values.forward = listMainSizeValues.forward;
2154 values.backward = listMainSizeValues.backward;
2155 groupPattern->LayoutCache(constraint, deadline, forwardCached, backwardCached, values);
2156 return true;
2157 }
2158
PredictBuildV2(RefPtr<FrameNode> frameNode,int64_t deadline,ListMainSizeValues listMainSizeValues,bool show)2159 void ListLayoutAlgorithm::PredictBuildV2(
2160 RefPtr<FrameNode> frameNode, int64_t deadline, ListMainSizeValues listMainSizeValues, bool show)
2161 {
2162 ACE_SCOPED_TRACE("List predict v2");
2163 CHECK_NULL_VOID(frameNode);
2164 auto pattern = frameNode->GetPattern<ListPattern>();
2165 CHECK_NULL_VOID(pattern);
2166 if (!pattern->GetPredictLayoutParamV2().has_value()) {
2167 return;
2168 }
2169 bool needMarkDirty = false;
2170 auto param = pattern->GetPredictLayoutParamV2().value();
2171 for (auto it = param.items.begin(); it != param.items.end();) {
2172 if (GetSysTimestamp() > deadline) {
2173 break;
2174 }
2175 ACE_SCOPED_TRACE("predict Item:%d", (*it).index);
2176 auto wrapper = frameNode->GetOrCreateChildByIndex((*it).index, show, true);
2177 if (!wrapper) {
2178 it = param.items.erase(it);
2179 continue;
2180 }
2181 if (wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
2182 break;
2183 }
2184 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2185 if (!isGroup) {
2186 auto frameNode = wrapper->GetHostNode();
2187 CHECK_NULL_VOID(frameNode);
2188 frameNode->GetGeometryNode()->SetParentLayoutConstraint(param.layoutConstraint);
2189 FrameNode::ProcessOffscreenNode(frameNode);
2190 } else {
2191 listMainSizeValues.forward = (*it).forwardCacheCount > -1;
2192 listMainSizeValues.backward = (*it).backwardCacheCount > -1;
2193 PredictBuildGroup(wrapper, param.groupLayoutConstraint, deadline, (*it).forwardCacheCount,
2194 (*it).backwardCacheCount, listMainSizeValues);
2195 }
2196 needMarkDirty = true;
2197 it = param.items.erase(it);
2198 }
2199 if (needMarkDirty) {
2200 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
2201 }
2202 pattern->SetPredictLayoutParamV2(std::nullopt);
2203 if (!param.items.empty()) {
2204 ListLayoutAlgorithm::PostIdleTaskV2(frameNode, param, listMainSizeValues, show);
2205 }
2206 }
2207
PostIdleTaskV2(RefPtr<FrameNode> frameNode,const ListPredictLayoutParamV2 & param,ListMainSizeValues listMainSizeValues,bool show)2208 void ListLayoutAlgorithm::PostIdleTaskV2(RefPtr<FrameNode> frameNode,
2209 const ListPredictLayoutParamV2& param, ListMainSizeValues listMainSizeValues, bool show)
2210 {
2211 ACE_SCOPED_TRACE("PostIdleTaskV2");
2212 CHECK_NULL_VOID(frameNode);
2213 auto pattern = frameNode->GetPattern<ListPattern>();
2214 CHECK_NULL_VOID(pattern);
2215 if (pattern->GetPredictLayoutParamV2()) {
2216 pattern->SetPredictLayoutParamV2(param);
2217 return;
2218 }
2219 pattern->SetPredictLayoutParamV2(param);
2220 auto context = PipelineContext::GetCurrentContext();
2221 CHECK_NULL_VOID(context);
2222 context->AddPredictTask(
2223 [weak = WeakClaim(RawPtr(frameNode)), value = listMainSizeValues, show = show](int64_t deadline,
2224 bool canUseLongPredictTask) { ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, value, show); });
2225 }
2226
GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const2227 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const
2228 {
2229 float stopOnScreen = 0;
2230 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2231 stopOnScreen = contentStartOffset_;
2232 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2233 stopOnScreen = contentMainSize_ / 2.0f;
2234 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2235 stopOnScreen = contentMainSize_ - contentEndOffset_;
2236 }
2237 return stopOnScreen;
2238 }
2239
FindPredictSnapIndexInItemPositionsStart(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2240 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsStart(
2241 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2242 {
2243 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::START);
2244 float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
2245 for (const auto& positionInfo : itemPosition_) {
2246 auto startPos = positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
2247 float itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2248 auto endPos = positionInfo.second.startPos + itemHeight / 2.0f;
2249 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2250 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2251 endIndex = positionInfo.first;
2252 }
2253 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2254 currIndex = positionInfo.first;
2255 }
2256 if (endIndex >= 0 && currIndex >= 0) {
2257 break;
2258 }
2259 }
2260 }
2261
FindPredictSnapIndexInItemPositionsCenter(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2262 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsCenter(
2263 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2264 {
2265 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::CENTER);
2266 for (const auto& positionInfo : itemPosition_) {
2267 auto startPos = positionInfo.second.startPos - spaceWidth_ / 2.0f;
2268 auto endPos = positionInfo.second.endPos + spaceWidth_ / 2.0f;
2269 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2270 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2271 endIndex = positionInfo.first;
2272 }
2273 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2274 currIndex = positionInfo.first;
2275 }
2276 if (endIndex >= 0 && currIndex >= 0) {
2277 break;
2278 }
2279 }
2280 }
2281
FindPredictSnapIndexInItemPositionsEnd(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2282 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsEnd(
2283 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2284 {
2285 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::END);
2286 float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
2287 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2288 auto endPos = pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
2289 itemHeight = pos->second.endPos - pos->second.startPos;
2290 auto startPos = pos->second.endPos - itemHeight / 2.0f;
2291 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2292 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2293 endIndex = pos->first;
2294 }
2295 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2296 currIndex = pos->first;
2297 }
2298 if (endIndex >= 0 && currIndex >= 0) {
2299 break;
2300 }
2301 }
2302 }
2303
FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)2304 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
2305 float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign)
2306 {
2307 int32_t endIndex = -1;
2308 int32_t currIndex = -1;
2309
2310 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2311 FindPredictSnapIndexInItemPositionsStart(predictEndPos, endIndex, currIndex);
2312 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2313 FindPredictSnapIndexInItemPositionsCenter(predictEndPos, endIndex, currIndex);
2314 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2315 FindPredictSnapIndexInItemPositionsEnd(predictEndPos, endIndex, currIndex);
2316 }
2317 if (endIndex == currIndex && currIndex >= 0) {
2318 if (scrollSnapVelocity_ < -SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2319 endIndex = std::min(GetEndIndex(), endIndex + 1);
2320 } else if (scrollSnapVelocity_ > SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2321 endIndex = std::min(GetStartIndex(), endIndex - 1);
2322 }
2323 }
2324 return endIndex;
2325 }
2326
IsUniformHeightProbably()2327 bool ListLayoutAlgorithm::IsUniformHeightProbably()
2328 {
2329 bool isUniformHeightProbably = true;
2330 float itemHeight = 0.0f;
2331 float currentItemHeight = 0.0f;
2332 for (const auto& positionInfo : itemPosition_) {
2333 currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2334 if (NearZero(itemHeight)) {
2335 itemHeight = currentItemHeight;
2336 } else if (!NearEqual(currentItemHeight, itemHeight)) {
2337 isUniformHeightProbably = false;
2338 break;
2339 }
2340 }
2341 return isUniformHeightProbably;
2342 }
2343
CalculatePredictSnapEndPositionByIndex(int32_t index,V2::ScrollSnapAlign scrollSnapAlign)2344 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(int32_t index, V2::ScrollSnapAlign scrollSnapAlign)
2345 {
2346 float predictSnapEndPos = 0;
2347 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2348 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
2349 float endPos = GetEndPosition();
2350 float itemTotalSize = endPos - GetStartPosition();
2351 float contentSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
2352 if ((GetEndIndex() == totalItemCount_ - 1) && GreatNotEqual(itemTotalSize, contentSize) &&
2353 GreatNotEqual(predictSnapEndPos + contentMainSize_ - contentEndOffset_, totalOffset_ + endPos)) {
2354 predictSnapEndPos = totalOffset_ + endPos - contentMainSize_ + contentEndOffset_;
2355 }
2356 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2357 float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
2358 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
2359 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2360 predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
2361 if (GetStartIndex() == 0 && LessNotEqual(predictSnapEndPos, totalOffset_ + GetStartPosition())) {
2362 predictSnapEndPos = totalOffset_ + GetStartPosition() - contentStartOffset_;
2363 }
2364 }
2365 return predictSnapEndPos;
2366 }
2367
OnItemPositionAddOrUpdate(LayoutWrapper * layoutWrapper,int32_t index)2368 void ListLayoutAlgorithm::OnItemPositionAddOrUpdate(LayoutWrapper* layoutWrapper, int32_t index)
2369 {
2370 if (!predictSnapEndPos_.has_value()) {
2371 return;
2372 }
2373 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
2374 CHECK_NULL_VOID(listLayoutProperty);
2375 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
2376 float startPos = 0.0f;
2377 float endPos = 0.0f;
2378 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2379 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_;
2380 endPos = totalOffset_ + itemPosition_[index].endPos;
2381 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2382 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_ / 2.0f;
2383 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_ / 2.0f;
2384 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2385 startPos = totalOffset_ + itemPosition_[index].startPos;
2386 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_;
2387 } else {
2388 return;
2389 }
2390
2391 float predictSnapEndPos = predictSnapEndPos_.value();
2392 float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign);
2393 if (GreatOrEqual(predictSnapEndPos + stopOnScreen, startPos) &&
2394 LessNotEqual(predictSnapEndPos + stopOnScreen, endPos)) {
2395 predictSnapEndPos = CalculatePredictSnapEndPositionByIndex(index, scrollSnapAlign);
2396 } else {
2397 return;
2398 }
2399
2400 if (!NearEqual(predictSnapEndPos, predictSnapEndPos_.value())) {
2401 predictSnapEndPos_ = predictSnapEndPos;
2402 }
2403 }
2404
GetSnapStartIndexAndPos()2405 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapStartIndexAndPos()
2406 {
2407 int32_t startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
2408 float startPos = itemPosition_.begin()->second.startPos;
2409 for (auto& pos : itemPosition_) {
2410 if (NearEqual(pos.second.startPos, prevContentStartOffset_)) {
2411 startIndex = pos.first;
2412 startPos = itemPosition_[startIndex].startPos + contentStartOffset_ - prevContentStartOffset_;
2413 break;
2414 } else if (GreatNotEqual(pos.second.startPos, prevContentStartOffset_)) {
2415 if ((GetEndIndex() == totalItemCount_ - 1) &&
2416 NearEqual(GetEndPosition(), prevContentMainSize_ - prevContentEndOffset_) && !canOverScroll_) {
2417 startIndex = pos.first;
2418 startPos = contentStartOffset_;
2419 adjustOffset_ = pos.second.startPos - prevContentStartOffset_;
2420 }
2421 break;
2422 }
2423 }
2424 return std::make_pair(std::min(startIndex, totalItemCount_ -1), startPos);
2425 }
2426
GetSnapEndIndexAndPos()2427 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapEndIndexAndPos()
2428 {
2429 int32_t endIndex = -1;
2430 float endPos = 0.0f;
2431 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2432 if (NearEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2433 endIndex = pos->first;
2434 endPos = itemPosition_[endIndex].endPos - contentEndOffset_ + prevContentEndOffset_;
2435 break;
2436 } else if (GreatNotEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2437 if ((GetStartIndex() == 0) && NearEqual(GetStartPosition(), prevContentStartOffset_) && !canOverScroll_) {
2438 endIndex = pos->first;
2439 endPos = prevContentMainSize_ - contentEndOffset_;
2440 adjustOffset_ = pos->second.endPos + prevContentEndOffset_ - prevContentMainSize_;
2441 }
2442 break;
2443 }
2444 }
2445 return std::make_pair(std::min(endIndex, totalItemCount_ -1), endPos);
2446 }
2447
UpdateDefaultCachedCount(const int32_t oldCacheCount,const int32_t itemCount)2448 int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount)
2449 {
2450 if (itemCount <= 0) {
2451 return oldCacheCount;
2452 }
2453 static float pageCount = SystemProperties::GetPageCount();
2454 if (pageCount <= 0.0f) {
2455 return oldCacheCount;
2456 }
2457 constexpr int32_t MAX_DEFAULT_CACHED_COUNT = 16;
2458 int32_t newCachedCount = static_cast<int32_t>(ceil(pageCount * itemCount));
2459 if (newCachedCount > MAX_DEFAULT_CACHED_COUNT) {
2460 TAG_LOGI(AceLogTag::ACE_LIST, "Default cachedCount exceed 16");
2461 return MAX_DEFAULT_CACHED_COUNT;
2462 } else {
2463 return std::max(newCachedCount, oldCacheCount);
2464 }
2465 }
2466 } // namespace OHOS::Ace::NG
2467