• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/geometry/point.h"
20 #include "base/log/dump_log.h"
21 #include "base/perfmonitor/perf_constants.h"
22 #include "base/perfmonitor/perf_monitor.h"
23 #include "base/ressched/ressched_report.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/components_ng/base/inspector_filter.h"
27 #include "core/components_ng/base/observer_handler.h"
28 #include "core/components_ng/manager/select_overlay/select_overlay_scroll_notifier.h"
29 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
30 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
31 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
32 #include "core/components_ng/pattern/scrollable/scrollable.h"
33 #include "core/components_ng/pattern/scrollable/scrollable_event_hub.h"
34 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
35 #include "core/pipeline/pipeline_base.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 
38 namespace OHOS::Ace::NG {
39 namespace {
40 constexpr Color SELECT_FILL_COLOR = Color(0x1A000000);
41 constexpr Color SELECT_STROKE_COLOR = Color(0x33FFFFFF);
42 constexpr float CUSTOM_ANIMATION_DURATION = 1000.0;
43 constexpr uint32_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
44 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
45 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; //max 100ms
46 constexpr uint32_t DEFAULT_VSYNC_DIFF_TIME = 16 * 1000 * 1000; // default is 16 ms
47 constexpr uint32_t EVENTS_FIRED_INFO_COUNT = 50;
48 constexpr uint32_t SCROLLABLE_FRAME_INFO_COUNT = 50;
49 const std::string SCROLLABLE_DRAG_SCENE = "scrollable_drag_scene";
50 const std::string SCROLL_BAR_DRAG_SCENE = "scrollBar_drag_scene";
51 const std::string SCROLLABLE_MOTION_SCENE = "scrollable_motion_scene";
52 const std::string SCROLLABLE_MULTI_TASK_SCENE = "scrollable_multi_task_scene";
53 const std::string SCROLL_IN_HOTZONE_SCENE = "scroll_in_hotzone_scene";
54 const std::string CUSTOM_SCROLL_BAR_SCENE = "custom_scroll_bar_scene";
55 } // namespace
56 using std::chrono::high_resolution_clock;
57 using std::chrono::milliseconds;
58 
ScrollablePattern()59 ScrollablePattern::ScrollablePattern()
60 {
61     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
62     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
63 }
64 
ScrollablePattern(EdgeEffect edgeEffect,bool alwaysEnabled)65 ScrollablePattern::ScrollablePattern(EdgeEffect edgeEffect, bool alwaysEnabled)
66     : edgeEffect_(edgeEffect), edgeEffectAlwaysEnabled_(alwaysEnabled)
67 {
68     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
69     friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
70 }
71 
CreatePaintProperty()72 RefPtr<PaintProperty> ScrollablePattern::CreatePaintProperty()
73 {
74     auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
75     auto property = MakeRefPtr<ScrollablePaintProperty>();
76     property->UpdateScrollBarMode(defaultDisplayMode);
77     return property;
78 }
79 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const80 void ScrollablePattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
81 {
82     /* no fixed attr below, just return */
83     if (filter.IsFastFilter()) {
84         return;
85     }
86     json->PutExtAttr("friction", GetFriction(), filter);
87     if (edgeEffect_ == EdgeEffect::SPRING) {
88         json->PutExtAttr("edgeEffect", "EdgeEffect.Spring", filter);
89     } else if (edgeEffect_ == EdgeEffect::FADE) {
90         json->PutExtAttr("edgeEffect", "EdgeEffect.Fade", filter);
91     } else {
92         json->PutExtAttr("edgeEffect", "EdgeEffect.None", filter);
93     }
94     json->PutExtAttr("flingSpeedLimit",
95         Dimension(maxFlingVelocity_, DimensionUnit::VP).ToString().c_str(), filter);
96     auto JsonEdgeEffectOptions = JsonUtil::Create(true);
97     JsonEdgeEffectOptions->Put("alwaysEnabled", GetAlwaysEnabled());
98     json->PutExtAttr("edgeEffectOptions", JsonEdgeEffectOptions, filter);
99 
100     auto nestedScrollOptions = JsonUtil::Create(true);
101     auto nestedScroll = GetNestedScroll();
102     nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
103     nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
104     json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
105 }
106 
SetAxis(Axis axis)107 void ScrollablePattern::SetAxis(Axis axis)
108 {
109     if (axis_ == axis) {
110         return;
111     }
112     axis_ = axis;
113     SetParentScrollable();
114     if (scrollBar_) {
115         auto positionMode = GetPositionMode();
116         scrollBar_->SetPositionMode(positionMode);
117         if (scrollBarOverlayModifier_) {
118             scrollBarOverlayModifier_->SetPositionMode(positionMode);
119         }
120     }
121     auto gestureHub = GetGestureHub();
122     CHECK_NULL_VOID(gestureHub);
123     if (scrollableEvent_) {
124         gestureHub->RemoveScrollableEvent(scrollableEvent_);
125         scrollableEvent_->SetAxis(axis);
126         gestureHub->AddScrollableEvent(scrollableEvent_);
127     }
128     if (scrollEffect_) {
129         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
130         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
131     }
132 }
133 
GetGestureHub()134 RefPtr<GestureEventHub> ScrollablePattern::GetGestureHub()
135 {
136     auto host = GetHost();
137     CHECK_NULL_RETURN(host, nullptr);
138     auto hub = host->GetEventHub<EventHub>();
139     CHECK_NULL_RETURN(hub, nullptr);
140     return hub->GetOrCreateGestureEventHub();
141 }
142 
GetInputHub()143 RefPtr<InputEventHub> ScrollablePattern::GetInputHub()
144 {
145     auto host = GetHost();
146     CHECK_NULL_RETURN(host, nullptr);
147     auto hub = host->GetEventHub<EventHub>();
148     CHECK_NULL_RETURN(host, nullptr);
149     return hub->GetOrCreateInputEventHub();
150 }
151 
OnScrollCallback(float offset,int32_t source)152 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
153 {
154     if (source == SCROLL_FROM_START) {
155         FireOnScrollStart();
156         return true;
157     }
158     SuggestOpIncGroup(true);
159     return UpdateCurrentOffset(offset, source);
160 }
161 
ProcessNavBarReactOnStart()162 void ScrollablePattern::ProcessNavBarReactOnStart()
163 {
164     CHECK_NULL_VOID(navBarPattern_);
165     navBarPattern_->OnCoordScrollStart();
166 }
167 
ProcessNavBarReactOnUpdate(float offset)168 float ScrollablePattern::ProcessNavBarReactOnUpdate(float offset)
169 {
170     CHECK_NULL_RETURN(navBarPattern_, false);
171     return navBarPattern_->OnCoordScrollUpdate(offset);
172 }
173 
ProcessNavBarReactOnEnd()174 void ScrollablePattern::ProcessNavBarReactOnEnd()
175 {
176     CHECK_NULL_VOID(navBarPattern_);
177     navBarPattern_->OnCoordScrollEnd();
178 }
179 
OnScrollPosition(double & offset,int32_t source)180 bool ScrollablePattern::OnScrollPosition(double& offset, int32_t source)
181 {
182     auto isSearchRefresh = GetIsSearchRefresh();
183     if (needLinked_) {
184         bool isAtTop = IsAtTop();
185         auto isAtTopAndPositive = (isAtTop && Positive(offset));
186         auto refreshCoordinateMode = RefreshCoordinationMode::UNKNOWN;
187         auto modalSheetCoordinationMode = ModalSheetCoordinationMode::UNKNOWN;
188         if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
189             modalSheetCoordinationMode = CoordinateWithSheet(offset, source, isAtTopAndPositive);
190         }
191         if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE) ||
192             !isSearchRefresh) {
193             refreshCoordinateMode = CoordinateWithRefresh(offset, source, isAtTopAndPositive);
194         }
195         auto navigationInCoordination = CoordinateWithNavigation(offset, source, isAtTop);
196         if ((refreshCoordinateMode == RefreshCoordinationMode::REFRESH_SCROLL) || navigationInCoordination ||
197             (modalSheetCoordinationMode == ModalSheetCoordinationMode::SHEET_SCROLL)) {
198             return false;
199         }
200     }
201 
202     if (source == SCROLL_FROM_START) {
203         SetParentScrollable();
204         StopScrollBarAnimatorByProxy();
205         AbortScrollAnimator();
206     } else if (!AnimateStoped()) {
207         return false;
208     }
209     return true;
210 }
211 
212 namespace {
FromDrag(int32_t source)213 inline bool FromDrag(int32_t source)
214 {
215     return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS;
216 }
217 } // namespace
218 
NeedSplitScroll(OverScrollOffset & overOffsets,int32_t source)219 bool ScrollablePattern::NeedSplitScroll(OverScrollOffset& overOffsets, int32_t source)
220 {
221     return GreatNotEqual(overOffsets.start, 0.0) && refreshCoordination_ && refreshCoordination_->InCoordination() &&
222            !isRefreshInReactive_ &&
223            (FromDrag(source) || source == SCROLL_FROM_ANIMATION_SPRING ||
224                source == SCROLL_FROM_ANIMATION) &&
225            axis_ == Axis::VERTICAL;
226 }
227 
CoordinateWithRefresh(double & offset,int32_t source,bool isAtTop)228 RefreshCoordinationMode ScrollablePattern::CoordinateWithRefresh(double& offset, int32_t source, bool isAtTop)
229 {
230     // use first scroll update to trigger scrollStart. Ignore SCROLL_FROM_START.
231     if (source == SCROLL_FROM_START) {
232         return RefreshCoordinationMode::UNKNOWN;
233     }
234     if (!refreshCoordination_) {
235         CreateRefreshCoordination();
236     }
237     auto overOffsets = GetOverScrollOffset(offset);
238     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !IsAtTop() && Positive(offset) &&
239         NeedSplitScroll(overOffsets, source)) {
240         offset = offset - overOffsets.start;
241         OnScrollCallback(offset, source);
242         isRefreshInReactive_ = true;
243         refreshCoordination_->OnScrollStart(FromDrag(source), GetVelocity());
244     }
245     bool hasScrollSpace = Positive(offset) || (Negative(offset) && refreshCoordination_->IsRefreshInScroll());
246     if (IsAtTop() && hasScrollSpace &&
247         (FromDrag(source) || source == SCROLL_FROM_ANIMATION) &&
248         !isRefreshInReactive_ && (axis_ == Axis::VERTICAL)) {
249         isRefreshInReactive_ = true;
250         refreshCoordination_->OnScrollStart(FromDrag(source), GetVelocity());
251     }
252     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
253         refreshCoordination_->InCoordination() && source != SCROLL_FROM_UPDATE &&
254         source != SCROLL_FROM_AXIS && isRefreshInReactive_) {
255         isRefreshInReactive_ = false;
256         refreshCoordination_->OnScrollEnd(GetVelocity());
257     }
258     auto mode = RefreshCoordinationMode::UNKNOWN;
259     if (refreshCoordination_->InCoordination() && isRefreshInReactive_) {
260         if (!refreshCoordination_->OnScroll(
261                 GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset, GetVelocity())) {
262             isRefreshInReactive_ = false;
263         }
264         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
265             mode = RefreshCoordinationMode::REFRESH_SCROLL;
266         } else {
267             if (scrollEffect_ && scrollEffect_->IsSpringEffect()) {
268                 mode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
269             } else {
270                 mode = RefreshCoordinationMode::REFRESH_SCROLL;
271             }
272         }
273     }
274     return mode;
275 }
276 
CoordinateWithSheet(double & offset,int32_t source,bool isAtTop)277 ModalSheetCoordinationMode ScrollablePattern::CoordinateWithSheet(double& offset, int32_t source, bool isAtTop)
278 {
279     auto coordinationMode = ModalSheetCoordinationMode::UNKNOWN;
280     if (source == SCROLL_FROM_START) {
281         isSheetInReactive_ = false;
282 
283         if (!sheetPattern_) {
284             GetParentModalSheet();
285         }
286     }
287     auto overOffsets = GetOverScrollOffset(offset);
288     if (IsAtTop() && (source == SCROLL_FROM_UPDATE) && !isSheetInReactive_ && (axis_ == Axis::VERTICAL)) {
289         isSheetInReactive_ = true;
290         if (sheetPattern_) {
291             sheetPattern_->OnCoordScrollStart();
292         }
293     }
294     if (sheetPattern_ && isSheetInReactive_) {
295         if (!sheetPattern_->OnCoordScrollUpdate(GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset)) {
296             isSheetInReactive_ = false;
297             coordinationMode = ModalSheetCoordinationMode::SCROLLABLE_SCROLL;
298         } else {
299             coordinationMode = ModalSheetCoordinationMode::SHEET_SCROLL;
300         }
301     }
302     return coordinationMode;
303 }
304 
CoordinateWithNavigation(double & offset,int32_t source,bool isAtTop)305 bool ScrollablePattern::CoordinateWithNavigation(double& offset, int32_t source, bool isAtTop)
306 {
307     if (source == SCROLL_FROM_START) {
308         GetParentNavigation();
309         CHECK_NULL_RETURN(navBarPattern_, false);
310         if (isAtTop && Positive(offset)) {
311             // Starting coordinating scroll at the beginning of scrolling.
312             isReactInParentMovement_ = true;
313             ProcessNavBarReactOnStart();
314         }
315         return false;
316     }
317 
318     CHECK_NULL_RETURN(navBarPattern_ && navBarPattern_->NeedCoordWithScroll(), false);
319 
320     float diff = navBarPattern_->GetTitleBarHeightLessThanMaxBarHeight();
321     auto overOffsets = GetOverScrollOffset(offset + std::max(diff, 0.0f));
322     overOffsets.start = Positive(offset) ? std::min(offset, overOffsets.start) : overOffsets.start;
323     float offsetRemain = 0.0f;
324     float offsetCoordinate = offset;
325 
326     if (!isReactInParentMovement_ && NeedCoordinateScrollWithNavigation(offset, source, overOffsets)) {
327         // Starting coordinating scroll during sliding or flipping.
328         isReactInParentMovement_ = true;
329         ProcessNavBarReactOnStart();
330     }
331 
332     if (isReactInParentMovement_) {
333         if (Positive(offset)) {
334             offsetRemain = offset - overOffsets.start;
335             offsetCoordinate = overOffsets.start;
336         }
337         float handledByNav = ProcessNavBarReactOnUpdate(offsetCoordinate);
338         if (NearEqual(handledByNav, offsetCoordinate) && !NearZero(offset)) {
339             // All offsets are handled by Navigation, list cannot scroll over.
340             SetCanOverScroll(false);
341             offset = offsetRemain;
342         } else {
343             offset = offsetRemain + (offsetCoordinate - handledByNav);
344         }
345         if (Negative(diff) && Negative(offset)) {
346             offset = overOffsets.start;
347         }
348 
349         if (Negative(offset) && (source == SCROLL_FROM_ANIMATION_SPRING || !navBarPattern_->CanCoordScrollUp(offset))) {
350             // When rebounding form scrolling over, trigger the ProcessNavBarReactOnEnd callback.
351             isReactInParentMovement_ = false;
352             ProcessNavBarReactOnEnd();
353         }
354     }
355 
356     return false;
357 }
358 
SetUiDvsyncSwitch(bool on)359 void ScrollablePattern::SetUiDvsyncSwitch(bool on)
360 {
361     auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
362     CHECK_NULL_VOID(context);
363     if (on && inScrollingStatus_) {
364         inScrollingStatus_ = false;
365         context->SetUiDvsyncSwitch(true);
366         switchOnStatus_ = true;
367     } else if (!on && switchOnStatus_) {
368         context->SetUiDvsyncSwitch(false);
369         switchOnStatus_ = false;
370     }
371 }
372 
OnScrollEnd()373 void ScrollablePattern::OnScrollEnd()
374 {
375     // Previous: Sets ScrollablePattern::OnScrollEnd to Scrollable->scrollEndCallback_
376     // Scrollable calls scrollEndCallback_ in HandleOverScroll
377 
378     // Now: HandleOverScroll moved to ScrollablePattern and renamed HandleScrollVelocity, directly
379     // calls OnScrollEnd in ScrollablePattern
380     if (refreshCoordination_) {
381         isRefreshInReactive_ = false;
382         refreshCoordination_->OnScrollEnd(GetVelocity());
383     }
384     if (isSheetInReactive_) {
385         isSheetInReactive_ = false;
386         if (sheetPattern_) {
387             sheetPattern_->OnCoordScrollEnd(GetVelocity());
388         }
389     }
390     if (isReactInParentMovement_) {
391         isReactInParentMovement_ = false;
392         ProcessNavBarReactOnEnd();
393     }
394     if (isAnimationStop_) {
395         SetUiDvsyncSwitch(false);
396     }
397     if (scrollStop_) {
398         scrollAbort_ = false;
399     }
400     OnScrollEndCallback();
401     SelectOverlayScrollNotifier::NotifyOnScrollEnd(WeakClaim(this));
402 }
403 
AttachAnimatableProperty(RefPtr<Scrollable> scrollable)404 void ScrollablePattern::AttachAnimatableProperty(RefPtr<Scrollable> scrollable)
405 {
406     auto host = GetHost();
407     CHECK_NULL_VOID(host);
408     auto renderContext = host->GetRenderContext();
409     CHECK_NULL_VOID(renderContext);
410     auto property = scrollable->GetFrictionProperty();
411     renderContext->AttachNodeAnimatableProperty(property);
412 
413     property = scrollable->GetSpringProperty();
414     renderContext->AttachNodeAnimatableProperty(property);
415     property = scrollable->GetSnapProperty();
416     renderContext->AttachNodeAnimatableProperty(property);
417 }
418 
AddScrollEvent()419 void ScrollablePattern::AddScrollEvent()
420 {
421     auto host = GetHost();
422     CHECK_NULL_VOID(host);
423     auto gestureHub = GetGestureHub();
424     CHECK_NULL_VOID(gestureHub);
425     if (scrollableEvent_) {
426         gestureHub->RemoveScrollableEvent(scrollableEvent_);
427     }
428     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
429         auto pattern = weak.Upgrade();
430         CHECK_NULL_RETURN(pattern, false);
431         return pattern->HandleScrollImpl(static_cast<float>(offset), source);
432     };
433     auto scrollable = MakeRefPtr<Scrollable>(std::move(scrollCallback), GetAxis());
434     scrollable->SetNodeId(host->GetAccessibilityId());
435     scrollable->SetNodeTag(host->GetTag());
436     scrollable->Initialize(host->GetContextRefPtr());
437     AttachAnimatableProperty(scrollable);
438 
439     // move HandleScroll and HandleOverScroll to ScrollablePattern by setting callbacks to scrollable
440     auto handleScroll = [weak = AceType::WeakClaim(this)](
441                             float offset, int32_t source, NestedState state) -> ScrollResult {
442         auto pattern = weak.Upgrade();
443         if (pattern) {
444             return pattern->HandleScroll(offset, source, state, pattern->GetVelocity());
445         }
446         return {};
447     };
448     scrollable->SetHandleScrollCallback(std::move(handleScroll));
449 
450     scrollable->SetOverScrollCallback([weak = WeakClaim(this)](float velocity) {
451         auto pattern = weak.Upgrade();
452         CHECK_NULL_RETURN(pattern, false);
453         return pattern->HandleOverScroll(velocity);
454     });
455 
456     scrollable->SetIsReverseCallback([weak = WeakClaim(this)]() {
457         auto pattern = weak.Upgrade();
458         CHECK_NULL_RETURN(pattern, false);
459         return pattern->IsReverse();
460     });
461 
462     auto scrollStart = [weak = WeakClaim(this)](float position) {
463         auto pattern = weak.Upgrade();
464         CHECK_NULL_VOID(pattern);
465         pattern->FireAndCleanScrollingListener();
466         pattern->OnScrollStartRecursiveInner(weak, position, pattern->GetVelocity());
467     };
468     scrollable->SetOnScrollStartRec(std::move(scrollStart));
469 
470     auto scrollEndRec = [weak = WeakClaim(this)](const std::optional<float>& velocity) {
471         auto pattern = weak.Upgrade();
472         CHECK_NULL_VOID(pattern);
473         pattern->OnScrollEndRecursiveInner(velocity);
474     };
475     scrollable->SetOnScrollEndRec(std::move(scrollEndRec));
476 
477     auto scrollEnd = [weak = WeakClaim(this)]() {
478         auto pattern = weak.Upgrade();
479         CHECK_NULL_VOID(pattern);
480         pattern->OnScrollEnd();
481     };
482     scrollable->SetScrollEndCallback(std::move(scrollEnd));
483 
484     auto RemainVelocityToChild = [weak = WeakClaim(this)](float remainVelocity) -> bool {
485         auto pattern = weak.Upgrade();
486         CHECK_NULL_RETURN(pattern, false);
487         auto child = pattern->GetScrollOriginChild();
488         if (child) {
489             child->RemainVelocityToChild(remainVelocity);
490             return true;
491         }
492         return false;
493     };
494     scrollable->SetRemainVelocityCallback(std::move(RemainVelocityToChild));
495 
496     auto dragEnd = [weak = WeakClaim(this)]() {
497         auto pattern = weak.Upgrade();
498         CHECK_NULL_VOID(pattern);
499         pattern->OnScrollDragEndRecursive();
500     };
501     scrollable->SetDragEndCallback(std::move(dragEnd));
502 
503     scrollable->SetUnstaticFriction(friction_);
504     scrollable->SetMaxFlingVelocity(maxFlingVelocity_);
505 
506     auto scrollSnap = [weak = WeakClaim(this)](double targetOffset, double velocity) -> bool {
507         auto pattern = weak.Upgrade();
508         CHECK_NULL_RETURN(pattern, false);
509         return pattern->OnScrollSnapCallback(targetOffset, velocity);
510     };
511     scrollable->SetOnScrollSnapCallback(scrollSnap);
512 
513     auto calePredictSnapOffsetCallback =
514             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
515         auto pattern = weak.Upgrade();
516         std::optional<float> predictSnapOffset;
517         CHECK_NULL_RETURN(pattern, predictSnapOffset);
518         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
519     };
520     scrollable->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
521 
522     auto needScrollSnapToSideCallback = [weak = WeakClaim(this)](float delta) -> bool {
523         auto pattern = weak.Upgrade();
524         CHECK_NULL_RETURN(pattern, false);
525         return pattern->NeedScrollSnapToSide(delta);
526     };
527     scrollable->SetNeedScrollSnapToSideCallback(std::move(needScrollSnapToSideCallback));
528 
529     auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
530         auto pattern = weak.Upgrade();
531         CHECK_NULL_VOID(pattern);
532         if (sceneStatus == NG::SceneStatus::START) {
533             pattern->inScrollingStatus_ = true;
534             pattern->SetUiDvsyncSwitch(false);
535         } else if (sceneStatus == NG::SceneStatus::END) {
536             pattern->SetUiDvsyncSwitch(true);
537         }
538         return pattern->NotifyFRCSceneInfo(SCROLLABLE_DRAG_SCENE, velocity, sceneStatus);
539     };
540     scrollable->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
541 
542     scrollable->SetOnContinuousSliding([weak = WeakClaim(this)]() -> double {
543         auto pattern = weak.Upgrade();
544         CHECK_NULL_RETURN(pattern, 0.0);
545         return pattern->GetMainContentSize();
546     });
547     scrollable->AddPanActionEndEvent([weak = WeakClaim(this)](GestureEvent& info) {
548         auto pattern = weak.Upgrade();
549         CHECK_NULL_VOID(pattern);
550         pattern->FireObserverOnPanActionEnd(info);
551     });
552 
553     scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
554     scrollableEvent_->SetScrollable(scrollable);
555     scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
556         auto pattern = weakScroll.Upgrade();
557         CHECK_NULL_RETURN(pattern, 0.0f);
558         float nestedVelocity = pattern->GetNestedScrollVelocity();
559         if (std::abs(nestedVelocity) > std::abs(pattern->GetCurrentVelocity())) {
560             return nestedVelocity;
561         }
562         return pattern->GetCurrentVelocity();
563     });
564     gestureHub->AddScrollableEvent(scrollableEvent_);
565     InitTouchEvent(gestureHub);
566     RegisterWindowStateChangedCallback();
567 }
568 
StopScrollAnimation()569 void ScrollablePattern::StopScrollAnimation()
570 {
571     StopScrollable();
572 }
573 
OnTouchDown(const TouchEventInfo & info)574 void ScrollablePattern::OnTouchDown(const TouchEventInfo& info)
575 {
576     if (GetNestedScrolling() && !NearZero(GetNestedScrollVelocity())) {
577         auto child = GetScrollOriginChild();
578         CHECK_NULL_VOID(child);
579         child->StopScrollAnimation();
580     }
581 }
582 
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)583 void ScrollablePattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
584 {
585     // use TouchEvent to receive next touch down event to stop animation.
586     if (touchEvent_) {
587         return;
588     }
589     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
590         auto pattern = weak.Upgrade();
591         CHECK_NULL_VOID(pattern);
592         pattern->FireObserverOnTouch(info);
593         CHECK_NULL_VOID(pattern->scrollableEvent_);
594         auto scrollable = pattern->scrollableEvent_->GetScrollable();
595         CHECK_NULL_VOID(scrollable);
596         switch (info.GetTouches().front().GetTouchType()) {
597             case TouchType::DOWN:
598                 scrollable->HandleTouchDown();
599                 pattern->OnTouchDown(info);
600                 break;
601             case TouchType::UP:
602                 scrollable->HandleTouchUp();
603                 break;
604             case TouchType::CANCEL:
605                 scrollable->HandleTouchCancel();
606                 break;
607             default:
608                 break;
609         }
610     };
611     if (touchEvent_) {
612         gestureHub->RemoveTouchEvent(touchEvent_);
613     }
614     touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
615     gestureHub->AddTouchEvent(touchEvent_);
616 }
617 
RegisterWindowStateChangedCallback()618 void ScrollablePattern::RegisterWindowStateChangedCallback()
619 {
620     auto host = GetHost();
621     CHECK_NULL_VOID(host);
622     auto context = NG::PipelineContext::GetCurrentContext();
623     CHECK_NULL_VOID(context);
624     context->AddWindowStateChangedCallback(host->GetId());
625 }
626 
OnDetachFromFrameNode(FrameNode * frameNode)627 void ScrollablePattern::OnDetachFromFrameNode(FrameNode* frameNode)
628 {
629     auto context = NG::PipelineContext::GetCurrentContext();
630     CHECK_NULL_VOID(context);
631     context->RemoveWindowStateChangedCallback(frameNode->GetId());
632 }
633 
OnWindowHide()634 void ScrollablePattern::OnWindowHide()
635 {
636     CHECK_NULL_VOID(scrollableEvent_);
637     auto scrollable = scrollableEvent_->GetScrollable();
638     CHECK_NULL_VOID(scrollable);
639     scrollable->StopFrictionAnimation();
640 }
641 
SetEdgeEffect(EdgeEffect edgeEffect)642 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
643 {
644     auto gestureHub = GetGestureHub();
645     CHECK_NULL_VOID(gestureHub);
646     if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
647         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
648         scrollEffect_.Reset();
649     }
650     if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
651         auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
652         CHECK_NULL_VOID(springEffect);
653         springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
654             auto pattern = weak.Upgrade();
655             CHECK_NULL_RETURN(pattern, false);
656             return pattern->OutBoundaryCallback();
657         });
658         // add callback to springEdgeEffect
659         SetEdgeEffectCallback(springEffect);
660         scrollEffect_ = springEffect;
661         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
662     }
663     if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
664         auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
665         CHECK_NULL_VOID(fadeEdgeEffect);
666         fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
667             auto pattern = weakScroll.Upgrade();
668             CHECK_NULL_VOID(pattern);
669             auto host = pattern->GetHost();
670             CHECK_NULL_VOID(host);
671             host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
672         });
673         SetEdgeEffectCallback(fadeEdgeEffect);
674         fadeEdgeEffect->InitialEdgeEffect();
675         scrollEffect_ = fadeEdgeEffect;
676         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
677     }
678     CHECK_NULL_VOID(scrollableEvent_);
679     auto scrollable = scrollableEvent_->GetScrollable();
680     CHECK_NULL_VOID(scrollable);
681     scrollable->SetEdgeEffect(edgeEffect);
682     if (edgeEffect != EdgeEffect::SPRING) {
683         scrollable->StopSpringAnimation(true);
684     }
685 }
686 
HandleFadeEffect(float offset,int32_t source,const SizeF & size,bool isNotPositiveScrollableDistance)687 void ScrollablePattern::HandleFadeEffect(float offset, int32_t source, const SizeF& size,
688     bool isNotPositiveScrollableDistance)
689 {
690     auto isScrollFromUpdate = source == SCROLL_FROM_UPDATE;
691     scrollEffect_->HandleOverScroll(GetAxis(), IsReverse() ? offset : -offset,
692         size, isScrollFromUpdate, isNotPositiveScrollableDistance);
693 }
694 
HandleEdgeEffect(float offset,int32_t source,const SizeF & size)695 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size)
696 {
697     bool isAtTop = IsAtTop();
698     bool isAtBottom = IsAtBottom();
699     bool isNotPositiveScrollableDistance = isAtTop && isAtBottom;
700     // check edgeEffect is not springEffect
701     if (scrollEffect_ && scrollEffect_->IsFadeEffect() &&
702         (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION)) { // handle edge effect
703         if ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset))) {
704             HandleFadeEffect(offset, source, size, isNotPositiveScrollableDistance);
705         }
706     }
707     if (!(scrollEffect_ && scrollEffect_->IsSpringEffect() &&
708             (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION ||
709                 source == SCROLL_FROM_ANIMATION_SPRING ||
710                 (source == SCROLL_FROM_ANIMATION_CONTROLLER && animateCanOverScroll_)))) {
711         if (isAtTop && Positive(offset)) {
712             animateOverScroll_ = false;
713             return false;
714         }
715         if (isAtBottom && Negative(offset)) {
716             animateOverScroll_ = false;
717             return false;
718         }
719     }
720     animateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && (isAtTop || isAtBottom);
721     isAnimateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && animateCanOverScroll_ &&
722                             ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset)));
723     return true;
724 }
725 
RegisterScrollBarEventTask()726 void ScrollablePattern::RegisterScrollBarEventTask()
727 {
728     CHECK_NULL_VOID(scrollBar_);
729     auto host = GetHost();
730     CHECK_NULL_VOID(host);
731     scrollBar_->SetAxis(axis_);
732     scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
733         auto host = weak.Upgrade();
734         CHECK_NULL_VOID(host);
735         host->MarkNeedRenderOnly();
736     });
737     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
738         auto pattern = weak.Upgrade();
739         CHECK_NULL_RETURN(pattern, false);
740         return pattern->OnScrollCallback(static_cast<float>(offset), source);
741     };
742     scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
743     auto scrollEnd = [weak = WeakClaim(this)]() {
744         auto pattern = weak.Upgrade();
745         CHECK_NULL_VOID(pattern);
746         pattern->OnScrollEnd();
747     };
748     scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
749     auto calePredictSnapOffsetCallback =
750             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
751         auto pattern = weak.Upgrade();
752         CHECK_NULL_RETURN(pattern, std::optional<float>());
753         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
754     };
755     scrollBar_->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
756     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
757         auto pattern = weak.Upgrade();
758         CHECK_NULL_VOID(pattern);
759         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
760     };
761     scrollBar_->SetStartScrollSnapMotionCallback(std::move(startScrollSnapMotionCallback));
762 
763     auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
764         auto pattern = weak.Upgrade();
765         CHECK_NULL_VOID(pattern);
766         return pattern->NotifyFRCSceneInfo(SCROLL_BAR_DRAG_SCENE, velocity, sceneStatus);
767     };
768     scrollBar_->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
769     InitScrollBarGestureEvent();
770 }
771 
InitScrollBarGestureEvent()772 void ScrollablePattern::InitScrollBarGestureEvent()
773 {
774     auto gestureHub = GetGestureHub();
775     CHECK_NULL_VOID(gestureHub);
776     auto inputHub = GetInputHub();
777     CHECK_NULL_VOID(inputHub);
778     scrollBar_->SetGestureEvent();
779     scrollBar_->SetMouseEvent();
780     scrollBar_->SetHoverEvent();
781     gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
782     inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
783     inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
784     CHECK_NULL_VOID(scrollableEvent_);
785     scrollableEvent_->SetInBarRegionCallback(
786         [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
787             auto scrollBar = weak.Upgrade();
788             CHECK_NULL_RETURN(scrollBar, false);
789             if (source == SourceType::MOUSE) {
790                 return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
791             }
792             return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
793         });
794     scrollableEvent_->SetBarCollectTouchTargetCallback(
795         [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const OffsetF& coordinateOffset,
796             const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
797             const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult) {
798             auto scrollBar = weak.Upgrade();
799             CHECK_NULL_VOID(scrollBar);
800             scrollBar->OnCollectTouchTarget(
801                 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
802         });
803 }
804 
SetScrollBar(DisplayMode displayMode)805 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
806 {
807     auto host = GetHost();
808     CHECK_NULL_VOID(host);
809     if (displayMode == DisplayMode::OFF) {
810         if (scrollBar_) {
811             auto gestureHub = GetGestureHub();
812             if (gestureHub) {
813                 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
814             }
815             scrollBar_.Reset();
816             if (scrollBarOverlayModifier_) {
817                 scrollBarOverlayModifier_->SetOpacity(0);
818             }
819         }
820         return;
821     }
822     DisplayMode oldDisplayMode = DisplayMode::OFF;
823     if (!scrollBar_) {
824         scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
825         RegisterScrollBarEventTask();
826     } else {
827         oldDisplayMode = scrollBar_->GetDisplayMode();
828     }
829     // set the scroll bar style
830     auto positionMode = GetPositionMode();
831     scrollBar_->SetPositionMode(positionMode);
832     if (scrollBarOverlayModifier_) {
833         scrollBarOverlayModifier_->SetPositionMode(positionMode);
834     }
835 
836     if (oldDisplayMode != displayMode) {
837         scrollBar_->SetDisplayMode(displayMode);
838         if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
839             scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
840         }
841         scrollBar_->ScheduleDisappearDelayTask();
842         if (isInitialized_ && !host->CheckNeedForceMeasureAndLayout()) {
843             UpdateScrollBarOffset();
844         }
845     }
846     UpdateBorderRadius();
847 }
848 
UpdateBorderRadius()849 void ScrollablePattern::UpdateBorderRadius()
850 {
851     auto host = GetHost();
852     CHECK_NULL_VOID(host);
853     auto renderContext = host->GetRenderContext();
854     CHECK_NULL_VOID(renderContext);
855     if (renderContext->HasBorderRadius()) {
856         auto borderRadius = renderContext->GetBorderRadius().value();
857         if (!(borderRadius == scrollBar_->GetHostBorderRadius())) {
858             scrollBar_->SetHostBorderRadius(borderRadius);
859             scrollBar_->CalcReservedHeight();
860         }
861     }
862 }
863 
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)864 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
865 {
866     if (!property) {
867         SetScrollBar(DisplayMode::AUTO);
868         return;
869     }
870     auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
871     SetScrollBar(displayMode);
872     if (scrollBar_) {
873         auto barWidth = property->GetScrollBarWidth();
874         if (barWidth) {
875             scrollBar_->SetInactiveWidth(barWidth.value());
876             scrollBar_->SetNormalWidth(barWidth.value());
877             scrollBar_->SetActiveWidth(barWidth.value());
878             scrollBar_->SetTouchWidth(barWidth.value());
879             scrollBar_->SetIsUserNormalWidth(true);
880         } else {
881             scrollBar_->SetIsUserNormalWidth(false);
882         }
883         auto barColor = property->GetScrollBarColor();
884         if (barColor) {
885             scrollBar_->SetForegroundColor(barColor.value());
886         } else {
887             auto pipelineContext = GetContext();
888             CHECK_NULL_VOID(pipelineContext);
889             auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
890             CHECK_NULL_VOID(theme);
891             scrollBar_->SetForegroundColor(theme->GetForegroundColor());
892             scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
893         }
894     }
895 }
896 
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)897 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
898 {
899     // inner scrollbar, viewOffset is padding offset
900     if (scrollBar_) {
901         auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
902         bool scrollable = GreatNotEqual(estimatedHeight, mainSize) && IsScrollable();
903         if (scrollBar_->IsScrollable() != scrollable) {
904             scrollBar_->SetScrollable(scrollable);
905             if (scrollBarOverlayModifier_) {
906                 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
907                 scrollBarOverlayModifier_->SetScrollable(scrollable);
908             }
909             if (scrollable) {
910                 scrollBar_->ScheduleDisappearDelayTask();
911             }
912         }
913         Offset scrollOffset = { offset, offset }; // fit for w/h switched.
914         UpdateBorderRadius();
915         scrollBar_->SetReverse(IsReverse());
916         scrollBar_->SetIsOutOfBoundary(IsOutOfBoundary());
917         scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
918         scrollBar_->MarkNeedRender();
919     }
920 
921     // outer scrollbar
922     if (scrollBarProxy_) {
923         estimatedHeight_ = estimatedHeight - (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
924         barOffset_ = -offset;
925         scrollBarProxy_->NotifyScrollBar(AceType::WeakClaim(this));
926     }
927 }
928 
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)929 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
930 {
931     CHECK_NULL_VOID(scrollBarProxy);
932     auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source) {
933         if (source != SCROLL_FROM_START) {
934             auto pattern = weak.Upgrade();
935             if (!pattern || pattern->GetAxis() == Axis::NONE) {
936                 return false;
937             }
938             return pattern->UpdateCurrentOffset(offset, source);
939         }
940         return true;
941     };
942     auto scrollStartCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
943         auto pattern = weak.Upgrade();
944         CHECK_NULL_RETURN(pattern, false);
945         // no source == SCROLL_FROM_START for ScrollBar
946         pattern->OnScrollStartCallback();
947         return pattern->OnScrollCallback(static_cast<float>(offset), source);
948     };
949     auto scrollEndCallback = [weak = WeakClaim(this)]() {
950         auto pattern = weak.Upgrade();
951         CHECK_NULL_VOID(pattern);
952         pattern->OnScrollEnd();
953     };
954     auto calePredictSnapOffsetCallback =
955             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
956         auto pattern = weak.Upgrade();
957         CHECK_NULL_RETURN(pattern, std::optional<float>());
958         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
959     };
960     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
961         auto pattern = weak.Upgrade();
962         CHECK_NULL_VOID(pattern);
963         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
964     };
965 
966     auto scrollbarFRcallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
967         auto pattern = weak.Upgrade();
968         CHECK_NULL_VOID(pattern);
969         return pattern->NotifyFRCSceneInfo(CUSTOM_SCROLL_BAR_SCENE, velocity, sceneStatus);
970     };
971 
972     ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction), std::move(scrollStartCallback),
973         std::move(scrollEndCallback), std::move(calePredictSnapOffsetCallback),
974         std::move(startScrollSnapMotionCallback), std::move(scrollbarFRcallback) };
975     scrollBarProxy->RegisterScrollableNode(nodeInfo);
976     scrollBarProxy_ = scrollBarProxy;
977 }
978 
CreateScrollBarOverlayModifier()979 void ScrollablePattern::CreateScrollBarOverlayModifier()
980 {
981     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedPaint());
982     CHECK_NULL_VOID(!scrollBarOverlayModifier_);
983     scrollBarOverlayModifier_ = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
984     scrollBarOverlayModifier_->SetRect(scrollBar_->GetActiveRect());
985     scrollBarOverlayModifier_->SetPositionMode(scrollBar_->GetPositionMode());
986 }
987 
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)988 void ScrollablePattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
989 {
990     scrollBarOutBoundaryExtent_ = scrollBarOutBoundaryExtent;
991     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
992     scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
993 }
994 
SetFriction(double friction)995 void ScrollablePattern::SetFriction(double friction)
996 {
997     if (LessOrEqual(friction, 0.0)) {
998         friction =
999             Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
1000         friction =
1001             Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction;
1002     }
1003     friction_ = friction;
1004     CHECK_NULL_VOID(scrollableEvent_);
1005     auto scrollable = scrollableEvent_->GetScrollable();
1006     scrollable->SetUnstaticFriction(friction_);
1007 }
1008 
SetMaxFlingVelocity(double max)1009 void ScrollablePattern::SetMaxFlingVelocity(double max)
1010 {
1011     if (LessOrEqual(max, 0.0f)) {
1012         max = MAX_VELOCITY;
1013     }
1014     maxFlingVelocity_ = max;
1015     CHECK_NULL_VOID(scrollableEvent_);
1016     auto scrollable = scrollableEvent_->GetScrollable();
1017     scrollable->SetMaxFlingVelocity(max);
1018 }
1019 
GetParentNavigation()1020 void ScrollablePattern::GetParentNavigation()
1021 {
1022     if (navBarPattern_) {
1023         return;
1024     }
1025     auto host = GetHost();
1026     CHECK_NULL_VOID(host);
1027     if ((host->GetTag() != V2::LIST_ETS_TAG) && (host->GetTag() != V2::GRID_ETS_TAG) &&
1028         (host->GetTag() != V2::SCROLL_ETS_TAG)) {
1029         return;
1030     }
1031     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
1032         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
1033         if (!frameNode) {
1034             continue;
1035         }
1036         if ((frameNode->GetTag() == V2::LIST_ETS_TAG) || (frameNode->GetTag() == V2::GRID_ETS_TAG) ||
1037             (frameNode->GetTag() == V2::SCROLL_ETS_TAG)) {
1038             break;
1039         }
1040         navBarPattern_ = frameNode->GetPattern<NavBarPattern>();
1041         if (!navBarPattern_) {
1042             continue;
1043         }
1044         return;
1045     }
1046     navBarPattern_ = nullptr;
1047     return;
1048 }
1049 
GetParentModalSheet()1050 void ScrollablePattern::GetParentModalSheet()
1051 {
1052     if (sheetPattern_) {
1053         return;
1054     }
1055     auto host = GetHost();
1056     CHECK_NULL_VOID(host);
1057 
1058     if (host->GetTag() != V2::SCROLL_ETS_TAG) {
1059         return;
1060     }
1061 
1062     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
1063         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
1064         if (!frameNode) {
1065             continue;
1066         }
1067         sheetPattern_ = frameNode->GetPattern<SheetPresentationPattern>();
1068         if (!sheetPattern_) {
1069             continue;
1070         }
1071         return;
1072     }
1073     return;
1074 }
1075 
StopAnimate()1076 void ScrollablePattern::StopAnimate()
1077 {
1078     if (!IsScrollableStopped()) {
1079         StopScrollable();
1080     }
1081     if (animator_ && !animator_->IsStopped()) {
1082         animator_->Stop();
1083     }
1084     if (!isAnimationStop_) {
1085         StopAnimation(springAnimation_);
1086         StopAnimation(curveAnimation_);
1087     }
1088     if (scrollBar_) {
1089         scrollBar_->StopFlingAnimation();
1090     }
1091 }
1092 
ScrollTo(float position)1093 void ScrollablePattern::ScrollTo(float position)
1094 {
1095     StopAnimate();
1096     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1097 }
1098 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll,bool useTotalOffset)1099 void ScrollablePattern::AnimateTo(
1100     float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll, bool useTotalOffset)
1101 {
1102     float currVelocity = 0.0f;
1103     if (!IsScrollableStopped()) {
1104         CHECK_NULL_VOID(scrollableEvent_);
1105         auto scrollable = scrollableEvent_->GetScrollable();
1106         CHECK_NULL_VOID(scrollable);
1107         currVelocity = -scrollable->GetCurrentVelocity();
1108         scrollAbort_ = true;
1109         StopScrollable();
1110     }
1111     if (!isAnimationStop_) {
1112         currVelocity = GetCurrentVelocity();
1113         scrollAbort_ = true;
1114         StopAnimation(springAnimation_);
1115         StopAnimation(curveAnimation_);
1116     }
1117     if (animator_ && !animator_->IsStopped()) {
1118         scrollAbort_ = true;
1119         animator_->Stop();
1120     }
1121     if (NearEqual(position, GetTotalOffset())) {
1122         return;
1123     }
1124     finalPosition_ = position;
1125     auto host = GetHost();
1126     CHECK_NULL_VOID(host);
1127     if (smooth) {
1128         PlaySpringAnimation(position, DEFAULT_SCROLL_TO_VELOCITY, DEFAULT_SCROLL_TO_MASS,
1129             DEFAULT_SCROLL_TO_STIFFNESS, DEFAULT_SCROLL_TO_DAMPING, useTotalOffset);
1130     } else {
1131         PlayCurveAnimation(position, duration, curve, canOverScroll);
1132     }
1133     if (!GetIsDragging()) {
1134         FireOnScrollStart();
1135     }
1136     PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
1137     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::SCROLLER_ANIMATION, PerfActionType::FIRST_MOVE, "");
1138     auto pipeline = PipelineBase::GetCurrentContext();
1139     CHECK_NULL_VOID(pipeline);
1140     pipeline->RequestFrame();
1141 }
1142 
OnAnimateFinish()1143 void ScrollablePattern::OnAnimateFinish()
1144 {
1145     useTotalOffset_ = true;
1146     auto host = GetHost();
1147     CHECK_NULL_VOID(host);
1148     if (isAnimationStop_) {
1149         SetUiDvsyncSwitch(false);
1150         NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::END);
1151         PerfMonitor::GetPerfMonitor()->End(PerfConstants::SCROLLER_ANIMATION, false);
1152     }
1153     if (animateToTraceFlag_) {
1154         animateToTraceFlag_ = false;
1155         AceAsyncTraceEnd(host->GetId(), TRAILING_ANIMATION);
1156     }
1157 }
1158 
PlaySpringAnimation(float position,float velocity,float mass,float stiffness,float damping,bool useTotalOffset)1159 void ScrollablePattern::PlaySpringAnimation(float position, float velocity, float mass, float stiffness, float damping,
1160                                             bool useTotalOffset)
1161 {
1162     if (!springOffsetProperty_) {
1163         InitSpringOffsetProperty();
1164         CHECK_NULL_VOID(springOffsetProperty_);
1165     }
1166 
1167     AnimationOption option;
1168     auto curve = AceType::MakeRefPtr<InterpolatingSpring>(velocity, mass, stiffness, damping);
1169     InitOption(option, CUSTOM_ANIMATION_DURATION, curve);
1170     isAnimationStop_ = false;
1171     useTotalOffset_ = useTotalOffset;
1172     AnimationUtils::ExecuteWithoutAnimation([this]() { springOffsetProperty_->Set(GetTotalOffset()); });
1173     springAnimation_ = AnimationUtils::StartAnimation(
1174         option,
1175         [weak = AceType::WeakClaim(this), position]() {
1176             auto pattern = weak.Upgrade();
1177             CHECK_NULL_VOID(pattern);
1178             pattern->SetUiDvsyncSwitch(true);
1179             pattern->springOffsetProperty_->Set(position);
1180         },
1181         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1182             ContainerScope scope(id);
1183             auto pattern = weak.Upgrade();
1184             CHECK_NULL_VOID(pattern);
1185             pattern->OnAnimateFinish();
1186             pattern->SetScrollEdgeType(ScrollEdgeType::SCROLL_NONE);
1187     });
1188     NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1189 }
1190 
PlayCurveAnimation(float position,float duration,const RefPtr<Curve> & curve,bool canOverScroll)1191 void ScrollablePattern::PlayCurveAnimation(
1192     float position, float duration, const RefPtr<Curve>& curve, bool canOverScroll)
1193 {
1194     AnimationOption option;
1195     InitOption(option, duration, curve);
1196     if (!curveOffsetProperty_) {
1197         InitCurveOffsetProperty();
1198         CHECK_NULL_VOID(curveOffsetProperty_);
1199     }
1200     isAnimationStop_ = false;
1201     SetAnimateCanOverScroll(canOverScroll);
1202     curveOffsetProperty_->Set(GetTotalOffset());
1203     curveAnimation_ = AnimationUtils::StartAnimation(
1204         option,
1205         [weak = AceType::WeakClaim(this), position]() {
1206             auto pattern = weak.Upgrade();
1207             CHECK_NULL_VOID(pattern);
1208             pattern->SetUiDvsyncSwitch(true);
1209             pattern->curveOffsetProperty_->Set(position);
1210         },
1211         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1212             ContainerScope scope(id);
1213             auto pattern = weak.Upgrade();
1214             CHECK_NULL_VOID(pattern);
1215             pattern->OnAnimateFinish();
1216         });
1217     NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1218 }
1219 
GetScrollDelta(float offset,bool & stopAnimation)1220 float ScrollablePattern::GetScrollDelta(float offset, bool& stopAnimation)
1221 {
1222     auto context = GetContext();
1223     CHECK_NULL_RETURN(context, 0.0f);
1224     uint64_t currentVsync = context->GetVsyncTime();
1225     uint64_t diff = currentVsync - lastVsyncTime_;
1226     if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1227         currentVelocity_ = (offset - lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1228         NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, currentVelocity_, SceneStatus::RUNNING);
1229     }
1230     stopAnimation = NearEqual(finalPosition_, offset, SPRING_ACCURACY);
1231     if (stopAnimation) {
1232         offset = finalPosition_;
1233     }
1234     if (NearEqual(offset, lastPosition_, 1.0) && !animateToTraceFlag_) {
1235         animateToTraceFlag_ = true;
1236         auto host = GetHost();
1237         auto id = host ? host->GetId() : 0;
1238         AceAsyncTraceBegin(id, TRAILING_ANIMATION);
1239     }
1240     auto delta = useTotalOffset_ ? GetTotalOffset() - offset : lastPosition_ - offset;
1241     lastVsyncTime_ = currentVsync;
1242     lastPosition_ = offset;
1243     return delta;
1244 }
1245 
InitSpringOffsetProperty()1246 void ScrollablePattern::InitSpringOffsetProperty()
1247 {
1248     auto host = GetHost();
1249     CHECK_NULL_VOID(host);
1250     auto renderContext = host->GetRenderContext();
1251     CHECK_NULL_VOID(renderContext);
1252     auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1253         auto pattern = weak.Upgrade();
1254         CHECK_NULL_VOID(pattern);
1255         if (pattern->isAnimationStop_) {
1256             return;
1257         }
1258         bool stopAnimation = false;
1259         auto delta = pattern->GetScrollDelta(offset, stopAnimation);
1260         if (!pattern->UpdateCurrentOffset(delta, SCROLL_FROM_ANIMATION_CONTROLLER) || stopAnimation) {
1261             pattern->StopAnimation(pattern->springAnimation_);
1262         }
1263     };
1264     springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1265     renderContext->AttachNodeAnimatableProperty(springOffsetProperty_);
1266 }
1267 
InitCurveOffsetProperty()1268 void ScrollablePattern::InitCurveOffsetProperty()
1269 {
1270     auto host = GetHost();
1271     CHECK_NULL_VOID(host);
1272     auto renderContext = host->GetRenderContext();
1273     CHECK_NULL_VOID(renderContext);
1274     auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1275         auto pattern = weak.Upgrade();
1276         CHECK_NULL_VOID(pattern);
1277         if (pattern->isAnimationStop_) {
1278             return;
1279         }
1280         bool stopAnimation = false;
1281         auto delta = pattern->GetScrollDelta(offset, stopAnimation);
1282         if (!pattern->UpdateCurrentOffset(delta, SCROLL_FROM_ANIMATION_CONTROLLER) ||
1283             stopAnimation || pattern->isAnimateOverScroll_) {
1284             if (pattern->isAnimateOverScroll_) {
1285                 pattern->isAnimateOverScroll_ = false;
1286                 auto pauseVelocity = -pattern->currentVelocity_;
1287                 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
1288                 CHECK_NULL_VOID(context);
1289                 context->MarkNeedFlushAnimationStartTime();
1290                 pattern->PauseAnimation(pattern->curveAnimation_);
1291                 pattern->HandleOverScroll(pauseVelocity);
1292             } else if (stopAnimation ||
1293                        (pattern->IsAtTop() && LessOrEqual(pattern->finalPosition_, pattern->GetTotalOffset())) ||
1294                        (pattern->IsAtBottom() && GreatOrEqual(pattern->finalPosition_, pattern->GetTotalOffset()))) {
1295                 pattern->StopAnimation(pattern->curveAnimation_);
1296             }
1297         }
1298     };
1299     curveOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1300     renderContext->AttachNodeAnimatableProperty(curveOffsetProperty_);
1301 }
1302 
InitOption(AnimationOption & option,float duration,const RefPtr<Curve> & curve)1303 void ScrollablePattern::InitOption(AnimationOption &option, float duration, const RefPtr<Curve>& curve)
1304 {
1305     if (!curve) {
1306         option.SetCurve(Curves::EASE); // default curve
1307     } else {
1308         option.SetCurve(curve);
1309     }
1310     if (duration > 0) {
1311         option.SetDuration(duration);
1312     } else {
1313         option.SetDuration(CUSTOM_ANIMATION_DURATION);
1314     }
1315 }
1316 
StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1317 void ScrollablePattern::StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1318 {
1319     SetAnimateCanOverScroll(false);
1320     isAnimationStop_ = true;
1321     currentVelocity_ = 0.0;
1322     if (!animation) {
1323         return;
1324     }
1325     AnimationUtils::StopAnimation(animation);
1326     OnAnimateStop();
1327 }
1328 
PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1329 void ScrollablePattern::PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1330 {
1331     SetAnimateCanOverScroll(false);
1332     isAnimationStop_ = true;
1333     currentVelocity_ = 0.0;
1334     if (!animation) {
1335         return;
1336     }
1337     AnimationUtils::StopAnimation(animation);
1338 }
1339 
OnAttachToFrameNode()1340 void ScrollablePattern::OnAttachToFrameNode()
1341 {
1342     auto host = GetHost();
1343     CHECK_NULL_VOID(host);
1344     host->GetRenderContext()->SetClipToBounds(true);
1345     host->GetRenderContext()->UpdateClipEdge(true);
1346 }
1347 
UninitMouseEvent()1348 void ScrollablePattern::UninitMouseEvent()
1349 {
1350     if (!boxSelectPanEvent_) {
1351         return;
1352     }
1353     auto host = GetHost();
1354     CHECK_NULL_VOID(host);
1355     auto gestureHub = host->GetOrCreateGestureEventHub();
1356     CHECK_NULL_VOID(gestureHub);
1357     gestureHub->RemovePanEvent(boxSelectPanEvent_);
1358     boxSelectPanEvent_.Reset();
1359     ClearMultiSelect();
1360     ClearInvisibleItemsSelectedStatus();
1361     isMouseEventInit_ = false;
1362 }
1363 
InitMouseEvent()1364 void ScrollablePattern::InitMouseEvent()
1365 {
1366     auto host = GetHost();
1367     CHECK_NULL_VOID(host);
1368     auto gestureHub = host->GetOrCreateGestureEventHub();
1369     CHECK_NULL_VOID(gestureHub);
1370     if (!boxSelectPanEvent_) {
1371         auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1372             auto pattern = weak.Upgrade();
1373             CHECK_NULL_VOID(pattern);
1374             pattern->HandleDragStart(info);
1375         };
1376 
1377         auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1378             auto pattern = weak.Upgrade();
1379             CHECK_NULL_VOID(pattern);
1380             pattern->HandleDragUpdate(info);
1381         };
1382 
1383         auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1384             auto pattern = weak.Upgrade();
1385             CHECK_NULL_VOID(pattern);
1386             pattern->HandleDragEnd();
1387         };
1388         GestureEventNoParameter actionCancelTask = [weak = WeakClaim(this)]() {
1389             auto pattern = weak.Upgrade();
1390             CHECK_NULL_VOID(pattern);
1391             pattern->HandleDragEnd();
1392         };
1393         boxSelectPanEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
1394             std::move(actionEndTask), std::move(actionCancelTask));
1395     }
1396     PanDirection panDirection = { .type = PanDirection::ALL };
1397     gestureHub->AddPanEvent(boxSelectPanEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
1398     gestureHub->SetPanEventType(GestureTypeName::BOXSELECT);
1399     gestureHub->SetOnGestureJudgeNativeBegin([](const RefPtr<NG::GestureInfo>& gestureInfo,
1400                                                  const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1401         if (gestureInfo->GetType() == GestureTypeName::BOXSELECT &&
1402             gestureInfo->GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1403             return GestureJudgeResult::REJECT;
1404         }
1405         return GestureJudgeResult::CONTINUE;
1406     });
1407     isMouseEventInit_ = true;
1408 }
1409 
HandleDragStart(const GestureEvent & info)1410 void ScrollablePattern::HandleDragStart(const GestureEvent& info)
1411 {
1412     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Box select start");
1413     auto mouseOffsetX = static_cast<float>(info.GetRawGlobalLocation().GetX());
1414     auto mouseOffsetY = static_cast<float>(info.GetRawGlobalLocation().GetY());
1415     mouseOffsetX -= info.GetOffsetX();
1416     mouseOffsetY -= info.GetOffsetY();
1417     SuggestOpIncGroup(true);
1418     if (!IsItemSelected(info)) {
1419         ClearMultiSelect();
1420         ClearInvisibleItemsSelectedStatus();
1421         mouseStartOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1422         lastMouseStart_ = mouseStartOffset_;
1423         mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1424         mousePressOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1425         totalOffsetOfMousePressed_ = mousePressOffset_.GetMainOffset(axis_) + GetTotalOffset();
1426         canMultiSelect_ = true;
1427     }
1428     mousePressed_ = true;
1429 }
1430 
HandleDragUpdate(const GestureEvent & info)1431 void ScrollablePattern::HandleDragUpdate(const GestureEvent& info)
1432 {
1433     auto mouseOffsetX = static_cast<float>(info.GetRawGlobalLocation().GetX());
1434     auto mouseOffsetY = static_cast<float>(info.GetRawGlobalLocation().GetY());
1435     if (!mousePressed_ || !canMultiSelect_) {
1436         return;
1437     }
1438     if (info.GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1439         HandleDragEnd();
1440         return;
1441     }
1442     lastMouseMove_ = info;
1443     auto delta = OffsetF(mouseOffsetX, mouseOffsetY) - mousePressOffset_;
1444     if (Offset(delta.GetX(), delta.GetY()).GetDistance() > DEFAULT_PAN_DISTANCE.ConvertToPx()) {
1445         mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1446         // avoid large select zone
1447         LimitMouseEndOffset();
1448         auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1449         MultiSelectWithoutKeyboard(selectedZone);
1450         HandleInvisibleItemsSelectedStatus(selectedZone);
1451     }
1452     SelectWithScroll();
1453 }
1454 
HandleDragEnd()1455 void ScrollablePattern::HandleDragEnd()
1456 {
1457     TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Box select end");
1458     mouseStartOffset_.Reset();
1459     lastMouseStart_.Reset();
1460     mouseEndOffset_.Reset();
1461     mousePressed_ = false;
1462     canMultiSelect_ = false;
1463     ClearSelectedZone();
1464     itemToBeSelected_.clear();
1465     lastMouseMove_.SetLocalLocation(Offset::Zero());
1466 }
ClearInvisibleItemsSelectedStatus()1467 void ScrollablePattern::ClearInvisibleItemsSelectedStatus()
1468 {
1469     for (auto& item : itemToBeSelected_) {
1470         item.second.FireSelectChangeEvent(false);
1471     }
1472     itemToBeSelected_.clear();
1473 }
1474 
HandleInvisibleItemsSelectedStatus(const RectF & selectedZone)1475 void ScrollablePattern::HandleInvisibleItemsSelectedStatus(const RectF& selectedZone)
1476 {
1477     auto newRect = selectedZone;
1478     auto startMainOffset = mouseStartOffset_.GetMainOffset(axis_);
1479     auto endMainOffset = mouseEndOffset_.GetMainOffset(axis_);
1480     SelectDirection oldDirection = selectDirection_;
1481     if (LessNotEqual(startMainOffset, endMainOffset)) {
1482         selectDirection_ = SELECT_DOWN;
1483         if (axis_ == Axis::VERTICAL) {
1484             newRect.SetOffset(OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_));
1485         } else {
1486             newRect.SetOffset(OffsetF(totalOffsetOfMousePressed_, selectedZone.Top()));
1487         }
1488     } else {
1489         selectDirection_ = SELECT_UP;
1490         if (axis_ == Axis::VERTICAL) {
1491             newRect.SetOffset(
1492                 OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset)));
1493         } else {
1494             newRect.SetOffset(
1495                 OffsetF(totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset), selectedZone.Top()));
1496         }
1497     }
1498     oldDirection = oldDirection == SELECT_NONE ? selectDirection_ : oldDirection;
1499 
1500     for (auto& item : itemToBeSelected_) {
1501         item.second.FireSelectChangeEvent(newRect.IsIntersectWith(item.second.rect));
1502     }
1503 
1504     if (oldDirection != selectDirection_) {
1505         itemToBeSelected_.clear();
1506     }
1507 }
1508 
SelectWithScroll()1509 void ScrollablePattern::SelectWithScroll()
1510 {
1511     if (!IsScrollable()) {
1512         return;
1513     }
1514     auto offset = GetOutOfScrollableOffset();
1515     if (NearZero(offset)) {
1516         return;
1517     }
1518 
1519     if (AnimateRunning()) {
1520         return;
1521     }
1522 
1523     if (!isAnimationStop_) {
1524         StopAnimation(springAnimation_);
1525         StopAnimation(curveAnimation_);
1526     }
1527 
1528     if (!animator_) {
1529         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
1530         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1531             auto pattern = weak.Upgrade();
1532             CHECK_NULL_VOID(pattern);
1533             pattern->OnAnimateStop();
1534         });
1535     } else if (!animator_->IsStopped()) {
1536         scrollAbort_ = true;
1537         animator_->Stop();
1538     }
1539 
1540     if (!selectMotion_) {
1541         selectMotion_ = AceType::MakeRefPtr<SelectMotion>(offset, [weak = WeakClaim(this)]() -> bool {
1542             auto pattern = weak.Upgrade();
1543             CHECK_NULL_RETURN(pattern, true);
1544             return pattern->ShouldSelectScrollBeStopped();
1545         });
1546         selectMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
1547             auto pattern = weakScroll.Upgrade();
1548             CHECK_NULL_VOID(pattern);
1549             offset = pattern->GetOffsetWithLimit(offset);
1550             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
1551             pattern->UpdateMouseStart(offset);
1552         });
1553     } else {
1554         selectMotion_->Reset(offset);
1555     }
1556 
1557     animator_->PlayMotion(selectMotion_);
1558 
1559     FireOnScrollStart();
1560 }
1561 
ClearSelectedZone()1562 void ScrollablePattern::ClearSelectedZone()
1563 {
1564     DrawSelectedZone(RectF());
1565 }
1566 
ComputeSelectedZone(const OffsetF & startOffset,const OffsetF & endOffset)1567 RectF ScrollablePattern::ComputeSelectedZone(const OffsetF& startOffset, const OffsetF& endOffset)
1568 {
1569     RectF selectedZone;
1570     if (startOffset.GetX() <= endOffset.GetX()) {
1571         if (startOffset.GetY() <= endOffset.GetY()) {
1572             // bottom right
1573             selectedZone = RectF(startOffset.GetX(), startOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1574                 endOffset.GetY() - startOffset.GetY());
1575         } else {
1576             // top right
1577             selectedZone = RectF(startOffset.GetX(), endOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1578                 startOffset.GetY() - endOffset.GetY());
1579         }
1580     } else {
1581         if (startOffset.GetY() <= endOffset.GetY()) {
1582             // bottom left
1583             selectedZone = RectF(endOffset.GetX(), startOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1584                 endOffset.GetY() - startOffset.GetY());
1585         } else {
1586             // top left
1587             selectedZone = RectF(endOffset.GetX(), endOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1588                 startOffset.GetY() - endOffset.GetY());
1589         }
1590     }
1591 
1592     return selectedZone;
1593 }
1594 
DrawSelectedZone(const RectF & selectedZone)1595 void ScrollablePattern::DrawSelectedZone(const RectF& selectedZone)
1596 {
1597     auto host = GetHost();
1598     CHECK_NULL_VOID(host);
1599     auto hostContext = host->GetRenderContext();
1600     CHECK_NULL_VOID(hostContext);
1601     hostContext->UpdateMouseSelectWithRect(selectedZone, SELECT_FILL_COLOR, SELECT_STROKE_COLOR);
1602 }
1603 
MarkSelectedItems()1604 void ScrollablePattern::MarkSelectedItems()
1605 {
1606     if (multiSelectable_ && mousePressed_) {
1607         auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1608         if (!selectedZone.IsEmpty()) {
1609             MultiSelectWithoutKeyboard(selectedZone);
1610             HandleInvisibleItemsSelectedStatus(selectedZone);
1611         }
1612     }
1613 }
1614 
ShouldSelectScrollBeStopped()1615 bool ScrollablePattern::ShouldSelectScrollBeStopped()
1616 {
1617     if (!mousePressed_) {
1618         return true;
1619     }
1620     auto offset = GetOutOfScrollableOffset();
1621     if (NearZero(offset)) {
1622         return true;
1623     }
1624 
1625     if (selectMotion_) {
1626         selectMotion_->Reset(offset);
1627     }
1628     return false;
1629 };
1630 
UpdateMouseStart(float offset)1631 void ScrollablePattern::UpdateMouseStart(float offset)
1632 {
1633     if (axis_ == Axis::VERTICAL) {
1634         mouseStartOffset_.AddY(offset);
1635     } else {
1636         mouseStartOffset_.AddX(offset);
1637     }
1638 }
1639 
GetOutOfScrollableOffset() const1640 float ScrollablePattern::GetOutOfScrollableOffset() const
1641 {
1642     auto offset = 0.0f;
1643     auto mouseMainOffset = static_cast<float>(
1644         axis_ == Axis::VERTICAL ? lastMouseMove_.GetLocalLocation().GetY() : lastMouseMove_.GetLocalLocation().GetX());
1645     auto hostSize = GetHostFrameSize();
1646     CHECK_NULL_RETURN(hostSize.has_value(), offset);
1647     auto mainTop = 0.0f;
1648     auto mainBottom = hostSize->MainSize(axis_);
1649     if (GreatOrEqual(mouseMainOffset, mainTop) && LessOrEqual(mouseMainOffset, mainBottom)) {
1650         return offset;
1651     }
1652     if (GreatNotEqual(mouseMainOffset, mainBottom)) {
1653         if (IsAtBottom()) {
1654             return offset;
1655         }
1656         offset = mainBottom - mouseMainOffset;
1657     }
1658     if (LessNotEqual(mouseMainOffset, mainTop)) {
1659         if (IsAtTop()) {
1660             return offset;
1661         }
1662         offset = mainTop - mouseMainOffset;
1663     }
1664     return offset;
1665 }
1666 
1667 // avoid start position move when offset is bigger then item height
GetOffsetWithLimit(float offset) const1668 float ScrollablePattern::GetOffsetWithLimit(float offset) const
1669 {
1670     if (Positive(offset)) {
1671         auto totalOffset = GetTotalOffset();
1672         return std::min(totalOffset, offset);
1673     } else if (Negative(offset)) {
1674         auto frameNode = GetHost();
1675         CHECK_NULL_RETURN(frameNode, true);
1676         auto hostSize = frameNode->GetGeometryNode()->GetFrameSize();
1677         auto remainHeight = GetTotalHeight() - GetTotalOffset() - hostSize.MainSize(axis_);
1678         return std::max(offset, -remainHeight);
1679     }
1680     return 0;
1681 }
1682 
LimitMouseEndOffset()1683 void ScrollablePattern::LimitMouseEndOffset()
1684 {
1685     float limitedMainOffset = -1.0f;
1686     float limitedCrossOffset = -1.0f;
1687     auto frameNode = GetHost();
1688     CHECK_NULL_VOID(frameNode);
1689     auto hostSize = frameNode->GetGeometryNode()->GetFrameSize();
1690     auto mainSize = hostSize.MainSize(axis_);
1691     auto crossSize = hostSize.CrossSize(axis_);
1692     auto mainOffset = mouseEndOffset_.GetMainOffset(axis_);
1693     auto crossOffset = mouseEndOffset_.GetCrossOffset(axis_);
1694     if (LessNotEqual(mainOffset, 0.0f)) {
1695         limitedMainOffset = 0.0f;
1696     }
1697     if (GreatNotEqual(mainOffset, mainSize)) {
1698         limitedMainOffset = mainSize;
1699     }
1700     if (LessNotEqual(crossOffset, 0.0f)) {
1701         limitedCrossOffset = 0.0f;
1702     }
1703     if (GreatNotEqual(crossOffset, crossSize)) {
1704         limitedCrossOffset = crossSize;
1705     }
1706 
1707     if (axis_ == Axis::VERTICAL) {
1708         mouseEndOffset_.SetX(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedCrossOffset);
1709         mouseEndOffset_.SetY(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedMainOffset);
1710     } else {
1711         mouseEndOffset_.SetX(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedMainOffset);
1712         mouseEndOffset_.SetY(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedCrossOffset);
1713     }
1714 }
1715 
HandleScrollImpl(float offset,int32_t source)1716 bool ScrollablePattern::HandleScrollImpl(float offset, int32_t source)
1717 {
1718     // Previous: Set HandleScrollImpl to Scrollable->callback_
1719     // Scrollable::HandleScroll calls callback_ through UpdateScrollPosition
1720 
1721     // Now: HandleScroll moved to ScrollablePattern, directly call HandleScrollImpl in
1722     // ScrollablePattern::HandleScroll
1723     double overOffset = offset;
1724     if (!OnScrollPosition(overOffset, source)) {
1725         return false;
1726     }
1727     auto result = OnScrollCallback(overOffset, source);
1728     SelectOverlayScrollNotifier::NotifyOnScrollCallback(WeakClaim(this), overOffset, source);
1729     return result;
1730 }
1731 
NotifyMoved(bool value)1732 void ScrollablePattern::NotifyMoved(bool value)
1733 {
1734     CHECK_NULL_VOID(scrollableEvent_);
1735     auto&& scroll = scrollableEvent_->GetScrollable();
1736     if (scroll) {
1737         scroll->SetMoved(value);
1738     }
1739 }
1740 
ProcessSpringEffect(float velocity,bool needRestart)1741 void ScrollablePattern::ProcessSpringEffect(float velocity, bool needRestart)
1742 {
1743     CHECK_NULL_VOID(InstanceOf<ScrollSpringEffect>(scrollEffect_));
1744     auto isOutOfBoundary = OutBoundaryCallback();
1745     if (!isOutOfBoundary && !GetCanOverScroll()) {
1746         OnScrollEnd();
1747         return;
1748     }
1749     CHECK_NULL_VOID(scrollableEvent_);
1750     auto scrollable = scrollableEvent_->GetScrollable();
1751     // HandleTouchUp may be triggered before HandleDragEnd when scrollable nested scrollable,
1752     // so need to update spring motion.
1753     if (needRestart || !(scrollable && scrollable->IsSpringMotionRunning())) {
1754         StopScrollable();
1755         scrollEffect_->ProcessScrollOver(velocity);
1756     } else {
1757         scrollEffect_->ProcessSpringUpdate();
1758     }
1759 }
1760 
SetCanOverScroll(bool val)1761 void ScrollablePattern::SetCanOverScroll(bool val)
1762 {
1763     CHECK_NULL_VOID(scrollableEvent_);
1764     auto&& scrollable = scrollableEvent_->GetScrollable();
1765     if (scrollable) {
1766         scrollable->SetCanOverScroll(val);
1767     }
1768 }
1769 
GetCanOverScroll() const1770 bool ScrollablePattern::GetCanOverScroll() const
1771 {
1772     CHECK_NULL_RETURN(scrollableEvent_, true);
1773     auto&& scrollable = scrollableEvent_->GetScrollable();
1774     if (scrollable) {
1775         return scrollable->CanOverScroll();
1776     }
1777     return true;
1778 }
1779 
GetEdgeEffect() const1780 EdgeEffect ScrollablePattern::GetEdgeEffect() const
1781 {
1782     return edgeEffect_;
1783 }
1784 
GetScrollState() const1785 ScrollState ScrollablePattern::GetScrollState() const
1786 {
1787     return ScrollablePattern::GetScrollState(scrollSource_);
1788 }
1789 
GetScrollState(int32_t scrollSource)1790 ScrollState ScrollablePattern::GetScrollState(int32_t scrollSource)
1791 {
1792     // with event
1793     if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS || scrollSource == SCROLL_FROM_BAR) {
1794         return ScrollState::SCROLL;
1795     }
1796     // without event
1797     if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
1798         scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
1799         return ScrollState::FLING;
1800     }
1801     // SCROLL_FROM_NONE, SCROLL_FROM_JUMP, SCROLL_FROM_CHILD, SCROLL_FROM_FOCUS_JUMP, SCROLL_FROM_ROTATE,
1802     // SCROLL_FROM_INDEXER, SCROLL_FROM_START
1803     return ScrollState::IDLE;
1804 }
1805 
ConvertScrollSource(int32_t source)1806 ScrollSource ScrollablePattern::ConvertScrollSource(int32_t source)
1807 {
1808     // static linear map must be sorted by key.
1809     static const LinearEnumMapNode<int32_t, ScrollSource> scrollSourceMap[] = {
1810         { SCROLL_FROM_UPDATE, ScrollSource::DRAG },
1811         { SCROLL_FROM_ANIMATION, ScrollSource::FLING },
1812         { SCROLL_FROM_JUMP, ScrollSource::SCROLLER },
1813         { SCROLL_FROM_ANIMATION_SPRING, ScrollSource::EDGE_EFFECT },
1814         { SCROLL_FROM_BAR, ScrollSource::SCROLL_BAR },
1815         { SCROLL_FROM_FOCUS_JUMP, ScrollSource::OTHER_USER_INPUT },
1816         { SCROLL_FROM_AXIS, ScrollSource::OTHER_USER_INPUT },
1817         { SCROLL_FROM_ANIMATION_CONTROLLER, ScrollSource::SCROLLER_ANIMATION },
1818         { SCROLL_FROM_BAR_FLING, ScrollSource::SCROLL_BAR_FLING },
1819     };
1820     ScrollSource sourceType = ScrollSource::OTHER_USER_INPUT;
1821     int64_t idx = BinarySearchFindIndex(scrollSourceMap, ArraySize(scrollSourceMap), source);
1822     if (idx >= 0) {
1823         sourceType = scrollSourceMap[idx].value;
1824     }
1825     return sourceType;
1826 }
1827 
HandleScrollParentFirst(float & offset,int32_t source,NestedState state)1828 ScrollResult ScrollablePattern::HandleScrollParentFirst(float& offset, int32_t source, NestedState state)
1829 {
1830     auto parent = GetNestedScrollParent();
1831     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1832     if (state == NestedState::CHILD_OVER_SCROLL) {
1833         if (GetEdgeEffect() == EdgeEffect::NONE) {
1834             return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1835         }
1836         ExecuteScrollFrameBegin(offset, scrollState);
1837         return { 0, true };
1838     }
1839     auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
1840     offset = IsReverse() ? -result.remain : result.remain;
1841     if (NearZero(offset)) {
1842         SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1843         return { 0, false };
1844     }
1845     float allOffset = offset;
1846     ExecuteScrollFrameBegin(offset, scrollState);
1847     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1848     auto overOffsets = GetOverScrollOffset(offset);
1849     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1850     remainOffset += overOffset;
1851     if (NearZero(remainOffset)) {
1852         SetCanOverScroll(false);
1853         return { 0, false };
1854     }
1855     if (state == NestedState::CHILD_SCROLL) {
1856         offset -= overOffset;
1857         SetCanOverScroll(false);
1858         return { remainOffset, !NearZero(overOffset) };
1859     }
1860     bool parentEdgeEffect = false;
1861     if (GetEdgeEffect() == EdgeEffect::NONE) {
1862         result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1863         if (NearZero(result.remain)) {
1864             offset -= overOffset;
1865             parentEdgeEffect = NearZero(offset) && result.reachEdge;
1866         }
1867     }
1868     SetCanOverScroll((!NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE) || parentEdgeEffect);
1869     return { 0, GetCanOverScroll() };
1870 }
1871 
HandleScrollSelfFirst(float & offset,int32_t source,NestedState state)1872 ScrollResult ScrollablePattern::HandleScrollSelfFirst(float& offset, int32_t source, NestedState state)
1873 {
1874     auto parent = GetNestedScrollParent();
1875     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1876     if (state == NestedState::CHILD_OVER_SCROLL) {
1877         auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1878         if (NearZero(result.remain)) {
1879             offset = 0;
1880             return result;
1881         }
1882         ExecuteScrollFrameBegin(offset, scrollState);
1883         if (GetEdgeEffect() == EdgeEffect::NONE) {
1884             return result;
1885         }
1886         return { 0, true };
1887     }
1888     float allOffset = offset;
1889     ExecuteScrollFrameBegin(offset, scrollState);
1890     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1891     auto overOffsets = GetOverScrollOffset(offset);
1892     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1893     if (NearZero(overOffset) && NearZero(remainOffset)) {
1894         SetCanOverScroll(false);
1895         return { 0, false };
1896     }
1897     offset -= overOffset;
1898     auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
1899     if (NearZero(result.remain)) {
1900         SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1901         return { 0, false };
1902     }
1903     if (state == NestedState::CHILD_SCROLL) {
1904         SetCanOverScroll(false);
1905         return result;
1906     }
1907     // triggering overScroll, parent always handle it first
1908     auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1909     offset += LessNotEqual(std::abs(overOffset), std::abs(result.remain)) ? overOffset : overRes.remain;
1910     bool parentEdgeEffect = result.reachEdge && NearZero(offset);
1911     SetCanOverScroll((!NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE) || parentEdgeEffect);
1912     return { 0, GetCanOverScroll() };
1913 }
1914 
HandleScrollSelfOnly(float & offset,int32_t source,NestedState state)1915 ScrollResult ScrollablePattern::HandleScrollSelfOnly(float& offset, int32_t source, NestedState state)
1916 {
1917     float allOffset = offset;
1918     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1919     ExecuteScrollFrameBegin(offset, scrollState);
1920     auto remainOffset = allOffset - offset;
1921     auto overOffsets = GetOverScrollOffset(offset);
1922     auto overOffset = (offset > 0) ? overOffsets.start : overOffsets.end;
1923     remainOffset += overOffset;
1924     if (NearZero(remainOffset)) {
1925         SetCanOverScroll(false);
1926         return { 0, false };
1927     }
1928     bool canOverScroll = false;
1929     if (state == NestedState::CHILD_SCROLL) {
1930         offset -= overOffset;
1931     } else if (state == NestedState::GESTURE) {
1932         canOverScroll = !NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE;
1933     } else if (GetEdgeEffect() != EdgeEffect::NONE) {
1934         remainOffset = 0;
1935     }
1936     SetCanOverScroll(canOverScroll);
1937     return { remainOffset, !NearZero(overOffset) };
1938 }
1939 
HandleScrollParallel(float & offset,int32_t source,NestedState state)1940 ScrollResult ScrollablePattern::HandleScrollParallel(float& offset, int32_t source, NestedState state)
1941 {
1942     auto remainOffset = 0.0;
1943     auto parent = GetNestedScrollParent();
1944     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1945     if (state == NestedState::CHILD_OVER_SCROLL) {
1946         if (GetEdgeEffect() == EdgeEffect::NONE) {
1947             auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1948             remainOffset = result.remain;
1949             offset = 0;
1950         } else {
1951             ExecuteScrollFrameBegin(offset, scrollState);
1952         }
1953         return { remainOffset, true };
1954     }
1955 
1956     bool canOverScroll = false;
1957     float parentOffset = offset;
1958     ExecuteScrollFrameBegin(offset, scrollState);
1959     auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
1960 
1961     auto overOffsets = GetOverScrollOffset(offset);
1962     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1963     if (!NearZero(overOffset) && result.reachEdge) {
1964         if (state == NestedState::CHILD_SCROLL) {
1965             remainOffset = overOffset;
1966             offset = offset - overOffset;
1967         } else if (GetEdgeEffect() == EdgeEffect::NONE) {
1968             parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1969             canOverScroll = true;
1970             offset = offset - overOffset;
1971         } else {
1972             canOverScroll = true;
1973         }
1974     } else if (!NearZero(overOffset)) {
1975         offset = offset - overOffset;
1976     }
1977     SetCanOverScroll(canOverScroll);
1978     return { remainOffset, !NearZero(overOffset) && result.reachEdge };
1979 }
1980 
HandleOutBoundary(float & offset,int32_t source,NestedState state,ScrollResult & result)1981 bool ScrollablePattern::HandleOutBoundary(float& offset, int32_t source, NestedState state, ScrollResult& result)
1982 {
1983     auto overOffsets = GetOverScrollOffset(offset);
1984     auto backOverOffset = Negative(offset) ? overOffsets.start : overOffsets.end;
1985     auto oppositeOverOffset = Negative(offset) ? overOffsets.end : overOffsets.start;
1986     if (state != NestedState::GESTURE) {
1987         if (NearZero(backOverOffset)) {
1988             return false;
1989         }
1990         result = {offset - backOverOffset, true};
1991         offset = backOverOffset;
1992         return true;
1993     }
1994     auto nestedScroll = GetNestedScroll();
1995     auto isAtTopOrBottom = !NearZero(backOverOffset) || !NearZero(oppositeOverOffset);
1996     if (!NestedScrollOutOfBoundary() && nestedScroll.NeedParent()) {
1997         for (auto ancestor = GetNestedScrollParent(); ancestor != nullptr;
1998             ancestor = ancestor->GetNestedScrollParent()) {
1999             if (ancestor->NestedScrollOutOfBoundary()) {
2000                 auto ancestorResult = ancestor->HandleScroll(offset, source,
2001                     isAtTopOrBottom ? NestedState::CHILD_OVER_SCROLL : NestedState::CHILD_SCROLL,
2002                     GetVelocity());
2003                 offset = ancestorResult.remain;
2004                 SetCanOverScroll(NearZero(offset));
2005                 return true;
2006             }
2007             auto ancestorNestedScroll = ancestor->GetNestedScroll();
2008             if (!ancestorNestedScroll.NeedParent()) {
2009                 break;
2010             }
2011         }
2012         return false;
2013     }
2014     return HandleSelfOutBoundary(offset, source, backOverOffset, oppositeOverOffset);
2015 }
2016 
HandleSelfOutBoundary(float & offset,int32_t source,const float backOverOffset,const float oppositeOverOffset)2017 bool ScrollablePattern::HandleSelfOutBoundary(float& offset, int32_t source, const float backOverOffset,
2018     const float oppositeOverOffset)
2019 {
2020     if (NearZero(backOverOffset)) {
2021         return false;
2022     }
2023     offset -= backOverOffset;
2024     ScrollResult result = { 0.f, false};
2025     auto parent = GetNestedScrollParent();
2026     if (!NearZero(offset) && parent) {
2027         auto nestedScrollOptions = GetNestedScroll();
2028         auto nestedScroll = Positive(offset) ? nestedScrollOptions.backward : nestedScrollOptions.forward;
2029         switch (nestedScroll) {
2030             case NestedScrollMode::SELF_FIRST: {
2031                 offset -= oppositeOverOffset;
2032                 result = parent->HandleScroll(oppositeOverOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
2033                 if (!NearZero(result.remain)) {
2034                     result = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2035                 }
2036                 break;
2037             }
2038             case NestedScrollMode::PARENT_FIRST: {
2039                 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
2040                 offset = 0.f;
2041                 break;
2042             }
2043             case NestedScrollMode::PARALLEL: {
2044                 parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
2045                 break;
2046             }
2047             default:
2048                 break;
2049         }
2050     }
2051     offset += result.remain;
2052     SetCanOverScroll(NearZero(offset));
2053     offset += backOverOffset;
2054     return true;
2055 }
2056 
HandleScroll(float offset,int32_t source,NestedState state,float velocity)2057 ScrollResult ScrollablePattern::HandleScroll(float offset, int32_t source, NestedState state, float velocity)
2058 {
2059     ScrollResult result = { 0, false };
2060     auto host = GetHost();
2061     CHECK_NULL_RETURN(host, result);
2062     auto nestedScroll = GetNestedScroll();
2063     auto parent = GetNestedScrollParent();
2064     auto initOffset = offset;
2065     if (NearZero(offset)) {
2066         ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
2067         ExecuteScrollFrameBegin(offset, scrollState);
2068     } else if (!HandleOutBoundary(offset, source, state, result)) {
2069         if (parent && !IsScrollSnap() &&
2070                 ((offset < 0 && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) ||
2071                     (offset > 0 && nestedScroll.backward == NestedScrollMode::PARENT_FIRST))) {
2072             result = HandleScrollParentFirst(offset, source, state);
2073         } else if (parent && ((offset < 0 && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
2074                                 (offset > 0 && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
2075             result = HandleScrollSelfFirst(offset, source, state);
2076         } else if (parent && ((offset < 0 && nestedScroll.forward == NestedScrollMode::PARALLEL) ||
2077                                 (offset > 0 && nestedScroll.backward == NestedScrollMode::PARALLEL))) {
2078             result = HandleScrollParallel(offset, source, state);
2079         } else {
2080             result = HandleScrollSelfOnly(offset, source, state);
2081         }
2082     }
2083     ACE_SCOPED_TRACE("HandleScroll, initOffset:%f, processedOffset:%f, "
2084                      "source:%d, nestedState:%d, canOverScroll:%u, id:%d, tag:%s",
2085         initOffset, offset, source, state, GetCanOverScroll(),
2086         static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2087     UpdateNestedScrollVelocity(offset, state);
2088     bool moved = HandleScrollImpl(offset, source);
2089     NotifyMoved(moved);
2090     return result;
2091 }
2092 
HandleScrollVelocity(float velocity,const RefPtr<NestableScrollContainer> & child)2093 bool ScrollablePattern::HandleScrollVelocity(float velocity,  const RefPtr<NestableScrollContainer>& child)
2094 {
2095     // if scrollable try to over scroll when it is at the boundary,
2096     // scrollable does not start fling animation.
2097     SetNestedScrolling(false);
2098     SetScrollOriginChild(AceType::WeakClaim(AceType::RawPtr(child)));
2099     auto edgeEffect = GetEdgeEffect();
2100     auto needFlingAtEdge = !(((IsAtTop() && Positive(velocity)) || (IsAtBottom() && Negative(velocity))));
2101     auto isOutOfBoundary = OutBoundaryCallback();
2102     auto host = GetHost();
2103     CHECK_NULL_RETURN(host, false);
2104     ACE_SCOPED_TRACE("HandleScrollVelocity, IsOutOfBoundary:%u, needFlingAtEdge:%u, edgeEffect:%d, IsAtTop:%u, "
2105                      "IsAtBottom:%u, velocity:%f, id:%d, tag:%s",
2106         isOutOfBoundary, needFlingAtEdge, edgeEffect, IsAtTop(), IsAtBottom(), velocity,
2107         static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2108     if (!isOutOfBoundary && needFlingAtEdge) {
2109         // trigger scroll animation if edge not reached
2110         if (scrollableEvent_ && scrollableEvent_->GetScrollable()) {
2111             scrollableEvent_->GetScrollable()->StartScrollAnimation(0.0f, velocity);
2112             return true;
2113         }
2114         return false;
2115     }
2116     SetCanOverScroll(true);
2117     return HandleOverScroll(velocity) || GetEdgeEffect() == EdgeEffect::FADE;
2118 }
2119 
RemainVelocityToChild(float remainVelocity)2120 void ScrollablePattern::RemainVelocityToChild(float remainVelocity)
2121 {
2122     auto host = GetHost();
2123     CHECK_NULL_VOID(host);
2124     ACE_SCOPED_TRACE("RemainVelocityToChild, remainVelocity:%f id:%d, tag:%s",
2125         remainVelocity, static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2126     Fling(remainVelocity);
2127 }
2128 
HandleScrollableOverScroll(float velocity)2129 bool ScrollablePattern::HandleScrollableOverScroll(float velocity)
2130 {
2131     bool result = false;
2132     for (auto ancestor = GetNestedScrollParent(); ancestor != nullptr; ancestor = ancestor->GetNestedScrollParent()) {
2133         if (ancestor->NestedScrollOutOfBoundary()) {
2134             result = ancestor->HandleScrollVelocity(velocity, Claim(this));
2135             break;
2136         }
2137         auto ancestorNestedScroll = ancestor->GetNestedScroll();
2138         if (!ancestorNestedScroll.NeedParent()) {
2139             break;
2140         }
2141     }
2142     if (result) {
2143         OnScrollEndRecursiveInner(velocity);
2144         return true;
2145     }
2146     OnScrollEnd();
2147     auto parent = GetNestedScrollParent();
2148     auto nestedScroll = GetNestedScroll();
2149     if (!result && parent && nestedScroll.NeedParent()) {
2150         result = parent->HandleScrollVelocity(velocity, Claim(this));
2151     }
2152     return result;
2153 }
2154 
HandleOverScroll(float velocity)2155 bool ScrollablePattern::HandleOverScroll(float velocity)
2156 {
2157     auto parent = GetNestedScrollParent();
2158     auto nestedScroll = GetNestedScroll();
2159     auto host = GetHost();
2160     CHECK_NULL_RETURN(host, false);
2161     auto isOutOfBoundary = IsOutOfBoundary();
2162     ACE_SCOPED_TRACE("HandleOverScroll, IsOutOfBoundary:%u, id:%d, tag:%s", isOutOfBoundary,
2163         static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2164     if (!parent || !nestedScroll.NeedParent(velocity < 0) || isOutOfBoundary) {
2165         if (GetEdgeEffect() == EdgeEffect::SPRING && AnimateStoped()) {
2166             // trigger onScrollEnd later, when spring animation finishes
2167             ProcessSpringEffect(velocity, true);
2168             return true;
2169         }
2170         OnScrollEnd();
2171         return false;
2172     }
2173     if (parent && InstanceOf<ScrollablePattern>(parent)) {
2174         // Components that are not ScrollablePattern do not implement NestedScrollOutOfBoundary and
2175         // handleScroll is handled differently, so isolate the implementation of handleOverScroll
2176         return HandleScrollableOverScroll(velocity);
2177     }
2178     // parent handle over scroll first
2179     if ((velocity < 0 && (nestedScroll.forward == NestedScrollMode::SELF_FIRST)) ||
2180         (velocity > 0 && (nestedScroll.backward == NestedScrollMode::SELF_FIRST)) ||
2181         (!InstanceOf<ScrollablePattern>(parent) && !isOutOfBoundary)) {
2182         if (parent->HandleScrollVelocity(velocity)) {
2183             OnScrollEnd();
2184             return true;
2185         }
2186         if (GetEdgeEffect() == EdgeEffect::SPRING) {
2187             ProcessSpringEffect(velocity);
2188             return true;
2189         }
2190     }
2191 
2192     // self handle over scroll first
2193     if (GetEdgeEffect() == EdgeEffect::SPRING) {
2194         ProcessSpringEffect(velocity);
2195         return true;
2196     }
2197     OnScrollEnd();
2198     return parent->HandleScrollVelocity(velocity);
2199 }
2200 
ExecuteScrollFrameBegin(float & mainDelta,ScrollState state)2201 void ScrollablePattern::ExecuteScrollFrameBegin(float& mainDelta, ScrollState state)
2202 {
2203     auto context = PipelineContext::GetCurrentContextSafely();
2204     auto eventHub = GetEventHub<ScrollableEventHub>();
2205     CHECK_NULL_VOID(eventHub);
2206     auto scrollFrameBeginCallback = eventHub->GetOnScrollFrameBegin();
2207     if (!context || !scrollFrameBeginCallback) {
2208         return;
2209     }
2210 
2211     auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
2212     auto scrollRes = scrollFrameBeginCallback(-offset, state);
2213     mainDelta = -context->NormalizeToPx(scrollRes.offset);
2214 }
2215 
OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child,float position,float velocity)2216 void ScrollablePattern::OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child, float position, float velocity)
2217 {
2218     OnScrollStartRecursiveInner(child, position, velocity);
2219     SetNestedScrolling(true);
2220     SetScrollOriginChild(child);
2221 }
2222 
OnScrollStartRecursiveInner(WeakPtr<NestableScrollContainer> child,float position,float velocity)2223 void ScrollablePattern::OnScrollStartRecursiveInner(
2224     WeakPtr<NestableScrollContainer> child, float position, float velocity)
2225 {
2226     SetIsNestedInterrupt(false);
2227     HandleScrollImpl(position, SCROLL_FROM_START);
2228     auto parent = GetNestedScrollParent();
2229     auto nestedScroll = GetNestedScroll();
2230     if (parent && nestedScroll.NeedParent()) {
2231         parent->OnScrollStartRecursive(child, position, GetVelocity());
2232     }
2233 }
2234 
OnScrollEndRecursive(const std::optional<float> & velocity)2235 void ScrollablePattern::OnScrollEndRecursive(const std::optional<float>& velocity)
2236 {
2237     OnScrollEndRecursiveInner(velocity);
2238     SetNestedScrolling(false);
2239     CheckRestartSpring(false);
2240 }
2241 
SetNestedScrolling(bool nestedScrolling)2242 void ScrollablePattern::SetNestedScrolling(bool nestedScrolling)
2243 {
2244     CHECK_NULL_VOID(scrollableEvent_);
2245     auto scrollable = scrollableEvent_->GetScrollable();
2246     CHECK_NULL_VOID(scrollable);
2247     scrollable->SetNestedScrolling(nestedScrolling);
2248     // Sliding the touchPad is an axis event, and the parent of the nested scroll cannot respond to TouchDown,
2249     // so the scrollable animation stops when the nested scroll scroll start.
2250     if (nestedScrolling) {
2251         scrollable->StopScrollable();
2252     }
2253 }
2254 
OnScrollEndRecursiveInner(const std::optional<float> & velocity)2255 void ScrollablePattern::OnScrollEndRecursiveInner(const std::optional<float>& velocity)
2256 {
2257     if (!IsScrollableStopped() && !GetNestedScrolling()) {
2258         return;
2259     }
2260     OnScrollEnd();
2261     auto parent = GetNestedScrollParent();
2262     auto nestedScroll = GetNestedScroll();
2263     if (parent && (nestedScroll.NeedParent() || GetIsNestedInterrupt())) {
2264         parent->OnScrollEndRecursive(velocity);
2265     }
2266     SetIsNestedInterrupt(false);
2267 }
2268 
OnScrollDragEndRecursive()2269 void ScrollablePattern::OnScrollDragEndRecursive()
2270 {
2271     auto parent = GetNestedScrollParent();
2272     auto nestedScroll = GetNestedScroll();
2273     if (parent && nestedScroll.NeedParent()) {
2274         parent->OnScrollDragEndRecursive();
2275     }
2276     CheckRestartSpring(false, false);
2277 }
2278 
GetVelocity() const2279 float ScrollablePattern::GetVelocity() const
2280 {
2281     float velocity = 0.0f;
2282     CHECK_NULL_RETURN(scrollableEvent_, velocity);
2283     auto scrollable = scrollableEvent_->GetScrollable();
2284     CHECK_NULL_RETURN(scrollable, velocity);
2285     velocity = scrollable->GetCurrentVelocity();
2286     return velocity;
2287 }
2288 
RegisterScrollingListener(const RefPtr<ScrollingListener> listener)2289 void ScrollablePattern::RegisterScrollingListener(const RefPtr<ScrollingListener> listener)
2290 {
2291     CHECK_NULL_VOID(listener);
2292     scrollingListener_.emplace_back(listener);
2293 }
2294 
FireAndCleanScrollingListener()2295 void ScrollablePattern::FireAndCleanScrollingListener()
2296 {
2297     for (auto listener : scrollingListener_) {
2298         CHECK_NULL_VOID(listener);
2299         listener->NotifyScrollingEvent();
2300     }
2301     scrollingListener_.clear();
2302 }
2303 
CleanScrollingListener()2304 void ScrollablePattern::CleanScrollingListener()
2305 {
2306     scrollingListener_.clear();
2307 }
2308 
GetMainContentSize() const2309 float ScrollablePattern::GetMainContentSize() const
2310 {
2311     auto host = GetHost();
2312     CHECK_NULL_RETURN(host, 0.0);
2313     auto geometryNode = host->GetGeometryNode();
2314     CHECK_NULL_RETURN(geometryNode, 0.0);
2315     return geometryNode->GetPaddingSize().MainSize(axis_);
2316 }
2317 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)2318 void ScrollablePattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
2319 {
2320     auto host = GetHost();
2321     CHECK_NULL_VOID(host);
2322     ACE_SCOPED_TRACE("ScrollToEdge scrollEdgeType:%zu, id:%d, tag:%s", scrollEdgeType,
2323         static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2324     if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
2325         ScrollToIndex(0, false, ScrollAlign::START);
2326     } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
2327         // use LAST_ITEM for children count changed after scrollEdge(Edge.Bottom) and before layout
2328         ScrollToIndex(LAST_ITEM, false, ScrollAlign::END);
2329     }
2330 }
2331 
Fling(double flingVelocity)2332 void ScrollablePattern::Fling(double flingVelocity)
2333 {
2334     if (!IsScrollableStopped()) {
2335         scrollAbort_ = true;
2336         StopScrollable();
2337     }
2338     if (!isAnimationStop_) {
2339         scrollAbort_ = true;
2340         StopAnimation(springAnimation_);
2341         StopAnimation(curveAnimation_);
2342     }
2343     if (animator_ && !animator_->IsStopped()) {
2344         scrollAbort_ = true;
2345         animator_->Stop();
2346     }
2347     CHECK_NULL_VOID(scrollableEvent_);
2348     auto scrollable = scrollableEvent_->GetScrollable();
2349     CHECK_NULL_VOID(scrollable);
2350     if (IsOutOfBoundary()) {
2351         scrollable->HandleOverScroll(flingVelocity);
2352     } else {
2353         FireOnScrollStart();
2354         scrollable->StartScrollAnimation(0.0f, flingVelocity);
2355     }
2356     auto pipeline = GetContext();
2357     CHECK_NULL_VOID(pipeline);
2358     pipeline->RequestFrame();
2359 }
2360 
NotifyFRCSceneInfo(const std::string & scene,double velocity,SceneStatus sceneStatus)2361 void ScrollablePattern::NotifyFRCSceneInfo(const std::string& scene, double velocity, SceneStatus sceneStatus)
2362 {
2363     auto host = GetHost();
2364     CHECK_NULL_VOID(host);
2365     host->AddFRCSceneInfo(scene, velocity, sceneStatus);
2366 }
2367 
FireOnScrollStart()2368 void ScrollablePattern::FireOnScrollStart()
2369 {
2370     auto host = GetHost();
2371     CHECK_NULL_VOID(host);
2372     auto hub = host->GetEventHub<ScrollableEventHub>();
2373     CHECK_NULL_VOID(hub);
2374     SuggestOpIncGroup(true);
2375     if (scrollStop_ && !GetScrollAbort()) {
2376         OnScrollStop(hub->GetOnScrollStop());
2377     }
2378     UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
2379         AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
2380     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
2381     if (GetScrollAbort()) {
2382         ACE_SCOPED_TRACE("ScrollAbort, no OnScrollStart, id:%d, tag:%s",
2383             static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2384         return;
2385     }
2386     auto scrollBar = GetScrollBar();
2387     if (scrollBar) {
2388         scrollBar->PlayScrollBarAppearAnimation();
2389     }
2390     StopScrollBarAnimatorByProxy();
2391     host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
2392     FireObserverOnScrollStart();
2393     auto onScrollStart = hub->GetOnScrollStart();
2394     CHECK_NULL_VOID(onScrollStart);
2395     ACE_SCOPED_TRACE(
2396         "OnScrollStart, id:%d, tag:%s", static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2397     onScrollStart();
2398     AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_START);
2399 }
2400 
OnScrollStartCallback()2401 void ScrollablePattern::OnScrollStartCallback()
2402 {
2403     FireOnScrollStart();
2404 };
2405 
FireOnScroll(float finalOffset,OnScrollEvent & onScroll) const2406 void ScrollablePattern::FireOnScroll(float finalOffset, OnScrollEvent& onScroll) const
2407 {
2408     auto offsetPX = Dimension(finalOffset);
2409     auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
2410     auto scrollState = GetScrollState();
2411     bool isTriggered = false;
2412     if (!NearZero(finalOffset)) {
2413         onScroll(offsetVP, scrollState);
2414         isTriggered = true;
2415     }
2416     if (scrollStop_ && !GetScrollAbort()) {
2417         if (scrollState != ScrollState::IDLE || !isTriggered) {
2418             onScroll(0.0_vp, ScrollState::IDLE);
2419         }
2420     }
2421 }
2422 
FireObserverOnTouch(const TouchEventInfo & info)2423 void ScrollablePattern::FireObserverOnTouch(const TouchEventInfo& info)
2424 {
2425     if (positionController_) {
2426         auto observer = positionController_->GetObserver();
2427         if (observer.onTouchEvent) {
2428             auto touchInfo = info;
2429             (*observer.onTouchEvent)(touchInfo);
2430         }
2431     }
2432 }
2433 
FireObserverOnPanActionEnd(GestureEvent & info)2434 void ScrollablePattern::FireObserverOnPanActionEnd(GestureEvent& info)
2435 {
2436     if (positionController_) {
2437         auto observer = positionController_->GetObserver();
2438         if (observer.onPanActionEndEvent) {
2439             observer.onPanActionEndEvent(info);
2440         }
2441     }
2442 }
2443 
FireObserverOnReachStart()2444 void ScrollablePattern::FireObserverOnReachStart()
2445 {
2446     if (positionController_) {
2447         auto observer = positionController_->GetObserver();
2448         if (observer.onReachStartEvent) {
2449             observer.onReachStartEvent();
2450         }
2451     }
2452 }
2453 
FireObserverOnReachEnd()2454 void ScrollablePattern::FireObserverOnReachEnd()
2455 {
2456     if (positionController_) {
2457         auto observer = positionController_->GetObserver();
2458         if (observer.onReachEndEvent) {
2459             observer.onReachEndEvent();
2460         }
2461     }
2462 }
2463 
FireObserverOnScrollStart()2464 void ScrollablePattern::FireObserverOnScrollStart()
2465 {
2466     if (positionController_) {
2467         auto observer = positionController_->GetObserver();
2468         if (observer.onScrollStartEvent) {
2469             observer.onScrollStartEvent();
2470         }
2471     }
2472 }
2473 
FireObserverOnScrollStop()2474 void ScrollablePattern::FireObserverOnScrollStop()
2475 {
2476     if (positionController_) {
2477         auto observer = positionController_->GetObserver();
2478         if (observer.onScrollStopEvent) {
2479             observer.onScrollStopEvent();
2480         }
2481     }
2482 }
2483 
FireObserverOnDidScroll(float finalOffset)2484 void ScrollablePattern::FireObserverOnDidScroll(float finalOffset)
2485 {
2486     OnScrollEvent onScroll = [weak = WeakClaim(this)](Dimension dimension, ScrollState state) {
2487         auto pattern = weak.Upgrade();
2488         CHECK_NULL_VOID(pattern);
2489         if (pattern->positionController_) {
2490             auto observer = pattern->positionController_->GetObserver();
2491             if (observer.onDidScrollEvent) {
2492                 observer.onDidScrollEvent(dimension, state, pattern->IsAtTop(), pattern->IsAtBottom());
2493             }
2494         }
2495     };
2496     FireOnScroll(finalOffset, onScroll);
2497 }
2498 
SuggestOpIncGroup(bool flag)2499 void ScrollablePattern::SuggestOpIncGroup(bool flag)
2500 {
2501     if (!SystemProperties::IsOpIncEnable()) {
2502         return;
2503     }
2504     auto host = GetHost();
2505     CHECK_NULL_VOID(host);
2506     if (host->GetSuggestOpIncActivatedOnce()) {
2507         return;
2508     }
2509     flag = flag && isVertical();
2510     if (flag) {
2511         ACE_SCOPED_TRACE("SuggestOpIncGroup %s", host->GetHostTag().c_str());
2512         auto parent = host->GetAncestorNodeOfFrame();
2513         CHECK_NULL_VOID(parent);
2514         parent->SetSuggestOpIncActivatedOnce();
2515         host->SetSuggestOpIncActivatedOnce();
2516         // get 1st layer
2517         for (auto child : host->GetAllChildren()) {
2518             if (!child) {
2519                 continue;
2520             }
2521             auto frameNode = AceType::DynamicCast<FrameNode>(child);
2522             if (!frameNode || frameNode->GetSuggestOpIncActivatedOnce()) {
2523                 continue;
2524             }
2525             std::string path(host->GetHostTag());
2526             frameNode->FindSuggestOpIncNode(path, host->GetGeometryNode()->GetFrameSize(), 1);
2527         }
2528     }
2529 }
2530 
OnScrollStop(const OnScrollStopEvent & onScrollStop)2531 void ScrollablePattern::OnScrollStop(const OnScrollStopEvent& onScrollStop)
2532 {
2533     auto host = GetHost();
2534     CHECK_NULL_VOID(host);
2535     if (!scrollStop_) {
2536         return;
2537     }
2538     UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
2539         AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
2540     if (!GetScrollAbort()) {
2541         if (host != nullptr) {
2542             host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
2543         }
2544         FireObserverOnScrollStop();
2545         if (onScrollStop) {
2546             ACE_SCOPED_TRACE("OnScrollStop, id:%d, tag:%s", static_cast<int32_t>(host->GetAccessibilityId()),
2547                 host->GetTag().c_str());
2548             onScrollStop();
2549             AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_STOP);
2550             SetScrollSource(SCROLL_FROM_NONE);
2551         }
2552         auto scrollBar = GetScrollBar();
2553         if (scrollBar) {
2554             scrollBar->ScheduleDisappearDelayTask();
2555         }
2556         StartScrollBarAnimatorByProxy();
2557         auto pipeline = host->GetContext();
2558         if (pipeline && pipeline->GetTaskExecutor() && pipeline->GetTHPExtraManager()) {
2559             auto taskExecutor = pipeline->GetTaskExecutor();
2560             const uint32_t delay = 100; // 100: ms
2561             taskExecutor->RemoveTask(TaskExecutor::TaskType::UI, "NotifyResponseRegionChanged");
2562             auto task = [weak = WeakClaim(pipeline)]() {
2563                 auto pipeline = weak.Upgrade();
2564                 CHECK_NULL_VOID(pipeline);
2565                 pipeline->NotifyResponseRegionChanged(pipeline->GetRootElement());
2566             };
2567             taskExecutor->PostDelayedTask(task, TaskExecutor::TaskType::UI, delay, "NotifyResponseRegionChanged");
2568         }
2569     } else {
2570         ACE_SCOPED_TRACE("ScrollAbort, no OnScrollStop, id:%d, tag:%s",
2571             static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2572     }
2573     PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
2574     AceAsyncTraceEnd(
2575         0, (TRAILING_ANIMATION + std::to_string(host->GetAccessibilityId()) + std::string(" ") + host->GetTag())
2576             .c_str());
2577     scrollStop_ = false;
2578     SetScrollAbort(false);
2579 }
2580 
FireOnWillScroll(float offset) const2581 float ScrollablePattern::FireOnWillScroll(float offset) const
2582 {
2583     auto eventHub = GetEventHub<ScrollableEventHub>();
2584     CHECK_NULL_RETURN(eventHub, offset);
2585     auto onScroll = eventHub->GetOnWillScroll();
2586     CHECK_NULL_RETURN(onScroll, offset);
2587     auto offsetPX = Dimension(offset);
2588     auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
2589     auto scrollRes = onScroll(offsetVP, GetScrollState(), ConvertScrollSource(scrollSource_));
2590     auto context = PipelineContext::GetCurrentContextSafely();
2591     CHECK_NULL_RETURN(context, offset);
2592     return context->NormalizeToPx(scrollRes.offset);
2593 }
2594 
2595 /**
2596  * @description: Register with the drag drop manager
2597  * @return None
2598  */
Register2DragDropManager()2599 void ScrollablePattern::Register2DragDropManager()
2600 {
2601     auto host = GetHost();
2602     CHECK_NULL_VOID(host);
2603     auto pipeline = PipelineContext::GetCurrentContext();
2604     CHECK_NULL_VOID(pipeline);
2605     auto dragDropManager = pipeline->GetDragDropManager();
2606     CHECK_NULL_VOID(dragDropManager);
2607     dragDropManager->RegisterDragStatusListener(host->GetId(), AceType::WeakClaim(AceType::RawPtr(host)));
2608 }
2609 
2610 /**
2611  * @description: Determine whether it is in the hot zone, then
2612  * 1.Gives the rolling direction according to the location of the hot zone
2613  * 2.Gives the distance from the edge of the hot zone from the drag point
2614  * @param {PointF&} point The drag point
2615  * @return The distance from the edge of the hot zone from the drag point.scroll up:Offset percent is positive, scroll
2616  * down:Offset percent is  negative
2617  */
IsInHotZone(const PointF & point)2618 float ScrollablePattern::IsInHotZone(const PointF& point)
2619 {
2620     auto host = GetHost();
2621     auto offset = 0.f;
2622     auto geometryNode = host->GetGeometryNode();
2623     CHECK_NULL_RETURN(geometryNode, 0.f);
2624 
2625     auto wholeRect = geometryNode->GetFrameRect();
2626     wholeRect.SetOffset(host->GetTransformRelativeOffset());
2627     auto hotZoneHeightPX = HOT_ZONE_HEIGHT_VP_DIM.ConvertToPx();
2628     auto hotZoneWidthPX = HOT_ZONE_WIDTH_VP_DIM.ConvertToPx();
2629     if (isVertical()) {
2630         // create top hot zone,it is a rectangle
2631         auto topHotzone = wholeRect;
2632         topHotzone.SetHeight(hotZoneHeightPX);
2633 
2634         // create bottom hot zone,it is a rectangle
2635         auto bottomHotzone = wholeRect;
2636         auto bottomZoneEdgeY = wholeRect.GetY() + wholeRect.Height();
2637         bottomHotzone.SetTop(bottomZoneEdgeY - hotZoneHeightPX);
2638         bottomHotzone.SetHeight(hotZoneHeightPX);
2639 
2640         // Determines whether the drag point is within the hot zone,
2641         // then gives the scroll component movement direction according to which hot zone the point is in
2642         // top or bottom hot zone
2643         if (topHotzone.IsInRegion(point)) {
2644             offset = hotZoneHeightPX - point.GetY() + topHotzone.GetY();
2645             if (!NearZero(hotZoneHeightPX)) {
2646                 return offset / hotZoneHeightPX;
2647             }
2648         } else if (bottomHotzone.IsInRegion(point)) {
2649             offset = bottomZoneEdgeY - point.GetY() - hotZoneHeightPX;
2650             if (!NearZero(hotZoneHeightPX)) {
2651                 return offset / hotZoneHeightPX;
2652             }
2653         }
2654     } else {
2655         auto leftHotzone = wholeRect;
2656 
2657         // create left hot zone,it is a rectangle
2658         leftHotzone.SetWidth(hotZoneWidthPX);
2659 
2660         // create right hot zone,it is a rectangle
2661         auto rightHotzone = wholeRect;
2662         rightHotzone.SetWidth(hotZoneWidthPX);
2663         auto rightZoneEdgeX = wholeRect.GetX() + wholeRect.Width();
2664         rightHotzone.SetLeft(rightZoneEdgeX - hotZoneWidthPX);
2665 
2666         // Determines whether the drag point is within the hot zone,
2667         // gives the scroll component movement direction according to which hot zone the point is in
2668         // left or right hot zone
2669         if (leftHotzone.IsInRegion(point)) {
2670             offset = hotZoneWidthPX - point.GetX() + wholeRect.GetX();
2671             if (!NearZero(hotZoneWidthPX)) {
2672                 return offset / hotZoneWidthPX;
2673             }
2674         } else if (rightHotzone.IsInRegion(point)) {
2675             offset = rightZoneEdgeX - point.GetX() - hotZoneWidthPX;
2676             if (!NearZero(hotZoneWidthPX)) {
2677                 return offset / hotZoneWidthPX;
2678             }
2679         }
2680     }
2681 
2682     return 0.f;
2683 }
2684 
2685 /**
2686  * @description: Determines whether the scroll component is in the vertical direction
2687  * @return True,If the scrolling component is vertical
2688  */
isVertical() const2689 bool ScrollablePattern::isVertical() const
2690 {
2691     return axis_ == Axis::VERTICAL;
2692 }
2693 
2694 /**
2695  * @description: scroll up or down
2696  * @param {float} offsetPct offset percent.When scrolling in the vertical or horizontal direction, there is a distance
2697  * between the drag point and the outer edge of the hot zone, and the percentage represents the proportion of this
2698  * distance to the height or width of the hot zone
2699  * @return None
2700  */
HotZoneScroll(const float offsetPct)2701 void ScrollablePattern::HotZoneScroll(const float offsetPct)
2702 {
2703     auto host = GetHost();
2704     CHECK_NULL_VOID(IsScrollable());
2705     CHECK_NULL_VOID(!NearZero(offsetPct));
2706 
2707     // There are three types of situations to consider.
2708     // 1. Enter the hot zone for the first time.
2709     // 2. When the drag point leaves the hot zone, it enters the hot zone again
2710     // 3. When the drag point moves within the hot zone, the hot zone offset changes
2711     CHECK_NULL_VOID(!NearEqual(lastHonezoneOffsetPct_, offsetPct));
2712 
2713     if (AnimateRunning()) {
2714         // Variable speed rolling
2715         // When the drag point is in the hot zone, and the hot zone offset changes.
2716         // Then need to modify the offset percent
2717         if (velocityMotion_) {
2718             velocityMotion_->Reset(offsetPct);
2719         }
2720         return;
2721     }
2722 
2723     if (!animator_) {
2724         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
2725         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
2726             auto pattern = weak.Upgrade();
2727             CHECK_NULL_VOID(pattern);
2728             pattern->OnAnimateStop();
2729         });
2730     }
2731 
2732     if (!velocityMotion_) {
2733         // Enter the hot zone for the first time.
2734         velocityMotion_ = AceType::MakeRefPtr<BezierVariableVelocityMotion>(
2735             offsetPct, [weak = WeakClaim(this)](float offset) -> bool {
2736                 auto pattern = weak.Upgrade();
2737                 CHECK_NULL_RETURN(pattern, true);
2738                 // Stop scrolling when reach the bottom or top
2739                 return ((LessNotEqual(offset, 0) && pattern->IsAtBottom()) ||
2740                     (GreatNotEqual(offset, 0) && pattern->IsAtTop()));
2741             });
2742         velocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
2743             // Get the distance component need to roll from BezierVariableVelocityMotion
2744             // Roll up: negative value, Roll up: positive value
2745             auto pattern = weakScroll.Upgrade();
2746             CHECK_NULL_VOID(pattern);
2747             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
2748             pattern->UpdateMouseStart(offset);
2749             if (pattern->hotZoneScrollCallback_) {
2750                 pattern->hotZoneScrollCallback_();
2751             }
2752         });
2753         velocityMotion_->ReInit(offsetPct);
2754     } else {
2755         // When the drag point leaves the hot zone, it enters the hot zone again.Then need to reset offset percent.
2756         velocityMotion_->ReInit(offsetPct);
2757     }
2758     // Save the last offset percent
2759     lastHonezoneOffsetPct_ = offsetPct;
2760     animator_->PlayMotion(velocityMotion_);
2761     FireOnScrollStart();
2762 }
2763 
2764 /**
2765  * @description: When the drag point leaves the hot zone, stop the animation.
2766  * @return None
2767  */
StopHotzoneScroll()2768 void ScrollablePattern::StopHotzoneScroll()
2769 {
2770     if (!AnimateStoped()) {
2771         animator_->Stop();
2772     }
2773 }
2774 
2775 /**
2776  * @description: Handle drag and drop events
2777  * When a drag point enters or moves over a component, determine whether it is within the hot zone.
2778  * When leave the component, stop scrolling
2779  * @param {DragEventType&} dragEventType Drag the event type
2780  * @param {NotifyDragEvent&} notifyDragEvent Drag event
2781  * @return None
2782  */
HandleHotZone(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2783 void ScrollablePattern::HandleHotZone(
2784     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2785 {
2786     // The starting version of the auto-scroll feature is 11
2787     CHECK_NULL_VOID(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN));
2788     PointF point(static_cast<float>(notifyDragEvent->GetX()), static_cast<float>(notifyDragEvent->GetY()));
2789     switch (dragEventType) {
2790         case DragEventType::ENTER: {
2791             HandleMoveEventInComp(point);
2792             break;
2793         }
2794         case DragEventType::MOVE: {
2795             HandleMoveEventInComp(point);
2796             break;
2797         }
2798         case DragEventType::DROP:
2799         case DragEventType::LEAVE: {
2800             HandleLeaveHotzoneEvent();
2801             break;
2802         }
2803         default:
2804             break;
2805     }
2806 }
2807 
2808 /**
2809  * @description:When a drag point is inside the scroll component, it is necessary to handle the events of each moving
2810  * point
2811  * @param {PointF&} point the drag point
2812  * @return None
2813  */
HandleMoveEventInComp(const PointF & point)2814 void ScrollablePattern::HandleMoveEventInComp(const PointF& point)
2815 {
2816     float offsetPct = IsInHotZone(point);
2817     if ((Positive(offsetPct) && !IsAtTop()) || (Negative(offsetPct) && !IsAtBottom())) {
2818         // The drag point enters the hot zone
2819         HotZoneScroll(offsetPct);
2820     } else {
2821         // Although it entered the rolling component, it is not in the rolling component hot zone.Then stop
2822         // scrolling
2823         HandleLeaveHotzoneEvent();
2824     }
2825 }
2826 
2827 /**
2828  * @description:When the drag point is not in the hot zone, need to stop scrolling, if it exists.
2829  * This function is executed multiple times
2830  * @return None
2831  */
HandleLeaveHotzoneEvent()2832 void ScrollablePattern::HandleLeaveHotzoneEvent()
2833 {
2834     // Stop scrolling up and down
2835     StopHotzoneScroll();
2836 }
2837 
2838 /**
2839  * @description: This is the entry point for handling drag events
2840  * @return None
2841  */
HandleOnDragStatusCallback(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2842 void ScrollablePattern::HandleOnDragStatusCallback(
2843     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2844 {
2845     HandleHotZone(dragEventType, notifyDragEvent);
2846 }
2847 
2848 /**
2849  * @description: Cancel registration with the drag drop manager
2850  * @return None
2851  */
UnRegister2DragDropManager()2852 void ScrollablePattern::UnRegister2DragDropManager()
2853 {
2854     auto host = GetHost();
2855     CHECK_NULL_VOID(host);
2856     auto pipeline = PipelineContext::GetCurrentContext();
2857     CHECK_NULL_VOID(pipeline);
2858     auto dragDropManager = pipeline->GetDragDropManager();
2859     CHECK_NULL_VOID(dragDropManager);
2860     dragDropManager->UnRegisterDragStatusListener(host->GetId());
2861 }
2862 
NeedCoordinateScrollWithNavigation(double offset,int32_t source,const OverScrollOffset & overOffsets)2863 bool ScrollablePattern::NeedCoordinateScrollWithNavigation(
2864     double offset, int32_t source, const OverScrollOffset& overOffsets)
2865 {
2866     if (!navBarPattern_) {
2867         return false;
2868     }
2869     return (GreatNotEqual(overOffsets.start, 0.0) || navBarPattern_->CanCoordScrollUp(offset)) &&
2870            (axis_ == Axis::VERTICAL) && (source != SCROLL_FROM_ANIMATION_SPRING);
2871 }
2872 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)2873 void ScrollablePattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
2874 {
2875     float distance = reverse ? GetMainContentSize() : -GetMainContentSize();
2876     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
2877         distance = distance / 2.f;
2878     }
2879     if (smooth) {
2880         float position = -GetTotalOffset() + distance;
2881         AnimateTo(-position, -1, nullptr, true);
2882     } else {
2883         StopAnimate();
2884         UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
2885     }
2886 }
2887 
PrintOffsetLog(AceLogTag tag,int32_t id,double finalOffset)2888 void ScrollablePattern::PrintOffsetLog(AceLogTag tag, int32_t id, double finalOffset)
2889 {
2890     if (SystemProperties::GetDebugOffsetLogEnabled() && !NearZero(finalOffset)) {
2891         TAG_LOGD(tag, "Scrollable id:%{public}d, scrollSource:%{public}d, scrollOffset:%{public}f",
2892             id, scrollSource_, finalOffset);
2893     }
2894 }
2895 
CheckRestartSpring(bool sizeDiminished,bool needNestedScrolling)2896 void ScrollablePattern::CheckRestartSpring(bool sizeDiminished, bool needNestedScrolling)
2897 {
2898     auto host = GetHost();
2899     CHECK_NULL_VOID(host);
2900     auto edgeEffect = GetScrollEdgeEffect();
2901     if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
2902         return;
2903     }
2904     // Check if need update Spring when itemTotalSize diminishes.
2905     if (IsScrollableSpringMotionRunning() && sizeDiminished) {
2906         ACE_SCOPED_TRACE("CheckRestartSpring, do ProcessSpringUpdate, id:%d, tag:%s",
2907             static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2908         edgeEffect->ProcessSpringUpdate();
2909         return;
2910     }
2911     if (AnimateRunning() || !IsOutOfBoundary()) {
2912         return;
2913     }
2914     if (needNestedScrolling && !ScrollableIdle()) {
2915         return;
2916     } else if (!needNestedScrolling && !IsScrollableAnimationNotRunning()) {
2917         return;
2918     }
2919     FireOnScrollStart();
2920     ACE_SCOPED_TRACE("CheckRestartSpring, do ProcessScrollOver, id:%d, tag:%s",
2921         static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2922     edgeEffect->ProcessScrollOver(0);
2923 }
2924 
AddEventsFiredInfo(ScrollableEventType eventType)2925 void ScrollablePattern::AddEventsFiredInfo(ScrollableEventType eventType)
2926 {
2927     if (eventsFiredInfos_.size() >= EVENTS_FIRED_INFO_COUNT) {
2928         eventsFiredInfos_.pop_front();
2929     }
2930     eventsFiredInfos_.push_back(ScrollableEventsFiredInfo({
2931         .eventFiredTime_ = GetSysTimestamp(),
2932         .eventType_ = eventType,
2933         .scrollSource_ = scrollSource_,
2934     }));
2935 }
2936 
AddScrollableFrameInfo(int32_t scrollSource)2937 void ScrollablePattern::AddScrollableFrameInfo(int32_t scrollSource)
2938 {
2939     if (scrollableFrameInfos_.size() >= SCROLLABLE_FRAME_INFO_COUNT) {
2940         scrollableFrameInfos_.pop_front();
2941     }
2942     uint32_t canOverScrollInfo = IsScrollableSpringEffect();
2943     canOverScrollInfo = (canOverScrollInfo << 1) | IsScrollable();
2944     canOverScrollInfo = (canOverScrollInfo << 1) | ScrollableIdle();
2945     canOverScrollInfo = (canOverScrollInfo << 1) | animateOverScroll_;
2946     canOverScrollInfo = (canOverScrollInfo << 1) | animateCanOverScroll_;
2947     scrollableFrameInfos_.push_back(ScrollableFrameInfo({
2948         .scrollStateTime_ = GetSysTimestamp(),
2949         .scrollState_ = scrollSource,
2950         .canOverScroll_ = lastCanOverScroll_,
2951         .canOverScrollInfo_ = canOverScrollInfo,
2952     }));
2953 }
2954 
GetEdgeEffectDumpInfo()2955 void ScrollablePattern::GetEdgeEffectDumpInfo()
2956 {
2957     switch (edgeEffect_) {
2958         case EdgeEffect::NONE: {
2959             DumpLog::GetInstance().AddDesc("edgeEffect: NONE");
2960             break;
2961         }
2962         case EdgeEffect::SPRING: {
2963             DumpLog::GetInstance().AddDesc("edgeEffect: SPRING");
2964             break;
2965         }
2966         case EdgeEffect::FADE: {
2967             DumpLog::GetInstance().AddDesc("edgeEffect: FADE");
2968             break;
2969         }
2970         default: {
2971             break;
2972         }
2973     }
2974 }
2975 
GetAxisDumpInfo()2976 void ScrollablePattern::GetAxisDumpInfo()
2977 {
2978     switch (axis_) {
2979         case Axis::NONE: {
2980             DumpLog::GetInstance().AddDesc("Axis: NONE");
2981             break;
2982         }
2983         case Axis::VERTICAL: {
2984             DumpLog::GetInstance().AddDesc("Axis: VERTICAL");
2985             break;
2986         }
2987         case Axis::HORIZONTAL: {
2988             DumpLog::GetInstance().AddDesc("Axis: HORIZONTAL");
2989             break;
2990         }
2991         case Axis::FREE: {
2992             DumpLog::GetInstance().AddDesc("Axis: FREE");
2993             break;
2994         }
2995         default: {
2996             break;
2997         }
2998     }
2999 }
3000 
GetPanDirectionDumpInfo()3001 void ScrollablePattern::GetPanDirectionDumpInfo()
3002 {
3003     switch (GetScrollablePanDirection()) {
3004         case Axis::NONE: {
3005             DumpLog::GetInstance().AddDesc("ScrollablePanDirection:NONE");
3006             break;
3007         }
3008         case Axis::VERTICAL: {
3009             DumpLog::GetInstance().AddDesc("ScrollablePanDirection:VERTICAL");
3010             break;
3011         }
3012         case Axis::HORIZONTAL: {
3013             DumpLog::GetInstance().AddDesc("ScrollablePanDirection:HORIZONTAL");
3014             break;
3015         }
3016         case Axis::FREE: {
3017             DumpLog::GetInstance().AddDesc("ScrollablePanDirection:FREE");
3018             break;
3019         }
3020         default: {
3021             DumpLog::GetInstance().AddDesc("ScrollablePanDirection is null");
3022             break;
3023         }
3024     }
3025 }
3026 
GetPaintPropertyDumpInfo()3027 void ScrollablePattern::GetPaintPropertyDumpInfo()
3028 {
3029     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3030     if (paintProperty) {
3031         switch (paintProperty->GetScrollBarMode().value_or(DisplayMode::OFF)) {
3032             case DisplayMode::OFF: {
3033                 DumpLog::GetInstance().AddDesc("innerScrollBarState: OFF");
3034                 break;
3035             }
3036             case DisplayMode::AUTO: {
3037                 DumpLog::GetInstance().AddDesc("innerScrollBarState: AUTO");
3038                 break;
3039             }
3040             case DisplayMode::ON: {
3041                 DumpLog::GetInstance().AddDesc("innerScrollBarState: ON");
3042                 break;
3043             }
3044             default: {
3045                 break;
3046             }
3047         }
3048         auto scrollBarWidth = paintProperty->GetScrollBarWidth();
3049         scrollBarWidth.has_value() ? DumpLog::GetInstance().AddDesc(std::string("scrollBarWidth: ")
3050             .append(paintProperty->GetScrollBarWidth().value().ToString()))
3051             : DumpLog::GetInstance().AddDesc("scrollBarWidth: None");
3052     }
3053 }
3054 
GetEventDumpInfo()3055 void ScrollablePattern::GetEventDumpInfo()
3056 {
3057     auto host = GetHost();
3058     CHECK_NULL_VOID(host);
3059     auto hub = host->GetEventHub<ScrollableEventHub>();
3060     CHECK_NULL_VOID(hub);
3061     auto onScrollStart = hub->GetOnScrollStart();
3062     onScrollStart ? DumpLog::GetInstance().AddDesc("hasOnScrollStart: true")
3063                   : DumpLog::GetInstance().AddDesc("hasOnScrollStart: false");
3064     auto onScrollStop = hub->GetOnScrollStop();
3065     onScrollStop ? DumpLog::GetInstance().AddDesc("hasOnScrollStop: true")
3066                  : DumpLog::GetInstance().AddDesc("hasOnScrollStop: false");
3067     auto scrollHub = host->GetEventHub<ScrollEventHub>();
3068     if (scrollHub) {
3069         auto onWillScroll = scrollHub->GetOnWillScrollEvent();
3070         onWillScroll ? DumpLog::GetInstance().AddDesc("hasOnWillScroll: true")
3071                      : DumpLog::GetInstance().AddDesc("hasOnWillScroll: false");
3072         auto onDidScroll = scrollHub->GetOnDidScrollEvent();
3073         onDidScroll ? DumpLog::GetInstance().AddDesc("hasOnDidScroll: true")
3074                     : DumpLog::GetInstance().AddDesc("hasOnDidScroll: false");
3075     } else {
3076         auto onWillScroll = hub->GetOnWillScroll();
3077         onWillScroll ? DumpLog::GetInstance().AddDesc("hasOnWillScroll: true")
3078                      : DumpLog::GetInstance().AddDesc("hasOnWillScroll: false");
3079         auto onDidScroll = hub->GetOnDidScroll();
3080         onDidScroll ? DumpLog::GetInstance().AddDesc("hasOnDidScroll: true")
3081                     : DumpLog::GetInstance().AddDesc("hasOnDidScroll: false");
3082     }
3083     auto onScrollFrameBegin = hub->GetOnScrollFrameBegin();
3084     onScrollFrameBegin ? DumpLog::GetInstance().AddDesc("hasOnScrollFrameBegin: true")
3085                        : DumpLog::GetInstance().AddDesc("hasOnScrollFrameBegin: false");
3086     auto onReachStart = hub->GetOnReachStart();
3087     onReachStart ? DumpLog::GetInstance().AddDesc("hasOnReachStart: true")
3088                  : DumpLog::GetInstance().AddDesc("hasOnReachStart: false");
3089     auto onReachEnd = hub->GetOnReachEnd();
3090     onReachEnd ? DumpLog::GetInstance().AddDesc("hasOnReachEnd: true")
3091                : DumpLog::GetInstance().AddDesc("hasOnReachEnd: false");
3092 }
3093 
DumpAdvanceInfo()3094 void ScrollablePattern::DumpAdvanceInfo()
3095 {
3096     GetEdgeEffectDumpInfo();
3097     edgeEffectAlwaysEnabled_ ? DumpLog::GetInstance().AddDesc("edgeEffectAlwaysEnabled: true")
3098                              : DumpLog::GetInstance().AddDesc("edgeEffectAlwaysEnabled: false");
3099     IsScrollable() ? DumpLog::GetInstance().AddDesc("isScrollable: true")
3100                    : DumpLog::GetInstance().AddDesc("isScrollable: false");
3101     GetEventDumpInfo();
3102     DumpLog::GetInstance().AddDesc(GetNestedScroll().ToString().c_str());
3103     GetIsSearchRefresh() ? DumpLog::GetInstance().AddDesc(std::string("isSearchRefresh: true"))
3104                          : DumpLog::GetInstance().AddDesc(std::string("isSearchRefresh: false"));
3105     GetIsFixedNestedScrollMode() ? DumpLog::GetInstance().AddDesc(std::string("isFixedNestedScrollMode: true"))
3106                                  : DumpLog::GetInstance().AddDesc(std::string("isFixedNestedScrollMode: false"));
3107     auto parent = GetNestedScrollParent();
3108     parent && parent->GetHost() ? DumpLog::GetInstance().AddDesc(std::string("nestedScrollParent id: ")
3109                                                                      .append(std::to_string(parent->GetHost()->GetId()))
3110                                                                      .append(" tag: ")
3111                                                                      .append(parent->GetHost()->GetTag()))
3112                                 : DumpLog::GetInstance().AddDesc("nestedScrollParent is null");
3113     GetAxisDumpInfo();
3114     GetPanDirectionDumpInfo();
3115     GetPaintPropertyDumpInfo();
3116     GetScrollEnabled() ? DumpLog::GetInstance().AddDesc("enableScrollInteraction: true")
3117                        : DumpLog::GetInstance().AddDesc("enableScrollInteraction: false");
3118     DumpLog::GetInstance().AddDesc(std::string("friction: ").append(std::to_string(friction_)));
3119     DumpLog::GetInstance().AddDesc(std::string("flingSpeedLimit: ").append(std::to_string(GetMaxFlingVelocity())));
3120     DumpLog::GetInstance().AddDesc("==========================eventsFiredInfos==============================");
3121     for (const auto& info : eventsFiredInfos_) {
3122         DumpLog::GetInstance().AddDesc(info.ToString());
3123     }
3124     DumpLog::GetInstance().AddDesc("==========================eventsFiredInfos==============================");
3125     DumpLog::GetInstance().AddDesc("==========================scrollableFrameInfos==========================");
3126     for (const auto& info : scrollableFrameInfos_) {
3127         DumpLog::GetInstance().AddDesc(info.ToString());
3128     }
3129     DumpLog::GetInstance().AddDesc("==========================scrollableFrameInfos==========================");
3130     DumpLog::GetInstance().AddDesc("==========================inner ScrollBar===============================");
3131     if (scrollBar_) {
3132         scrollBar_->DumpAdvanceInfo();
3133     } else {
3134         DumpLog::GetInstance().AddDesc("inner ScrollBar is null");
3135     }
3136     DumpLog::GetInstance().AddDesc("==========================inner ScrollBar===============================");
3137 }
3138 
SetAccessibilityAction()3139 void ScrollablePattern::SetAccessibilityAction()
3140 {
3141     auto host = GetHost();
3142     CHECK_NULL_VOID(host);
3143     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3144     CHECK_NULL_VOID(accessibilityProperty);
3145     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
3146         const auto& pattern = weakPtr.Upgrade();
3147         CHECK_NULL_VOID(pattern);
3148         auto host = pattern->GetHost();
3149         CHECK_NULL_VOID(host);
3150         ACE_SCOPED_TRACE("accessibility action, scroll forward, isScrollable:%u, scrollType:%d, id:%d, tag:%s",
3151             pattern->IsScrollable(), scrollType, static_cast<int32_t>(host->GetAccessibilityId()),
3152             host->GetTag().c_str());
3153         CHECK_NULL_VOID(pattern->IsScrollable());
3154         pattern->ScrollPage(false, true, scrollType);
3155     });
3156 
3157     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
3158         const auto& pattern = weakPtr.Upgrade();
3159         CHECK_NULL_VOID(pattern);
3160         auto host = pattern->GetHost();
3161         CHECK_NULL_VOID(host);
3162         ACE_SCOPED_TRACE("accessibility action, scroll backward, isScrollable:%u, scrollType:%d, id:%d, tag:%s",
3163             pattern->IsScrollable(), scrollType, static_cast<int32_t>(host->GetAccessibilityId()),
3164             host->GetTag().c_str());
3165         CHECK_NULL_VOID(pattern->IsScrollable());
3166         pattern->ScrollPage(true, true, scrollType);
3167     });
3168 }
3169 
ScrollAtFixedVelocity(float velocity)3170 void ScrollablePattern::ScrollAtFixedVelocity(float velocity)
3171 {
3172     auto host = GetHost();
3173     CHECK_NULL_VOID(host);
3174     if (AnimateRunning()) {
3175         StopAnimate();
3176     }
3177 
3178     if (!animator_) {
3179         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
3180         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
3181             auto pattern = weak.Upgrade();
3182             CHECK_NULL_VOID(pattern);
3183             pattern->OnAnimateStop();
3184             auto host = pattern->GetHost();
3185             CHECK_NULL_VOID(host);
3186             AceAsyncTraceEnd(
3187                 host->GetId(), (SCROLLER_FIX_VELOCITY_ANIMATION + std::to_string(host->GetAccessibilityId()) +
3188                                    std::string(" ") + host->GetTag()).c_str());
3189         });
3190     }
3191 
3192     if (!fixedVelocityMotion_) {
3193         fixedVelocityMotion_ = AceType::MakeRefPtr<VelocityMotion>([weak = WeakClaim(this)](float offset) -> bool {
3194             auto pattern = weak.Upgrade();
3195             CHECK_NULL_RETURN(pattern, true);
3196             if (LessNotEqual(offset, 0) && pattern->IsAtBottom()) {
3197                 // Stop scrolling when reach the bottom
3198                 pattern->fixedVelocityMotion_->Init();
3199                 return true;
3200             } else if (GreatNotEqual(offset, 0) && pattern->IsAtTop()) {
3201                 // Stop scrolling when reach the top
3202                 pattern->fixedVelocityMotion_->Init();
3203                 return true;
3204             }
3205             return false;
3206         });
3207         fixedVelocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
3208             auto pattern = weakScroll.Upgrade();
3209             CHECK_NULL_VOID(pattern);
3210             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_ANIMATION_CONTROLLER);
3211         });
3212         fixedVelocityMotion_->SetVelocity(velocity);
3213     } else {
3214         fixedVelocityMotion_->Init();
3215         fixedVelocityMotion_->SetVelocity(velocity);
3216     }
3217     AceAsyncTraceBegin(host->GetId(), (SCROLLER_FIX_VELOCITY_ANIMATION + std::to_string(host->GetAccessibilityId()) +
3218         std::string(" ") + host->GetTag()).c_str());
3219     animator_->PlayMotion(fixedVelocityMotion_);
3220     FireOnScrollStart();
3221 }
3222 
GetPositionMode()3223 PositionMode ScrollablePattern::GetPositionMode()
3224 {
3225     auto host = GetHost();
3226     CHECK_NULL_RETURN(host, PositionMode::RIGHT);
3227     auto positionMode = PositionMode::RIGHT;
3228     if (axis_ == Axis::HORIZONTAL) {
3229         positionMode = PositionMode::BOTTOM;
3230     } else {
3231         auto isRtl = host->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
3232         if (isRtl) {
3233             positionMode = PositionMode::LEFT;
3234         }
3235     }
3236     return positionMode;
3237 }
3238 
CheckScrollBarOff()3239 void ScrollablePattern::CheckScrollBarOff()
3240 {
3241     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3242     CHECK_NULL_VOID(paintProperty);
3243     auto displayMode = paintProperty->GetScrollBarMode().value_or(GetDefaultScrollBarDisplayMode());
3244     if (displayMode == DisplayMode::OFF) {
3245         SetScrollBar(DisplayMode::OFF);
3246     }
3247 }
3248 
UpdateNestedScrollVelocity(float offset,NestedState state)3249 void ScrollablePattern::UpdateNestedScrollVelocity(float offset, NestedState state)
3250 {
3251     if (state == NestedState::GESTURE) {
3252         return;
3253     }
3254     auto pipeline = GetContext();
3255     CHECK_NULL_VOID(pipeline);
3256     uint64_t currentVsync = pipeline->GetVsyncTime();
3257     uint64_t diff = currentVsync - nestedScrollTimestamp_;
3258     if (diff >= MAX_VSYNC_DIFF_TIME || diff <= MIN_DIFF_VSYNC) {
3259         diff = DEFAULT_VSYNC_DIFF_TIME;
3260     }
3261     nestedScrollVelocity_ = (offset / diff) * MILLOS_PER_NANO_SECONDS;
3262     nestedScrollTimestamp_ = currentVsync;
3263 }
3264 
GetNestedScrollVelocity()3265 float ScrollablePattern::GetNestedScrollVelocity()
3266 {
3267     if (NearZero(nestedScrollVelocity_)) {
3268         return 0.0f;
3269     }
3270     uint64_t currentVsync = static_cast<uint64_t>(GetSysTimestamp());
3271     uint64_t diff = currentVsync > nestedScrollTimestamp_ ? currentVsync - nestedScrollTimestamp_ : 0;
3272     if (diff >= MAX_VSYNC_DIFF_TIME) {
3273         nestedScrollVelocity_ = 0.0f;
3274     }
3275     return nestedScrollVelocity_;
3276 }
3277 } // namespace OHOS::Ace::NG
3278