• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/text_field/text_field_manager.h"
36 #include "core/components_ng/property/layout_constraint.h"
37 #include "core/components_ng/property/measure_property.h"
38 #include "core/components_ng/property/measure_utils.h"
39 #include "core/components_ng/property/property.h"
40 #include "core/components_v2/inspector/inspector_constants.h"
41 #include "core/components_v2/list/list_properties.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43 
44 namespace OHOS::Ace::NG {
45 
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)46 void ListLayoutAlgorithm::UpdateListItemConstraint(
47     Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
48 {
49     contentConstraint.parentIdealSize = selfIdealSize;
50     contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
51     auto crossSize = selfIdealSize.CrossSize(axis);
52     if (crossSize.has_value()) {
53         contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
54         contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
55     }
56 }
57 
Measure(LayoutWrapper * layoutWrapper)58 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
59 {
60     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
61     CHECK_NULL_VOID(listLayoutProperty);
62 
63     const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value();
64 
65     // calculate idealSize and set FrameSize
66     axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
67 
68     // calculate main size.
69     auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value();
70     auto contentIdealSize = CreateIdealSize(
71         contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
72 
73     const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
74     paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
75     paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
76     contentMainSize_ = 0.0f;
77     contentStartOffset_ = listLayoutProperty->GetContentStartOffset().value_or(0.0f);
78     contentEndOffset_ = listLayoutProperty->GetContentEndOffset().value_or(0.0f);
79     totalItemCount_ = layoutWrapper->GetTotalChildCount();
80     if (!GetMainAxisSize(contentIdealSize, axis_)) {
81         if (totalItemCount_ == 0) {
82             contentMainSize_ = 0.0f;
83         } else {
84             // use parent max size first.
85             auto parentMaxSize = contentConstraint.maxSize;
86             contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_) - paddingBeforeContent_ - paddingAfterContent_;
87             mainSizeIsDefined_ = false;
88         }
89     } else {
90         contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
91         mainSizeIsDefined_ = true;
92     }
93 
94     if (totalItemCount_ > 0) {
95         OnSurfaceChanged(layoutWrapper);
96         CheckJumpToIndex();
97         currentOffset_ = currentDelta_;
98         startMainPos_ = currentOffset_;
99         endMainPos_ = currentOffset_ + contentMainSize_;
100         stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
101         childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
102         auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
103         auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
104         spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
105         if (GreatOrEqual(spaceWidth_, contentMainSize_)) {
106             spaceWidth_ = 0.0f;
107         }
108         if (listLayoutProperty->GetDivider().has_value()) {
109             auto divider = listLayoutProperty->GetDivider().value();
110             std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
111             if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
112                 dividerSpace.reset();
113             }
114             if (dividerSpace.has_value()) {
115                 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
116             }
117         }
118         spaceWidth_ += chainInterval_;
119         CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
120         listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
121         // calculate child layout constraint.
122         UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
123         MeasureList(layoutWrapper);
124     } else {
125         itemPosition_.clear();
126         layoutWrapper->RemoveAllChildInRenderTree();
127         LOGI("child size is empty");
128     }
129 
130     auto crossSize = contentIdealSize.CrossSize(axis_);
131     if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
132         contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
133         crossMatchChild_ = true;
134     }
135     contentIdealSize.SetMainSize(contentMainSize_, axis_);
136     AddPaddingToSize(padding, contentIdealSize);
137     layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
138 
139     // set list cache info.
140     layoutWrapper->SetCacheCount(listLayoutProperty->GetCachedCountValue(1) * GetLanes());
141 
142     LOGD("new start index is %{public}d, new end index is %{public}d, offset is %{public}f, mainSize is %{public}f",
143         GetStartIndex(), GetEndIndex(), currentOffset_, contentMainSize_);
144 }
145 
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const146 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
147 {
148     if (GetItemPosition().empty()) {
149         return 0.0f;
150     }
151     float maxCrossSize = 0.0f;
152     float crossSize = 0.0f;
153     float prevPos = GetItemPosition().begin()->second.startPos;
154     for (const auto& pos : GetItemPosition()) {
155         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
156         if (!wrapper) {
157             continue;
158         }
159         auto getGeometryNode = wrapper->GetGeometryNode();
160         if (!getGeometryNode) {
161             continue;
162         }
163         if (NearEqual(prevPos, pos.second.startPos)) {
164             crossSize += getGeometryNode->GetMarginFrameSize().CrossSize(axis);
165         } else {
166             crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
167         }
168         prevPos = pos.second.startPos;
169         maxCrossSize = std::max(crossSize, maxCrossSize);
170     }
171     return maxCrossSize;
172 }
173 
CalculateEstimateOffset(ScrollAlign align)174 void ListLayoutAlgorithm::CalculateEstimateOffset(ScrollAlign align)
175 {
176     if (itemPosition_.empty()) {
177         estimateOffset_ = 0.0f;
178         return;
179     }
180     float itemsHeight = (itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos) + spaceWidth_;
181     auto lines = static_cast<int32_t>(itemPosition_.size());
182     if (GetLanes() > 1) {
183         lines = (lines / GetLanes()) + (lines % GetLanes() > 0 ? 1 : 0);
184     }
185     if (lines > 0) {
186         float averageHeight = itemsHeight / static_cast<float>(lines);
187         switch (align) {
188             case ScrollAlign::START:
189             case ScrollAlign::NONE:
190                 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes());
191                 break;
192             case ScrollAlign::CENTER:
193                 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes()) -
194                     contentMainSize_ / 2.0f + (averageHeight - spaceWidth_) / 2.0f;
195                 break;
196             case ScrollAlign::END:
197                 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes() + 1) -
198                     spaceWidth_ - contentMainSize_;
199                 break;
200             case ScrollAlign::AUTO:
201                 switch (scrollAutoType_) {
202                     case ScrollAutoType::NOT_CHANGE:
203                         estimateOffset_ = averageHeight * static_cast<float>(itemPosition_.begin()->first /
204                             GetLanes()) - itemPosition_.begin()->second.startPos;
205                         break;
206                     case ScrollAutoType::START:
207                         estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes());
208                         break;
209                     case ScrollAutoType::END:
210                         estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes() + 1) -
211                             spaceWidth_ - contentMainSize_;
212                         break;
213                 }
214                 break;
215         }
216     } else {
217         estimateOffset_ = 0.0f;
218     }
219 }
220 
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)221 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
222 {
223     LayoutForward(layoutWrapper, jumpIndex_.value(), startPos);
224     if (((jumpIndex_.value() > 0) || (!IsScrollSnapAlignCenter(layoutWrapper) && jumpIndex_.value() == 0)) &&
225         GreatNotEqual(GetStartPosition(), (contentStartOffset_ + startMainPos_))) {
226         LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition());
227         if (LessNotEqual(GetEndIndex(), totalItemCount_ - 1) &&
228             LessNotEqual(GetEndPosition(), endMainPos_)) {
229             LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
230         }
231     }
232 }
233 
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)234 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
235 {
236     LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos);
237     if (LessOrEqual(jumpIndex_.value(), totalItemCount_ - 1) &&
238         LessNotEqual(GetEndPosition(), endMainPos_)) {
239         LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition());
240         if (Positive(GetStartIndex()) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
241             LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
242         }
243     }
244 }
245 
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t & startIndex,int32_t & endIndex,float & startPos,float & endPos)246 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper,
247     int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos)
248 {
249     bool isSmoothJump = false;
250     int32_t jumpIndex = 0;
251     if (jumpIndex_.has_value()) {
252         jumpIndex = jumpIndex_.value();
253     } else {
254         jumpIndex = targetIndex_.value();
255         isSmoothJump = true;
256     }
257     int32_t tempStartIndex = startIndex;
258     int32_t tempEndIndex = endIndex;
259     if (GreatNotEqual(GetLanes(), 1)) {
260         jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
261         tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
262         tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
263     }
264     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
265     CHECK_NULL_VOID(wrapper);
266     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
267     if (!isGroup) {
268         if (jumpIndex >= tempEndIndex) {
269             scrollAutoType_ = ScrollAutoType::END;
270             if (!isSmoothJump) {
271                 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()) + GetLanes() - 1;
272                 startPos = contentMainSize_;
273                 BeginLayoutBackward(startPos, layoutWrapper);
274             }
275         } else if (jumpIndex <= tempStartIndex) {
276             scrollAutoType_ = ScrollAutoType::START;
277             if (!isSmoothJump) {
278                 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
279                 startPos = 0.0f;
280                 BeginLayoutForward(startPos, layoutWrapper);
281             }
282         }
283     } else if (jumpIndex <= tempStartIndex) {
284         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
285         SetListItemGroupParam(wrapper, contentMainSize_, false, listLayoutProperty, false);
286         wrapper->Measure(childLayoutConstraint_);
287         float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
288         if (GreatNotEqual(contentMainSize_, mainLen)) {
289             scrollAutoType_ = ScrollAutoType::START;
290             if (!isSmoothJump) {
291                 startPos = 0.0f;
292                 BeginLayoutForward(startPos, layoutWrapper);
293             }
294         } else {
295             scrollAutoType_ = ScrollAutoType::END;
296             if (!isSmoothJump) {
297                 startPos = contentMainSize_;
298                 BeginLayoutBackward(startPos, layoutWrapper);
299             }
300         }
301     } else if (jumpIndex >= tempEndIndex) {
302         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
303         SetListItemGroupParam(wrapper, 0.0f, false, listLayoutProperty, false);
304         wrapper->Measure(childLayoutConstraint_);
305         float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
306         if (GreatOrEqual(mainLen, contentMainSize_)) {
307             scrollAutoType_ = ScrollAutoType::START;
308             if (!isSmoothJump) {
309                 startPos = 0.0f;
310                 BeginLayoutForward(startPos, layoutWrapper);
311             }
312         } else {
313             scrollAutoType_ = ScrollAutoType::END;
314             if (!isSmoothJump) {
315                 startPos = contentMainSize_;
316                 BeginLayoutBackward(startPos, layoutWrapper);
317             }
318         }
319     }
320 }
321 
HandleJumpEnd(LayoutWrapper * layoutWrapper)322 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
323 {
324     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
325     CHECK_NULL_VOID(wrapper);
326     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
327     if (!isGroup) {
328         jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()) + GetLanes() - 1;
329     }
330     BeginLayoutBackward(contentMainSize_, layoutWrapper);
331 }
332 
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex)333 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
334     int32_t startIndex, int32_t endIndex)
335 {
336     int32_t jumpIndex = 0;
337     if (jumpIndex_.has_value()) {
338         jumpIndex = jumpIndex_.value();
339     } else {
340         jumpIndex = targetIndex_.value();
341     }
342     int32_t tempStartIndex = startIndex;
343     int32_t tempEndIndex = endIndex;
344     if (GreatNotEqual(GetLanes(), 1)) {
345         jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
346         tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
347         tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
348     }
349     if (jumpIndex > tempStartIndex && jumpIndex < tempEndIndex) {
350         return true;
351     }
352     if (jumpIndex == tempStartIndex && jumpIndex == tempEndIndex) {
353         return true;
354     }
355     if ((jumpIndex == tempStartIndex) && GreatOrEqual(startPos, 0.0f)) {
356         return true;
357     }
358     if ((jumpIndex == tempEndIndex) && LessOrEqual(endPos, contentMainSize_)) {
359         return true;
360     }
361 
362     return false;
363 }
364 
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)365 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
366 {
367     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
368     CHECK_NULL_RETURN(wrapper, 0.0f);
369     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
370     if (isGroup) {
371         auto listLayoutProperty =
372             AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
373         // true: layout forward, true: layout all group items.
374         SetListItemGroupParam(wrapper, 0.0f, true, listLayoutProperty, true);
375     }
376     wrapper->Measure(childLayoutConstraint_);
377     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
378     return mainLen;
379 }
380 
CheckJumpToIndex()381 void ListLayoutAlgorithm::CheckJumpToIndex()
382 {
383     if (jumpIndex_.has_value()) {
384         return;
385     }
386     if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
387         return;
388     }
389     for (const auto& pos : itemPosition_) {
390         if (pos.second.isGroup) {
391             return;
392         }
393     }
394     float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
395     float averageHeight = totalHeight / itemPosition_.size();
396     int32_t targetIndex = itemPosition_.begin()->first;
397     if (NonNegative(currentDelta_)) {
398         int32_t items = currentDelta_ / averageHeight;
399         targetIndex += items;
400         currentDelta_ -= items * averageHeight;
401     } else {
402         int32_t items = -currentDelta_ / averageHeight;
403         targetIndex -= items;
404         currentDelta_ += items * averageHeight;
405     }
406     jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
407 }
408 
MeasureList(LayoutWrapper * layoutWrapper)409 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
410 {
411     int32_t startIndex = 0;
412     int32_t endIndex = 0;
413     int32_t midIndex = 0;
414     float midItemMidPos = 0.0f;
415     float startPos = 0.0f;
416     float endPos = 0.0f;
417     if (jumpIndex_) {
418         if (jumpIndex_.value() == LAST_ITEM) {
419             jumpIndex_ = totalItemCount_ - 1;
420         } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
421             LOGW("jump index is illegal, %{public}d, %{public}d", jumpIndex_.value(), totalItemCount_);
422             jumpIndex_.reset();
423         }
424     }
425     if (!itemPosition_.empty()) {
426         startPos = itemPosition_.begin()->second.startPos;
427         endPos = itemPosition_.rbegin()->second.endPos;
428         startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
429         endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
430         if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
431             jumpIndex_ = totalItemCount_ - 1;
432             scrollAlign_ = ScrollAlign::END;
433         }
434         if (IsScrollSnapAlignCenter(layoutWrapper) && overScrollFeature_) {
435             float itemHeight = 0.0f;
436             if (startIndex == 0) {
437                 itemHeight = itemPosition_.begin()->second.endPos - startPos;
438                 contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
439             }
440             if (endIndex == totalItemCount_ - 1) {
441                 itemHeight = endPos - itemPosition_.rbegin()->second.startPos;
442                 contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
443             }
444         }
445         if (IsScrollSnapAlignCenter(layoutWrapper)) {
446             midIndex = GetMidIndex(layoutWrapper, true);
447             midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
448                 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
449             midIndex = std::min(midIndex, totalItemCount_ - 1);
450         }
451         OffScreenLayoutDirection();
452         itemPosition_.clear();
453         layoutWrapper->RemoveAllChildInRenderTree();
454     }
455     if ((jumpIndex_ || targetIndex_) && scrollAlign_ == ScrollAlign::AUTO &&
456         NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex)) {
457         jumpIndex_.reset();
458         targetIndex_.reset();
459     }
460     if (jumpIndex_) {
461         LOGD("Jump index: %{public}d, offset is %{public}f, startMainPos: %{public}f, endMainPos: %{public}f",
462             jumpIndex_.value(), currentOffset_, startMainPos_, endMainPos_);
463         switch (scrollAlign_) {
464             case ScrollAlign::START:
465             case ScrollAlign::NONE:
466             case ScrollAlign::CENTER:
467                 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
468                 if (scrollAlign_ == ScrollAlign::START) {
469                     startPos = contentStartOffset_;
470                 } else {
471                     float mainLen = MeasureAndGetChildHeight(layoutWrapper, jumpIndex_.value());
472                     startPos = (contentMainSize_ - mainLen) / 2.0f;
473                 }
474                 BeginLayoutForward(startPos, layoutWrapper);
475                 break;
476             case ScrollAlign::END:
477                 HandleJumpEnd(layoutWrapper);
478                 break;
479             case ScrollAlign::AUTO:
480                 HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
481                 break;
482         }
483         CalculateEstimateOffset(scrollAlign_);
484     } else if (targetIndex_.has_value()) {
485         if (scrollAlign_ == ScrollAlign::AUTO) {
486             HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
487         }
488         if (LessOrEqual(startIndex, targetIndex_.value())) {
489             LayoutForward(layoutWrapper, startIndex, startPos);
490             if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
491                 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
492             }
493         } else if (GreatNotEqual(startIndex, targetIndex_.value())) {
494             LayoutBackward(layoutWrapper, endIndex, endPos);
495             if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
496                 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
497             }
498         }
499     } else {
500         jumpIndexInGroup_.reset();
501         LOGD("StartIndex index: %{public}d, offset is %{public}f, startMainPos: %{public}f, endMainPos: %{public}f",
502             startIndex, currentOffset_, startMainPos_, endMainPos_);
503         bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos, startMainPos_);
504         float midItemHeight = 0.0f;
505         if (IsScrollSnapAlignCenter(layoutWrapper)) {
506             midItemHeight = MeasureAndGetChildHeight(layoutWrapper, midIndex);
507         }
508         if ((!overScrollFeature_ && NonNegative(currentOffset_)) ||
509             (overScrollFeature_ && overScrollTop)) {
510             if (IsScrollSnapAlignCenter(layoutWrapper)) {
511                 midIndex = GetLanesFloor(layoutWrapper, midIndex);
512                 LayoutForward(layoutWrapper, midIndex, midItemMidPos - midItemHeight / 2.0f);
513             } else {
514                 startIndex = GetLanesFloor(layoutWrapper, startIndex);
515                 LayoutForward(layoutWrapper, startIndex, startPos);
516             }
517             if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
518                 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
519             }
520         } else {
521             if (overScrollFeature_ && !overScrollTop && !NearZero(prevContentMainSize_)) {
522                 endPos += contentMainSize_ - prevContentMainSize_;
523             }
524             if (IsScrollSnapAlignCenter(layoutWrapper)) {
525                 midIndex = GetLanesFloor(layoutWrapper, midIndex) + GetLanes() - 1;
526                 LayoutBackward(layoutWrapper, midIndex, midItemMidPos + midItemHeight / 2.0f);
527             } else {
528                 LayoutBackward(layoutWrapper, endIndex, endPos);
529             }
530             if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
531                 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
532             }
533         }
534     }
535 }
536 
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)537 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
538     int32_t& currentIndex, float startPos, float& endPos)
539 {
540     if (currentIndex + 1 >= totalItemCount_) {
541         return 0;
542     }
543     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
544     CHECK_NULL_RETURN(wrapper, 0);
545     ++currentIndex;
546     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
547     if (isGroup) {
548         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
549         SetListItemGroupParam(wrapper, startPos, true, listLayoutProperty, false);
550     }
551     {
552         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
553         wrapper->Measure(childLayoutConstraint_);
554     }
555     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
556     endPos = startPos + mainLen;
557     itemPosition_[currentIndex] = { startPos, endPos, isGroup };
558     return 1;
559 }
560 
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)561 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
562     int32_t& currentIndex, float endPos, float& startPos)
563 {
564     if (currentIndex - 1 < 0) {
565         return 0;
566     }
567     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
568     CHECK_NULL_RETURN(wrapper, 0);
569     --currentIndex;
570     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
571     if (isGroup) {
572         auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
573         SetListItemGroupParam(wrapper, endPos, false, listLayoutProperty, false);
574     }
575     {
576         ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
577         wrapper->Measure(childLayoutConstraint_);
578     }
579     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
580     startPos = endPos - mainLen;
581     itemPosition_[currentIndex] = { startPos, endPos, isGroup };
582     return 1;
583 }
584 
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)585 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
586 {
587     float currentEndPos = startPos;
588     float currentStartPos = 0.0f;
589     float endMainPos = overScrollFeature_ ?
590         std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
591     if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
592         endMainPos = Infinity<float>();
593     }
594 
595     auto currentIndex = startIndex - 1;
596     auto chainOffset = 0.0f;
597     do {
598         currentStartPos = currentEndPos;
599         int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
600         if (count == 0) {
601             break;
602         }
603         if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
604             currentEndPos += spaceWidth_;
605         }
606         LOGD("LayoutForward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
607             currentStartPos, currentEndPos);
608         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
609         // reach the valid target index
610         if (forwardFeature_ && targetIndex_ && GreatNotEqual(currentIndex, targetIndex_.value())) {
611             endMainPos = GetEndPosition() + contentMainSize_;
612             targetIndex_.reset();
613         }
614     } while (LessNotEqual(currentEndPos + chainOffset, endMainPos));
615 
616     if (overScrollFeature_ && canOverScroll_) {
617         LOGD("during over scroll, just return in LayoutForward");
618         return;
619     }
620 
621     currentEndPos += chainOffset;
622     // adjust offset.
623     if (IsScrollSnapAlignCenter(layoutWrapper) && currentIndex == totalItemCount_ - 1) {
624         float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
625         contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
626     }
627     if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
628         auto firstItemTop = itemPosition_.begin()->second.startPos;
629         auto itemTotalSize = currentEndPos - firstItemTop;
630         if (IsScrollSnapAlignCenter(layoutWrapper)) {
631             if (jumpIndex_.has_value() && (itemPosition_.find(jumpIndex_.value()) != itemPosition_.end())) {
632                 auto jumpItemStartPos = itemPosition_[jumpIndex_.value()].startPos;
633                 auto jumpItemEndPos = itemPosition_[jumpIndex_.value()].endPos;
634                 currentOffset_ = jumpItemEndPos - (jumpItemEndPos - jumpItemStartPos) / 2.0f - contentMainSize_ / 2.0f;
635             }
636             startMainPos_ = currentOffset_;
637         } else if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
638             // all items size is less than list.
639             currentOffset_ = firstItemTop;
640             startMainPos_ = currentOffset_;
641             if (!mainSizeIsDefined_) {
642                 // adapt child size.
643                 LOGD("LayoutForward: adapt child total size");
644                 contentMainSize_ = itemTotalSize;
645             }
646         } else {
647             // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
648             if (!canOverScroll_ || jumpIndex_.has_value()) {
649                 currentOffset_ = currentEndPos - contentMainSize_;
650                 LOGD("LayoutForward: adjust offset to %{public}f", currentOffset_);
651             }
652             startMainPos_ = currentEndPos - contentMainSize_;
653             endMainPos_ = currentEndPos;
654         }
655         if (Negative(currentOffset_) && itemPosition_.begin()->second.isGroup) {
656             AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex());
657         }
658     }
659 
660     // Mark inactive in wrapper.
661     for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
662         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
663         if (GreatOrEqual(pos->second.endPos + chainOffset, startMainPos_)) {
664             if (pos->second.isGroup) {
665                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
666             }
667             break;
668         }
669         LOGI("recycle item:%{public}d", pos->first);
670         layoutWrapper->RemoveChildInRenderTree(pos->first);
671         itemPosition_.erase(pos++);
672     }
673 }
674 
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)675 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
676 {
677     float currentStartPos = endPos;
678     float currentEndPos = 0.0f;
679     float startMainPos = overScrollFeature_ ?
680         std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
681     if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
682         startMainPos = -Infinity<float>();
683     }
684     auto currentIndex = endIndex + 1;
685     auto chainOffset = 0.0f;
686     do {
687         currentEndPos = currentStartPos;
688         int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
689         if (count == 0) {
690             break;
691         }
692         if (currentIndex > 0) {
693             currentStartPos = currentStartPos - spaceWidth_;
694         }
695         LOGD("LayoutBackward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
696             currentStartPos, currentEndPos);
697         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
698         // reach the valid target index
699         if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
700             startMainPos = GetStartPosition() - contentMainSize_;
701             targetIndex_.reset();
702         }
703     } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
704 
705     currentStartPos += chainOffset;
706     // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
707     if (IsScrollSnapAlignCenter(layoutWrapper) && currentIndex == 0) {
708         float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
709         contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
710     }
711     if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_)) {
712         bool overBottom = LessNotEqual(GetEndPosition(), endMainPos_);
713         if (((!canOverScroll_ || overBottom) && !IsScrollSnapAlignCenter(layoutWrapper)) || jumpIndex_.has_value()) {
714             currentOffset_ = currentStartPos;
715             if (!mainSizeIsDefined_ && GetEndIndex() == totalItemCount_ - 1) {
716                 auto itemTotalSize = GetEndPosition() - currentStartPos;
717                 contentMainSize_ = std::min(contentMainSize_, itemTotalSize);
718             }
719         }
720         if (!IsScrollSnapAlignCenter(layoutWrapper) || jumpIndex_.has_value()) {
721             endMainPos_ = currentStartPos + contentMainSize_;
722             startMainPos_ = currentStartPos;
723         }
724     }
725 
726     if (overScrollFeature_) {
727         LOGD("during over scroll, just return in LayoutBackward");
728         return;
729     }
730 
731     // Mark inactive in wrapper.
732     std::list<int32_t> removeIndexes;
733     for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
734         chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
735         if (LessOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
736             if (pos->second.isGroup) {
737                 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
738             }
739             break;
740         }
741         layoutWrapper->RemoveChildInRenderTree(pos->first);
742         removeIndexes.emplace_back(pos->first);
743     }
744     for (const auto& index : removeIndexes) {
745         itemPosition_.erase(index);
746     }
747 }
748 
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)749 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
750 {
751     if (!predictSnapOffset_.has_value()) {
752         return;
753     }
754 
755     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
756     if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
757         FixPredictSnapOffsetAlignStart();
758     } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
759         FixPredictSnapOffsetAlignCenter();
760     } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
761         FixPredictSnapOffsetAlignEnd();
762     } else {
763         predictSnapOffset_.reset();
764     }
765 
766     return;
767 }
768 
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)769 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
770 {
771     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
772     CHECK_NULL_RETURN(listLayoutProperty, false);
773     auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
774     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
775         return true;
776     }
777 
778     return false;
779 }
780 
FixPredictSnapOffsetAlignStart()781 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
782 {
783     if (itemPosition_.empty()) {
784         return;
785     }
786     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
787     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
788 
789     if (LessNotEqual(predictEndPos, 0.0f)) {
790         if (isSpringEffect_) {
791             return;
792         }
793         predictEndPos = 0.0f;
794     } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
795         if (isSpringEffect_) {
796             return;
797         }
798         predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
799     } else {
800         int32_t index;
801         for (index = 0; index <= GetMaxListItemIndex(); index++) {
802             if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
803                 break;
804             }
805         }
806         predictEndPos = index * itemHeight;
807         if (LessNotEqual(predictEndPos, 0.0f)) {
808             predictEndPos = 0.0f;
809         } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
810             predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
811         }
812     }
813 
814     predictSnapOffset_ = totalOffset_ - predictEndPos;
815 }
816 
FixPredictSnapOffsetAlignCenter()817 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
818 {
819     if (itemPosition_.empty()) {
820         return;
821     }
822     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
823     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
824 
825     if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
826         if (isSpringEffect_) {
827             return;
828         }
829         predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
830     } else if (GreatNotEqual(
831         predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
832         if (isSpringEffect_) {
833             return;
834         }
835         predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
836     } else {
837         int32_t index;
838         for (index = 0; index <= GetMaxListItemIndex(); index++) {
839             if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
840                 itemHeight / 2.0f) {
841                 break;
842             }
843         }
844         predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
845         if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
846             predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
847         } else if (GreatNotEqual(
848             predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
849             predictEndPos =
850                 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
851         }
852     }
853 
854     predictSnapOffset_ = totalOffset_ - predictEndPos;
855 }
856 
FixPredictSnapOffsetAlignEnd()857 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
858 {
859     if (itemPosition_.empty()) {
860         return;
861     }
862     auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
863     auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
864 
865     if (LessNotEqual(predictEndPos, 0.0f)) {
866         if (isSpringEffect_) {
867             return;
868         }
869         predictEndPos = 0.0f;
870     } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
871         if (isSpringEffect_) {
872             return;
873         }
874         predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
875     } else {
876         int32_t index;
877         for (index = 0; index <= GetMaxListItemIndex(); index++) {
878             if (std::abs(predictEndPos + contentMainSize_ - index * itemHeight) < itemHeight / 2.0f) {
879                 break;
880             }
881         }
882         predictEndPos = index * itemHeight - contentMainSize_ - spaceWidth_;
883         if (LessNotEqual(predictEndPos, 0.0f)) {
884             predictEndPos = 0.0f;
885         } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
886             predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
887         }
888     }
889 
890     predictSnapOffset_ = totalOffset_ - predictEndPos;
891 }
892 
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)893 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
894     int32_t& startIndex, float crossSize)
895 {
896     CHECK_NULL_VOID(wrapper);
897     auto offset = paddingOffset_;
898     float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
899     float crossOffset = 0.0f;
900     if (GetLanes() > 1) {
901         int32_t laneIndex = 0;
902         if (pos.isGroup) {
903             startIndex = index + 1;
904         } else {
905             laneIndex = (index - startIndex) % GetLanes();
906         }
907 
908         float laneGutter = GetLaneGutter();
909         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
910         crossOffset += ((crossSize + laneGutter) / GetLanes() - laneGutter) * laneIndex + laneGutter * laneIndex;
911     } else {
912         crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
913     }
914     auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
915     if (axis_ == Axis::VERTICAL) {
916         offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
917     } else {
918         offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
919     }
920     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
921     SetListItemIndex(wrapper, index);
922 }
923 
Layout(LayoutWrapper * layoutWrapper)924 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
925 {
926     auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
927     CHECK_NULL_VOID(listLayoutProperty);
928     auto axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
929     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
930     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
931     MinusPaddingToSize(padding, size);
932     paddingOffset_ = padding.Offset();
933     float crossSize = GetCrossAxisSize(size, axis_);
934     totalItemCount_ = layoutWrapper->GetTotalChildCount();
935     listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
936     int32_t startIndex = GetStartIndex();
937 
938     totalOffset_ += currentOffset_;
939     FixPredictSnapOffset(listLayoutProperty);
940     // layout items.
941     for (auto& pos : itemPosition_) {
942         auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
943         if (!wrapper) {
944             LOGI("wrapper is out of boundary");
945             continue;
946         }
947         pos.second.startPos -= currentOffset_;
948         pos.second.endPos -= currentOffset_;
949         LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
950         wrapper->Layout();
951     }
952     auto cacheCount = listLayoutProperty->GetCachedCountValue(1);
953     if (!itemPosition_.empty() && cacheCount > 0) {
954         auto items = LayoutCachedItem(layoutWrapper, cacheCount);
955         if (!items.empty()) {
956             PostIdleTask(layoutWrapper->GetHostNode(), { items, childLayoutConstraint_ });
957         }
958     }
959 }
960 
CalculateLaneCrossOffset(float crossSize,float childCrossSize)961 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
962 {
963     float delta = crossSize - childCrossSize;
964     if (LessOrEqual(delta, 0)) {
965         return 0.0f;
966     }
967     switch (listItemAlign_) {
968         case OHOS::Ace::V2::ListItemAlign::START:
969             return 0.0f;
970         case OHOS::Ace::V2::ListItemAlign::CENTER:
971             return delta / 2.0f;
972         case OHOS::Ace::V2::ListItemAlign::END:
973             return delta;
974         default:
975             LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_);
976             return 0.0f;
977     }
978 }
979 
OnSurfaceChanged(LayoutWrapper * layoutWrapper)980 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
981 {
982     if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
983         return;
984     }
985     auto host = layoutWrapper->GetHostNode();
986     CHECK_NULL_VOID(host);
987     auto focusHub = host->GetFocusHub();
988     CHECK_NULL_VOID(focusHub);
989     // textField not in List
990     if (!focusHub->IsCurrentFocus()) {
991         return;
992     }
993     auto context = PipelineContext::GetCurrentContext();
994     CHECK_NULL_VOID(context);
995     auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
996     CHECK_NULL_VOID_NOLOG(textFieldManager);
997     // only when textField is onFocus
998     auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
999     CHECK_NULL_VOID_NOLOG(textField);
1000     auto textFieldHost = textField->GetHost();
1001     CHECK_NULL_VOID(textFieldHost);
1002     auto position = textFieldHost->GetTransformRelativeOffset().GetY() + textField->GetHostFrameSize()->Height();
1003     auto globalOffset = host->GetTransformRelativeOffset();
1004     auto offset = contentMainSize_ + globalOffset.GetY() - position;
1005     if (LessOrEqual(offset, 0.0)) {
1006         // negative offset to scroll down
1007         currentDelta_ -= static_cast<float>(offset);
1008         LOGI("update offset on virtual keyboard height change, %{public}f", offset);
1009     }
1010 }
1011 
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout)1012 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, float referencePos,
1013     bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout)
1014 {
1015     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1016     CHECK_NULL_VOID(layoutAlgorithmWrapper);
1017     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1018     CHECK_NULL_VOID(itemGroup);
1019     itemGroup->SetListMainSize(startMainPos_, endMainPos_, referencePos, forwardLayout);
1020     itemGroup->SetListLayoutProperty(layoutProperty);
1021     if (jumpIndex_.has_value() && (!jumpIndexInGroup_.has_value())) {
1022         if (forwardLayout&& (scrollAlign_ == ScrollAlign::START ||
1023             (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1024             jumpIndexInGroup_ = 0;
1025         } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1026             (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1027             jumpIndexInGroup_ = LAST_ITEM;
1028         }
1029     }
1030 
1031     if (groupNeedAllLayout) {
1032         itemGroup->SetNeedAllLayout();
1033     }
1034     if (targetIndex_) {
1035         itemGroup->SetTargetIndex(targetIndex_.value());
1036     }
1037     if (jumpIndexInGroup_.has_value()) {
1038         itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1039         jumpIndexInGroup_.reset();
1040     }
1041     layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1042 }
1043 
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1044 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1045 {
1046     auto host = layoutWrapper->GetHostNode();
1047     CHECK_NULL_VOID_NOLOG(host);
1048     auto listItem = host->GetPattern<ListItemPattern>();
1049     if (listItem) {
1050         listItem->SetIndexInList(index);
1051         return;
1052     }
1053     auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1054     CHECK_NULL_VOID_NOLOG(listItemGroup);
1055     listItemGroup->SetIndexInList(index);
1056 }
1057 
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1058 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1059     float referencePos, bool forwardLayout) const
1060 {
1061     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1062     CHECK_NULL_VOID(wrapper);
1063     auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1064     CHECK_NULL_VOID(algorithmWrapper);
1065     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1066     CHECK_NULL_VOID(itemGroup);
1067     itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1068 }
1069 
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index)1070 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index)
1071 {
1072     auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1073     CHECK_NULL_VOID(wrapper);
1074     auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1075     CHECK_NULL_VOID(algorithmWrapper);
1076     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1077     CHECK_NULL_VOID(itemGroup);
1078     itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, false);
1079     itemGroup->Measure(AceType::RawPtr(wrapper));
1080     float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1081     float delta = 0.0f;
1082     for (auto& pos : itemPosition_) {
1083         if (pos.first == index) {
1084             delta = (pos.second.endPos - pos.second.startPos) - mainLen;
1085             pos.second.endPos = pos.second.startPos + mainLen;
1086         } else if (pos.first > index) {
1087             pos.second.startPos -= delta;
1088             pos.second.endPos -= delta;
1089         }
1090     }
1091 }
1092 
OffScreenLayoutDirection()1093 void ListLayoutAlgorithm::OffScreenLayoutDirection()
1094 {
1095     if (!targetIndex_ || itemPosition_.empty() || (itemPosition_.find(targetIndex_.value()) != itemPosition_.end())) {
1096         forwardFeature_ = false;
1097         backwardFeature_ = false;
1098         return;
1099     }
1100     if (GreatNotEqual(targetIndex_.value(), GetEndIndex())) {
1101         forwardFeature_ = true;
1102         backwardFeature_ = false;
1103     } else {
1104         forwardFeature_ = false;
1105         backwardFeature_ = true;
1106     }
1107 }
1108 
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1109 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1110 {
1111     float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1112     float midPos = contentSize / 2.0f;
1113     if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1114         GreatNotEqual(GetStartPosition(), startMainPos_)) {
1115         midPos = GetStartPosition() + contentSize / 2.0f;
1116     } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1117         LessNotEqual(GetEndPosition(), endMainPos_) &&
1118         (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1119         midPos = GetEndPosition() - contentSize / 2.0f;
1120     }
1121     for (auto & pos : itemPosition_) {
1122         if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1123             return pos.first;
1124         }
1125     }
1126     return totalItemCount_ - 1;
1127 }
1128 
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1129 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1130 {
1131     CHECK_NULL_VOID(wrapper);
1132     auto host = wrapper->GetHostNode();
1133     CHECK_NULL_VOID(host);
1134     host->ForceSyncGeometryNode();
1135 }
1136 
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1137 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1138 {
1139     std::list<int32_t> predictBuildList;
1140     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1141     float crossSize = GetCrossAxisSize(size, axis_);
1142 
1143     auto currIndex = itemPosition_.rbegin()->first + 1;
1144     auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1145     for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1146         int32_t index = currIndex + i;
1147         auto wrapper = layoutWrapper->GetChildByIndex(index);
1148         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1149             predictBuildList.emplace_back(index);
1150             continue;
1151         }
1152         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1153         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1154         auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1155         ListItemInfo pos = { currPos, endPos, isGroup };
1156         currPos = endPos + spaceWidth_;
1157         auto startIndex = index;
1158         LayoutItem(wrapper, index, pos, startIndex, crossSize);
1159         SyncGeometry(wrapper);
1160         wrapper->SetActive(false);
1161     }
1162 
1163     currIndex = itemPosition_.begin()->first - 1;
1164     currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1165     for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1166         int32_t index = currIndex - i;
1167         auto wrapper = layoutWrapper->GetChildByIndex(index);
1168         if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1169             predictBuildList.emplace_back(index);
1170             continue;
1171         }
1172         bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1173         auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1174         auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1175         ListItemInfo pos = { startPos, currPos, isGroup };
1176         currPos = startPos - spaceWidth_;
1177         auto startIndex = index;
1178         LayoutItem(wrapper, index, pos, startIndex, crossSize);
1179         SyncGeometry(wrapper);
1180         wrapper->SetActive(false);
1181     }
1182     return predictBuildList;
1183 }
1184 
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1185 void ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1186 {
1187     CHECK_NULL_VOID(wrapper);
1188     wrapper->SetActive(false);
1189     bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1190     if (!isGroup) {
1191         auto frameNode = wrapper->GetHostNode();
1192         CHECK_NULL_VOID(frameNode);
1193         frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1194         FrameNode::ProcessOffscreenNode(frameNode);
1195     }
1196 }
1197 
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1198 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1199 {
1200     CHECK_NULL_VOID(frameNode);
1201     auto pattern = frameNode->GetPattern<ListPattern>();
1202     CHECK_NULL_VOID(pattern);
1203     if (pattern->GetPredictLayoutParam()) {
1204         pattern->SetPredictLayoutParam(param);
1205         return;
1206     }
1207     pattern->SetPredictLayoutParam(param);
1208     auto context = PipelineContext::GetCurrentContext();
1209     CHECK_NULL_VOID(context);
1210     context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1211         ACE_SCOPED_TRACE("List predict");
1212         auto frameNode = weak.Upgrade();
1213         CHECK_NULL_VOID(frameNode);
1214         auto pattern = frameNode->GetPattern<ListPattern>();
1215         CHECK_NULL_VOID(pattern);
1216         if (!pattern->GetPredictLayoutParam().has_value()) {
1217             return;
1218         }
1219         auto param = pattern->GetPredictLayoutParam().value();
1220         for (auto it = param.items.begin(); it != param.items.end();) {
1221             if (GetSysTimestamp() > deadline) {
1222                 break;
1223             }
1224             auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false);
1225             PredictBuildItem(wrapper, param.layoutConstraint);
1226             frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1227             param.items.erase(it++);
1228         }
1229         pattern->SetPredictLayoutParam(std::nullopt);
1230         if (!param.items.empty()) {
1231             ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1232             pattern->SetPredictLayoutParam(param);
1233         }
1234     });
1235 }
1236 } // namespace OHOS::Ace::NG
1237