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 return 1;
992 }
993
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)994 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
995 int32_t& currentIndex, float endPos, float& startPos)
996 {
997 if (currentIndex - 1 < 0) {
998 return 0;
999 }
1000 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex - 1) {
1001 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
1002 CHECK_NULL_RETURN(wrapper, 0);
1003 int32_t id = wrapper->GetHostNode()->GetId();
1004 --currentIndex;
1005 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1006 if (isGroup) {
1007 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1008 SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
1009 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, endPos);
1010 wrapper->Measure(childLayoutConstraint_);
1011 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) {
1012 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, endPos);
1013 wrapper->Measure(childLayoutConstraint_);
1014 }
1015 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) :
1016 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1017 startPos = endPos - mainLen;
1018 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
1019 } else {
1020 --currentIndex;
1021 itemPosition_[currentIndex] = firstItemInfo_.value().second;
1022 startPos = itemPosition_[currentIndex].startPos;
1023 }
1024 if (firstItemInfo_) {
1025 firstItemInfo_.reset();
1026 }
1027 return 1;
1028 }
1029
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)1030 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
1031 {
1032 float currentEndPos = startPos;
1033 float currentStartPos = 0.0f;
1034 float endMainPos = (overScrollFeature_ && startIndex == 0) ?
1035 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
1036 layoutEndMainPos_ = endMainPos;
1037 if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1038 endMainPos = Infinity<float>();
1039 }
1040
1041 auto currentIndex = startIndex - 1;
1042 auto chainOffset = 0.0f;
1043 do {
1044 currentStartPos = currentEndPos;
1045 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
1046 if (count == 0) {
1047 break;
1048 }
1049 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
1050 currentEndPos += spaceWidth_;
1051 }
1052 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1053 // reach the valid target index
1054 if (forwardFeature_ && targetIndex_ && currentIndex >= targetIndex_.value()) {
1055 endMainPos = layoutEndMainPos_.value_or(endMainPos_);
1056 forwardFeature_ = false;
1057 }
1058 } while (LessOrEqual(currentEndPos + chainOffset, endMainPos));
1059 currentEndPos += chainOffset;
1060
1061 while (itemPosition_.size() > 1 && !targetIndex_) {
1062 auto pos = itemPosition_.rbegin();
1063 float chainDelta = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1064 if ((GreatNotEqual(pos->second.endPos + chainDelta, endMainPos) &&
1065 GreatOrEqual(pos->second.startPos + chainDelta, endMainPos))) {
1066 recycledItemPosition_.emplace(pos->first, pos->second);
1067 itemPosition_.erase(pos->first);
1068 } else {
1069 break;
1070 }
1071 }
1072 // adjust offset.
1073 UpdateSnapCenterContentOffset(layoutWrapper);
1074 if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
1075 endMainPos_ = currentEndPos + contentEndOffset_;
1076 startMainPos_ = endMainPos_ - contentMainSize_;
1077 ReMeasureListItemGroup(layoutWrapper, true);
1078 auto firstItemTop = itemPosition_.begin()->second.startPos;
1079 auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
1080 if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
1081 // all items size is less than list.
1082 if (!canOverScroll_) {
1083 currentOffset_ = firstItemTop - contentStartOffset_;
1084 startMainPos_ = currentOffset_;
1085 endMainPos_ = startMainPos_ + contentMainSize_;
1086 }
1087 if (!mainSizeIsDefined_) {
1088 // adapt child size.
1089 contentMainSize_ = itemTotalSize;
1090 }
1091 } else {
1092 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1093 if (!canOverScroll_ || jumpIndex_.has_value()) {
1094 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
1095 }
1096 }
1097 }
1098 if (overScrollFeature_ && canOverScroll_) {
1099 return;
1100 }
1101 // Mark inactive in wrapper.
1102 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
1103 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1104 // Don't recycle When the head item is Visibility.None.
1105 if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) ||
1106 GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) {
1107 if (pos->second.isGroup) {
1108 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
1109 }
1110 break;
1111 }
1112 recycledItemPosition_.emplace(pos->first, pos->second);
1113 pos = itemPosition_.erase(pos);
1114 }
1115 }
1116
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)1117 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
1118 {
1119 float currentStartPos = endPos;
1120 float currentEndPos = 0.0f;
1121 float startMainPos = (overScrollFeature_ && endIndex == totalItemCount_ - 1) ?
1122 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
1123 layoutStartMainPos_ = startMainPos;
1124 if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
1125 startMainPos = -Infinity<float>();
1126 }
1127 auto currentIndex = endIndex + 1;
1128 auto chainOffset = 0.0f;
1129 do {
1130 currentEndPos = currentStartPos;
1131 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
1132 if (count == 0) {
1133 break;
1134 }
1135 if (currentIndex > 0) {
1136 currentStartPos = currentStartPos - spaceWidth_;
1137 }
1138 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
1139 // reach the valid target index
1140 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
1141 startMainPos = layoutStartMainPos_.value_or(startMainPos_);
1142 backwardFeature_ = false;
1143 }
1144 } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
1145
1146 currentStartPos += chainOffset;
1147 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
1148 UpdateSnapCenterContentOffset(layoutWrapper);
1149 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_) && !itemPosition_.empty()) {
1150 auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
1151 bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
1152 if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
1153 if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
1154 currentOffset_ += contentMainSize_ - prevContentMainSize_;
1155 }
1156 contentMainSize_ = itemTotalSize;
1157 }
1158 if (!canOverScroll_ || jumpIndex_.has_value()) {
1159 currentOffset_ = currentStartPos - contentStartOffset_;
1160 }
1161 endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
1162 startMainPos_ = currentStartPos - contentStartOffset_;
1163 ReMeasureListItemGroup(layoutWrapper, false);
1164 }
1165
1166 if (overScrollFeature_) {
1167 return;
1168 }
1169
1170 // Mark inactive in wrapper.
1171 std::list<int32_t> removeIndexes;
1172 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1173 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1174 // Don't recycle When the tail item is Visibility.None.
1175 if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) ||
1176 LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) {
1177 if (pos->second.isGroup) {
1178 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
1179 }
1180 break;
1181 }
1182 recycledItemPosition_.emplace(pos->first, pos->second);
1183 removeIndexes.emplace_back(pos->first);
1184 }
1185 for (const auto& index : removeIndexes) {
1186 itemPosition_.erase(index);
1187 }
1188 }
1189
ReMeasureListItemGroup(LayoutWrapper * layoutWrapper,bool forwardLayout)1190 void ListLayoutAlgorithm::ReMeasureListItemGroup(LayoutWrapper* layoutWrapper, bool forwardLayout)
1191 {
1192 if (forwardFeature_ || backwardFeature_) {
1193 return;
1194 }
1195 if (forwardLayout) {
1196 if (itemPosition_.begin()->second.isGroup) {
1197 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), forwardLayout);
1198 }
1199 return;
1200 }
1201 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end(); pos++) {
1202 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
1203 if (GreatOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
1204 break;
1205 } else if (!pos->second.isGroup) {
1206 continue;
1207 }
1208 AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout);
1209 }
1210 }
1211
FixPredictSnapPos()1212 void ListLayoutAlgorithm::FixPredictSnapPos()
1213 {
1214 if (!predictSnapEndPos_.has_value() || itemPosition_.empty()) {
1215 return;
1216 }
1217 float predictEndPos = predictSnapEndPos_.value();
1218 int32_t predictIndex = -1;
1219 int32_t curIndex = -1; // here invalid.
1220 switch (scrollSnapAlign_) {
1221 case V2::ScrollSnapAlign::START:
1222 FindPredictSnapIndexInItemPositionsStart(predictEndPos + currentOffset_, predictIndex, curIndex);
1223 break;
1224 case V2::ScrollSnapAlign::CENTER:
1225 FindPredictSnapIndexInItemPositionsCenter(predictEndPos + currentOffset_, predictIndex, curIndex);
1226 break;
1227 case V2::ScrollSnapAlign::END:
1228 FindPredictSnapIndexInItemPositionsEnd(predictEndPos + currentOffset_, predictIndex, curIndex);
1229 break;
1230 default:
1231 return;
1232 }
1233 if (GetStartIndex() <= predictIndex && predictIndex <= GetEndIndex()) {
1234 predictEndPos = CalculatePredictSnapEndPositionByIndex(predictIndex, scrollSnapAlign_);
1235 predictEndPos -= currentOffset_;
1236 }
1237 if (!NearEqual(predictEndPos, predictSnapEndPos_.value())) {
1238 predictSnapEndPos_ = predictEndPos;
1239 }
1240 }
1241
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)1242 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
1243 {
1244 if (!predictSnapOffset_.has_value() || itemPosition_.empty()) {
1245 return;
1246 }
1247 if (scrollSnapAlign_ == V2::ScrollSnapAlign::NONE) {
1248 predictSnapOffset_.reset();
1249 predictSnapEndPos_.reset();
1250 return;
1251 }
1252
1253 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1254 int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign_);
1255 if (GetStartIndex() <= endIndex && endIndex <= GetEndIndex()) {
1256 predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign_);
1257 predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_;
1258 predictSnapEndPos_.reset();
1259 } else {
1260 if (IsUniformHeightProbably()) {
1261 if (scrollSnapAlign_ == V2::ScrollSnapAlign::START) {
1262 FixPredictSnapOffsetAlignStart();
1263 } else if (scrollSnapAlign_ == V2::ScrollSnapAlign::CENTER) {
1264 FixPredictSnapOffsetAlignCenter();
1265 } else if (scrollSnapAlign_ == V2::ScrollSnapAlign::END) {
1266 FixPredictSnapOffsetAlignEnd();
1267 }
1268 } else {
1269 predictSnapEndPos_ = predictEndPos;
1270 }
1271 }
1272
1273 return;
1274 }
1275
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1276 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1277 {
1278 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1279 CHECK_NULL_RETURN(listLayoutProperty, false);
1280 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1281 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1282 return true;
1283 }
1284
1285 return false;
1286 }
1287
FixPredictSnapOffsetAlignStart()1288 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1289 {
1290 if (itemPosition_.empty()) {
1291 return;
1292 }
1293 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1294 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1295 float startPos = contentStartOffset_;
1296 float endPos = contentMainSize_ - contentEndOffset_;
1297 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1298
1299 if (LessNotEqual(predictEndPos, -startPos)) {
1300 if (isSpringEffect_) {
1301 return;
1302 }
1303 predictEndPos = -startPos;
1304 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1305 if (isSpringEffect_) {
1306 return;
1307 }
1308 predictEndPos = maxPos;
1309 } else {
1310 int32_t index;
1311 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1312 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1313 break;
1314 }
1315 }
1316 predictEndPos = index * itemHeight - startPos;
1317 if (LessNotEqual(predictEndPos, -startPos)) {
1318 predictEndPos = -startPos;
1319 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1320 predictEndPos = maxPos;
1321 }
1322 }
1323
1324 predictSnapOffset_ = totalOffset_ - predictEndPos;
1325 predictSnapEndPos_ = predictEndPos;
1326 }
1327
FixPredictSnapOffsetAlignCenter()1328 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1329 {
1330 if (itemPosition_.empty()) {
1331 return;
1332 }
1333 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1334 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1335 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1336 if (isSpringEffect_) {
1337 return;
1338 }
1339 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1340 } else if (GreatNotEqual(
1341 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1342 if (isSpringEffect_) {
1343 return;
1344 }
1345 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1346 } else {
1347 int32_t index;
1348 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1349 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1350 itemHeight / 2.0f) {
1351 break;
1352 }
1353 }
1354 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1355 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1356 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1357 } else if (GreatNotEqual(
1358 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1359 predictEndPos =
1360 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1361 }
1362 }
1363
1364 predictSnapOffset_ = totalOffset_ - predictEndPos;
1365 predictSnapEndPos_ = predictEndPos;
1366 }
1367
FixPredictSnapOffsetAlignEnd()1368 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1369 {
1370 if (itemPosition_.empty()) {
1371 return;
1372 }
1373 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1374 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1375 float startPos = contentStartOffset_;
1376 float endPos = contentMainSize_ - contentEndOffset_;
1377 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1378
1379 if (LessNotEqual(predictEndPos, -startPos)) {
1380 if (isSpringEffect_) {
1381 return;
1382 }
1383 predictEndPos = -startPos;
1384 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1385 if (isSpringEffect_) {
1386 return;
1387 }
1388 predictEndPos = maxPos;
1389 } else {
1390 int32_t index;
1391 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1392 if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1393 break;
1394 }
1395 }
1396 predictEndPos = index * itemHeight - endPos - spaceWidth_;
1397 if (LessNotEqual(predictEndPos, -startPos)) {
1398 predictEndPos = -startPos;
1399 } else if (GreatNotEqual(predictEndPos, maxPos)) {
1400 predictEndPos = maxPos;
1401 }
1402 }
1403
1404 predictSnapOffset_ = totalOffset_ - predictEndPos;
1405 predictSnapEndPos_ = predictEndPos;
1406 }
1407
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1408 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1409 int32_t& startIndex, float crossSize)
1410 {
1411 CHECK_NULL_VOID(wrapper);
1412 auto offset = paddingOffset_;
1413 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1414 float crossOffset = 0.0f;
1415 if (GetLanes() > 1) {
1416 int32_t laneIndex = 0;
1417 if (pos.isGroup) {
1418 startIndex = index + 1;
1419 } else {
1420 laneIndex = (index - startIndex) % GetLanes();
1421 }
1422
1423 float laneGutter = GetLaneGutter();
1424 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
1425 crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1426 } else {
1427 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1428 }
1429 auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
1430 if (isReverse_) {
1431 if (axis_ == Axis::VERTICAL) {
1432 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize();
1433 offset = offset + OffsetF(crossSize - crossOffset - size.Width(), pos.startPos + chainOffset);
1434 } else {
1435 offset = offset + OffsetF(contentMainSize_ - pos.endPos - chainOffset, crossOffset);
1436 }
1437 } else {
1438 if (axis_ == Axis::VERTICAL) {
1439 offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
1440 } else {
1441 offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
1442 }
1443 }
1444 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1445 SetListItemIndex(wrapper, index);
1446 }
1447
ProcessCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)1448 void ListLayoutAlgorithm::ProcessCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show)
1449 {
1450 if (!itemPosition_.empty() && cacheCount > 0) {
1451 auto items = LayoutCachedItemV2(layoutWrapper, cacheCount, show);
1452 auto host = layoutWrapper->GetHostNode();
1453 CHECK_NULL_VOID(host);
1454 if (!items.empty()) {
1455 ListMainSizeValues value = { startMainPos_, endMainPos_, jumpIndexInGroup_, prevContentMainSize_,
1456 scrollAlign_, layoutStartMainPos_, layoutEndMainPos_ };
1457 PostIdleTaskV2(host, { items, childLayoutConstraint_, GetGroupLayoutConstraint() }, value, show);
1458 } else {
1459 auto pattern = host->GetPattern<ListPattern>();
1460 CHECK_NULL_VOID(pattern);
1461 pattern->SetPredictLayoutParamV2(std::nullopt);
1462 }
1463 } else {
1464 ResetLayoutItem(layoutWrapper);
1465 SetActiveChildRange(layoutWrapper, cacheCount, cacheCount, show);
1466 }
1467 }
1468
GetListItemGroupLayoutInfo(const RefPtr<LayoutWrapper> & wrapper) const1469 std::optional<ListItemGroupLayoutInfo> ListLayoutAlgorithm::GetListItemGroupLayoutInfo(
1470 const RefPtr<LayoutWrapper>& wrapper) const
1471 {
1472 CHECK_NULL_RETURN(wrapper, std::nullopt);
1473 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1474 CHECK_NULL_RETURN(layoutAlgorithmWrapper, std::nullopt);
1475 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1476 CHECK_NULL_RETURN(itemGroup, std::nullopt);
1477 return itemGroup->GetLayoutInfo();
1478 }
1479
GetListItemGroupItemCount(const RefPtr<LayoutWrapper> & wrapper) const1480 int32_t ListLayoutAlgorithm::GetListItemGroupItemCount(const RefPtr<LayoutWrapper>& wrapper) const
1481 {
1482 CHECK_NULL_RETURN(wrapper, 0);
1483 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1484 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0);
1485 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1486 CHECK_NULL_RETURN(itemGroup, 0);
1487 int32_t itemCount = itemGroup->GetListItemCount();
1488 return itemCount == 0 ? 1 : itemCount;
1489 }
1490
ResetLayoutItem(LayoutWrapper * layoutWrapper)1491 void ListLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper)
1492 {
1493 for (auto& pos : recycledItemPosition_) {
1494 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1495 pos.second.startPos -= currentOffset_;
1496 pos.second.endPos -= currentOffset_;
1497 if (pos.second.isGroup) {
1498 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1499 if (wrapper && wrapper->GetHostNode() && wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1500 auto groupPattern = wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1501 groupPattern->ClearItemPosition();
1502 }
1503 } else {
1504 pos.second.groupInfo.reset();
1505 }
1506 auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper);
1507 if (wrapperFrameNode) {
1508 wrapperFrameNode->ClearSubtreeLayoutAlgorithm();
1509 }
1510 }
1511 }
1512
Layout(LayoutWrapper * layoutWrapper)1513 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1514 {
1515 auto listProps = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1516 CHECK_NULL_VOID(listProps);
1517 auto axis_ = listProps->GetListDirection().value_or(Axis::VERTICAL);
1518 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1519 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1520 MinusPaddingToSize(padding, size);
1521 paddingOffset_ = padding.Offset();
1522 float crossSize = GetCrossAxisSize(size, axis_);
1523 totalItemCount_ = layoutWrapper->GetTotalChildCount();
1524 listItemAlign_ = listProps->GetListItemAlign().value_or(V2::ListItemAlign::START);
1525 int32_t startIndex = GetStartIndex();
1526 isReverse_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
1527
1528 totalOffset_ += currentOffset_;
1529 FixPredictSnapPos();
1530 FixPredictSnapOffset(listProps);
1531 // layout items.
1532 int32_t itemCount = 0;
1533 for (auto& pos : itemPosition_) {
1534 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1535 if (!wrapper) {
1536 LOGI("wrapper is out of boundary");
1537 continue;
1538 }
1539 pos.second.startPos -= currentOffset_;
1540 pos.second.endPos -= currentOffset_;
1541 if (pos.second.isGroup) {
1542 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper);
1543 itemCount += GetListItemGroupItemCount(wrapper);
1544 } else {
1545 pos.second.groupInfo.reset();
1546 itemCount++;
1547 }
1548 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1549 if (expandSafeArea_ || wrapper->CheckNeedForceMeasureAndLayout()) {
1550 wrapper->Layout();
1551 } else {
1552 SyncGeometry(wrapper);
1553 }
1554 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
1555 if (frameNode) {
1556 frameNode->MarkAndCheckNewOpIncNode();
1557 }
1558 }
1559 auto cacheCount = listProps->GetCachedCountWithDefault();
1560 if (!listProps->HasCachedCount()) {
1561 int32_t newCacheCount = UpdateDefaultCachedCount(cacheCount, itemCount);
1562 listProps->SetDefaultCachedCount(newCacheCount);
1563 }
1564 ProcessCacheCount(layoutWrapper, cacheCount, listProps->GetShowCachedItemsValue(false));
1565 isLayouted_ = true;
1566 UpdateOverlay(layoutWrapper);
1567 }
1568
UpdateOverlay(LayoutWrapper * layoutWrapper)1569 void ListLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper)
1570 {
1571 auto frameNode = layoutWrapper->GetHostNode();
1572 CHECK_NULL_VOID(frameNode);
1573 auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>();
1574 CHECK_NULL_VOID(paintProperty);
1575 if (!paintProperty->GetFadingEdge().value_or(false)) {
1576 return;
1577 }
1578 auto overlayNode = frameNode->GetOverlayNode();
1579 CHECK_NULL_VOID(overlayNode);
1580 auto geometryNode = frameNode->GetGeometryNode();
1581 CHECK_NULL_VOID(geometryNode);
1582 auto listFrameSize = geometryNode->GetFrameSize();
1583 auto overlayGeometryNode = overlayNode->GetGeometryNode();
1584 CHECK_NULL_VOID(overlayGeometryNode);
1585 overlayGeometryNode->SetFrameSize(listFrameSize);
1586 }
1587
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1588 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1589 {
1590 float delta = crossSize - GetLaneGutter() - childCrossSize;
1591 if (LessOrEqual(delta, 0)) {
1592 return 0.0f;
1593 }
1594 switch (listItemAlign_) {
1595 case OHOS::Ace::V2::ListItemAlign::START:
1596 return 0.0f;
1597 case OHOS::Ace::V2::ListItemAlign::CENTER:
1598 return delta / 2.0f;
1599 case OHOS::Ace::V2::ListItemAlign::END:
1600 return delta;
1601 default:
1602 LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_);
1603 return 0.0f;
1604 }
1605 }
1606
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1607 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1608 {
1609 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1610 return;
1611 }
1612 auto host = layoutWrapper->GetHostNode();
1613 CHECK_NULL_VOID(host);
1614 auto focusHub = host->GetFocusHub();
1615 CHECK_NULL_VOID(focusHub);
1616 // textField not in List
1617 if (!focusHub->IsCurrentFocus()) {
1618 return;
1619 }
1620 auto context = PipelineContext::GetCurrentContext();
1621 CHECK_NULL_VOID(context);
1622 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1623 CHECK_NULL_VOID(textFieldManager);
1624 // only when textField is onFocus
1625 auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1626 CHECK_NULL_VOID(textField);
1627 auto textFieldHost = textField->GetHost();
1628 CHECK_NULL_VOID(textFieldHost);
1629 auto textBase = DynamicCast<TextBase>(textField);
1630 CHECK_NULL_VOID(textBase);
1631 auto caretPos = textFieldHost->GetTransformRelativeOffset().GetY() + textBase->GetCaretRect().Bottom();
1632 auto globalOffset = host->GetTransformRelativeOffset();
1633 auto offset = contentMainSize_ + globalOffset.GetY() - caretPos - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
1634 if (LessOrEqual(offset, 0.0)) {
1635 // negative offset to scroll down
1636 currentDelta_ -= static_cast<float>(offset);
1637 }
1638 }
1639
SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm> & itemGroup,bool forwardLayout,int32_t index)1640 void ListLayoutAlgorithm::SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm>& itemGroup,
1641 bool forwardLayout, int32_t index)
1642 {
1643 if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1644 if (!jumpIndexInGroup_.has_value()) {
1645 if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1646 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1647 jumpIndexInGroup_ = 0;
1648 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1649 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1650 jumpIndexInGroup_ = LAST_ITEM;
1651 }
1652 }
1653
1654 if (jumpIndexInGroup_.has_value()) {
1655 itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1656 itemGroup->SetScrollAlign(scrollAlign_);
1657 jumpIndexInGroup_.reset();
1658 }
1659 }
1660 }
1661
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout,bool needAdjustRefPos)1662 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1663 float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout,
1664 bool needAdjustRefPos)
1665 {
1666 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1667 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1668 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1669 CHECK_NULL_VOID(itemGroup);
1670 if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1671 referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1672 }
1673 if (jumpIndex_) {
1674 itemGroup->ClearItemPosition();
1675 }
1676 if (forwardLayout) {
1677 float endPos = layoutEndMainPos_.value_or(endMainPos_);
1678 float startPos = endPos - contentMainSize_;
1679 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1680 } else {
1681 float startPos = layoutStartMainPos_.value_or(startMainPos_);
1682 float endPos = startPos + contentMainSize_;
1683 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout);
1684 }
1685 bool needMeasureFormLastItem = index < preStartIndex_;
1686 itemGroup->SetNeedMeasureFormLastItem(needMeasureFormLastItem);
1687 itemGroup->SetNeedAdjustRefPos(needAdjustRefPos);
1688 itemGroup->SetListLayoutProperty(layoutProperty);
1689 itemGroup->SetNeedCheckOffset(isNeedCheckOffset_);
1690 if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER) {
1691 itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
1692 }
1693 SetListItemGroupJumpIndex(itemGroup, forwardLayout, index);
1694
1695 if (groupNeedAllLayout || (targetIndex_ && targetIndex_.value() == index) ||
1696 (scrollSnapAlign_ != V2::ScrollSnapAlign::NONE && !childrenSize_)) {
1697 itemGroup->SetNeedAllLayout();
1698 } else if (forwardFeature_ || backwardFeature_) {
1699 itemGroup->CheckNeedAllLayout(layoutWrapper, forwardLayout);
1700 }
1701 if (CheckNeedMeasure(layoutWrapper)) {
1702 itemGroup->ResetCachedItemPosition();
1703 itemGroup->ResetCachedIndex();
1704 if (layoutWrapper->GetHostNode() && layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) {
1705 auto groupPattern = layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
1706 groupPattern->SetRecache(true);
1707 }
1708 }
1709 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1710 }
1711
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1712 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1713 {
1714 int32_t id = layoutWrapper->GetHostNode()->GetId();
1715 ListItemInfo pos = { id, 0, 0, true };
1716 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1717 CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
1718 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1719 CHECK_NULL_RETURN(itemGroup, pos);
1720 auto res = itemGroup->GetItemGroupPosition(index);
1721 return { id, res.first, res.second, true };
1722 }
1723
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1724 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1725 {
1726 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1727 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
1728 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1729 CHECK_NULL_RETURN(itemGroup, 0.0f);
1730 return itemGroup->GetItemHeight(index);
1731 }
1732
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1733 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1734 {
1735 auto host = layoutWrapper->GetHostNode();
1736 CHECK_NULL_VOID(host);
1737 auto listItem = host->GetPattern<ListItemPattern>();
1738 if (listItem) {
1739 listItem->SetIndexInList(index);
1740 return;
1741 }
1742 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1743 CHECK_NULL_VOID(listItemGroup);
1744 listItemGroup->SetIndexInList(index);
1745 }
1746
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1747 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1748 float referencePos, bool forwardLayout) const
1749 {
1750 if (targetIndex_.has_value()) {
1751 return;
1752 }
1753 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1754 CHECK_NULL_VOID(wrapper);
1755 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1756 CHECK_NULL_VOID(algorithmWrapper);
1757 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1758 CHECK_NULL_VOID(itemGroup);
1759 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1760 }
1761
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1762 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
1763 bool forwardLayout)
1764 {
1765 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1766 CHECK_NULL_VOID(wrapper);
1767 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(true);
1768 CHECK_NULL_VOID(algorithmWrapper);
1769 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1770 CHECK_NULL_VOID(itemGroup);
1771 if (forwardLayout) {
1772 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, prevContentMainSize_,
1773 !forwardLayout);
1774 } else {
1775 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, prevContentMainSize_,
1776 !forwardLayout);
1777 }
1778 itemGroup->SetScrollAlign(ScrollAlign::NONE);
1779 wrapper->Measure(GetGroupLayoutConstraint());
1780 if (childrenSize_) {
1781 return;
1782 }
1783 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1784 auto& pos = itemPosition_[index];
1785 if (forwardLayout) {
1786 pos.startPos = pos.endPos - mainLen;
1787 } else {
1788 pos.endPos = pos.startPos + mainLen;
1789 }
1790 }
1791
OffScreenLayoutDirection(LayoutWrapper * layoutWrapper)1792 void ListLayoutAlgorithm::OffScreenLayoutDirection(LayoutWrapper* layoutWrapper)
1793 {
1794 if (!targetIndex_ || itemPosition_.empty()) {
1795 forwardFeature_ = false;
1796 backwardFeature_ = false;
1797 return;
1798 }
1799 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_);
1800 if (layoutDirection == LayoutDirection::BACKWARD) {
1801 forwardFeature_ = false;
1802 backwardFeature_ = true;
1803 } else {
1804 forwardFeature_ = true;
1805 backwardFeature_ = false;
1806 }
1807 }
1808
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1809 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1810 {
1811 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1812 float midPos = contentSize / 2.0f;
1813 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1814 GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
1815 midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
1816 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1817 LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
1818 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1819 midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
1820 }
1821 for (auto& pos : itemPosition_) {
1822 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1823 return pos.first;
1824 }
1825 }
1826 return totalItemCount_ - 1;
1827 }
1828
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1829 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1830 {
1831 CHECK_NULL_VOID(wrapper);
1832 auto host = wrapper->GetHostNode();
1833 CHECK_NULL_VOID(host);
1834 host->ForceSyncGeometryNode();
1835 host->ResetLayoutAlgorithm();
1836 host->RebuildRenderContextTree();
1837 }
1838
LayoutCachedALine(LayoutWrapper * layoutWrapper,int32_t index,bool forward,float & currPos,float crossSize)1839 bool ListLayoutAlgorithm::LayoutCachedALine(LayoutWrapper* layoutWrapper, int32_t index,
1840 bool forward, float &currPos, float crossSize)
1841 {
1842 auto wrapper = layoutWrapper->GetChildByIndex(index, true);
1843 if (!wrapper) {
1844 return true;
1845 }
1846 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1847 if (CheckNeedMeasure(wrapper)) {
1848 return !isGroup;
1849 }
1850 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1851 int32_t id = wrapper->GetHostNode()->GetId();
1852 ListItemInfo pos;
1853 if (forward) {
1854 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1855 pos = { id, currPos, endPos, isGroup };
1856 currPos = endPos + spaceWidth_;
1857 } else {
1858 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1859 pos = { id, startPos, currPos, isGroup };
1860 currPos = startPos - spaceWidth_;
1861 }
1862 auto startIndex = index;
1863 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1864 SyncGeometry(wrapper);
1865 wrapper->SetActive(false);
1866 return false;
1867 }
1868
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1869 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1870 {
1871 std::list<int32_t> predictBuildList;
1872 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1873 float crossSize = GetCrossAxisSize(size, axis_);
1874
1875 auto currIndex = itemPosition_.rbegin()->first + 1;
1876 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1877 for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1878 int32_t index = currIndex + i;
1879 if (LayoutCachedALine(layoutWrapper, index, true, currPos, crossSize)) {
1880 predictBuildList.emplace_back(index);
1881 }
1882 }
1883
1884 currIndex = itemPosition_.begin()->first - 1;
1885 currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1886 for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1887 int32_t index = currIndex - i;
1888 if (LayoutCachedALine(layoutWrapper, index, false, currPos, crossSize)) {
1889 predictBuildList.emplace_back(index);
1890 }
1891 }
1892 return predictBuildList;
1893 }
1894
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1895 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1896 {
1897 CHECK_NULL_RETURN(wrapper, false);
1898 wrapper->SetActive(false);
1899 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1900 if (!isGroup) {
1901 auto frameNode = wrapper->GetHostNode();
1902 CHECK_NULL_RETURN(frameNode, false);
1903 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1904 FrameNode::ProcessOffscreenNode(frameNode);
1905 return true;
1906 }
1907 return false;
1908 }
1909
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1910 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1911 {
1912 CHECK_NULL_VOID(frameNode);
1913 auto pattern = frameNode->GetPattern<ListPattern>();
1914 CHECK_NULL_VOID(pattern);
1915 if (pattern->GetPredictLayoutParam()) {
1916 pattern->SetPredictLayoutParam(param);
1917 return;
1918 }
1919 pattern->SetPredictLayoutParam(param);
1920 auto context = PipelineContext::GetCurrentContext();
1921 CHECK_NULL_VOID(context);
1922 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1923 ACE_SCOPED_TRACE("List predict");
1924 auto frameNode = weak.Upgrade();
1925 CHECK_NULL_VOID(frameNode);
1926 auto pattern = frameNode->GetPattern<ListPattern>();
1927 CHECK_NULL_VOID(pattern);
1928 if (!pattern->GetPredictLayoutParam().has_value()) {
1929 return;
1930 }
1931 bool needMarkDirty = false;
1932 auto param = pattern->GetPredictLayoutParam().value();
1933 for (auto it = param.items.begin(); it != param.items.end();) {
1934 if (GetSysTimestamp() > deadline) {
1935 break;
1936 }
1937 auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false, true);
1938 if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
1939 break;
1940 }
1941 needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty;
1942 it = param.items.erase(it);
1943 }
1944 if (needMarkDirty) {
1945 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1946 }
1947 pattern->SetPredictLayoutParam(std::nullopt);
1948 if (!param.items.empty()) {
1949 ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1950 pattern->SetPredictLayoutParam(param);
1951 }
1952 });
1953 }
1954
1955 // return current CachedCount and max CacheCount
GetLayoutGroupCachedCount(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & wrapper,int32_t forwardCache,int32_t backwardCache,int32_t index,bool outOfView)1956 CachedIndexInfo ListLayoutAlgorithm::GetLayoutGroupCachedCount(LayoutWrapper* layoutWrapper,
1957 const RefPtr<LayoutWrapper>& wrapper, int32_t forwardCache, int32_t backwardCache, int32_t index, bool outOfView)
1958 {
1959 CachedIndexInfo res;
1960 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
1961 CHECK_NULL_RETURN(groupNode, res);
1962 auto group = groupNode->GetPattern<ListItemGroupPattern>();
1963 CHECK_NULL_RETURN(group, res);
1964 const auto& itemPos = group->GetItemPosition();
1965 bool reCache = false;
1966 if (outOfView && recycledItemPosition_.count(index) == 0) {
1967 reCache = CheckNeedMeasure(wrapper);
1968 } else if (outOfView) {
1969 wrapper->SetActive(true);
1970 wrapper->Layout();
1971 group->SyncItemsToCachedItemPosition();
1972 }
1973 bool forward = forwardCache > -1;
1974 bool backward = backwardCache > -1;
1975 if (forward && backward && itemPos.empty()) {
1976 forward = group->NeedCacheForward(layoutWrapper);
1977 backward = !forward;
1978 forwardCache = forward ? forwardCache : -1;
1979 backwardCache = backward ? backwardCache : -1;
1980 }
1981 res = group->UpdateCachedIndex(outOfView, reCache, forwardCache, backwardCache);
1982 if ((group->GetTotalItemCount() == 0 && outOfView) || !group->IsVisible()) {
1983 if (groupNode->CheckNeedForceMeasureAndLayout()) {
1984 res = {0, 0, 1, 1};
1985 } else {
1986 res = {1, 1, 1, 1};
1987 }
1988 }
1989 ACE_SCOPED_TRACE("GetLayoutGroupCachedCount forward:%d, %d, backward:%d, %d",
1990 res.forwardCachedCount, res.forwardCacheMax, res.backwardCachedCount, res.backwardCacheMax);
1991 return res;
1992 }
1993
GetLayoutCrossAxisSize(LayoutWrapper * layoutWrapper)1994 float ListLayoutAlgorithm::GetLayoutCrossAxisSize(LayoutWrapper* layoutWrapper)
1995 {
1996 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1997 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1998 MinusPaddingToSize(padding, size);
1999 return GetCrossAxisSize(size, axis_);
2000 }
2001
LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2002 int32_t ListLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper,
2003 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2004 {
2005 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2006 curIndex = itemPosition_.rbegin()->first + 1;
2007 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
2008 while (cachedCount < cacheCount && curIndex < totalItemCount_) {
2009 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
2010 if (!wrapper) {
2011 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2012 return curIndex - 1;
2013 }
2014 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2015 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2016 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
2017 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2018 }
2019 if (!isGroup && isDirty) {
2020 return curIndex - 1;
2021 }
2022 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2023 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
2024 int32_t id = wrapper->GetHostNode()->GetId();
2025 ListItemInfo pos = { id, currPos, endPos, isGroup };
2026 currPos = endPos + spaceWidth_;
2027 auto startIndex = curIndex;
2028 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2029 cachedItemPosition_[curIndex] = pos;
2030 if (isGroup) {
2031 auto res = GetLayoutGroupCachedCount(
2032 layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true);
2033 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) {
2034 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 });
2035 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1;
2036 }
2037 cachedCount += std::max(res.forwardCacheMax, 1);
2038 } else {
2039 cachedCount++;
2040 }
2041 SyncGeometry(wrapper);
2042 wrapper->SetActive(false);
2043 curIndex++;
2044 }
2045 return curIndex - 1;
2046 }
2047
LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2048 int32_t ListLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper,
2049 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show)
2050 {
2051 float crossSize = GetLayoutCrossAxisSize(layoutWrapper);
2052 curIndex = itemPosition_.begin()->first - 1;
2053 auto currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
2054 while (cachedCount < cacheCount && curIndex >= 0) {
2055 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show);
2056 if (!wrapper) {
2057 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2058 return curIndex + 1;
2059 }
2060 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2061 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper);
2062 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) {
2063 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2064 }
2065 if (!isGroup && isDirty) {
2066 return curIndex + 1;
2067 }
2068 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
2069 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
2070 int32_t id = wrapper->GetHostNode()->GetId();
2071 ListItemInfo pos = { id, startPos, currPos, isGroup };
2072 currPos = startPos - spaceWidth_;
2073 auto startIndex = curIndex;
2074 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize);
2075 cachedItemPosition_[curIndex] = pos;
2076 if (isGroup) {
2077 auto res = GetLayoutGroupCachedCount(
2078 layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true);
2079 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) {
2080 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount });
2081 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1;
2082 }
2083 cachedCount += std::max(res.backwardCacheMax, 1);
2084 } else {
2085 cachedCount++;
2086 }
2087 SyncGeometry(wrapper);
2088 wrapper->SetActive(false);
2089 curIndex--;
2090 }
2091 return curIndex + 1;
2092 }
2093
LayoutCachedItemInEdgeGroup(LayoutWrapper * layoutWrapper,int32_t cacheCount,std::list<PredictLayoutItem> & predictList)2094 std::tuple<int32_t, int32_t, int32_t, int32_t> ListLayoutAlgorithm::LayoutCachedItemInEdgeGroup(
2095 LayoutWrapper* layoutWrapper, int32_t cacheCount, std::list<PredictLayoutItem>& predictList)
2096 {
2097 int32_t startIndex = GetStartIndex();
2098 int32_t endIndex = GetEndIndex();
2099 int32_t cachedForward = 0;
2100 int32_t cachedBackward = 0;
2101 if (startIndex == endIndex && itemPosition_.begin()->second.isGroup) {
2102 auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2103 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, cacheCount, startIndex, false);
2104 if ((res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) ||
2105 (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount)) {
2106 int32_t forwardCached = res.forwardCacheMax > 0 ? cachedForward : -1;
2107 int32_t backwardCached = res.backwardCacheMax > 0 ? cachedBackward : -1;
2108 predictList.emplace_back(PredictLayoutItem { startIndex, forwardCached, backwardCached });
2109 }
2110 cachedForward += res.forwardCacheMax;
2111 cachedBackward += res.backwardCacheMax;
2112 } else {
2113 if (itemPosition_.rbegin()->second.isGroup) {
2114 auto wrapper = layoutWrapper->GetChildByIndex(endIndex);
2115 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, -1, endIndex, false);
2116 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) {
2117 predictList.emplace_back(PredictLayoutItem { endIndex, cachedForward, -1 });
2118 }
2119 cachedForward += res.forwardCacheMax;
2120 }
2121 if (itemPosition_.begin()->second.isGroup) {
2122 auto wrapper = layoutWrapper->GetChildByIndex(startIndex);
2123 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount, startIndex, false);
2124 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount) {
2125 predictList.emplace_back(PredictLayoutItem { startIndex, -1, cachedBackward });
2126 }
2127 cachedBackward += res.backwardCacheMax;
2128 }
2129 }
2130 return { startIndex, endIndex, cachedForward, cachedBackward };
2131 }
2132
LayoutCachedItemV2(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)2133 std::list<PredictLayoutItem> ListLayoutAlgorithm::LayoutCachedItemV2(LayoutWrapper* layoutWrapper, int32_t cacheCount,
2134 bool show)
2135 {
2136 ACE_SCOPED_TRACE("LayoutCachedItemV2");
2137 std::list<PredictLayoutItem> predictBuildList;
2138 auto [startIndex, endIndex, cachedForward, cachedBackward] =
2139 LayoutCachedItemInEdgeGroup(layoutWrapper, cacheCount, predictBuildList);
2140 if (cachedForward < cacheCount && endIndex < totalItemCount_ - 1) {
2141 endIndex = LayoutCachedForward(layoutWrapper, cacheCount, cachedForward, endIndex, predictBuildList, show);
2142 }
2143 if (cachedBackward < cacheCount && startIndex > 0) {
2144 startIndex =
2145 LayoutCachedBackward(layoutWrapper, cacheCount, cachedBackward, startIndex, predictBuildList, show);
2146 }
2147 int32_t cacheStart = itemPosition_.begin()->first - startIndex;
2148 int32_t cacheEnd = endIndex - itemPosition_.rbegin()->first;
2149 ResetLayoutItem(layoutWrapper);
2150 SetActiveChildRange(layoutWrapper, cacheStart, cacheEnd, show);
2151 return predictBuildList;
2152 }
2153
PredictBuildGroup(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,const ListMainSizeValues & listMainSizeValues)2154 bool ListLayoutAlgorithm::PredictBuildGroup(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint,
2155 int64_t deadline, int32_t forwardCached, int32_t backwardCached, const ListMainSizeValues& listMainSizeValues)
2156 {
2157 CHECK_NULL_RETURN(wrapper, false);
2158 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper);
2159 CHECK_NULL_RETURN(groupNode, false);
2160 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
2161 CHECK_NULL_RETURN(groupPattern, false);
2162 float referencePos = 0.0f;
2163 if (listMainSizeValues.jumpIndexInGroup.has_value() && listMainSizeValues.scrollAlign == ScrollAlign::CENTER) {
2164 referencePos = (listMainSizeValues.startPos + listMainSizeValues.endPos) / 2; // 2:average
2165 }
2166 float endPos = 0.0f;
2167 float startPos = 0.0f;
2168 if (listMainSizeValues.forward) {
2169 startPos = listMainSizeValues.startPos;
2170 endPos = listMainSizeValues.layoutEndMainPos.value_or(listMainSizeValues.endPos);
2171 } else {
2172 startPos = listMainSizeValues.layoutStartMainPos.value_or(listMainSizeValues.startPos);
2173 endPos = listMainSizeValues.endPos;
2174 }
2175 ListMainSizeValues values;
2176 values.startPos = startPos;
2177 values.endPos = endPos;
2178 values.referencePos = referencePos;
2179 values.prevContentMainSize = listMainSizeValues.prevContentMainSize;
2180 values.forward = listMainSizeValues.forward;
2181 values.backward = listMainSizeValues.backward;
2182 groupPattern->LayoutCache(constraint, deadline, forwardCached, backwardCached, values);
2183 return true;
2184 }
2185
PredictBuildV2(RefPtr<FrameNode> frameNode,int64_t deadline,ListMainSizeValues listMainSizeValues,bool show)2186 void ListLayoutAlgorithm::PredictBuildV2(
2187 RefPtr<FrameNode> frameNode, int64_t deadline, ListMainSizeValues listMainSizeValues, bool show)
2188 {
2189 ACE_SCOPED_TRACE("List predict v2");
2190 CHECK_NULL_VOID(frameNode);
2191 auto pattern = frameNode->GetPattern<ListPattern>();
2192 CHECK_NULL_VOID(pattern);
2193 if (!pattern->GetPredictLayoutParamV2().has_value()) {
2194 return;
2195 }
2196 bool needMarkDirty = false;
2197 auto param = pattern->GetPredictLayoutParamV2().value();
2198 for (auto it = param.items.begin(); it != param.items.end();) {
2199 if (GetSysTimestamp() > deadline) {
2200 break;
2201 }
2202 ACE_SCOPED_TRACE("predict Item:%d", (*it).index);
2203 auto wrapper = frameNode->GetOrCreateChildByIndex((*it).index, show, true);
2204 if (!wrapper) {
2205 it = param.items.erase(it);
2206 continue;
2207 }
2208 if (wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
2209 break;
2210 }
2211 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
2212 if (!isGroup) {
2213 auto frameNode = wrapper->GetHostNode();
2214 CHECK_NULL_VOID(frameNode);
2215 frameNode->GetGeometryNode()->SetParentLayoutConstraint(param.layoutConstraint);
2216 FrameNode::ProcessOffscreenNode(frameNode);
2217 } else {
2218 listMainSizeValues.forward = (*it).forwardCacheCount > -1;
2219 listMainSizeValues.backward = (*it).backwardCacheCount > -1;
2220 PredictBuildGroup(wrapper, param.groupLayoutConstraint, deadline, (*it).forwardCacheCount,
2221 (*it).backwardCacheCount, listMainSizeValues);
2222 }
2223 needMarkDirty = true;
2224 it = param.items.erase(it);
2225 }
2226 if (needMarkDirty) {
2227 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
2228 }
2229 pattern->SetPredictLayoutParamV2(std::nullopt);
2230 if (!param.items.empty()) {
2231 ListLayoutAlgorithm::PostIdleTaskV2(frameNode, param, listMainSizeValues, show);
2232 }
2233 }
2234
PostIdleTaskV2(RefPtr<FrameNode> frameNode,const ListPredictLayoutParamV2 & param,ListMainSizeValues listMainSizeValues,bool show)2235 void ListLayoutAlgorithm::PostIdleTaskV2(RefPtr<FrameNode> frameNode,
2236 const ListPredictLayoutParamV2& param, ListMainSizeValues listMainSizeValues, bool show)
2237 {
2238 ACE_SCOPED_TRACE("PostIdleTaskV2");
2239 CHECK_NULL_VOID(frameNode);
2240 auto pattern = frameNode->GetPattern<ListPattern>();
2241 CHECK_NULL_VOID(pattern);
2242 if (pattern->GetPredictLayoutParamV2()) {
2243 pattern->SetPredictLayoutParamV2(param);
2244 return;
2245 }
2246 pattern->SetPredictLayoutParamV2(param);
2247 auto context = PipelineContext::GetCurrentContext();
2248 CHECK_NULL_VOID(context);
2249 context->AddPredictTask(
2250 [weak = WeakClaim(RawPtr(frameNode)), value = listMainSizeValues, show = show](int64_t deadline,
2251 bool canUseLongPredictTask) { ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, value, show); });
2252 }
2253
GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const2254 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const
2255 {
2256 float stopOnScreen = 0;
2257 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2258 stopOnScreen = contentStartOffset_;
2259 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2260 stopOnScreen = contentMainSize_ / 2.0f;
2261 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2262 stopOnScreen = contentMainSize_ - contentEndOffset_;
2263 }
2264 return stopOnScreen;
2265 }
2266
FindPredictSnapIndexInItemPositionsStart(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2267 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsStart(
2268 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2269 {
2270 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::START);
2271 float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
2272 for (const auto& positionInfo : itemPosition_) {
2273 auto startPos = positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
2274 itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2275 auto endPos = positionInfo.second.startPos + itemHeight / 2.0f;
2276 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2277 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2278 endIndex = positionInfo.first;
2279 }
2280 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2281 currIndex = positionInfo.first;
2282 }
2283 if (endIndex >= 0 && currIndex >= 0) {
2284 break;
2285 }
2286 }
2287 }
2288
FindPredictSnapIndexInItemPositionsCenter(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2289 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsCenter(
2290 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2291 {
2292 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::CENTER);
2293 for (const auto& positionInfo : itemPosition_) {
2294 auto startPos = positionInfo.second.startPos - spaceWidth_ / 2.0f;
2295 auto endPos = positionInfo.second.endPos + spaceWidth_ / 2.0f;
2296 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2297 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2298 endIndex = positionInfo.first;
2299 }
2300 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2301 currIndex = positionInfo.first;
2302 }
2303 if (endIndex >= 0 && currIndex >= 0) {
2304 break;
2305 }
2306 }
2307 }
2308
FindPredictSnapIndexInItemPositionsEnd(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2309 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsEnd(
2310 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const
2311 {
2312 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::END);
2313 float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
2314 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2315 auto endPos = pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
2316 itemHeight = pos->second.endPos - pos->second.startPos;
2317 auto startPos = pos->second.endPos - itemHeight / 2.0f;
2318 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) &&
2319 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) {
2320 endIndex = pos->first;
2321 }
2322 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) {
2323 currIndex = pos->first;
2324 }
2325 if (endIndex >= 0 && currIndex >= 0) {
2326 break;
2327 }
2328 }
2329 }
2330
FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)2331 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
2332 float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign)
2333 {
2334 int32_t endIndex = -1;
2335 int32_t currIndex = -1;
2336
2337 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2338 FindPredictSnapIndexInItemPositionsStart(predictEndPos, endIndex, currIndex);
2339 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2340 FindPredictSnapIndexInItemPositionsCenter(predictEndPos, endIndex, currIndex);
2341 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2342 FindPredictSnapIndexInItemPositionsEnd(predictEndPos, endIndex, currIndex);
2343 }
2344 if (endIndex == currIndex && currIndex >= 0) {
2345 if (scrollSnapVelocity_ < -SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2346 endIndex = std::min(GetEndIndex(), endIndex + 1);
2347 } else if (scrollSnapVelocity_ > SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) {
2348 endIndex = std::min(GetStartIndex(), endIndex - 1);
2349 }
2350 }
2351 return endIndex;
2352 }
2353
IsUniformHeightProbably()2354 bool ListLayoutAlgorithm::IsUniformHeightProbably()
2355 {
2356 bool isUniformHeightProbably = true;
2357 float itemHeight = 0.0f;
2358 float currentItemHeight = 0.0f;
2359 for (const auto& positionInfo : itemPosition_) {
2360 currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
2361 if (NearZero(itemHeight)) {
2362 itemHeight = currentItemHeight;
2363 } else if (!NearEqual(currentItemHeight, itemHeight)) {
2364 isUniformHeightProbably = false;
2365 break;
2366 }
2367 }
2368 return isUniformHeightProbably;
2369 }
2370
CalculatePredictSnapEndPositionByIndex(int32_t index,V2::ScrollSnapAlign scrollSnapAlign)2371 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(int32_t index, V2::ScrollSnapAlign scrollSnapAlign)
2372 {
2373 float predictSnapEndPos = 0;
2374 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
2375 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
2376 float endPos = GetEndPosition();
2377 float itemTotalSize = endPos - GetStartPosition();
2378 float contentSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
2379 if ((GetEndIndex() == totalItemCount_ - 1) && GreatNotEqual(itemTotalSize, contentSize) &&
2380 GreatNotEqual(predictSnapEndPos + contentMainSize_ - contentEndOffset_, totalOffset_ + endPos)) {
2381 predictSnapEndPos = totalOffset_ + endPos - contentMainSize_ + contentEndOffset_;
2382 }
2383 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
2384 float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
2385 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
2386 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
2387 predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
2388 if (GetStartIndex() == 0 && LessNotEqual(predictSnapEndPos, totalOffset_ + GetStartPosition())) {
2389 predictSnapEndPos = totalOffset_ + GetStartPosition() - contentStartOffset_;
2390 }
2391 }
2392 return predictSnapEndPos;
2393 }
2394
GetSnapStartIndexAndPos()2395 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapStartIndexAndPos()
2396 {
2397 int32_t startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
2398 float startPos = itemPosition_.begin()->second.startPos;
2399 for (auto& pos : itemPosition_) {
2400 if (NearEqual(pos.second.startPos, prevContentStartOffset_)) {
2401 startIndex = pos.first;
2402 startPos = itemPosition_[startIndex].startPos + contentStartOffset_ - prevContentStartOffset_;
2403 break;
2404 } else if (GreatNotEqual(pos.second.startPos, prevContentStartOffset_)) {
2405 if ((GetEndIndex() == totalItemCount_ - 1) &&
2406 NearEqual(GetEndPosition(), prevContentMainSize_ - prevContentEndOffset_) && !canOverScroll_) {
2407 startIndex = pos.first;
2408 startPos = contentStartOffset_;
2409 adjustOffset_ = pos.second.startPos - prevContentStartOffset_;
2410 }
2411 break;
2412 }
2413 }
2414 return std::make_pair(std::min(startIndex, totalItemCount_ -1), startPos);
2415 }
2416
GetSnapEndIndexAndPos()2417 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapEndIndexAndPos()
2418 {
2419 int32_t endIndex = -1;
2420 float endPos = 0.0f;
2421 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
2422 if (NearEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2423 endIndex = pos->first;
2424 endPos = itemPosition_[endIndex].endPos - contentEndOffset_ + prevContentEndOffset_;
2425 break;
2426 } else if (GreatNotEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) {
2427 if ((GetStartIndex() == 0) && NearEqual(GetStartPosition(), prevContentStartOffset_) && !canOverScroll_) {
2428 endIndex = pos->first;
2429 endPos = prevContentMainSize_ - contentEndOffset_;
2430 adjustOffset_ = pos->second.endPos + prevContentEndOffset_ - prevContentMainSize_;
2431 }
2432 break;
2433 }
2434 }
2435 return std::make_pair(std::min(endIndex, totalItemCount_ -1), endPos);
2436 }
2437
UpdateDefaultCachedCount(const int32_t oldCacheCount,const int32_t itemCount)2438 int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount)
2439 {
2440 if (itemCount <= 0) {
2441 return oldCacheCount;
2442 }
2443 static float pageCount = SystemProperties::GetPageCount();
2444 if (pageCount <= 0.0f) {
2445 return oldCacheCount;
2446 }
2447 constexpr int32_t MAX_DEFAULT_CACHED_COUNT = 16;
2448 int32_t newCachedCount = static_cast<int32_t>(ceil(pageCount * itemCount));
2449 if (newCachedCount > MAX_DEFAULT_CACHED_COUNT) {
2450 TAG_LOGI(AceLogTag::ACE_LIST, "Default cachedCount exceed 16");
2451 return MAX_DEFAULT_CACHED_COUNT;
2452 } else {
2453 return std::max(newCachedCount, oldCacheCount);
2454 }
2455 }
2456 } // namespace OHOS::Ace::NG
2457