• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 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/utils/system_properties.h"
21 #include "base/memory/referenced.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components/list/list_theme.h"
24 #include "core/components/scroll/scroll_bar_theme.h"
25 #include "core/components_ng/base/inspector_filter.h"
26 #include "core/components_ng/pattern/list/list_height_offset_calculator.h"
27 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
28 #include "core/components_ng/pattern/list/list_item_pattern.h"
29 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
30 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_layout_property.h"
32 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
33 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
34 #include "core/components_ng/pattern/scrollable/scrollable.h"
35 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
36 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
37 #include "core/components_ng/property/measure_utils.h"
38 #include "core/components_v2/inspector/inspector_constants.h"
39 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
40 #include "core/components_ng/manager/scroll_adjust/scroll_adjust_manager.h"
41 
42 namespace OHOS::Ace::NG {
43 namespace {
44 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
45 constexpr double CHAIN_SPRING_MASS = 1.0;
46 constexpr double CHAIN_SPRING_DAMPING = 30.0;
47 constexpr double CHAIN_SPRING_STIFFNESS = 228;
48 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
49 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
50 constexpr int DEFAULT_HEADER_VALUE = 2;
51 constexpr int DEFAULT_FOOTER_VALUE = 3;
52 #ifdef SUPPORT_DIGITAL_CROWN
53 constexpr const char* HAPTIC_STRENGTH1 = "watchhaptic.feedback.crown.strength3";
54 #endif
55 } // namespace
56 
OnModifyDone()57 void ListPattern::OnModifyDone()
58 {
59     Pattern::OnModifyDone();
60     auto host = GetHost();
61     CHECK_NULL_VOID(host);
62     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
63     CHECK_NULL_VOID(listLayoutProperty);
64     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
65     if (axis != GetAxis()) {
66         needReEstimateOffset_ = true;
67         SetAxis(axis);
68         ChangeAxis(GetHost());
69     }
70     if (!GetScrollableEvent()) {
71         AddScrollEvent();
72         auto scrollableEvent = GetScrollableEvent();
73         CHECK_NULL_VOID(scrollableEvent);
74         scrollable_ = scrollableEvent->GetScrollable();
75 #ifdef SUPPORT_DIGITAL_CROWN
76         SetDigitalCrownEvent();
77 #endif
78     }
79 
80     SetEdgeEffect();
81 
82     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
83     CHECK_NULL_VOID(paintProperty);
84     if (paintProperty->GetScrollBarProperty()) {
85         SetScrollBar(paintProperty->GetScrollBarProperty());
86     }
87 
88     SetChainAnimation();
89     if (multiSelectable_ && !isMouseEventInit_) {
90         InitMouseEvent();
91     }
92     if (!multiSelectable_ && isMouseEventInit_) {
93         UninitMouseEvent();
94     }
95     auto focusHub = host->GetFocusHub();
96     CHECK_NULL_VOID(focusHub);
97     focusHub->SetFocusDependence(FocusDependence::AUTO);
98     InitOnKeyEvent(focusHub);
99     Register2DragDropManager();
100     SetAccessibilityAction();
101     auto fadingEdge = GetFadingEdge(paintProperty);
102     auto overlayNode = host->GetOverlayNode();
103     if (!overlayNode && fadingEdge) {
104         CreateAnalyzerOverlay(host);
105     }
106 }
107 
GetFadingEdge(RefPtr<ScrollablePaintProperty> & paintProperty)108 bool ListPattern::GetFadingEdge(RefPtr<ScrollablePaintProperty>& paintProperty)
109 {
110     auto defaultFadingEdge = false;
111     if (!paintProperty->HasDefaultFadingEdge()) {
112         auto host = GetHost();
113         CHECK_NULL_RETURN(host, false);
114         auto context = host->GetContextRefPtr();
115         CHECK_NULL_RETURN(context, false);
116         auto listTheme = context->GetTheme<ListTheme>();
117         CHECK_NULL_RETURN(listTheme, false);
118         defaultFadingEdge = GetAxis() == Axis::VERTICAL ? listTheme->GetFadingEdge()
119                                                         : false;
120         paintProperty->UpdateDefaultFadingEdge(defaultFadingEdge);
121     } else {
122         defaultFadingEdge = paintProperty->GetDefaultFadingEdge().value_or(false);
123     }
124     auto fadingEdge = paintProperty->GetFadingEdge().value_or(defaultFadingEdge);
125     return fadingEdge;
126 }
127 
ChangeAxis(RefPtr<UINode> node)128 void ListPattern::ChangeAxis(RefPtr<UINode> node)
129 {
130     CHECK_NULL_VOID(node);
131     auto children = node->GetChildren();
132     for (const auto& child : children) {
133         if (AceType::InstanceOf<FrameNode>(child)) {
134             auto frameNode = AceType::DynamicCast<FrameNode>(child);
135             CHECK_NULL_VOID(frameNode);
136             auto listItemPattern = frameNode->GetPattern<ListItemPattern>();
137             if (listItemPattern) {
138                 listItemPattern->ChangeAxis(GetAxis());
139                 continue;
140             }
141             auto listItemGroupPattern = frameNode->GetPattern<ListItemGroupPattern>();
142             if (listItemGroupPattern) {
143                 listItemGroupPattern->ResetLayoutedInfo();
144                 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
145                 ChangeAxis(child);
146             }
147         } else {
148             ChangeAxis(child);
149         }
150     }
151 }
152 
HandleTargetIndex(bool isJump)153 bool ListPattern::HandleTargetIndex(bool isJump)
154 {
155     if (isJump) {
156         MarkDirtyNodeSelf();
157         return true;
158     }
159     auto iter = itemPosition_.find(targetIndex_.value());
160     if (iter == itemPosition_.end()) {
161         ResetExtraOffset();
162     } else if (!isLayoutListForFocus_) {
163         // isLayoutListForFocus_ is true means only do Layout and do NOT call AnimateToTarget.
164         AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_);
165     }
166 
167     // AniamteToTarget does not need to update endIndex and startIndex in the first frame.
168     targetIndex_.reset();
169     targetIndexInGroup_.reset();
170     isLayoutListForFocus_.reset();
171     return false;
172 }
173 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)174 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
175 {
176     if (config.skipMeasure && config.skipLayout) {
177         return false;
178     }
179     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
180     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
181     auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
182     CHECK_NULL_RETURN(listLayoutAlgorithm, false);
183     prevMeasureBreak_ = listLayoutAlgorithm->MeasureInNextFrame();
184     if (!prevMeasureBreak_) {
185         jumpIndexInGroup_.reset();
186         jumpIndex_.reset();
187     }
188     itemPosition_ = listLayoutAlgorithm->GetItemPosition();
189     cachedItemPosition_ = listLayoutAlgorithm->GetCachedItemPosition();
190     laneIdx4Divider_ = listLayoutAlgorithm->GetLaneIdx4Divider();
191     maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
192     spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
193     auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
194     auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition();
195     bool isJump = listLayoutAlgorithm->NeedEstimateOffset();
196     auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
197     isStackFromEnd_ = listLayoutAlgorithm->GetStackFromEnd();
198     if (lanesLayoutAlgorithm) {
199         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
200         if (lanesLayoutAlgorithm->GetLanes() != lanes_) {
201             needReEstimateOffset_ = true;
202             auto item = swiperItem_.Upgrade();
203             if (item) {
204                 item->ResetSwipeStatus();
205             }
206         }
207         lanes_ = lanesLayoutAlgorithm->GetLanes();
208         laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
209     } else {
210         lanes_ = listLayoutAlgorithm->GetLanes();
211     }
212     float relativeOffset = UpdateTotalOffset(listLayoutAlgorithm, isJump);
213     bool isNeedUpdateIndex = targetIndex_ ? HandleTargetIndex(isJump) : true;
214     if (predictSnapOffset.has_value()) {
215         if (scrollable_ && !(NearZero(predictSnapOffset.value()) && NearZero(scrollSnapVelocity_)) &&
216             (!AnimateRunning() || lastSnapTargetIndex_.has_value())) {
217             StartListSnapAnimation(predictSnapOffset.value(), scrollSnapVelocity_);
218             if (snapTrigOnScrollStart_) {
219                 FireOnScrollStart();
220             } else {
221                 scrollStop_ = false;
222             }
223         } else if (!snapTrigOnScrollStart_) {
224             OnAnimateStop();
225         }
226         scrollSnapVelocity_ = 0.0f;
227         predictSnapOffset_.reset();
228         ResetLastSnapTargetIndex();
229         snapTrigOnScrollStart_ = false;
230     }
231     if (predictSnapEndPos.has_value() && predictSnapEndPos_.has_value() &&
232         !NearEqual(predictSnapEndPos.value(), predictSnapEndPos_.value())) {
233         if (scrollable_) {
234             scrollable_->UpdateScrollSnapEndWithOffset(predictSnapEndPos.value() - predictSnapEndPos_.value());
235         }
236     }
237     predictSnapEndPos_ = predictSnapEndPos;
238 
239     if (isScrollEnd_) {
240         auto host = GetHost();
241         CHECK_NULL_RETURN(host, false);
242         host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
243         // AccessibilityEventType::SCROLL_END
244         isScrollEnd_ = false;
245     }
246     currentDelta_ = 0.0f;
247     isNeedCheckOffset_ = false;
248     prevStartOffset_ = startMainPos_;
249     prevEndOffset_ = endMainPos_ - contentMainSize_ + contentEndOffset_;
250     contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
251     contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
252     contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
253     startMainPos_ = listLayoutAlgorithm->GetStartPosition();
254     endMainPos_ = listLayoutAlgorithm->GetEndPosition();
255     crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
256     if (!prevMeasureBreak_) {
257         auto endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
258         CheckScrollable();
259         if (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty))) {
260             OnMidIndexChanged();
261         }
262         bool indexChanged = false;
263         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
264             if (isNeedUpdateIndex) {
265                 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
266                             (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
267                             (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
268             }
269         } else {
270             indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
271                 (endIndex_ != listLayoutAlgorithm->GetEndIndex());
272         }
273         startIndexChanged_ = startIndex_ != listLayoutAlgorithm->GetStartIndex();
274         endIndexChanged_ = endIndex_ != listLayoutAlgorithm->GetEndIndex();
275         if (indexChanged) {
276             startIndex_ = listLayoutAlgorithm->GetStartIndex();
277             endIndex_ = listLayoutAlgorithm->GetEndIndex();
278             centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
279         }
280         ProcessEvent(indexChanged, relativeOffset, isJump);
281         HandleScrollBarOutBoundary();
282         UpdateScrollBarOffset();
283         if (config.frameSizeChange) {
284             if (GetScrollBar() != nullptr) {
285                 GetScrollBar()->ScheduleDisappearDelayTask();
286             }
287         }
288         bool sizeDiminished =
289             !chainAnimation_ && IsOutOfBoundary(false) && (endOffset + relativeOffset - prevEndOffset_ < -0.1f);
290         if (!GetCanStayOverScroll()) {
291             CheckRestartSpring(sizeDiminished);
292         }
293         isInitialized_ = true;
294     } else {
295         ACE_SCOPED_TRACE("List MeasureInNextFrame");
296         auto host = GetHost();
297         CHECK_NULL_RETURN(host, false);
298         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
299     }
300     DrivenRender(dirty);
301 
302     ChangeAnimateOverScroll();
303     SetScrollSource(SCROLL_FROM_NONE);
304     MarkSelectedItems();
305     UpdateListDirectionInCardStyle();
306     snapTrigByScrollBar_ = false;
307     ChangeCanStayOverScroll();
308     return true;
309 }
310 
UpdateListDirectionInCardStyle()311 void ListPattern::UpdateListDirectionInCardStyle()
312 {
313     if (isNeedToUpdateListDirection_) {
314         auto layoutProperty = GetLayoutProperty<ListLayoutProperty>();
315         layoutProperty->UpdateListDirection(Axis::VERTICAL);
316         isNeedToUpdateListDirection_ = false;
317     }
318 }
319 
GetInitialScrollAlign() const320 ScrollAlign ListPattern::GetInitialScrollAlign() const
321 {
322     auto snapAlign = GetScrollSnapAlign();
323     if (snapAlign == ScrollSnapAlign::CENTER) {
324         return ScrollAlign::CENTER;
325     } else if (isStackFromEnd_ && snapAlign == ScrollSnapAlign::START) {
326         return ScrollAlign::END;
327     }
328     return ScrollAlign::START;
329 }
330 
CalculateTargetPos(float startPos,float endPos)331 float ListPattern::CalculateTargetPos(float startPos, float endPos)
332 {
333     float topOffset = startPos;
334     float bottomOffset = endPos - contentMainSize_;
335     if (!IsScrollSnapAlignCenter()) {
336         topOffset -= contentStartOffset_;
337         bottomOffset += contentEndOffset_;
338     }
339     if (GreatOrEqual(topOffset, 0.0f) && LessOrEqual(bottomOffset, 0.0f)) {
340         return 0.0f;
341     }
342     if ((NearEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) ||
343         (LessNotEqual(topOffset, 0.0f) && NearEqual(bottomOffset, 0.0f))) {
344         return 0.0f;
345     }
346     if (LessNotEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) {
347         if (GreatOrEqual(std::abs(topOffset), std::abs(bottomOffset))) {
348             return bottomOffset;
349         } else {
350             return topOffset;
351         }
352     }
353     if (GreatNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
354         return bottomOffset;
355     } else if (LessNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
356         return topOffset;
357     } else {
358         if (LessNotEqual(topOffset, 0.0f)) {
359             return topOffset;
360         } else {
361             return bottomOffset;
362         }
363     }
364     return 0.0f;
365 }
366 
CreateNodePaintMethod()367 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
368 {
369     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
370     V2::ItemDivider divider;
371     if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
372         divider = listLayoutProperty->GetDivider().value();
373     }
374     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
375     auto layoutDirection = listLayoutProperty->GetNonAutoLayoutDirection();
376     auto drawVertical = (axis == Axis::HORIZONTAL);
377     auto drawDirection = (layoutDirection == TextDirection::RTL);
378     auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, drawDirection, lanes_, spaceWidth_);
379     if (drawDirection) {
380         paint->SetDirection(true);
381     }
382     paint->SetScrollBar(GetScrollBar());
383     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
384     paint->SetTotalItemCount(maxListItemIndex_ + 1);
385     auto scrollEffect = GetScrollEdgeEffect();
386     if (scrollEffect && scrollEffect->IsFadeEffect()) {
387         paint->SetEdgeEffect(scrollEffect);
388     }
389     auto host = GetHost();
390     CHECK_NULL_RETURN(host, paint);
391     const auto& geometryNode = host->GetGeometryNode();
392     auto renderContext = host->GetRenderContext();
393     auto frameRect = renderContext->GetPaintRectWithoutTransform();
394     if (!listContentModifier_) {
395         CHECK_NULL_RETURN(renderContext, paint);
396         auto size = frameRect.GetSize();
397         auto& padding = geometryNode->GetPadding();
398         if (padding) {
399             size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
400         }
401         OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
402         listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
403     }
404     listContentModifier_->SetIsNeedDividerAnimation(isNeedDividerAnimation_);
405     paint->SetLaneGutter(laneGutter_);
406     bool showCached = listLayoutProperty->GetShowCachedItemsValue(false);
407     bool clip = !renderContext || renderContext->GetClipEdge().value_or(true);
408     paint->SetItemsPosition(itemPosition_, cachedItemPosition_, pressedItem_, showCached, clip);
409     paint->SetLaneIdx(laneIdx4Divider_);
410     paint->SetContentModifier(listContentModifier_);
411     paint->SetAdjustOffset(geometryNode->GetParentAdjust().GetOffset().GetY());
412     paint->UpdateBoundsRect(frameRect, clip);
413     UpdateFadingEdge(paint);
414     return paint;
415 }
416 
UpdateStartListItemIndex()417 bool ListPattern::UpdateStartListItemIndex()
418 {
419     auto host = GetHost();
420     CHECK_NULL_RETURN(host, false);
421     CHECK_EQUAL_RETURN(host->GetChildTrueTotalCount(), 0, false);
422     auto startWrapper = host->GetOrCreateChildByIndex(startIndex_);
423     int32_t startArea = -1;
424     int32_t startItemIndexInGroup = -1;
425     bool startFlagChanged = (startInfo_.index != startIndex_);
426     bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
427     if (startIsGroup) {
428         auto startPattern = startWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
429         VisibleContentInfo startGroupInfo = startPattern->GetStartListItemIndex();
430         startFlagChanged = startFlagChanged || (startInfo_.area != startGroupInfo.area) ||
431                            (startInfo_.indexInGroup != startGroupInfo.indexInGroup);
432         startArea = startGroupInfo.area;
433         startItemIndexInGroup = startGroupInfo.indexInGroup;
434         if (startFlagChanged) {
435             VisibleContentInfo endGroupInfo = startPattern->GetEndListItemIndex();
436             int32_t endItemIndexInGroup = endGroupInfo.indexInGroup;
437             startWrapper->GetHostNode()->OnAccessibilityEvent(
438                 AccessibilityEventType::SCROLLING_EVENT, startItemIndexInGroup, endItemIndexInGroup);
439         }
440     }
441     startInfo_ = { startIndex_, startArea, startItemIndexInGroup };
442     return startFlagChanged;
443 }
444 
UpdateEndListItemIndex()445 bool ListPattern::UpdateEndListItemIndex()
446 {
447     auto host = GetHost();
448     CHECK_NULL_RETURN(host, false);
449     CHECK_EQUAL_RETURN(host->GetChildTrueTotalCount(), 0, false);
450     auto endWrapper = host->GetOrCreateChildByIndex(endIndex_);
451     int32_t endArea = -1;
452     int32_t endItemIndexInGroup = -1;
453     bool endFlagChanged = (endInfo_.index != endIndex_);
454     bool endIsGroup = endWrapper && endWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
455     if (endIsGroup) {
456         auto endPattern = endWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
457         VisibleContentInfo endGroupInfo = endPattern->GetEndListItemIndex();
458         endFlagChanged = endFlagChanged || (endInfo_.area != endGroupInfo.area) ||
459                          (endInfo_.indexInGroup != endGroupInfo.indexInGroup);
460         endArea = endGroupInfo.area;
461         endItemIndexInGroup = endGroupInfo.indexInGroup;
462         if (endFlagChanged) {
463             VisibleContentInfo startGroupInfo = endPattern->GetStartListItemIndex();
464             int32_t startItemIndexInGroup = startGroupInfo.indexInGroup;
465             endWrapper->GetHostNode()->OnAccessibilityEvent(
466                 AccessibilityEventType::SCROLLING_EVENT, startItemIndexInGroup, endItemIndexInGroup);
467         }
468     }
469     endInfo_ = { endIndex_, endArea, endItemIndexInGroup };
470     return endFlagChanged;
471 }
472 
ProcessEvent(bool indexChanged,float finalOffset,bool isJump)473 void ListPattern::ProcessEvent(bool indexChanged, float finalOffset, bool isJump)
474 {
475     auto host = GetHost();
476     CHECK_NULL_VOID(host);
477     auto listEventHub = host->GetOrCreateEventHub<ListEventHub>();
478     CHECK_NULL_VOID(listEventHub);
479     paintStateFlag_ = !NearZero(finalOffset) && !isJump;
480     isFramePaintStateValid_ = true;
481     auto onScroll = listEventHub->GetOnScroll();
482     PrintOffsetLog(AceLogTag::ACE_LIST, host->GetId(), finalOffset);
483     if (onScroll) {
484         FireOnScrollWithVersionCheck(finalOffset, onScroll);
485     }
486     FireObserverOnDidScroll(finalOffset);
487     FireObserverOnScrollerAreaChange(finalOffset);
488     auto onDidScroll = listEventHub->GetOnDidScroll();
489     if (onDidScroll) {
490         FireOnScroll(finalOffset, onDidScroll);
491     }
492     auto onJSFrameNodeDidScroll = listEventHub->GetJSFrameNodeOnDidScroll();
493     if (onJSFrameNodeDidScroll) {
494         FireOnScroll(finalOffset, onJSFrameNodeDidScroll);
495     }
496     auto onScrollIndex = listEventHub->GetOnScrollIndex();
497     auto onJSFrameNodeScrollIndex = listEventHub->GetJSFrameNodeOnListScrollIndex();
498     FireOnScrollIndex(indexChanged, onScrollIndex);
499     FireOnScrollIndex(indexChanged, onJSFrameNodeScrollIndex);
500     if (startIndexChanged_ || endIndexChanged_) {
501         host->OnAccessibilityEvent(AccessibilityEventType::SCROLLING_EVENT, startIndex_, endIndex_);
502     }
503     OnScrollVisibleContentChange(listEventHub, indexChanged);
504     auto onReachStart = listEventHub->GetOnReachStart();
505     auto onJSFrameNodeReachStart = listEventHub->GetJSFrameNodeOnReachStart();
506     FireOnReachStart(onReachStart, onJSFrameNodeReachStart);
507     auto onReachEnd = listEventHub->GetOnReachEnd();
508     auto onJSFrameNodeReachEnd = listEventHub->GetJSFrameNodeOnReachEnd();
509     FireOnReachEnd(onReachEnd, onJSFrameNodeReachEnd);
510     OnScrollStop(listEventHub->GetOnScrollStop(), listEventHub->GetJSFrameNodeOnScrollStop());
511     ProcessFocusEvent(indexChanged);
512 }
513 
FireOnScrollWithVersionCheck(float finalOffset,OnScrollEvent & onScroll)514 void ListPattern::FireOnScrollWithVersionCheck(float finalOffset, OnScrollEvent& onScroll)
515 {
516     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
517         FireOnScroll(finalOffset, onScroll);
518     } else {
519         if (!NearZero(finalOffset)) {
520             auto offsetPX = Dimension(finalOffset);
521             auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
522             auto source = GetScrollSource();
523             if (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR || source == SCROLL_FROM_ANIMATION_CONTROLLER) {
524                 source = SCROLL_FROM_NONE;
525             }
526             onScroll(offsetVP, GetScrollState(source));
527         }
528     }
529 }
530 
FireOnReachStart(const OnReachEvent & onReachStart,const OnReachEvent & onJSFrameNodeReachStart)531 void ListPattern::FireOnReachStart(const OnReachEvent& onReachStart, const OnReachEvent& onJSFrameNodeReachStart)
532 {
533     auto host = GetHost();
534     CHECK_NULL_VOID(host);
535     if (startIndex_ == 0) {
536         bool scrollUpToStart =
537             GreatNotEqual(prevStartOffset_, contentStartOffset_) && LessOrEqual(startMainPos_, contentStartOffset_);
538         bool scrollDownToStart =
539             (startIndexChanged_ || LessNotEqual(prevStartOffset_, contentStartOffset_) || !isInitialized_) &&
540             GreatOrEqual(startMainPos_, contentStartOffset_);
541         if (scrollUpToStart || scrollDownToStart) {
542             FireObserverOnReachStart();
543             ReportOnItemListEvent("onReachStart");
544             CHECK_NULL_VOID(onReachStart || onJSFrameNodeReachStart);
545             ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:List",
546                 scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
547             if (onReachStart) {
548                 onReachStart();
549             }
550             if (onJSFrameNodeReachStart) {
551                 onJSFrameNodeReachStart();
552             }
553             AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
554         }
555     }
556 }
557 
FireOnReachEnd(const OnReachEvent & onReachEnd,const OnReachEvent & onJSFrameNodeReachEnd)558 void ListPattern::FireOnReachEnd(const OnReachEvent& onReachEnd, const OnReachEvent& onJSFrameNodeReachEnd)
559 {
560     auto host = GetHost();
561     CHECK_NULL_VOID(host);
562     if (endIndex_ == GetMaxIndexByRepeat()) {
563         float endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
564         // deltaOffset passes through multiple items also needs to fire reachEnd
565         bool scrollUpToEnd =
566             (endIndexChanged_ || (Positive(prevEndOffset_) || !isInitialized_)) && NonPositive(endOffset);
567         bool scrollDownToEnd = Negative(prevEndOffset_) && NonNegative(endOffset);
568         auto scrollSource = GetScrollSource();
569         if (scrollUpToEnd || (scrollDownToEnd && scrollSource != SCROLL_FROM_NONE)) {
570             FireObserverOnReachEnd();
571             ReportOnItemListEvent("onReachEnd");
572             CHECK_NULL_VOID(onReachEnd || onJSFrameNodeReachEnd);
573             ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, scrollSource:%d, id:%d, tag:List",
574                 scrollUpToEnd, scrollDownToEnd, scrollSource, static_cast<int32_t>(host->GetAccessibilityId()));
575             if (onReachEnd) {
576                 onReachEnd();
577             }
578             if (onJSFrameNodeReachEnd) {
579                 onJSFrameNodeReachEnd();
580             }
581             AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
582         }
583     }
584 }
585 
FireOnScrollIndex(bool indexChanged,const OnScrollIndexEvent & onScrollIndex)586 void ListPattern::FireOnScrollIndex(bool indexChanged, const OnScrollIndexEvent& onScrollIndex)
587 {
588     CHECK_NULL_VOID(indexChanged && onScrollIndex);
589     int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
590     int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
591     if (SystemProperties::IsWhiteBlockEnabled()) {
592         endIndex = ScrollAdjustmanager::GetInstance().AdjustEndIndex(endIndex);
593     }
594     onScrollIndex(startIndex, endIndex, centerIndex_);
595     ReportOnItemListScrollEvent("onScrollIndex", startIndex, endIndex);
596 }
597 
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)598 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
599 {
600     auto host = GetHost();
601     CHECK_NULL_VOID(host);
602     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
603     auto listPaintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
604     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
605     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
606     bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
607     auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
608     bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE || barNeedPaint ||
609                           chainAnimation || !isScrollable_);
610 
611     auto renderContext = host->GetRenderContext();
612     CHECK_NULL_VOID(renderContext);
613     renderContext->MarkDrivenRender(drivenRender);
614     if (drivenRender && isFramePaintStateValid_) {
615         // Mark items
616         int32_t indexStep = 0;
617         int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
618         for (auto& pos : itemPosition_) {
619             auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first + itemStartIndex_);
620             CHECK_NULL_VOID(wrapper);
621             auto itemHost = wrapper->GetHostNode();
622             CHECK_NULL_VOID(itemHost);
623             auto itemRenderContext = itemHost->GetRenderContext();
624             CHECK_NULL_VOID(itemRenderContext);
625             itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
626             indexStep++;
627         }
628         renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
629         isFramePaintStateValid_ = false;
630     }
631 }
632 
CheckScrollable()633 void ListPattern::CheckScrollable()
634 {
635     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
636     CHECK_NULL_VOID(listProperty);
637     auto lastScrollable = isScrollable_;
638     if (itemPosition_.empty()) {
639         isScrollable_ = false;
640     } else {
641         if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
642             !IsScrollSnapAlignCenter()) {
643             isScrollable_ = GetAlwaysEnabled() || GreatNotEqual(endMainPos_ - startMainPos_,
644                 contentMainSize_ - contentStartOffset_ - contentEndOffset_);
645         } else {
646             isScrollable_ = true;
647         }
648     }
649     if (chainAnimation_ && lastScrollable && !isScrollable_) {
650         ACE_SCOPED_TRACE("Scrollable List changes to un-scrollable, reset chainAnimation");
651         chainAnimation_.Reset();
652     }
653     if (!chainAnimation_ && !lastScrollable && isScrollable_) {
654         SetChainAnimation();
655     }
656     SetScrollEnabled(isScrollable_);
657 
658     if (!listProperty->GetScrollEnabled().value_or(isScrollable_)) {
659         SetScrollEnabled(false);
660     }
661 }
662 
CreateLayoutAlgorithm()663 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
664 {
665     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
666     CHECK_NULL_RETURN(listLayoutProperty, nullptr);
667     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
668     if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
669         listLayoutProperty->HasLaneMaxLength()) {
670         auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
671         RefreshLanesItemRange();
672         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
673         lanesLayoutAlgorithm->SetLanes(lanes_);
674         listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
675     } else {
676         listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
677     }
678     if (!posMap_) {
679         posMap_ = MakeRefPtr<ListPositionMap>();
680     }
681 
682     SetLayoutAlgorithmParams(listLayoutAlgorithm, listLayoutProperty);
683 
684     auto pipeline = GetContext();
685     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
686         listLayoutAlgorithm->SetIsRoundingMode();
687     }
688     return listLayoutAlgorithm;
689 }
690 
SetLayoutAlgorithmParams(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)691 void ListPattern::SetLayoutAlgorithmParams(
692     const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
693 {
694     CHECK_NULL_VOID(listLayoutAlgorithm);
695     CHECK_NULL_VOID(listLayoutProperty);
696     if (childrenSize_) {
697         listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_);
698     }
699     listLayoutAlgorithm->SetListPositionMap(posMap_);
700     SetLayoutAlgorithmJumpAlign(listLayoutAlgorithm, listLayoutProperty);
701     if (targetIndex_) {
702         listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
703         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
704     }
705     if (jumpIndexInGroup_) {
706         listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
707     }
708     if (targetIndexInGroup_) {
709         listLayoutAlgorithm->SetTargetIndexInGroup(targetIndexInGroup_.value());
710     }
711     SetLayoutAlgorithmSnapParam(listLayoutAlgorithm);
712     listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
713     listLayoutAlgorithm->SetIsNeedCheckOffset(isNeedCheckOffset_);
714     listLayoutAlgorithm->SetItemsPosition(itemPosition_);
715     listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
716     listLayoutAlgorithm->SetPrevContentStartOffset(contentStartOffset_);
717     listLayoutAlgorithm->SetPrevContentEndOffset(contentEndOffset_);
718     if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
719         listLayoutAlgorithm->SetOverScrollFeature();
720     }
721     listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
722     listLayoutAlgorithm->SetCanOverScrollStart(JudgeCanOverScrollStart());
723     listLayoutAlgorithm->SetCanOverScrollEnd(JudgeCanOverScrollEnd());
724     if (chainAnimation_ && GetEffectEdge() == EffectEdge::ALL) {
725         SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
726         SetChainAnimationToPosMap();
727     }
728     listLayoutAlgorithm->SetPrevMeasureBreak(prevMeasureBreak_);
729     listLayoutAlgorithm->SetDraggingIndex(draggingIndex_);
730 }
731 
JudgeCanOverScrollStart()732 bool ListPattern::JudgeCanOverScrollStart()
733 {
734     auto source = GetScrollSource();
735     if (!CanOverScrollStart(source)) {
736         return false;
737     }
738     if (IsAtTop()) {
739         return true;
740     }
741 
742     return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING;
743 }
744 
JudgeCanOverScrollEnd()745 bool ListPattern::JudgeCanOverScrollEnd()
746 {
747     auto source = GetScrollSource();
748     if (!CanOverScrollEnd(source)) {
749         return false;
750     }
751     if (IsAtBottom(true)) {
752         return true;
753     }
754     return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING;
755 }
756 
SetChainAnimationToPosMap()757 void ListPattern::SetChainAnimationToPosMap()
758 {
759     CHECK_NULL_VOID(posMap_);
760     posMap_->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
761         auto list = weak.Upgrade();
762         CHECK_NULL_RETURN(list, 0.0f);
763         return list->GetChainDelta(index);
764     });
765 }
766 
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)767 void ListPattern::SetChainAnimationLayoutAlgorithm(
768     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
769 {
770     CHECK_NULL_VOID(listLayoutAlgorithm);
771     CHECK_NULL_VOID(listLayoutProperty);
772     listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
773         auto list = weak.Upgrade();
774         CHECK_NULL_RETURN(list, 0.0f);
775         return list->GetChainDelta(index);
776     });
777     if (chainAnimation_ && chainAnimation_->HasSpaceDelta() && !itemPosition_.empty()) {
778         auto res = GetOutBoundaryOffset(currentDelta_, false);
779         if ((NearZero(res.start) || !CanOverScrollStart(GetScrollSource())) &&
780             (NearZero(res.end) || !CanOverScrollEnd(GetScrollSource()))) {
781             chainAnimation_->ResetSpaceDelta();
782         }
783     }
784     if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
785         float chainInterval = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
786         auto pipeline = GetContext();
787         if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
788             chainInterval = Round(chainInterval);
789         }
790         listLayoutAlgorithm->SetChainInterval(chainInterval);
791     }
792 }
793 
SetLayoutAlgorithmJumpAlign(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)794 void ListPattern::SetLayoutAlgorithmJumpAlign(
795     const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
796 {
797     CHECK_NULL_VOID(listLayoutAlgorithm);
798     CHECK_NULL_VOID(listLayoutProperty);
799     bool needUseInitialIndex = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN) ?
800         !isInitialized_ && !jumpIndex_ : !isInitialized_;
801     if (needUseInitialIndex) {
802         isStackFromEnd_ = listLayoutProperty->GetStackFromEnd().value_or(false);
803         int32_t defaultInitialIndex = isStackFromEnd_ ? ListLayoutAlgorithm::LAST_ITEM : 0;
804         jumpIndex_ = listLayoutProperty->GetInitialIndex().value_or(defaultInitialIndex);
805         if (NeedScrollSnapAlignEffect()) {
806             scrollAlign_ = GetInitialScrollAlign();
807         } else if (isStackFromEnd_) {
808             scrollAlign_ = ScrollAlign::END;
809         }
810     }
811     if (jumpIndex_) {
812         listLayoutAlgorithm->SetIndex(jumpIndex_.value());
813         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
814     }
815 }
816 
SetLayoutAlgorithmSnapParam(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm)817 void ListPattern::SetLayoutAlgorithmSnapParam(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm)
818 {
819     CHECK_NULL_VOID(listLayoutAlgorithm);
820     if (predictSnapOffset_.has_value()) {
821         listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
822         listLayoutAlgorithm->SetScrollSnapVelocity(scrollSnapVelocity_);
823     }
824     listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
825     if (scrollable_) {
826         auto snapOffset = scrollable_->GetPredictSnapOffset();
827         if (snapOffset) {
828             predictSnapEndPos_ = GetTotalOffset() - snapOffset.value() + currentDelta_;
829         } else {
830             predictSnapEndPos_.reset();
831         }
832     }
833     if (predictSnapEndPos_.has_value()) {
834         listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value());
835     }
836 }
837 
IsScrollSnapAlignCenter() const838 bool ListPattern::IsScrollSnapAlignCenter() const
839 {
840     auto snapAlign = GetScrollSnapAlign();
841     return snapAlign == ScrollSnapAlign::CENTER;
842 }
843 
NeedScrollSnapAlignEffect() const844 bool ListPattern::NeedScrollSnapAlignEffect() const
845 {
846     auto snapAlign = GetScrollSnapAlign();
847     return snapAlign != ScrollSnapAlign::NONE;
848 }
849 
GetScrollSnapAlign() const850 ScrollSnapAlign ListPattern::GetScrollSnapAlign() const
851 {
852     auto host = GetHost();
853     CHECK_NULL_RETURN(host, ScrollSnapAlign::NONE);
854     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
855     CHECK_NULL_RETURN(listProperty, ScrollSnapAlign::NONE);
856     return listProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
857 }
858 
IsAtTop() const859 bool ListPattern::IsAtTop() const
860 {
861     bool groupAtStart = true;
862     bool groupAtEnd = true;
863     GetListItemGroupEdge(groupAtStart, groupAtEnd);
864     int32_t startIndex = startIndex_;
865     float startMainPos = startMainPos_;
866     float endMainPos = endMainPos_;
867     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
868     if (GreatNotEqual(contentMainSize, endMainPos - startMainPos) && isStackFromEnd_) {
869         startMainPos = endMainPos - contentMainSize;
870     }
871     return (startIndex == 0 && groupAtStart) &&
872            NonNegative(startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_);
873 }
874 
IsAtBottom(bool considerRepeat) const875 bool ListPattern::IsAtBottom(bool considerRepeat) const
876 {
877     bool groupAtStart = true;
878     bool groupAtEnd = true;
879     GetListItemGroupEdge(groupAtStart, groupAtEnd);
880     int32_t endIndex = endIndex_;
881     float endMainPos = endMainPos_;
882     float startMainPos = startMainPos_;
883     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
884     if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) && !isStackFromEnd_) {
885         endMainPos = startMainPos + contentMainSize;
886     }
887     auto maxListItemIndex = considerRepeat ? GetMaxIndexByRepeat() : maxListItemIndex_;
888     return (endIndex == maxListItemIndex && groupAtEnd) &&
889            LessOrEqual(endMainPos - currentDelta_ + GetChainDelta(endIndex), contentMainSize_ - contentEndOffset_);
890 }
891 
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const892 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
893 {
894     if (itemPosition_.empty()) {
895         return;
896     }
897     if (startIndex_ == 0 && itemPosition_.begin()->second.isGroup) {
898         auto& groupInfo = itemPosition_.begin()->second.groupInfo;
899         groupAtStart = groupInfo && groupInfo.value().atStart;
900     }
901     if (endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup) {
902         auto& groupInfo = itemPosition_.rbegin()->second.groupInfo;
903         groupAtEnd = groupInfo && groupInfo.value().atEnd;
904     }
905 }
906 
GetOffsetWithLimit(float offset) const907 float ListPattern::GetOffsetWithLimit(float offset) const
908 {
909     float currentOffset = GetTotalOffset() + contentStartOffset_;
910     if (Positive(offset)) {
911         return std::min(currentOffset, offset);
912     } else if (Negative(offset)) {
913         auto remainHeight = GetTotalHeight() - currentOffset;
914         return std::max(offset, -remainHeight);
915     }
916     return 0;
917 }
918 
GetOverScrollOffset(double delta) const919 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
920 {
921     OverScrollOffset offset = { 0, 0 };
922     bool groupAtStart = true;
923     bool groupAtEnd = true;
924     GetListItemGroupEdge(groupAtStart, groupAtEnd);
925 
926     int32_t startIndex = startIndex_;
927     float startMainPos = startMainPos_;
928     int32_t endIndex = endIndex_;
929     float endMainPos = endMainPos_;
930     if (startIndex == 0 && groupAtStart) {
931         offset.start = GetStartOverScrollOffset(delta, startMainPos);
932     }
933     if (endIndex == GetMaxIndexByRepeat() && groupAtEnd) {
934         offset.end = GetEndOverScrollOffset(delta, endMainPos, startMainPos);
935     }
936     return offset;
937 }
938 
GetStartOverScrollOffset(float offset,float startMainPos) const939 float ListPattern::GetStartOverScrollOffset(float offset, float startMainPos) const
940 {
941     float startOffset = 0.0f;
942     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(0, -offset) : 0.f;
943     auto startPos = startMainPos + ChainDelta - currentDelta_;
944     auto contentStartPos = contentStartOffset_;
945     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
946     if (GreatNotEqual(contentMainSize, endMainPos_ - startMainPos_) && isStackFromEnd_) {
947         contentStartPos = contentMainSize - endMainPos_ + startMainPos_;
948     }
949     auto newStartPos = startPos + offset;
950     if (startPos > contentStartPos && newStartPos > contentStartPos) {
951         startOffset = offset;
952     }
953     if (startPos > contentStartPos && newStartPos <= contentStartPos) {
954         startOffset = contentStartPos - startPos;
955     }
956     if (startPos <= contentStartPos && newStartPos > contentStartPos) {
957         startOffset = newStartPos - contentStartPos;
958     }
959     return startOffset;
960 }
961 
GetEndOverScrollOffset(float offset,float endMainPos,float startMainPos) const962 float ListPattern::GetEndOverScrollOffset(float offset, float endMainPos, float startMainPos) const
963 {
964     float endOffset = 0.0f;
965     float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(maxListItemIndex_, -offset) : 0.f;
966     auto endPos = endMainPos + ChainDelta - currentDelta_;
967     auto contentEndPos = contentMainSize_ - contentEndOffset_;
968     auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
969     if (GreatNotEqual(contentMainSize, endMainPos - startMainPos) && !isStackFromEnd_) {
970         endPos = startMainPos + contentMainSize;
971     }
972     auto newEndPos = endPos + offset;
973     if (endPos < contentEndPos && newEndPos < contentEndPos) {
974         endOffset = offset;
975     }
976     if (endPos < contentEndPos && newEndPos >= contentEndPos) {
977         endOffset = contentEndPos - endPos;
978     }
979     if (endPos >= contentEndPos && newEndPos < contentEndPos) {
980         endOffset = newEndPos - contentEndPos;
981     }
982     return endOffset;
983 }
984 
GetOutBoundaryOffset(float delta,bool useChainDelta) const985 OverScrollOffset ListPattern::GetOutBoundaryOffset(float delta, bool useChainDelta) const
986 {
987     OverScrollOffset offset = { 0, 0 };
988     bool groupAtStart = true;
989     bool groupAtEnd = true;
990     GetListItemGroupEdge(groupAtStart, groupAtEnd);
991 
992     int32_t startIndex = startIndex_;
993     float startMainPos = startMainPos_;
994     int32_t endIndex = endIndex_;
995     float endMainPos = endMainPos_;
996     if (startIndex == 0 && groupAtStart) {
997         auto startChainDelta = useChainDelta ? GetChainDelta(0) : 0.0f;
998         auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
999         if (endIndex_ == maxListItemIndex_ && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1000             isStackFromEnd_) {
1001             startMainPos = endMainPos - contentMainSize;
1002         }
1003         offset.start = startMainPos - delta + startChainDelta - contentStartOffset_;
1004         offset.start = std::max(offset.start, 0.0);
1005     }
1006     if (endIndex >= maxListItemIndex_ && groupAtEnd) {
1007         auto endChainDelta = useChainDelta ? GetChainDelta(endIndex) : 0.0f;
1008         endMainPos = endMainPos + endChainDelta;
1009         auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
1010         if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
1011             endMainPos = startMainPos + contentMainSize;
1012         }
1013         offset.end = contentMainSize_ - contentEndOffset_ - (endMainPos - delta);
1014         offset.end = std::max(offset.end, 0.0);
1015     }
1016     return offset;
1017 }
1018 
UpdateCurrentOffset(float offset,int32_t source)1019 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
1020 {
1021     // check edgeEffect is not springEffect
1022     if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
1023         if (IsOutOfBoundary(false)) {
1024             MarkDirtyNodeSelf();
1025         }
1026         return false;
1027     }
1028 
1029     SetScrollSource(source);
1030     FireAndCleanScrollingListener();
1031     auto lastDelta = currentDelta_;
1032     currentDelta_ = currentDelta_ - offset;
1033     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING || source == SCROLL_FROM_STATUSBAR) {
1034         isNeedCheckOffset_ = true;
1035     }
1036     if (!NearZero(offset)) {
1037         MarkDirtyNodeSelf();
1038     }
1039     if (itemPosition_.empty() || !IsOutOfBoundary() || !isScrollable_) {
1040         auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
1041         userOffset = FireObserverOnWillScroll(userOffset);
1042         currentDelta_ = lastDelta + userOffset;
1043         return true;
1044     }
1045 
1046     if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_CROWN) {
1047         auto res = GetOutBoundaryOffset(currentDelta_);
1048         // over scroll in drag update from normal to over scroll.
1049         float overScroll = std::max(res.start, res.end);
1050         // adjust offset.
1051         auto friction = GetScrollUpdateFriction(overScroll);
1052         offset = offset * friction;
1053         currentDelta_ = lastDelta - offset;
1054     }
1055 
1056     auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
1057     userOffset = FireObserverOnWillScroll(userOffset);
1058     currentDelta_ = lastDelta + userOffset;
1059     MarkScrollBarProxyDirty();
1060     return true;
1061 }
1062 
MarkDirtyNodeSelf()1063 void ListPattern::MarkDirtyNodeSelf()
1064 {
1065     auto host = GetHost();
1066     CHECK_NULL_VOID(host);
1067     if (!crossMatchChild_) {
1068         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1069     } else {
1070         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
1071     }
1072 }
1073 
OnScrollEndCallback()1074 void ListPattern::OnScrollEndCallback()
1075 {
1076     if (AnimateStoped()) {
1077         scrollStop_ = true;
1078         MarkDirtyNodeSelf();
1079     }
1080 }
1081 
GetContentSize() const1082 SizeF ListPattern::GetContentSize() const
1083 {
1084     auto host = GetHost();
1085     CHECK_NULL_RETURN(host, SizeF());
1086     auto geometryNode = host->GetGeometryNode();
1087     CHECK_NULL_RETURN(geometryNode, SizeF());
1088     auto renderContext = host->GetRenderContext();
1089     CHECK_NULL_RETURN(renderContext, SizeF());
1090     auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
1091     auto& padding = geometryNode->GetPadding();
1092     if (padding) {
1093         size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
1094     }
1095     return size;
1096 }
1097 
IsOutOfBoundary(bool useCurrentDelta)1098 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
1099 {
1100     if (itemPosition_.empty()) {
1101         return false;
1102     }
1103     auto currentDelta = useCurrentDelta ? currentDelta_ : 0.0f;
1104     auto res = GetOutBoundaryOffset(currentDelta);
1105     // over scroll in drag update from normal to over scroll.
1106     return Positive(res.start) || Positive(res.end);
1107 }
1108 
OnScrollCallback(float offset,int32_t source)1109 bool ListPattern::OnScrollCallback(float offset, int32_t source)
1110 {
1111     if (source == SCROLL_FROM_START) {
1112         auto item = swiperItem_.Upgrade();
1113         if (item) {
1114             item->ResetSwipeStatus();
1115         }
1116         FireOnScrollStart();
1117         return true;
1118     }
1119     ProcessDragUpdate(offset, source);
1120     return UpdateCurrentOffset(offset, source);
1121 }
1122 
StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)1123 bool ListPattern::StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)
1124 {
1125     auto snapDirection = snapAnimationOptions.snapDirection;
1126     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1127     CHECK_NULL_RETURN(listProperty, false);
1128     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
1129     CHECK_NULL_RETURN(scrollSnapAlign != ScrollSnapAlign::NONE, false);
1130     if (snapDirection != SnapDirection::NONE) {
1131         return ScrollToSnapIndex(snapDirection, scrollSnapAlign);
1132     }
1133     if (snapAnimationOptions.fromScrollBar &&
1134         ((GreatNotEqual(snapAnimationOptions.animationVelocity, 0.0f) && IsAtTop()) ||
1135             (LessNotEqual(snapAnimationOptions.animationVelocity, 0.0f) && IsAtBottom()))) {
1136         return false;
1137     }
1138     if (!IsScrolling()) {
1139         snapTrigOnScrollStart_ = true;
1140     }
1141     predictSnapOffset_ = snapAnimationOptions.snapDelta;
1142     scrollSnapVelocity_ = snapAnimationOptions.animationVelocity;
1143     snapTrigByScrollBar_ = snapAnimationOptions.fromScrollBar;
1144     predictSnapEndPos_.reset();
1145     MarkDirtyNodeSelf();
1146     return true;
1147 }
1148 
ScrollToSnapIndex(SnapDirection snapDirection,ScrollSnapAlign scrollSnapAlign)1149 bool ListPattern::ScrollToSnapIndex(SnapDirection snapDirection, ScrollSnapAlign scrollSnapAlign)
1150 {
1151     ScrollAlign align = ScrollAlign::NONE;
1152     auto anchorIndex = startIndex_;
1153     switch (scrollSnapAlign) {
1154         case ScrollSnapAlign::NONE:
1155             return false;
1156         case ScrollSnapAlign::START:
1157             align = ScrollAlign::START;
1158             anchorIndex = GetStartIndexExcludeStartOffset();
1159             break;
1160         case ScrollSnapAlign::CENTER:
1161             align = ScrollAlign::CENTER;
1162             anchorIndex = centerIndex_;
1163             break;
1164         case ScrollSnapAlign::END:
1165             align = ScrollAlign::END;
1166             anchorIndex = GetEndIndexExcludeEndOffset();
1167             break;
1168     }
1169     if (snapDirection == SnapDirection::FORWARD) {
1170         if (!lastSnapTargetIndex_.has_value()) {
1171             if (align == ScrollAlign::START) {
1172                 auto isAligned = GreatOrEqual(itemPosition_[anchorIndex].startPos, contentStartOffset_);
1173                 lastSnapTargetIndex_ = isAligned ? anchorIndex - 1 : anchorIndex;
1174             } else if (align == ScrollAlign::END) {
1175                 lastSnapTargetIndex_ = anchorIndex - 1;
1176             } else {
1177                 auto item = itemPosition_[anchorIndex];
1178                 auto itemCenterPos = (item.startPos + item.endPos) / 2;
1179                 lastSnapTargetIndex_ =
1180                     GreatOrEqual(itemCenterPos, contentMainSize_ / 2) ? anchorIndex - 1 : anchorIndex;
1181             }
1182         } else {
1183             lastSnapTargetIndex_ = lastSnapTargetIndex_.value() - 1;
1184         }
1185     } else if (snapDirection == SnapDirection::BACKWARD) {
1186         if (!lastSnapTargetIndex_.has_value()) {
1187             if (align == ScrollAlign::START) {
1188                 lastSnapTargetIndex_ = anchorIndex + 1;
1189             } else if (align == ScrollAlign::END) {
1190                 auto isAligned = LessOrEqual(itemPosition_[anchorIndex].endPos, contentMainSize_ - contentEndOffset_);
1191                 lastSnapTargetIndex_ = isAligned ? anchorIndex + 1 : anchorIndex;
1192             } else {
1193                 auto item = itemPosition_[anchorIndex];
1194                 auto itemCenterPos = (item.startPos + item.endPos) / 2;
1195                 lastSnapTargetIndex_ = LessOrEqual(itemCenterPos, contentMainSize_ / 2) ? anchorIndex + 1 : anchorIndex;
1196             }
1197         } else {
1198             lastSnapTargetIndex_ = lastSnapTargetIndex_.value() + 1;
1199         }
1200     }
1201     lastSnapTargetIndex_ = std::max(lastSnapTargetIndex_.value(), 0);
1202     lastSnapTargetIndex_ = std::min(lastSnapTargetIndex_.value(), maxListItemIndex_);
1203     ScrollToIndex(lastSnapTargetIndex_.value(), true, align);
1204     return true;
1205 }
1206 
GetEndIndexExcludeEndOffset()1207 int32_t ListPattern::GetEndIndexExcludeEndOffset()
1208 {
1209     auto endPos = contentMainSize_ - contentEndOffset_;
1210     auto iter = itemPosition_.rbegin();
1211     while (iter != itemPosition_.rend() && GreatOrEqual(iter->second.startPos, endPos)) {
1212         iter++;
1213     }
1214     return iter->first;
1215 }
1216 
GetStartIndexExcludeStartOffset()1217 int32_t ListPattern::GetStartIndexExcludeStartOffset()
1218 {
1219     auto iter = itemPosition_.begin();
1220     while (iter != itemPosition_.end() && LessOrEqual(iter->second.endPos, contentStartOffset_)) {
1221         iter++;
1222     }
1223     return iter->first;
1224 }
1225 
StartListSnapAnimation(float scrollSnapDelta,float scrollSnapVelocity)1226 void ListPattern::StartListSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity)
1227 {
1228     CHECK_NULL_VOID(scrollable_);
1229     scrollable_->StartListSnapAnimation(scrollSnapDelta, scrollSnapVelocity, snapTrigByScrollBar_);
1230 }
1231 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1232 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1233 {
1234     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1235         auto list = weak.Upgrade();
1236         CHECK_NULL_RETURN(list, 0.0);
1237         return list->startMainPos_ - list->currentDelta_;
1238     });
1239     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1240         auto list = weak.Upgrade();
1241         auto endPos = list->endMainPos_;
1242         auto startPos = list->startMainPos_;
1243         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
1244         if (list->isStackFromEnd_) {
1245             return leading;
1246         }
1247         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
1248     });
1249     scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
1250         auto list = weak.Upgrade();
1251         CHECK_NULL_RETURN(list, 0.0);
1252         auto startMainPos = list->startMainPos_;
1253         auto endMainPos = list->endMainPos_;
1254         auto contentMainSize = list->contentMainSize_ - list->contentEndOffset_ - list->contentStartOffset_;
1255         if (list->startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1256             list->isStackFromEnd_) {
1257             return contentMainSize - endMainPos + startMainPos;
1258         }
1259         return list->contentStartOffset_;
1260     });
1261     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1262         auto list = weak.Upgrade();
1263         auto endPos = list->endMainPos_;
1264         auto startPos = list->startMainPos_;
1265         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
1266         if (list->isStackFromEnd_) {
1267             return leading;
1268         }
1269         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
1270     });
1271     scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
1272         auto list = weak.Upgrade();
1273         CHECK_NULL_RETURN(list, 0.0);
1274         auto startMainPos = list->startMainPos_;
1275         auto endMainPos = list->endMainPos_;
1276         auto contentMainSize = list->contentMainSize_ - list->contentEndOffset_ - list->contentStartOffset_;
1277         if (list->startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1278             list->isStackFromEnd_) {
1279             return contentMainSize - endMainPos + startMainPos;
1280         }
1281         return list->contentStartOffset_;
1282     });
1283 }
1284 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1285 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1286 {
1287     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1288         auto pattern = wp.Upgrade();
1289         CHECK_NULL_RETURN(pattern, false);
1290         return pattern->OnKeyEvent(event);
1291     };
1292     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1293 }
1294 
OnKeyEvent(const KeyEvent & event)1295 bool ListPattern::OnKeyEvent(const KeyEvent& event)
1296 {
1297     if (event.action != KeyAction::DOWN) {
1298         return false;
1299     }
1300     if (event.code == KeyCode::KEY_PAGE_DOWN) {
1301         ScrollPage(false);
1302         return true;
1303     }
1304     if (event.code == KeyCode::KEY_PAGE_UP) {
1305         ScrollPage(true);
1306         return true;
1307     }
1308     if (FocusHub::IsFocusStepKey(event.code)) {
1309         return ScrollToLastFocusIndex(event);
1310     }
1311     return HandleDirectionKey(event);
1312 }
1313 
HandleDirectionKey(const KeyEvent & event)1314 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
1315 {
1316     return false;
1317 }
1318 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1319 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1320 {
1321     auto curFocus = currentFocusNode.Upgrade();
1322     CHECK_NULL_RETURN(curFocus, nullptr);
1323     auto curFrame = curFocus->GetFrameNode();
1324     CHECK_NULL_RETURN(curFrame, nullptr);
1325     auto curPattern = curFrame->GetPattern();
1326     CHECK_NULL_RETURN(curPattern, nullptr);
1327     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
1328     auto curIndex = -1;
1329     auto curIndexInGroup = -1;
1330     if (!curItemPattern) {
1331         auto parentNode = curFrame->GetParentFrameNode();
1332         CHECK_NULL_RETURN(parentNode, nullptr);
1333         auto parentPattern = AceType::DynamicCast<ListItemGroupPattern>(parentNode->GetPattern());
1334         CHECK_NULL_RETURN(parentPattern, nullptr);
1335         if (parentPattern->GetHeader() == curFrame) {
1336             curIndex = parentPattern->GetIndexInList();
1337             curIndexInGroup = -1;
1338         } else if (parentPattern->GetFooter() == curFrame) {
1339             curIndex = parentPattern->GetIndexInList();
1340             curIndexInGroup = parentPattern->GetTotalItemCount();
1341         } else {
1342             return nullptr;
1343         }
1344     } else {
1345         curIndex = curItemPattern->GetIndexInList();
1346         curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
1347     }
1348 
1349     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1350     CHECK_NULL_RETURN(listProperty, nullptr);
1351     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1352     auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
1353     if (curIndex < 0 || curIndex > maxListItemIndex_) {
1354         return nullptr;
1355     }
1356 
1357     auto moveStep = 0;
1358     auto nextIndex = curIndex;
1359     auto nextIndexInGroup = curIndexInGroup;
1360     if (lanes_ <= 1) {
1361         if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
1362             moveStep = 1;
1363             nextIndex = 0;
1364             nextIndexInGroup = -1;
1365         } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
1366             moveStep = -1;
1367             nextIndex = maxListItemIndex_;
1368             nextIndexInGroup = -1;
1369         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
1370                    (step == FocusStep::TAB)) {
1371             moveStep = 1;
1372             if ((curIndexInGroup == -1 && !curListItemGroupPara.hasHeader) ||
1373                 (curIndexInGroup == curListItemGroupPara.itemEndIndex && !curListItemGroupPara.hasFooter) ||
1374                 (curIndexInGroup > curListItemGroupPara.itemEndIndex)) {
1375                 nextIndex = curIndex + moveStep;
1376                 nextIndexInGroup = -1;
1377             } else {
1378                 nextIndexInGroup = curIndexInGroup + moveStep;
1379             }
1380         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
1381                    (step == FocusStep::SHIFT_TAB)) {
1382             moveStep = -1;
1383             if (curIndexInGroup == 0 && curListItemGroupPara.hasHeader) {
1384                 nextIndexInGroup = -1;
1385             } else if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1386                 nextIndex = curIndex + moveStep;
1387                 nextIndexInGroup = -1;
1388             } else {
1389                 nextIndexInGroup = curIndexInGroup + moveStep;
1390             }
1391         }
1392     } else {
1393         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1394             moveStep = 1;
1395             nextIndex = 0;
1396             nextIndexInGroup = -1;
1397         } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1398             moveStep = -1;
1399             nextIndex = maxListItemIndex_;
1400             nextIndexInGroup = -1;
1401         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1402             if (curIndexInGroup == -1) {
1403                 moveStep = lanes_;
1404                 nextIndex = GetNextLineFocusIndex(curIndex);
1405                 nextIndexInGroup = -1;
1406             } else {
1407                 moveStep = curListItemGroupPara.lanes;
1408                 nextIndexInGroup = curIndexInGroup + moveStep;
1409                 VerifyFocusIndex(nextIndex, nextIndexInGroup, curListItemGroupPara);
1410             }
1411         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1412             if (curIndexInGroup == -1) {
1413                 moveStep = -lanes_;
1414                 nextIndex = curIndex + moveStep;
1415                 nextIndexInGroup = -1;
1416             } else {
1417                 moveStep = -curListItemGroupPara.lanes;
1418                 nextIndexInGroup = curIndexInGroup + moveStep;
1419                 VerifyFocusIndex(nextIndex, nextIndexInGroup, curListItemGroupPara);
1420             }
1421         } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1422             moveStep = 1;
1423             if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1424                 ((curIndexInGroup != -1) &&
1425                     ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1426                 nextIndex = curIndex + moveStep;
1427                 nextIndexInGroup = -1;
1428             } else if ((curIndexInGroup != -1) &&
1429                        ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1430                 nextIndexInGroup = curIndexInGroup + moveStep;
1431             }
1432         } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1433             moveStep = -1;
1434             if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1435                 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1436                 nextIndex = curIndex + moveStep;
1437                 nextIndexInGroup = -1;
1438             } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1439                 nextIndexInGroup = curIndexInGroup + moveStep;
1440             }
1441         } else if (step == FocusStep::TAB) {
1442             moveStep = 1;
1443             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1444                 nextIndex = curIndex + moveStep;
1445                 nextIndexInGroup = -1;
1446             } else {
1447                 nextIndexInGroup = curIndexInGroup + moveStep;
1448             }
1449         } else if (step == FocusStep::SHIFT_TAB) {
1450             moveStep = -1;
1451             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1452                 nextIndex = curIndex + moveStep;
1453                 nextIndexInGroup = -1;
1454             } else {
1455                 nextIndexInGroup = curIndexInGroup + moveStep;
1456             }
1457         }
1458     }
1459     while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1460         if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1461             return nullptr;
1462         }
1463         auto nextFocusNode =
1464             ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1465         if (nextFocusNode.Upgrade()) {
1466             return nextFocusNode;
1467         }
1468         if (nextIndexInGroup > -1) {
1469             nextIndexInGroup += moveStep;
1470         } else {
1471             nextIndex += moveStep;
1472         }
1473     }
1474     return nullptr;
1475 }
1476 
VerifyFocusIndex(int32_t & nextIndex,int32_t & nextIndexInGroup,const ListItemGroupPara & param)1477 void ListPattern::VerifyFocusIndex(int32_t& nextIndex, int32_t& nextIndexInGroup, const ListItemGroupPara& param)
1478 {
1479     if (nextIndexInGroup < 0) {
1480         nextIndex--;
1481         nextIndexInGroup = -1;
1482     } else if (nextIndexInGroup > param.itemEndIndex) {
1483         if (param.lanes > 1 &&
1484             nextIndexInGroup <= param.itemEndIndex + param.lanes - param.itemEndIndex % param.lanes - 1) {
1485             nextIndexInGroup = param.itemEndIndex;
1486         } else {
1487             nextIndex++;
1488             nextIndexInGroup = -1;
1489         }
1490     }
1491 }
1492 
GetNextLineFocusIndex(int32_t currIndex)1493 int32_t ListPattern::GetNextLineFocusIndex(int32_t currIndex)
1494 {
1495     int32_t nextIndex = currIndex + lanes_;
1496     if (nextIndex <= maxListItemIndex_) {
1497         return nextIndex;
1498     }
1499     auto it1 = itemPosition_.find(currIndex);
1500     if (it1 == itemPosition_.end()) {
1501         return nextIndex;
1502     }
1503     auto it2 = itemPosition_.find(maxListItemIndex_);
1504     if (it2 == itemPosition_.end()) {
1505         return maxListItemIndex_;
1506     }
1507     if (NearEqual(it1->second.startPos, it2->second.startPos)) {
1508         return nextIndex;
1509     }
1510     return maxListItemIndex_;
1511 }
1512 
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1513 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1514 {
1515     auto listFrame = GetHost();
1516     CHECK_NULL_RETURN(listFrame, nullptr);
1517     auto listFocus = listFrame->GetFocusHub();
1518     CHECK_NULL_RETURN(listFocus, nullptr);
1519     WeakPtr<FocusHub> target;
1520     listFocus->AnyChildFocusHub([&target, tarMainIndex, tarGroupIndex](const RefPtr<FocusHub>& childFocus) {
1521         if (!childFocus->IsFocusable()) {
1522             return false;
1523         }
1524         auto childFrame = childFocus->GetFrameNode();
1525         if (!childFrame) {
1526             return false;
1527         }
1528         auto childPattern = childFrame->GetPattern();
1529         if (!childPattern) {
1530             return false;
1531         }
1532         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1533         if (!childItemPattern) {
1534             auto parentNode = childFrame->GetParentFrameNode();
1535             CHECK_NULL_RETURN(parentNode, false);
1536             auto parentPattern = AceType::DynamicCast<ListItemGroupPattern>(parentNode->GetPattern());
1537             CHECK_NULL_RETURN(parentPattern, false);
1538             if (parentPattern->GetIndexInList() == tarMainIndex) {
1539                 if ((parentPattern->GetHeader() == childFrame && tarGroupIndex == -1) ||
1540                     (parentPattern->GetFooter() == childFrame && tarGroupIndex == parentPattern->GetTotalItemCount())) {
1541                     target = childFocus;
1542                     return true;
1543                 }
1544             }
1545             return false;
1546         }
1547         auto curIndex = childItemPattern->GetIndexInList();
1548         auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1549         if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1550             target = childFocus;
1551             return true;
1552         }
1553         return false;
1554     });
1555     return target;
1556 }
1557 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1558 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1559 {
1560     CHECK_NULL_RETURN(focusFrameNode, false);
1561     auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1562     CHECK_NULL_RETURN(focusPattern, false);
1563     auto curIndex = focusPattern->GetIndexInList();
1564     ScrollToIndex(curIndex, smooth_, GetScrollToNodeAlign());
1565     auto pipeline = GetContext();
1566     if (pipeline) {
1567         pipeline->FlushUITasks();
1568     }
1569     return true;
1570 }
1571 
GetScrollOffsetAbility()1572 ScrollOffsetAbility ListPattern::GetScrollOffsetAbility()
1573 {
1574     return {
1575         [wp = WeakClaim(this)](float moveOffset) -> bool {
1576             auto pattern = wp.Upgrade();
1577             CHECK_NULL_RETURN(pattern, false);
1578             pattern->ScrollBy(-moveOffset);
1579             return true;
1580         },
1581         GetAxis(),
1582         IsScrollSnapAlignCenter() ? 0 : contentStartOffset_,
1583         IsScrollSnapAlignCenter() ? 0 : contentEndOffset_,
1584     };
1585 }
1586 
GetScrollIndexAbility()1587 std::function<bool(int32_t)> ListPattern::GetScrollIndexAbility()
1588 {
1589     return [wp = WeakClaim(this)](int32_t index) -> bool {
1590         auto pattern = wp.Upgrade();
1591         CHECK_NULL_RETURN(pattern, false);
1592         if (index == FocusHub::SCROLL_TO_HEAD) {
1593             // When the focus framework calls to find Head and Tail, it should reset. Otherwise, due to scrolling, the
1594             // newly acquired focus will immediately lose focus and set depend to SELF.
1595             pattern->ResetFocusIndex();
1596             pattern->ResetGroupFocusIndex();
1597             pattern->ScrollToIndex(0, false, ScrollAlign::START);
1598         } else if (index == FocusHub::SCROLL_TO_TAIL) {
1599             pattern->ResetFocusIndex();
1600             pattern->ResetGroupFocusIndex();
1601             pattern->ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, false, ScrollAlign::END);
1602         } else {
1603             pattern->ScrollToIndex(index, false, ScrollAlign::AUTO);
1604         }
1605         return true;
1606     };
1607 }
1608 
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1609 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1610     int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1611 {
1612     bool isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1613     bool needFindNextFocusNode = ScrollListItemGroupForFocus(
1614         nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step, isScrollIndex);
1615 
1616     return needFindNextFocusNode ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1617 }
1618 
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1619 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1620 {
1621     auto isScrollIndex = false;
1622     auto pipeline = GetContext();
1623     CHECK_NULL_RETURN(pipeline, isScrollIndex);
1624     if (nextIndex < startIndex_ && nextIndexInGroup == -1) {
1625         isScrollIndex = true;
1626         ScrollToIndex(nextIndex, false, ScrollAlign::START);
1627         pipeline->FlushUITasks();
1628     } else if (nextIndex > endIndex_ && nextIndexInGroup == -1) {
1629         isScrollIndex = true;
1630         ScrollToIndex(nextIndex, false, ScrollAlign::END);
1631         pipeline->FlushUITasks();
1632     } else if (nextIndexInGroup == -1 && HandleDisplayedChildFocus(nextIndex, curIndex)) {
1633         isScrollIndex = true;
1634         pipeline->FlushUITasks();
1635     }
1636     return isScrollIndex;
1637 }
1638 
HandleDisplayedChildFocus(int32_t nextIndex,int32_t curIndex)1639 bool ListPattern::HandleDisplayedChildFocus(int32_t nextIndex, int32_t curIndex)
1640 {
1641     auto iter = itemPosition_.find(nextIndex);
1642     if (iter == itemPosition_.end()) {
1643         return false;
1644     }
1645     float targetPos = 0.0f;
1646     float startPos = iter->second.startPos;
1647     float endPos = iter->second.endPos;
1648     ScrollAlign align = ScrollAlign::AUTO;
1649     if (iter->second.isGroup) {
1650         auto host = GetHost();
1651         CHECK_NULL_RETURN(host, false);
1652         auto itemGroupWrapper = host->GetChildByIndex(nextIndex);
1653         CHECK_NULL_RETURN(itemGroupWrapper, false);
1654         auto itemGroup = itemGroupWrapper->GetHostNode();
1655         CHECK_NULL_RETURN(itemGroup, false);
1656         auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1657         CHECK_NULL_RETURN(groupPattern, false);
1658         int32_t indexInGroup = nextIndex < curIndex ? groupPattern->GetEndIndexInGroup() : 0;
1659         if (!GetListItemGroupAnimatePosWithIndexInGroup(nextIndex, indexInGroup, startPos, align, targetPos)) {
1660             targetPos = startPos;
1661         }
1662     } else {
1663         GetListItemAnimatePos(startPos, endPos, align, targetPos);
1664     }
1665     if (Positive(targetPos)) {
1666         ScrollToIndex(nextIndex, false, ScrollAlign::END);
1667         return iter->second.isGroup;
1668     } else if (Negative(targetPos)) {
1669         ScrollToIndex(nextIndex, false, ScrollAlign::START);
1670         return iter->second.isGroup;
1671     }
1672     return false;
1673 }
1674 
CalcAlignForFocusToGroupItem(int32_t moveStep,FocusStep step) const1675 ScrollAlign ListPattern::CalcAlignForFocusToGroupItem(int32_t moveStep, FocusStep step) const
1676 {
1677     // Extract method only for ScrollListItemGroupForFocus.
1678     ScrollAlign scrollAlign = ScrollAlign::END;
1679     if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1680         (step == FocusStep::RIGHT_END)) {
1681         scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1682     } else {
1683         scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1684     }
1685     return scrollAlign;
1686 }
1687 
CalcNextIndexInGroup(int32_t nextIndex,int32_t curIndex,int32_t curIndexInGroup,int32_t moveStep,ListItemGroupPara & nextListItemGroupPara) const1688 int32_t ListPattern::CalcNextIndexInGroup(int32_t nextIndex, int32_t curIndex, int32_t curIndexInGroup,
1689     int32_t moveStep, ListItemGroupPara& nextListItemGroupPara) const
1690 {
1691     // Extract method only for ScrollListItemGroupForFocus.
1692     int32_t nextIndexInGroup = -1;
1693     if (nextIndex != curIndex) {
1694         nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1695     }
1696     if (moveStep == -1) {
1697         if (curIndexInGroup == -1 && nextListItemGroupPara.hasFooter) {
1698             nextIndexInGroup = nextListItemGroupPara.itemEndIndex + 1;
1699         }
1700     } else if (moveStep == 1) {
1701         if (!nextListItemGroupPara.hasHeader) {
1702             nextIndexInGroup = 0;
1703         } else {
1704             nextIndexInGroup = -1;
1705         }
1706     }
1707     return nextIndexInGroup;
1708 }
1709 
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1710 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1711     int32_t curIndexInGroup, int32_t moveStep, FocusStep step, bool isScrollIndex)
1712 {
1713     auto pipeline = GetContext();
1714     CHECK_NULL_RETURN(pipeline, true);
1715     RefPtr<FrameNode> nextIndexNode;
1716     auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1717     if (!isNextInGroup || !nextIndexNode) {
1718         nextIndexInGroup = -1;
1719         return true;
1720     }
1721     auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1722     if (nextIndexInGroup == -1) {
1723         nextIndexInGroup = CalcNextIndexInGroup(nextIndex, curIndex, curIndexInGroup, moveStep, nextListItemGroupPara);
1724         if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1725             (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1726             ScrollAlign align = CalcAlignForFocusToGroupItem(moveStep, step);
1727             ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, align);
1728             pipeline->FlushUITasks();
1729         }
1730     } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1731         if (nextListItemGroupPara.hasFooter) {
1732             ScrollToIndex(nextIndex, false, ScrollAlign::END);
1733             pipeline->FlushUITasks();
1734         } else {
1735             nextIndexInGroup = -1;
1736             return false;
1737         }
1738     } else if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1739         ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, ScrollAlign::START);
1740         pipeline->FlushUITasks();
1741     } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1742         ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, ScrollAlign::END);
1743         pipeline->FlushUITasks();
1744     }
1745     return true;
1746 }
1747 
OnAnimateStop()1748 void ListPattern::OnAnimateStop()
1749 {
1750     if (!GetIsDragging() || GetScrollAbort()) {
1751         scrollStop_ = true;
1752         MarkDirtyNodeSelf();
1753         isScrollEnd_ = true;
1754     }
1755     scrollTarget_.reset();
1756 }
1757 
ScrollTo(float position)1758 void ListPattern::ScrollTo(float position)
1759 {
1760     StopAnimate();
1761     jumpIndex_.reset();
1762     targetIndex_.reset();
1763     currentDelta_ = 0.0f;
1764     SetAnimateCanOverScroll(GetCanStayOverScroll());
1765     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1766     SetIsOverScroll(GetCanStayOverScroll());
1767     MarkDirtyNodeSelf();
1768     isScrollEnd_ = true;
1769 }
1770 
ResetScrollToIndexParams()1771 void ListPattern::ResetScrollToIndexParams()
1772 {
1773     targetIndex_.reset();
1774     targetIndexInGroup_.reset();
1775     scrollAlign_ = ScrollAlign::START;
1776 }
1777 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1778 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1779 {
1780     SetScrollSource(SCROLL_FROM_JUMP);
1781     // When snap align scrolling with the mouse wheel, do not interrupt the animation.
1782     if (!smooth && !lastSnapTargetIndex_.has_value()) {
1783         ResetScrollToIndexParams();
1784         StopAnimate();
1785     }
1786     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1787         currentDelta_ = 0.0f;
1788         smooth_ = smooth;
1789         if (smooth_) {
1790             SetExtraOffset(extraOffset);
1791             if (!AnimateToTarget(index, std::nullopt, align)) {
1792                 targetIndex_ = index;
1793                 scrollAlign_ = align;
1794             }
1795         } else {
1796             if (extraOffset.has_value()) {
1797                 currentDelta_ = extraOffset.value();
1798             }
1799             jumpIndex_ = index;
1800             scrollAlign_ = align;
1801             jumpIndexInGroup_.reset();
1802         }
1803         MarkDirtyNodeSelf();
1804     }
1805     isScrollEnd_ = true;
1806     FireAndCleanScrollingListener();
1807 }
1808 
CheckTargetValid(int32_t index,int32_t indexInGroup)1809 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1810 {
1811     auto host = GetHost();
1812     auto totalItemCount = host->GetTotalChildCount();
1813     if ((index < 0) || (index >= totalItemCount)) {
1814         return false;
1815     }
1816     auto groupWrapper = host->GetOrCreateChildByIndex(index);
1817     CHECK_NULL_RETURN(groupWrapper, false);
1818     if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1819         return false;
1820     }
1821     auto groupNode = groupWrapper->GetHostNode();
1822     CHECK_NULL_RETURN(groupNode, false);
1823     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1824     CHECK_NULL_RETURN(groupPattern, false);
1825     auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1826     if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1827         return false;
1828     }
1829     return true;
1830 }
1831 
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1832 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1833 {
1834     SetScrollSource(SCROLL_FROM_JUMP);
1835     if (!smooth) {
1836         StopAnimate();
1837         targetIndex_.reset();
1838         targetIndexInGroup_.reset();
1839     }
1840     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1841         currentDelta_ = 0.0f;
1842         smooth_ = smooth;
1843         if (smooth_) {
1844             if (!AnimateToTarget(index, indexInGroup, align) && CheckTargetValid(index, indexInGroup)) {
1845                 targetIndex_ = index;
1846                 currentDelta_ = 0;
1847                 targetIndexInGroup_ = indexInGroup;
1848                 scrollAlign_ = align;
1849             }
1850         } else {
1851             jumpIndex_ = index;
1852             jumpIndexInGroup_ = indexInGroup;
1853             scrollAlign_ = align;
1854             if (align == ScrollAlign::AUTO && !isInitialized_) {
1855                 scrollAlign_ = ScrollAlign::END;
1856             }
1857         }
1858         MarkDirtyNodeSelf();
1859     }
1860     isScrollEnd_ = true;
1861     FireAndCleanScrollingListener();
1862 }
1863 
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1864 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1865 {
1866     switch (align) {
1867         case ScrollAlign::START:
1868         case ScrollAlign::NONE:
1869             targetPos = startPos;
1870             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1871                 targetPos -= contentStartOffset_;
1872             }
1873             break;
1874         case ScrollAlign::CENTER:
1875             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1876             break;
1877         case ScrollAlign::END:
1878             targetPos = endPos - contentMainSize_;
1879             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1880                 targetPos += contentEndOffset_;
1881             }
1882             break;
1883         case ScrollAlign::AUTO:
1884             targetPos = CalculateTargetPos(startPos, endPos);
1885             break;
1886     }
1887     return true;
1888 }
1889 
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1890 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(
1891     int32_t index, float startPos, float endPos, ScrollAlign align, float& targetPos)
1892 {
1893     auto host = GetHost();
1894     CHECK_NULL_RETURN(host, false);
1895     auto groupWrapper = host->GetChildByIndex(index);
1896     CHECK_NULL_RETURN(groupWrapper, false);
1897     auto groupNode = groupWrapper->GetHostNode();
1898     CHECK_NULL_RETURN(groupNode, false);
1899     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1900     CHECK_NULL_RETURN(groupPattern, false);
1901     auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1902     CHECK_NULL_RETURN(groupLayoutProperty, false);
1903     auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1904 
1905     switch (align) {
1906         case ScrollAlign::START:
1907         case ScrollAlign::NONE:
1908             if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1909                 return false;
1910             }
1911             targetPos = startPos;
1912             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1913                 targetPos -= contentStartOffset_;
1914             }
1915             break;
1916         case ScrollAlign::CENTER:
1917             if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1918                 return false;
1919             }
1920             targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1921             break;
1922         case ScrollAlign::END:
1923             if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1924                 return false;
1925             }
1926             targetPos = endPos - contentMainSize_;
1927             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1928                 targetPos += contentEndOffset_;
1929             }
1930             break;
1931         case ScrollAlign::AUTO:
1932             if (targetIndex_.has_value()) {
1933                 targetPos = CalculateTargetPos(startPos, endPos);
1934                 return true;
1935             }
1936             return false;
1937     }
1938 
1939     return true;
1940 }
1941 
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1942 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(
1943     int32_t index, int32_t indexInGroup, float startPos, ScrollAlign align, float& targetPos)
1944 {
1945     auto host = GetHost();
1946     CHECK_NULL_RETURN(host, false);
1947     auto groupWrapper = host->GetChildByIndex(index);
1948     CHECK_NULL_RETURN(groupWrapper, false);
1949     auto groupNode = groupWrapper->GetHostNode();
1950     CHECK_NULL_RETURN(groupNode, false);
1951     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1952     CHECK_NULL_RETURN(groupPattern, false);
1953     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1954     CHECK_NULL_RETURN(listLayoutProperty, false);
1955     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1956     auto itemsPosInGroup = groupPattern->GetItemPosition();
1957     auto it = itemsPosInGroup.find(indexInGroup);
1958     if (it == itemsPosInGroup.end()) {
1959         return false;
1960     }
1961     auto axis = GetAxis();
1962     std::optional<float> padding;
1963     std::optional<float> margin;
1964     if (axis == Axis::HORIZONTAL) {
1965         padding = IsReverse() ? groupWrapper->GetGeometryNode()->GetPadding()->right
1966                               : groupWrapper->GetGeometryNode()->GetPadding()->left;
1967         margin = IsReverse() ? groupWrapper->GetGeometryNode()->GetMargin()->right
1968                              : groupWrapper->GetGeometryNode()->GetMargin()->left;
1969     } else {
1970         padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1971         margin = groupWrapper->GetGeometryNode()->GetMargin()->top;
1972     }
1973     auto marginValue = margin.value_or(0.f);
1974     auto paddingValue = padding.value_or(0.f);
1975     if (align == ScrollAlign::CENTER) {
1976         targetPos = paddingValue + marginValue + startPos + (it->second.startPos + it->second.endPos) / 2.0f -
1977                     contentMainSize_ / 2.0f;
1978     } else {
1979         float itemStartPos = paddingValue + marginValue + startPos + it->second.startPos;
1980         float itemEndPos = paddingValue + marginValue + startPos + it->second.endPos;
1981         if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1982             itemStartPos -= groupPattern->GetHeaderMainSize();
1983         }
1984         if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1985             itemEndPos += groupPattern->GetFooterMainSize();
1986         }
1987         if (align == ScrollAlign::AUTO) {
1988             targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1989         } else {
1990             if (!IsScrollSnapAlignCenter() || childrenSize_) {
1991                 itemStartPos -= contentStartOffset_;
1992                 itemEndPos += contentEndOffset_;
1993             }
1994             targetPos = align == ScrollAlign::END ? itemEndPos - contentMainSize_ : itemStartPos;
1995         }
1996     }
1997     return true;
1998 }
1999 
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)2000 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
2001 {
2002     auto iter = itemPosition_.find(index);
2003     if (iter == itemPosition_.end()) {
2004         return false;
2005     }
2006     float targetPos = 0.0f;
2007     if (iter->second.isGroup) {
2008         if (indexInGroup.has_value()) {
2009             if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
2010                 align, targetPos)) {
2011                 return false;
2012             }
2013         } else {
2014             if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
2015                 align, targetPos)) {
2016                 return false;
2017             }
2018         }
2019     } else {
2020         if (indexInGroup.has_value()) {
2021             return false;
2022         }
2023         GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
2024     }
2025     float extraOffset = 0.0f;
2026     if (GetExtraOffset().has_value()) {
2027         extraOffset = GetExtraOffset().value();
2028         targetPos += extraOffset;
2029         ResetExtraOffset();
2030     }
2031     if (lastSnapTargetIndex_.has_value()) {
2032         if ((Positive(targetPos) && IsAtBottom()) || (Negative(targetPos) && IsAtTop())) {
2033             ResetLastSnapTargetIndex();
2034             return true;
2035         }
2036     }
2037     if (!NearZero(targetPos)) {
2038         AnimateTo(targetPos + currentOffset_, -1, nullptr, true, false, false);
2039         if (predictSnapOffset_.has_value() && AnimateRunning()) {
2040             scrollSnapVelocity_ = 0.0f;
2041             predictSnapOffset_.reset();
2042             snapTrigOnScrollStart_ = false;
2043         }
2044         if (!indexInGroup.has_value()) {
2045             scrollTarget_ = { index, extraOffset, align, targetPos + currentOffset_ };
2046         }
2047     } else {
2048         ResetScrollToIndexParams();
2049         StopAnimate();
2050     }
2051     return true;
2052 }
2053 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)2054 void ListPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
2055 {
2056     float distance = reverse ? contentMainSize_ : -contentMainSize_;
2057     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
2058         distance = distance / 2.f;
2059     }
2060     if (smooth) {
2061         float position = -GetTotalOffset() + distance;
2062         AnimateTo(-position, -1, nullptr, true, false, false);
2063     } else {
2064         StopAnimate();
2065         UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
2066         isScrollEnd_ = true;
2067     }
2068 }
2069 
ScrollBy(float offset)2070 void ListPattern::ScrollBy(float offset)
2071 {
2072     StopAnimate();
2073     SetIsOverScroll(false);
2074     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
2075     isScrollEnd_ = true;
2076 }
2077 
GetCurrentOffset() const2078 Offset ListPattern::GetCurrentOffset() const
2079 {
2080     if (GetAxis() == Axis::HORIZONTAL) {
2081         return { GetTotalOffset(), 0.0 };
2082     }
2083     return { 0.0, GetTotalOffset() };
2084 }
2085 
HandleScrollBarOutBoundary()2086 void ListPattern::HandleScrollBarOutBoundary()
2087 {
2088     if (itemPosition_.empty()) {
2089         return;
2090     }
2091     if (!GetScrollBar() && !GetScrollBarProxy()) {
2092         return;
2093     }
2094     if (!isScrollable_) {
2095         ScrollablePattern::HandleScrollBarOutBoundary(0);
2096         return;
2097     }
2098     auto res = GetOutBoundaryOffset(0.0f);
2099     float overScroll = std::max(res.start, res.end);
2100     ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
2101 }
2102 
GetItemRect(int32_t index) const2103 Rect ListPattern::GetItemRect(int32_t index) const
2104 {
2105     if (index < 0 || index < startIndex_ || index > endIndex_) {
2106         return Rect();
2107     }
2108     auto host = GetHost();
2109     CHECK_NULL_RETURN(host, Rect());
2110     auto item = host->GetChildByIndex(index + itemStartIndex_);
2111     CHECK_NULL_RETURN(item, Rect());
2112     auto itemGeometry = item->GetGeometryNode();
2113     CHECK_NULL_RETURN(itemGeometry, Rect());
2114     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
2115         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
2116 }
2117 
GetItemIndex(double x,double y) const2118 int32_t ListPattern::GetItemIndex(double x, double y) const
2119 {
2120     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2121         Rect rect = GetItemRect(index);
2122         if (rect.IsInRegion({x, y})) {
2123             return index;
2124         }
2125     }
2126     return -1;
2127 }
2128 
GetItemIndexInGroup(double x,double y) const2129 ListItemIndex ListPattern::GetItemIndexInGroup(double x, double y) const
2130 {
2131     ListItemIndex itemIndex = { -1, -1, -1 };
2132 
2133     auto host = GetHost();
2134     CHECK_NULL_RETURN(host, itemIndex);
2135     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2136         auto item = host->GetChildByIndex(index);
2137         if (!AceType::InstanceOf<FrameNode>(item)) {
2138             continue;
2139         }
2140         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
2141         auto groupItemPattern  = itemFrameNode->GetPattern<ListItemGroupPattern>();
2142         if (groupItemPattern) {
2143             if (GetGroupItemIndex(x, y, itemFrameNode, index, itemIndex)) {
2144                 return itemIndex;
2145             }
2146         } else {
2147             Rect rect = GetItemRect(index);
2148             if (rect.IsInRegion({x, y})) {
2149                 itemIndex.index = index;
2150                 return itemIndex;
2151             }
2152         }
2153     }
2154     return itemIndex;
2155 }
2156 
GetGroupItemIndex(double x,double y,RefPtr<FrameNode> itemFrameNode,int32_t & index,ListItemIndex & itemIndex) const2157 bool ListPattern::GetGroupItemIndex(double x, double y, RefPtr<FrameNode> itemFrameNode,
2158     int32_t& index, ListItemIndex& itemIndex) const
2159 {
2160     auto groupItemPattern = itemFrameNode->GetPattern<ListItemGroupPattern>();
2161     Rect rect = GetItemRect(index);
2162     if (groupItemPattern && rect.IsInRegion({x, y})) {
2163         itemIndex.index = index;
2164         for (int32_t groupIndex = groupItemPattern->GetDisplayStartIndexInGroup();
2165             groupIndex <= groupItemPattern->GetDisplayEndIndexInGroup(); ++groupIndex) {
2166             Rect groupRect = GetItemRectInGroup(index, groupIndex);
2167             if (groupRect.IsInRegion({x, y})) {
2168                 itemIndex.index = index;
2169                 itemIndex.area = 1; // item area
2170                 itemIndex.indexInGroup = groupIndex;
2171                 return true;
2172             }
2173         }
2174 
2175         int32_t areaValue = 0;
2176         if (GetAxis() == Axis::VERTICAL) {
2177             areaValue = ProcessAreaVertical(x, y, rect, index, groupItemPattern);
2178         } else {
2179             areaValue = ProcessAreaHorizontal(x, y, rect, index, groupItemPattern);
2180         }
2181         if (areaValue != -1) {
2182             itemIndex.index = index;
2183             itemIndex.area = areaValue;
2184             itemIndex.indexInGroup = -1;
2185             return true;
2186         }
2187     }
2188 
2189     return false;
2190 }
2191 
ProcessAreaVertical(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const2192 int32_t ListPattern::ProcessAreaVertical(double& x, double& y, Rect& groupRect, int32_t& index,
2193     RefPtr<ListItemGroupPattern> groupItemPattern) const
2194 {
2195     if (groupItemPattern->GetTotalItemCount() > 0) { // has item
2196         Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
2197         Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
2198 
2199         if (groupItemPattern->IsHasHeader() && LessOrEqual(y, firstRect.Top()) && GreatOrEqual(y, groupRect.Top())) {
2200             return  DEFAULT_HEADER_VALUE;
2201         }
2202 
2203         if (groupItemPattern->IsHasFooter() && GreatOrEqual(y, endRect.Bottom()) &&
2204             LessOrEqual(y, groupRect.Bottom())) {
2205             return  DEFAULT_FOOTER_VALUE;
2206         }
2207     } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
2208         float headerHeight = groupItemPattern->GetHeaderMainSize();
2209         float footerHeight = groupItemPattern->GetFooterMainSize();
2210         float topPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->top.value_or(0.0f);
2211         float bottomPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->bottom.value_or(0.0f);
2212         if (LessOrEqual(y, groupRect.Top() + headerHeight + topPaddng)  && GreatOrEqual(y, groupRect.Top())) { //header
2213             return  DEFAULT_HEADER_VALUE;
2214         } else if (GreatOrEqual(y, groupRect.Bottom() - footerHeight - bottomPaddng) &&
2215             LessOrEqual(y, groupRect.Bottom())) {
2216             return  DEFAULT_FOOTER_VALUE;
2217         }
2218     } else if (GreatOrEqual(y, groupRect.Top())  && LessOrEqual(y, groupRect.Bottom())) {
2219         return  0;
2220     }
2221 
2222     return -1;
2223 }
2224 
ProcessAreaHorizontal(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const2225 int32_t ListPattern::ProcessAreaHorizontal(double& x, double& y, Rect& groupRect, int32_t& index,
2226     RefPtr<ListItemGroupPattern> groupItemPattern) const
2227 {
2228     if (groupItemPattern->GetTotalItemCount() > 0) { // has item
2229         Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
2230         Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
2231 
2232         if (groupItemPattern->IsHasHeader() && LessOrEqual(x, firstRect.Left()) && GreatOrEqual(x, groupRect.Left())) {
2233             return  DEFAULT_HEADER_VALUE;
2234         }
2235 
2236         if (groupItemPattern->IsHasFooter() && GreatOrEqual(x, endRect.Right()) && LessOrEqual(x, groupRect.Right())) {
2237             return  DEFAULT_FOOTER_VALUE;
2238         }
2239     } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
2240         float headerHeight = groupItemPattern->GetHeaderMainSize();
2241         float footerHeight = groupItemPattern->GetFooterMainSize();
2242         float leftPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->left.value_or(0.0f);
2243         float rightPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->right.value_or(0.0f);
2244         if (LessOrEqual(x, groupRect.Left() + headerHeight + leftPaddng)  && GreatOrEqual(x, groupRect.Left())) {
2245             return  DEFAULT_HEADER_VALUE;
2246         } else if (GreatOrEqual(x, groupRect.Right() - footerHeight - rightPaddng) &&
2247             LessOrEqual(x, groupRect.Right())) {
2248             return  DEFAULT_FOOTER_VALUE;
2249         }
2250     } else if (GreatOrEqual(x, groupRect.Left())  && LessOrEqual(x, groupRect.Right())) {
2251         return  0;
2252     }
2253 
2254     return -1;
2255 }
2256 
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const2257 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
2258 {
2259     if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
2260         return Rect();
2261     }
2262     auto host = GetHost();
2263     CHECK_NULL_RETURN(host, Rect());
2264     auto itemGroupWrapper = host->GetChildByIndex(index);
2265     CHECK_NULL_RETURN(itemGroupWrapper, Rect());
2266     auto itemGroup = itemGroupWrapper->GetHostNode();
2267     CHECK_NULL_RETURN(itemGroup, Rect());
2268     if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
2269         return Rect();
2270     }
2271     auto itemGroupGeometry = itemGroup->GetGeometryNode();
2272     CHECK_NULL_RETURN(itemGroupGeometry, Rect());
2273     auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
2274     CHECK_NULL_RETURN(groupPattern, Rect());
2275     if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
2276         indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
2277         return Rect();
2278     }
2279     auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
2280     CHECK_NULL_RETURN(groupItem, Rect());
2281     auto groupItemGeometry = groupItem->GetGeometryNode();
2282     CHECK_NULL_RETURN(groupItemGeometry, Rect());
2283     return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
2284         itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
2285         groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
2286 }
2287 
CalculateJumpOffset()2288 bool ListPattern::CalculateJumpOffset()
2289 {
2290     for (const auto& pos : itemPosition_) {
2291         if (pos.second.groupInfo && !pos.second.groupInfo.value().atStart) {
2292             continue;
2293         }
2294         ListPositionInfo info = posMap_->GetPositionInfo(pos.first);
2295         if (!Negative(info.mainSize)) {
2296             currentOffset_ = info.mainPos - pos.second.startPos;
2297             return true;
2298         }
2299     }
2300     return false;
2301 }
2302 
UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,bool isJump)2303 float ListPattern::UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, bool isJump)
2304 {
2305     if (prevMeasureBreak_) {
2306         return 0;
2307     }
2308     float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
2309     double prevOffset = currentOffset_;
2310     if (childrenSize_) {
2311         listTotalHeight_ = posMap_->GetTotalHeight();
2312         currentOffset_ = itemPosition_.empty() ? 0.0f :
2313             posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos);
2314     } else {
2315         if (isJump && !needReEstimateOffset_) {
2316             if (CalculateJumpOffset()) {
2317                 relativeOffset = 0.0f;
2318             } else {
2319                 needReEstimateOffset_ = true;
2320             }
2321         }
2322         if (needReEstimateOffset_) {
2323             posMap_->ClearPosMap();
2324             auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis(), itemStartIndex_);
2325             calculate.SetPosMap(posMap_, true);
2326             calculate.GetEstimateHeightAndOffset(GetHost());
2327             currentOffset_ = calculate.GetEstimateOffset();
2328             listTotalHeight_ = calculate.GetEstimateHeight();
2329             relativeOffset = 0;
2330             needReEstimateOffset_ = false;
2331             heightEstimated_ = true;
2332         }
2333         CalculateCurrentOffset(relativeOffset, listLayoutAlgorithm->GetRecycledItemPosition());
2334     }
2335     CheckAndUpdateAnimateTo(relativeOffset, prevOffset);
2336 
2337     return currentOffset_ - prevOffset;
2338 }
2339 
CheckAndUpdateAnimateTo(float relativeOffset,float prevOffset)2340 void ListPattern::CheckAndUpdateAnimateTo(float relativeOffset, float prevOffset)
2341 {
2342     if (!scrollTarget_) {
2343         return;
2344     }
2345     auto& target = scrollTarget_.value();
2346     auto posInfo = posMap_->GetPositionInfo(target.index);
2347     if (Negative(posInfo.mainPos)) {
2348         return;
2349     }
2350     float startPos = posInfo.mainPos - currentOffset_;
2351     float targetPos = 0.0f;
2352     GetListItemAnimatePos(startPos + target.extraOffset, startPos + posInfo.mainSize + target.extraOffset,
2353         target.align, targetPos);
2354     targetPos += currentOffset_;
2355     const float epsilon = 0.1f;
2356     if (!NearEqual(relativeOffset + prevOffset, currentOffset_, epsilon) ||
2357         !NearEqual(target.targetOffset, targetPos, epsilon)) {
2358         target.targetOffset = targetPos;
2359         if (NearEqual(currentOffset_, targetPos)) {
2360             StopAnimate();
2361         } else {
2362             AnimateTo(targetPos, -1, nullptr, true, false, false);
2363         }
2364     }
2365 }
2366 
CalculateCurrentOffset(float delta,const ListLayoutAlgorithm::PositionMap & recycledItemPosition)2367 void ListPattern::CalculateCurrentOffset(float delta, const ListLayoutAlgorithm::PositionMap& recycledItemPosition)
2368 {
2369     posMap_->UpdateTotalCount(maxListItemIndex_ + 1);
2370     if (itemPosition_.empty()) {
2371         return;
2372     }
2373     auto itemPos = itemPosition_;
2374     for (auto& [index, pos] : recycledItemPosition) {
2375         itemPos.try_emplace(index, pos);
2376     }
2377     float startPos = itemPos.begin()->second.startPos;
2378     int32_t startIndex = itemPos.begin()->first;
2379     auto& groupInfo = itemPos.begin()->second.groupInfo;
2380     bool groupAtStart = (!groupInfo || groupInfo.value().atStart);
2381     if (startIndex == 0 && groupAtStart) {
2382         // startPos has a small accuracy error.
2383         currentOffset_ = NearZero(startPos) ? 0.0f : -startPos;
2384         UpdatePosMap(itemPos);
2385         return;
2386     }
2387     currentOffset_ += delta;
2388     auto res = posMap_->GetStartIndexAndPos();
2389     if (res.first >= 0) {
2390         auto iter = itemPos.lower_bound(res.first);
2391         // skip same line for lanes
2392         if (iter->first == res.first && iter != itemPos.end()) {
2393             do {
2394                 startPos = iter->second.startPos;
2395                 iter++;
2396             } while (iter != itemPos.end() && NearEqual(iter->second.startPos, startPos));
2397         }
2398         if (iter != itemPos.end()) {
2399             groupAtStart = iter->second.groupInfo ? iter->second.groupInfo.value().atStart : true;
2400             startPos = iter->second.startPos;
2401             posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, iter->first, startPos, groupAtStart);
2402         }
2403     }
2404     UpdatePosMap(itemPos);
2405 }
2406 
UpdatePosMap(const ListLayoutAlgorithm::PositionMap & itemPos)2407 void ListPattern::UpdatePosMap(const ListLayoutAlgorithm::PositionMap& itemPos)
2408 {
2409     for (auto& [index, pos] : itemPos) {
2410         float height = pos.endPos - pos.startPos;
2411         if (pos.groupInfo) {
2412             bool groupAtStart = pos.groupInfo.value().atStart;
2413             bool groupAtEnd = pos.groupInfo.value().atEnd;
2414             if (groupAtStart && groupAtEnd) {
2415                 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2416             } else {
2417                 posMap_->UpdatePosWithCheck(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2418             }
2419         } else {
2420             posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2421         }
2422     }
2423     auto& endGroupInfo = itemPos.rbegin()->second.groupInfo;
2424     bool groupAtEnd = (!endGroupInfo || endGroupInfo.value().atEnd);
2425     posMap_->UpdatePosMapEnd(itemPos.rbegin()->first, spaceWidth_, groupAtEnd);
2426 }
2427 
UpdateChildPosInfo(int32_t index,float delta,float sizeChange)2428 void ListPattern::UpdateChildPosInfo(int32_t index, float delta, float sizeChange)
2429 {
2430     if (itemPosition_.find(index) == itemPosition_.end()) {
2431         return;
2432     }
2433     auto posInfo = posMap_->GetPositionInfo(index);
2434     auto prevPosInfo = posMap_->GetPositionInfo(index - 1);
2435     delta = isStackFromEnd_ ? -(delta + sizeChange) : delta;
2436     if (Negative(prevPosInfo.mainPos)) {
2437         posMap_->UpdatePos(index, {posInfo.mainPos + delta, posInfo.mainSize + sizeChange, posInfo.isGroup});
2438     }
2439     if (index == GetStartIndex()) {
2440         sizeChange += delta;
2441         float startPos = itemPosition_.begin()->second.startPos;
2442         auto iter = itemPosition_.begin();
2443         while (iter != itemPosition_.end() && NearEqual(startPos, iter->second.startPos)) {
2444             iter->second.startPos += delta;
2445             iter++;
2446         }
2447     }
2448     if (index == GetEndIndex()) {
2449         float endPos = itemPosition_.rbegin()->second.endPos;
2450         auto iter = itemPosition_.rbegin();
2451         while (iter != itemPosition_.rend() && NearEqual(endPos, iter->second.endPos)) {
2452             iter->second.endPos += sizeChange;
2453             iter++;
2454         }
2455     }
2456     CalculateCurrentOffset(0.0f, ListLayoutAlgorithm::PositionMap());
2457 }
2458 
UpdateScrollBarOffset()2459 void ListPattern::UpdateScrollBarOffset()
2460 {
2461     CheckScrollBarOff();
2462     if (!GetScrollBar() && !GetScrollBarProxy()) {
2463         heightEstimated_ = false;
2464         return;
2465     }
2466     auto host = GetHost();
2467     CHECK_NULL_VOID(host);
2468     const auto& geometryNode = host->GetGeometryNode();
2469     auto frameSize = geometryNode->GetFrameSize();
2470     Size size(frameSize.Width(), frameSize.Height());
2471     if (itemPosition_.empty()) {
2472         UpdateScrollBarRegion(0.f, 0.f, size, Offset(0.0f, 0.0f));
2473         heightEstimated_ = false;
2474         return;
2475     }
2476     float currentOffset = 0.0f;
2477     float estimatedHeight = 0.0f;
2478     if (childrenSize_ || heightEstimated_) {
2479         currentOffset = currentOffset_;
2480         estimatedHeight = listTotalHeight_;
2481         heightEstimated_ = false;
2482     } else {
2483         auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis(), itemStartIndex_);
2484         calculate.SetPosMap(posMap_);
2485         calculate.GetEstimateHeightAndOffset(GetHost());
2486         currentOffset = calculate.GetEstimateOffset();
2487         estimatedHeight = calculate.GetEstimateHeight();
2488     }
2489     if (GetAlwaysEnabled()) {
2490         estimatedHeight = estimatedHeight - spaceWidth_;
2491     }
2492 
2493     // calculate padding offset of list
2494     auto layoutPriority = host->GetLayoutProperty();
2495     CHECK_NULL_VOID(layoutPriority);
2496     auto padding = layoutPriority->CreatePaddingAndBorder();
2497     auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
2498 
2499     auto mainSize = GetAxis() == Axis::VERTICAL ? size.Height() : size.Width();
2500     if (IsNeedAddContentOffset(LessOrEqual(estimatedHeight + paddingMain, mainSize))) {
2501         currentOffset += contentStartOffset_;
2502         estimatedHeight += contentStartOffset_ + contentEndOffset_;
2503     }
2504     float expandHeight = ScrollableUtils::CheckHeightExpansion(layoutPriority, GetAxis());
2505     estimatedHeight -= expandHeight;
2506 
2507     UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
2508 }
2509 
GetTotalHeight() const2510 float ListPattern::GetTotalHeight() const
2511 {
2512     auto currentOffset = GetTotalOffset();
2513     if (endIndex_ >= maxListItemIndex_) {
2514         return currentOffset + endMainPos_ + contentEndOffset_;
2515     }
2516     if (itemPosition_.empty()) {
2517         return 0.0f;
2518     }
2519     int32_t remainCount = maxListItemIndex_ - endIndex_;
2520     float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
2521     float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
2522     return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
2523 }
2524 
TriggerModifyDone()2525 void ListPattern::TriggerModifyDone()
2526 {
2527     OnModifyDone();
2528 }
2529 
SetChainAnimationCallback()2530 void ListPattern::SetChainAnimationCallback()
2531 {
2532     CHECK_NULL_VOID(chainAnimation_);
2533     chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
2534         auto list = weak.Upgrade();
2535         CHECK_NULL_VOID(list);
2536         list->MarkDirtyNodeSelf();
2537     });
2538     auto scrollEffect = AceType::DynamicCast<ScrollSpringEffect>(GetScrollEdgeEffect());
2539     CHECK_NULL_VOID(scrollEffect);
2540     scrollEffect->SetOnWillStartSpringCallback([weak = AceType::WeakClaim(this)]() {
2541         auto list = weak.Upgrade();
2542         CHECK_NULL_VOID(list);
2543         if (!list->dragFromSpring_ && list->chainAnimation_) {
2544             auto delta = list->chainAnimation_->SetControlIndex(list->IsAtTop() ? 0 : list->maxListItemIndex_);
2545             list->currentDelta_ -= delta;
2546             list->dragFromSpring_ = true;
2547         }
2548     });
2549 }
2550 
SetChainAnimation()2551 void ListPattern::SetChainAnimation()
2552 {
2553     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2554     CHECK_NULL_VOID(listLayoutProperty);
2555     auto edgeEffect = GetEdgeEffect();
2556     int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
2557     bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
2558     bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
2559     bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
2560     if (!enable || GetEffectEdge() != EffectEdge::ALL) {
2561         chainAnimation_.Reset();
2562         return;
2563     }
2564     auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2565     if (Negative(space)) {
2566         space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2567     }
2568     if (!chainAnimation_ || (chainAnimation_ && space != chainAnimation_->GetSpace())) {
2569         springProperty_ =
2570             AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
2571         if (chainAnimationOptions_.has_value()) {
2572             float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
2573             float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
2574             minSpace = Negative(minSpace) ? 0.0f : minSpace;
2575             minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2576             maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2577             springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2578             springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2579             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2580             auto conductivity = chainAnimationOptions_.value().conductivity;
2581             if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2582                 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2583             }
2584             chainAnimation_->SetConductivity(conductivity);
2585             auto intensity = chainAnimationOptions_.value().intensity;
2586             if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2587                 intensity = ChainAnimation::DEFAULT_INTENSITY;
2588             }
2589             chainAnimation_->SetIntensity(intensity);
2590             auto effect = chainAnimationOptions_.value().edgeEffect;
2591             chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2592         } else {
2593             auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
2594             auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
2595             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2596         }
2597         SetChainAnimationCallback();
2598     }
2599 }
2600 
SetChainAnimationOptions(const ChainAnimationOptions & options)2601 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
2602 {
2603     chainAnimationOptions_ = options;
2604     if (chainAnimation_) {
2605         auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2606         CHECK_NULL_VOID(listLayoutProperty);
2607         auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2608         if (Negative(space)) {
2609             space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2610         }
2611         float maxSpace = options.maxSpace.ConvertToPx();
2612         float minSpace = options.minSpace.ConvertToPx();
2613         minSpace = Negative(minSpace) ? 0.0f : minSpace;
2614         minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2615         maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2616         chainAnimation_->SetSpace(space, maxSpace, minSpace);
2617         auto conductivity = chainAnimationOptions_.value().conductivity;
2618         if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2619             conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2620         }
2621         chainAnimation_->SetConductivity(conductivity);
2622         auto intensity = chainAnimationOptions_.value().intensity;
2623         if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2624             intensity = ChainAnimation::DEFAULT_INTENSITY;
2625         }
2626         chainAnimation_->SetIntensity(intensity);
2627         auto effect = options.edgeEffect;
2628         chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2629     }
2630     if (springProperty_) {
2631         springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2632         springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2633     }
2634 }
2635 
OnTouchDown(const TouchEventInfo & info)2636 void ListPattern::OnTouchDown(const TouchEventInfo& info)
2637 {
2638     ScrollablePattern::OnTouchDown(info);
2639     auto& touches = info.GetTouches();
2640     if (touches.empty()) {
2641         return;
2642     }
2643     auto offset = touches.front().GetLocalLocation();
2644     float startPosition = GetAxis() == Axis::HORIZONTAL ? offset.GetX() : offset.GetY();
2645     ProcessDragStart(startPosition);
2646 }
2647 
ProcessDragStart(float startPosition)2648 void ListPattern::ProcessDragStart(float startPosition)
2649 {
2650     CHECK_NULL_VOID(chainAnimation_);
2651     auto host = GetHost();
2652     CHECK_NULL_VOID(host);
2653     auto globalOffset = host->GetTransformRelativeOffset();
2654     int32_t index = -1;
2655     auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
2656     auto it = std::find_if(
2657         itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
2658     if (it != itemPosition_.end()) {
2659         index = it->first;
2660     } else if (!itemPosition_.empty()) {
2661         index = itemPosition_.rbegin()->first + 1;
2662     }
2663     dragFromSpring_ = false;
2664     float delta = chainAnimation_->SetControlIndex(index);
2665     currentDelta_ -= delta;
2666     chainAnimation_->SetMaxIndex(maxListItemIndex_);
2667 }
2668 
ProcessDragUpdate(float dragOffset,int32_t source)2669 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
2670 {
2671     CHECK_NULL_VOID(chainAnimation_);
2672     if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR_FLING) {
2673         return;
2674     }
2675     if (NeedScrollSnapAlignEffect()) {
2676         auto delta = 0.0f;
2677         if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
2678             delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
2679         }
2680         if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
2681             delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
2682         }
2683         if (!NearZero(delta)) {
2684             auto scrollableEvent = GetScrollableEvent();
2685             CHECK_NULL_VOID(scrollableEvent);
2686             auto scrollable = scrollableEvent->GetScrollable();
2687             CHECK_NULL_VOID(scrollable);
2688             scrollable->UpdateScrollSnapStartOffset(delta);
2689             currentDelta_ -= delta;
2690         }
2691     }
2692     float overOffset = 0.0f;
2693     if (!itemPosition_.empty()) {
2694         auto res = GetOutBoundaryOffset(0.0f, true);
2695         overOffset = std::max(res.start, res.end);
2696         if (!NearZero(res.end)) {
2697             overOffset = -overOffset;
2698         }
2699     }
2700     if (source == SCROLL_FROM_UPDATE && !NearZero(overOffset)) {
2701         dragOffset = 0.0f;
2702     }
2703     chainAnimation_->SetDelta(-dragOffset, overOffset);
2704     if (source == SCROLL_FROM_UPDATE && GetCanOverScroll()) {
2705         float tempDelta = currentDelta_;
2706         currentDelta_ -= dragOffset;
2707         bool isAtEdge = IsAtTop() || IsAtBottom(true);
2708         currentDelta_ = tempDelta;
2709         SetCanOverScroll(isAtEdge);
2710     }
2711 }
2712 
GetChainDelta(int32_t index) const2713 float ListPattern::GetChainDelta(int32_t index) const
2714 {
2715     CHECK_NULL_RETURN(chainAnimation_, 0.0f);
2716     float chainDelta = chainAnimation_->GetValue(index);
2717     auto pipeline = GetContext();
2718     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
2719         chainDelta = Round(chainDelta);
2720     }
2721     return chainDelta;
2722 }
2723 
MultiSelectWithoutKeyboard(const RectF & selectedZone)2724 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
2725 {
2726     auto host = GetHost();
2727     CHECK_NULL_VOID(host);
2728     std::list<RefPtr<FrameNode>> childrens;
2729     host->GenerateOneDepthVisibleFrame(childrens);
2730     for (const auto& item : childrens) {
2731         if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2732             auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
2733             CHECK_NULL_VOID(itemGroupPattern);
2734             auto itemGroupGeometry = item->GetGeometryNode();
2735             CHECK_NULL_VOID(itemGroupGeometry);
2736             auto itemGroupRect = itemGroupGeometry->GetFrameRect();
2737             if (!selectedZone.IsIntersectWith(itemGroupRect)) {
2738                 continue;
2739             }
2740             HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.GetOffset());
2741             continue;
2742         }
2743         auto itemPattern = item->GetPattern<ListItemPattern>();
2744         CHECK_NULL_VOID(itemPattern);
2745         if (!itemPattern->Selectable()) {
2746             continue;
2747         }
2748 
2749         auto itemGeometry = item->GetGeometryNode();
2750         CHECK_NULL_VOID(itemGeometry);
2751 
2752         auto itemRect = itemGeometry->GetFrameRect();
2753         if (!selectedZone.IsIntersectWith(itemRect)) {
2754             itemPattern->MarkIsSelected(false);
2755         } else {
2756             itemPattern->MarkIsSelected(true);
2757         }
2758     }
2759 
2760     DrawSelectedZone(selectedZone);
2761 }
2762 
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,const OffsetF & groupOffset)2763 void ListPattern::HandleCardModeSelectedEvent(
2764     const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, const OffsetF& groupOffset)
2765 {
2766     CHECK_NULL_VOID(itemGroupNode);
2767     std::list<RefPtr<FrameNode>> childrens;
2768     itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
2769     for (const auto& item : childrens) {
2770         auto itemPattern = item->GetPattern<ListItemPattern>();
2771         if (!itemPattern) {
2772             continue;
2773         }
2774         if (!itemPattern->Selectable()) {
2775             continue;
2776         }
2777         auto itemGeometry = item->GetGeometryNode();
2778         CHECK_NULL_VOID(itemGeometry);
2779         auto context = item->GetRenderContext();
2780         CHECK_NULL_VOID(context);
2781         auto itemRect = itemGeometry->GetFrameRect();
2782         RectF itemRectInGroup(itemRect.GetX() + groupOffset.GetX(),
2783             itemRect.GetY() + groupOffset.GetY(), itemRect.Width(), itemRect.Height());
2784         if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
2785             itemPattern->MarkIsSelected(false);
2786         } else {
2787             itemPattern->MarkIsSelected(true);
2788         }
2789     }
2790 }
2791 
ClearMultiSelect()2792 void ListPattern::ClearMultiSelect()
2793 {
2794     auto host = GetHost();
2795     CHECK_NULL_VOID(host);
2796     std::list<RefPtr<FrameNode>> children;
2797     host->GenerateOneDepthAllFrame(children);
2798     for (const auto& child : children) {
2799         if (!child) {
2800             continue;
2801         }
2802         auto itemPattern = child->GetPattern<ListItemPattern>();
2803         if (itemPattern) {
2804             itemPattern->MarkIsSelected(false);
2805             continue;
2806         }
2807         auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
2808         if (itemGroupPattern) {
2809             std::list<RefPtr<FrameNode>> itemChildren;
2810             child->GenerateOneDepthAllFrame(itemChildren);
2811             for (const auto& item : itemChildren) {
2812                 if (!item) {
2813                     continue;
2814                 }
2815                 itemPattern = item->GetPattern<ListItemPattern>();
2816                 if (itemPattern) {
2817                     itemPattern->MarkIsSelected(false);
2818                 }
2819             }
2820         }
2821     }
2822 
2823     ClearSelectedZone();
2824 }
2825 
IsItemSelected(float offsetX,float offsetY)2826 bool ListPattern::IsItemSelected(float offsetX, float offsetY)
2827 {
2828     auto host = GetHost();
2829     CHECK_NULL_RETURN(host, false);
2830     auto node = host->FindChildByPosition(offsetX, offsetY);
2831     CHECK_NULL_RETURN(node, false);
2832     auto itemPattern = node->GetPattern<ListItemPattern>();
2833     if (itemPattern) {
2834         return itemPattern->IsSelected();
2835     }
2836     auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
2837     if (itemGroupPattern) {
2838         auto itemNode = node->FindChildByPosition(offsetX, offsetY);
2839         CHECK_NULL_RETURN(itemNode, false);
2840         itemPattern = itemNode->GetPattern<ListItemPattern>();
2841         CHECK_NULL_RETURN(itemPattern, false);
2842         return itemPattern->IsSelected();
2843     }
2844     return false;
2845 }
2846 
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)2847 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
2848 {
2849     // swiper item only can be replaced when no other items be dragged
2850     if (canReplaceSwiperItem_) {
2851         if (swiperItem != swiperItem_) {
2852             auto item = swiperItem_.Upgrade();
2853             if (item) {
2854                 item->ResetSwipeStatus();
2855             }
2856             swiperItem_ = std::move(swiperItem);
2857         }
2858         canReplaceSwiperItem_ = false;
2859     }
2860     FireAndCleanScrollingListener();
2861 }
2862 
GetItemIndexByPosition(float xOffset,float yOffset)2863 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
2864 {
2865     auto host = GetHost();
2866     CHECK_NULL_RETURN(host, GetStartIndex());
2867     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
2868     CHECK_NULL_RETURN(listProperty, GetStartIndex());
2869     auto globalOffset = host->GetTransformRelativeOffset();
2870     float relativeX = xOffset - globalOffset.GetX();
2871     float relativeY = yOffset - globalOffset.GetY();
2872     float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
2873     float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
2874     float mainSize = GetMainAxisSize(GetContentSize(), GetAxis());
2875     float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
2876     if (TextDirection::RTL == listProperty->GetNonAutoLayoutDirection()) {
2877         if (Axis::VERTICAL == GetAxis()) {
2878             crossOffset = crossSize - crossOffset;
2879         } else {
2880             mainOffset = mainSize - mainOffset;
2881         }
2882     }
2883     int32_t lanesOffset = 0;
2884     if (lanes_ > 1 && !NearZero(crossSize + laneGutter_)) {
2885         lanesOffset = static_cast<int32_t>(crossOffset / ((crossSize + laneGutter_) / lanes_));
2886     }
2887     for (auto& pos : itemPosition_) {
2888         if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2889             return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
2890         }
2891     }
2892     if (!itemPosition_.empty()) {
2893         return itemPosition_.rbegin()->first + 1;
2894     }
2895     return 0;
2896 }
2897 
FocusWrapModeToString(FocusWrapMode mode)2898 std::string static FocusWrapModeToString(FocusWrapMode mode)
2899 {
2900     switch (mode) {
2901         case FocusWrapMode::WRAP_WITH_ARROW:
2902             return "WRAP_WITH_ARROW";
2903         default:
2904             return "DEFAULT";
2905     }
2906 }
2907 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2908 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2909 {
2910     ScrollablePattern::ToJsonValue(json, filter);
2911     /* no fixed attr below, just return */
2912     if (filter.IsFastFilter()) {
2913         return;
2914     }
2915     json->PutExtAttr("multiSelectable", multiSelectable_, filter);
2916     json->PutExtAttr("startIndex", startIndex_, filter);
2917     if (!itemPosition_.empty()) {
2918         json->PutExtAttr("itemStartPos", itemPosition_.begin()->second.startPos, filter);
2919     }
2920     json->PutExtAttr("maintainVisibleContentPosition", maintainVisibleContentPosition_, filter);
2921     auto nestedScrollOptions = JsonUtil::Create(true);
2922     auto nestedScroll = GetNestedScroll();
2923     nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
2924     nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
2925     json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
2926     json->PutExtAttr("focusWrapMode", FocusWrapModeToString(focusWrapMode_).c_str(), filter);
2927 }
2928 
FromJson(const std::unique_ptr<JsonValue> & json)2929 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
2930 {
2931     ScrollToIndex(json->GetInt("startIndex"));
2932     if (json->Contains("itemStartPos")) {
2933         ScrollBy(-json->GetDouble("itemStartPos"));
2934     }
2935     auto host = GetHost();
2936     CHECK_NULL_VOID(host);
2937     host->GetRenderContext()->UpdateClipEdge(true);
2938     ScrollablePattern::FromJson(json);
2939 }
2940 
GetListItemGroupParameter(const RefPtr<FrameNode> & node)2941 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
2942 {
2943     ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
2944     auto curFrameParent = node->GetParent();
2945     auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2946     while (curFrameParent && (!curFrameParentNode)) {
2947         curFrameParent = curFrameParent->GetParent();
2948         curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2949     }
2950     CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
2951     if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2952         auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
2953         CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
2954         listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
2955         listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2956         listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2957         listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2958         listItemGroupPara.hasHeader = itemGroupPattern->IsHasHeader();
2959         listItemGroupPara.hasFooter = itemGroupPattern->IsHasFooter();
2960     }
2961     return listItemGroupPara;
2962 }
2963 
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2964 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2965 {
2966     auto listFrame = GetHost();
2967     CHECK_NULL_RETURN(listFrame, false);
2968     auto listFocus = listFrame->GetFocusHub();
2969     CHECK_NULL_RETURN(listFocus, false);
2970     bool isItemGroup = false;
2971     listFocus->AnyChildFocusHub([&isItemGroup, &node, listIndex](const RefPtr<FocusHub>& childFocus) {
2972         if (!childFocus->IsFocusable()) {
2973             return false;
2974         }
2975         if (auto childFrame = childFocus->GetFrameNode()) {
2976             if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2977                 auto curIndex = childPattern->GetIndexInList();
2978                 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2979                 if (curIndex == listIndex) {
2980                     node = childFrame;
2981                     isItemGroup = curIndexInGroup > -1;
2982                     return true;
2983                 }
2984             }
2985         }
2986         return false;
2987     });
2988     return isItemGroup;
2989 }
2990 
RefreshLanesItemRange()2991 void ListPattern::RefreshLanesItemRange()
2992 {
2993     auto host = GetHost();
2994     CHECK_NULL_VOID(host);
2995     auto updatePos = host->GetChildrenUpdated();
2996     if (updatePos == -1) {
2997         return;
2998     }
2999     host->ChildrenUpdatedFrom(-1);
3000     if (updatePos == 0) {
3001         lanesItemRange_.clear();
3002         return;
3003     }
3004     for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
3005         if (it->second < updatePos) {
3006             it++;
3007         } else if (it->first >= updatePos) {
3008             it = lanesItemRange_.erase(it);
3009         } else {
3010             it->second = updatePos - 1;
3011             it++;
3012         }
3013     }
3014 }
3015 
ProvideRestoreInfo()3016 std::string ListPattern::ProvideRestoreInfo()
3017 {
3018     return std::to_string(startIndex_);
3019 }
3020 
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)3021 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
3022 {
3023     auto item = swiperItem_.Upgrade();
3024     if (item) {
3025         return item->CloseSwipeAction(std::move(onFinishCallback));
3026     }
3027 }
3028 
OnRestoreInfo(const std::string & restoreInfo)3029 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
3030 {
3031     jumpIndex_ = StringUtils::StringToInt(restoreInfo);
3032 }
3033 
DumpAdvanceInfo()3034 void ListPattern::DumpAdvanceInfo()
3035 {
3036     ScrollablePattern::DumpAdvanceInfo();
3037     DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
3038     DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
3039     DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
3040     DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
3041     DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
3042     DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
3043     DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
3044     DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
3045     DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
3046     DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
3047     DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
3048     DumpLog::GetInstance().AddDesc("stackFromEnd:" + std::to_string(isStackFromEnd_));
3049     crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
3050                      : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
3051     smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
3052     if (jumpIndex_.has_value()) {
3053         DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
3054     } else {
3055         DumpLog::GetInstance().AddDesc("jumpIndex:null");
3056     }
3057     if (jumpIndexInGroup_.has_value()) {
3058         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
3059     } else {
3060         DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
3061     }
3062     if (targetIndex_.has_value()) {
3063         DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
3064     } else {
3065         DumpLog::GetInstance().AddDesc("targetIndex:null");
3066     }
3067     if (predictSnapOffset_.has_value()) {
3068         DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
3069     } else {
3070         DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
3071     }
3072     if (predictSnapEndPos_.has_value()) {
3073         DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
3074     } else {
3075         DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
3076     }
3077     // DumpLog::GetInstance().AddDesc("scrollAlign:%{public}d", scrollAlign_);
3078     paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
3079                     : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
3080     isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
3081                             : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
3082     for (auto item : itemPosition_) {
3083         DumpLog::GetInstance().AddDesc("------------------------------------------");
3084         DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
3085         DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
3086         DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
3087         DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
3088     }
3089     DumpLog::GetInstance().AddDesc("------------------------------------------");
3090     scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
3091                 : DumpLog::GetInstance().AddDesc("scrollStop:false");
3092     for (auto item : lanesItemRange_) {
3093         DumpLog::GetInstance().AddDesc("------------------------------------------");
3094         DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
3095         DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
3096     }
3097     DumpLog::GetInstance().AddDesc("------------------------------------------");
3098     DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
3099     DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
3100     dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
3101                     : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
3102     isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
3103                  : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
3104     IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
3105     IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
3106                  : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
3107 }
3108 
GetEventDumpInfo()3109 void ListPattern::GetEventDumpInfo()
3110 {
3111     ScrollablePattern::GetEventDumpInfo();
3112     auto host = GetHost();
3113     CHECK_NULL_VOID(host);
3114     auto hub = host->GetOrCreateEventHub<ListEventHub>();
3115     CHECK_NULL_VOID(hub);
3116     auto onScrollIndex = hub->GetOnScrollIndex();
3117     onScrollIndex ? DumpLog::GetInstance().AddDesc("hasOnScrollIndex: true")
3118                   : DumpLog::GetInstance().AddDesc("hasOnScrollIndex: false");
3119     auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnListScrollIndex();
3120     onJSFrameNodeScrollIndex ? DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: true")
3121                              : DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: false");
3122     auto onScrollVisibleContentChange = hub->GetOnScrollVisibleContentChange();
3123     onScrollVisibleContentChange ? DumpLog::GetInstance().AddDesc("hasOnScrollChange: true")
3124                                  : DumpLog::GetInstance().AddDesc("hasOnScrollChange: false");
3125     auto onJSFrameNodeScrollVisibleContentChange = hub->GetJSFrameNodeOnScrollVisibleContentChange();
3126     onJSFrameNodeScrollVisibleContentChange
3127         ? DumpLog::GetInstance().AddDesc("nodeOnScrollChange: true")
3128         : DumpLog::GetInstance().AddDesc("nodeOnScrollChange: false");
3129 }
3130 
GetEventDumpInfo(std::unique_ptr<JsonValue> & json)3131 void ListPattern::GetEventDumpInfo(std::unique_ptr<JsonValue>& json)
3132 {
3133     ScrollablePattern::GetEventDumpInfo(json);
3134     auto host = GetHost();
3135     CHECK_NULL_VOID(host);
3136     auto hub = host->GetOrCreateEventHub<ListEventHub>();
3137     CHECK_NULL_VOID(hub);
3138     auto onScrollIndex = hub->GetOnScrollIndex();
3139     json->Put("hasOnScrollIndex", onScrollIndex ? "true" : "false");
3140     auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnListScrollIndex();
3141     json->Put("nodeOnScrollIndex", onJSFrameNodeScrollIndex ? "true" : "false");
3142     auto onScrollVisibleContentChange = hub->GetOnScrollVisibleContentChange();
3143     json->Put("hasOnScrollChange", onScrollVisibleContentChange ? "true" : "false");
3144     auto onJSFrameNodeScrollVisibleContentChange = hub->GetJSFrameNodeOnScrollVisibleContentChange();
3145     json->Put("nodeOnScrollChange", onJSFrameNodeScrollVisibleContentChange ? "true" : "false");
3146 }
3147 
GetDefaultScrollBarDisplayMode() const3148 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
3149 {
3150     auto defaultDisplayMode = DisplayMode::OFF;
3151     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
3152         defaultDisplayMode = DisplayMode::AUTO;
3153     }
3154     return defaultDisplayMode;
3155 }
3156 
GetVisibleSelectedItems()3157 std::vector<RefPtr<FrameNode>> ListPattern::GetVisibleSelectedItems()
3158 {
3159     std::vector<RefPtr<FrameNode>> children;
3160     auto host = GetHost();
3161     CHECK_NULL_RETURN(host, children);
3162     for (int32_t index = startIndex_; index <= endIndex_; ++index) {
3163         auto item = host->GetChildByIndex(index + itemStartIndex_);
3164         if (!AceType::InstanceOf<FrameNode>(item)) {
3165             continue;
3166         }
3167         auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
3168         auto itemPattern = itemFrameNode->GetPattern<ListItemPattern>();
3169         if (!itemPattern) {
3170             continue;
3171         }
3172         if (!itemPattern->IsSelected()) {
3173             continue;
3174         }
3175         children.emplace_back(itemFrameNode);
3176     }
3177     return children;
3178 }
3179 
GetOrCreateListChildrenMainSize()3180 RefPtr<ListChildrenMainSize> ListPattern::GetOrCreateListChildrenMainSize()
3181 {
3182     if (childrenSize_) {
3183         return childrenSize_;
3184     }
3185     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
3186     auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
3187         auto pattern = weakPattern.Upgrade();
3188         CHECK_NULL_VOID(pattern);
3189         auto context = pattern->GetContext();
3190         CHECK_NULL_VOID(context);
3191         context->AddBuildFinishCallBack([weakPattern, change, flag]() {
3192             auto pattern = weakPattern.Upgrade();
3193             CHECK_NULL_VOID(pattern);
3194             pattern->OnChildrenSizeChanged(change, flag);
3195         });
3196         context->RequestFrame();
3197     };
3198     childrenSize_->SetOnDataChange(callback);
3199     auto pipeline = GetContext();
3200     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
3201         childrenSize_->SetIsRoundingMode();
3202     }
3203     return childrenSize_;
3204 }
3205 
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)3206 void ListPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
3207 {
3208     if (!posMap_) {
3209         posMap_ = MakeRefPtr<ListPositionMap>();
3210     }
3211     posMap_->MarkDirty(flag);
3212     MarkDirtyNodeSelf();
3213 }
3214 
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)3215 void ListPattern::SetListChildrenMainSize(float defaultSize, const std::vector<float>& mainSize)
3216 {
3217     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
3218     OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
3219     auto pipeline = GetContext();
3220     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
3221         childrenSize_->SetIsRoundingMode();
3222     }
3223 }
3224 
ResetChildrenSize()3225 void ListPattern::ResetChildrenSize()
3226 {
3227     if (childrenSize_) {
3228         childrenSize_ = nullptr;
3229         MarkDirtyNodeSelf();
3230         OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
3231     }
3232 }
3233 
OnScrollVisibleContentChange(const RefPtr<ListEventHub> & listEventHub,bool indexChanged)3234 void ListPattern::OnScrollVisibleContentChange(const RefPtr<ListEventHub>& listEventHub, bool indexChanged)
3235 {
3236     CHECK_NULL_VOID(listEventHub);
3237     bool startChanged = UpdateStartListItemIndex();
3238     bool endChanged = UpdateEndListItemIndex();
3239     auto onScrollVisibleContentChange = listEventHub->GetOnScrollVisibleContentChange();
3240     auto OnJSFrameNodeScrollVisibleContentChange = listEventHub->GetJSFrameNodeOnScrollVisibleContentChange();
3241     if (onScrollVisibleContentChange) {
3242         if (indexChanged || startChanged || endChanged) {
3243             onScrollVisibleContentChange(startInfo_, endInfo_);
3244             groupIndexChanged_ = true;
3245             ReportOnItemListScrollEvent("onScrollVisibleContentChange", startInfo_.index, endInfo_.index);
3246         }
3247     }
3248     if (OnJSFrameNodeScrollVisibleContentChange) {
3249         if (indexChanged || startChanged || endChanged) {
3250             OnJSFrameNodeScrollVisibleContentChange(startInfo_, endInfo_);
3251         }
3252     }
3253 }
3254 
GetScrollUpdateFriction(float overScroll)3255 float ListPattern::GetScrollUpdateFriction(float overScroll)
3256 {
3257     return ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
3258 }
3259 
NotifyDataChange(int32_t index,int32_t count)3260 void ListPattern::NotifyDataChange(int32_t index, int32_t count)
3261 {
3262     if (focusIndex_.has_value() && focusIndex_.value() >= index) {
3263         focusIndex_ = focusIndex_.value() + count;
3264         if (focusIndex_ < 0) {
3265             focusIndex_ = 0;
3266         }
3267     }
3268 
3269     if (!maintainVisibleContentPosition_ || itemPosition_.empty() || count == 0) {
3270         return;
3271     }
3272     if (index == 0 && count > 0 && IsBackToTopRunning()) {
3273         SetUseTotalOffset(false);
3274     }
3275     auto startIndex = itemPosition_.begin()->first;
3276     auto endIndex = itemPosition_.rbegin()->first;
3277     if (!CheckDataChangeOutOfStart(index, count, startIndex, endIndex)) {
3278         return;
3279     }
3280     count = !isStackFromEnd_ ? std::max(count, index - startIndex) : - std::max(count, endIndex - index);
3281     int32_t mod = 0;
3282     if (count < 0 && lanes_ > 1 && !(itemPosition_.begin()->second.isGroup)) {
3283         mod = -count % lanes_;
3284     }
3285     auto prevPosMap = std::move(itemPosition_);
3286     for (auto &pos : prevPosMap) {
3287         if (mod > 0) {
3288             mod--;
3289         } else {
3290             itemPosition_[pos.first + count] = pos.second;
3291         }
3292     }
3293     needReEstimateOffset_ = true;
3294 }
3295 
CheckDataChangeOutOfStart(int32_t index,int32_t count,int32_t startIndex,int32_t endIndex)3296 bool ListPattern::CheckDataChangeOutOfStart(int32_t index, int32_t count, int32_t startIndex, int32_t endIndex)
3297 {
3298     if (((count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) && !isStackFromEnd_) {
3299         return false;
3300     }
3301     if (((count > 0 && index < endIndex) || (count < 0 && index <= endIndex)) && isStackFromEnd_) {
3302         return false;
3303     }
3304     return true;
3305 }
3306 
CreatePositionInfo(std::unique_ptr<JsonValue> & json)3307 void ListPattern::CreatePositionInfo(std::unique_ptr<JsonValue>& json)
3308 {
3309     std::unique_ptr<JsonValue> children = JsonUtil::CreateArray(true);
3310     for (auto item : itemPosition_) {
3311         std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
3312         child->Put("itemPosition.first", std::to_string(item.first).c_str());
3313         child->Put("startPos", std::to_string(item.second.startPos).c_str());
3314         child->Put("endPos", std::to_string(item.second.endPos).c_str());
3315         child->Put("isGroup", std::to_string(item.second.isGroup).c_str());
3316         children->Put(child);
3317     }
3318     json->Put("itemPosition", children);
3319 }
3320 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)3321 void ListPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
3322 {
3323     ScrollablePattern::DumpAdvanceInfo(json);
3324     json->Put("maxListItemIndex", maxListItemIndex_);
3325     json->Put("startIndex", startIndex_);
3326     json->Put("endIndex", endIndex_);
3327     json->Put("centerIndex", centerIndex_);
3328     json->Put("startMainPos", startMainPos_);
3329     json->Put("endMainPos", endMainPos_);
3330     json->Put("currentOffset", currentOffset_);
3331     json->Put("contentMainSize", contentMainSize_);
3332     json->Put("contentStartOffset", contentStartOffset_);
3333     json->Put("contentEndOffset", contentEndOffset_);
3334     json->Put("currentDelta", currentDelta_);
3335     json->Put("crossMatchChild", crossMatchChild_);
3336     json->Put("smooth", smooth_);
3337     json->Put("jumpIndex", jumpIndex_.has_value() ? std::to_string(jumpIndex_.value()).c_str() : "null");
3338     json->Put(
3339         "jumpIndexInGroup", jumpIndexInGroup_.has_value() ? std::to_string(jumpIndexInGroup_.value()).c_str() : "null");
3340     json->Put("targetIndex", targetIndex_.has_value() ? std::to_string(targetIndex_.value()).c_str() : "null");
3341     json->Put("predictSnapOffset",
3342         predictSnapOffset_.has_value() ? std::to_string(predictSnapOffset_.value()).c_str() : "null");
3343     json->Put("predictSnapEndPos",
3344         predictSnapEndPos_.has_value() ? std::to_string(predictSnapEndPos_.value()).c_str() : "null");
3345     json->Put("paintStateFlag", paintStateFlag_);
3346     json->Put("isFramePaintStateValid", isFramePaintStateValid_);
3347     CreatePositionInfo(json);
3348     json->Put("scrollStop", scrollStop_);
3349     std::unique_ptr<JsonValue> lanesChildrenArray = JsonUtil::CreateArray(true);
3350     for (auto item : lanesItemRange_) {
3351         std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
3352         child->Put("lanesItemRange.first", std::to_string(item.first).c_str());
3353         child->Put("lanesItemRange.second", std::to_string(item.second).c_str());
3354         lanesChildrenArray->Put(child);
3355     }
3356     json->Put("lanesItemRange", lanesChildrenArray);
3357     json->Put("lanes", std::to_string(lanes_).c_str());
3358     json->Put("laneGutter", std::to_string(laneGutter_).c_str());
3359     json->Put("dragFromSpring", dragFromSpring_);
3360     json->Put("isScrollEnd", isScrollEnd_);
3361     json->Put("IsAtTop", IsAtTop());
3362     json->Put("IsAtBottom", IsAtBottom());
3363 }
3364 
GetChildrenExpandedSize()3365 SizeF ListPattern::GetChildrenExpandedSize()
3366 {
3367     auto viewSize = GetViewSizeMinusPadding();
3368     auto axis = GetAxis();
3369     float estimatedHeight = 0.0f;
3370     if (childrenSize_) {
3371         estimatedHeight = listTotalHeight_;
3372     } else if (!itemPosition_.empty()) {
3373         auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, axis, itemStartIndex_);
3374         calculate.SetPosMap(posMap_);
3375         calculate.GetEstimateHeightAndOffset(GetHost());
3376         estimatedHeight = calculate.GetEstimateHeight();
3377     }
3378 
3379     if (axis == Axis::VERTICAL) {
3380         return SizeF(viewSize.Width(), estimatedHeight);
3381     } else if (axis == Axis::HORIZONTAL) {
3382         return SizeF(estimatedHeight, viewSize.Height());
3383     }
3384     return SizeF();
3385 }
3386 
LayoutItemInGroupForFocus(int32_t indexInList,int32_t nextIndexInGroup,int32_t curIndexInGroup,const ListItemGroupPara & listItemGroupPara,int32_t maxListItemIndex)3387 bool ListPattern::LayoutItemInGroupForFocus(int32_t indexInList, int32_t nextIndexInGroup, int32_t curIndexInGroup,
3388     const ListItemGroupPara& listItemGroupPara, int32_t maxListItemIndex)
3389 {
3390     // nextIndexInGroup = -1 indicates the current position is Header; nextIndexInGroup = maxListItemIndex indicates the
3391     // current position is Footer.
3392     // Header and Footer will always be laid out, directly return true.
3393     if (nextIndexInGroup == -1 || nextIndexInGroup == maxListItemIndex) {
3394         return true;
3395     }
3396     ScrollAlign align = ScrollAlign::AUTO;
3397     // The second "||" condition indicates that currently only the footer or header is displayed,
3398     // without displaying any ListItems.
3399     if ((nextIndexInGroup < curIndexInGroup && nextIndexInGroup < listItemGroupPara.displayStartIndex) ||
3400         (nextIndexInGroup < curIndexInGroup &&
3401             listItemGroupPara.displayStartIndex == listItemGroupPara.displayEndIndex)) {
3402         align = ScrollAlign::START;
3403     } else if ((nextIndexInGroup > curIndexInGroup && nextIndexInGroup > listItemGroupPara.displayEndIndex) ||
3404                (nextIndexInGroup > curIndexInGroup &&
3405                    listItemGroupPara.displayStartIndex == listItemGroupPara.displayEndIndex)) {
3406         align = ScrollAlign::END;
3407     } else {
3408         return true;
3409     }
3410 
3411     if (!IsLayout(indexInList, nextIndexInGroup, align)) {
3412         // Do the Layout for indexInList
3413         isLayoutListForFocus_ = true;
3414         targetIndex_ = indexInList;
3415         targetIndexInGroup_ = nextIndexInGroup;
3416         scrollAlign_ = align;
3417         auto pipeline = GetContext();
3418         CHECK_NULL_RETURN(pipeline, false);
3419         MarkDirtyNodeSelf();
3420         pipeline->FlushUITasks();
3421         return false;
3422     }
3423     return true;
3424 }
3425 
LayoutListForFocus(int32_t nextIndex,std::optional<int32_t> indexInGroup)3426 bool ListPattern::LayoutListForFocus(int32_t nextIndex, std::optional<int32_t> indexInGroup)
3427 {
3428     if (!IsLayout(nextIndex, indexInGroup, ScrollAlign::AUTO)) {
3429         isLayoutListForFocus_ = true;
3430         targetIndex_ = nextIndex;
3431         if (nextIndex < startIndex_) {
3432             scrollAlign_ = ScrollAlign::START;
3433         } else if (nextIndex > endIndex_) {
3434             scrollAlign_ = ScrollAlign::END;
3435         }
3436         if (indexInGroup) {
3437             targetIndexInGroup_ = indexInGroup.value();
3438         }
3439         auto pipeline = GetContext();
3440         CHECK_NULL_RETURN(pipeline, false);
3441         MarkDirtyNodeSelf();
3442         pipeline->FlushUITasks();
3443         return false;
3444     }
3445 
3446     return true;
3447 }
3448 
IsLayout(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)3449 bool ListPattern::IsLayout(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
3450 {
3451     auto pipeline = GetContext();
3452     CHECK_NULL_RETURN(pipeline, false);
3453     auto iter = itemPosition_.find(index);
3454     if (iter == itemPosition_.end()) {
3455         return false;
3456     }
3457     float targetPos = 0.0f;
3458     if (iter->second.isGroup) {
3459         if (indexInGroup.has_value()) {
3460             if (!GetListItemGroupAnimatePosWithIndexInGroup(
3461                 index, indexInGroup.value(), iter->second.startPos, align, targetPos)) {
3462                 return false;
3463             }
3464         } else {
3465             if (!GetListItemGroupAnimatePosWithoutIndexInGroup(
3466                 index, iter->second.startPos, iter->second.endPos, align, targetPos)) {
3467                 return false;
3468             }
3469         }
3470     }
3471     return true;
3472 }
3473 
GetNextMoveStepForMultiLanes(int32_t curIndex,FocusStep focusStep,bool isVertical,int32_t & nextIndex)3474 int32_t ListPattern::GetNextMoveStepForMultiLanes(
3475     int32_t curIndex, FocusStep focusStep, bool isVertical, int32_t& nextIndex)
3476 {
3477     // Only for DetermineMultiLaneStep.
3478     // In mixed layout scenarios(List include ListItem and ListItemGroup), when increment nextIndex, if during the loop
3479     // a ListItemGroup is encountered, move to this ListItemGroup directly.
3480 
3481     // Check if focusStep is within a valid range.
3482     bool isDown = true;
3483     if ((isVertical && (focusStep == FocusStep::DOWN)) || (!isVertical && focusStep == FocusStep::RIGHT)) {
3484         isDown = true;
3485     } else if ((isVertical && focusStep == FocusStep::UP) || (!isVertical && focusStep == FocusStep::LEFT)) {
3486         isDown = false;
3487     } else {
3488         return -1;
3489     }
3490 
3491     // Determine moveStep and loop range based on focusStep.
3492     int32_t moveStep = isDown ? lanes_ : -lanes_;
3493     int32_t loopStart = isDown ? curIndex + 1 : curIndex - 1;
3494     int32_t loopEnd = isDown ? curIndex + lanes_ - 1 : curIndex - lanes_ + 1;
3495     int32_t loopStep = isDown ? 1 : -1;
3496 
3497     for (int32_t loopIndex = loopStart; loopStep > 0 ? loopIndex <= loopEnd : loopIndex >= loopEnd;
3498         loopIndex += loopStep) {
3499         // Check if the index exists in itemPosition_ and cachedItemPosition_.
3500         auto it = itemPosition_.find(loopIndex);
3501         auto itCache = cachedItemPosition_.find(loopIndex);
3502         if (it == itemPosition_.end() && itCache == cachedItemPosition_.end()) {
3503             LayoutListForFocus(loopIndex, std::nullopt);
3504             it = itemPosition_.find(loopIndex);
3505             itCache = cachedItemPosition_.find(loopIndex);
3506             if (it == itemPosition_.end() && itCache == cachedItemPosition_.end()) {
3507                 continue;
3508             }
3509         }
3510 
3511         // Check if the index corresponds to a ListItemGroup.
3512         if ((it != itemPosition_.end() && it->second.isGroup) ||
3513             (itCache != cachedItemPosition_.end() && itCache->second.isGroup)) {
3514             nextIndex = loopIndex;
3515             return (focusStep == FocusStep::DOWN || focusStep == FocusStep::RIGHT) ? 1 : -1;
3516         }
3517     }
3518     nextIndex = curIndex + moveStep;
3519     return moveStep;
3520 }
3521 
GetNextFocusNodeInList(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)3522 WeakPtr<FocusHub> ListPattern::GetNextFocusNodeInList(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
3523 {
3524     auto curFocus = currentFocusNode.Upgrade();
3525     CHECK_NULL_RETURN(curFocus, nullptr);
3526     auto curFrame = curFocus->GetFrameNode();
3527     CHECK_NULL_RETURN(curFrame, nullptr);
3528     auto curPattern = curFrame->GetPattern();
3529     CHECK_NULL_RETURN(curPattern, nullptr);
3530     auto curIndex = GetCurrentFocusIndex(curPattern);
3531 
3532     int32_t moveStep = 0;
3533     int32_t nextIndex = curIndex;
3534     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
3535     CHECK_NULL_RETURN(listProperty, nullptr);
3536     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
3537 
3538     AdjustFocusStepForRtl(step, isVertical);
3539 
3540     if (lanes_ <= 1) {
3541         DetermineSingleLaneStep(step, isVertical, curIndex, moveStep, nextIndex);
3542     } else {
3543         DetermineMultiLaneStep(step, isVertical, curIndex, moveStep, nextIndex);
3544     }
3545 
3546     bool loopFlag = true;
3547     while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
3548         if (nextIndex == curIndex) {
3549             return nullptr;
3550         }
3551         LayoutListForFocus(nextIndex, std::nullopt);
3552         auto nextFocusNode = FindChildFocusNodeByIndex(nextIndex, step, curIndex);
3553         auto isDefault = GetFocusWrapMode() == FocusWrapMode::DEFAULT;
3554         if (nextFocusNode.Upgrade()) {
3555             const ListItemInfo* curPos = GetPosition(curIndex);
3556             const ListItemInfo* nextPos = GetPosition(nextIndex);
3557             const bool isForward = (isVertical && step == FocusStep::RIGHT) || (!isVertical && step == FocusStep::DOWN);
3558             const bool isBackward = (isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP);
3559             if ((isForward || isBackward) && NextPositionBlocksMove(curPos, nextPos, isVertical) && isDefault) {
3560                 return nullptr;
3561             }
3562             // Scroll and display the ListItem.
3563             if (IsListItem(nextFocusNode)) {
3564                 AdjustScrollPosition(nextIndex, curIndex);
3565             }
3566             return nextFocusNode;
3567         }
3568         nextIndex += moveStep;
3569         HandleIndexToBounds(nextIndex, loopFlag);
3570     }
3571 
3572     return nullptr;
3573 }
3574 
IsListItemGroupByIndex(int32_t index)3575 bool ListPattern::IsListItemGroupByIndex(int32_t index)
3576 {
3577     if (index < 0) {
3578         return false;
3579     }
3580     auto list = GetHost();
3581     CHECK_NULL_RETURN(list, false);
3582     auto layoutWapper = list->GetChildByIndex(static_cast<uint32_t>(index));
3583     CHECK_NULL_RETURN(layoutWapper, false);
3584     auto frameNode = layoutWapper->GetHostNode();
3585     CHECK_NULL_RETURN(frameNode, false);
3586     return frameNode && frameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
3587 }
3588 
JudgeFocusStep(int32_t tarMainIndex,const FocusStep & step,int32_t curFocusIndex,FocusWrapMode focusWrapMode,bool isVertical)3589 FocusStep static JudgeFocusStep(
3590     int32_t tarMainIndex, const FocusStep& step, int32_t curFocusIndex, FocusWrapMode focusWrapMode, bool isVertical)
3591 {
3592     if (isVertical && focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3593         if (curFocusIndex < tarMainIndex && step == FocusStep::RIGHT) {
3594             return FocusStep::UP_END;
3595         } else if (curFocusIndex > tarMainIndex && step == FocusStep::LEFT) {
3596             return FocusStep::DOWN_END;
3597         }
3598     }
3599     if (!isVertical && focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3600         if (curFocusIndex < tarMainIndex && step == FocusStep::DOWN) {
3601             return FocusStep::LEFT_END;
3602         } else if (curFocusIndex > tarMainIndex && step == FocusStep::UP) {
3603             return FocusStep::RIGHT_END;
3604         }
3605     }
3606     return step;
3607 }
3608 
FindChildFocusNodeByIndex(int32_t tarMainIndex,const FocusStep & step,int32_t curFocusIndex)3609 WeakPtr<FocusHub> ListPattern::FindChildFocusNodeByIndex(
3610     int32_t tarMainIndex, const FocusStep& step, int32_t curFocusIndex)
3611 {
3612     auto focusWrapMode = GetFocusWrapMode();
3613     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
3614     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
3615     CHECK_NULL_RETURN(listProperty, nullptr);
3616     // Only for GetNextFocusNodeInList.
3617     auto listFrame = GetHost();
3618     CHECK_NULL_RETURN(listFrame, nullptr);
3619     auto listFocus = listFrame->GetFocusHub();
3620     CHECK_NULL_RETURN(listFocus, nullptr);
3621     WeakPtr<FocusHub> target;
3622     listFocus->AnyChildFocusHub([&target, tarMainIndex, step, curFocusIndex, focusWrapMode, isVertical](
3623         const RefPtr<FocusHub>& childFocus) {
3624         if (!childFocus->IsFocusable()) {
3625             return false;
3626         }
3627         auto childFrame = childFocus->GetFrameNode();
3628         if (!childFrame) {
3629             return false;
3630         }
3631         auto childPattern = childFrame->GetPattern();
3632         if (!childPattern) {
3633             return false;
3634         }
3635         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
3636         if (!childItemPattern) {
3637             auto childItemGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(childPattern);
3638             CHECK_NULL_RETURN(childItemGroupPattern, false);
3639             if (childItemGroupPattern->GetIndexInList() == tarMainIndex) {
3640                 auto tempStep = JudgeFocusStep(tarMainIndex, step, curFocusIndex, focusWrapMode, isVertical);
3641                 bool isFindTailOrHead = childItemGroupPattern->FindHeadOrTailChild(childFocus, tempStep, target);
3642                 target = isFindTailOrHead ? target : childFocus;
3643                 return true;
3644             }
3645             return false;
3646         }
3647         auto curIndex = childItemPattern->GetIndexInList();
3648         if (curIndex == tarMainIndex) {
3649             auto isFindTailOrHead = childItemPattern->FindHeadOrTailChild(childFocus, step, target);
3650             target = !isFindTailOrHead ? childFocus : target;
3651             return true;
3652         }
3653         return false;
3654     });
3655     return target;
3656 }
3657 
IsForwardStep(FocusStep step,bool isVertical,bool isDefault)3658 bool ListPattern::IsForwardStep(FocusStep step, bool isVertical, bool isDefault)
3659 {
3660     if (step == FocusStep::TAB) {
3661         return true;
3662     }
3663     if (isVertical) {
3664         if ((step == FocusStep::DOWN) || (step == FocusStep::RIGHT && !isDefault)) {
3665             return true;
3666         }
3667     } else {
3668         if ((step == FocusStep::RIGHT) || (step == FocusStep::DOWN && !isDefault)) {
3669             return true;
3670         }
3671     }
3672     return false;
3673 }
3674 
IsBackwardStep(FocusStep step,bool isVertical,bool isDefault)3675 bool ListPattern::IsBackwardStep(FocusStep step, bool isVertical, bool isDefault)
3676 {
3677     if (step == FocusStep::SHIFT_TAB) {
3678         return true;
3679     }
3680     if (isVertical) {
3681         if ((step == FocusStep::UP) || (step == FocusStep::LEFT && !isDefault)) {
3682             return true;
3683         }
3684     } else {
3685         if ((step == FocusStep::LEFT) || (step == FocusStep::UP && !isDefault)) {
3686             return true;
3687         }
3688     }
3689     return false;
3690 }
3691 
DetermineSingleLaneStep(FocusStep step,bool isVertical,int32_t curIndex,int32_t & moveStep,int32_t & nextIndex)3692 void ListPattern::DetermineSingleLaneStep(
3693     FocusStep step, bool isVertical, int32_t curIndex, int32_t& moveStep, int32_t& nextIndex)
3694 {
3695     // Only for GetNextFocusNodeInList
3696     auto isDefault = GetFocusWrapMode() == FocusWrapMode::DEFAULT;
3697     if (step == FocusStep::UP_END || step == FocusStep::LEFT_END) {
3698         moveStep = 1;
3699         nextIndex = 0;
3700         // Clear focus retention flag when HOME/END is pressed
3701         focusIndex_.reset();
3702     } else if (step == FocusStep::DOWN_END || step == FocusStep::RIGHT_END) {
3703         moveStep = -1;
3704         nextIndex = maxListItemIndex_;
3705     } else if (IsForwardStep(step, isVertical, isDefault)) {
3706         moveStep = 1;
3707         nextIndex = curIndex + moveStep;
3708     } else if (IsBackwardStep(step, isVertical, isDefault)) {
3709         moveStep = -1;
3710         nextIndex = curIndex + moveStep;
3711     }
3712 }
3713 
DetermineMultiLaneStep(FocusStep step,bool isVertical,int32_t curIndex,int32_t & moveStep,int32_t & nextIndex)3714 void ListPattern::DetermineMultiLaneStep(
3715     FocusStep step, bool isVertical, int32_t curIndex, int32_t& moveStep, int32_t& nextIndex)
3716 {
3717     // Only for GetNextFocusNodeInList.
3718     if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
3719         moveStep = 1;
3720         nextIndex = 0;
3721         // Clear focus retention flag when HOME/END is pressed
3722         focusIndex_.reset();
3723     } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
3724         moveStep = -1;
3725         nextIndex = maxListItemIndex_;
3726     } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
3727         if (IsListItemGroupByIndex(curIndex)) {
3728             // If the current item is a ListItemGroupPattern, the moveStep must be 1.
3729             moveStep = 1;
3730             nextIndex = nextIndex + moveStep;
3731         } else {
3732             // If the current item is a ListItem, the moveStep needs to be determined.
3733             // In the next step, sequentially check up to lanes-1. If a ListItemGroup is encountered, move only one
3734             // step. Otherwise, proceed according to the lanes.
3735             moveStep = GetNextMoveStepForMultiLanes(curIndex, step, isVertical, nextIndex);
3736         }
3737         nextIndex = AdjustNextIndexForEdgeRow(nextIndex, moveStep, curIndex);
3738     } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
3739         if (IsListItemGroupByIndex(curIndex)) {
3740             // If the current item is a ListItemGroupPattern, the moveStep must be 1.
3741             moveStep = -1;
3742             nextIndex = curIndex + moveStep;
3743         } else {
3744             moveStep = GetNextMoveStepForMultiLanes(curIndex, step, isVertical, nextIndex);
3745         }
3746     } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
3747         moveStep = 1;
3748         nextIndex = GetCrossAxisNextIndex(curIndex, isVertical, moveStep, step);
3749     } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
3750         moveStep = -1;
3751         nextIndex = GetCrossAxisNextIndex(curIndex, isVertical, moveStep, step);
3752     } else if (step == FocusStep::TAB) {
3753         moveStep = 1;
3754         nextIndex = curIndex + 1;
3755     } else if (step == FocusStep::SHIFT_TAB) {
3756         moveStep = -1;
3757         nextIndex = curIndex - 1;
3758     }
3759 }
3760 
GetCurrentFocusIndex(const RefPtr<Pattern> & curPattern)3761 int32_t ListPattern::GetCurrentFocusIndex(const RefPtr<Pattern>& curPattern)
3762 {
3763     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
3764     if (!curItemPattern) {
3765         auto curItemGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(curPattern);
3766         CHECK_NULL_RETURN(curItemGroupPattern, -1);
3767         return curItemGroupPattern->GetIndexInList();
3768     }
3769     return curItemPattern->GetIndexInList();
3770 }
3771 
AdjustFocusStepForRtl(FocusStep & step,bool isVertical)3772 void ListPattern::AdjustFocusStepForRtl(FocusStep& step, bool isVertical)
3773 {
3774     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
3775     bool isRtl = false;
3776     if (listLayoutProperty) {
3777         isRtl = TextDirection::RTL == listLayoutProperty->GetNonAutoLayoutDirection();
3778     }
3779 
3780     // FocusStep LEFT/RIGH reverse flag
3781     bool reverseHorizontal = false;
3782     // FocusStep UP/DOWN reverse flag
3783     bool reverseVertical = false;
3784 
3785     if ((!isRtl && !isVertical && isStackFromEnd_)) {
3786         reverseVertical = true;
3787     } else if ((!isRtl && isVertical && isStackFromEnd_) || (isRtl && !isStackFromEnd_)) {
3788         reverseHorizontal = true;
3789     } else if (isRtl && !isVertical && isStackFromEnd_) {
3790         reverseHorizontal = true;
3791         reverseVertical = true;
3792     }
3793 
3794     if (reverseHorizontal) {
3795         if (step == FocusStep::LEFT) {
3796             step = FocusStep::RIGHT;
3797         } else if (step == FocusStep::RIGHT) {
3798             step = FocusStep::LEFT;
3799         }
3800     }
3801 
3802     if (reverseVertical) {
3803         if (step == FocusStep::UP) {
3804             step = FocusStep::DOWN;
3805         } else if (step == FocusStep::DOWN) {
3806             step = FocusStep::UP;
3807         }
3808     }
3809 }
3810 
IsListItem(const WeakPtr<FocusHub> & focusNode)3811 bool ListPattern::IsListItem(const WeakPtr<FocusHub>& focusNode)
3812 {
3813     // Determine if it is a list item.
3814     auto focusHub = focusNode.Upgrade();
3815     CHECK_NULL_RETURN(focusHub, false);
3816     auto frameNode = focusHub->GetFrameNode();
3817     return frameNode &&
3818            (frameNode->GetTag() == V2::LIST_ITEM_ETS_TAG || frameNode->GetTag() == V2::ARC_LIST_ITEM_ETS_TAG);
3819 }
3820 
AdjustScrollPosition(int32_t nextIndex,int32_t curIndex)3821 void ListPattern::AdjustScrollPosition(int32_t nextIndex, int32_t curIndex)
3822 {
3823     // Only for GetNextFocusNodeInList
3824     // Adjust the scroll position.
3825     if (nextIndex < startIndex_) {
3826         ScrollToIndex(nextIndex, false, ScrollAlign::START);
3827     } else if (nextIndex > endIndex_) {
3828         ScrollToIndex(nextIndex, false, ScrollAlign::END);
3829     } else {
3830         HandleDisplayedChildFocus(nextIndex, curIndex);
3831     }
3832 }
3833 
HandleIndexToBounds(int32_t & nextIndex,bool & loopFlag)3834 void ListPattern::HandleIndexToBounds(int32_t& nextIndex, bool& loopFlag)
3835 {
3836     // Only for GetNextFocusNodeInList.
3837     // Handle boundary conditions.
3838     if ((nextIndex < 0 || nextIndex > maxListItemIndex_) && loopFlag) {
3839         loopFlag = false; // Single loop protection.
3840         nextIndex = (nextIndex < 0) ? 0 : maxListItemIndex_;
3841     }
3842 }
3843 
GetCrossAxisNextIndex(int32_t curIndex,bool isVertical,int32_t moveStep,FocusStep step)3844 int32_t ListPattern::GetCrossAxisNextIndex(int32_t curIndex, bool isVertical, int32_t moveStep, FocusStep step)
3845 {
3846     // Only for DetermineMultiLaneStep
3847     auto focusWrapMode = GetFocusWrapMode();
3848     int32_t nextIndex = curIndex + moveStep;
3849     const bool isForward = (isVertical && step == FocusStep::RIGHT) || (!isVertical && step == FocusStep::DOWN);
3850     const bool isBackward = (isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP);
3851 
3852     if (focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3853         return nextIndex;
3854     }
3855 
3856     if (!isForward && !isBackward) {
3857         return nextIndex;
3858     }
3859 
3860     const bool isGroup = IsListItemGroupByIndex(curIndex);
3861     if (isGroup) {
3862         // Currently at a group or the last column, do not move.
3863         return curIndex;
3864     }
3865 
3866     // Check if the next position is a group or in the same column.
3867     const ListItemInfo* curPos = GetPosition(curIndex);
3868     const ListItemInfo* nextPos = GetPosition(nextIndex);
3869     if (NextPositionBlocksMove(curPos, nextPos, isVertical)) {
3870         return curIndex;
3871     }
3872     return nextIndex;
3873 }
3874 
GetPosition(int32_t index) const3875 const ListItemInfo* ListPattern::GetPosition(int32_t index) const
3876 {
3877     // Only for GetCrossAxisNextIndex
3878     auto it = itemPosition_.find(index);
3879     if (it != itemPosition_.end()) {
3880         return &it->second;
3881     }
3882     auto cachedIt = cachedItemPosition_.find(index);
3883     return (cachedIt != cachedItemPosition_.end()) ? &cachedIt->second : nullptr;
3884 }
3885 
NextPositionBlocksMove(const ListItemInfo * curPos,const ListItemInfo * nextPos,bool isVertical) const3886 bool ListPattern::NextPositionBlocksMove(const ListItemInfo* curPos, const ListItemInfo* nextPos, bool isVertical) const
3887 {
3888     // Only for GetCrossAxisNextIndex, determine if the next position blocks movement.
3889 
3890     if (!nextPos) {
3891         // No position information, allow movement (or handle externally).
3892         return false;
3893     }
3894     if (nextPos->isGroup) {
3895         // The next position is a group, block the movement.
3896         return true;
3897     }
3898 
3899     // Check if the current and next positions are in the same column.
3900     // If the endPos and startPos of two items are the same, it indicates they are in the same row or column, allowing
3901     // focus movement; otherwise, it is considered to have reached the first column (row) or the last column (row),
3902     // disallowing focus movement.
3903     return curPos && (!NearEqual(curPos->endPos, nextPos->endPos) && !NearEqual(curPos->startPos, nextPos->startPos));
3904 }
3905 
AdjustNextIndexForEdgeRow(int32_t nextIndex,int32_t moveStep,int32_t curIndex)3906 int32_t ListPattern::AdjustNextIndexForEdgeRow(int32_t nextIndex, int32_t moveStep, int32_t curIndex)
3907 {
3908     // Only for DetermineMultiLaneStep
3909     if (nextIndex >= maxListItemIndex_ && moveStep == lanes_) {
3910         auto row = maxListItemIndex_ / lanes_;
3911         auto curRow = curIndex / lanes_;
3912         if (curRow == row - 1) {
3913             return maxListItemIndex_;
3914         }
3915     }
3916     return nextIndex;
3917 }
3918 
GetScopeFocusAlgorithm()3919 ScopeFocusAlgorithm ListPattern::GetScopeFocusAlgorithm()
3920 {
3921     auto property = GetLayoutProperty<ListLayoutProperty>();
3922     if (!property) {
3923         return {};
3924     }
3925     return ScopeFocusAlgorithm(property->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL, true,
3926         ScopeType::OTHERS,
3927         [wp = WeakClaim(this)](
3928             FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) -> bool {
3929             auto list = wp.Upgrade();
3930             if (list) {
3931                 nextFocusNode = list->GetNextFocusNodeInList(step, currFocusNode);
3932             }
3933             return nextFocusNode.Upgrade() != currFocusNode.Upgrade();
3934         });
3935 }
3936 
UpdateDefaultColor()3937 void ListPattern::UpdateDefaultColor()
3938 {
3939     auto host = GetHost();
3940     CHECK_NULL_VOID(host);
3941     auto pipeline = host->GetContextWithCheck();
3942     CHECK_NULL_VOID(pipeline);
3943     auto theme = pipeline->GetTheme<ListTheme>();
3944     CHECK_NULL_VOID(theme);
3945     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
3946     CHECK_NULL_VOID(listLayoutProperty);
3947     if (listLayoutProperty->GetDivider()->color != Color::TRANSPARENT &&
3948         (!listLayoutProperty->HasDividerColorSetByUser() ||
3949             !listLayoutProperty->GetDividerColorSetByUserValue(false))) {
3950         V2::ItemDivider divider = listLayoutProperty->GetDivider().value_or(V2::ItemDivider());
3951         divider.color = theme->GetDividerColor();
3952         listLayoutProperty->UpdateDivider(divider);
3953     }
3954 }
3955 
OnColorModeChange(uint32_t colorMode)3956 void ListPattern::OnColorModeChange(uint32_t colorMode)
3957 {
3958     Pattern::OnColorModeChange(colorMode);
3959     auto host = GetHost();
3960     CHECK_NULL_VOID(host);
3961     CHECK_NULL_VOID(SystemProperties::ConfigChangePerform());
3962     UpdateDefaultColor();
3963     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3964     CHECK_NULL_VOID(paintProperty);
3965     if (paintProperty->GetScrollBarProperty()) {
3966         SetScrollBar(paintProperty->GetScrollBarProperty());
3967     }
3968     host->MarkDirtyNode(PROPERTY_UPDATE_NORMAL);
3969 }
3970 
OnMidIndexChanged()3971 void ListPattern::OnMidIndexChanged()
3972 {
3973 #ifdef SUPPORT_DIGITAL_CROWN
3974     if (!GetCrownEventDragging()) {
3975         return;
3976     }
3977     VibratorUtils::StartVibraFeedback(HAPTIC_STRENGTH1);
3978 #endif
3979 }
3980 
ReportOnItemListEvent(const std::string & event)3981 void ListPattern::ReportOnItemListEvent(const std::string& event)
3982 {
3983     auto host = GetHost();
3984     CHECK_NULL_VOID(host);
3985     std::string value = std::string("List.") + event;
3986     UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", value);
3987     TAG_LOGI(AceLogTag::ACE_LIST, "nodeId:[%{public}d] List reportComponentChangeEvent %{public}s", host->GetId(),
3988         event.c_str());
3989 }
3990 
ReportOnItemListScrollEvent(const std::string & event,int32_t startindex,int32_t endindex)3991 void ListPattern::ReportOnItemListScrollEvent(const std::string& event, int32_t startindex, int32_t endindex)
3992 {
3993     auto host = GetHost();
3994     CHECK_NULL_VOID(host);
3995     std::string value = std::string("List.") + event;
3996 
3997     auto params = JsonUtil::Create();
3998     CHECK_NULL_VOID(params);
3999     params->Put("StartX", startindex);
4000     params->Put("StartY", endindex);
4001 
4002     auto eventData = JsonUtil::Create();
4003     CHECK_NULL_VOID(eventData);
4004     eventData->Put("name", value.c_str());
4005     eventData->Put("params", params);
4006 
4007     auto json = JsonUtil::Create();
4008     CHECK_NULL_VOID(json);
4009     json->Put("nodeId", host->GetId());
4010     json->Put("event", eventData);
4011 
4012     auto result = JsonUtil::Create();
4013     CHECK_NULL_VOID(result);
4014     result->Put("result", json);
4015 
4016     UiSessionManager::GetInstance()->ReportComponentChangeEvent("result", result->ToString());
4017     TAG_LOGI(AceLogTag::ACE_LIST,
4018         "nodeId:[%{public}d] List reportComponentChangeEvent %{public}s startindex:%{public}d endindex:%{public}d",
4019         host->GetId(), event.c_str(), startindex, endindex);
4020 }
4021 
OnInjectionEvent(const std::string & command)4022 int32_t ListPattern::OnInjectionEvent(const std::string& command)
4023 {
4024     TAG_LOGI(AceLogTag::ACE_LIST, "OnInjectionEvent command: %{public}s", command.c_str());
4025 
4026     std::string ret = ScrollablePattern::ParseCommand(command);
4027     if (ret == "scrollForward") {
4028         ScrollPage(true);
4029     } else if (ret == "scrollBackward") {
4030         ScrollPage(false);
4031     } else {
4032         return RET_FAILED;
4033     }
4034     return RET_SUCCESS;
4035 }
4036 
SetFocusWrapMode(FocusWrapMode focusWrapMode)4037 void ListPattern::SetFocusWrapMode(FocusWrapMode focusWrapMode)
4038 {
4039     if (focusWrapMode != FocusWrapMode::DEFAULT && focusWrapMode != FocusWrapMode::WRAP_WITH_ARROW) {
4040         focusWrapMode_ = FocusWrapMode::DEFAULT;
4041     } else {
4042         focusWrapMode_ = focusWrapMode;
4043     }
4044 }
4045 
GetFocusWrapMode() const4046 FocusWrapMode ListPattern::GetFocusWrapMode() const
4047 {
4048     return focusWrapMode_;
4049 }
4050 
ScrollToLastFocusIndex(const KeyEvent & event)4051 bool ListPattern::ScrollToLastFocusIndex(const KeyEvent& event)
4052 {
4053     auto pipeline = GetContext();
4054     CHECK_NULL_RETURN(pipeline, false);
4055     CHECK_NULL_RETURN(pipeline->GetIsFocusActive(), false);
4056     auto host = GetHost();
4057     CHECK_NULL_RETURN(host, false);
4058     auto focusHub = host->GetFocusHub();
4059     CHECK_NULL_RETURN(focusHub, false);
4060     CHECK_NULL_RETURN(focusHub->IsCurrentFocus(), false);
4061     CHECK_NULL_RETURN(focusIndex_, false);
4062     auto focusIndex = focusIndex_.value();
4063     auto focusGroupIndex = focusGroupIndex_.value_or(-1);
4064     keyEvent_ = event;
4065     if (!IsInViewport(focusIndex) || !GetIsInViewInGroup(focusIndex, focusGroupIndex)) {
4066         StopAnimate();
4067         return UpdateStartIndex(focusIndex, focusGroupIndex);
4068     }
4069     return false;
4070 }
4071 
IsInViewport(int32_t index) const4072 bool ListPattern::IsInViewport(int32_t index) const
4073 {
4074     return index >= startIndex_ && index <= endIndex_;
4075 }
4076 
CheckValidInList(int32_t index)4077 bool ListPattern::CheckValidInList(int32_t index)
4078 {
4079     return index >= 0 && index <= maxListItemIndex_;
4080 }
4081 
AdjustFocusGroupIndex(int32_t index,int32_t & indexInGroup)4082 void ListPattern::AdjustFocusGroupIndex(int32_t index, int32_t& indexInGroup)
4083 {
4084     if (!CheckValidInList(index) || indexInGroup < 0) {
4085         return;
4086     }
4087 
4088     auto host = GetHost();
4089     CHECK_NULL_VOID(host);
4090     auto child = host->GetOrCreateChildByIndex(index);
4091     CHECK_NULL_VOID(child);
4092     auto childNode = child->GetHostNode();
4093     CHECK_NULL_VOID(childNode);
4094     auto itemInGroup = AceType::DynamicCast<ListItemGroupPattern>(childNode->GetPattern());
4095     CHECK_NULL_VOID(itemInGroup);
4096 
4097     auto totalCount = child->GetTotalChildCount();
4098     auto footerCount = itemInGroup->GetFooter() ? 1 : 0;
4099     auto headerCount = itemInGroup->GetHeader() ? 1 : 0;
4100     if (indexInGroup >= totalCount - footerCount - headerCount) {
4101         indexInGroup = totalCount - 1;
4102     }
4103     if (indexInGroup < 0) {
4104         indexInGroup = 0;
4105     }
4106     focusGroupIndex_ = indexInGroup;
4107 }
4108 
UpdateStartIndex(int32_t index,int32_t indexInGroup)4109 bool ListPattern::UpdateStartIndex(int32_t index, int32_t indexInGroup)
4110 {
4111     if (!CheckValidInList(index)) {
4112         return false;
4113     }
4114     jumpIndex_ = index;
4115 
4116     if (focusGroupIndex_.has_value() && indexInGroup >= 0) {
4117         jumpIndexInGroup_ = indexInGroup;
4118     }
4119 
4120     auto host = GetHost();
4121     CHECK_NULL_RETURN(host, false);
4122     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4123     SetScrollSource(SCROLL_FROM_FOCUS_JUMP);
4124 
4125     auto pipeline = host->GetContext();
4126     CHECK_NULL_RETURN(pipeline, false);
4127     pipeline->FlushUITasks();
4128     RequestFocusForItem(index, focusGroupIndex_.has_value() && indexInGroup >= 0 ? indexInGroup : -1);
4129     auto child = host->GetChildByIndex(focusIndex_.value_or(-1));
4130     if (child && focusGroupIndex_.has_value()) {
4131         auto childNode = child->GetHostNode();
4132         CHECK_NULL_RETURN(childNode, false);
4133         auto itemInGroup = AceType::DynamicCast<ListItemGroupPattern>(childNode->GetPattern());
4134         CHECK_NULL_RETURN(itemInGroup, false);
4135         auto itemInGroupFocusHub = childNode->GetFocusHub();
4136         CHECK_NULL_RETURN(itemInGroupFocusHub, false);
4137         return itemInGroupFocusHub->GetNextFocusByStep(keyEvent_);
4138     }
4139     return false;
4140 }
4141 
ProcessFocusEvent(bool indexChanged)4142 void ListPattern::ProcessFocusEvent(bool indexChanged)
4143 {
4144     if (indexChanged && focusIndex_.has_value()) {
4145         FireFocus();
4146     }
4147 }
4148 
HandleFocusParentCheck(const RefPtr<FocusHub> & childFocusHub,const RefPtr<FocusHub> & focusHub)4149 void ListPattern::HandleFocusParentCheck(const RefPtr<FocusHub>& childFocusHub, const RefPtr<FocusHub>& focusHub)
4150 {
4151     CHECK_NULL_VOID(focusHub);
4152     CHECK_NULL_VOID(childFocusHub);
4153     auto child = childFocusHub->GetFrameNode();
4154     CHECK_NULL_VOID(child);
4155     auto childNode = child->GetHostNode();
4156     CHECK_NULL_VOID(childNode);
4157     auto parentNode = childNode->GetParent();
4158     while (parentNode && !AceType::InstanceOf<FrameNode>(parentNode)) {
4159         if (AceType::InstanceOf<LazyForEachNode>(parentNode) ||
4160             AceType::InstanceOf<RepeatVirtualScroll2Node>(parentNode) ||
4161             AceType::InstanceOf<RepeatVirtualScrollNode>(parentNode)) {
4162             focusHub->LostChildFocusToSelf();
4163             return;
4164         }
4165         parentNode = parentNode->GetParent();
4166     }
4167 }
4168 
FireFocus()4169 void ListPattern::FireFocus()
4170 {
4171     CHECK_NULL_VOID(focusIndex_);
4172     auto host = GetHost();
4173     CHECK_NULL_VOID(host);
4174     auto focusHub = host->GetFocusHub();
4175     CHECK_NULL_VOID(focusHub);
4176     CHECK_NULL_VOID(focusHub->IsCurrentFocus());
4177 
4178     auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4179     if (childFocusHub && childFocusHub->IsCurrentFocus()) {
4180         // If the focus is on the group's header or footer, clear the focus retention state.
4181         // Otherwise, after user scrolling, the focus will return to the previously retained list item.
4182         if (CheckFocusOnHeaderOrFooter(childFocusHub)) {
4183             focusIndex_.reset();
4184             focusGroupIndex_.reset();
4185             return;
4186         }
4187     }
4188 
4189     if (IsInViewport(focusIndex_.value())) {
4190         if (focusGroupIndex_.has_value()) {
4191             auto groupWrapper = host->GetChildByIndex(focusIndex_.value());
4192             CHECK_NULL_VOID(groupWrapper);
4193             auto groupNode = groupWrapper->GetHostNode();
4194             CHECK_NULL_VOID(groupNode);
4195             auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4196             CHECK_NULL_VOID(groupPattern);
4197             FireFocusInListItemGroup(focusIndex_.value());
4198             return;
4199         }
4200         auto child = host->GetChildByIndex(focusIndex_.value());
4201         CHECK_NULL_VOID(child);
4202         auto childNode = child->GetHostNode();
4203         CHECK_NULL_VOID(childNode);
4204         auto childFocusHub = childNode->GetFocusHub();
4205         CHECK_NULL_VOID(childFocusHub);
4206         if (!childFocusHub->IsCurrentFocus()) {
4207             ResetFocusIndex();
4208             focusHub->SetFocusDependence(FocusDependence::AUTO);
4209             childFocusHub->RequestFocusImmediately(FocusReason::FOCUS_TRAVEL);
4210         }
4211     } else {
4212         auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4213         CHECK_NULL_VOID(childFocusHub);
4214         if (childFocusHub->IsCurrentFocus()) {
4215             HandleFocusParentCheck(childFocusHub, focusHub);
4216         }
4217     }
4218 }
4219 
CheckFocusOnHeaderOrFooter(const RefPtr<FocusHub> & childFocusHub)4220 bool ListPattern::CheckFocusOnHeaderOrFooter(const RefPtr<FocusHub>& childFocusHub)
4221 {
4222     CHECK_NULL_RETURN(childFocusHub, false);
4223     auto groupNode = childFocusHub->GetFrameNode();
4224     CHECK_NULL_RETURN(groupNode, false);
4225     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4226     CHECK_NULL_RETURN(groupPattern, false);
4227 
4228     auto focus = childFocusHub->GetLastWeakFocusNode().Upgrade();
4229     CHECK_NULL_RETURN(focus, false);
4230 
4231     auto curFrame = focus->GetFrameNode();
4232     CHECK_NULL_RETURN(curFrame, false);
4233     auto curPattern = curFrame->GetPattern();
4234     CHECK_NULL_RETURN(curPattern, false);
4235     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
4236     CHECK_NULL_RETURN(curFrame, false);
4237 
4238     if (groupPattern->GetHeader() == curFrame || groupPattern->GetFooter() == curFrame) {
4239         return true;
4240     }
4241     return false;
4242 }
4243 
GetChildFocusHubInGroup(int32_t indexInList,int32_t indexInListItemGroup) const4244 RefPtr<FocusHub> ListPattern::GetChildFocusHubInGroup(int32_t indexInList, int32_t indexInListItemGroup) const
4245 {
4246     auto host = GetHost();
4247     CHECK_NULL_RETURN(host, nullptr);
4248     auto groupWrapper = host->GetChildByIndex(indexInList);
4249     CHECK_NULL_RETURN(groupWrapper, nullptr);
4250     auto groupNode = groupWrapper->GetHostNode();
4251     CHECK_NULL_RETURN(groupNode, nullptr);
4252     auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4253     CHECK_NULL_RETURN(groupPattern, nullptr);
4254     auto itemWrapper = groupNode->GetOrCreateChildByIndex(indexInListItemGroup + groupPattern->GetItemStartIndex());
4255     CHECK_NULL_RETURN(itemWrapper, nullptr);
4256     auto itemNode = itemWrapper->GetHostNode();
4257     CHECK_NULL_RETURN(itemNode, nullptr);
4258     return itemNode->GetFocusHub();
4259 }
4260 
FireFocusInListItemGroup(int32_t groupIndexInList)4261 void ListPattern::FireFocusInListItemGroup(int32_t groupIndexInList)
4262 {
4263     CHECK_NULL_VOID(focusIndex_);
4264     CHECK_NULL_VOID(focusGroupIndex_);
4265     if (groupIndexInList != focusIndex_.value()) {
4266         return;
4267     }
4268 
4269     auto host = GetHost();
4270     CHECK_NULL_VOID(host);
4271     auto focusHub = host->GetFocusHub();
4272     CHECK_NULL_VOID(focusHub);
4273     CHECK_NULL_VOID(focusHub->IsCurrentFocus());
4274     CHECK_NULL_VOID(focusGroupIndex_);
4275     if (GetIsInViewInGroup(focusIndex_.value(), focusGroupIndex_.value())) {
4276         auto childFocusHub = GetChildFocusHubInGroup(focusIndex_.value(), focusGroupIndex_.value());
4277         CHECK_NULL_VOID(childFocusHub);
4278         if (!childFocusHub->IsCurrentFocus()) {
4279             ResetFocusIndex();
4280             ResetGroupFocusIndex();
4281             ResetGroupIndexChanged();
4282             focusHub->SetFocusDependence(FocusDependence::AUTO);
4283             childFocusHub->RequestFocusImmediately(FocusReason::FOCUS_TRAVEL);
4284         }
4285     } else {
4286         auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4287         CHECK_NULL_VOID(childFocusHub);
4288         if (childFocusHub->IsCurrentFocus()) {
4289             focusHub->LostChildFocusToSelf();
4290         }
4291     }
4292 }
4293 
GetIsInViewInGroup(int32_t groupIndex,int32_t index)4294 bool ListPattern::GetIsInViewInGroup(int32_t groupIndex, int32_t index)
4295 {
4296     auto result = true;
4297     if (index < 0) {
4298         return result;
4299     }
4300     auto host = GetHost();
4301     CHECK_NULL_RETURN(host, true);
4302     auto startWrapper = host->GetChildByIndex(groupIndex);
4303     bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
4304     if (startIsGroup) {
4305         auto groupNode = startWrapper->GetHostNode();
4306         CHECK_NULL_RETURN(groupNode, true);
4307         auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4308         CHECK_NULL_RETURN(groupPattern, true);
4309         result = groupPattern->IsInViewport(index);
4310     }
4311     return result;
4312 }
4313 
RequestFocusForItem(int32_t focusIndex,int32_t focusGroupIndex)4314 void ListPattern::RequestFocusForItem(int32_t focusIndex, int32_t focusGroupIndex)
4315 {
4316     auto host = GetHost();
4317     CHECK_NULL_VOID(host);
4318     auto child = host->GetChildByIndex(focusIndex);
4319     CHECK_NULL_VOID(child);
4320     auto childNode = child->GetHostNode();
4321     CHECK_NULL_VOID(childNode);
4322     if (focusGroupIndex >= 0) {
4323         auto itemInGroupFocusHub = GetChildFocusHubInGroup(focusIndex, focusGroupIndex);
4324         CHECK_NULL_VOID(itemInGroupFocusHub);
4325         if (!itemInGroupFocusHub->IsCurrentFocus() && itemInGroupFocusHub->IsFocusable()) {
4326             itemInGroupFocusHub->SetFocusDependence(FocusDependence::AUTO);
4327             itemInGroupFocusHub->RequestFocusImmediately();
4328         }
4329     } else {
4330         auto childFocusHub = childNode->GetFocusHub();
4331         CHECK_NULL_VOID(childFocusHub);
4332         if (!childFocusHub->IsCurrentFocus() && childFocusHub->IsFocusable()) {
4333             childFocusHub->SetFocusDependence(FocusDependence::AUTO);
4334             childFocusHub->RequestFocusImmediately();
4335         }
4336     }
4337     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4338 }
4339 
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)4340 int32_t ListPattern::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
4341 {
4342     auto tarFrame = focusNode->GetFrameNode();
4343     CHECK_NULL_RETURN(tarFrame, -1);
4344     auto tarPattern = tarFrame->GetPattern();
4345     CHECK_NULL_RETURN(tarPattern, -1);
4346     auto tarItemPattern = AceType::DynamicCast<ListItemPattern>(tarPattern);
4347     if (!tarItemPattern) {
4348         auto tarGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(tarPattern);
4349         CHECK_NULL_RETURN(tarGroupPattern, -1);
4350         return tarGroupPattern->GetIndexInList();
4351     }
4352     return tarItemPattern->GetIndexInList();
4353 }
4354 
ScrollToFocusNodeIndex(int32_t index)4355 void ListPattern::ScrollToFocusNodeIndex(int32_t index)
4356 {
4357     int32_t indexInGroup = -1;
4358     if (focusIndex_.has_value()) {
4359         index = focusIndex_.value();
4360         indexInGroup = focusGroupIndex_.value_or(-1);
4361     }
4362 
4363     StopAnimate();
4364     if (!IsInViewport(index) || !GetIsInViewInGroup(index, indexInGroup)) {
4365         auto host = GetHost();
4366         CHECK_NULL_VOID(host);
4367 
4368         // If the group that needs to maintain focus is not mounted when DataChangeNotify occurs,
4369         // focusGroupIndex_ will not be updated. If the focus is on the last item of this group,
4370         // and that item is deleted, focusGroupIndex_ should be set to the new last item after deletion.
4371         AdjustFocusGroupIndex(index, indexInGroup);
4372         jumpIndex_ = index;
4373         if (indexInGroup >= 0) {
4374             jumpIndexInGroup_ = indexInGroup;
4375         }
4376 
4377         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4378         SetScrollSource(SCROLL_FROM_FOCUS_JUMP);
4379         auto pipeline = host->GetContext();
4380         if (pipeline) {
4381             pipeline->FlushUITasks();
4382         }
4383     }
4384     RequestFocusForItem(index, indexInGroup);
4385 }
4386 
UpdateGroupFocusIndexForDataChange(int32_t groupIndexInList,int32_t indexInGroup,int32_t count)4387 void ListPattern::UpdateGroupFocusIndexForDataChange(int32_t groupIndexInList, int32_t indexInGroup, int32_t count)
4388 {
4389     if (focusIndex_.has_value() && focusIndex_.value() == groupIndexInList && focusGroupIndex_.has_value() &&
4390         focusGroupIndex_ >= indexInGroup) {
4391         focusGroupIndex_ = focusGroupIndex_.value() + count;
4392         if (focusGroupIndex_ < 0) {
4393             focusGroupIndex_ = 0;
4394         }
4395     }
4396 }
4397 
ResetForExtScroll()4398 void ListPattern::ResetForExtScroll()
4399 {
4400     currentDelta_ = 0;
4401 }
4402 } // namespace OHOS::Ace::NG
4403