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 <unordered_set>
20
21 #include "base/geometry/axis.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/log/ace_trace.h"
25 #include "base/memory/ace_type.h"
26 #include "base/utils/time_util.h"
27 #include "base/utils/utils.h"
28 #include "core/components/common/layout/layout_param.h"
29 #include "core/components_ng/base/frame_node.h"
30 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
32 #include "core/components_ng/pattern/list/list_item_pattern.h"
33 #include "core/components_ng/pattern/list/list_layout_property.h"
34 #include "core/components_ng/pattern/list/list_pattern.h"
35 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
36 #include "core/components_ng/pattern/text_field/text_field_manager.h"
37 #include "core/components_ng/property/layout_constraint.h"
38 #include "core/components_ng/property/measure_property.h"
39 #include "core/components_ng/property/measure_utils.h"
40 #include "core/components_ng/property/property.h"
41 #include "core/components_v2/inspector/inspector_constants.h"
42 #include "core/components_v2/list/list_properties.h"
43 #include "core/pipeline_ng/pipeline_context.h"
44
45 namespace OHOS::Ace::NG {
46
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)47 void ListLayoutAlgorithm::UpdateListItemConstraint(
48 Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
49 {
50 contentConstraint.parentIdealSize = selfIdealSize;
51 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
52 auto crossSize = selfIdealSize.CrossSize(axis);
53 if (crossSize.has_value()) {
54 contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
55 contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
56 }
57 }
58
Measure(LayoutWrapper * layoutWrapper)59 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
60 {
61 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
62 CHECK_NULL_VOID(listLayoutProperty);
63
64 const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value();
65
66 // calculate idealSize and set FrameSize
67 axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
68 auto startOffset = listLayoutProperty->GetContentStartOffset().value_or(0.0f);
69 contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0);
70 auto endOffset = listLayoutProperty->GetContentEndOffset().value_or(0.0f);
71 contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0);
72
73 // calculate main size.
74 auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value();
75
76 float expandHeight = ScrollableUtils::CheckHeightExpansion(listLayoutProperty, axis_);
77 contentEndOffset_ += expandHeight;
78 // expand contentSize
79 contentConstraint.MinusPadding(std::nullopt, std::nullopt, std::nullopt, -expandHeight);
80
81 auto contentIdealSize = CreateIdealSize(
82 contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
83
84 const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
85 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
86 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
87 contentMainSize_ = 0.0f;
88 totalItemCount_ = layoutWrapper->GetTotalChildCount();
89 if (!GetMainAxisSize(contentIdealSize, axis_)) {
90 if (totalItemCount_ == 0) {
91 contentMainSize_ = 0.0f;
92 } else {
93 // use parent max size first.
94 auto parentMaxSize = contentConstraint.maxSize;
95 contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_);
96 mainSizeIsDefined_ = false;
97 }
98 } else {
99 contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
100 mainSizeIsDefined_ = true;
101 }
102 if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, contentMainSize_)) {
103 contentStartOffset_ = 0;
104 contentEndOffset_ = 0;
105 }
106
107 if (totalItemCount_ > 0) {
108 OnSurfaceChanged(layoutWrapper);
109
110 stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
111 childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
112 auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
113 auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
114 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
115 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) {
116 spaceWidth_ = 0.0f;
117 }
118 if (listLayoutProperty->GetDivider().has_value()) {
119 auto divider = listLayoutProperty->GetDivider().value();
120 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
121 if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
122 dividerSpace.reset();
123 }
124 if (dividerSpace.has_value()) {
125 spaceWidth_ = std::max(spaceWidth_, static_cast<float>(Round(dividerSpace.value())));
126 }
127 }
128 spaceWidth_ += chainInterval_;
129 CheckJumpToIndex();
130 currentOffset_ = currentDelta_;
131 startMainPos_ = currentOffset_;
132 endMainPos_ = currentOffset_ + contentMainSize_;
133 CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
134 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
135 // calculate child layout constraint.
136 UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
137 MeasureList(layoutWrapper);
138 } else {
139 itemPosition_.clear();
140 layoutWrapper->RemoveAllChildInRenderTree();
141 }
142
143 auto crossSize = contentIdealSize.CrossSize(axis_);
144 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
145 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
146 crossMatchChild_ = true;
147 }
148 contentIdealSize.SetMainSize(contentMainSize_, axis_);
149 AddPaddingToSize(padding, contentIdealSize);
150
151 auto size = contentIdealSize.ConvertToSizeT();
152 // Cancel frame size expansion, only expand content size here.
153 // Frame expansion will be determined after Layout.
154 size.MinusHeight(expandHeight);
155 layoutWrapper->GetGeometryNode()->SetFrameSize(size);
156
157 // set list cache info.
158 SetCacheCount(layoutWrapper, listLayoutProperty->GetCachedCountValue(1));
159 }
160
SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)161 void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount)
162 {
163 layoutWrapper->SetCacheCount(cacheCount);
164 }
165
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const166 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
167 {
168 if (GetItemPosition().empty()) {
169 return 0.0f;
170 }
171 float maxCrossSize = 0.0f;
172 float crossSize = 0.0f;
173 float prevPos = GetItemPosition().begin()->second.startPos;
174 for (const auto& pos : GetItemPosition()) {
175 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
176 if (!wrapper) {
177 continue;
178 }
179 auto getGeometryNode = wrapper->GetGeometryNode();
180 if (!getGeometryNode) {
181 continue;
182 }
183 if (NearEqual(prevPos, pos.second.startPos)) {
184 crossSize += getGeometryNode->GetMarginFrameSize().CrossSize(axis);
185 } else {
186 crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
187 }
188 prevPos = pos.second.startPos;
189 maxCrossSize = std::max(crossSize, maxCrossSize);
190 }
191 return maxCrossSize;
192 }
193
ClearAllItemPosition(LayoutWrapper * layoutWrapper)194 void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper)
195 {
196 for (auto& pos : itemPosition_) {
197 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
198 if (!wrapper) {
199 continue;
200 }
201 auto algorithm = wrapper->GetLayoutAlgorithm(true);
202 if (!algorithm) {
203 continue;
204 }
205 auto groupAlgorithm = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithm->GetLayoutAlgorithm());
206 if (!groupAlgorithm) {
207 continue;
208 }
209 CHECK_NULL_VOID(groupAlgorithm);
210 groupAlgorithm->ClearItemPosition(&(*wrapper));
211 }
212 itemPosition_.clear();
213 layoutWrapper->RemoveAllChildInRenderTree();
214 }
215
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)216 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
217 {
218 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
219 LayoutForward(layoutWrapper, index, startPos);
220 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
221 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
222 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
223 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
224 }
225 }
226 }
227
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)228 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
229 {
230 int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value());
231 LayoutBackward(layoutWrapper, index, startPos);
232 if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
233 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
234 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
235 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
236 }
237 }
238 }
239
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t & startIndex,int32_t & endIndex,float & startPos,float & endPos)240 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper,
241 int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos)
242 {
243 bool isSmoothJump = false;
244 int32_t jumpIndex = 0;
245 if (jumpIndex_.has_value()) {
246 jumpIndex = jumpIndex_.value();
247 } else {
248 jumpIndex = targetIndex_.value();
249 isSmoothJump = true;
250 }
251 int32_t tempStartIndex = startIndex;
252 int32_t tempEndIndex = endIndex;
253 if (GreatNotEqual(GetLanes(), 1)) {
254 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
255 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
256 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
257 }
258 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
259 CHECK_NULL_VOID(wrapper);
260 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
261 if (!isGroup) {
262 if (jumpIndex >= tempEndIndex) {
263 scrollAutoType_ = ScrollAutoType::END;
264 if (!isSmoothJump) {
265 jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value());
266 startPos = contentMainSize_ - contentEndOffset_;
267 BeginLayoutBackward(startPos, layoutWrapper);
268 }
269 } else if (jumpIndex <= tempStartIndex) {
270 scrollAutoType_ = ScrollAutoType::START;
271 if (!isSmoothJump) {
272 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
273 startPos = contentStartOffset_;
274 BeginLayoutForward(startPos, layoutWrapper);
275 }
276 }
277 } else if (jumpIndexInGroup_) {
278 if (scrollAutoType_ == ScrollAutoType::START) {
279 scrollAlign_ = ScrollAlign::START;
280 HandleJumpStart(layoutWrapper);
281 } else if (scrollAutoType_ == ScrollAutoType::END) {
282 scrollAlign_ = ScrollAlign::END;
283 HandleJumpEnd(layoutWrapper);
284 }
285 } else if (jumpIndex <= tempStartIndex) {
286 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
287 SetListItemGroupParam(wrapper, jumpIndex, contentMainSize_, false, listLayoutProperty, false);
288 wrapper->Measure(childLayoutConstraint_);
289 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
290 if (GreatNotEqual(contentMainSize_, mainLen)) {
291 scrollAutoType_ = ScrollAutoType::START;
292 if (!isSmoothJump) {
293 startPos = contentStartOffset_;
294 BeginLayoutForward(startPos, layoutWrapper);
295 }
296 } else {
297 scrollAutoType_ = ScrollAutoType::END;
298 if (!isSmoothJump) {
299 startPos = contentMainSize_ - contentEndOffset_;
300 BeginLayoutBackward(startPos, layoutWrapper);
301 }
302 }
303 } else if (jumpIndex >= tempEndIndex) {
304 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
305 SetListItemGroupParam(wrapper, jumpIndex, 0.0f, false, listLayoutProperty, false);
306 wrapper->Measure(childLayoutConstraint_);
307 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
308 if (GreatOrEqual(mainLen, contentMainSize_)) {
309 scrollAutoType_ = ScrollAutoType::START;
310 if (!isSmoothJump) {
311 startPos = contentStartOffset_;
312 BeginLayoutForward(startPos, layoutWrapper);
313 }
314 } else {
315 scrollAutoType_ = ScrollAutoType::END;
316 if (!isSmoothJump) {
317 startPos = contentMainSize_ - contentEndOffset_;
318 BeginLayoutBackward(startPos, layoutWrapper);
319 }
320 }
321 }
322 }
323
HandleJumpCenter(LayoutWrapper * layoutWrapper)324 void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper)
325 {
326 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
327 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
328 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
329 if (isGroup && jumpIndexInGroup_.has_value()) {
330 int32_t indexInGroup = jumpIndexInGroup_.value();
331 auto listLayoutProperty =
332 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
333 SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
334 wrapper->Measure(GetGroupLayoutConstraint());
335 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
336 if (LessNotEqual(GetEndPosition(), endMainPos_)) {
337 LayoutForward(layoutWrapper, index + 1, GetEndPosition());
338 }
339 } else {
340 float mainLen = MeasureAndGetChildHeight(layoutWrapper, index);
341 float startPos = (contentMainSize_ - mainLen) / 2.0f;
342 if (LessNotEqual(startPos, endMainPos_)) {
343 LayoutForward(layoutWrapper, index, startPos);
344 }
345 }
346 if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
347 LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
348 }
349 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_ - contentEndOffset_)) {
350 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
351 }
352 }
353
HandleJumpStart(LayoutWrapper * layoutWrapper)354 void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper)
355 {
356 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value());
357 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
358 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
359 if (isGroup && jumpIndexInGroup_.has_value()) {
360 int32_t indexInGroup = jumpIndexInGroup_.value();
361 auto listLayoutProperty =
362 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
363 SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false);
364 wrapper->Measure(GetGroupLayoutConstraint());
365 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
366 if (LessNotEqual(GetEndPosition(), endMainPos_)) {
367 LayoutForward(layoutWrapper, index + 1, GetEndPosition());
368 }
369 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
370 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
371 }
372 } else {
373 BeginLayoutForward(contentStartOffset_, layoutWrapper);
374 }
375 }
376
HandleJumpEnd(LayoutWrapper * layoutWrapper)377 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
378 {
379 int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value());
380 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
381 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
382 if (isGroup && jumpIndexInGroup_.has_value()) {
383 int32_t indexInGroup = jumpIndexInGroup_.value();
384 auto listLayoutProperty =
385 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
386 SetListItemGroupParam(wrapper, index, contentMainSize_, true, listLayoutProperty, false);
387 wrapper->Measure(GetGroupLayoutConstraint());
388 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup);
389 if (GreatNotEqual(GetStartPosition(), startMainPos_)) {
390 LayoutBackward(layoutWrapper, index - 1, GetStartPosition());
391 }
392 if (GetEndIndex() <= totalItemCount_ -1 && LessNotEqual(GetEndPosition(), endMainPos_)) {
393 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
394 }
395 } else {
396 BeginLayoutBackward(contentMainSize_ - contentEndOffset_, layoutWrapper);
397 }
398 }
399
CheckNoNeedJumpListItem(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex)400 bool ListLayoutAlgorithm::CheckNoNeedJumpListItem(LayoutWrapper* layoutWrapper,
401 float startPos, float endPos, int32_t startIndex, int32_t endIndex, int32_t jumpIndex)
402 {
403 int32_t tempJumpIndex = jumpIndex;
404 int32_t tempStartIndex = startIndex;
405 int32_t tempEndIndex = endIndex;
406 if (GreatNotEqual(GetLanes(), 1)) {
407 tempJumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
408 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
409 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
410 }
411 if (tempJumpIndex > tempStartIndex && tempJumpIndex < tempEndIndex) {
412 return true;
413 }
414 if (tempJumpIndex == tempStartIndex && tempJumpIndex == tempEndIndex) {
415 return true;
416 }
417 if ((tempJumpIndex == tempStartIndex) && GreatOrEqual(startPos, contentStartOffset_)) {
418 return true;
419 }
420 if ((tempJumpIndex == tempEndIndex) && LessOrEqual(endPos, contentMainSize_ - contentEndOffset_)) {
421 return true;
422 }
423 return false;
424 }
425
CheckNoNeedJumpListItemGroup(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)426 bool ListLayoutAlgorithm::CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper,
427 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
428 {
429 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
430 CHECK_NULL_RETURN(wrapper, true);
431 if (wrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
432 return true;
433 }
434 int32_t jumpIndexInGroup = 0;
435 if (jumpIndexInGroup_.has_value()) {
436 jumpIndexInGroup = jumpIndexInGroup_.value();
437 } else {
438 return false;
439 }
440
441 auto layoutAlgorithm = wrapper->GetLayoutAlgorithm();
442 CHECK_NULL_RETURN(layoutAlgorithm, true);
443 auto groupLayoutAlgorithm =
444 AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithm->GetLayoutAlgorithm());
445 CHECK_NULL_RETURN(groupLayoutAlgorithm, true);
446 auto groupItemPosition = groupLayoutAlgorithm->GetItemPosition();
447 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
448 CHECK_NULL_RETURN(listLayoutProperty, false);
449
450 if (jumpIndex >= startIndex && jumpIndex <= endIndex) {
451 auto it = groupItemPosition.find(jumpIndexInGroup);
452 if (it != groupItemPosition.end()) {
453 auto topPos = jumpIndexStartPos + it->second.first - contentStartOffset_;
454 auto bottomPos = jumpIndexStartPos + it->second.second + contentEndOffset_;
455 if (JudgeInOfScreenScrollAutoType(wrapper, listLayoutProperty, topPos, bottomPos)) {
456 return true;
457 }
458 } else if (groupItemPosition.size() > 0) {
459 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndexInGroup,
460 groupItemPosition.begin()->first, groupItemPosition.rbegin()->first);
461 } else {
462 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
463 return true;
464 }
465 } else {
466 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndex,
467 startIndex, endIndex);
468 }
469 return false;
470 }
471
JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<ListLayoutProperty> & layoutProperty,float topPos,float bottomPos)472 bool ListLayoutAlgorithm::JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper,
473 const RefPtr<ListLayoutProperty>& layoutProperty, float topPos, float bottomPos)
474 {
475 auto stickyStyle = layoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
476
477 auto groupNode = layoutWrapper->GetHostNode();
478 CHECK_NULL_RETURN(groupNode, true);
479 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
480 CHECK_NULL_RETURN(groupPattern, true);
481
482 float headerMainSize = 0.0f;
483 float footerMainSize = 0.0f;
484 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::HEADER) {
485 headerMainSize = groupPattern->GetHeaderMainSize();
486 }
487 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::FOOTER) {
488 footerMainSize = groupPattern->GetFooterMainSize();
489 }
490
491 if (GreatOrEqual(topPos, startMainPos_ + headerMainSize) &&
492 LessOrEqual(bottomPos, endMainPos_ - footerMainSize)) {
493 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
494 return true;
495 } else if (NearEqual(topPos, startMainPos_ + headerMainSize) ||
496 NearEqual(bottomPos, endMainPos_ - footerMainSize)) {
497 scrollAutoType_ = ScrollAutoType::NOT_CHANGE;
498 return true;
499 } else if (GreatOrEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
500 scrollAutoType_ = ScrollAutoType::END;
501 } else if (LessNotEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) {
502 scrollAutoType_ = ScrollAutoType::START;
503 }
504
505 return false;
506 }
507
JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,const RefPtr<ListLayoutProperty> & layoutProperty,int32_t indexInGroup,int32_t judgeIndex,int32_t startIndex,int32_t endIndex)508 void ListLayoutAlgorithm::JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
509 const RefPtr<ListLayoutProperty>& layoutProperty, int32_t indexInGroup, int32_t judgeIndex,
510 int32_t startIndex, int32_t endIndex)
511 {
512 SetListItemGroupParam(layoutWrapper, index, 0.0f, true, layoutProperty, false);
513 layoutWrapper->Measure(childLayoutConstraint_);
514 auto jumpItemHeight = GetListGroupItemHeight(layoutWrapper, indexInGroup);
515 jumpIndexInGroup_ = indexInGroup;
516
517 if (judgeIndex < startIndex) {
518 if (jumpItemHeight > contentMainSize_) {
519 scrollAutoType_ = ScrollAutoType::END;
520 } else {
521 scrollAutoType_ = ScrollAutoType::START;
522 }
523 } else if (judgeIndex > endIndex) {
524 if (jumpItemHeight > contentMainSize_) {
525 scrollAutoType_ = ScrollAutoType::START;
526 } else {
527 scrollAutoType_ = ScrollAutoType::END;
528 }
529 }
530 }
531
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)532 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
533 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos)
534 {
535 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
536 CHECK_NULL_RETURN(wrapper, true);
537 if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG && jumpIndexInGroup_.has_value()) {
538 if (CheckNoNeedJumpListItemGroup(layoutWrapper, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
539 return true;
540 }
541 } else {
542 if (CheckNoNeedJumpListItem(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex)) {
543 return true;
544 }
545 }
546 return false;
547 }
548
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)549 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
550 {
551 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
552 CHECK_NULL_RETURN(wrapper, 0.0f);
553 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
554 if (isGroup) {
555 auto listLayoutProperty =
556 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
557 // true: layout forward, true: layout all group items.
558 SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, true);
559 }
560 wrapper->Measure(childLayoutConstraint_);
561 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
562 return mainLen;
563 }
564
CheckJumpToIndex()565 void ListLayoutAlgorithm::CheckJumpToIndex()
566 {
567 if (jumpIndex_.has_value() || !isNeedCheckOffset_) {
568 return;
569 }
570 if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
571 return;
572 }
573 for (const auto& pos : itemPosition_) {
574 if (pos.second.isGroup) {
575 return;
576 }
577 }
578 float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
579 float averageHeight = totalHeight / itemPosition_.size();
580 int32_t targetIndex = itemPosition_.begin()->first;
581 currentDelta_ -= itemPosition_.begin()->second.startPos;
582 if (NonNegative(currentDelta_)) {
583 int32_t items = currentDelta_ / averageHeight;
584 targetIndex += items;
585 currentDelta_ -= items * averageHeight;
586 } else {
587 int32_t items = -currentDelta_ / averageHeight;
588 targetIndex -= items;
589 currentDelta_ += items * averageHeight;
590 if (targetIndex <= 0) {
591 currentDelta_ = 0;
592 }
593 }
594 jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
595 }
596
UpdateSnapCenterContentOffset(LayoutWrapper * layoutWrapper)597 void ListLayoutAlgorithm::UpdateSnapCenterContentOffset(LayoutWrapper* layoutWrapper)
598 {
599 if (IsScrollSnapAlignCenter(layoutWrapper) && !itemPosition_.empty()) {
600 float itemHeight = 0.0f;
601 if (GetStartIndex() == 0) {
602 itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
603 contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
604 }
605 if (GetEndIndex() == totalItemCount_ - 1) {
606 itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
607 contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
608 }
609 }
610 }
611
CheckJumpValid(LayoutWrapper * layoutWrapper)612 bool ListLayoutAlgorithm::CheckJumpValid(LayoutWrapper* layoutWrapper)
613 {
614 if (jumpIndex_.value() == LAST_ITEM) {
615 jumpIndex_ = totalItemCount_ - 1;
616 } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
617 return false;
618 }
619 if (jumpIndex_ && jumpIndexInGroup_) {
620 auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
621 CHECK_NULL_RETURN(groupWrapper, false);
622 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
623 return false;
624 }
625 auto groupNode = groupWrapper->GetHostNode();
626 CHECK_NULL_RETURN(groupNode, false);
627 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
628 CHECK_NULL_RETURN(groupPattern, false);
629
630 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
631
632 if (jumpIndexInGroup_.value() == LAST_ITEM) {
633 jumpIndexInGroup_ = groupItemCount - 1;
634 } else if ((jumpIndexInGroup_.value() < 0) || (jumpIndexInGroup_.value() >= groupItemCount)) {
635 return false;
636 }
637 }
638 return true;
639 }
640
MeasureList(LayoutWrapper * layoutWrapper)641 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
642 {
643 int32_t startIndex = 0;
644 int32_t endIndex = 0;
645 int32_t midIndex = 0;
646 float midItemMidPos = contentMainSize_ / 2.0f;
647 float startPos = 0.0f;
648 float endPos = 0.0f;
649 float itemTotalSize = 0.0f;
650 float jumpIndexStartPos = 0.0f;
651 int32_t jumpIndex = 0;
652
653 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) {
654 if (jumpIndex_.has_value()) {
655 jumpIndex = jumpIndex_.value();
656 }
657 auto it = itemPosition_.find(jumpIndex);
658 if (it != itemPosition_.end()) {
659 jumpIndexStartPos = it->second.startPos;
660 }
661 }
662
663 if (jumpIndex_) {
664 if (!CheckJumpValid(layoutWrapper)) {
665 jumpIndex_.reset();
666 jumpIndexInGroup_.reset();
667 } else {
668 if (jumpIndex_ && scrollAlign_ == ScrollAlign::CENTER) {
669 ClearAllItemPosition(layoutWrapper);
670 }
671 }
672 }
673 if (targetIndex_) {
674 if (targetIndex_.value() == LAST_ITEM) {
675 targetIndex_ = totalItemCount_ - 1;
676 } else if ((targetIndex_.value() < 0) || (targetIndex_.value() >= totalItemCount_)) {
677 targetIndex_.reset();
678 }
679 targetIndexStaged_ = targetIndex_;
680 }
681 if (!itemPosition_.empty()) {
682 startPos = itemPosition_.begin()->second.startPos;
683 endPos = itemPosition_.rbegin()->second.endPos;
684 itemTotalSize = endPos - startPos;
685 startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
686 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
687 if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
688 jumpIndex_ = totalItemCount_ - 1;
689 scrollAlign_ = ScrollAlign::END;
690 }
691 if (overScrollFeature_) {
692 UpdateSnapCenterContentOffset(layoutWrapper);
693 }
694 if (IsScrollSnapAlignCenter(layoutWrapper)) {
695 midIndex = GetMidIndex(layoutWrapper, true);
696 midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
697 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
698 midIndex = std::min(midIndex, totalItemCount_ - 1);
699 }
700 OffScreenLayoutDirection();
701 itemPosition_.clear();
702 layoutWrapper->RemoveAllChildInRenderTree();
703 }
704 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO &&
705 NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) {
706 jumpIndex_.reset();
707 jumpIndexInGroup_.reset();
708 }
709 if (jumpIndex_) {
710 switch (scrollAlign_) {
711 case ScrollAlign::START:
712 case ScrollAlign::NONE:
713 HandleJumpStart(layoutWrapper);
714 break;
715 case ScrollAlign::CENTER:
716 HandleJumpCenter(layoutWrapper);
717 break;
718 case ScrollAlign::END:
719 HandleJumpEnd(layoutWrapper);
720 break;
721 case ScrollAlign::AUTO:
722 HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
723 break;
724 }
725 needEstimateOffset_ = true;
726 } else if (targetIndex_.has_value()) {
727 if (LessOrEqual(startIndex, targetIndex_.value())) {
728 LayoutForward(layoutWrapper, startIndex, startPos);
729 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
730 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
731 }
732 } else if (GreatNotEqual(startIndex, targetIndex_.value())) {
733 LayoutBackward(layoutWrapper, endIndex, endPos);
734 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
735 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
736 }
737 }
738 } else {
739 jumpIndexInGroup_.reset();
740 bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos, startMainPos_ + contentStartOffset_);
741 float midItemHeight = 0.0f;
742 if (IsScrollSnapAlignCenter(layoutWrapper)) {
743 midItemHeight = MeasureAndGetChildHeight(layoutWrapper, midIndex);
744 }
745 if (NearZero(currentOffset_) || (!overScrollFeature_ && NonNegative(currentOffset_)) ||
746 (overScrollFeature_ && overScrollTop) ||
747 LessOrEqual(itemTotalSize, contentMainSize_ - contentStartOffset_ - contentEndOffset_)) {
748 if (overScrollTop && !canOverScroll_) {
749 startPos = startMainPos_ + contentStartOffset_;
750 }
751 if (IsScrollSnapAlignCenter(layoutWrapper)) {
752 midIndex = GetLanesFloor(layoutWrapper, midIndex);
753 LayoutForward(layoutWrapper, midIndex, midItemMidPos - midItemHeight / 2.0f);
754 } else {
755 startIndex = GetLanesFloor(layoutWrapper, startIndex);
756 LayoutForward(layoutWrapper, startIndex, startPos);
757 }
758 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
759 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
760 }
761 } else {
762 if (overScrollFeature_ && !overScrollTop && !NearZero(prevContentMainSize_)) {
763 endPos += contentMainSize_ - prevContentMainSize_;
764 }
765 if (IsScrollSnapAlignCenter(layoutWrapper)) {
766 endIndex = midIndex;
767 endPos = midItemMidPos + midItemHeight / 2.0f;
768 }
769 endIndex = GetLanesCeil(layoutWrapper, endIndex);
770 LayoutBackward(layoutWrapper, endIndex, endPos);
771 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
772 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
773 }
774 }
775 }
776 }
777
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)778 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
779 int32_t& currentIndex, float startPos, float& endPos)
780 {
781 if (currentIndex + 1 >= totalItemCount_) {
782 return 0;
783 }
784 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
785 CHECK_NULL_RETURN(wrapper, 0);
786 int32_t id = wrapper->GetHostNode()->GetId();
787 ++currentIndex;
788 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
789 if (isGroup) {
790 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
791 SetListItemGroupParam(wrapper, currentIndex, startPos, true, listLayoutProperty, false);
792 }
793 {
794 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
795 wrapper->Measure(childLayoutConstraint_);
796 }
797 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
798 endPos = startPos + mainLen;
799 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
800 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
801 return 1;
802 }
803
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)804 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
805 int32_t& currentIndex, float endPos, float& startPos)
806 {
807 if (currentIndex - 1 < 0) {
808 return 0;
809 }
810 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
811 CHECK_NULL_RETURN(wrapper, 0);
812 int32_t id = wrapper->GetHostNode()->GetId();
813 --currentIndex;
814 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
815 if (isGroup) {
816 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
817 SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false);
818 }
819 {
820 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
821 wrapper->Measure(childLayoutConstraint_);
822 }
823 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
824 startPos = endPos - mainLen;
825 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup };
826 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex);
827 return 1;
828 }
829
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)830 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
831 {
832 float currentEndPos = startPos;
833 float currentStartPos = 0.0f;
834 float endMainPos = overScrollFeature_ ?
835 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
836 if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
837 endMainPos = Infinity<float>();
838 }
839
840 auto currentIndex = startIndex - 1;
841 auto chainOffset = 0.0f;
842 do {
843 currentStartPos = currentEndPos;
844 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
845 if (count == 0) {
846 break;
847 }
848 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
849 currentEndPos += spaceWidth_;
850 }
851 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
852 // reach the valid target index
853 if (forwardFeature_ && targetIndex_ && GreatNotEqual(currentIndex, targetIndex_.value())) {
854 endMainPos = GetEndPosition() + contentMainSize_;
855 targetIndex_.reset();
856 }
857 } while (LessNotEqual(currentEndPos + chainOffset, endMainPos));
858
859 currentEndPos += chainOffset;
860 // adjust offset.
861 UpdateSnapCenterContentOffset(layoutWrapper);
862 if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
863 endMainPos_ = currentEndPos + contentEndOffset_;
864 startMainPos_ = endMainPos_ - contentMainSize_;
865 auto firstItemTop = itemPosition_.begin()->second.startPos;
866 if (itemPosition_.begin()->second.isGroup) {
867 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), true);
868 firstItemTop = itemPosition_.begin()->second.startPos;
869 }
870 auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_;
871 if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
872 // all items size is less than list.
873 if (!canOverScroll_) {
874 currentOffset_ = firstItemTop - contentStartOffset_;
875 startMainPos_ = currentOffset_;
876 endMainPos_ = startMainPos_ + contentMainSize_;
877 }
878 if (!mainSizeIsDefined_) {
879 // adapt child size.
880 contentMainSize_ = itemTotalSize;
881 }
882 } else {
883 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
884 if (!canOverScroll_ || jumpIndex_.has_value()) {
885 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_;
886 }
887 }
888 }
889 if (overScrollFeature_ && canOverScroll_) {
890 return;
891 }
892 // Mark inactive in wrapper.
893 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
894 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
895 // Don't recycle When the head item is Visibility.None.
896 if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) ||
897 GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) {
898 if (pos->second.isGroup) {
899 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
900 }
901 break;
902 }
903 layoutWrapper->RemoveChildInRenderTree(pos->first);
904 itemPosition_.erase(pos++);
905 }
906 }
907
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)908 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
909 {
910 float currentStartPos = endPos;
911 float currentEndPos = 0.0f;
912 float startMainPos = overScrollFeature_ ?
913 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
914 if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
915 startMainPos = -Infinity<float>();
916 }
917 auto currentIndex = endIndex + 1;
918 auto chainOffset = 0.0f;
919 do {
920 currentEndPos = currentStartPos;
921 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
922 if (count == 0) {
923 break;
924 }
925 if (currentIndex > 0) {
926 currentStartPos = currentStartPos - spaceWidth_;
927 }
928 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
929 // reach the valid target index
930 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
931 startMainPos = GetStartPosition() - contentMainSize_;
932 targetIndex_.reset();
933 }
934 } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
935
936 currentStartPos += chainOffset;
937 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
938 UpdateSnapCenterContentOffset(layoutWrapper);
939 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_)) {
940 auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_;
941 bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_));
942 if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) {
943 if (overScrollFeature_ && !NearZero(prevContentMainSize_)) {
944 currentOffset_ += contentMainSize_ - prevContentMainSize_;
945 }
946 contentMainSize_ = itemTotalSize;
947 }
948 if (!canOverScroll_ || jumpIndex_.has_value()) {
949 currentOffset_ = currentStartPos - contentStartOffset_;
950 }
951 endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_;
952 startMainPos_ = currentStartPos - contentStartOffset_;
953 if (Positive(currentOffset_) && itemPosition_.rbegin()->second.isGroup) {
954 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetEndIndex(), false);
955 }
956 }
957
958 if (overScrollFeature_) {
959 return;
960 }
961
962 // Mark inactive in wrapper.
963 std::list<int32_t> removeIndexes;
964 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
965 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
966 // Don't recycle When the tail item is Visibility.None.
967 if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) ||
968 LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) {
969 if (pos->second.isGroup) {
970 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
971 }
972 break;
973 }
974 layoutWrapper->RemoveChildInRenderTree(pos->first);
975 removeIndexes.emplace_back(pos->first);
976 }
977 for (const auto& index : removeIndexes) {
978 itemPosition_.erase(index);
979 }
980 }
981
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)982 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
983 {
984 if (!predictSnapOffset_.has_value()) {
985 return;
986 }
987 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
988 if ((scrollSnapAlign != V2::ScrollSnapAlign::START) && (scrollSnapAlign != V2::ScrollSnapAlign::CENTER) &&
989 (scrollSnapAlign != V2::ScrollSnapAlign::END)) {
990 predictSnapOffset_.reset();
991 predictSnapEndPos_.reset();
992 return;
993 }
994
995 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
996 int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign);
997 if (endIndex != -1) {
998 predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign);
999 predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_;
1000 predictSnapEndPos_.reset();
1001 } else {
1002 if (IsUniformHeightProbably()) {
1003 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1004 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1005 FixPredictSnapOffsetAlignStart();
1006 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1007 FixPredictSnapOffsetAlignCenter();
1008 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1009 FixPredictSnapOffsetAlignEnd();
1010 }
1011 } else {
1012 predictSnapEndPos_ = predictEndPos;
1013 }
1014 }
1015
1016 return;
1017 }
1018
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1019 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
1020 {
1021 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1022 CHECK_NULL_RETURN(listLayoutProperty, false);
1023 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1024 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1025 return true;
1026 }
1027
1028 return false;
1029 }
1030
FixPredictSnapOffsetAlignStart()1031 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
1032 {
1033 if (itemPosition_.empty()) {
1034 return;
1035 }
1036 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1037 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1038 float startPos = contentStartOffset_;
1039 float endPos = contentMainSize_ - contentEndOffset_;
1040
1041 if (LessNotEqual(predictEndPos, -startPos)) {
1042 if (isSpringEffect_) {
1043 return;
1044 }
1045 predictEndPos = -startPos;
1046 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1047 if (isSpringEffect_) {
1048 return;
1049 }
1050 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1051 } else {
1052 int32_t index;
1053 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1054 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
1055 break;
1056 }
1057 }
1058 predictEndPos = index * itemHeight - startPos;
1059 if (LessNotEqual(predictEndPos, -startPos)) {
1060 predictEndPos = -startPos;
1061 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1062 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1063 }
1064 }
1065
1066 predictSnapOffset_ = totalOffset_ - predictEndPos;
1067 predictSnapEndPos_ = predictEndPos;
1068 }
1069
FixPredictSnapOffsetAlignCenter()1070 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
1071 {
1072 if (itemPosition_.empty()) {
1073 return;
1074 }
1075 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1076 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1077
1078 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) {
1079 if (isSpringEffect_) {
1080 return;
1081 }
1082 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1083 } else if (GreatNotEqual(
1084 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1085 if (isSpringEffect_) {
1086 return;
1087 }
1088 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1089 } else {
1090 int32_t index;
1091 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1092 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
1093 itemHeight / 2.0f) {
1094 break;
1095 }
1096 }
1097 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1098 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1099 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1100 } else if (GreatNotEqual(
1101 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
1102 predictEndPos =
1103 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
1104 }
1105 }
1106
1107 predictSnapOffset_ = totalOffset_ - predictEndPos;
1108 predictSnapEndPos_ = predictEndPos;
1109 }
1110
FixPredictSnapOffsetAlignEnd()1111 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
1112 {
1113 if (itemPosition_.empty()) {
1114 return;
1115 }
1116 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
1117 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1118 float startPos = contentStartOffset_;
1119 float endPos = contentMainSize_ - contentEndOffset_;
1120
1121 if (LessNotEqual(predictEndPos, -startPos)) {
1122 if (isSpringEffect_) {
1123 return;
1124 }
1125 predictEndPos = -startPos;
1126 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1127 if (isSpringEffect_) {
1128 return;
1129 }
1130 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1131 } else {
1132 int32_t index;
1133 for (index = 0; index <= GetMaxListItemIndex(); index++) {
1134 if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) {
1135 break;
1136 }
1137 }
1138 predictEndPos = index * itemHeight - endPos - spaceWidth_;
1139 if (LessNotEqual(predictEndPos, -startPos)) {
1140 predictEndPos = -startPos;
1141 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
1142 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos;
1143 }
1144 }
1145
1146 predictSnapOffset_ = totalOffset_ - predictEndPos;
1147 predictSnapEndPos_ = predictEndPos;
1148 }
1149
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1150 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
1151 int32_t& startIndex, float crossSize)
1152 {
1153 CHECK_NULL_VOID(wrapper);
1154 auto offset = paddingOffset_;
1155 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
1156 float crossOffset = 0.0f;
1157 if (GetLanes() > 1) {
1158 int32_t laneIndex = 0;
1159 if (pos.isGroup) {
1160 startIndex = index + 1;
1161 } else {
1162 laneIndex = (index - startIndex) % GetLanes();
1163 }
1164
1165 float laneGutter = GetLaneGutter();
1166 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
1167 crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex;
1168 } else {
1169 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
1170 }
1171 auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
1172 if (axis_ == Axis::VERTICAL) {
1173 offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
1174 } else {
1175 offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
1176 }
1177 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1178 SetListItemIndex(wrapper, index);
1179 }
1180
Layout(LayoutWrapper * layoutWrapper)1181 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1182 {
1183 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1184 CHECK_NULL_VOID(listLayoutProperty);
1185 auto axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
1186 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1187 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
1188 MinusPaddingToSize(padding, size);
1189 paddingOffset_ = padding.Offset();
1190 float crossSize = GetCrossAxisSize(size, axis_);
1191 totalItemCount_ = layoutWrapper->GetTotalChildCount();
1192 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
1193 int32_t startIndex = GetStartIndex();
1194
1195 totalOffset_ += currentOffset_;
1196 FixPredictSnapOffset(listLayoutProperty);
1197 // layout items.
1198 for (auto& pos : itemPosition_) {
1199 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
1200 if (!wrapper) {
1201 continue;
1202 }
1203 pos.second.startPos -= currentOffset_;
1204 pos.second.endPos -= currentOffset_;
1205 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
1206 wrapper->Layout();
1207 }
1208 auto cacheCount = listLayoutProperty->GetCachedCountValue(1);
1209 if (!itemPosition_.empty() && cacheCount > 0) {
1210 auto items = LayoutCachedItem(layoutWrapper, cacheCount);
1211 if (!items.empty()) {
1212 PostIdleTask(layoutWrapper->GetHostNode(), { items, childLayoutConstraint_ });
1213 } else {
1214 auto host = layoutWrapper->GetHostNode();
1215 CHECK_NULL_VOID(host);
1216 auto pattern = host->GetPattern<ListPattern>();
1217 CHECK_NULL_VOID(pattern);
1218 pattern->SetPredictLayoutParam(std::nullopt);
1219 }
1220 }
1221 }
1222
CalculateLaneCrossOffset(float crossSize,float childCrossSize)1223 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
1224 {
1225 float delta = crossSize - GetLaneGutter() - childCrossSize;
1226 if (LessOrEqual(delta, 0)) {
1227 return 0.0f;
1228 }
1229 switch (listItemAlign_) {
1230 case OHOS::Ace::V2::ListItemAlign::START:
1231 return 0.0f;
1232 case OHOS::Ace::V2::ListItemAlign::CENTER:
1233 return delta / 2.0f;
1234 case OHOS::Ace::V2::ListItemAlign::END:
1235 return delta;
1236 default:
1237 return 0.0f;
1238 }
1239 }
1240
OnSurfaceChanged(LayoutWrapper * layoutWrapper)1241 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
1242 {
1243 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
1244 return;
1245 }
1246 auto host = layoutWrapper->GetHostNode();
1247 CHECK_NULL_VOID(host);
1248 auto focusHub = host->GetFocusHub();
1249 CHECK_NULL_VOID(focusHub);
1250 // textField not in List
1251 if (!focusHub->IsCurrentFocus()) {
1252 return;
1253 }
1254 auto context = PipelineContext::GetCurrentContext();
1255 CHECK_NULL_VOID(context);
1256 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
1257 CHECK_NULL_VOID(textFieldManager);
1258 // only when textField is onFocus
1259 auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
1260 CHECK_NULL_VOID(textField);
1261 auto textFieldHost = textField->GetHost();
1262 CHECK_NULL_VOID(textFieldHost);
1263 auto position = textFieldHost->GetTransformRelativeOffset().GetY() + textField->GetHostFrameSize()->Height();
1264 auto globalOffset = host->GetTransformRelativeOffset();
1265 auto offset = contentMainSize_ + globalOffset.GetY() - position;
1266 if (LessOrEqual(offset, 0.0)) {
1267 // negative offset to scroll down
1268 currentDelta_ -= static_cast<float>(offset);
1269 }
1270 }
1271
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout)1272 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index,
1273 float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout)
1274 {
1275 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1276 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1277 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1278 CHECK_NULL_VOID(itemGroup);
1279 if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) {
1280 referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average
1281 }
1282 if (jumpIndex_) {
1283 auto wrapper = layoutWrapper;
1284 itemGroup->ClearItemPosition(&(*wrapper));
1285 }
1286 itemGroup->SetListMainSize(startMainPos_, endMainPos_, referencePos, forwardLayout);
1287 itemGroup->SetListLayoutProperty(layoutProperty);
1288 itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_);
1289 if (jumpIndex_.has_value() && jumpIndex_.value() == index) {
1290 if (!jumpIndexInGroup_.has_value()) {
1291 if (forwardLayout && (scrollAlign_ == ScrollAlign::START ||
1292 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1293 jumpIndexInGroup_ = 0;
1294 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1295 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1296 jumpIndexInGroup_ = LAST_ITEM;
1297 }
1298 }
1299
1300 if (jumpIndexInGroup_.has_value()) {
1301 itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1302 itemGroup->SetScrollAlign(scrollAlign_);
1303 jumpIndexInGroup_.reset();
1304 }
1305 }
1306
1307 if (groupNeedAllLayout || targetIndex_) {
1308 auto groupItemPosition = itemGroup->GetItemPosition();
1309 int32_t groupTotalItemCount = layoutWrapper->GetTotalChildCount() - itemGroup->GetItemStartIndex();
1310 if (groupNeedAllLayout ||
1311 (targetIndex_ && targetIndex_.value() == index) ||
1312 (!(forwardLayout && !groupItemPosition.empty() &&
1313 groupItemPosition.rbegin()->first == groupTotalItemCount - 1) &&
1314 !(!forwardLayout && !groupItemPosition.empty() &&
1315 groupItemPosition.begin()->first == 0))) {
1316 itemGroup->SetNeedAllLayout();
1317 }
1318 }
1319 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1320 }
1321
GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1322 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1323 {
1324 int32_t id = layoutWrapper->GetHostNode()->GetId();
1325 ListItemInfo pos = { id, 0, 0, true };
1326 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1327 CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos);
1328 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1329 CHECK_NULL_RETURN(itemGroup, pos);
1330 auto res = itemGroup->GetItemGroupPosition(index);
1331 return { id, res.first, res.second, true };
1332 }
1333
GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1334 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1335 {
1336 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1337 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f);
1338 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1339 CHECK_NULL_RETURN(itemGroup, 0.0f);
1340 return itemGroup->GetItemHeight(index);
1341 }
1342
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1343 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1344 {
1345 auto host = layoutWrapper->GetHostNode();
1346 CHECK_NULL_VOID(host);
1347 auto listItem = host->GetPattern<ListItemPattern>();
1348 if (listItem) {
1349 listItem->SetIndexInList(index);
1350 return;
1351 }
1352 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1353 CHECK_NULL_VOID(listItemGroup);
1354 listItemGroup->SetIndexInList(index);
1355 }
1356
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1357 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1358 float referencePos, bool forwardLayout) const
1359 {
1360 if (targetIndex_.has_value()) {
1361 return;
1362 }
1363 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1364 CHECK_NULL_VOID(wrapper);
1365 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1366 CHECK_NULL_VOID(algorithmWrapper);
1367 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1368 CHECK_NULL_VOID(itemGroup);
1369 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1370 }
1371
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1372 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index,
1373 bool forwardLayout)
1374 {
1375 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1376 CHECK_NULL_VOID(wrapper);
1377 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1378 CHECK_NULL_VOID(algorithmWrapper);
1379 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1380 CHECK_NULL_VOID(itemGroup);
1381 if (forwardLayout) {
1382 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, !forwardLayout);
1383 } else {
1384 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, !forwardLayout);
1385 }
1386 itemGroup->SetScrollAlign(ScrollAlign::NONE);
1387 wrapper->Measure(GetGroupLayoutConstraint());
1388 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1389 auto& pos = itemPosition_[index];
1390 if (forwardLayout) {
1391 pos.startPos = pos.endPos - mainLen;
1392 } else {
1393 pos.endPos = pos.startPos + mainLen;
1394 }
1395 }
1396
OffScreenLayoutDirection()1397 void ListLayoutAlgorithm::OffScreenLayoutDirection()
1398 {
1399 if (!targetIndex_ || itemPosition_.empty() || (itemPosition_.find(targetIndex_.value()) != itemPosition_.end())) {
1400 forwardFeature_ = false;
1401 backwardFeature_ = false;
1402 return;
1403 }
1404 if (GreatNotEqual(targetIndex_.value(), GetEndIndex())) {
1405 forwardFeature_ = true;
1406 backwardFeature_ = false;
1407 } else {
1408 forwardFeature_ = false;
1409 backwardFeature_ = true;
1410 }
1411 }
1412
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1413 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1414 {
1415 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1416 float midPos = contentSize / 2.0f;
1417 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1418 GreatNotEqual(GetStartPosition(), contentStartOffset_)) {
1419 midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_;
1420 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1421 LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) &&
1422 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1423 midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_;
1424 }
1425 for (auto & pos : itemPosition_) {
1426 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1427 return pos.first;
1428 }
1429 }
1430 return totalItemCount_ - 1;
1431 }
1432
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1433 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1434 {
1435 CHECK_NULL_VOID(wrapper);
1436 auto host = wrapper->GetHostNode();
1437 CHECK_NULL_VOID(host);
1438 host->ForceSyncGeometryNode();
1439 }
1440
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1441 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1442 {
1443 std::list<int32_t> predictBuildList;
1444 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1445 float crossSize = GetCrossAxisSize(size, axis_);
1446
1447 auto currIndex = itemPosition_.rbegin()->first + 1;
1448 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1449 for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1450 int32_t index = currIndex + i;
1451 auto wrapper = layoutWrapper->GetChildByIndex(index);
1452 if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1453 predictBuildList.emplace_back(index);
1454 continue;
1455 }
1456 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1457 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1458 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1459 int32_t id = wrapper->GetHostNode()->GetId();
1460 ListItemInfo pos = { id, currPos, endPos, isGroup };
1461 currPos = endPos + spaceWidth_;
1462 auto startIndex = index;
1463 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1464 SyncGeometry(wrapper);
1465 wrapper->SetActive(false);
1466 }
1467
1468 currIndex = itemPosition_.begin()->first - 1;
1469 currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1470 for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1471 int32_t index = currIndex - i;
1472 auto wrapper = layoutWrapper->GetChildByIndex(index);
1473 if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1474 predictBuildList.emplace_back(index);
1475 continue;
1476 }
1477 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1478 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1479 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1480 int32_t id = wrapper->GetHostNode()->GetId();
1481 ListItemInfo pos = { id, startPos, currPos, isGroup };
1482 currPos = startPos - spaceWidth_;
1483 auto startIndex = index;
1484 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1485 SyncGeometry(wrapper);
1486 wrapper->SetActive(false);
1487 }
1488 return predictBuildList;
1489 }
1490
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1491 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1492 {
1493 CHECK_NULL_RETURN(wrapper, false);
1494 wrapper->SetActive(false);
1495 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1496 if (!isGroup) {
1497 auto frameNode = wrapper->GetHostNode();
1498 CHECK_NULL_RETURN(frameNode, false);
1499 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1500 FrameNode::ProcessOffscreenNode(frameNode);
1501 return true;
1502 }
1503 return false;
1504 }
1505
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1506 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1507 {
1508 CHECK_NULL_VOID(frameNode);
1509 auto pattern = frameNode->GetPattern<ListPattern>();
1510 CHECK_NULL_VOID(pattern);
1511 if (pattern->GetPredictLayoutParam()) {
1512 pattern->SetPredictLayoutParam(param);
1513 return;
1514 }
1515 pattern->SetPredictLayoutParam(param);
1516 auto context = PipelineContext::GetCurrentContext();
1517 CHECK_NULL_VOID(context);
1518 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1519 ACE_SCOPED_TRACE("List predict");
1520 auto frameNode = weak.Upgrade();
1521 CHECK_NULL_VOID(frameNode);
1522 auto pattern = frameNode->GetPattern<ListPattern>();
1523 CHECK_NULL_VOID(pattern);
1524 if (!pattern->GetPredictLayoutParam().has_value()) {
1525 return;
1526 }
1527 bool needMarkDirty = false;
1528 auto param = pattern->GetPredictLayoutParam().value();
1529 for (auto it = param.items.begin(); it != param.items.end();) {
1530 if (GetSysTimestamp() > deadline) {
1531 break;
1532 }
1533 auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false);
1534 if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) {
1535 break;
1536 }
1537 needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty;
1538 param.items.erase(it++);
1539 }
1540 if (needMarkDirty) {
1541 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1542 }
1543 pattern->SetPredictLayoutParam(std::nullopt);
1544 if (!param.items.empty()) {
1545 ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1546 pattern->SetPredictLayoutParam(param);
1547 }
1548 });
1549 }
1550
GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign)1551 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign)
1552 {
1553 float stopOnScreen = 0;
1554 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1555 stopOnScreen = contentStartOffset_;
1556 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1557 stopOnScreen = contentMainSize_ / 2.0f;
1558 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1559 stopOnScreen = contentMainSize_ - contentEndOffset_;
1560 }
1561 return stopOnScreen;
1562 }
1563
FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)1564 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions(
1565 float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign)
1566 {
1567 int32_t endIndex = -1;
1568 float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign);
1569 float startPos = 0.0f;
1570 float endPos = 0.0f;
1571 float itemHeight = 0.0f;
1572
1573 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1574 for (const auto& positionInfo : itemPosition_) {
1575 startPos = totalOffset_ + positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_;
1576 itemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
1577 endPos = totalOffset_ + positionInfo.second.startPos + itemHeight / 2.0f;
1578 if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1579 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1580 endIndex = positionInfo.first;
1581 break;
1582 }
1583 }
1584 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1585 for (const auto& positionInfo : itemPosition_) {
1586 startPos = totalOffset_ + positionInfo.second.startPos - spaceWidth_ / 2.0f;
1587 endPos = totalOffset_ + positionInfo.second.endPos + spaceWidth_ / 2.0f;
1588 if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1589 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1590 endIndex = positionInfo.first;
1591 break;
1592 }
1593 }
1594 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1595 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
1596 endPos = totalOffset_ + pos->second.endPos + itemHeight / 2.0f + spaceWidth_;
1597 itemHeight = pos->second.endPos - pos->second.startPos;
1598 startPos = totalOffset_ + pos->second.endPos - itemHeight / 2.0f;
1599 if (GreatOrEqual(predictEndPos + stopOnScreen, startPos) &&
1600 LessNotEqual(predictEndPos + stopOnScreen, endPos)) {
1601 endIndex = pos->first;
1602 break;
1603 }
1604 }
1605 }
1606 return endIndex;
1607 }
1608
IsUniformHeightProbably()1609 bool ListLayoutAlgorithm::IsUniformHeightProbably()
1610 {
1611 bool isUniformHeightProbably = true;
1612 float itemHeight = 0.0f;
1613 float currentItemHeight = 0.0f;
1614 for (const auto& positionInfo : itemPosition_) {
1615 currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos;
1616 if (NearZero(itemHeight)) {
1617 itemHeight = currentItemHeight;
1618 } else if (!NearEqual(currentItemHeight, itemHeight)) {
1619 isUniformHeightProbably = false;
1620 break;
1621 }
1622 }
1623 return isUniformHeightProbably;
1624 }
1625
CalculatePredictSnapEndPositionByIndex(uint32_t index,V2::ScrollSnapAlign scrollSnapAlign)1626 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(uint32_t index, V2::ScrollSnapAlign scrollSnapAlign)
1627 {
1628 float predictSnapEndPos = 0;
1629 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1630 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_;
1631 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1632 float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos;
1633 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1634 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1635 predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_;
1636 }
1637 return predictSnapEndPos;
1638 }
1639
OnItemPositionAddOrUpdate(LayoutWrapper * layoutWrapper,uint32_t index)1640 void ListLayoutAlgorithm::OnItemPositionAddOrUpdate(LayoutWrapper* layoutWrapper, uint32_t index)
1641 {
1642 if (!predictSnapEndPos_.has_value()) {
1643 return;
1644 }
1645 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
1646 CHECK_NULL_VOID(listLayoutProperty);
1647 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
1648 float startPos = 0.0f;
1649 float endPos = 0.0f;
1650 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
1651 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_;
1652 endPos = totalOffset_ + itemPosition_[index].endPos;
1653 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
1654 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_ / 2.0f;
1655 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_ / 2.0f;
1656 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
1657 startPos = totalOffset_ + itemPosition_[index].startPos;
1658 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_;
1659 } else {
1660 return;
1661 }
1662
1663 float predictSnapEndPos = predictSnapEndPos_.value();
1664 float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign);
1665 if (GreatOrEqual(predictSnapEndPos + stopOnScreen, startPos) &&
1666 LessNotEqual(predictSnapEndPos + stopOnScreen, endPos)) {
1667 predictSnapEndPos = CalculatePredictSnapEndPositionByIndex(index, scrollSnapAlign);
1668 } else {
1669 return;
1670 }
1671
1672 if (!NearEqual(predictSnapEndPos, predictSnapEndPos_.value())) {
1673 predictSnapEndPos_ = predictSnapEndPos;
1674 }
1675 }
1676 } // namespace OHOS::Ace::NG
1677