• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 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_pattern.h"
17 
18 #include <cstdint>
19 #include <string>
20 
21 #include "base/geometry/axis.h"
22 #include "base/geometry/rect.h"
23 #include "base/log/dump_log.h"
24 #include "base/memory/referenced.h"
25 #include "base/utils/utils.h"
26 #include "core/animation/bilateral_spring_node.h"
27 #include "core/animation/spring_model.h"
28 #include "core/common/container.h"
29 #include "core/components/common/layout/constants.h"
30 #include "core/components/scroll/scroll_bar_theme.h"
31 #include "core/components_ng/base/inspector_filter.h"
32 #include "core/components_ng/pattern/list/list_height_offset_calculator.h"
33 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
34 #include "core/components_ng/pattern/list/list_item_pattern.h"
35 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
36 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
37 #include "core/components_ng/pattern/list/list_layout_property.h"
38 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
39 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
40 #include "core/components_ng/pattern/scrollable/scrollable.h"
41 #include "core/components_ng/property/measure_utils.h"
42 #include "core/components_v2/inspector/inspector_constants.h"
43 
44 namespace OHOS::Ace::NG {
45 namespace {
46 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
47 constexpr double CHAIN_SPRING_MASS = 1.0;
48 constexpr double CHAIN_SPRING_DAMPING = 30.0;
49 constexpr double CHAIN_SPRING_STIFFNESS = 228;
50 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
51 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
52 } // namespace
53 
OnModifyDone()54 void ListPattern::OnModifyDone()
55 {
56     Pattern::OnModifyDone();
57     auto host = GetHost();
58     CHECK_NULL_VOID(host);
59     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
60     CHECK_NULL_VOID(listLayoutProperty);
61     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
62     if (axis != GetAxis()) {
63         needReEstimateOffset_ = true;
64         SetAxis(axis);
65         ChangeAxis(GetHost());
66     }
67     if (!GetScrollableEvent()) {
68         AddScrollEvent();
69         auto scrollableEvent = GetScrollableEvent();
70         CHECK_NULL_VOID(scrollableEvent);
71         scrollable_ = scrollableEvent->GetScrollable();
72     }
73 
74     SetEdgeEffect();
75 
76     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
77     CHECK_NULL_VOID(paintProperty);
78     if (paintProperty->GetScrollBarProperty()) {
79         SetScrollBar(paintProperty->GetScrollBarProperty());
80     }
81 
82     SetChainAnimation();
83     if (multiSelectable_ && !isMouseEventInit_) {
84         InitMouseEvent();
85     }
86     if (!multiSelectable_ && isMouseEventInit_) {
87         UninitMouseEvent();
88     }
89     auto focusHub = host->GetFocusHub();
90     CHECK_NULL_VOID(focusHub);
91     InitOnKeyEvent(focusHub);
92     Register2DragDropManager();
93     SetAccessibilityAction();
94     if (IsNeedInitClickEventRecorder()) {
95         Pattern::InitClickEventRecorder();
96     }
97 }
98 
ChangeAxis(RefPtr<UINode> node)99 void ListPattern::ChangeAxis(RefPtr<UINode> node)
100 {
101     CHECK_NULL_VOID(node);
102     auto children = node->GetChildren();
103     for (const auto& child : children) {
104         if (AceType::InstanceOf<FrameNode>(child)) {
105             auto frameNode = AceType::DynamicCast<FrameNode>(child);
106             CHECK_NULL_VOID(frameNode);
107             auto listItemPattern = frameNode->GetPattern<ListItemPattern>();
108             if (listItemPattern) {
109                 listItemPattern->ChangeAxis(GetAxis());
110                 return;
111             }
112             auto listItemGroupPattern = frameNode->GetPattern<ListItemGroupPattern>();
113             if (listItemGroupPattern) {
114                 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
115                 ChangeAxis(child);
116             }
117         } else {
118             ChangeAxis(child);
119         }
120     }
121 }
122 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)123 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
124 {
125     if (config.skipMeasure && config.skipLayout) {
126         return false;
127     }
128     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
129     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
130     auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
131     CHECK_NULL_RETURN(listLayoutAlgorithm, false);
132     itemPosition_ = listLayoutAlgorithm->GetItemPosition();
133     maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
134     spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
135     auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
136     auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition();
137     bool isJump = listLayoutAlgorithm->NeedEstimateOffset();
138     auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
139     if (lanesLayoutAlgorithm) {
140         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
141         if (lanesLayoutAlgorithm->GetLanes() != lanes_) {
142             needReEstimateOffset_ = true;
143             auto item = swiperItem_.Upgrade();
144             if (item) {
145                 item->ResetSwipeStatus();
146             }
147         }
148         lanes_ = lanesLayoutAlgorithm->GetLanes();
149         laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
150     } else {
151         lanes_ = listLayoutAlgorithm->GetLanes();
152     }
153     float relativeOffset = UpdateTotalOffset(listLayoutAlgorithm, isJump);
154     auto isNeedUpdateIndex = true;
155     if (targetIndex_) {
156         AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_);
157         // AniamteToTarget does not need to update endIndex and startIndex in the first frame.
158         isNeedUpdateIndex = false;
159         targetIndex_.reset();
160         targetIndexInGroup_.reset();
161     }
162     if (predictSnapOffset.has_value()) {
163         if (scrollable_ && !(NearZero(predictSnapOffset.value()) && NearZero(scrollSnapVelocity_)) &&
164             !AnimateRunning()) {
165             scrollable_->StartScrollSnapMotion(predictSnapOffset.value(), scrollSnapVelocity_);
166             if (snapTrigOnScrollStart_) {
167                 FireOnScrollStart();
168             }
169         } else if (!snapTrigOnScrollStart_) {
170             OnAnimateStop();
171         }
172         scrollSnapVelocity_ = 0.0f;
173         predictSnapOffset_.reset();
174         snapTrigOnScrollStart_ = false;
175         if (predictSnapEndPos.has_value()) {
176             predictSnapEndPos_ = predictSnapEndPos;
177         } else {
178             predictSnapEndPos_.reset();
179         }
180     }
181     if (predictSnapEndPos.has_value() && predictSnapEndPos_.has_value() &&
182         !NearEqual(predictSnapEndPos.value(), predictSnapEndPos_.value())) {
183         if (scrollable_) {
184             scrollable_->UpdateScrollSnapEndWithOffset(
185                 predictSnapEndPos.value() - predictSnapEndPos_.value());
186         }
187         predictSnapEndPos_.reset();
188     }
189 
190     if (isScrollEnd_) {
191         auto host = GetHost();
192         CHECK_NULL_RETURN(host, false);
193         host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
194         // AccessibilityEventType::SCROLL_END
195         isScrollEnd_ = false;
196     }
197     currentDelta_ = 0.0f;
198     isNeedCheckOffset_ = false;
199     prevStartOffset_ = startMainPos_;
200     prevEndOffset_ = endMainPos_ - contentMainSize_ + contentEndOffset_;
201     contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
202     contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
203     contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
204     startMainPos_ = listLayoutAlgorithm->GetStartPosition();
205     endMainPos_ = listLayoutAlgorithm->GetEndPosition();
206     crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
207     auto endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
208     CheckScrollable();
209 
210     bool indexChanged = false;
211     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
212         if (isNeedUpdateIndex) {
213             indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
214                            (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
215                            (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
216         }
217     } else {
218         indexChanged =
219             (startIndex_ != listLayoutAlgorithm->GetStartIndex()) || (endIndex_ != listLayoutAlgorithm->GetEndIndex());
220     }
221     startIndexChanged_ = startIndex_ != listLayoutAlgorithm->GetStartIndex();
222     endIndexChanged_ = endIndex_ != listLayoutAlgorithm->GetEndIndex();
223     if (indexChanged) {
224         startIndex_ = listLayoutAlgorithm->GetStartIndex();
225         endIndex_ = listLayoutAlgorithm->GetEndIndex();
226         centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
227     }
228     ProcessEvent(indexChanged, relativeOffset, isJump);
229     HandleScrollBarOutBoundary();
230     UpdateScrollBarOffset();
231     if (config.frameSizeChange) {
232         if (GetScrollBar() != nullptr) {
233             GetScrollBar()->ScheduleDisappearDelayTask();
234         }
235     }
236     bool sizeDiminished =
237         !chainAnimation_ && IsOutOfBoundary(false) && (endOffset + relativeOffset - prevEndOffset_ < -0.1f);
238     CheckRestartSpring(sizeDiminished);
239 
240     DrivenRender(dirty);
241 
242     SetScrollSource(SCROLL_FROM_NONE);
243     isInitialized_ = true;
244     MarkSelectedItems();
245     UpdateListDirectionInCardStyle();
246     return true;
247 }
248 
UpdateListDirectionInCardStyle()249 void ListPattern::UpdateListDirectionInCardStyle()
250 {
251     if (isNeedToUpdateListDirection_) {
252         auto layoutProperty = GetLayoutProperty<ListLayoutProperty>();
253         layoutProperty->UpdateListDirection(Axis::VERTICAL);
254         isNeedToUpdateListDirection_ = false;
255     }
256 }
257 
GetScrollAlignByScrollSnapAlign() const258 ScrollAlign ListPattern::GetScrollAlignByScrollSnapAlign() const
259 {
260     auto scrollAlign = ScrollAlign::START;
261     auto host = GetHost();
262     CHECK_NULL_RETURN(host, scrollAlign);
263     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
264     CHECK_NULL_RETURN(listProperty, scrollAlign);
265     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
266     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
267         scrollAlign = ScrollAlign::CENTER;
268     }
269     return scrollAlign;
270 }
271 
CalculateTargetPos(float startPos,float endPos)272 float ListPattern::CalculateTargetPos(float startPos, float endPos)
273 {
274     float topOffset = startPos - contentStartOffset_;
275     float bottomOffset = endPos - contentMainSize_ + contentEndOffset_;
276     if (GreatOrEqual(topOffset, 0.0f) && LessOrEqual(bottomOffset, 0.0f)) {
277         return 0.0f;
278     }
279     if ((NearEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) ||
280         (LessNotEqual(topOffset, 0.0f) && NearEqual(bottomOffset, 0.0f))) {
281         return 0.0f;
282     }
283     if (LessNotEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) {
284         if (GreatOrEqual(std::abs(topOffset), std::abs(bottomOffset))) {
285             return bottomOffset;
286         } else {
287             return topOffset;
288         }
289     }
290     if (GreatNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
291         return bottomOffset;
292     } else if (LessNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
293         return topOffset;
294     } else {
295         if (LessNotEqual(topOffset, 0.0f)) {
296             return topOffset;
297         } else {
298             return bottomOffset;
299         }
300     }
301     return 0.0f;
302 }
303 
CreateNodePaintMethod()304 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
305 {
306     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
307     V2::ItemDivider divider;
308     if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
309         divider = listLayoutProperty->GetDivider().value();
310     }
311     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
312     auto drawVertical = (axis == Axis::HORIZONTAL);
313     auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, lanes_, spaceWidth_);
314     paint->SetScrollBar(GetScrollBar());
315     CreateScrollBarOverlayModifier();
316     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
317     paint->SetTotalItemCount(maxListItemIndex_ + 1);
318     auto layoutDirection = listLayoutProperty->GetNonAutoLayoutDirection();
319     if (layoutDirection == TextDirection::RTL) {
320         paint->SetDirection(true);
321     }
322     auto scrollEffect = GetScrollEdgeEffect();
323     if (scrollEffect && scrollEffect->IsFadeEffect()) {
324         paint->SetEdgeEffect(scrollEffect);
325     }
326     if (!listContentModifier_) {
327         auto host = GetHost();
328         CHECK_NULL_RETURN(host, paint);
329         auto renderContext = host->GetRenderContext();
330         CHECK_NULL_RETURN(renderContext, paint);
331         const auto& geometryNode = host->GetGeometryNode();
332         auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
333         auto& padding = geometryNode->GetPadding();
334         if (padding) {
335             size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
336         }
337         OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
338         listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
339     }
340     paint->SetLaneGutter(laneGutter_);
341     paint->SetItemsPosition(itemPosition_, pressedItem_);
342     paint->SetContentModifier(listContentModifier_);
343     return paint;
344 }
345 
UpdateStartListItemIndex()346 bool ListPattern::UpdateStartListItemIndex()
347 {
348     auto host = GetHost();
349     CHECK_NULL_RETURN(host, false);
350     auto startWrapper = host->GetOrCreateChildByIndex(startIndex_);
351     int32_t startArea = -1;
352     int32_t startItemIndexInGroup = -1;
353     bool startFlagChanged = (startInfo_.index != startIndex_);
354     bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
355     if (startIsGroup) {
356         auto startPattern = startWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
357         VisibleContentInfo startGroupInfo = startPattern->GetStartListItemIndex();
358         startFlagChanged = startFlagChanged || (startInfo_.area != startGroupInfo.area) ||
359                            (startInfo_.indexInGroup != startGroupInfo.indexInGroup);
360         startArea = startGroupInfo.area;
361         startItemIndexInGroup = startGroupInfo.indexInGroup;
362     }
363     startInfo_ = { startIndex_, startArea, startItemIndexInGroup };
364     return startFlagChanged;
365 }
366 
UpdateEndListItemIndex()367 bool ListPattern::UpdateEndListItemIndex()
368 {
369     auto host = GetHost();
370     CHECK_NULL_RETURN(host, false);
371     auto endWrapper = host->GetOrCreateChildByIndex(endIndex_);
372     int32_t endArea = -1;
373     int32_t endItemIndexInGroup = -1;
374     bool endFlagChanged = (endInfo_.index != endIndex_);
375     bool endIsGroup = endWrapper && endWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
376     if (endIsGroup) {
377         auto endPattern = endWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
378         VisibleContentInfo endGroupInfo = endPattern->GetEndListItemIndex();
379         endFlagChanged = endFlagChanged || (endInfo_.area != endGroupInfo.area) ||
380                          (endInfo_.indexInGroup != endGroupInfo.indexInGroup);
381         endArea = endGroupInfo.area;
382         endItemIndexInGroup = endGroupInfo.indexInGroup;
383     }
384     endInfo_ = { endIndex_, endArea, endItemIndexInGroup };
385     return endFlagChanged;
386 }
387 
ProcessEvent(bool indexChanged,float finalOffset,bool isJump)388 void ListPattern::ProcessEvent(bool indexChanged, float finalOffset, bool isJump)
389 {
390     auto host = GetHost();
391     CHECK_NULL_VOID(host);
392     auto listEventHub = host->GetEventHub<ListEventHub>();
393     CHECK_NULL_VOID(listEventHub);
394     paintStateFlag_ = !NearZero(finalOffset) && !isJump;
395     isFramePaintStateValid_ = true;
396     auto onScroll = listEventHub->GetOnScroll();
397     PrintOffsetLog(AceLogTag::ACE_LIST, host->GetId(), finalOffset);
398     if (onScroll) {
399         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
400             FireOnScroll(finalOffset, onScroll);
401         } else {
402             if (!NearZero(finalOffset)) {
403                 auto offsetPX = Dimension(finalOffset);
404                 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
405                 auto source = GetScrollSource();
406                 if (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR ||
407                     source == SCROLL_FROM_ANIMATION_CONTROLLER) {
408                     source = SCROLL_FROM_NONE;
409                 }
410                 onScroll(offsetVP, GetScrollState(source));
411             }
412         }
413     }
414     FireObserverOnDidScroll(finalOffset);
415     auto onDidScroll = listEventHub->GetOnDidScroll();
416     if (onDidScroll) {
417         FireOnScroll(finalOffset, onDidScroll);
418     }
419     auto onScrollIndex = listEventHub->GetOnScrollIndex();
420     FireOnScrollIndex(indexChanged, onScrollIndex);
421     auto onScrollVisibleContentChange = listEventHub->GetOnScrollVisibleContentChange();
422     if (onScrollVisibleContentChange) {
423         bool startChanged = UpdateStartListItemIndex();
424         bool endChanged = UpdateEndListItemIndex();
425         if (startChanged || endChanged) {
426             onScrollVisibleContentChange(startInfo_, endInfo_);
427         }
428     }
429     auto onReachStart = listEventHub->GetOnReachStart();
430     FireOnReachStart(onReachStart);
431     auto onReachEnd = listEventHub->GetOnReachEnd();
432     FireOnReachEnd(onReachEnd);
433     OnScrollStop(listEventHub->GetOnScrollStop());
434 }
435 
FireOnReachStart(const OnReachEvent & onReachStart)436 void ListPattern::FireOnReachStart(const OnReachEvent& onReachStart)
437 {
438     auto host = GetHost();
439     CHECK_NULL_VOID(host);
440     if (startIndex_ == 0) {
441         bool scrollUpToStart =
442             GreatNotEqual(prevStartOffset_, contentStartOffset_) && LessOrEqual(startMainPos_, contentStartOffset_);
443         bool scrollDownToStart =
444             (startIndexChanged_ || LessNotEqual(prevStartOffset_, contentStartOffset_) || !isInitialized_) &&
445             GreatOrEqual(startMainPos_, contentStartOffset_);
446         if (scrollUpToStart || scrollDownToStart) {
447             FireObserverOnReachStart();
448             CHECK_NULL_VOID(onReachStart);
449             ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:List",
450                 scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
451             onReachStart();
452             AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
453         }
454     }
455 }
456 
FireOnReachEnd(const OnReachEvent & onReachEnd)457 void ListPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
458 {
459     auto host = GetHost();
460     CHECK_NULL_VOID(host);
461     if (endIndex_ == maxListItemIndex_) {
462         float endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
463         // deltaOffset passes through multiple items also needs to fire reachEnd
464         bool scrollUpToEnd =
465             (endIndexChanged_ || (Positive(prevEndOffset_) || !isInitialized_)) && NonPositive(endOffset);
466         bool scrollDownToEnd = Negative(prevEndOffset_) && NonNegative(endOffset);
467         auto scrollSource = GetScrollSource();
468         if (scrollUpToEnd || (scrollDownToEnd && scrollSource != SCROLL_FROM_NONE)) {
469             FireObserverOnReachEnd();
470             CHECK_NULL_VOID(onReachEnd);
471             ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, scrollSource:%d, id:%d, tag:List",
472                 scrollUpToEnd, scrollDownToEnd, scrollSource, static_cast<int32_t>(host->GetAccessibilityId()));
473             onReachEnd();
474             AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
475         }
476     }
477 }
478 
FireOnScrollIndex(bool indexChanged,const OnScrollIndexEvent & onScrollIndex)479 void ListPattern::FireOnScrollIndex(bool indexChanged, const OnScrollIndexEvent& onScrollIndex)
480 {
481     CHECK_NULL_VOID(indexChanged && onScrollIndex);
482     int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
483     int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
484     onScrollIndex(startIndex, endIndex, centerIndex_);
485 }
486 
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)487 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
488 {
489     auto host = GetHost();
490     CHECK_NULL_VOID(host);
491     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
492     auto listPaintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
493     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
494     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
495     bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
496     auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
497     bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE || barNeedPaint ||
498                           chainAnimation || !isScrollable_);
499 
500     auto renderContext = host->GetRenderContext();
501     CHECK_NULL_VOID(renderContext);
502     renderContext->MarkDrivenRender(drivenRender);
503     if (drivenRender && isFramePaintStateValid_) {
504         // Mark items
505         int32_t indexStep = 0;
506         int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
507         for (auto& pos : itemPosition_) {
508             auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
509             CHECK_NULL_VOID(wrapper);
510             auto itemHost = wrapper->GetHostNode();
511             CHECK_NULL_VOID(itemHost);
512             auto itemRenderContext = itemHost->GetRenderContext();
513             CHECK_NULL_VOID(itemRenderContext);
514             itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
515             indexStep++;
516         }
517         renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
518         isFramePaintStateValid_ = false;
519     }
520 }
521 
CheckScrollable()522 void ListPattern::CheckScrollable()
523 {
524     auto host = GetHost();
525     CHECK_NULL_VOID(host);
526     auto hub = host->GetEventHub<EventHub>();
527     CHECK_NULL_VOID(hub);
528     auto gestureHub = hub->GetOrCreateGestureEventHub();
529     CHECK_NULL_VOID(gestureHub);
530     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
531     CHECK_NULL_VOID(listProperty);
532     if (itemPosition_.empty()) {
533         isScrollable_ = false;
534     } else {
535         if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
536             !IsScrollSnapAlignCenter()) {
537             isScrollable_ = GetAlwaysEnabled() || GreatNotEqual(endMainPos_ - startMainPos_,
538                 contentMainSize_ - contentStartOffset_ - contentEndOffset_);
539         } else {
540             isScrollable_ = true;
541         }
542     }
543 
544     SetScrollEnabled(isScrollable_);
545 
546     if (!listProperty->GetScrollEnabled().value_or(isScrollable_)) {
547         SetScrollEnabled(false);
548     }
549 }
550 
CreateLayoutAlgorithm()551 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
552 {
553     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
554     CHECK_NULL_RETURN(listLayoutProperty, nullptr);
555     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
556     if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
557         listLayoutProperty->HasLaneMaxLength()) {
558         auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
559         RefreshLanesItemRange();
560         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
561         lanesLayoutAlgorithm->SetLanes(lanes_);
562         listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
563     } else {
564         listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
565     }
566     if (!posMap_) {
567         posMap_ = MakeRefPtr<ListPositionMap>();
568     }
569     if (childrenSize_) {
570         listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_);
571         listLayoutAlgorithm->SetListPositionMap(posMap_);
572     }
573     if (!isInitialized_) {
574         jumpIndex_ = listLayoutProperty->GetInitialIndex().value_or(0);
575         if (NeedScrollSnapAlignEffect()) {
576             scrollAlign_ = GetScrollAlignByScrollSnapAlign();
577         }
578     }
579     if (jumpIndex_) {
580         listLayoutAlgorithm->SetIndex(jumpIndex_.value());
581         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
582         jumpIndex_.reset();
583     }
584     if (targetIndex_) {
585         listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
586         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
587     }
588     if (jumpIndexInGroup_) {
589         listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
590         jumpIndexInGroup_.reset();
591     }
592     if (targetIndexInGroup_) {
593         listLayoutAlgorithm->SetTargetIndexInGroup(targetIndexInGroup_.value());
594     }
595     if (predictSnapOffset_.has_value()) {
596         listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
597         listLayoutAlgorithm->SetScrollSnapVelocity(scrollSnapVelocity_);
598     }
599     listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
600     listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
601     listLayoutAlgorithm->SetIsNeedCheckOffset(isNeedCheckOffset_);
602     listLayoutAlgorithm->SetItemsPosition(itemPosition_);
603     listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
604     listLayoutAlgorithm->SetPrevContentStartOffset(contentStartOffset_);
605     listLayoutAlgorithm->SetPrevContentEndOffset(contentEndOffset_);
606     if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
607         listLayoutAlgorithm->SetOverScrollFeature();
608     }
609     listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
610     listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
611     if (chainAnimation_) {
612         SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
613         SetChainAnimationToPosMap();
614     }
615     if (predictSnapEndPos_.has_value()) {
616         listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value());
617     }
618     return listLayoutAlgorithm;
619 }
620 
SetChainAnimationToPosMap()621 void ListPattern::SetChainAnimationToPosMap()
622 {
623     CHECK_NULL_VOID(posMap_);
624     posMap_->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
625         auto list = weak.Upgrade();
626         CHECK_NULL_RETURN(list, 0.0f);
627         return list->GetChainDelta(index);
628     });
629 }
630 
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,RefPtr<ListLayoutProperty> listLayoutProperty)631 void ListPattern::SetChainAnimationLayoutAlgorithm(
632     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, RefPtr<ListLayoutProperty> listLayoutProperty)
633 {
634     CHECK_NULL_VOID(listLayoutAlgorithm);
635     CHECK_NULL_VOID(listLayoutProperty);
636     listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
637         auto list = weak.Upgrade();
638         CHECK_NULL_RETURN(list, 0.0f);
639         return list->GetChainDelta(index);
640     });
641     if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
642         listLayoutAlgorithm->SetChainInterval(CHAIN_INTERVAL_DEFAULT.ConvertToPx());
643     }
644 }
645 
IsScrollSnapAlignCenter() const646 bool ListPattern::IsScrollSnapAlignCenter() const
647 {
648     auto host = GetHost();
649     CHECK_NULL_RETURN(host, false);
650     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
651     CHECK_NULL_RETURN(listProperty, false);
652     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
653     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
654         return true;
655     }
656 
657     return false;
658 }
659 
NeedScrollSnapAlignEffect() const660 bool ListPattern::NeedScrollSnapAlignEffect() const
661 {
662     auto host = GetHost();
663     CHECK_NULL_RETURN(host, false);
664     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
665     CHECK_NULL_RETURN(listProperty, false);
666     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
667     if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
668         return false;
669     }
670 
671     return true;
672 }
673 
IsAtTop() const674 bool ListPattern::IsAtTop() const
675 {
676     bool groupAtStart = true;
677     bool groupAtEnd = true;
678     GetListItemGroupEdge(groupAtStart, groupAtEnd);
679     int32_t startIndex = startIndex_;
680     float startMainPos = startMainPos_;
681     return (startIndex == 0 && groupAtStart) &&
682            NonNegative(startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_);
683 }
684 
IsAtBottom() const685 bool ListPattern::IsAtBottom() const
686 {
687     bool groupAtStart = true;
688     bool groupAtEnd = true;
689     GetListItemGroupEdge(groupAtStart, groupAtEnd);
690     int32_t endIndex = endIndex_;
691     float endMainPos = endMainPos_;
692     return (endIndex == maxListItemIndex_ && groupAtEnd) &&
693            LessOrEqual(endMainPos - currentDelta_ + GetChainDelta(endIndex), contentMainSize_ - contentEndOffset_);
694 }
695 
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const696 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
697 {
698     if (itemPosition_.empty()) {
699         return;
700     }
701     if (startIndex_ == 0 && itemPosition_.begin()->second.isGroup) {
702         auto& groupInfo = itemPosition_.begin()->second.groupInfo;
703         groupAtStart = groupInfo && groupInfo.value().atStart;
704     }
705     if (endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup) {
706         auto& groupInfo = itemPosition_.rbegin()->second.groupInfo;
707         groupAtEnd = groupInfo && groupInfo.value().atEnd;
708     }
709 }
710 
GetOffsetWithLimit(float offset) const711 float ListPattern::GetOffsetWithLimit(float offset) const
712 {
713     auto currentOffset = GetTotalOffset() + contentStartOffset_;
714     if (Positive(offset)) {
715         return std::min(currentOffset, offset);
716     } else if (Negative(offset)) {
717         auto remainHeight = GetTotalHeight() - currentOffset;
718         return std::max(offset, -remainHeight);
719     }
720     return 0;
721 }
722 
GetOverScrollOffset(double delta) const723 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
724 {
725     OverScrollOffset offset = { 0, 0 };
726     bool groupAtStart = true;
727     bool groupAtEnd = true;
728     GetListItemGroupEdge(groupAtStart, groupAtEnd);
729 
730     int32_t startIndex = startIndex_;
731     float startMainPos = startMainPos_;
732     int32_t endIndex = endIndex_;
733     float endMainPos = endMainPos_;
734     if (startIndex == 0 && groupAtStart) {
735         offset.start = GetStartOverScrollOffset(delta, startMainPos);
736     }
737     if (endIndex == maxListItemIndex_ && groupAtEnd) {
738         offset.end = GetEndOverScrollOffset(delta, endMainPos, startMainPos);
739     }
740     return offset;
741 }
742 
GetStartOverScrollOffset(float offset,float startMainPos) const743 float ListPattern::GetStartOverScrollOffset(float offset, float startMainPos) const
744 {
745     float startOffset = 0.0f;
746     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(0, -offset) : 0.f;
747     auto startPos = startMainPos + ChainDelta - currentDelta_;
748     auto newStartPos = startPos + offset;
749     if (startPos > contentStartOffset_ && newStartPos > contentStartOffset_) {
750         startOffset = offset;
751     }
752     if (startPos > contentStartOffset_ && newStartPos <= contentStartOffset_) {
753         startOffset = contentStartOffset_ - startPos;
754     }
755     if (startPos <= contentStartOffset_ && newStartPos > contentStartOffset_) {
756         startOffset = newStartPos - contentStartOffset_;
757     }
758     return startOffset;
759 }
760 
GetEndOverScrollOffset(float offset,float endMainPos,float startMainPos) const761 float ListPattern::GetEndOverScrollOffset(float offset, float endMainPos, float startMainPos) const
762 {
763     float endOffset = 0.0f;
764     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(maxListItemIndex_, -offset) : 0.f;
765     auto endPos = endMainPos + ChainDelta - currentDelta_;
766     auto contentEndPos = contentMainSize_ - contentEndOffset_;
767     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
768     if (GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
769         endPos = startMainPos + contentMainSize;
770     }
771     auto newEndPos = endPos + offset;
772     if (endPos < contentEndPos && newEndPos < contentEndPos) {
773         endOffset = offset;
774     }
775     if (endPos < contentEndPos && newEndPos >= contentEndPos) {
776         endOffset = contentEndPos - endPos;
777     }
778     if (endPos >= contentEndPos && newEndPos < contentEndPos) {
779         endOffset = newEndPos - contentEndPos;
780     }
781     return endOffset;
782 }
783 
GetOutBoundaryOffset(bool useCurrentDelta) const784 OverScrollOffset ListPattern::GetOutBoundaryOffset(bool useCurrentDelta) const
785 {
786     OverScrollOffset offset = { 0, 0 };
787     bool groupAtStart = true;
788     bool groupAtEnd = true;
789     GetListItemGroupEdge(groupAtStart, groupAtEnd);
790 
791     int32_t startIndex = startIndex_;
792     float startMainPos = startMainPos_;
793     int32_t endIndex = endIndex_;
794     float endMainPos = endMainPos_;
795     if (startIndex == 0 && groupAtStart) {
796         if (useCurrentDelta) {
797             offset.start = startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_;
798         } else {
799             offset.start = startMainPos + GetChainDelta(0) - contentStartOffset_;
800         }
801         offset.start = std::max(offset.start, 0.0);
802     }
803     if (endIndex >= maxListItemIndex_ && groupAtEnd) {
804         endMainPos = endMainPos + GetChainDelta(endIndex);
805         auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
806         if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
807             endMainPos = startMainPos + contentMainSize;
808         }
809         if (useCurrentDelta) {
810             offset.end = contentMainSize_ - contentEndOffset_ - (endMainPos - currentDelta_);
811         } else {
812             offset.end = contentMainSize_ - contentEndOffset_ - endMainPos;
813         }
814         offset.end = std::max(offset.end, 0.0);
815     }
816     return offset;
817 }
818 
UpdateCurrentOffset(float offset,int32_t source)819 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
820 {
821     // check edgeEffect is not springEffect
822     if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
823         if (IsOutOfBoundary(false)) {
824             MarkDirtyNodeSelf();
825         }
826         return false;
827     }
828     SetScrollSource(source);
829     FireAndCleanScrollingListener();
830     auto lastDelta = currentDelta_;
831     currentDelta_ = currentDelta_ - offset;
832     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING) {
833         isNeedCheckOffset_ = true;
834     }
835     if (!NearZero(offset)) {
836         MarkDirtyNodeSelf();
837     }
838     if (itemPosition_.empty() || !IsOutOfBoundary() || !isScrollable_) {
839         auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
840         currentDelta_ = lastDelta + userOffset;
841         return true;
842     }
843 
844     if (GetScrollSource() == SCROLL_FROM_UPDATE) {
845         auto res = GetOutBoundaryOffset(true);
846         // over scroll in drag update from normal to over scroll.
847         float overScroll = std::max(res.start, res.end);
848         // adjust offset.
849         auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
850         currentDelta_ = currentDelta_ * friction;
851     }
852 
853     auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
854     currentDelta_ = lastDelta + userOffset;
855     return true;
856 }
857 
MarkDirtyNodeSelf()858 void ListPattern::MarkDirtyNodeSelf()
859 {
860     auto host = GetHost();
861     CHECK_NULL_VOID(host);
862     if (!crossMatchChild_) {
863         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
864     } else {
865         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
866     }
867 }
868 
OnScrollEndCallback()869 void ListPattern::OnScrollEndCallback()
870 {
871     if (AnimateStoped()) {
872         scrollStop_ = true;
873         MarkDirtyNodeSelf();
874     }
875 }
876 
GetContentSize() const877 SizeF ListPattern::GetContentSize() const
878 {
879     auto host = GetHost();
880     CHECK_NULL_RETURN(host, SizeF());
881     auto geometryNode = host->GetGeometryNode();
882     CHECK_NULL_RETURN(geometryNode, SizeF());
883     auto renderContext = host->GetRenderContext();
884     CHECK_NULL_RETURN(renderContext, SizeF());
885     auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
886     auto& padding = geometryNode->GetPadding();
887     if (padding) {
888         size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
889     }
890     return size;
891 }
892 
IsOutOfBoundary(bool useCurrentDelta)893 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
894 {
895     if (itemPosition_.empty()) {
896         return false;
897     }
898     auto res = GetOutBoundaryOffset(useCurrentDelta);
899     // over scroll in drag update from normal to over scroll.
900     return Positive(res.start) || Positive(res.end);
901 }
902 
OnScrollCallback(float offset,int32_t source)903 bool ListPattern::OnScrollCallback(float offset, int32_t source)
904 {
905     if (source == SCROLL_FROM_START) {
906         auto item = swiperItem_.Upgrade();
907         if (item) {
908             item->ResetSwipeStatus();
909         }
910         FireOnScrollStart();
911         return true;
912     }
913     ProcessDragUpdate(offset, source);
914     return UpdateCurrentOffset(offset, source);
915 }
916 
OnScrollSnapCallback(double targetOffset,double velocity)917 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
918 {
919     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
920     CHECK_NULL_RETURN(listProperty, false);
921     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
922     if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
923         return false;
924     }
925     if (AnimateRunning()) {
926         return false;
927     }
928     if (!GetIsDragging()) {
929         snapTrigOnScrollStart_ = true;
930     }
931     predictSnapOffset_ = targetOffset;
932     scrollSnapVelocity_ = velocity;
933     MarkDirtyNodeSelf();
934     return true;
935 }
936 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)937 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
938 {
939     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
940         auto list = weak.Upgrade();
941         CHECK_NULL_RETURN(list, 0.0);
942         return list->startMainPos_ - list->currentDelta_;
943     });
944     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
945         auto list = weak.Upgrade();
946         auto endPos = list->endMainPos_;
947         auto startPos = list->startMainPos_;
948         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
949         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
950     });
951     scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
952         auto list = weak.Upgrade();
953         CHECK_NULL_RETURN(list, 0.0);
954         return list->contentStartOffset_;
955     });
956     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
957         auto list = weak.Upgrade();
958         auto endPos = list->endMainPos_;
959         auto startPos = list->startMainPos_;
960         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
961         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
962     });
963     scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
964         auto list = weak.Upgrade();
965         CHECK_NULL_RETURN(list, 0.0);
966         return list->contentStartOffset_;
967     });
968 }
969 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)970 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
971 {
972     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
973         auto pattern = wp.Upgrade();
974         CHECK_NULL_RETURN(pattern, false);
975         return pattern->OnKeyEvent(event);
976     };
977     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
978 }
979 
OnKeyEvent(const KeyEvent & event)980 bool ListPattern::OnKeyEvent(const KeyEvent& event)
981 {
982     if (event.action != KeyAction::DOWN) {
983         return false;
984     }
985     if (event.code == KeyCode::KEY_PAGE_DOWN) {
986         ScrollPage(false);
987         return true;
988     }
989     if (event.code == KeyCode::KEY_PAGE_UP) {
990         ScrollPage(true);
991         return true;
992     }
993     return HandleDirectionKey(event);
994 }
995 
HandleDirectionKey(const KeyEvent & event)996 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
997 {
998     return false;
999 }
1000 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1001 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1002 {
1003     auto curFocus = currentFocusNode.Upgrade();
1004     CHECK_NULL_RETURN(curFocus, nullptr);
1005     auto curFrame = curFocus->GetFrameNode();
1006     CHECK_NULL_RETURN(curFrame, nullptr);
1007     auto curPattern = curFrame->GetPattern();
1008     CHECK_NULL_RETURN(curPattern, nullptr);
1009     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
1010     CHECK_NULL_RETURN(curItemPattern, nullptr);
1011     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1012     CHECK_NULL_RETURN(listProperty, nullptr);
1013 
1014     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1015     auto curIndex = curItemPattern->GetIndexInList();
1016     auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
1017     auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
1018     if (curIndex < 0 || curIndex > maxListItemIndex_) {
1019         return nullptr;
1020     }
1021 
1022     auto moveStep = 0;
1023     auto nextIndex = curIndex;
1024     auto nextIndexInGroup = curIndexInGroup;
1025     if (lanes_ <= 1) {
1026         if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
1027             moveStep = 1;
1028             nextIndex = 0;
1029             nextIndexInGroup = -1;
1030         } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
1031             moveStep = -1;
1032             nextIndex = maxListItemIndex_;
1033             nextIndexInGroup = -1;
1034         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
1035                    (step == FocusStep::TAB)) {
1036             moveStep = 1;
1037             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1038                 nextIndex = curIndex + moveStep;
1039                 nextIndexInGroup = -1;
1040             } else {
1041                 nextIndexInGroup = curIndexInGroup + moveStep;
1042             }
1043         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
1044                    (step == FocusStep::SHIFT_TAB)) {
1045             moveStep = -1;
1046             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1047                 nextIndex = curIndex + moveStep;
1048                 nextIndexInGroup = -1;
1049             } else {
1050                 nextIndexInGroup = curIndexInGroup + moveStep;
1051             }
1052         }
1053     } else {
1054         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1055             moveStep = 1;
1056             nextIndex = 0;
1057             nextIndexInGroup = -1;
1058         } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1059             moveStep = -1;
1060             nextIndex = maxListItemIndex_;
1061             nextIndexInGroup = -1;
1062         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1063             if (curIndexInGroup == -1) {
1064                 moveStep = lanes_;
1065                 nextIndex = curIndex + moveStep;
1066                 nextIndexInGroup = -1;
1067             } else {
1068                 moveStep = curListItemGroupPara.lanes;
1069                 nextIndexInGroup = curIndexInGroup + moveStep;
1070             }
1071         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1072             if (curIndexInGroup == -1) {
1073                 moveStep = -lanes_;
1074                 nextIndex = curIndex + moveStep;
1075                 nextIndexInGroup = -1;
1076             } else {
1077                 moveStep = -curListItemGroupPara.lanes;
1078                 nextIndexInGroup = curIndexInGroup + moveStep;
1079             }
1080         } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1081             moveStep = 1;
1082             if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1083                 ((curIndexInGroup != -1) &&
1084                     ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1085                 nextIndex = curIndex + moveStep;
1086                 nextIndexInGroup = -1;
1087             } else if ((curIndexInGroup != -1) &&
1088                        ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1089                 nextIndexInGroup = curIndexInGroup + moveStep;
1090             }
1091         } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1092             moveStep = -1;
1093             if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1094                 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1095                 nextIndex = curIndex + moveStep;
1096                 nextIndexInGroup = -1;
1097             } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1098                 nextIndexInGroup = curIndexInGroup + moveStep;
1099             }
1100         } else if (step == FocusStep::TAB) {
1101             moveStep = 1;
1102             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1103                 nextIndex = curIndex + moveStep;
1104                 nextIndexInGroup = -1;
1105             } else {
1106                 nextIndexInGroup = curIndexInGroup + moveStep;
1107             }
1108         } else if (step == FocusStep::SHIFT_TAB) {
1109             moveStep = -1;
1110             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1111                 nextIndex = curIndex + moveStep;
1112                 nextIndexInGroup = -1;
1113             } else {
1114                 nextIndexInGroup = curIndexInGroup + moveStep;
1115             }
1116         }
1117     }
1118     while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1119         if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1120             return nullptr;
1121         }
1122         auto nextFocusNode =
1123             ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1124         if (nextFocusNode.Upgrade()) {
1125             return nextFocusNode;
1126         }
1127         if (nextIndexInGroup > -1) {
1128             nextIndexInGroup += moveStep;
1129         } else {
1130             nextIndex += moveStep;
1131         }
1132     }
1133     return nullptr;
1134 }
1135 
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1136 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1137 {
1138     auto listFrame = GetHost();
1139     CHECK_NULL_RETURN(listFrame, nullptr);
1140     auto listFocus = listFrame->GetFocusHub();
1141     CHECK_NULL_RETURN(listFocus, nullptr);
1142     auto childFocusList = listFocus->GetChildren();
1143     for (const auto& childFocus : childFocusList) {
1144         if (!childFocus->IsFocusable()) {
1145             continue;
1146         }
1147         auto childFrame = childFocus->GetFrameNode();
1148         if (!childFrame) {
1149             continue;
1150         }
1151         auto childPattern = childFrame->GetPattern();
1152         if (!childPattern) {
1153             continue;
1154         }
1155         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1156         if (!childItemPattern) {
1157             continue;
1158         }
1159         auto curIndex = childItemPattern->GetIndexInList();
1160         auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1161         if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1162             return AceType::WeakClaim(AceType::RawPtr(childFocus));
1163         }
1164     }
1165     return nullptr;
1166 }
1167 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1168 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1169 {
1170     CHECK_NULL_RETURN(focusFrameNode, false);
1171     auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1172     CHECK_NULL_RETURN(focusPattern, false);
1173     auto curIndex = focusPattern->GetIndexInList();
1174     ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1175     auto pipeline = GetContext();
1176     if (pipeline) {
1177         pipeline->FlushUITasks();
1178     }
1179     return true;
1180 }
1181 
GetScrollOffsetAbility()1182 std::pair<std::function<bool(float)>, Axis> ListPattern::GetScrollOffsetAbility()
1183 {
1184     return { [wp = WeakClaim(this)](float moveOffset) -> bool {
1185                 auto pattern = wp.Upgrade();
1186                 CHECK_NULL_RETURN(pattern, false);
1187                 pattern->ScrollBy(-moveOffset);
1188                 return true;
1189             },
1190         GetAxis() };
1191 }
1192 
GetScrollIndexAbility()1193 std::function<bool(int32_t)> ListPattern::GetScrollIndexAbility()
1194 {
1195     return [wp = WeakClaim(this)](int32_t index) -> bool {
1196         auto pattern = wp.Upgrade();
1197         CHECK_NULL_RETURN(pattern, false);
1198         if (index == FocusHub::SCROLL_TO_HEAD) {
1199             pattern->ScrollToIndex(0, false, ScrollAlign::START);
1200         } else if (index == FocusHub::SCROLL_TO_TAIL) {
1201             pattern->ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, false, ScrollAlign::END);
1202         } else {
1203             pattern->ScrollToIndex(index, false, ScrollAlign::AUTO);
1204         }
1205         return true;
1206     };
1207 }
1208 
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1209 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1210     int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1211 {
1212     auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1213     auto groupIndexInGroup = ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup,
1214         curIndexInGroup, moveStep, step, isScrollIndex);
1215 
1216     return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1217 }
1218 
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1219 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1220 {
1221     auto isScrollIndex = false;
1222     auto pipeline = PipelineContext::GetCurrentContext();
1223     CHECK_NULL_RETURN(pipeline, isScrollIndex);
1224     if (nextIndex < startIndex_) {
1225         if (nextIndexInGroup == -1) {
1226             isScrollIndex = true;
1227             ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1228             pipeline->FlushUITasks();
1229         } else {
1230             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1231             pipeline->FlushUITasks();
1232         }
1233     } else if (nextIndex > endIndex_) {
1234         if (nextIndexInGroup == -1) {
1235             isScrollIndex = true;
1236             ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1237             pipeline->FlushUITasks();
1238         } else {
1239             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1240             pipeline->FlushUITasks();
1241         }
1242     }
1243     return isScrollIndex;
1244 }
1245 
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1246 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1247     int32_t moveStep, FocusStep step, bool isScrollIndex)
1248 {
1249     auto groupIndexInGroup = true;
1250     auto pipeline = PipelineContext::GetCurrentContext();
1251     CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1252     RefPtr<FrameNode> nextIndexNode;
1253     auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1254     CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1255     if (!isNextInGroup) {
1256         nextIndexInGroup = -1;
1257         return groupIndexInGroup;
1258     }
1259     auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1260     if (nextIndexInGroup == -1) {
1261         auto scrollAlign = ScrollAlign::END;
1262         nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1263         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1264             (step == FocusStep::RIGHT_END)) {
1265             scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1266         } else {
1267             scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1268         }
1269         if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1270             (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1271             ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1272             pipeline->FlushUITasks();
1273         }
1274     } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1275         nextIndexInGroup = -1;
1276         groupIndexInGroup = false;
1277     } else {
1278         if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1279             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1280             pipeline->FlushUITasks();
1281         } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1282             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1283             pipeline->FlushUITasks();
1284         }
1285     }
1286     return groupIndexInGroup;
1287 }
1288 
OnAnimateStop()1289 void ListPattern::OnAnimateStop()
1290 {
1291     if (!GetIsDragging() || GetScrollAbort()) {
1292         scrollStop_ = true;
1293         MarkDirtyNodeSelf();
1294         isScrollEnd_ = true;
1295     }
1296     scrollTarget_.reset();
1297 }
1298 
ScrollTo(float position)1299 void ListPattern::ScrollTo(float position)
1300 {
1301     StopAnimate();
1302     jumpIndex_.reset();
1303     targetIndex_.reset();
1304     currentDelta_ = 0.0f;
1305     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1306     MarkDirtyNodeSelf();
1307     isScrollEnd_ = true;
1308 }
1309 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1310 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1311 {
1312     SetScrollSource(SCROLL_FROM_JUMP);
1313     if (!smooth) {
1314         StopAnimate();
1315     }
1316     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1317         currentDelta_ = 0.0f;
1318         smooth_ = smooth;
1319         if (smooth_) {
1320             SetExtraOffset(extraOffset);
1321             if (!AnimateToTarget(index, std::nullopt, align)) {
1322                 targetIndex_ = index;
1323                 scrollAlign_ = align;
1324             }
1325         } else {
1326             if (extraOffset.has_value()) {
1327                 currentDelta_ = extraOffset.value();
1328             }
1329             jumpIndex_ = index;
1330             scrollAlign_ = align;
1331         }
1332         MarkDirtyNodeSelf();
1333     }
1334     isScrollEnd_ = true;
1335     FireAndCleanScrollingListener();
1336 }
1337 
CheckTargetValid(int32_t index,int32_t indexInGroup)1338 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1339 {
1340     auto host = GetHost();
1341     auto totalItemCount = host->GetTotalChildCount();
1342     if ((index < 0) || (index >= totalItemCount)) {
1343         return false;
1344     }
1345     auto groupWrapper = host->GetOrCreateChildByIndex(index);
1346     CHECK_NULL_RETURN(groupWrapper, false);
1347     if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1348         return false;
1349     }
1350     auto groupNode = groupWrapper->GetHostNode();
1351     CHECK_NULL_RETURN(groupNode, false);
1352     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1353     CHECK_NULL_RETURN(groupPattern, false);
1354     auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1355     if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1356         return false;
1357     }
1358     return true;
1359 }
1360 
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1361 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1362 {
1363     SetScrollSource(SCROLL_FROM_JUMP);
1364     StopAnimate();
1365     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1366         currentDelta_ = 0.0f;
1367         smooth_ = smooth;
1368         if (smooth_) {
1369             if (!AnimateToTarget(index, indexInGroup, align)) {
1370                 if (CheckTargetValid(index, indexInGroup)) {
1371                     targetIndex_ = index;
1372                     currentDelta_ = 0;
1373                     targetIndexInGroup_ = indexInGroup;
1374                     scrollAlign_ = align;
1375                 }
1376             }
1377         } else {
1378             jumpIndex_ = index;
1379             jumpIndexInGroup_ = indexInGroup;
1380             scrollAlign_ = align;
1381         }
1382         MarkDirtyNodeSelf();
1383     }
1384     isScrollEnd_ = true;
1385     FireAndCleanScrollingListener();
1386 }
1387 
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1388 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1389 {
1390     switch (align) {
1391         case ScrollAlign::START:
1392         case ScrollAlign::NONE:
1393             targetPos = startPos;
1394             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1395                 targetPos -= contentStartOffset_;
1396             }
1397             break;
1398         case ScrollAlign::CENTER:
1399             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1400             break;
1401         case ScrollAlign::END:
1402             targetPos = endPos - contentMainSize_;
1403             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1404                 targetPos += contentEndOffset_;
1405             }
1406             break;
1407         case ScrollAlign::AUTO:
1408             targetPos = CalculateTargetPos(startPos, endPos);
1409             break;
1410     }
1411     return true;
1412 }
1413 
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1414 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(
1415     int32_t index, float startPos, float endPos, ScrollAlign align, float& targetPos)
1416 {
1417     auto host = GetHost();
1418     CHECK_NULL_RETURN(host, false);
1419     auto groupWrapper = host->GetChildByIndex(index);
1420     CHECK_NULL_RETURN(groupWrapper, false);
1421     auto groupNode = groupWrapper->GetHostNode();
1422     CHECK_NULL_RETURN(groupNode, false);
1423     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1424     CHECK_NULL_RETURN(groupPattern, false);
1425     auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1426     CHECK_NULL_RETURN(groupLayoutProperty, false);
1427     auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1428 
1429     switch (align) {
1430         case ScrollAlign::START:
1431         case ScrollAlign::NONE:
1432             if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1433                 return false;
1434             }
1435             targetPos = startPos;
1436             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1437                 targetPos -= contentStartOffset_;
1438             }
1439             break;
1440         case ScrollAlign::CENTER:
1441             if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1442                 return false;
1443             }
1444             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1445             break;
1446         case ScrollAlign::END:
1447             if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1448                 return false;
1449             }
1450             targetPos = endPos - contentMainSize_;
1451             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1452                 targetPos += contentEndOffset_;
1453             }
1454             break;
1455         case ScrollAlign::AUTO:
1456             if (targetIndex_.has_value()) {
1457                 targetPos = CalculateTargetPos(startPos, endPos);
1458                 return true;
1459             }
1460             return false;
1461     }
1462 
1463     return true;
1464 }
1465 
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1466 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(
1467     int32_t index, int32_t indexInGroup, float startPos, ScrollAlign align, float& targetPos)
1468 {
1469     auto host = GetHost();
1470     CHECK_NULL_RETURN(host, false);
1471     auto groupWrapper = host->GetChildByIndex(index);
1472     CHECK_NULL_RETURN(groupWrapper, false);
1473     auto groupNode = groupWrapper->GetHostNode();
1474     CHECK_NULL_RETURN(groupNode, false);
1475     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1476     CHECK_NULL_RETURN(groupPattern, false);
1477     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1478     CHECK_NULL_RETURN(listLayoutProperty, false);
1479     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1480     auto itemsPosInGroup = groupPattern->GetItemPosition();
1481     auto it = itemsPosInGroup.find(indexInGroup);
1482     if (it == itemsPosInGroup.end()) {
1483         return false;
1484     }
1485     auto axis = GetAxis();
1486     std::optional<float> padding;
1487     std::optional<float> margin;
1488     if (axis == Axis::HORIZONTAL) {
1489         padding = IsReverse() ? groupWrapper->GetGeometryNode()->GetPadding()->right
1490                               : groupWrapper->GetGeometryNode()->GetPadding()->left;
1491         margin = IsReverse() ? groupWrapper->GetGeometryNode()->GetMargin()->right
1492                              : groupWrapper->GetGeometryNode()->GetMargin()->left;
1493     } else {
1494         padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1495         margin = groupWrapper->GetGeometryNode()->GetMargin()->top;
1496     }
1497     auto marginValue = margin.value_or(0.f);
1498     auto paddingValue = padding.value_or(0.f);
1499     if (align == ScrollAlign::CENTER) {
1500         targetPos = paddingValue + marginValue + startPos + (it->second.startPos + it->second.endPos) / 2.0f -
1501                     contentMainSize_ / 2.0f;
1502     } else {
1503         float itemStartPos = paddingValue + marginValue + startPos + it->second.startPos;
1504         float itemEndPos = paddingValue + marginValue + startPos + it->second.endPos;
1505         if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1506             itemStartPos -= groupPattern->GetHeaderMainSize();
1507         }
1508         if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1509             itemEndPos += groupPattern->GetFooterMainSize();
1510         }
1511         if (!IsScrollSnapAlignCenter() || childrenSize_) {
1512             itemStartPos -= contentStartOffset_;
1513             itemEndPos += contentEndOffset_;
1514         }
1515         if (align == ScrollAlign::AUTO) {
1516             targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1517         } else {
1518             targetPos = align == ScrollAlign::END ? itemEndPos - contentMainSize_ : itemStartPos;
1519         }
1520     }
1521     return true;
1522 }
1523 
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)1524 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
1525 {
1526     auto iter = itemPosition_.find(index);
1527     if (iter == itemPosition_.end()) {
1528         return false;
1529     }
1530     float targetPos = 0.0f;
1531     if (iter->second.isGroup) {
1532         if (indexInGroup.has_value()) {
1533             if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
1534                 align, targetPos)) {
1535                 return false;
1536             }
1537         } else {
1538             if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
1539                 align, targetPos)) {
1540                 return false;
1541             }
1542         }
1543     } else {
1544         if (indexInGroup.has_value()) {
1545             return false;
1546         }
1547         GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
1548     }
1549     float extraOffset = 0.0f;
1550     if (GetExtraOffset().has_value()) {
1551         extraOffset = GetExtraOffset().value();
1552         targetPos += extraOffset;
1553         ResetExtraOffset();
1554     }
1555     if (!NearZero(targetPos)) {
1556         AnimateTo(targetPos + currentOffset_, -1, nullptr, true);
1557         if (predictSnapOffset_.has_value() && AnimateRunning()) {
1558             scrollSnapVelocity_ = 0.0f;
1559             predictSnapOffset_.reset();
1560             snapTrigOnScrollStart_ = false;
1561         }
1562         if (!indexInGroup.has_value()) {
1563             scrollTarget_ = { index, extraOffset, align, targetPos + currentOffset_ };
1564         }
1565     }
1566     return true;
1567 }
1568 
ScrollPage(bool reverse,AccessibilityScrollType scrollType)1569 bool ListPattern::ScrollPage(bool reverse, AccessibilityScrollType scrollType)
1570 {
1571     LOGI("ScrollPage:%{public}d", reverse);
1572     StopAnimate();
1573     float distance = reverse ? contentMainSize_ : -contentMainSize_;
1574     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
1575         distance = distance / 2.f;
1576     }
1577     UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1578     isScrollEnd_ = true;
1579     return true;
1580 }
1581 
ScrollBy(float offset)1582 void ListPattern::ScrollBy(float offset)
1583 {
1584     StopAnimate();
1585     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1586     isScrollEnd_ = true;
1587 }
1588 
GetCurrentOffset() const1589 Offset ListPattern::GetCurrentOffset() const
1590 {
1591     if (GetAxis() == Axis::HORIZONTAL) {
1592         return { GetTotalOffset(), 0.0 };
1593     }
1594     return { 0.0, GetTotalOffset() };
1595 }
1596 
HandleScrollBarOutBoundary()1597 void ListPattern::HandleScrollBarOutBoundary()
1598 {
1599     if (itemPosition_.empty()) {
1600         return;
1601     }
1602     if (!GetScrollBar() && !GetScrollBarProxy()) {
1603         return;
1604     }
1605     if (!IsOutOfBoundary(false) || !isScrollable_) {
1606         ScrollablePattern::HandleScrollBarOutBoundary(0);
1607         return;
1608     }
1609     float overScroll = 0.0f;
1610     if (!IsScrollSnapAlignCenter()) {
1611         if ((itemPosition_.begin()->first == 0) && GreatNotEqual(startMainPos_, contentStartOffset_)) {
1612             overScroll = startMainPos_ - contentStartOffset_;
1613         } else {
1614             overScroll = contentMainSize_ - contentEndOffset_ - endMainPos_;
1615         }
1616     } else {
1617         float itemHeight = itemPosition_[centerIndex_].endPos - itemPosition_[centerIndex_].startPos;
1618         if (startIndex_ == 0 && Positive(startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1619             overScroll = startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1620         } else if ((endIndex_ == maxListItemIndex_) &&
1621                    LessNotEqual(endMainPos_ - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
1622             overScroll = endMainPos_ - itemHeight / 2.0f - contentMainSize_ / 2.0f;
1623         }
1624     }
1625     ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
1626 }
1627 
GetItemRect(int32_t index) const1628 Rect ListPattern::GetItemRect(int32_t index) const
1629 {
1630     if (index < 0 || index < startIndex_ || index > endIndex_) {
1631         return Rect();
1632     }
1633     auto host = GetHost();
1634     CHECK_NULL_RETURN(host, Rect());
1635     auto item = host->GetChildByIndex(index);
1636     CHECK_NULL_RETURN(item, Rect());
1637     auto itemGeometry = item->GetGeometryNode();
1638     CHECK_NULL_RETURN(itemGeometry, Rect());
1639     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1640         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1641 }
1642 
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const1643 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
1644 {
1645     if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
1646         return Rect();
1647     }
1648     auto host = GetHost();
1649     CHECK_NULL_RETURN(host, Rect());
1650     auto itemGroupWrapper = host->GetChildByIndex(index);
1651     CHECK_NULL_RETURN(itemGroupWrapper, Rect());
1652     auto itemGroup = itemGroupWrapper->GetHostNode();
1653     CHECK_NULL_RETURN(itemGroup, Rect());
1654     if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
1655         return Rect();
1656     }
1657     auto itemGroupGeometry = itemGroup->GetGeometryNode();
1658     CHECK_NULL_RETURN(itemGroupGeometry, Rect());
1659     auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1660     CHECK_NULL_RETURN(groupPattern, Rect());
1661     if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
1662         indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
1663         return Rect();
1664     }
1665     auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
1666     CHECK_NULL_RETURN(groupItem, Rect());
1667     auto groupItemGeometry = groupItem->GetGeometryNode();
1668     CHECK_NULL_RETURN(groupItemGeometry, Rect());
1669     return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
1670         itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
1671         groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
1672 }
1673 
UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,bool isJump)1674 float ListPattern::UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, bool isJump)
1675 {
1676     float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
1677     float prevOffset = currentOffset_;
1678     if (childrenSize_) {
1679         listTotalHeight_ = posMap_->GetTotalHeight();
1680         currentOffset_ = itemPosition_.empty() ? 0.0f :
1681             posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos);
1682     } else {
1683         if (isJump || needReEstimateOffset_) {
1684             auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1685             calculate.GetEstimateHeightAndOffset(GetHost());
1686             currentOffset_ = calculate.GetEstimateOffset();
1687             relativeOffset = 0;
1688             needReEstimateOffset_ = false;
1689             posMap_->ClearPosMap();
1690         }
1691         CalculateCurrentOffset(relativeOffset, listLayoutAlgorithm->GetRecycledItemPosition());
1692     }
1693     if (scrollTarget_) {
1694         auto& target = scrollTarget_.value();
1695         auto posInfo = posMap_->GetPositionInfo(target.index);
1696         if (!Negative(posInfo.mainPos)) {
1697             float startPos = posInfo.mainPos - currentOffset_;
1698             float targetPos = 0.0f;
1699             GetListItemAnimatePos(startPos, startPos + posInfo.mainSize, target.align, targetPos);
1700             targetPos += currentOffset_ + target.extraOffset;
1701             const float epsilon = 0.1f;
1702             if (!NearEqual(relativeOffset + prevOffset, currentOffset_, epsilon) ||
1703                 !NearEqual(target.targetOffset, targetPos, epsilon)) {
1704                 target.targetOffset = targetPos;
1705                 AnimateTo(targetPos, -1, nullptr, true);
1706             }
1707         }
1708     }
1709     return currentOffset_ - prevOffset;
1710 }
1711 
CalculateCurrentOffset(float delta,const ListLayoutAlgorithm::PositionMap & recycledItemPosition)1712 void ListPattern::CalculateCurrentOffset(float delta, const ListLayoutAlgorithm::PositionMap& recycledItemPosition)
1713 {
1714     posMap_->UpdateTotalCount(maxListItemIndex_ + 1);
1715     if (itemPosition_.empty()) {
1716         return;
1717     }
1718     auto itemPos = itemPosition_;
1719     for (auto& [index, pos] : recycledItemPosition) {
1720         itemPos.try_emplace(index, pos);
1721     }
1722     float startPos = itemPos.begin()->second.startPos;
1723     int32_t startIndex = itemPos.begin()->first;
1724     auto& groupInfo = itemPos.begin()->second.groupInfo;
1725     bool groupAtStart = (!groupInfo || groupInfo.value().atStart);
1726     if (startIndex == 0 && groupAtStart) {
1727         currentOffset_ = -startPos;
1728     } else {
1729         posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, startIndex, startPos, groupAtStart);
1730     }
1731     for (auto& [index, pos] : itemPos) {
1732         float height = pos.endPos - pos.startPos;
1733         if (pos.groupInfo) {
1734             bool groupAtStart = pos.groupInfo.value().atStart;
1735             if (groupAtStart) {
1736                 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1737             } else {
1738                 posMap_->UpdatePosWithCheck(index, { currentOffset_ + pos.startPos, height });
1739             }
1740         } else {
1741             posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1742         }
1743     }
1744     auto& endGroupInfo = itemPos.rbegin()->second.groupInfo;
1745     bool groupAtEnd = (!endGroupInfo || endGroupInfo.value().atEnd);
1746     posMap_->UpdatePosMapEnd(itemPos.rbegin()->first, spaceWidth_, groupAtEnd);
1747 }
1748 
UpdateScrollBarOffset()1749 void ListPattern::UpdateScrollBarOffset()
1750 {
1751     CheckScrollBarOff();
1752     if (itemPosition_.empty()) {
1753         return;
1754     }
1755     if (!GetScrollBar() && !GetScrollBarProxy()) {
1756         return;
1757     }
1758     float currentOffset = 0.0f;
1759     float estimatedHeight = 0.0f;
1760     if (childrenSize_) {
1761         currentOffset = currentOffset_;
1762         estimatedHeight = listTotalHeight_;
1763     } else {
1764         auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1765         calculate.GetEstimateHeightAndOffset(GetHost());
1766         currentOffset = calculate.GetEstimateOffset();
1767         estimatedHeight = calculate.GetEstimateHeight();
1768     }
1769     if (GetAlwaysEnabled()) {
1770         estimatedHeight = estimatedHeight - spaceWidth_;
1771     }
1772     if (!IsScrollSnapAlignCenter() || childrenSize_) {
1773         currentOffset += contentStartOffset_;
1774         estimatedHeight += contentStartOffset_ + contentEndOffset_;
1775     }
1776 
1777     // calculate padding offset of list
1778     auto host = GetHost();
1779     CHECK_NULL_VOID(host);
1780     auto layoutPriority = host->GetLayoutProperty();
1781     CHECK_NULL_VOID(layoutPriority);
1782     auto padding = layoutPriority->CreatePaddingAndBorder();
1783     auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
1784     const auto& geometryNode = host->GetGeometryNode();
1785     auto frameSize = geometryNode->GetFrameSize();
1786     Size size(frameSize.Width(), frameSize.Height());
1787     UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
1788 }
1789 
GetTotalHeight() const1790 float ListPattern::GetTotalHeight() const
1791 {
1792     auto currentOffset = GetTotalOffset();
1793     if (endIndex_ >= maxListItemIndex_) {
1794         return currentOffset + endMainPos_ + contentEndOffset_;
1795     }
1796     if (itemPosition_.empty()) {
1797         return 0.0f;
1798     }
1799     int32_t remainCount = maxListItemIndex_ - endIndex_;
1800     float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1801     float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
1802     return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
1803 }
1804 
TriggerModifyDone()1805 void ListPattern::TriggerModifyDone()
1806 {
1807     OnModifyDone();
1808 }
1809 
SetChainAnimationCallback()1810 void ListPattern::SetChainAnimationCallback()
1811 {
1812     CHECK_NULL_VOID(chainAnimation_);
1813     chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1814         auto list = weak.Upgrade();
1815         CHECK_NULL_VOID(list);
1816         list->MarkDirtyNodeSelf();
1817     });
1818     auto scrollEffect = AceType::DynamicCast<ScrollSpringEffect>(GetScrollEdgeEffect());
1819     CHECK_NULL_VOID(scrollEffect);
1820     scrollEffect->SetOnWillStartSpringCallback([weak = AceType::WeakClaim(this)]() {
1821         auto list = weak.Upgrade();
1822         CHECK_NULL_VOID(list);
1823         if (!list->dragFromSpring_ && list->chainAnimation_) {
1824             auto delta = list->chainAnimation_->SetControlIndex(list->IsAtTop() ? 0 : list->maxListItemIndex_);
1825             list->currentDelta_ -= delta;
1826             list->dragFromSpring_ = true;
1827         }
1828     });
1829 }
1830 
SetChainAnimation()1831 void ListPattern::SetChainAnimation()
1832 {
1833     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1834     CHECK_NULL_VOID(listLayoutProperty);
1835     auto edgeEffect = GetEdgeEffect();
1836     int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
1837     bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
1838     bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
1839     bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
1840     if (!enable) {
1841         chainAnimation_.Reset();
1842         return;
1843     }
1844     auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1845     if (Negative(space)) {
1846         space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1847     }
1848     if (!chainAnimation_ || (chainAnimation_ && space != chainAnimation_->GetSpace())) {
1849         springProperty_ =
1850             AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
1851         if (chainAnimationOptions_.has_value()) {
1852             float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
1853             float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
1854             minSpace = Negative(minSpace) ? 0.0f : minSpace;
1855             minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1856             maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1857             springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1858             springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1859             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1860             auto conductivity = chainAnimationOptions_.value().conductivity;
1861             if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1862                 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1863             }
1864             chainAnimation_->SetConductivity(conductivity);
1865             auto intensity = chainAnimationOptions_.value().intensity;
1866             if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1867                 intensity = ChainAnimation::DEFAULT_INTENSITY;
1868             }
1869             chainAnimation_->SetIntensity(intensity);
1870             auto effect = chainAnimationOptions_.value().edgeEffect;
1871             chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1872         } else {
1873             auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
1874             auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
1875             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1876         }
1877         SetChainAnimationCallback();
1878     }
1879 }
1880 
SetChainAnimationOptions(const ChainAnimationOptions & options)1881 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
1882 {
1883     chainAnimationOptions_ = options;
1884     if (chainAnimation_) {
1885         auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1886         CHECK_NULL_VOID(listLayoutProperty);
1887         auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1888         if (Negative(space)) {
1889             space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1890         }
1891         float maxSpace = options.maxSpace.ConvertToPx();
1892         float minSpace = options.minSpace.ConvertToPx();
1893         minSpace = Negative(minSpace) ? 0.0f : minSpace;
1894         minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1895         maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1896         chainAnimation_->SetSpace(space, maxSpace, minSpace);
1897         auto conductivity = chainAnimationOptions_.value().conductivity;
1898         if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1899             conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1900         }
1901         chainAnimation_->SetConductivity(conductivity);
1902         auto intensity = chainAnimationOptions_.value().intensity;
1903         if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1904             intensity = ChainAnimation::DEFAULT_INTENSITY;
1905         }
1906         chainAnimation_->SetIntensity(intensity);
1907         auto effect = options.edgeEffect;
1908         chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1909     }
1910     if (springProperty_) {
1911         springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1912         springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1913     }
1914 }
1915 
OnTouchDown(const TouchEventInfo & info)1916 void ListPattern::OnTouchDown(const TouchEventInfo& info)
1917 {
1918     ScrollablePattern::OnTouchDown(info);
1919     auto& touches = info.GetTouches();
1920     if (touches.empty()) {
1921         return;
1922     }
1923     auto offset = touches.front().GetLocalLocation();
1924     float startPosition = GetAxis() == Axis::HORIZONTAL ? offset.GetX() : offset.GetY();
1925     ProcessDragStart(startPosition);
1926 }
1927 
ProcessDragStart(float startPosition)1928 void ListPattern::ProcessDragStart(float startPosition)
1929 {
1930     CHECK_NULL_VOID(chainAnimation_);
1931     auto host = GetHost();
1932     CHECK_NULL_VOID(host);
1933     auto globalOffset = host->GetTransformRelativeOffset();
1934     int32_t index = -1;
1935     auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
1936     auto it = std::find_if(
1937         itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
1938     if (it != itemPosition_.end()) {
1939         index = it->first;
1940     } else if (!itemPosition_.empty()) {
1941         index = itemPosition_.rbegin()->first + 1;
1942     }
1943     dragFromSpring_ = false;
1944     float delta = chainAnimation_->SetControlIndex(index);
1945     currentDelta_ -= delta;
1946     chainAnimation_->SetMaxIndex(maxListItemIndex_);
1947 }
1948 
ProcessDragUpdate(float dragOffset,int32_t source)1949 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
1950 {
1951     CHECK_NULL_VOID(chainAnimation_);
1952     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR_FLING) {
1953         return;
1954     }
1955     if (NeedScrollSnapAlignEffect()) {
1956         auto delta = 0.0f;
1957         if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
1958             delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
1959         }
1960         if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
1961             delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
1962         }
1963         if (!NearZero(delta)) {
1964             auto scrollableEvent = GetScrollableEvent();
1965             CHECK_NULL_VOID(scrollableEvent);
1966             auto scrollable = scrollableEvent->GetScrollable();
1967             CHECK_NULL_VOID(scrollable);
1968             scrollable->UpdateScrollSnapStartOffset(delta);
1969             currentDelta_ -= delta;
1970         }
1971     }
1972     float overOffset = 0.0f;
1973     if (!itemPosition_.empty()) {
1974         auto res = GetOutBoundaryOffset(false);
1975         overOffset = std::max(res.start, res.end);
1976         if (!NearZero(res.end)) {
1977             overOffset = -overOffset;
1978         }
1979     }
1980     if (source == SCROLL_FROM_UPDATE && !NearZero(overOffset)) {
1981         dragOffset = 0.0f;
1982     }
1983     chainAnimation_->SetDelta(-dragOffset, overOffset);
1984     if (source == SCROLL_FROM_UPDATE && GetCanOverScroll()) {
1985         float tempDelta = currentDelta_;
1986         currentDelta_ -= dragOffset;
1987         bool isAtEdge = IsAtTop() || IsAtBottom();
1988         currentDelta_ = tempDelta;
1989         SetCanOverScroll(isAtEdge);
1990     }
1991 }
1992 
GetChainDelta(int32_t index) const1993 float ListPattern::GetChainDelta(int32_t index) const
1994 {
1995     CHECK_NULL_RETURN(chainAnimation_, 0.0f);
1996     return chainAnimation_->GetValue(index);
1997 }
1998 
MultiSelectWithoutKeyboard(const RectF & selectedZone)1999 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
2000 {
2001     auto host = GetHost();
2002     CHECK_NULL_VOID(host);
2003     std::list<RefPtr<FrameNode>> childrens;
2004     host->GenerateOneDepthVisibleFrame(childrens);
2005     for (const auto& item : childrens) {
2006         if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2007             auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
2008             CHECK_NULL_VOID(itemGroupPattern);
2009             auto itemGroupGeometry = item->GetGeometryNode();
2010             CHECK_NULL_VOID(itemGroupGeometry);
2011             auto itemGroupRect = itemGroupGeometry->GetFrameRect();
2012             if (!selectedZone.IsIntersectWith(itemGroupRect)) {
2013                 continue;
2014             }
2015             HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
2016             continue;
2017         }
2018         auto itemPattern = item->GetPattern<ListItemPattern>();
2019         CHECK_NULL_VOID(itemPattern);
2020         if (!itemPattern->Selectable()) {
2021             continue;
2022         }
2023 
2024         auto itemGeometry = item->GetGeometryNode();
2025         CHECK_NULL_VOID(itemGeometry);
2026 
2027         auto itemRect = itemGeometry->GetFrameRect();
2028         if (!selectedZone.IsIntersectWith(itemRect)) {
2029             itemPattern->MarkIsSelected(false);
2030         } else {
2031             itemPattern->MarkIsSelected(true);
2032         }
2033     }
2034 
2035     DrawSelectedZone(selectedZone);
2036 }
2037 
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)2038 void ListPattern::HandleCardModeSelectedEvent(
2039     const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
2040 {
2041     CHECK_NULL_VOID(itemGroupNode);
2042     std::list<RefPtr<FrameNode>> childrens;
2043     itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
2044     for (const auto& item : childrens) {
2045         auto itemPattern = item->GetPattern<ListItemPattern>();
2046         if (!itemPattern) {
2047             continue;
2048         }
2049         if (!itemPattern->Selectable()) {
2050             continue;
2051         }
2052         auto itemGeometry = item->GetGeometryNode();
2053         CHECK_NULL_VOID(itemGeometry);
2054         auto context = item->GetRenderContext();
2055         CHECK_NULL_VOID(context);
2056         auto itemRect = itemGeometry->GetFrameRect();
2057         RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
2058         if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
2059             itemPattern->MarkIsSelected(false);
2060         } else {
2061             itemPattern->MarkIsSelected(true);
2062         }
2063     }
2064 }
2065 
ClearMultiSelect()2066 void ListPattern::ClearMultiSelect()
2067 {
2068     auto host = GetHost();
2069     CHECK_NULL_VOID(host);
2070     std::list<RefPtr<FrameNode>> children;
2071     host->GenerateOneDepthAllFrame(children);
2072     for (const auto& child : children) {
2073         if (!child) {
2074             continue;
2075         }
2076         auto itemPattern = child->GetPattern<ListItemPattern>();
2077         if (itemPattern) {
2078             itemPattern->MarkIsSelected(false);
2079             continue;
2080         }
2081         auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
2082         if (itemGroupPattern) {
2083             std::list<RefPtr<FrameNode>> itemChildren;
2084             child->GenerateOneDepthAllFrame(itemChildren);
2085             for (const auto& item : itemChildren) {
2086                 if (!item) {
2087                     continue;
2088                 }
2089                 itemPattern = item->GetPattern<ListItemPattern>();
2090                 if (itemPattern) {
2091                     itemPattern->MarkIsSelected(false);
2092                 }
2093             }
2094         }
2095     }
2096 
2097     ClearSelectedZone();
2098 }
2099 
IsItemSelected(const GestureEvent & info)2100 bool ListPattern::IsItemSelected(const GestureEvent& info)
2101 {
2102     auto host = GetHost();
2103     CHECK_NULL_RETURN(host, false);
2104     auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2105     CHECK_NULL_RETURN(node, false);
2106     auto itemPattern = node->GetPattern<ListItemPattern>();
2107     if (itemPattern) {
2108         return itemPattern->IsSelected();
2109     }
2110     auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
2111     if (itemGroupPattern) {
2112         auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2113         CHECK_NULL_RETURN(itemNode, false);
2114         itemPattern = itemNode->GetPattern<ListItemPattern>();
2115         CHECK_NULL_RETURN(itemPattern, false);
2116         return itemPattern->IsSelected();
2117     }
2118     return false;
2119 }
2120 
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)2121 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
2122 {
2123     // swiper item only can be replaced when no other items be dragged
2124     if (canReplaceSwiperItem_) {
2125         if (swiperItem != swiperItem_) {
2126             auto item = swiperItem_.Upgrade();
2127             if (item) {
2128                 item->ResetSwipeStatus();
2129             }
2130             swiperItem_ = std::move(swiperItem);
2131         }
2132         canReplaceSwiperItem_ = false;
2133     }
2134     FireAndCleanScrollingListener();
2135 }
2136 
GetItemIndexByPosition(float xOffset,float yOffset)2137 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
2138 {
2139     auto host = GetHost();
2140     auto globalOffset = host->GetTransformRelativeOffset();
2141     float relativeX = xOffset - globalOffset.GetX();
2142     float relativeY = yOffset - globalOffset.GetY();
2143     float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
2144     float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
2145     float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
2146     int32_t lanesOffset = 0;
2147     if (lanes_ > 1) {
2148         lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
2149     }
2150     for (auto& pos : itemPosition_) {
2151         if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2152             return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
2153         }
2154     }
2155     if (!itemPosition_.empty()) {
2156         return itemPosition_.rbegin()->first + 1;
2157     }
2158     return 0;
2159 }
2160 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2161 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2162 {
2163     ScrollablePattern::ToJsonValue(json, filter);
2164     /* no fixed attr below, just return */
2165     if (filter.IsFastFilter()) {
2166         return;
2167     }
2168     json->PutExtAttr("multiSelectable", multiSelectable_, filter);
2169     json->PutExtAttr("startIndex", startIndex_, filter);
2170     if (!itemPosition_.empty()) {
2171         json->PutExtAttr("itemStartPos", itemPosition_.begin()->second.startPos, filter);
2172     }
2173     auto nestedScrollOptions = JsonUtil::Create(true);
2174     auto nestedScroll = GetNestedScroll();
2175     nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
2176     nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
2177     json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
2178     json->PutExtAttr("maintainVisibleContentPosition", maintainVisibleContentPosition_, filter);
2179 }
2180 
FromJson(const std::unique_ptr<JsonValue> & json)2181 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
2182 {
2183     ScrollToIndex(json->GetInt("startIndex"));
2184     if (json->Contains("itemStartPos")) {
2185         ScrollBy(-json->GetDouble("itemStartPos"));
2186     }
2187     auto host = GetHost();
2188     CHECK_NULL_VOID(host);
2189     host->GetRenderContext()->UpdateClipEdge(true);
2190     ScrollablePattern::FromJson(json);
2191 }
2192 
GetListItemGroupParameter(const RefPtr<FrameNode> & node)2193 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
2194 {
2195     ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
2196     auto curFrameParent = node->GetParent();
2197     auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2198     while (curFrameParent && (!curFrameParentNode)) {
2199         curFrameParent = curFrameParent->GetParent();
2200         curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2201     }
2202     CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
2203     if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2204         auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
2205         CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
2206         listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
2207         listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2208         listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2209         listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2210     }
2211     return listItemGroupPara;
2212 }
2213 
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2214 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2215 {
2216     auto listFrame = GetHost();
2217     CHECK_NULL_RETURN(listFrame, false);
2218     auto listFocus = listFrame->GetFocusHub();
2219     CHECK_NULL_RETURN(listFocus, false);
2220     for (const auto& childFocus : listFocus->GetChildren()) {
2221         if (!childFocus->IsFocusable()) {
2222             continue;
2223         }
2224         if (auto childFrame = childFocus->GetFrameNode()) {
2225             if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2226                 auto curIndex = childPattern->GetIndexInList();
2227                 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2228                 if (curIndex == listIndex) {
2229                     node = childFrame;
2230                     return curIndexInGroup > -1;
2231                 }
2232             }
2233         }
2234     }
2235     return false;
2236 }
2237 
RefreshLanesItemRange()2238 void ListPattern::RefreshLanesItemRange()
2239 {
2240     auto host = GetHost();
2241     CHECK_NULL_VOID(host);
2242     auto updatePos = host->GetChildrenUpdated();
2243     if (updatePos == -1) {
2244         return;
2245     }
2246     host->ChildrenUpdatedFrom(-1);
2247     if (updatePos == 0) {
2248         lanesItemRange_.clear();
2249         return;
2250     }
2251     for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
2252         if (it->second < updatePos) {
2253             it++;
2254         } else if (it->first >= updatePos) {
2255             it = lanesItemRange_.erase(it);
2256         } else {
2257             it->second = updatePos - 1;
2258             it++;
2259         }
2260     }
2261 }
2262 
ProvideRestoreInfo()2263 std::string ListPattern::ProvideRestoreInfo()
2264 {
2265     return std::to_string(startIndex_);
2266 }
2267 
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)2268 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
2269 {
2270     auto item = swiperItem_.Upgrade();
2271     if (item) {
2272         return item->CloseSwipeAction(std::move(onFinishCallback));
2273     }
2274 }
2275 
OnRestoreInfo(const std::string & restoreInfo)2276 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
2277 {
2278     jumpIndex_ = StringUtils::StringToInt(restoreInfo);
2279 }
2280 
DumpAdvanceInfo()2281 void ListPattern::DumpAdvanceInfo()
2282 {
2283     ScrollablePattern::DumpAdvanceInfo();
2284     DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
2285     DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
2286     DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
2287     DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
2288     DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
2289     DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
2290     DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
2291     DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
2292     DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
2293     DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
2294     DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
2295     crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
2296                      : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
2297     smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
2298     if (jumpIndex_.has_value()) {
2299         DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
2300     } else {
2301         DumpLog::GetInstance().AddDesc("jumpIndex:null");
2302     }
2303     if (jumpIndexInGroup_.has_value()) {
2304         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
2305     } else {
2306         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
2307     }
2308     if (targetIndex_.has_value()) {
2309         DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
2310     } else {
2311         DumpLog::GetInstance().AddDesc("targetIndex:null");
2312     }
2313     if (predictSnapOffset_.has_value()) {
2314         DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
2315     } else {
2316         DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
2317     }
2318     if (predictSnapEndPos_.has_value()) {
2319         DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
2320     } else {
2321         DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
2322     }
2323     paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
2324                     : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
2325     isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
2326                             : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
2327     for (auto item : itemPosition_) {
2328         DumpLog::GetInstance().AddDesc("------------------------------------------");
2329         DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
2330         DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
2331         DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
2332         DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
2333     }
2334     DumpLog::GetInstance().AddDesc("------------------------------------------");
2335     scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
2336                 : DumpLog::GetInstance().AddDesc("scrollStop:false");
2337     for (auto item : lanesItemRange_) {
2338         DumpLog::GetInstance().AddDesc("------------------------------------------");
2339         DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
2340         DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
2341     }
2342     DumpLog::GetInstance().AddDesc("------------------------------------------");
2343     DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
2344     DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
2345     dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
2346                     : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
2347     isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
2348                  : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
2349     IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
2350     IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
2351                  : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
2352 }
2353 
GetDefaultScrollBarDisplayMode() const2354 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
2355 {
2356     auto defaultDisplayMode = DisplayMode::OFF;
2357     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
2358         defaultDisplayMode = DisplayMode::AUTO;
2359     }
2360     return defaultDisplayMode;
2361 }
2362 
GetVisibleSelectedItems()2363 std::vector<RefPtr<FrameNode>> ListPattern::GetVisibleSelectedItems()
2364 {
2365     std::vector<RefPtr<FrameNode>> children;
2366     auto host = GetHost();
2367     CHECK_NULL_RETURN(host, children);
2368     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2369         auto item = host->GetChildByIndex(index);
2370         if (!AceType::InstanceOf<FrameNode>(item)) {
2371             continue;
2372         }
2373         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
2374         auto itemPattern = itemFrameNode->GetPattern<ListItemPattern>();
2375         if (!itemPattern) {
2376             continue;
2377         }
2378         if (!itemPattern->IsSelected()) {
2379             continue;
2380         }
2381         children.emplace_back(itemFrameNode);
2382     }
2383     return children;
2384 }
2385 
GetOrCreateListChildrenMainSize()2386 RefPtr<ListChildrenMainSize> ListPattern::GetOrCreateListChildrenMainSize()
2387 {
2388     if (childrenSize_) {
2389         return childrenSize_;
2390     }
2391     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
2392     auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
2393         auto pattern = weakPattern.Upgrade();
2394         CHECK_NULL_VOID(pattern);
2395         auto context = PipelineContext::GetCurrentContext();
2396         CHECK_NULL_VOID(context);
2397         context->AddBuildFinishCallBack([weakPattern, change, flag]() {
2398             auto pattern = weakPattern.Upgrade();
2399             CHECK_NULL_VOID(pattern);
2400             pattern->OnChildrenSizeChanged(change, flag);
2401         });
2402         context->RequestFrame();
2403     };
2404     childrenSize_->SetOnDataChange(callback);
2405     return childrenSize_;
2406 }
2407 
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)2408 void ListPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
2409 {
2410     if (!posMap_) {
2411         posMap_ = MakeRefPtr<ListPositionMap>();
2412     }
2413     posMap_->MarkDirty(flag);
2414     MarkDirtyNodeSelf();
2415 }
2416 
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)2417 void ListPattern::SetListChildrenMainSize(float defaultSize, const std::vector<float>& mainSize)
2418 {
2419     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
2420     OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2421 }
2422 
ResetChildrenSize()2423 void ListPattern::ResetChildrenSize()
2424 {
2425     if (childrenSize_) {
2426         childrenSize_ = nullptr;
2427         MarkDirtyNodeSelf();
2428         OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2429     }
2430 }
2431 
NotifyDataChange(int32_t index,int32_t count)2432 void ListPattern::NotifyDataChange(int32_t index, int32_t count)
2433 {
2434     if (!maintainVisibleContentPosition_ || itemPosition_.empty()) {
2435         return;
2436     }
2437     auto startIndex = itemPosition_.begin()->first;
2438     if (count == 0 || (count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) {
2439         return;
2440     }
2441     count = std::max(count, index - startIndex);
2442     int32_t mod = 0;
2443     if (count < 0 && lanes_ > 1 && !(itemPosition_.begin()->second.isGroup)) {
2444         mod = -count % lanes_;
2445     }
2446     auto prevPosMap = std::move(itemPosition_);
2447     for (auto &pos : prevPosMap) {
2448         if (mod > 0) {
2449             mod--;
2450         } else {
2451             itemPosition_[pos.first + count] = pos.second;
2452         }
2453     }
2454     needReEstimateOffset_ = true;
2455 }
2456 } // namespace OHOS::Ace::NG
2457