• 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 "base/geometry/axis.h"
19 #include "base/memory/referenced.h"
20 #include "base/utils/utils.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/perfmonitor/perf_constants.h"
23 #include "core/animation/bilateral_spring_node.h"
24 #include "core/animation/spring_model.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components/scroll/scroll_bar_theme.h"
27 #include "core/components/scroll/scrollable.h"
28 #include "core/components_ng/pattern/list/list_item_pattern.h"
29 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
30 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_layout_property.h"
32 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
33 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
34 #include "core/components_ng/property/measure_utils.h"
35 #include "core/components_ng/property/property.h"
36 #include "core/components_v2/inspector/inspector_constants.h"
37 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
38 
39 namespace OHOS::Ace::NG {
40 namespace {
41 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
42 constexpr double CHAIN_SPRING_MASS = 1.0;
43 constexpr double CHAIN_SPRING_DAMPING = 30.0;
44 constexpr double CHAIN_SPRING_STIFFNESS = 228;
45 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
46 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
47 } // namespace
48 
OnModifyDone()49 void ListPattern::OnModifyDone()
50 {
51     if (!isInitialized_) {
52         jumpIndex_ = GetLayoutProperty<ListLayoutProperty>()->GetInitialIndex().value_or(0);
53         if (NeedScrollSnapAlignEffect()) {
54             scrollAlign_ = GetScrollAlignByScrollSnapAlign();
55         }
56     }
57     auto host = GetHost();
58     CHECK_NULL_VOID(host);
59     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
60     CHECK_NULL_VOID(listLayoutProperty);
61     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
62     SetAxis(axis);
63     if (!GetScrollableEvent()) {
64         InitScrollableEvent();
65     }
66     auto edgeEffect = listLayoutProperty->GetEdgeEffect().value_or(EdgeEffect::SPRING);
67     SetEdgeEffect(edgeEffect);
68 
69     auto defaultDisplayMode = DisplayMode::OFF;
70     const static int32_t PLATFORM_VERSION_TEN = 10;
71     auto pipeline = PipelineContext::GetCurrentContext();
72     CHECK_NULL_VOID(pipeline);
73     if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
74         defaultDisplayMode = DisplayMode::AUTO;
75     }
76     auto listPaintProperty = host->GetPaintProperty<ListPaintProperty>();
77     SetScrollBar(listPaintProperty->GetBarDisplayMode().value_or(defaultDisplayMode));
78     SetChainAnimation();
79     if (multiSelectable_ && !isMouseEventInit_) {
80         InitMouseEvent();
81     }
82     if (!multiSelectable_ && isMouseEventInit_) {
83         UninitMouseEvent();
84     }
85     auto focusHub = host->GetFocusHub();
86     CHECK_NULL_VOID_NOLOG(focusHub);
87     InitOnKeyEvent(focusHub);
88     SetAccessibilityAction();
89 }
90 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)91 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
92 {
93     if (config.skipMeasure && config.skipLayout) {
94         return false;
95     }
96     bool isJump = false;
97     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
98     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
99     auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
100     CHECK_NULL_RETURN(listLayoutAlgorithm, false);
101     itemPosition_ = listLayoutAlgorithm->GetItemPosition();
102     maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
103     spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
104     float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
105     auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
106     if (listLayoutAlgorithm->GetEstimateOffset().has_value()) {
107         float absoluteOffset = listLayoutAlgorithm->GetEstimateOffset().value_or(currentOffset_);
108         relativeOffset += absoluteOffset - currentOffset_;
109         isJump = true;
110     }
111     if (targetIndex_) {
112         auto iter = itemPosition_.find(targetIndex_.value());
113         if (iter != itemPosition_.end()) {
114             float targetPos = 0.0f;
115             switch (scrollAlign_) {
116                 case ScrollAlign::START:
117                 case ScrollAlign::NONE:
118                     targetPos = iter->second.startPos;
119                     break;
120                 case ScrollAlign::CENTER:
121                     targetPos = (iter->second.endPos + iter->second.startPos) / 2.0f - contentMainSize_ / 2.0f;
122                     break;
123                 case ScrollAlign::END:
124                     targetPos = iter->second.endPos - contentMainSize_;
125                     break;
126                 case ScrollAlign::AUTO:
127                     ScrollAutoType scrollAutoType = listLayoutAlgorithm->GetScrollAutoType();
128                     targetPos = CalculateTargetPos(iter->second.startPos, iter->second.endPos, scrollAutoType);
129                     break;
130             }
131             // correct the currentOffset when the startIndex is 0.
132             if (listLayoutAlgorithm->GetStartIndex() == 0) {
133                 currentOffset_ = -itemPosition_.begin()->second.startPos;
134             } else {
135                 currentOffset_ = currentOffset_ + relativeOffset;
136             }
137             AnimateTo(targetPos + currentOffset_, -1, nullptr, true);
138         }
139         targetIndex_.reset();
140     } else {
141         if (listLayoutAlgorithm->GetStartIndex() == 0) {
142             currentOffset_ = -itemPosition_.begin()->second.startPos;
143         } else {
144             currentOffset_ = currentOffset_ + relativeOffset;
145         }
146     }
147     if (predictSnapOffset.has_value()) {
148         if (scrollableTouchEvent_) {
149             scrollableTouchEvent_->StartScrollSnapMotion(predictSnapOffset.value(), scrollSnapVelocity_);
150             scrollSnapVelocity_ = 0.0f;
151         }
152         predictSnapOffset_.reset();
153     }
154     if (isScrollEnd_) {
155         auto host = GetHost();
156         CHECK_NULL_RETURN(host, false);
157         host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
158         isScrollEnd_ = false;
159     }
160     currentDelta_ = 0.0f;
161     float prevStartOffset = startMainPos_;
162     float prevEndOffset = endMainPos_ - contentMainSize_;
163     contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
164     contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
165     contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
166     startMainPos_ = listLayoutAlgorithm->GetStartPosition();
167     endMainPos_ = listLayoutAlgorithm->GetEndPosition();
168     crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
169     auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
170     if (lanesLayoutAlgorithm) {
171         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
172         lanes_ = lanesLayoutAlgorithm->GetLanes();
173         laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
174     }
175     CheckScrollable();
176 
177     bool indexChanged = false;
178     const static int32_t PLATFORM_VERSION_TEN = 10;
179     auto pipeline = PipelineContext::GetCurrentContext();
180     CHECK_NULL_RETURN(pipeline, false);
181     if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
182         indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
183             (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
184             (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
185     } else {
186         indexChanged =
187             (startIndex_ != listLayoutAlgorithm->GetStartIndex()) || (endIndex_ != listLayoutAlgorithm->GetEndIndex());
188     }
189     if (indexChanged) {
190         startIndex_ = listLayoutAlgorithm->GetStartIndex();
191         endIndex_ = listLayoutAlgorithm->GetEndIndex();
192         centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
193     }
194     ProcessEvent(indexChanged, relativeOffset, isJump, prevStartOffset, prevEndOffset);
195     UpdateScrollBarOffset();
196     if (config.frameSizeChange) {
197         if (GetScrollBar() != nullptr) {
198             GetScrollBar()->ScheduleDisapplearDelayTask();
199         }
200     }
201     CheckRestartSpring();
202 
203     DrivenRender(dirty);
204 
205     SetScrollSource(SCROLL_FROM_NONE);
206     isInitialized_ = true;
207     MarkSelectedItems();
208     return true;
209 }
210 
GetScrollAlignByScrollSnapAlign() const211 ScrollAlign ListPattern::GetScrollAlignByScrollSnapAlign() const
212 {
213     auto scrollAlign = ScrollAlign::START;
214     auto host = GetHost();
215     CHECK_NULL_RETURN(host, scrollAlign);
216     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
217     CHECK_NULL_RETURN(listProperty, scrollAlign);
218     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
219     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
220         scrollAlign = ScrollAlign::CENTER;
221     }
222     return scrollAlign;
223 }
224 
CalculateTargetPos(float startPos,float endPos,ScrollAutoType scrollAutoType)225 float ListPattern::CalculateTargetPos(float startPos, float endPos, ScrollAutoType scrollAutoType)
226 {
227     float targetPos = 0.0f;
228     switch (scrollAutoType) {
229         case ScrollAutoType::NOT_CHANGE:
230             LOGI("item is fully visible, no need to scroll.");
231             break;
232         case ScrollAutoType::START:
233             targetPos = startPos;
234             break;
235         case ScrollAutoType::END:
236             targetPos = endPos - contentMainSize_;
237             break;
238     }
239     return targetPos;
240 }
241 
CreateNodePaintMethod()242 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
243 {
244     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
245     V2::ItemDivider divider;
246     if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
247         divider = listLayoutProperty->GetDivider().value();
248     }
249     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
250     auto drawVertical = (axis == Axis::HORIZONTAL);
251     auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, lanes_, spaceWidth_);
252     paint->SetScrollBar(GetScrollBar());
253     CreateScrollBarOverlayModifier();
254     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
255     paint->SetTotalItemCount(maxListItemIndex_ + 1);
256     auto scrollEffect = GetScrollEdgeEffect();
257     if (scrollEffect && scrollEffect->IsFadeEffect()) {
258         paint->SetEdgeEffect(scrollEffect);
259     }
260     if (!listContentModifier_) {
261         auto host = GetHost();
262         CHECK_NULL_RETURN(host, paint);
263         const auto& geometryNode = host->GetGeometryNode();
264         auto size = geometryNode->GetPaddingSize();
265         OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
266         listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
267     }
268 
269     paint->SetLaneGutter(laneGutter_);
270     listContentModifier_->SetItemsPosition(itemPosition_);
271     paint->SetContentModifier(listContentModifier_);
272     return paint;
273 }
274 
ProcessEvent(bool indexChanged,float finalOffset,bool isJump,float prevStartOffset,float prevEndOffset)275 void ListPattern::ProcessEvent(
276     bool indexChanged, float finalOffset, bool isJump, float prevStartOffset, float prevEndOffset)
277 {
278     auto host = GetHost();
279     CHECK_NULL_VOID(host);
280     auto listEventHub = host->GetEventHub<ListEventHub>();
281     CHECK_NULL_VOID(listEventHub);
282 
283     paintStateFlag_ = !NearZero(finalOffset) && !isJump;
284     isFramePaintStateValid_ = true;
285     const static int32_t PLATFORM_VERSION_TEN = 10;
286     auto pipeline = PipelineContext::GetCurrentContext();
287     CHECK_NULL_VOID(pipeline);
288     auto onScroll = listEventHub->GetOnScroll();
289     if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN && scrollStop_ && !GetScrollAbort()) {
290         auto source = GetScrollSource();
291         auto offsetPX = Dimension(finalOffset);
292         auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
293         if (onScroll) {
294             if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR) {
295                 onScroll(offsetVP, ScrollState::SCROLL);
296                 onScroll(0.0_vp, ScrollState::IDLE);
297             } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING ||
298                 source == SCROLL_FROM_ANIMATION_CONTROLLER || source == SCROLL_FROM_BAR_FLING) {
299                 onScroll(offsetVP, ScrollState::FLING);
300                 onScroll(0.0_vp, ScrollState::IDLE);
301             } else {
302                 onScroll(offsetVP, ScrollState::IDLE);
303             }
304         }
305     } else if (onScroll && !NearZero(finalOffset)) {
306         auto source = GetScrollSource();
307         auto offsetPX = Dimension(finalOffset);
308         auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
309         if (pipeline->GetMinPlatformVersion() < PLATFORM_VERSION_TEN &&
310             (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR || source == SCROLL_FROM_ANIMATION_CONTROLLER)) {
311             source = SCROLL_FROM_NONE;
312         }
313         if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR) {
314             onScroll(offsetVP, ScrollState::SCROLL);
315         } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING ||
316             source == SCROLL_FROM_ANIMATION_CONTROLLER || source == SCROLL_FROM_BAR_FLING) {
317             onScroll(offsetVP, ScrollState::FLING);
318         } else {
319             onScroll(offsetVP, ScrollState::IDLE);
320         }
321     }
322 
323     if (indexChanged) {
324         auto onScrollIndex = listEventHub->GetOnScrollIndex();
325         if (onScrollIndex) {
326             int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
327             int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
328             onScrollIndex(startIndex, endIndex, centerIndex_);
329         }
330     }
331 
332     auto onReachStart = listEventHub->GetOnReachStart();
333     if (onReachStart && (startIndex_ == 0)) {
334         bool scrollUpToStart = Positive(prevStartOffset) && NonPositive(startMainPos_);
335         bool scrollDownToStart = (Negative(prevStartOffset) || !isInitialized_) && NonNegative(startMainPos_);
336         if (scrollUpToStart || scrollDownToStart) {
337             onReachStart();
338         }
339     }
340     auto onReachEnd = listEventHub->GetOnReachEnd();
341     if (onReachEnd && (endIndex_ == maxListItemIndex_)) {
342         float endOffset = endMainPos_ - contentMainSize_;
343         bool scrollUpToEnd = (Positive(prevEndOffset) || !isInitialized_) && NonPositive(endOffset);
344         bool scrollDownToEnd = Negative(prevEndOffset) && NonNegative(endOffset);
345         if (scrollUpToEnd || (scrollDownToEnd && GetScrollSource() != SCROLL_FROM_NONE)) {
346             onReachEnd();
347         }
348     }
349 
350     if (scrollStop_) {
351         auto onScrollStop = listEventHub->GetOnScrollStop();
352         if (!GetScrollAbort()) {
353             if (onScrollStop) {
354                 SetScrollSource(SCROLL_FROM_NONE);
355                 onScrollStop();
356             }
357             auto scrollBar = GetScrollBar();
358             if (scrollBar) {
359                 scrollBar->ScheduleDisapplearDelayTask();
360             }
361             StartScrollBarAnimatorByProxy();
362         }
363         if (!GetScrollAbort()) {
364             PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
365         }
366         scrollStop_ = false;
367         SetScrollAbort(false);
368     }
369 }
370 
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)371 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
372 {
373     auto host = GetHost();
374     CHECK_NULL_VOID(host);
375     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
376     auto listPaintProperty = host->GetPaintProperty<ListPaintProperty>();
377     auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
378     auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
379     bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
380     auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
381     bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE ||
382         barNeedPaint || chainAnimation || !scrollable_);
383 
384     auto renderContext = host->GetRenderContext();
385     CHECK_NULL_VOID(renderContext);
386     renderContext->MarkDrivenRender(drivenRender);
387     if (drivenRender && isFramePaintStateValid_) {
388         // Mark items
389         int32_t indexStep = 0;
390         int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
391         for (auto& pos : itemPosition_) {
392             auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
393             CHECK_NULL_VOID(wrapper);
394             auto itemHost = wrapper->GetHostNode();
395             CHECK_NULL_VOID(itemHost);
396             auto itemRenderContext = itemHost->GetRenderContext();
397             CHECK_NULL_VOID(itemRenderContext);
398             itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
399             indexStep++;
400         }
401         renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
402         isFramePaintStateValid_ = false;
403     }
404 }
405 
CheckScrollable()406 void ListPattern::CheckScrollable()
407 {
408     auto host = GetHost();
409     CHECK_NULL_VOID(host);
410     auto hub = host->GetEventHub<EventHub>();
411     CHECK_NULL_VOID(hub);
412     auto gestureHub = hub->GetOrCreateGestureEventHub();
413     CHECK_NULL_VOID(gestureHub);
414     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
415     CHECK_NULL_VOID(listProperty);
416     if (itemPosition_.empty()) {
417         scrollable_ = false;
418     } else {
419         if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
420             !IsScrollSnapAlignCenter()) {
421             scrollable_ = GreatNotEqual((endMainPos_ - startMainPos_), contentMainSize_);
422         } else {
423             scrollable_ = true;
424         }
425     }
426 
427     SetScrollEnable(scrollable_);
428 
429     if (!listProperty->GetScrollEnabled().value_or(scrollable_)) {
430         SetScrollEnable(false);
431     }
432 }
433 
CreateLayoutAlgorithm()434 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
435 {
436     auto host = GetHost();
437     CHECK_NULL_RETURN(host, nullptr);
438     auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
439     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
440     if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
441         listLayoutProperty->HasLaneMaxLength()) {
442         auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
443         RefreshLanesItemRange();
444         lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
445         lanesLayoutAlgorithm->SetLanes(lanes_);
446         listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
447     } else {
448         listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
449     }
450     if (jumpIndex_) {
451         listLayoutAlgorithm->SetIndex(jumpIndex_.value());
452         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
453         jumpIndex_.reset();
454     }
455     if (targetIndex_) {
456         listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
457         listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
458     }
459     if (jumpIndexInGroup_) {
460         listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
461         jumpIndexInGroup_.reset();
462     }
463     if (predictSnapOffset_.has_value()) {
464         listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
465         listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
466     }
467     listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
468     listLayoutAlgorithm->SetItemsPosition(itemPosition_);
469     listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
470     listLayoutAlgorithm->SetContentStartOffset(contentStartOffset_);
471     listLayoutAlgorithm->SetContentEndOffset(contentEndOffset_);
472     if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
473         listLayoutAlgorithm->SetOverScrollFeature();
474     }
475     listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
476     listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
477     if (chainAnimation_) {
478         SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
479     }
480     return listLayoutAlgorithm;
481 }
482 
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,RefPtr<ListLayoutProperty> listLayoutProperty)483 void ListPattern::SetChainAnimationLayoutAlgorithm(
484     RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, RefPtr<ListLayoutProperty> listLayoutProperty)
485 {
486     CHECK_NULL_VOID(listLayoutAlgorithm);
487     CHECK_NULL_VOID(listLayoutProperty);
488     listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
489         auto list = weak.Upgrade();
490         CHECK_NULL_RETURN(list, 0.0f);
491         return list->GetChainDelta(index);
492     });
493     if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
494         listLayoutAlgorithm->SetChainInterval(CHAIN_INTERVAL_DEFAULT.ConvertToPx());
495     }
496 }
497 
IsScrollSnapAlignCenter() const498 bool ListPattern::IsScrollSnapAlignCenter() const
499 {
500     auto host = GetHost();
501     CHECK_NULL_RETURN(host, false);
502     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
503     CHECK_NULL_RETURN(listProperty, false);
504     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
505     if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
506         return true;
507     }
508 
509     return false;
510 }
511 
UpdateScrollSnap()512 void ListPattern::UpdateScrollSnap()
513 {
514     if (!AnimateStoped()) {
515         return;
516     }
517     predictSnapOffset_ = 0.0f;
518 }
519 
NeedScrollSnapAlignEffect() const520 bool ListPattern::NeedScrollSnapAlignEffect() const
521 {
522     auto host = GetHost();
523     CHECK_NULL_RETURN(host, false);
524     auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
525     CHECK_NULL_RETURN(listProperty, false);
526     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
527     if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
528         return false;
529     }
530 
531     return true;
532 }
533 
IsAtTop() const534 bool ListPattern::IsAtTop() const
535 {
536     if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
537         float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
538         return (startIndex_ == 0) && GreatOrEqual(startMainPos_ - currentDelta_ + GetChainDelta(0),
539             contentMainSize_ / 2.0f - startItemHeight / 2.0f);
540     }
541 
542     return (startIndex_ == 0) && NonNegative(startMainPos_ - currentDelta_ + GetChainDelta(0));
543 }
544 
IsAtBottom() const545 bool ListPattern::IsAtBottom() const
546 {
547     if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
548         float endItemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
549         return (endIndex_ == maxListItemIndex_) && LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_),
550             contentMainSize_ / 2.0f + endItemHeight / 2.0f);
551     }
552 
553     return endIndex_ == maxListItemIndex_ &&
554            LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_), contentMainSize_);
555 }
556 
OutBoundaryCallback()557 bool ListPattern::OutBoundaryCallback()
558 {
559     bool outBoundary = IsAtTop() || IsAtBottom();
560     if (!dragFromSpring_ && outBoundary && chainAnimation_) {
561         chainAnimation_->SetOverDrag(false);
562         auto delta = chainAnimation_->SetControlIndex(IsAtTop() ? 0 : maxListItemIndex_);
563         currentDelta_ -= delta;
564         dragFromSpring_ = true;
565     }
566     return outBoundary;
567 }
568 
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const569 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
570 {
571     if (itemPosition_.empty()) {
572         return;
573     }
574     bool firstIsGroup = startIndex_ == 0 && itemPosition_.begin()->second.isGroup;
575     bool lastIsGroup = endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup;
576     if (!firstIsGroup && !lastIsGroup) {
577         return;
578     }
579     auto host = GetHost();
580     CHECK_NULL_VOID(host);
581     std::list<RefPtr<FrameNode>> childrens;
582     host->GenerateOneDepthVisibleFrame(childrens);
583     if (childrens.empty()) {
584         return;
585     }
586     if (firstIsGroup) {
587         auto itemGroup = (*childrens.begin())->GetPattern<ListItemGroupPattern>();
588         if (itemGroup) {
589             groupAtStart = itemGroup->GetDiasplayStartIndexInGroup() == 0;
590         }
591     }
592     if (lastIsGroup) {
593         auto itemGroup = (*childrens.rbegin())->GetPattern<ListItemGroupPattern>();
594         if (itemGroup) {
595             groupAtEnd = itemGroup->GetDisplayEndIndexInGroup() == itemGroup->GetEndIndexInGroup();
596         }
597     }
598 }
599 
GetOverScrollOffset(double delta) const600 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
601 {
602     OverScrollOffset offset = { 0, 0 };
603     bool groupAtStart = true;
604     bool groupAtEnd = true;
605     GetListItemGroupEdge(groupAtStart, groupAtEnd);
606     if (startIndex_ == 0 && groupAtStart) {
607         auto startPos = startMainPos_ + GetChainDelta(0);
608         auto newStartPos = startPos + delta;
609         if (startPos > 0 && newStartPos > 0) {
610             offset.start = delta;
611         }
612         if (startPos > 0 && newStartPos <= 0) {
613             offset.start = -startPos;
614         }
615         if (startPos <= 0 && newStartPos > 0) {
616             offset.start = newStartPos;
617         }
618         if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
619             float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
620             if (newStartPos > (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f)) {
621                 offset.start = newStartPos - (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f);
622             } else {
623                 offset.start = 0.0;
624             }
625         }
626     }
627     if (endIndex_ == maxListItemIndex_ && groupAtEnd) {
628         auto endPos = endMainPos_ + GetChainDelta(endIndex_);
629         auto newEndPos = endPos + delta;
630         if (endPos < contentMainSize_ && newEndPos < contentMainSize_) {
631             offset.end = delta;
632         }
633         if (endPos < contentMainSize_ && newEndPos >= contentMainSize_) {
634             offset.end = contentMainSize_ - endPos;
635         }
636         if (endPos >= contentMainSize_ && newEndPos < contentMainSize_) {
637             offset.end = newEndPos - contentMainSize_;
638         }
639         if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
640             float endItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
641             if (newEndPos < (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f)) {
642                 offset.end = newEndPos - (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f);
643             } else {
644                 offset.end = 0.0;
645             }
646         }
647     }
648     return offset;
649 }
650 
UpdateCurrentOffset(float offset,int32_t source)651 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
652 {
653     if (itemPosition_.empty()) {
654         return false;
655     }
656     // check edgeEffect is not springEffect
657     if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
658         if (IsOutOfBoundary(false)) {
659             MarkDirtyNodeSelf();
660         }
661         return false;
662     }
663     SetScrollSource(source);
664     currentDelta_ = currentDelta_ - offset;
665     MarkDirtyNodeSelf();
666     if (!IsOutOfBoundary() || !scrollable_) {
667         return true;
668     }
669 
670     // over scroll in drag update from normal to over scroll.
671     float overScroll = 0.0f;
672     // over scroll in drag update during over scroll.
673     auto startPos = startMainPos_ - currentDelta_;
674     if ((itemPosition_.begin()->first == 0) && Positive(startPos)) {
675         overScroll = startPos;
676     } else {
677         overScroll = contentMainSize_ - (endMainPos_ - currentDelta_);
678     }
679     if (IsScrollSnapAlignCenter()) {
680         auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
681         auto endPos = endMainPos_ - currentDelta_;
682         if (startIndex_ == 0 && Positive(startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
683             overScroll = startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
684         } else if ((endIndex_ == maxListItemIndex_) &&
685             LessNotEqual(endPos - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
686             overScroll = endPos - itemHeight / 2.0f - contentMainSize_ / 2.0f;
687         }
688     }
689     HandleScrollBarOutBoundary(overScroll);
690 
691     if (GetScrollSource() == SCROLL_FROM_UPDATE) {
692         // adjust offset.
693         auto friction = CalculateFriction(std::abs(overScroll) / contentMainSize_);
694         currentDelta_ = currentDelta_ * friction;
695     }
696     return true;
697 }
698 
MarkDirtyNodeSelf()699 void ListPattern::MarkDirtyNodeSelf()
700 {
701     auto host = GetHost();
702     CHECK_NULL_VOID(host);
703     if (!crossMatchChild_) {
704         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
705     } else {
706         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
707     }
708 }
709 
OnScrollEndCallback()710 void ListPattern::OnScrollEndCallback()
711 {
712     scrollStop_ = true;
713     MarkDirtyNodeSelf();
714 }
715 
OnScrollStartCallback()716 void ListPattern::OnScrollStartCallback()
717 {
718     FireOnScrollStart();
719 }
720 
GetContentSize() const721 SizeF ListPattern::GetContentSize() const
722 {
723     auto host = GetHost();
724     CHECK_NULL_RETURN(host, SizeF());
725     auto geometryNode = host->GetGeometryNode();
726     CHECK_NULL_RETURN(geometryNode, SizeF());
727     return geometryNode->GetPaddingSize();
728 }
729 
IsOutOfBoundary(bool useCurrentDelta)730 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
731 {
732     if (itemPosition_.empty()) {
733         return false;
734     }
735     auto startPos = useCurrentDelta ? startMainPos_ - currentDelta_ : startMainPos_;
736     auto endPos = useCurrentDelta ? endMainPos_ - currentDelta_ : endMainPos_;
737     if (startIndex_ == 0) {
738         startPos += GetChainDelta(0);
739     }
740     if (endIndex_ == maxListItemIndex_) {
741         endPos += GetChainDelta(endIndex_);
742     }
743     bool outOfStart = (startIndex_ == 0) && Positive(startPos) && GreatNotEqual(endPos, contentMainSize_);
744     bool outOfEnd = (endIndex_ == maxListItemIndex_) && LessNotEqual(endPos, contentMainSize_) && Negative(startPos);
745     if (IsScrollSnapAlignCenter()) {
746         auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
747         outOfStart = (startIndex_ == 0) && Positive(startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f);
748         outOfEnd = (endIndex_ == maxListItemIndex_) &&
749             LessNotEqual(endPos - itemHeight / 2.0f, contentMainSize_ / 2.0f);
750     }
751     return outOfStart || outOfEnd;
752 }
753 
FireOnScrollStart()754 void ListPattern::FireOnScrollStart()
755 {
756     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
757     if (GetScrollAbort()) {
758         return;
759     }
760     auto scrollBar = GetScrollBar();
761     if (scrollBar) {
762         scrollBar->PlayScrollBarStartAnimation();
763     }
764     StopScrollBarAnimatorByProxy();
765     auto host = GetHost();
766     CHECK_NULL_VOID(host);
767     auto hub = host->GetEventHub<ListEventHub>();
768     CHECK_NULL_VOID_NOLOG(hub);
769     auto onScrollStart = hub->GetOnScrollStart();
770     CHECK_NULL_VOID_NOLOG(onScrollStart);
771     onScrollStart();
772 }
773 
OnScrollCallback(float offset,int32_t source)774 bool ListPattern::OnScrollCallback(float offset, int32_t source)
775 {
776     if (source == SCROLL_FROM_START) {
777         ProcessDragStart(offset);
778         auto item = swiperItem_.Upgrade();
779         if (item) {
780             item->SwiperReset();
781         }
782         FireOnScrollStart();
783         return true;
784     }
785     ProcessDragUpdate(offset, source);
786     return UpdateCurrentOffset(offset, source);
787 }
788 
InitScrollableEvent()789 void ListPattern::InitScrollableEvent()
790 {
791     AddScrollEvent();
792     auto host = GetHost();
793     CHECK_NULL_VOID(host);
794     auto listEventHub = host->GetEventHub<ListEventHub>();
795     auto onScrollFrameBegin = listEventHub->GetOnScrollFrameBegin();
796     SetScrollFrameBeginCallback(onScrollFrameBegin);
797     auto scrollableEvent = GetScrollableEvent();
798     CHECK_NULL_VOID(scrollableEvent);
799     scrollableTouchEvent_ = scrollableEvent->GetScrollable();
800     CHECK_NULL_VOID(scrollableTouchEvent_);
801     scrollableTouchEvent_->SetOnContinuousSliding([weak = AceType::WeakClaim(this)]() -> double {
802         auto list = weak.Upgrade();
803         return list->contentMainSize_;
804     });
805 }
806 
OnScrollSnapCallback(double targetOffset,double velocity)807 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
808 {
809     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
810     CHECK_NULL_RETURN(listProperty, false);
811     auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
812     if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
813         return false;
814     }
815     predictSnapOffset_ = targetOffset;
816     scrollSnapVelocity_ = velocity;
817     MarkDirtyNodeSelf();
818     return true;
819 }
820 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)821 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
822 {
823     scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
824         auto list = weak.Upgrade();
825         CHECK_NULL_RETURN_NOLOG(list, 0.0);
826         return list->startMainPos_ + list->GetChainDelta(list->startIndex_) - list->currentDelta_;
827     });
828     scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
829         auto list = weak.Upgrade();
830         auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
831         auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
832         if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
833             float endItemHeight =
834                 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
835             return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
836         }
837         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
838         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
839     });
840     scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
841         auto list = weak.Upgrade();
842         CHECK_NULL_RETURN_NOLOG(list, 0.0);
843         if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
844             float startItemHeight =
845                 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
846             return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
847         }
848 
849         return list->contentStartOffset_;
850     });
851     scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
852         auto list = weak.Upgrade();
853         auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
854         auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
855         if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
856             float endItemHeight =
857                 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
858             return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
859         }
860         float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
861         return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
862     });
863     scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
864         auto list = weak.Upgrade();
865         CHECK_NULL_RETURN_NOLOG(list, 0.0);
866         if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
867             float startItemHeight =
868                 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
869             return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
870         }
871 
872         return list->contentStartOffset_;
873     });
874 }
875 
CheckRestartSpring()876 void ListPattern::CheckRestartSpring()
877 {
878     if (!ScrollableIdle() || !IsOutOfBoundary()) {
879         return;
880     }
881     auto edgeEffect = GetScrollEdgeEffect();
882     if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
883         return;
884     }
885     if (AnimateRunning()) {
886         return;
887     }
888     FireOnScrollStart();
889     edgeEffect->ProcessScrollOver(0);
890 }
891 
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)892 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
893 {
894     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
895         auto pattern = wp.Upgrade();
896         CHECK_NULL_RETURN_NOLOG(pattern, false);
897         return pattern->OnKeyEvent(event);
898     };
899     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
900 }
901 
OnKeyEvent(const KeyEvent & event)902 bool ListPattern::OnKeyEvent(const KeyEvent& event)
903 {
904     if (event.action != KeyAction::DOWN) {
905         return false;
906     }
907     if (event.code == KeyCode::KEY_PAGE_DOWN) {
908         LOGD("Keycode is PgDn. Scroll to next page");
909         ScrollPage(false);
910         return true;
911     }
912     if (event.code == KeyCode::KEY_PAGE_UP) {
913         LOGD("Keycode is PgDn. Scroll to next page");
914         ScrollPage(true);
915         return true;
916     }
917     return HandleDirectionKey(event);
918 }
919 
HandleDirectionKey(const KeyEvent & event)920 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
921 {
922     return false;
923 }
924 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)925 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
926 {
927     auto curFocus = currentFocusNode.Upgrade();
928     CHECK_NULL_RETURN(curFocus, nullptr);
929     auto curFrame = curFocus->GetFrameNode();
930     CHECK_NULL_RETURN(curFrame, nullptr);
931     auto curPattern = curFrame->GetPattern();
932     CHECK_NULL_RETURN(curPattern, nullptr);
933     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
934     CHECK_NULL_RETURN(curItemPattern, nullptr);
935     auto listProperty = GetLayoutProperty<ListLayoutProperty>();
936     CHECK_NULL_RETURN(listProperty, nullptr);
937 
938     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
939     auto curIndex = curItemPattern->GetIndexInList();
940     auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
941     auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
942     if (curIndex < 0 || curIndex > maxListItemIndex_) {
943         LOGE("can't find focused child.");
944         return nullptr;
945     }
946 
947     auto moveStep = 0;
948     auto nextIndex = curIndex;
949     auto nextIndexInGroup = curIndexInGroup;
950     if (lanes_ <= 1) {
951         if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
952             moveStep = 1;
953             nextIndex = 0;
954             nextIndexInGroup = -1;
955         } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
956             moveStep = -1;
957             nextIndex = maxListItemIndex_;
958             nextIndexInGroup = -1;
959         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
960                 (step == FocusStep::TAB)) {
961             moveStep = 1;
962             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
963                 nextIndex = curIndex + moveStep;
964                 nextIndexInGroup = -1;
965             } else {
966                 nextIndexInGroup = curIndexInGroup + moveStep;
967             }
968         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
969                 (step == FocusStep::SHIFT_TAB)) {
970             moveStep = -1;
971             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
972                 nextIndex = curIndex + moveStep;
973                 nextIndexInGroup = -1;
974             } else {
975                 nextIndexInGroup = curIndexInGroup + moveStep;
976             }
977         }
978     } else {
979         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
980             moveStep = 1;
981             nextIndex = 0;
982             nextIndexInGroup = -1;
983         } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
984             moveStep = -1;
985             nextIndex = maxListItemIndex_;
986             nextIndexInGroup = -1;
987         } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
988             if (curIndexInGroup == -1) {
989                 moveStep = lanes_;
990                 nextIndex = curIndex + moveStep;
991                 nextIndexInGroup = -1;
992             } else {
993                 moveStep = curListItemGroupPara.lanes;
994                 nextIndexInGroup = curIndexInGroup + moveStep;
995             }
996         } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
997             if (curIndexInGroup == -1) {
998                 moveStep = -lanes_;
999                 nextIndex = curIndex + moveStep;
1000                 nextIndexInGroup = -1;
1001             } else {
1002                 moveStep = -curListItemGroupPara.lanes;
1003                 nextIndexInGroup = curIndexInGroup + moveStep;
1004             }
1005         } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1006             moveStep = 1;
1007             if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) || ((curIndexInGroup != -1) &&
1008                 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1009                 nextIndex = curIndex + moveStep;
1010                 nextIndexInGroup = -1;
1011             } else if ((curIndexInGroup != -1) &&
1012                 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1013                 nextIndexInGroup = curIndexInGroup + moveStep;
1014             }
1015         } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1016             moveStep = -1;
1017             if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) || ((curIndexInGroup != -1) &&
1018                 (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1019                 nextIndex = curIndex + moveStep;
1020                 nextIndexInGroup = -1;
1021             } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1022                 nextIndexInGroup = curIndexInGroup + moveStep;
1023             }
1024         }  else if (step == FocusStep::TAB) {
1025             moveStep = 1;
1026             if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1027                 nextIndex = curIndex + moveStep;
1028                 nextIndexInGroup = -1;
1029             } else {
1030                 nextIndexInGroup = curIndexInGroup + moveStep;
1031             }
1032         } else if (step == FocusStep::SHIFT_TAB) {
1033             moveStep = -1;
1034             if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1035                 nextIndex = curIndex + moveStep;
1036                 nextIndexInGroup = -1;
1037             } else {
1038                 nextIndexInGroup = curIndexInGroup + moveStep;
1039             }
1040         }
1041     }
1042     while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1043         if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1044             return nullptr;
1045         }
1046         auto nextFocusNode = ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep,
1047             step);
1048         if (nextFocusNode.Upgrade()) {
1049             return nextFocusNode;
1050         }
1051         if (nextIndexInGroup > -1) {
1052             nextIndexInGroup += moveStep;
1053         } else {
1054             nextIndex += moveStep;
1055         }
1056     }
1057     return nullptr;
1058 }
1059 
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1060 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1061 {
1062     LOGD("Get target item location is (%{public}d,%{public}d)", tarMainIndex, tarGroupIndex);
1063     auto listFrame = GetHost();
1064     CHECK_NULL_RETURN(listFrame, nullptr);
1065     auto listFocus = listFrame->GetFocusHub();
1066     CHECK_NULL_RETURN(listFocus, nullptr);
1067     auto childFocusList = listFocus->GetChildren();
1068     for (const auto& childFocus : childFocusList) {
1069         if (!childFocus->IsFocusable()) {
1070             continue;
1071         }
1072         auto childFrame = childFocus->GetFrameNode();
1073         if (!childFrame) {
1074             continue;
1075         }
1076         auto childPattern = childFrame->GetPattern();
1077         if (!childPattern) {
1078             continue;
1079         }
1080         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1081         if (!childItemPattern) {
1082             continue;
1083         }
1084         auto curIndex = childItemPattern->GetIndexInList();
1085         auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1086         if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1087             return AceType::WeakClaim(AceType::RawPtr(childFocus));
1088         }
1089     }
1090     LOGD("The target item at location(%{public}d,%{public}d) can not found.", tarMainIndex, tarGroupIndex);
1091     return nullptr;
1092 }
1093 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1094 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1095 {
1096     CHECK_NULL_RETURN_NOLOG(focusFrameNode, false);
1097     auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1098     CHECK_NULL_RETURN_NOLOG(focusPattern, false);
1099     auto curIndex = focusPattern->GetIndexInList();
1100     ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1101     auto pipeline = PipelineContext::GetCurrentContext();
1102     if (pipeline) {
1103         pipeline->FlushUITasks();
1104     }
1105     return true;
1106 }
1107 
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1108 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1109     int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1110 {
1111     auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1112     auto groupIndexInGroup = ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup,
1113         curIndexInGroup, moveStep, step, isScrollIndex);
1114 
1115     return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1116 }
1117 
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1118 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1119 {
1120     auto isScrollIndex = false;
1121     auto pipeline = PipelineContext::GetCurrentContext();
1122     CHECK_NULL_RETURN(pipeline, isScrollIndex);
1123     if (nextIndex < startIndex_) {
1124         if (nextIndexInGroup == -1) {
1125             isScrollIndex = true;
1126             ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1127             pipeline->FlushUITasks();
1128         } else {
1129             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1130             pipeline->FlushUITasks();
1131         }
1132     } else if (nextIndex > endIndex_) {
1133         if (nextIndexInGroup == -1) {
1134             isScrollIndex = true;
1135             ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1136             pipeline->FlushUITasks();
1137         } else {
1138             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1139             pipeline->FlushUITasks();
1140         }
1141     }
1142     return isScrollIndex;
1143 }
1144 
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1145 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1146     int32_t moveStep, FocusStep step, bool isScrollIndex)
1147 {
1148     auto groupIndexInGroup = true;
1149     auto pipeline = PipelineContext::GetCurrentContext();
1150     CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1151     RefPtr<FrameNode> nextIndexNode;
1152     auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1153     CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1154     if (!isNextInGroup) {
1155         nextIndexInGroup = -1;
1156         return groupIndexInGroup;
1157     }
1158     auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1159     if (nextIndexInGroup == -1) {
1160         auto scrollAlign = ScrollAlign::END;
1161         nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1162         if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) ||
1163             (step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1164             scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1165         } else {
1166             scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1167         }
1168         if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1169             (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1170             ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1171             pipeline->FlushUITasks();
1172         }
1173     } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1174         nextIndexInGroup = -1;
1175         groupIndexInGroup = false;
1176     } else {
1177         if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1178             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1179             pipeline->FlushUITasks();
1180         } else if ((nextIndexInGroup > curIndexInGroup) &&
1181             (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1182             ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1183             pipeline->FlushUITasks();
1184         }
1185     }
1186     return groupIndexInGroup;
1187 }
1188 
OnAnimateStop()1189 void ListPattern::OnAnimateStop()
1190 {
1191     scrollStop_ = true;
1192     MarkDirtyNodeSelf();
1193     isScrollEnd_ = true;
1194 }
1195 
ScrollTo(float position)1196 void ListPattern::ScrollTo(float position)
1197 {
1198     LOGI("ScrollTo:%{public}f", position);
1199     StopAnimate();
1200     jumpIndex_.reset();
1201     targetIndex_.reset();
1202     currentDelta_ = 0.0f;
1203     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1204     MarkDirtyNodeSelf();
1205     isScrollEnd_ = true;
1206 }
1207 
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align)1208 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align)
1209 {
1210     LOGI("ScrollToIndex:%{public}d, align:%{public}d.", index, align);
1211     SetScrollSource(SCROLL_FROM_JUMP);
1212     StopAnimate();
1213     auto host = GetHost();
1214     CHECK_NULL_VOID(host);
1215     auto totalItemCount = host->TotalChildCount();
1216     if ((index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM)) {
1217         currentDelta_ = 0.0f;
1218         smooth_ = smooth;
1219         if (smooth_) {
1220             targetIndex_ = index;
1221             if (index == ListLayoutAlgorithm::LAST_ITEM) {
1222                 targetIndex_ = totalItemCount - 1;
1223             } else if ((LessNotEqual(targetIndex_.value(), 0)) ||
1224                        (GreatOrEqual(targetIndex_.value(), totalItemCount))) {
1225                 targetIndex_.reset();
1226             }
1227         } else {
1228             jumpIndex_ = index;
1229         }
1230         scrollAlign_ = align;
1231         MarkDirtyNodeSelf();
1232     }
1233     isScrollEnd_ = true;
1234 }
1235 
ScrollToIndex(int32_t index,int32_t indexInGroup,ScrollAlign align)1236 void ListPattern::ScrollToIndex(int32_t index, int32_t indexInGroup, ScrollAlign align)
1237 {
1238     LOGI("ScrollToIndex:%{public}d, %{public}d, align:%{public}d.", index, indexInGroup, align);
1239     SetScrollSource(SCROLL_FROM_JUMP);
1240     StopAnimate();
1241     if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1242         currentDelta_ = 0;
1243         jumpIndex_ = index;
1244         jumpIndexInGroup_ = indexInGroup;
1245         scrollAlign_ = align;
1246         MarkDirtyNodeSelf();
1247     }
1248     isScrollEnd_ = true;
1249 }
1250 
ScrollToEdge(ScrollEdgeType scrollEdgeType)1251 void ListPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType)
1252 {
1253     LOGI("ScrollToEdge:%{public}zu", scrollEdgeType);
1254     if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1255         ScrollToIndex(0, smooth_, ScrollAlign::START);
1256     } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1257         ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, smooth_, ScrollAlign::END);
1258     }
1259 }
1260 
ScrollPage(bool reverse)1261 bool ListPattern::ScrollPage(bool reverse)
1262 {
1263     LOGI("ScrollPage:%{public}d", reverse);
1264     StopAnimate();
1265     float distance = reverse ? contentMainSize_ : -contentMainSize_;
1266     UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1267     isScrollEnd_ = true;
1268     return true;
1269 }
1270 
ScrollBy(float offset)1271 void ListPattern::ScrollBy(float offset)
1272 {
1273     StopAnimate();
1274     UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1275     isScrollEnd_ = true;
1276 }
1277 
GetCurrentOffset() const1278 Offset ListPattern::GetCurrentOffset() const
1279 {
1280     if (GetAxis() == Axis::HORIZONTAL) {
1281         return { GetTotalOffset(), 0.0 };
1282     }
1283     return { 0.0, GetTotalOffset() };
1284 }
1285 
UpdateScrollBarOffset()1286 void ListPattern::UpdateScrollBarOffset()
1287 {
1288     if (itemPosition_.empty()) {
1289         return;
1290     }
1291     if (!GetScrollBar() && !GetScrollBarProxy()) {
1292         return;
1293     }
1294     float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1295     float currentOffset = itemsSize / itemPosition_.size() * itemPosition_.begin()->first - startMainPos_;
1296     Offset scrollOffset = { currentOffset, currentOffset }; // fit for w/h switched.
1297     auto estimatedHeight = itemsSize / itemPosition_.size() * (maxListItemIndex_ + 1);
1298 
1299     // calculate padding offset of list
1300     auto host = GetHost();
1301     CHECK_NULL_VOID_NOLOG(host);
1302     auto layoutPriority = host->GetLayoutProperty();
1303     CHECK_NULL_VOID_NOLOG(layoutPriority);
1304     auto paddingOffset = layoutPriority->CreatePaddingAndBorder().Offset();
1305     Offset viewOffset = { paddingOffset.GetX(), paddingOffset.GetY() };
1306     const auto& geometryNode =  host->GetGeometryNode();
1307     auto frameSize = geometryNode->GetFrameSize();
1308     Size size(frameSize.Width(), frameSize.Height());
1309     UpdateScrollBarRegion(currentOffset, estimatedHeight, size, Offset(0.0f, 0.0f));
1310 }
1311 
GetTotalHeight() const1312 float ListPattern::GetTotalHeight() const
1313 {
1314     if (itemPosition_.empty()) {
1315         return 0.0f;
1316     }
1317 
1318     float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1319     return itemsSize / itemPosition_.size() * (maxListItemIndex_ + 1);
1320 }
1321 
SetChainAnimation()1322 void ListPattern::SetChainAnimation()
1323 {
1324     auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1325     CHECK_NULL_VOID(listLayoutProperty);
1326     auto edgeEffect = listLayoutProperty->GetEdgeEffect().value_or(EdgeEffect::SPRING);
1327     int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
1328     bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
1329     bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
1330     bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
1331     if (!enable) {
1332         chainAnimation_.Reset();
1333         return;
1334     }
1335     if (!chainAnimation_) {
1336         auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1337         springProperty_ =
1338             AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
1339         if (chainAnimationOptions_.has_value()) {
1340             float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
1341             float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
1342             if (GreatNotEqual(minSpace, maxSpace)) {
1343                 minSpace = space;
1344                 maxSpace = space;
1345             }
1346             springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1347             springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1348             chainAnimation_ =
1349                 AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1350             auto conductivity = chainAnimationOptions_.value().conductivity;
1351             if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1352                 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1353             }
1354             chainAnimation_->SetConductivity(conductivity);
1355             auto intensity = chainAnimationOptions_.value().intensity;
1356             if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1357                 intensity = ChainAnimation::DEFAULT_INTENSITY;
1358             }
1359             chainAnimation_->SetIntensity(intensity);
1360             auto effect = chainAnimationOptions_.value().edgeEffect;
1361             chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1362         } else {
1363             auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
1364             auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
1365             chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1366         }
1367         chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1368             auto list = weak.Upgrade();
1369             CHECK_NULL_VOID(list);
1370             list->MarkDirtyNodeSelf();
1371         });
1372     }
1373 }
1374 
SetChainAnimationOptions(const ChainAnimationOptions & options)1375 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
1376 {
1377     chainAnimationOptions_ = options;
1378     if (chainAnimation_) {
1379         auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1380         CHECK_NULL_VOID(listLayoutProperty);
1381         auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1382         float maxSpace = options.maxSpace.ConvertToPx();
1383         float minSpace = options.minSpace.ConvertToPx();
1384         if (GreatNotEqual(minSpace, maxSpace)) {
1385             minSpace = space;
1386             maxSpace = space;
1387         }
1388         chainAnimation_->SetSpace(space, maxSpace, minSpace);
1389         auto conductivity = chainAnimationOptions_.value().conductivity;
1390         if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1391             conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1392         }
1393         chainAnimation_->SetConductivity(conductivity);
1394         auto intensity = chainAnimationOptions_.value().intensity;
1395         if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1396             intensity = ChainAnimation::DEFAULT_INTENSITY;
1397         }
1398         chainAnimation_->SetIntensity(intensity);
1399         auto effect = options.edgeEffect;
1400         chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1401     }
1402     if (springProperty_) {
1403         springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1404         springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1405     }
1406 }
1407 
ProcessDragStart(float startPosition)1408 void ListPattern::ProcessDragStart(float startPosition)
1409 {
1410     CHECK_NULL_VOID_NOLOG(chainAnimation_);
1411     auto host = GetHost();
1412     CHECK_NULL_VOID(host);
1413     auto globalOffset = host->GetTransformRelativeOffset();
1414     int32_t index = -1;
1415     auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
1416     auto it = std::find_if(itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) {
1417         return offset <= pos.second.endPos;
1418     });
1419     if (it != itemPosition_.end()) {
1420         index = it->first;
1421     } else if (!itemPosition_.empty()) {
1422         index = itemPosition_.rbegin()->first + 1;
1423     }
1424     dragFromSpring_ = false;
1425     chainAnimation_->SetControlIndex(index);
1426     chainAnimation_->SetMaxIndex(maxListItemIndex_);
1427 }
1428 
ProcessDragUpdate(float dragOffset,int32_t source)1429 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
1430 {
1431     CHECK_NULL_VOID_NOLOG(chainAnimation_);
1432     if (NearZero(dragOffset)) {
1433         return;
1434     }
1435     if (NeedScrollSnapAlignEffect()) {
1436         auto delta = 0.0f;
1437         if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
1438             delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
1439         }
1440         if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
1441             delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
1442         }
1443         if (!NearZero(delta)) {
1444             auto scrollableEvent = GetScrollableEvent();
1445             CHECK_NULL_VOID(scrollableEvent);
1446             auto scrollable = scrollableEvent->GetScrollable();
1447             CHECK_NULL_VOID(scrollable);
1448             scrollable->UpdateScrollSnapStartOffset(delta);
1449             currentDelta_ -= delta;
1450         }
1451     }
1452     bool overDrag = (source == SCROLL_FROM_UPDATE) && (IsAtTop() || IsAtBottom());
1453     chainAnimation_->SetDelta(-dragOffset, overDrag);
1454 }
1455 
GetChainDelta(int32_t index) const1456 float ListPattern::GetChainDelta(int32_t index) const
1457 {
1458     CHECK_NULL_RETURN_NOLOG(chainAnimation_, 0.0f);
1459     return chainAnimation_->GetValue(index);
1460 }
1461 
MultiSelectWithoutKeyboard(const RectF & selectedZone)1462 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
1463 {
1464     auto host = GetHost();
1465     CHECK_NULL_VOID(host);
1466     std::list<RefPtr<FrameNode>> childrens;
1467     host->GenerateOneDepthVisibleFrame(childrens);
1468     for (const auto& item : childrens) {
1469         if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1470             auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
1471             CHECK_NULL_VOID(itemGroupPattern);
1472             auto itemGroupGeometry = item->GetGeometryNode();
1473             CHECK_NULL_VOID(itemGroupGeometry);
1474             auto itemGroupRect = itemGroupGeometry->GetFrameRect();
1475             if (!selectedZone.IsIntersectWith(itemGroupRect)) {
1476                 continue;
1477             }
1478             HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
1479             continue;
1480         }
1481         auto itemPattern = item->GetPattern<ListItemPattern>();
1482         CHECK_NULL_VOID(itemPattern);
1483         if (!itemPattern->Selectable()) {
1484             continue;
1485         }
1486 
1487         auto itemGeometry = item->GetGeometryNode();
1488         CHECK_NULL_VOID(itemGeometry);
1489 
1490         auto itemRect = itemGeometry->GetFrameRect();
1491         if (!selectedZone.IsIntersectWith(itemRect)) {
1492             itemPattern->MarkIsSelected(false);
1493         } else {
1494             itemPattern->MarkIsSelected(true);
1495         }
1496     }
1497 
1498     DrawSelectedZone(selectedZone);
1499 }
1500 
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)1501 void ListPattern::HandleCardModeSelectedEvent(
1502     const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
1503 {
1504     CHECK_NULL_VOID(itemGroupNode);
1505     std::list<RefPtr<FrameNode>> childrens;
1506     itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
1507     for (const auto& item : childrens) {
1508         auto itemPattern = item->GetPattern<ListItemPattern>();
1509         if (!itemPattern) {
1510             continue;
1511         }
1512         if (!itemPattern->Selectable()) {
1513             continue;
1514         }
1515         auto itemGeometry = item->GetGeometryNode();
1516         CHECK_NULL_VOID(itemGeometry);
1517         auto context = item->GetRenderContext();
1518         CHECK_NULL_VOID(context);
1519         auto itemRect = itemGeometry->GetFrameRect();
1520         RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
1521         if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
1522             itemPattern->MarkIsSelected(false);
1523         } else {
1524             itemPattern->MarkIsSelected(true);
1525         }
1526     }
1527 }
1528 
ClearMultiSelect()1529 void ListPattern::ClearMultiSelect()
1530 {
1531     auto host = GetHost();
1532     CHECK_NULL_VOID(host);
1533     std::list<RefPtr<FrameNode>> children;
1534     host->GenerateOneDepthAllFrame(children);
1535     for (const auto& child : children) {
1536         if (!child) {
1537             continue;
1538         }
1539         auto itemPattern = child->GetPattern<ListItemPattern>();
1540         if (itemPattern) {
1541             itemPattern->MarkIsSelected(false);
1542             continue;
1543         }
1544         auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
1545         if (itemGroupPattern) {
1546             std::list<RefPtr<FrameNode>> itemChildren;
1547             child->GenerateOneDepthAllFrame(itemChildren);
1548             for (const auto& item : itemChildren) {
1549                 if (!item) {
1550                     continue;
1551                 }
1552                 itemPattern = item->GetPattern<ListItemPattern>();
1553                 if (itemPattern) {
1554                     itemPattern->MarkIsSelected(false);
1555                 }
1556             }
1557         }
1558     }
1559 
1560     ClearSelectedZone();
1561 }
1562 
IsItemSelected(const MouseInfo & info)1563 bool ListPattern::IsItemSelected(const MouseInfo& info)
1564 {
1565     auto host = GetHost();
1566     CHECK_NULL_RETURN(host, false);
1567     auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1568     CHECK_NULL_RETURN_NOLOG(node, false);
1569     auto itemPattern = node->GetPattern<ListItemPattern>();
1570     if (itemPattern) {
1571         return itemPattern->IsSelected();
1572     }
1573     auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
1574     if (itemGroupPattern) {
1575         auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1576         CHECK_NULL_RETURN_NOLOG(itemNode, false);
1577         itemPattern = itemNode->GetPattern<ListItemPattern>();
1578         CHECK_NULL_RETURN_NOLOG(itemPattern, false);
1579         return itemPattern->IsSelected();
1580     }
1581     return false;
1582 }
1583 
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)1584 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
1585 {
1586     if (swiperItem_ != swiperItem) {
1587         auto item = swiperItem_.Upgrade();
1588         if (item) {
1589             item->SwiperReset();
1590         }
1591         swiperItem_ = std::move(swiperItem);
1592     }
1593 }
1594 
GetItemIndexByPosition(float xOffset,float yOffset)1595 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
1596 {
1597     auto host = GetHost();
1598     auto globalOffset = host->GetTransformRelativeOffset();
1599     float relativeX = xOffset - globalOffset.GetX();
1600     float relativeY = yOffset - globalOffset.GetY();
1601     float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
1602     float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
1603     float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
1604     int32_t lanesOffset = 0;
1605     if (lanes_ > 1) {
1606         lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
1607     }
1608     for (auto & pos : itemPosition_) {
1609         if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1610             return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
1611         }
1612     }
1613     if (!itemPosition_.empty()) {
1614         return itemPosition_.rbegin()->first + 1;
1615     }
1616     return 0;
1617 }
1618 
ToJsonValue(std::unique_ptr<JsonValue> & json) const1619 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1620 {
1621     json->Put("multiSelectable", multiSelectable_);
1622     json->Put("startIndex", startIndex_);
1623     if (!itemPosition_.empty()) {
1624         json->Put("itemStartPos", itemPosition_.begin()->second.startPos);
1625     }
1626     json->Put("friction", GetFriction());
1627 }
1628 
FromJson(const std::unique_ptr<JsonValue> & json)1629 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
1630 {
1631     ScrollToIndex(json->GetInt("startIndex"));
1632     if (json->Contains("itemStartPos")) {
1633         ScrollBy(-json->GetDouble("itemStartPos"));
1634     }
1635     auto host = GetHost();
1636     CHECK_NULL_VOID(host);
1637     host->GetRenderContext()->UpdateClipEdge(true);
1638     ScrollablePattern::FromJson(json);
1639 }
1640 
SetAccessibilityAction()1641 void ListPattern::SetAccessibilityAction()
1642 {
1643     auto host = GetHost();
1644     CHECK_NULL_VOID(host);
1645     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1646     CHECK_NULL_VOID(accessibilityProperty);
1647     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1648         const auto& pattern = weakPtr.Upgrade();
1649         CHECK_NULL_VOID(pattern);
1650         if (!pattern->IsScrollable()) {
1651             return;
1652         }
1653         pattern->ScrollPage(false);
1654     });
1655 
1656     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1657         const auto& pattern = weakPtr.Upgrade();
1658         CHECK_NULL_VOID(pattern);
1659         if (!pattern->IsScrollable()) {
1660             return;
1661         }
1662         pattern->ScrollPage(true);
1663     });
1664 }
1665 
GetListItemGroupParameter(const RefPtr<FrameNode> & node)1666 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
1667 {
1668     ListItemGroupPara listItemGroupPara = {-1, -1, -1, -1};
1669     auto curFrameParent = node->GetParent();
1670     auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1671     while (curFrameParent && (!curFrameParentNode)) {
1672         curFrameParent = curFrameParent->GetParent();
1673         curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1674     }
1675     CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
1676     if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1677         auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
1678         CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
1679         listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
1680         listItemGroupPara.displayStartIndex = itemGroupPattern->GetDiasplayStartIndexInGroup();
1681         listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
1682         listItemGroupPara.itemEndIndex =  itemGroupPattern->GetEndIndexInGroup();
1683         LOGD("TListPattern::GetListItemGroupParameter(%{public}d,%{public}d,%{public}d,%{public}d).",
1684             listItemGroupPara.displayEndIndex, listItemGroupPara.displayStartIndex,
1685             listItemGroupPara.lanes, listItemGroupPara.itemEndIndex);
1686     }
1687     return listItemGroupPara;
1688 }
1689 
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)1690 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
1691 {
1692     auto listFrame = GetHost();
1693     CHECK_NULL_RETURN(listFrame, false);
1694     auto listFocus = listFrame->GetFocusHub();
1695     CHECK_NULL_RETURN(listFocus, false);
1696     for (const auto& childFocus : listFocus->GetChildren()) {
1697         if (!childFocus->IsFocusable()) {
1698             continue;
1699         }
1700         if (auto childFrame = childFocus->GetFrameNode()) {
1701             if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
1702                 auto curIndex = childPattern->GetIndexInList();
1703                 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
1704                 if (curIndex == listIndex) {
1705                     node = childFrame;
1706                     return curIndexInGroup > -1;
1707                 }
1708             }
1709         }
1710     }
1711     return false;
1712 }
1713 
RefreshLanesItemRange()1714 void ListPattern::RefreshLanesItemRange()
1715 {
1716     auto host = GetHost();
1717     CHECK_NULL_VOID(host);
1718     auto updatePos = host->GetChildrenUpdated();
1719     if (updatePos == -1) {
1720         return;
1721     }
1722     if (updatePos == 0) {
1723         lanesItemRange_.clear();
1724         return;
1725     }
1726     for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
1727         if (it->second < updatePos) {
1728             it++;
1729         } else if (it->first >= updatePos) {
1730             lanesItemRange_.erase(it++);
1731         } else {
1732             it->second = updatePos - 1;
1733             it++;
1734         }
1735     }
1736 }
1737 } // namespace OHOS::Ace::NG
1738