• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/list/list_pattern.h"
17 
18 #include "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     float startMainPos = startMainPos_;
700     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
701     if (GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
702         endMainPos = startMainPos + contentMainSize;
703     }
704     return (endIndex == maxListItemIndex_ && groupAtEnd) &&
705            LessOrEqual(endMainPos - currentDelta_ + GetChainDelta(endIndex), contentMainSize_ - contentEndOffset_);
706 }
707 
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const708 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
709 {
710     if (itemPosition_.empty()) {
711         return;
712     }
713     if (startIndex_ == 0 && itemPosition_.begin()->second.isGroup) {
714         auto& groupInfo = itemPosition_.begin()->second.groupInfo;
715         groupAtStart = groupInfo && groupInfo.value().atStart;
716     }
717     if (endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup) {
718         auto& groupInfo = itemPosition_.rbegin()->second.groupInfo;
719         groupAtEnd = groupInfo && groupInfo.value().atEnd;
720     }
721 }
722 
GetOffsetWithLimit(float offset) const723 float ListPattern::GetOffsetWithLimit(float offset) const
724 {
725     auto currentOffset = GetTotalOffset() + contentStartOffset_;
726     if (Positive(offset)) {
727         return std::min(currentOffset, offset);
728     } else if (Negative(offset)) {
729         auto remainHeight = GetTotalHeight() - currentOffset;
730         return std::max(offset, -remainHeight);
731     }
732     return 0;
733 }
734 
GetOverScrollOffset(double delta) const735 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
736 {
737     OverScrollOffset offset = { 0, 0 };
738     bool groupAtStart = true;
739     bool groupAtEnd = true;
740     GetListItemGroupEdge(groupAtStart, groupAtEnd);
741 
742     int32_t startIndex = startIndex_;
743     float startMainPos = startMainPos_;
744     int32_t endIndex = endIndex_;
745     float endMainPos = endMainPos_;
746     if (startIndex == 0 && groupAtStart) {
747         offset.start = GetStartOverScrollOffset(delta, startMainPos);
748     }
749     if (endIndex == maxListItemIndex_ && groupAtEnd) {
750         offset.end = GetEndOverScrollOffset(delta, endMainPos, startMainPos);
751     }
752     return offset;
753 }
754 
GetStartOverScrollOffset(float offset,float startMainPos) const755 float ListPattern::GetStartOverScrollOffset(float offset, float startMainPos) const
756 {
757     float startOffset = 0.0f;
758     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(0, -offset) : 0.f;
759     auto startPos = startMainPos + ChainDelta - currentDelta_;
760     auto newStartPos = startPos + offset;
761     if (startPos > contentStartOffset_ && newStartPos > contentStartOffset_) {
762         startOffset = offset;
763     }
764     if (startPos > contentStartOffset_ && newStartPos <= contentStartOffset_) {
765         startOffset = contentStartOffset_ - startPos;
766     }
767     if (startPos <= contentStartOffset_ && newStartPos > contentStartOffset_) {
768         startOffset = newStartPos - contentStartOffset_;
769     }
770     return startOffset;
771 }
772 
GetEndOverScrollOffset(float offset,float endMainPos,float startMainPos) const773 float ListPattern::GetEndOverScrollOffset(float offset, float endMainPos, float startMainPos) const
774 {
775     float endOffset = 0.0f;
776     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(maxListItemIndex_, -offset) : 0.f;
777     auto endPos = endMainPos + ChainDelta - currentDelta_;
778     auto contentEndPos = contentMainSize_ - contentEndOffset_;
779     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
780     if (GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
781         endPos = startMainPos + contentMainSize;
782     }
783     auto newEndPos = endPos + offset;
784     if (endPos < contentEndPos && newEndPos < contentEndPos) {
785         endOffset = offset;
786     }
787     if (endPos < contentEndPos && newEndPos >= contentEndPos) {
788         endOffset = contentEndPos - endPos;
789     }
790     if (endPos >= contentEndPos && newEndPos < contentEndPos) {
791         endOffset = newEndPos - contentEndPos;
792     }
793     return endOffset;
794 }
795 
GetOutBoundaryOffset(float delta,bool useChainDelta) const796 OverScrollOffset ListPattern::GetOutBoundaryOffset(float delta, bool useChainDelta) const
797 {
798     OverScrollOffset offset = { 0, 0 };
799     bool groupAtStart = true;
800     bool groupAtEnd = true;
801     GetListItemGroupEdge(groupAtStart, groupAtEnd);
802 
803     int32_t startIndex = startIndex_;
804     float startMainPos = startMainPos_;
805     int32_t endIndex = endIndex_;
806     float endMainPos = endMainPos_;
807     if (startIndex == 0 && groupAtStart) {
808         auto startChainDelta = useChainDelta ? GetChainDelta(0) : 0.0f;
809         offset.start = startMainPos - delta + startChainDelta - contentStartOffset_;
810         offset.start = std::max(offset.start, 0.0);
811     }
812     if (endIndex >= maxListItemIndex_ && groupAtEnd) {
813         auto endChainDelta = useChainDelta ? GetChainDelta(endIndex) : 0.0f;
814         endMainPos = endMainPos + endChainDelta;
815         auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
816         if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
817             endMainPos = startMainPos + contentMainSize;
818         }
819         offset.end = contentMainSize_ - contentEndOffset_ - (endMainPos - delta);
820         offset.end = std::max(offset.end, 0.0);
821     }
822     return offset;
823 }
824 
UpdateCurrentOffset(float offset,int32_t source)825 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
826 {
827     // check edgeEffect is not springEffect
828     if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
829         if (IsOutOfBoundary(false)) {
830             MarkDirtyNodeSelf();
831         }
832         return false;
833     }
834     SetScrollSource(source);
835     FireAndCleanScrollingListener();
836     auto lastDelta = currentDelta_;
837     currentDelta_ = currentDelta_ - offset;
838     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING) {
839         isNeedCheckOffset_ = true;
840     }
841     if (!NearZero(offset)) {
842         MarkDirtyNodeSelf();
843     }
844     if (itemPosition_.empty() || !IsOutOfBoundary() || !isScrollable_) {
845         auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
846         currentDelta_ = lastDelta + userOffset;
847         return true;
848     }
849 
850     if (GetScrollSource() == SCROLL_FROM_UPDATE) {
851         auto res = GetOutBoundaryOffset(currentDelta_);
852         // over scroll in drag update from normal to over scroll.
853         float overScroll = std::max(res.start, res.end);
854         // adjust offset.
855         auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
856         currentDelta_ = currentDelta_ * friction;
857     }
858 
859     auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
860     currentDelta_ = lastDelta + userOffset;
861     return true;
862 }
863 
MarkDirtyNodeSelf()864 void ListPattern::MarkDirtyNodeSelf()
865 {
866     auto host = GetHost();
867     CHECK_NULL_VOID(host);
868     if (!crossMatchChild_) {
869         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
870     } else {
871         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
872     }
873 }
874 
OnScrollEndCallback()875 void ListPattern::OnScrollEndCallback()
876 {
877     if (AnimateStoped()) {
878         scrollStop_ = true;
879         MarkDirtyNodeSelf();
880     }
881 }
882 
GetContentSize() const883 SizeF ListPattern::GetContentSize() const
884 {
885     auto host = GetHost();
886     CHECK_NULL_RETURN(host, SizeF());
887     auto geometryNode = host->GetGeometryNode();
888     CHECK_NULL_RETURN(geometryNode, SizeF());
889     auto renderContext = host->GetRenderContext();
890     CHECK_NULL_RETURN(renderContext, SizeF());
891     auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
892     auto& padding = geometryNode->GetPadding();
893     if (padding) {
894         size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
895     }
896     return size;
897 }
898 
IsOutOfBoundary(bool useCurrentDelta)899 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
900 {
901     if (itemPosition_.empty()) {
902         return false;
903     }
904     auto currentDelta = useCurrentDelta ? currentDelta_ : 0.0f;
905     auto res = GetOutBoundaryOffset(currentDelta);
906     // over scroll in drag update from normal to over scroll.
907     return Positive(res.start) || Positive(res.end);
908 }
909 
OnScrollCallback(float offset,int32_t source)910 bool ListPattern::OnScrollCallback(float offset, int32_t source)
911 {
912     if (source == SCROLL_FROM_START) {
913         auto item = swiperItem_.Upgrade();
914         if (item) {
915             item->ResetSwipeStatus();
916         }
917         FireOnScrollStart();
918         return true;
919     }
920     ProcessDragUpdate(offset, source);
921     return UpdateCurrentOffset(offset, source);
922 }
923 
OnScrollSnapCallback(double targetOffset,double velocity)924 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
925 {
926     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
927     CHECK_NULL_RETURN(listProperty, false);
928     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
929     if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
930         return false;
931     }
932     if (AnimateRunning()) {
933         return false;
934     }
935     if (!GetIsDragging()) {
936         snapTrigOnScrollStart_ = true;
937     }
938     predictSnapOffset_ = targetOffset;
939     scrollSnapVelocity_ = velocity;
940     MarkDirtyNodeSelf();
941     return true;
942 }
943 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)944 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
945 {
946     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
947         auto list = weak.Upgrade();
948         CHECK_NULL_RETURN(list, 0.0);
949         return list->startMainPos_ - list->currentDelta_;
950     });
951     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
952         auto list = weak.Upgrade();
953         auto endPos = list->endMainPos_;
954         auto startPos = list->startMainPos_;
955         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
956         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
957     });
958     scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
959         auto list = weak.Upgrade();
960         CHECK_NULL_RETURN(list, 0.0);
961         return list->contentStartOffset_;
962     });
963     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
964         auto list = weak.Upgrade();
965         auto endPos = list->endMainPos_;
966         auto startPos = list->startMainPos_;
967         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
968         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
969     });
970     scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
971         auto list = weak.Upgrade();
972         CHECK_NULL_RETURN(list, 0.0);
973         return list->contentStartOffset_;
974     });
975 }
976 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)977 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
978 {
979     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
980         auto pattern = wp.Upgrade();
981         CHECK_NULL_RETURN(pattern, false);
982         return pattern->OnKeyEvent(event);
983     };
984     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
985 }
986 
OnKeyEvent(const KeyEvent & event)987 bool ListPattern::OnKeyEvent(const KeyEvent& event)
988 {
989     if (event.action != KeyAction::DOWN) {
990         return false;
991     }
992     if (event.code == KeyCode::KEY_PAGE_DOWN) {
993         ScrollPage(false);
994         return true;
995     }
996     if (event.code == KeyCode::KEY_PAGE_UP) {
997         ScrollPage(true);
998         return true;
999     }
1000     return HandleDirectionKey(event);
1001 }
1002 
HandleDirectionKey(const KeyEvent & event)1003 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
1004 {
1005     return false;
1006 }
1007 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1008 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1009 {
1010     auto curFocus = currentFocusNode.Upgrade();
1011     CHECK_NULL_RETURN(curFocus, nullptr);
1012     auto curFrame = curFocus->GetFrameNode();
1013     CHECK_NULL_RETURN(curFrame, nullptr);
1014     auto curPattern = curFrame->GetPattern();
1015     CHECK_NULL_RETURN(curPattern, nullptr);
1016     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
1017     CHECK_NULL_RETURN(curItemPattern, nullptr);
1018     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1019     CHECK_NULL_RETURN(listProperty, nullptr);
1020 
1021     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1022     auto curIndex = curItemPattern->GetIndexInList();
1023     auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
1024     auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
1025     if (curIndex < 0 || curIndex > maxListItemIndex_) {
1026         return nullptr;
1027     }
1028 
1029     auto moveStep = 0;
1030     auto nextIndex = curIndex;
1031     auto nextIndexInGroup = curIndexInGroup;
1032     if (lanes_ <= 1) {
1033         if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
1034             moveStep = 1;
1035             nextIndex = 0;
1036             nextIndexInGroup = -1;
1037         } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
1038             moveStep = -1;
1039             nextIndex = maxListItemIndex_;
1040             nextIndexInGroup = -1;
1041         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
1042                    (step == FocusStep::TAB)) {
1043             moveStep = 1;
1044             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1045                 nextIndex = curIndex + moveStep;
1046                 nextIndexInGroup = -1;
1047             } else {
1048                 nextIndexInGroup = curIndexInGroup + moveStep;
1049             }
1050         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
1051                    (step == FocusStep::SHIFT_TAB)) {
1052             moveStep = -1;
1053             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1054                 nextIndex = curIndex + moveStep;
1055                 nextIndexInGroup = -1;
1056             } else {
1057                 nextIndexInGroup = curIndexInGroup + moveStep;
1058             }
1059         }
1060     } else {
1061         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1062             moveStep = 1;
1063             nextIndex = 0;
1064             nextIndexInGroup = -1;
1065         } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1066             moveStep = -1;
1067             nextIndex = maxListItemIndex_;
1068             nextIndexInGroup = -1;
1069         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1070             if (curIndexInGroup == -1) {
1071                 moveStep = lanes_;
1072                 nextIndex = curIndex + moveStep;
1073                 nextIndexInGroup = -1;
1074             } else {
1075                 moveStep = curListItemGroupPara.lanes;
1076                 nextIndexInGroup = curIndexInGroup + moveStep;
1077             }
1078         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1079             if (curIndexInGroup == -1) {
1080                 moveStep = -lanes_;
1081                 nextIndex = curIndex + moveStep;
1082                 nextIndexInGroup = -1;
1083             } else {
1084                 moveStep = -curListItemGroupPara.lanes;
1085                 nextIndexInGroup = curIndexInGroup + moveStep;
1086             }
1087         } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1088             moveStep = 1;
1089             if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1090                 ((curIndexInGroup != -1) &&
1091                     ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1092                 nextIndex = curIndex + moveStep;
1093                 nextIndexInGroup = -1;
1094             } else if ((curIndexInGroup != -1) &&
1095                        ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1096                 nextIndexInGroup = curIndexInGroup + moveStep;
1097             }
1098         } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1099             moveStep = -1;
1100             if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1101                 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1102                 nextIndex = curIndex + moveStep;
1103                 nextIndexInGroup = -1;
1104             } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1105                 nextIndexInGroup = curIndexInGroup + moveStep;
1106             }
1107         } else if (step == FocusStep::TAB) {
1108             moveStep = 1;
1109             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1110                 nextIndex = curIndex + moveStep;
1111                 nextIndexInGroup = -1;
1112             } else {
1113                 nextIndexInGroup = curIndexInGroup + moveStep;
1114             }
1115         } else if (step == FocusStep::SHIFT_TAB) {
1116             moveStep = -1;
1117             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1118                 nextIndex = curIndex + moveStep;
1119                 nextIndexInGroup = -1;
1120             } else {
1121                 nextIndexInGroup = curIndexInGroup + moveStep;
1122             }
1123         }
1124     }
1125     while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1126         if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1127             return nullptr;
1128         }
1129         auto nextFocusNode =
1130             ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1131         if (nextFocusNode.Upgrade()) {
1132             return nextFocusNode;
1133         }
1134         if (nextIndexInGroup > -1) {
1135             nextIndexInGroup += moveStep;
1136         } else {
1137             nextIndex += moveStep;
1138         }
1139     }
1140     return nullptr;
1141 }
1142 
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1143 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1144 {
1145     auto listFrame = GetHost();
1146     CHECK_NULL_RETURN(listFrame, nullptr);
1147     auto listFocus = listFrame->GetFocusHub();
1148     CHECK_NULL_RETURN(listFocus, nullptr);
1149     WeakPtr<FocusHub> target;
1150     listFocus->AnyChildFocusHub([&target, tarMainIndex, tarGroupIndex](const RefPtr<FocusHub>& childFocus) {
1151         if (!childFocus->IsFocusable()) {
1152             return false;
1153         }
1154         auto childFrame = childFocus->GetFrameNode();
1155         if (!childFrame) {
1156             return false;
1157         }
1158         auto childPattern = childFrame->GetPattern();
1159         if (!childPattern) {
1160             return false;
1161         }
1162         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1163         if (!childItemPattern) {
1164             return false;
1165         }
1166         auto curIndex = childItemPattern->GetIndexInList();
1167         auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1168         if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1169             target = childFocus;
1170             return true;
1171         }
1172         return false;
1173     });
1174     return target;
1175 }
1176 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1177 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1178 {
1179     CHECK_NULL_RETURN(focusFrameNode, false);
1180     auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1181     CHECK_NULL_RETURN(focusPattern, false);
1182     auto curIndex = focusPattern->GetIndexInList();
1183     ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1184     auto pipeline = GetContext();
1185     if (pipeline) {
1186         pipeline->FlushUITasks();
1187     }
1188     return true;
1189 }
1190 
GetScrollOffsetAbility()1191 ScrollOffsetAbility ListPattern::GetScrollOffsetAbility()
1192 {
1193     return {
1194         [wp = WeakClaim(this)](float moveOffset) -> bool {
1195             auto pattern = wp.Upgrade();
1196             CHECK_NULL_RETURN(pattern, false);
1197             pattern->ScrollBy(-moveOffset);
1198             return true;
1199         },
1200         GetAxis(),
1201         IsScrollSnapAlignCenter() ? 0 : contentStartOffset_,
1202         IsScrollSnapAlignCenter() ? 0 : contentEndOffset_,
1203     };
1204 }
1205 
GetScrollIndexAbility()1206 std::function<bool(int32_t)> ListPattern::GetScrollIndexAbility()
1207 {
1208     return [wp = WeakClaim(this)](int32_t index) -> bool {
1209         auto pattern = wp.Upgrade();
1210         CHECK_NULL_RETURN(pattern, false);
1211         if (index == FocusHub::SCROLL_TO_HEAD) {
1212             pattern->ScrollToIndex(0, false, ScrollAlign::START);
1213         } else if (index == FocusHub::SCROLL_TO_TAIL) {
1214             pattern->ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, false, ScrollAlign::END);
1215         } else {
1216             pattern->ScrollToIndex(index, false, ScrollAlign::AUTO);
1217         }
1218         return true;
1219     };
1220 }
1221 
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1222 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1223     int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1224 {
1225     auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1226     auto groupIndexInGroup = ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup,
1227         curIndexInGroup, moveStep, step, isScrollIndex);
1228 
1229     return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1230 }
1231 
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1232 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1233 {
1234     auto isScrollIndex = false;
1235     auto pipeline = PipelineContext::GetCurrentContext();
1236     CHECK_NULL_RETURN(pipeline, isScrollIndex);
1237     if (nextIndex < startIndex_) {
1238         if (nextIndexInGroup == -1) {
1239             isScrollIndex = true;
1240             ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1241             pipeline->FlushUITasks();
1242         } else {
1243             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1244             pipeline->FlushUITasks();
1245         }
1246     } else if (nextIndex > endIndex_) {
1247         if (nextIndexInGroup == -1) {
1248             isScrollIndex = true;
1249             ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1250             pipeline->FlushUITasks();
1251         } else {
1252             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1253             pipeline->FlushUITasks();
1254         }
1255     }
1256     return isScrollIndex;
1257 }
1258 
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1259 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1260     int32_t moveStep, FocusStep step, bool isScrollIndex)
1261 {
1262     auto groupIndexInGroup = true;
1263     auto pipeline = PipelineContext::GetCurrentContext();
1264     CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1265     RefPtr<FrameNode> nextIndexNode;
1266     auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1267     CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1268     if (!isNextInGroup) {
1269         nextIndexInGroup = -1;
1270         return groupIndexInGroup;
1271     }
1272     auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1273     if (nextIndexInGroup == -1) {
1274         auto scrollAlign = ScrollAlign::END;
1275         nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1276         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1277             (step == FocusStep::RIGHT_END)) {
1278             scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1279         } else {
1280             scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1281         }
1282         if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1283             (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1284             ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1285             pipeline->FlushUITasks();
1286         }
1287     } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1288         nextIndexInGroup = -1;
1289         groupIndexInGroup = false;
1290     } else {
1291         if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1292             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1293             pipeline->FlushUITasks();
1294         } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1295             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1296             pipeline->FlushUITasks();
1297         }
1298     }
1299     return groupIndexInGroup;
1300 }
1301 
OnAnimateStop()1302 void ListPattern::OnAnimateStop()
1303 {
1304     if (!GetIsDragging() || GetScrollAbort()) {
1305         scrollStop_ = true;
1306         MarkDirtyNodeSelf();
1307         isScrollEnd_ = true;
1308     }
1309     scrollTarget_.reset();
1310 }
1311 
ScrollTo(float position)1312 void ListPattern::ScrollTo(float position)
1313 {
1314     StopAnimate();
1315     jumpIndex_.reset();
1316     targetIndex_.reset();
1317     currentDelta_ = 0.0f;
1318     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1319     MarkDirtyNodeSelf();
1320     isScrollEnd_ = true;
1321 }
1322 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1323 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1324 {
1325     SetScrollSource(SCROLL_FROM_JUMP);
1326     if (!smooth) {
1327         StopAnimate();
1328     }
1329     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1330         currentDelta_ = 0.0f;
1331         smooth_ = smooth;
1332         if (smooth_) {
1333             SetExtraOffset(extraOffset);
1334             if (!AnimateToTarget(index, std::nullopt, align)) {
1335                 targetIndex_ = index;
1336                 scrollAlign_ = align;
1337             }
1338         } else {
1339             if (extraOffset.has_value()) {
1340                 currentDelta_ = extraOffset.value();
1341             }
1342             jumpIndex_ = index;
1343             scrollAlign_ = align;
1344         }
1345         MarkDirtyNodeSelf();
1346     }
1347     isScrollEnd_ = true;
1348     FireAndCleanScrollingListener();
1349 }
1350 
CheckTargetValid(int32_t index,int32_t indexInGroup)1351 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1352 {
1353     auto host = GetHost();
1354     auto totalItemCount = host->GetTotalChildCount();
1355     if ((index < 0) || (index >= totalItemCount)) {
1356         return false;
1357     }
1358     auto groupWrapper = host->GetOrCreateChildByIndex(index);
1359     CHECK_NULL_RETURN(groupWrapper, false);
1360     if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1361         return false;
1362     }
1363     auto groupNode = groupWrapper->GetHostNode();
1364     CHECK_NULL_RETURN(groupNode, false);
1365     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1366     CHECK_NULL_RETURN(groupPattern, false);
1367     auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1368     if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1369         return false;
1370     }
1371     return true;
1372 }
1373 
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1374 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1375 {
1376     SetScrollSource(SCROLL_FROM_JUMP);
1377     StopAnimate();
1378     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1379         currentDelta_ = 0.0f;
1380         smooth_ = smooth;
1381         if (smooth_) {
1382             if (!AnimateToTarget(index, indexInGroup, align)) {
1383                 if (CheckTargetValid(index, indexInGroup)) {
1384                     targetIndex_ = index;
1385                     currentDelta_ = 0;
1386                     targetIndexInGroup_ = indexInGroup;
1387                     scrollAlign_ = align;
1388                 }
1389             }
1390         } else {
1391             jumpIndex_ = index;
1392             jumpIndexInGroup_ = indexInGroup;
1393             scrollAlign_ = align;
1394         }
1395         MarkDirtyNodeSelf();
1396     }
1397     isScrollEnd_ = true;
1398     FireAndCleanScrollingListener();
1399 }
1400 
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1401 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1402 {
1403     switch (align) {
1404         case ScrollAlign::START:
1405         case ScrollAlign::NONE:
1406             targetPos = startPos;
1407             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1408                 targetPos -= contentStartOffset_;
1409             }
1410             break;
1411         case ScrollAlign::CENTER:
1412             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1413             break;
1414         case ScrollAlign::END:
1415             targetPos = endPos - contentMainSize_;
1416             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1417                 targetPos += contentEndOffset_;
1418             }
1419             break;
1420         case ScrollAlign::AUTO:
1421             targetPos = CalculateTargetPos(startPos, endPos);
1422             break;
1423     }
1424     return true;
1425 }
1426 
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1427 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(
1428     int32_t index, float startPos, float endPos, ScrollAlign align, float& targetPos)
1429 {
1430     auto host = GetHost();
1431     CHECK_NULL_RETURN(host, false);
1432     auto groupWrapper = host->GetChildByIndex(index);
1433     CHECK_NULL_RETURN(groupWrapper, false);
1434     auto groupNode = groupWrapper->GetHostNode();
1435     CHECK_NULL_RETURN(groupNode, false);
1436     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1437     CHECK_NULL_RETURN(groupPattern, false);
1438     auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1439     CHECK_NULL_RETURN(groupLayoutProperty, false);
1440     auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1441 
1442     switch (align) {
1443         case ScrollAlign::START:
1444         case ScrollAlign::NONE:
1445             if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1446                 return false;
1447             }
1448             targetPos = startPos;
1449             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1450                 targetPos -= contentStartOffset_;
1451             }
1452             break;
1453         case ScrollAlign::CENTER:
1454             if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1455                 return false;
1456             }
1457             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1458             break;
1459         case ScrollAlign::END:
1460             if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1461                 return false;
1462             }
1463             targetPos = endPos - contentMainSize_;
1464             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1465                 targetPos += contentEndOffset_;
1466             }
1467             break;
1468         case ScrollAlign::AUTO:
1469             if (targetIndex_.has_value()) {
1470                 targetPos = CalculateTargetPos(startPos, endPos);
1471                 return true;
1472             }
1473             return false;
1474     }
1475 
1476     return true;
1477 }
1478 
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1479 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(
1480     int32_t index, int32_t indexInGroup, float startPos, ScrollAlign align, float& targetPos)
1481 {
1482     auto host = GetHost();
1483     CHECK_NULL_RETURN(host, false);
1484     auto groupWrapper = host->GetChildByIndex(index);
1485     CHECK_NULL_RETURN(groupWrapper, false);
1486     auto groupNode = groupWrapper->GetHostNode();
1487     CHECK_NULL_RETURN(groupNode, false);
1488     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1489     CHECK_NULL_RETURN(groupPattern, false);
1490     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1491     CHECK_NULL_RETURN(listLayoutProperty, false);
1492     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1493     auto itemsPosInGroup = groupPattern->GetItemPosition();
1494     auto it = itemsPosInGroup.find(indexInGroup);
1495     if (it == itemsPosInGroup.end()) {
1496         return false;
1497     }
1498     auto axis = GetAxis();
1499     std::optional<float> padding;
1500     std::optional<float> margin;
1501     if (axis == Axis::HORIZONTAL) {
1502         padding = IsReverse() ? groupWrapper->GetGeometryNode()->GetPadding()->right
1503                               : groupWrapper->GetGeometryNode()->GetPadding()->left;
1504         margin = IsReverse() ? groupWrapper->GetGeometryNode()->GetMargin()->right
1505                              : groupWrapper->GetGeometryNode()->GetMargin()->left;
1506     } else {
1507         padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1508         margin = groupWrapper->GetGeometryNode()->GetMargin()->top;
1509     }
1510     auto marginValue = margin.value_or(0.f);
1511     auto paddingValue = padding.value_or(0.f);
1512     if (align == ScrollAlign::CENTER) {
1513         targetPos = paddingValue + marginValue + startPos + (it->second.startPos + it->second.endPos) / 2.0f -
1514                     contentMainSize_ / 2.0f;
1515     } else {
1516         float itemStartPos = paddingValue + marginValue + startPos + it->second.startPos;
1517         float itemEndPos = paddingValue + marginValue + startPos + it->second.endPos;
1518         if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1519             itemStartPos -= groupPattern->GetHeaderMainSize();
1520         }
1521         if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1522             itemEndPos += groupPattern->GetFooterMainSize();
1523         }
1524         if (!IsScrollSnapAlignCenter() || childrenSize_) {
1525             itemStartPos -= contentStartOffset_;
1526             itemEndPos += contentEndOffset_;
1527         }
1528         if (align == ScrollAlign::AUTO) {
1529             targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1530         } else {
1531             targetPos = align == ScrollAlign::END ? itemEndPos - contentMainSize_ : itemStartPos;
1532         }
1533     }
1534     return true;
1535 }
1536 
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)1537 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
1538 {
1539     auto iter = itemPosition_.find(index);
1540     if (iter == itemPosition_.end()) {
1541         return false;
1542     }
1543     float targetPos = 0.0f;
1544     if (iter->second.isGroup) {
1545         if (indexInGroup.has_value()) {
1546             if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
1547                 align, targetPos)) {
1548                 return false;
1549             }
1550         } else {
1551             if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
1552                 align, targetPos)) {
1553                 return false;
1554             }
1555         }
1556     } else {
1557         if (indexInGroup.has_value()) {
1558             return false;
1559         }
1560         GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
1561     }
1562     float extraOffset = 0.0f;
1563     if (GetExtraOffset().has_value()) {
1564         extraOffset = GetExtraOffset().value();
1565         targetPos += extraOffset;
1566         ResetExtraOffset();
1567     }
1568     if (!NearZero(targetPos)) {
1569         AnimateTo(targetPos + currentOffset_, -1, nullptr, true, false);
1570         if (predictSnapOffset_.has_value() && AnimateRunning()) {
1571             scrollSnapVelocity_ = 0.0f;
1572             predictSnapOffset_.reset();
1573             snapTrigOnScrollStart_ = false;
1574         }
1575         if (!indexInGroup.has_value()) {
1576             scrollTarget_ = { index, extraOffset, align, targetPos + currentOffset_ };
1577         }
1578     }
1579     return true;
1580 }
1581 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)1582 void ListPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
1583 {
1584     float distance = reverse ? contentMainSize_ : -contentMainSize_;
1585     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
1586         distance = distance / 2.f;
1587     }
1588     if (smooth) {
1589         float position = -GetTotalOffset() + distance;
1590         AnimateTo(-position, -1, nullptr, true, false, false);
1591     } else {
1592         StopAnimate();
1593         UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1594         isScrollEnd_ = true;
1595     }
1596 }
1597 
ScrollBy(float offset)1598 void ListPattern::ScrollBy(float offset)
1599 {
1600     StopAnimate();
1601     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1602     isScrollEnd_ = true;
1603 }
1604 
GetCurrentOffset() const1605 Offset ListPattern::GetCurrentOffset() const
1606 {
1607     if (GetAxis() == Axis::HORIZONTAL) {
1608         return { GetTotalOffset(), 0.0 };
1609     }
1610     return { 0.0, GetTotalOffset() };
1611 }
1612 
HandleScrollBarOutBoundary()1613 void ListPattern::HandleScrollBarOutBoundary()
1614 {
1615     if (itemPosition_.empty()) {
1616         return;
1617     }
1618     if (!GetScrollBar() && !GetScrollBarProxy()) {
1619         return;
1620     }
1621     if (!IsOutOfBoundary(false) || !isScrollable_) {
1622         ScrollablePattern::HandleScrollBarOutBoundary(0);
1623         return;
1624     }
1625     float overScroll = 0.0f;
1626     if (!IsScrollSnapAlignCenter()) {
1627         if ((itemPosition_.begin()->first == 0) && GreatNotEqual(startMainPos_, contentStartOffset_)) {
1628             overScroll = startMainPos_ - contentStartOffset_;
1629         } else {
1630             overScroll = contentMainSize_ - contentEndOffset_ - endMainPos_;
1631         }
1632     } else {
1633         float itemHeight = itemPosition_[centerIndex_].endPos - itemPosition_[centerIndex_].startPos;
1634         if (startIndex_ == 0 && Positive(startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1635             overScroll = startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1636         } else if ((endIndex_ == maxListItemIndex_) &&
1637                    LessNotEqual(endMainPos_ - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
1638             overScroll = endMainPos_ - itemHeight / 2.0f - contentMainSize_ / 2.0f;
1639         }
1640     }
1641     ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
1642 }
1643 
GetItemRect(int32_t index) const1644 Rect ListPattern::GetItemRect(int32_t index) const
1645 {
1646     if (index < 0 || index < startIndex_ || index > endIndex_) {
1647         return Rect();
1648     }
1649     auto host = GetHost();
1650     CHECK_NULL_RETURN(host, Rect());
1651     auto item = host->GetChildByIndex(index);
1652     CHECK_NULL_RETURN(item, Rect());
1653     auto itemGeometry = item->GetGeometryNode();
1654     CHECK_NULL_RETURN(itemGeometry, Rect());
1655     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1656         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1657 }
1658 
GetItemIndex(double x,double y) const1659 int32_t ListPattern::GetItemIndex(double x, double y) const
1660 {
1661     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
1662         Rect rect = GetItemRect(index);
1663         if (rect.IsInRegion({x, y})) {
1664             return index;
1665         }
1666     }
1667     return -1;
1668 }
1669 
GetItemIndexInGroup(double x,double y) const1670 ListItemIndex ListPattern::GetItemIndexInGroup(double x, double y) const
1671 {
1672     ListItemIndex itemIndex = { -1, -1, -1 };
1673 
1674     auto host = GetHost();
1675     CHECK_NULL_RETURN(host, itemIndex);
1676     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
1677         auto item = host->GetChildByIndex(index);
1678         if (!AceType::InstanceOf<FrameNode>(item)) {
1679             continue;
1680         }
1681         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
1682         auto groupItemPattern  = itemFrameNode->GetPattern<ListItemGroupPattern>();
1683         if (groupItemPattern) {
1684             if (GetGroupItemIndex(x, y, itemFrameNode, index, itemIndex)) {
1685                 return itemIndex;
1686             }
1687         } else {
1688             Rect rect = GetItemRect(index);
1689             if (rect.IsInRegion({x, y})) {
1690                 itemIndex.index = index;
1691                 return itemIndex;
1692             }
1693         }
1694     }
1695     return itemIndex;
1696 }
1697 
GetGroupItemIndex(double x,double y,RefPtr<FrameNode> itemFrameNode,int32_t & index,ListItemIndex & itemIndex) const1698 bool ListPattern::GetGroupItemIndex(double x, double y, RefPtr<FrameNode> itemFrameNode,
1699     int32_t& index, ListItemIndex& itemIndex) const
1700 {
1701     auto groupItemPattern = itemFrameNode->GetPattern<ListItemGroupPattern>();
1702     Rect rect = GetItemRect(index);
1703     if (groupItemPattern && rect.IsInRegion({x, y})) {
1704         itemIndex.index = index;
1705         for (int32_t groupIndex = groupItemPattern->GetDisplayStartIndexInGroup();
1706             groupIndex <= groupItemPattern->GetDisplayEndIndexInGroup(); ++groupIndex) {
1707             Rect groupRect = GetItemRectInGroup(index, groupIndex);
1708             if (groupRect.IsInRegion({x, y})) {
1709                 itemIndex.index = index;
1710                 itemIndex.area = 1; // item area
1711                 itemIndex.indexInGroup = groupIndex;
1712                 return true;
1713             }
1714         }
1715 
1716         int32_t areaValue = 0;
1717         if (GetAxis() == Axis::VERTICAL) {
1718             areaValue = ProcessAreaVertical(x, y, rect, index, groupItemPattern);
1719         } else {
1720             areaValue = ProcessAreaHorizontal(x, y, rect, index, groupItemPattern);
1721         }
1722         if (areaValue != -1) {
1723             itemIndex.index = index;
1724             itemIndex.area = areaValue;
1725             itemIndex.indexInGroup = -1;
1726             return true;
1727         }
1728     }
1729 
1730     return false;
1731 }
1732 
ProcessAreaVertical(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const1733 int32_t ListPattern::ProcessAreaVertical(double& x, double& y, Rect& groupRect, int32_t& index,
1734     RefPtr<ListItemGroupPattern> groupItemPattern) const
1735 {
1736     if (groupItemPattern->GetTotalItemCount() > 0) { // has item
1737         Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
1738         Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
1739 
1740         if (groupItemPattern->IsHasHeader() && LessOrEqual(y, firstRect.Top()) && GreatOrEqual(y, groupRect.Top())) {
1741             return  DEFAULT_HEADER_VALUE;
1742         }
1743 
1744         if (groupItemPattern->IsHasFooter() && GreatOrEqual(y, endRect.Bottom()) &&
1745             LessOrEqual(y, groupRect.Bottom())) {
1746             return  DEFAULT_FOOTER_VALUE;
1747         }
1748     } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
1749         float headerHeight = groupItemPattern->GetHeaderMainSize();
1750         float footerHeight = groupItemPattern->GetFooterMainSize();
1751         float topPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->top.value_or(0.0f);
1752         float bottomPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->bottom.value_or(0.0f);
1753         if (LessOrEqual(y, groupRect.Top() + headerHeight + topPaddng)  && GreatOrEqual(y, groupRect.Top())) { //header
1754             return  DEFAULT_HEADER_VALUE;
1755         } else if (GreatOrEqual(y, groupRect.Bottom() - footerHeight - bottomPaddng) &&
1756             LessOrEqual(y, groupRect.Bottom())) {
1757             return  DEFAULT_FOOTER_VALUE;
1758         }
1759     } else if (GreatOrEqual(y, groupRect.Top())  && LessOrEqual(y, groupRect.Bottom())) {
1760         return  0;
1761     }
1762 
1763     return -1;
1764 }
1765 
ProcessAreaHorizontal(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const1766 int32_t ListPattern::ProcessAreaHorizontal(double& x, double& y, Rect& groupRect, int32_t& index,
1767     RefPtr<ListItemGroupPattern> groupItemPattern) const
1768 {
1769     if (groupItemPattern->GetTotalItemCount() > 0) { // has item
1770         Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
1771         Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
1772 
1773         if (groupItemPattern->IsHasHeader() && LessOrEqual(x, firstRect.Left()) && GreatOrEqual(x, groupRect.Left())) {
1774             return  DEFAULT_HEADER_VALUE;
1775         }
1776 
1777         if (groupItemPattern->IsHasFooter() && GreatOrEqual(x, endRect.Right()) && LessOrEqual(x, groupRect.Right())) {
1778             return  DEFAULT_FOOTER_VALUE;
1779         }
1780     } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
1781         float headerHeight = groupItemPattern->GetHeaderMainSize();
1782         float footerHeight = groupItemPattern->GetFooterMainSize();
1783         float leftPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->left.value_or(0.0f);
1784         float rightPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->right.value_or(0.0f);
1785         if (LessOrEqual(x, groupRect.Left() + headerHeight + leftPaddng)  && GreatOrEqual(x, groupRect.Left())) {
1786             return  DEFAULT_HEADER_VALUE;
1787         } else if (GreatOrEqual(x, groupRect.Right() - footerHeight - rightPaddng) &&
1788             LessOrEqual(x, groupRect.Right())) {
1789             return  DEFAULT_FOOTER_VALUE;
1790         }
1791     } else if (GreatOrEqual(x, groupRect.Left())  && LessOrEqual(x, groupRect.Right())) {
1792         return  0;
1793     }
1794 
1795     return -1;
1796 }
1797 
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const1798 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
1799 {
1800     if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
1801         return Rect();
1802     }
1803     auto host = GetHost();
1804     CHECK_NULL_RETURN(host, Rect());
1805     auto itemGroupWrapper = host->GetChildByIndex(index);
1806     CHECK_NULL_RETURN(itemGroupWrapper, Rect());
1807     auto itemGroup = itemGroupWrapper->GetHostNode();
1808     CHECK_NULL_RETURN(itemGroup, Rect());
1809     if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
1810         return Rect();
1811     }
1812     auto itemGroupGeometry = itemGroup->GetGeometryNode();
1813     CHECK_NULL_RETURN(itemGroupGeometry, Rect());
1814     auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1815     CHECK_NULL_RETURN(groupPattern, Rect());
1816     if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
1817         indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
1818         return Rect();
1819     }
1820     auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
1821     CHECK_NULL_RETURN(groupItem, Rect());
1822     auto groupItemGeometry = groupItem->GetGeometryNode();
1823     CHECK_NULL_RETURN(groupItemGeometry, Rect());
1824     return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
1825         itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
1826         groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
1827 }
1828 
UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,bool isJump)1829 float ListPattern::UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, bool isJump)
1830 {
1831     float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
1832     float prevOffset = currentOffset_;
1833     if (childrenSize_) {
1834         listTotalHeight_ = posMap_->GetTotalHeight();
1835         currentOffset_ = itemPosition_.empty() ? 0.0f :
1836             posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos);
1837     } else {
1838         if (isJump || needReEstimateOffset_) {
1839             auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1840             calculate.GetEstimateHeightAndOffset(GetHost());
1841             currentOffset_ = calculate.GetEstimateOffset();
1842             relativeOffset = 0;
1843             needReEstimateOffset_ = false;
1844             posMap_->ClearPosMap();
1845         }
1846         CalculateCurrentOffset(relativeOffset, listLayoutAlgorithm->GetRecycledItemPosition());
1847     }
1848     if (scrollTarget_) {
1849         auto& target = scrollTarget_.value();
1850         auto posInfo = posMap_->GetPositionInfo(target.index);
1851         if (!Negative(posInfo.mainPos)) {
1852             float startPos = posInfo.mainPos - currentOffset_;
1853             float targetPos = 0.0f;
1854             GetListItemAnimatePos(startPos, startPos + posInfo.mainSize, target.align, targetPos);
1855             targetPos += currentOffset_ + target.extraOffset;
1856             const float epsilon = 0.1f;
1857             if (!NearEqual(relativeOffset + prevOffset, currentOffset_, epsilon) ||
1858                 !NearEqual(target.targetOffset, targetPos, epsilon)) {
1859                 target.targetOffset = targetPos;
1860                 AnimateTo(targetPos, -1, nullptr, true);
1861             }
1862         }
1863     }
1864     return currentOffset_ - prevOffset;
1865 }
1866 
CalculateCurrentOffset(float delta,const ListLayoutAlgorithm::PositionMap & recycledItemPosition)1867 void ListPattern::CalculateCurrentOffset(float delta, const ListLayoutAlgorithm::PositionMap& recycledItemPosition)
1868 {
1869     posMap_->UpdateTotalCount(maxListItemIndex_ + 1);
1870     if (itemPosition_.empty()) {
1871         return;
1872     }
1873     auto itemPos = itemPosition_;
1874     for (auto& [index, pos] : recycledItemPosition) {
1875         itemPos.try_emplace(index, pos);
1876     }
1877     float startPos = itemPos.begin()->second.startPos;
1878     int32_t startIndex = itemPos.begin()->first;
1879     auto& groupInfo = itemPos.begin()->second.groupInfo;
1880     bool groupAtStart = (!groupInfo || groupInfo.value().atStart);
1881     if (startIndex == 0 && groupAtStart) {
1882         currentOffset_ = -startPos;
1883     } else {
1884         posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, startIndex, startPos, groupAtStart);
1885     }
1886     for (auto& [index, pos] : itemPos) {
1887         float height = pos.endPos - pos.startPos;
1888         if (pos.groupInfo) {
1889             bool groupAtStart = pos.groupInfo.value().atStart;
1890             if (groupAtStart) {
1891                 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1892             } else {
1893                 posMap_->UpdatePosWithCheck(index, { currentOffset_ + pos.startPos, height });
1894             }
1895         } else {
1896             posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1897         }
1898     }
1899     auto& endGroupInfo = itemPos.rbegin()->second.groupInfo;
1900     bool groupAtEnd = (!endGroupInfo || endGroupInfo.value().atEnd);
1901     posMap_->UpdatePosMapEnd(itemPos.rbegin()->first, spaceWidth_, groupAtEnd);
1902 }
1903 
UpdateChildPosInfo(int32_t index,float delta,float sizeChange)1904 void ListPattern::UpdateChildPosInfo(int32_t index, float delta, float sizeChange)
1905 {
1906     if (itemPosition_.find(index) == itemPosition_.end()) {
1907         return;
1908     }
1909     if (index == GetStartIndex()) {
1910         sizeChange += delta;
1911         float startPos = itemPosition_.begin()->second.startPos;
1912         auto iter = itemPosition_.begin();
1913         while (iter != itemPosition_.end() && NearEqual(startPos, iter->second.startPos)) {
1914             iter->second.startPos += delta;
1915             iter++;
1916         }
1917     }
1918     if (index == GetEndIndex()) {
1919         float endPos = itemPosition_.rbegin()->second.endPos;
1920         auto iter = itemPosition_.rbegin();
1921         while (iter != itemPosition_.rend() && NearEqual(endPos, iter->second.endPos)) {
1922             iter->second.endPos += sizeChange;
1923             iter++;
1924         }
1925     }
1926     CalculateCurrentOffset(0.0f, ListLayoutAlgorithm::PositionMap());
1927 }
1928 
UpdateScrollBarOffset()1929 void ListPattern::UpdateScrollBarOffset()
1930 {
1931     CheckScrollBarOff();
1932     if (itemPosition_.empty()) {
1933         return;
1934     }
1935     if (!GetScrollBar() && !GetScrollBarProxy()) {
1936         return;
1937     }
1938     float currentOffset = 0.0f;
1939     float estimatedHeight = 0.0f;
1940     if (childrenSize_) {
1941         currentOffset = currentOffset_;
1942         estimatedHeight = listTotalHeight_;
1943     } else {
1944         auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1945         calculate.GetEstimateHeightAndOffset(GetHost());
1946         currentOffset = calculate.GetEstimateOffset();
1947         estimatedHeight = calculate.GetEstimateHeight();
1948     }
1949     if (GetAlwaysEnabled()) {
1950         estimatedHeight = estimatedHeight - spaceWidth_;
1951     }
1952     if (!IsScrollSnapAlignCenter() || childrenSize_) {
1953         currentOffset += contentStartOffset_;
1954         estimatedHeight += contentStartOffset_ + contentEndOffset_;
1955     }
1956 
1957     // calculate padding offset of list
1958     auto host = GetHost();
1959     CHECK_NULL_VOID(host);
1960     auto layoutPriority = host->GetLayoutProperty();
1961     CHECK_NULL_VOID(layoutPriority);
1962     auto padding = layoutPriority->CreatePaddingAndBorder();
1963     auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
1964     const auto& geometryNode = host->GetGeometryNode();
1965     auto frameSize = geometryNode->GetFrameSize();
1966     Size size(frameSize.Width(), frameSize.Height());
1967     UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
1968 }
1969 
GetTotalHeight() const1970 float ListPattern::GetTotalHeight() const
1971 {
1972     auto currentOffset = GetTotalOffset();
1973     if (endIndex_ >= maxListItemIndex_) {
1974         return currentOffset + endMainPos_ + contentEndOffset_;
1975     }
1976     if (itemPosition_.empty()) {
1977         return 0.0f;
1978     }
1979     int32_t remainCount = maxListItemIndex_ - endIndex_;
1980     float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1981     float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
1982     return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
1983 }
1984 
TriggerModifyDone()1985 void ListPattern::TriggerModifyDone()
1986 {
1987     OnModifyDone();
1988 }
1989 
SetChainAnimationCallback()1990 void ListPattern::SetChainAnimationCallback()
1991 {
1992     CHECK_NULL_VOID(chainAnimation_);
1993     chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1994         auto list = weak.Upgrade();
1995         CHECK_NULL_VOID(list);
1996         list->MarkDirtyNodeSelf();
1997     });
1998     auto scrollEffect = AceType::DynamicCast<ScrollSpringEffect>(GetScrollEdgeEffect());
1999     CHECK_NULL_VOID(scrollEffect);
2000     scrollEffect->SetOnWillStartSpringCallback([weak = AceType::WeakClaim(this)]() {
2001         auto list = weak.Upgrade();
2002         CHECK_NULL_VOID(list);
2003         if (!list->dragFromSpring_ && list->chainAnimation_) {
2004             auto delta = list->chainAnimation_->SetControlIndex(list->IsAtTop() ? 0 : list->maxListItemIndex_);
2005             list->currentDelta_ -= delta;
2006             list->dragFromSpring_ = true;
2007         }
2008     });
2009 }
2010 
SetChainAnimation()2011 void ListPattern::SetChainAnimation()
2012 {
2013     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2014     CHECK_NULL_VOID(listLayoutProperty);
2015     auto edgeEffect = GetEdgeEffect();
2016     int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
2017     bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
2018     bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
2019     bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
2020     if (!enable) {
2021         chainAnimation_.Reset();
2022         return;
2023     }
2024     auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2025     if (Negative(space)) {
2026         space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2027     }
2028     if (!chainAnimation_ || (chainAnimation_ && space != chainAnimation_->GetSpace())) {
2029         springProperty_ =
2030             AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
2031         if (chainAnimationOptions_.has_value()) {
2032             float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
2033             float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
2034             minSpace = Negative(minSpace) ? 0.0f : minSpace;
2035             minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2036             maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2037             springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2038             springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2039             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2040             auto conductivity = chainAnimationOptions_.value().conductivity;
2041             if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2042                 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2043             }
2044             chainAnimation_->SetConductivity(conductivity);
2045             auto intensity = chainAnimationOptions_.value().intensity;
2046             if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2047                 intensity = ChainAnimation::DEFAULT_INTENSITY;
2048             }
2049             chainAnimation_->SetIntensity(intensity);
2050             auto effect = chainAnimationOptions_.value().edgeEffect;
2051             chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2052         } else {
2053             auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
2054             auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
2055             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2056         }
2057         SetChainAnimationCallback();
2058     }
2059 }
2060 
SetChainAnimationOptions(const ChainAnimationOptions & options)2061 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
2062 {
2063     chainAnimationOptions_ = options;
2064     if (chainAnimation_) {
2065         auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2066         CHECK_NULL_VOID(listLayoutProperty);
2067         auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2068         if (Negative(space)) {
2069             space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2070         }
2071         float maxSpace = options.maxSpace.ConvertToPx();
2072         float minSpace = options.minSpace.ConvertToPx();
2073         minSpace = Negative(minSpace) ? 0.0f : minSpace;
2074         minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2075         maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2076         chainAnimation_->SetSpace(space, maxSpace, minSpace);
2077         auto conductivity = chainAnimationOptions_.value().conductivity;
2078         if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2079             conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2080         }
2081         chainAnimation_->SetConductivity(conductivity);
2082         auto intensity = chainAnimationOptions_.value().intensity;
2083         if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2084             intensity = ChainAnimation::DEFAULT_INTENSITY;
2085         }
2086         chainAnimation_->SetIntensity(intensity);
2087         auto effect = options.edgeEffect;
2088         chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2089     }
2090     if (springProperty_) {
2091         springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2092         springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2093     }
2094 }
2095 
OnTouchDown(const TouchEventInfo & info)2096 void ListPattern::OnTouchDown(const TouchEventInfo& info)
2097 {
2098     ScrollablePattern::OnTouchDown(info);
2099     auto& touches = info.GetTouches();
2100     if (touches.empty()) {
2101         return;
2102     }
2103     auto offset = touches.front().GetLocalLocation();
2104     float startPosition = GetAxis() == Axis::HORIZONTAL ? offset.GetX() : offset.GetY();
2105     ProcessDragStart(startPosition);
2106 }
2107 
ProcessDragStart(float startPosition)2108 void ListPattern::ProcessDragStart(float startPosition)
2109 {
2110     CHECK_NULL_VOID(chainAnimation_);
2111     auto host = GetHost();
2112     CHECK_NULL_VOID(host);
2113     auto globalOffset = host->GetTransformRelativeOffset();
2114     int32_t index = -1;
2115     auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
2116     auto it = std::find_if(
2117         itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
2118     if (it != itemPosition_.end()) {
2119         index = it->first;
2120     } else if (!itemPosition_.empty()) {
2121         index = itemPosition_.rbegin()->first + 1;
2122     }
2123     dragFromSpring_ = false;
2124     float delta = chainAnimation_->SetControlIndex(index);
2125     currentDelta_ -= delta;
2126     chainAnimation_->SetMaxIndex(maxListItemIndex_);
2127 }
2128 
ProcessDragUpdate(float dragOffset,int32_t source)2129 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
2130 {
2131     CHECK_NULL_VOID(chainAnimation_);
2132     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR_FLING) {
2133         return;
2134     }
2135     if (NeedScrollSnapAlignEffect()) {
2136         auto delta = 0.0f;
2137         if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
2138             delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
2139         }
2140         if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
2141             delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
2142         }
2143         if (!NearZero(delta)) {
2144             auto scrollableEvent = GetScrollableEvent();
2145             CHECK_NULL_VOID(scrollableEvent);
2146             auto scrollable = scrollableEvent->GetScrollable();
2147             CHECK_NULL_VOID(scrollable);
2148             scrollable->UpdateScrollSnapStartOffset(delta);
2149             currentDelta_ -= delta;
2150         }
2151     }
2152     float overOffset = 0.0f;
2153     if (!itemPosition_.empty()) {
2154         auto res = GetOutBoundaryOffset(-dragOffset, false);
2155         overOffset = std::max(res.start, res.end);
2156         if (!NearZero(res.end)) {
2157             overOffset = -overOffset;
2158         }
2159     }
2160     if (source == SCROLL_FROM_UPDATE && !NearZero(overOffset)) {
2161         dragOffset = 0.0f;
2162     }
2163     chainAnimation_->SetDelta(-dragOffset, overOffset);
2164     if (source == SCROLL_FROM_UPDATE && GetCanOverScroll()) {
2165         float tempDelta = currentDelta_;
2166         currentDelta_ -= dragOffset;
2167         bool isAtEdge = IsAtTop() || IsAtBottom();
2168         currentDelta_ = tempDelta;
2169         SetCanOverScroll(isAtEdge);
2170     }
2171 }
2172 
GetChainDelta(int32_t index) const2173 float ListPattern::GetChainDelta(int32_t index) const
2174 {
2175     CHECK_NULL_RETURN(chainAnimation_, 0.0f);
2176     return chainAnimation_->GetValue(index);
2177 }
2178 
MultiSelectWithoutKeyboard(const RectF & selectedZone)2179 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
2180 {
2181     auto host = GetHost();
2182     CHECK_NULL_VOID(host);
2183     std::list<RefPtr<FrameNode>> childrens;
2184     host->GenerateOneDepthVisibleFrame(childrens);
2185     for (const auto& item : childrens) {
2186         if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2187             auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
2188             CHECK_NULL_VOID(itemGroupPattern);
2189             auto itemGroupGeometry = item->GetGeometryNode();
2190             CHECK_NULL_VOID(itemGroupGeometry);
2191             auto itemGroupRect = itemGroupGeometry->GetFrameRect();
2192             if (!selectedZone.IsIntersectWith(itemGroupRect)) {
2193                 continue;
2194             }
2195             HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
2196             continue;
2197         }
2198         auto itemPattern = item->GetPattern<ListItemPattern>();
2199         CHECK_NULL_VOID(itemPattern);
2200         if (!itemPattern->Selectable()) {
2201             continue;
2202         }
2203 
2204         auto itemGeometry = item->GetGeometryNode();
2205         CHECK_NULL_VOID(itemGeometry);
2206 
2207         auto itemRect = itemGeometry->GetFrameRect();
2208         if (!selectedZone.IsIntersectWith(itemRect)) {
2209             itemPattern->MarkIsSelected(false);
2210         } else {
2211             itemPattern->MarkIsSelected(true);
2212         }
2213     }
2214 
2215     DrawSelectedZone(selectedZone);
2216 }
2217 
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)2218 void ListPattern::HandleCardModeSelectedEvent(
2219     const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
2220 {
2221     CHECK_NULL_VOID(itemGroupNode);
2222     std::list<RefPtr<FrameNode>> childrens;
2223     itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
2224     for (const auto& item : childrens) {
2225         auto itemPattern = item->GetPattern<ListItemPattern>();
2226         if (!itemPattern) {
2227             continue;
2228         }
2229         if (!itemPattern->Selectable()) {
2230             continue;
2231         }
2232         auto itemGeometry = item->GetGeometryNode();
2233         CHECK_NULL_VOID(itemGeometry);
2234         auto context = item->GetRenderContext();
2235         CHECK_NULL_VOID(context);
2236         auto itemRect = itemGeometry->GetFrameRect();
2237         RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
2238         if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
2239             itemPattern->MarkIsSelected(false);
2240         } else {
2241             itemPattern->MarkIsSelected(true);
2242         }
2243     }
2244 }
2245 
ClearMultiSelect()2246 void ListPattern::ClearMultiSelect()
2247 {
2248     auto host = GetHost();
2249     CHECK_NULL_VOID(host);
2250     std::list<RefPtr<FrameNode>> children;
2251     host->GenerateOneDepthAllFrame(children);
2252     for (const auto& child : children) {
2253         if (!child) {
2254             continue;
2255         }
2256         auto itemPattern = child->GetPattern<ListItemPattern>();
2257         if (itemPattern) {
2258             itemPattern->MarkIsSelected(false);
2259             continue;
2260         }
2261         auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
2262         if (itemGroupPattern) {
2263             std::list<RefPtr<FrameNode>> itemChildren;
2264             child->GenerateOneDepthAllFrame(itemChildren);
2265             for (const auto& item : itemChildren) {
2266                 if (!item) {
2267                     continue;
2268                 }
2269                 itemPattern = item->GetPattern<ListItemPattern>();
2270                 if (itemPattern) {
2271                     itemPattern->MarkIsSelected(false);
2272                 }
2273             }
2274         }
2275     }
2276 
2277     ClearSelectedZone();
2278 }
2279 
IsItemSelected(const GestureEvent & info)2280 bool ListPattern::IsItemSelected(const GestureEvent& info)
2281 {
2282     auto host = GetHost();
2283     CHECK_NULL_RETURN(host, false);
2284     auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2285     CHECK_NULL_RETURN(node, false);
2286     auto itemPattern = node->GetPattern<ListItemPattern>();
2287     if (itemPattern) {
2288         return itemPattern->IsSelected();
2289     }
2290     auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
2291     if (itemGroupPattern) {
2292         auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2293         CHECK_NULL_RETURN(itemNode, false);
2294         itemPattern = itemNode->GetPattern<ListItemPattern>();
2295         CHECK_NULL_RETURN(itemPattern, false);
2296         return itemPattern->IsSelected();
2297     }
2298     return false;
2299 }
2300 
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)2301 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
2302 {
2303     // swiper item only can be replaced when no other items be dragged
2304     if (canReplaceSwiperItem_) {
2305         if (swiperItem != swiperItem_) {
2306             auto item = swiperItem_.Upgrade();
2307             if (item) {
2308                 item->ResetSwipeStatus();
2309             }
2310             swiperItem_ = std::move(swiperItem);
2311         }
2312         canReplaceSwiperItem_ = false;
2313     }
2314     FireAndCleanScrollingListener();
2315 }
2316 
GetItemIndexByPosition(float xOffset,float yOffset)2317 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
2318 {
2319     auto host = GetHost();
2320     auto globalOffset = host->GetTransformRelativeOffset();
2321     float relativeX = xOffset - globalOffset.GetX();
2322     float relativeY = yOffset - globalOffset.GetY();
2323     float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
2324     float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
2325     float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
2326     int32_t lanesOffset = 0;
2327     if (lanes_ > 1) {
2328         lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
2329     }
2330     for (auto& pos : itemPosition_) {
2331         if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2332             return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
2333         }
2334     }
2335     if (!itemPosition_.empty()) {
2336         return itemPosition_.rbegin()->first + 1;
2337     }
2338     return 0;
2339 }
2340 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2341 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2342 {
2343     ScrollablePattern::ToJsonValue(json, filter);
2344     /* no fixed attr below, just return */
2345     if (filter.IsFastFilter()) {
2346         return;
2347     }
2348     json->PutExtAttr("multiSelectable", multiSelectable_, filter);
2349     json->PutExtAttr("startIndex", startIndex_, filter);
2350     if (!itemPosition_.empty()) {
2351         json->PutExtAttr("itemStartPos", itemPosition_.begin()->second.startPos, filter);
2352     }
2353     auto nestedScrollOptions = JsonUtil::Create(true);
2354     auto nestedScroll = GetNestedScroll();
2355     nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
2356     nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
2357     json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
2358     json->PutExtAttr("maintainVisibleContentPosition", maintainVisibleContentPosition_, filter);
2359 }
2360 
FromJson(const std::unique_ptr<JsonValue> & json)2361 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
2362 {
2363     ScrollToIndex(json->GetInt("startIndex"));
2364     if (json->Contains("itemStartPos")) {
2365         ScrollBy(-json->GetDouble("itemStartPos"));
2366     }
2367     auto host = GetHost();
2368     CHECK_NULL_VOID(host);
2369     host->GetRenderContext()->UpdateClipEdge(true);
2370     ScrollablePattern::FromJson(json);
2371 }
2372 
GetListItemGroupParameter(const RefPtr<FrameNode> & node)2373 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
2374 {
2375     ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
2376     auto curFrameParent = node->GetParent();
2377     auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2378     while (curFrameParent && (!curFrameParentNode)) {
2379         curFrameParent = curFrameParent->GetParent();
2380         curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2381     }
2382     CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
2383     if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2384         auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
2385         CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
2386         listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
2387         listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2388         listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2389         listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2390     }
2391     return listItemGroupPara;
2392 }
2393 
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2394 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2395 {
2396     auto listFrame = GetHost();
2397     CHECK_NULL_RETURN(listFrame, false);
2398     auto listFocus = listFrame->GetFocusHub();
2399     CHECK_NULL_RETURN(listFocus, false);
2400     bool isItemGroup = false;
2401     listFocus->AnyChildFocusHub([&isItemGroup, &node, listIndex](const RefPtr<FocusHub>& childFocus) {
2402         if (!childFocus->IsFocusable()) {
2403             return false;
2404         }
2405         if (auto childFrame = childFocus->GetFrameNode()) {
2406             if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2407                 auto curIndex = childPattern->GetIndexInList();
2408                 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2409                 if (curIndex == listIndex) {
2410                     node = childFrame;
2411                     isItemGroup = curIndexInGroup > -1;
2412                     return true;
2413                 }
2414             }
2415         }
2416         return false;
2417     });
2418     return isItemGroup;
2419 }
2420 
RefreshLanesItemRange()2421 void ListPattern::RefreshLanesItemRange()
2422 {
2423     auto host = GetHost();
2424     CHECK_NULL_VOID(host);
2425     auto updatePos = host->GetChildrenUpdated();
2426     if (updatePos == -1) {
2427         return;
2428     }
2429     host->ChildrenUpdatedFrom(-1);
2430     if (updatePos == 0) {
2431         lanesItemRange_.clear();
2432         return;
2433     }
2434     for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
2435         if (it->second < updatePos) {
2436             it++;
2437         } else if (it->first >= updatePos) {
2438             it = lanesItemRange_.erase(it);
2439         } else {
2440             it->second = updatePos - 1;
2441             it++;
2442         }
2443     }
2444 }
2445 
ProvideRestoreInfo()2446 std::string ListPattern::ProvideRestoreInfo()
2447 {
2448     return std::to_string(startIndex_);
2449 }
2450 
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)2451 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
2452 {
2453     auto item = swiperItem_.Upgrade();
2454     if (item) {
2455         return item->CloseSwipeAction(std::move(onFinishCallback));
2456     }
2457 }
2458 
OnRestoreInfo(const std::string & restoreInfo)2459 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
2460 {
2461     jumpIndex_ = StringUtils::StringToInt(restoreInfo);
2462 }
2463 
DumpAdvanceInfo()2464 void ListPattern::DumpAdvanceInfo()
2465 {
2466     ScrollablePattern::DumpAdvanceInfo();
2467     DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
2468     DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
2469     DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
2470     DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
2471     DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
2472     DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
2473     DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
2474     DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
2475     DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
2476     DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
2477     DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
2478     crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
2479                      : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
2480     smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
2481     if (jumpIndex_.has_value()) {
2482         DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
2483     } else {
2484         DumpLog::GetInstance().AddDesc("jumpIndex:null");
2485     }
2486     if (jumpIndexInGroup_.has_value()) {
2487         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
2488     } else {
2489         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
2490     }
2491     if (targetIndex_.has_value()) {
2492         DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
2493     } else {
2494         DumpLog::GetInstance().AddDesc("targetIndex:null");
2495     }
2496     if (predictSnapOffset_.has_value()) {
2497         DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
2498     } else {
2499         DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
2500     }
2501     if (predictSnapEndPos_.has_value()) {
2502         DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
2503     } else {
2504         DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
2505     }
2506     paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
2507                     : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
2508     isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
2509                             : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
2510     for (auto item : itemPosition_) {
2511         DumpLog::GetInstance().AddDesc("------------------------------------------");
2512         DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
2513         DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
2514         DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
2515         DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
2516     }
2517     DumpLog::GetInstance().AddDesc("------------------------------------------");
2518     scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
2519                 : DumpLog::GetInstance().AddDesc("scrollStop:false");
2520     for (auto item : lanesItemRange_) {
2521         DumpLog::GetInstance().AddDesc("------------------------------------------");
2522         DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
2523         DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
2524     }
2525     DumpLog::GetInstance().AddDesc("------------------------------------------");
2526     DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
2527     DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
2528     dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
2529                     : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
2530     isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
2531                  : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
2532     IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
2533     IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
2534                  : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
2535 }
2536 
GetDefaultScrollBarDisplayMode() const2537 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
2538 {
2539     auto defaultDisplayMode = DisplayMode::OFF;
2540     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
2541         defaultDisplayMode = DisplayMode::AUTO;
2542     }
2543     return defaultDisplayMode;
2544 }
2545 
GetVisibleSelectedItems()2546 std::vector<RefPtr<FrameNode>> ListPattern::GetVisibleSelectedItems()
2547 {
2548     std::vector<RefPtr<FrameNode>> children;
2549     auto host = GetHost();
2550     CHECK_NULL_RETURN(host, children);
2551     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2552         auto item = host->GetChildByIndex(index);
2553         if (!AceType::InstanceOf<FrameNode>(item)) {
2554             continue;
2555         }
2556         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
2557         auto itemPattern = itemFrameNode->GetPattern<ListItemPattern>();
2558         if (!itemPattern) {
2559             continue;
2560         }
2561         if (!itemPattern->IsSelected()) {
2562             continue;
2563         }
2564         children.emplace_back(itemFrameNode);
2565     }
2566     return children;
2567 }
2568 
GetOrCreateListChildrenMainSize()2569 RefPtr<ListChildrenMainSize> ListPattern::GetOrCreateListChildrenMainSize()
2570 {
2571     if (childrenSize_) {
2572         return childrenSize_;
2573     }
2574     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
2575     auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
2576         auto pattern = weakPattern.Upgrade();
2577         CHECK_NULL_VOID(pattern);
2578         auto context = PipelineContext::GetCurrentContext();
2579         CHECK_NULL_VOID(context);
2580         context->AddBuildFinishCallBack([weakPattern, change, flag]() {
2581             auto pattern = weakPattern.Upgrade();
2582             CHECK_NULL_VOID(pattern);
2583             pattern->OnChildrenSizeChanged(change, flag);
2584         });
2585         context->RequestFrame();
2586     };
2587     childrenSize_->SetOnDataChange(callback);
2588     return childrenSize_;
2589 }
2590 
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)2591 void ListPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
2592 {
2593     if (!posMap_) {
2594         posMap_ = MakeRefPtr<ListPositionMap>();
2595     }
2596     posMap_->MarkDirty(flag);
2597     MarkDirtyNodeSelf();
2598 }
2599 
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)2600 void ListPattern::SetListChildrenMainSize(float defaultSize, const std::vector<float>& mainSize)
2601 {
2602     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
2603     OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2604 }
2605 
ResetChildrenSize()2606 void ListPattern::ResetChildrenSize()
2607 {
2608     if (childrenSize_) {
2609         childrenSize_ = nullptr;
2610         MarkDirtyNodeSelf();
2611         OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2612     }
2613 }
2614 
NotifyDataChange(int32_t index,int32_t count)2615 void ListPattern::NotifyDataChange(int32_t index, int32_t count)
2616 {
2617     if (!maintainVisibleContentPosition_ || itemPosition_.empty()) {
2618         return;
2619     }
2620     auto startIndex = itemPosition_.begin()->first;
2621     if (count == 0 || (count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) {
2622         return;
2623     }
2624     count = std::max(count, index - startIndex);
2625     int32_t mod = 0;
2626     if (count < 0 && lanes_ > 1 && !(itemPosition_.begin()->second.isGroup)) {
2627         mod = -count % lanes_;
2628     }
2629     auto prevPosMap = std::move(itemPosition_);
2630     for (auto &pos : prevPosMap) {
2631         if (mod > 0) {
2632             mod--;
2633         } else {
2634             itemPosition_[pos.first + count] = pos.second;
2635         }
2636     }
2637     needReEstimateOffset_ = true;
2638 }
2639 
GetChildrenExpandedSize()2640 SizeF ListPattern::GetChildrenExpandedSize()
2641 {
2642     auto viewSize = GetViewSizeMinusPadding();
2643     auto axis = GetAxis();
2644     float estimatedHeight = 0.0f;
2645     if (childrenSize_) {
2646         estimatedHeight = listTotalHeight_;
2647     } else if (!itemPosition_.empty()) {
2648         auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, axis);
2649         calculate.GetEstimateHeightAndOffset(GetHost());
2650         estimatedHeight = calculate.GetEstimateHeight();
2651     }
2652 
2653     if (axis == Axis::VERTICAL) {
2654         return SizeF(viewSize.Width(), estimatedHeight);
2655     } else if (axis == Axis::HORIZONTAL) {
2656         return SizeF(estimatedHeight, viewSize.Height());
2657     }
2658     return SizeF();
2659 }
2660 } // namespace OHOS::Ace::NG
2661