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