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