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