• 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/perfmonitor/perf_constants.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/ressched/ressched_report.h"
23 #include "base/utils/utils.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/pattern/scrollable/scrollable.h"
26 #include "core/components_ng/manager/select_overlay/select_overlay_scroll_notifier.h"
27 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
28 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
29 #include "core/components_ng/pattern/scrollable/nestable_scroll_container.h"
30 #include "core/components_ng/pattern/scrollable/scrollable_event_hub.h"
31 #include "core/pipeline/pipeline_base.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 
34 namespace OHOS::Ace::NG {
35 namespace {
36 constexpr Color SELECT_FILL_COLOR = Color(0x1A000000);
37 constexpr Color SELECT_STROKE_COLOR = Color(0x33FFFFFF);
38 constexpr float CUSTOM_ANIMATION_DURATION = 1000.0;
39 constexpr uint32_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
40 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
41 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; //max 100ms
42 constexpr float SPRING_ACCURACY = 0.1;
43 const std::string SCROLLABLE_DRAG_SCENE = "scrollable_drag_scene";
44 const std::string SCROLL_BAR_DRAG_SCENE = "scrollBar_drag_scene";
45 const std::string SCROLLABLE_MOTION_SCENE = "scrollable_motion_scene";
46 const std::string SCROLLABLE_MULTI_TASK_SCENE = "scrollable_multi_task_scene";
47 const std::string SCROLL_IN_HOTZONE_SCENE = "scroll_in_hotzone_scene";
48 const std::string CUSTOM_SCROLL_BAR_SCENE = "custom_scroll_bar_scene";
49 } // namespace
50 using std::chrono::high_resolution_clock;
51 using std::chrono::milliseconds;
52 
ScrollablePattern()53 ScrollablePattern::ScrollablePattern()
54 {
55     friction_ = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
56 }
57 
ScrollablePattern(EdgeEffect edgeEffect,bool alwaysEnabled)58 ScrollablePattern::ScrollablePattern(EdgeEffect edgeEffect, bool alwaysEnabled)
59     : edgeEffect_(edgeEffect), edgeEffectAlwaysEnabled_(alwaysEnabled)
60 {
61     friction_ = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
62 }
63 
CreatePaintProperty()64 RefPtr<PaintProperty> ScrollablePattern::CreatePaintProperty()
65 {
66     auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
67     auto property = MakeRefPtr<ScrollablePaintProperty>();
68     property->UpdateScrollBarMode(defaultDisplayMode);
69     return property;
70 }
71 
ToJsonValue(std::unique_ptr<JsonValue> & json) const72 void ScrollablePattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
73 {
74     json->Put("friction", GetFriction());
75     if (edgeEffect_ == EdgeEffect::SPRING) {
76         json->Put("edgeEffect", "EdgeEffect.Spring");
77     } else if (edgeEffect_ == EdgeEffect::FADE) {
78         json->Put("edgeEffect", "EdgeEffect.Fade");
79     } else {
80         json->Put("edgeEffect", "EdgeEffect.None");
81     }
82     json->Put("flingSpeedLimit", Dimension(maxFlingVelocity_, DimensionUnit::PX).ToString().c_str());
83     auto JsonEdgeEffectOptions = JsonUtil::Create(true);
84     JsonEdgeEffectOptions->Put("alwaysEnabled", GetAlwaysEnabled());
85     json->Put("edgeEffectOptions", JsonEdgeEffectOptions);
86 }
87 
SetAxis(Axis axis)88 void ScrollablePattern::SetAxis(Axis axis)
89 {
90     if (axis_ == axis) {
91         return;
92     }
93     axis_ = axis;
94     if (scrollBar_) {
95         scrollBar_->SetPositionMode(axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
96     }
97     if (scrollBarOverlayModifier_) {
98         scrollBarOverlayModifier_->SetPositionMode(
99             axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
100     }
101     auto gestureHub = GetGestureHub();
102     CHECK_NULL_VOID(gestureHub);
103     if (scrollableEvent_) {
104         gestureHub->RemoveScrollableEvent(scrollableEvent_);
105         scrollableEvent_->SetAxis(axis);
106         gestureHub->AddScrollableEvent(scrollableEvent_);
107     }
108     if (scrollEffect_) {
109         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
110         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
111     }
112 }
113 
GetGestureHub()114 RefPtr<GestureEventHub> ScrollablePattern::GetGestureHub()
115 {
116     auto host = GetHost();
117     CHECK_NULL_RETURN(host, nullptr);
118     auto hub = host->GetEventHub<EventHub>();
119     CHECK_NULL_RETURN(hub, nullptr);
120     return hub->GetOrCreateGestureEventHub();
121 }
122 
GetInputHub()123 RefPtr<InputEventHub> ScrollablePattern::GetInputHub()
124 {
125     auto host = GetHost();
126     CHECK_NULL_RETURN(host, nullptr);
127     auto hub = host->GetEventHub<EventHub>();
128     CHECK_NULL_RETURN(host, nullptr);
129     return hub->GetOrCreateInputEventHub();
130 }
131 
OnScrollCallback(float offset,int32_t source)132 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
133 {
134     if (source == SCROLL_FROM_START) {
135         FireOnScrollStart();
136         return true;
137     }
138     return UpdateCurrentOffset(offset, source);
139 }
140 
ProcessNavBarReactOnStart()141 void ScrollablePattern::ProcessNavBarReactOnStart()
142 {
143     CHECK_NULL_VOID(navBarPattern_);
144     navBarPattern_->OnCoordScrollStart();
145 }
146 
ProcessNavBarReactOnUpdate(float offset)147 float ScrollablePattern::ProcessNavBarReactOnUpdate(float offset)
148 {
149     CHECK_NULL_RETURN(navBarPattern_, false);
150     return navBarPattern_->OnCoordScrollUpdate(offset);
151 }
152 
ProcessNavBarReactOnEnd()153 void ScrollablePattern::ProcessNavBarReactOnEnd()
154 {
155     CHECK_NULL_VOID(navBarPattern_);
156     navBarPattern_->OnCoordScrollEnd();
157 }
158 
OnScrollPosition(double & offset,int32_t source)159 bool ScrollablePattern::OnScrollPosition(double& offset, int32_t source)
160 {
161     if (needLinked_) {
162         auto isAtTop = (IsAtTop() && Positive(offset));
163         auto refreshCoordinateMode = CoordinateWithRefresh(offset, source, isAtTop);
164         auto navigationInCoordination = CoordinateWithNavigation(offset, source, isAtTop);
165         auto modalSheetCoordinationMode = CoordinateWithSheet(offset, source, isAtTop);
166         if ((refreshCoordinateMode == RefreshCoordinationMode::REFRESH_SCROLL) || navigationInCoordination ||
167             (modalSheetCoordinationMode == ModalSheetCoordinationMode::SHEET_SCROLL)) {
168             return false;
169         }
170     }
171 
172     if (source == SCROLL_FROM_START) {
173         SetParentScrollable();
174         StopScrollBarAnimatorByProxy();
175         AbortScrollAnimator();
176     } else if (!AnimateStoped()) {
177         return false;
178     }
179     return true;
180 }
181 
NeedSplitScroll(OverScrollOffset & overOffsets,int32_t source)182 bool ScrollablePattern::NeedSplitScroll(OverScrollOffset& overOffsets, int32_t source)
183 {
184     return GreatNotEqual(overOffsets.start, 0.0) && refreshCoordination_ && refreshCoordination_->InCoordination() &&
185            !isRefreshInReactive_ &&
186            (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_ANIMATION_SPRING ||
187                source == SCROLL_FROM_ANIMATION) &&
188            (axis_ == Axis::VERTICAL);
189 }
190 
CoordinateWithRefresh(double & offset,int32_t source,bool isAtTop)191 RefreshCoordinationMode ScrollablePattern::CoordinateWithRefresh(double& offset, int32_t source, bool isAtTop)
192 {
193     auto coordinationMode = RefreshCoordinationMode::UNKNOWN;
194     if (!refreshCoordination_) {
195         CreateRefreshCoordination();
196     }
197     auto overOffsets = GetOverScrollOffset(offset);
198     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !IsAtTop() && Positive(offset) &&
199         NeedSplitScroll(overOffsets, source)) {
200         offset = offset - overOffsets.start;
201         OnScrollCallback(offset, source);
202         isRefreshInReactive_ = true;
203         if (refreshCoordination_) {
204             refreshCoordination_->OnScrollStart(
205                 source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS, GetVelocity());
206         }
207     }
208     if (IsAtTop() &&
209         (Positive(offset) || (Negative(offset) && refreshCoordination_ && refreshCoordination_->IsRefreshInScroll())) &&
210         (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_ANIMATION) &&
211         !isRefreshInReactive_ && (axis_ == Axis::VERTICAL)) {
212         isRefreshInReactive_ = true;
213         if (refreshCoordination_) {
214             refreshCoordination_->OnScrollStart(
215                 source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS, GetVelocity());
216         }
217     }
218     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
219         (refreshCoordination_ && refreshCoordination_->InCoordination()) && source != SCROLL_FROM_UPDATE &&
220         source != SCROLL_FROM_AXIS && isRefreshInReactive_) {
221         isRefreshInReactive_ = false;
222         refreshCoordination_->OnScrollEnd(GetVelocity());
223     }
224     if (refreshCoordination_ && refreshCoordination_->InCoordination() && isRefreshInReactive_) {
225         if (!refreshCoordination_->OnScroll(
226                 GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset, GetVelocity())) {
227             isRefreshInReactive_ = false;
228             coordinationMode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
229         }
230         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
231             coordinationMode = RefreshCoordinationMode::REFRESH_SCROLL;
232         } else {
233             if (scrollEffect_ && scrollEffect_->IsSpringEffect()) {
234                 coordinationMode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
235             } else {
236                 coordinationMode = RefreshCoordinationMode::REFRESH_SCROLL;
237             }
238         }
239     }
240     return coordinationMode;
241 }
242 
CoordinateWithSheet(double & offset,int32_t source,bool isAtTop)243 ModalSheetCoordinationMode ScrollablePattern::CoordinateWithSheet(double& offset, int32_t source, bool isAtTop)
244 {
245     auto coordinationMode = ModalSheetCoordinationMode::UNKNOWN;
246     if (source == SCROLL_FROM_START) {
247         isSheetInReactive_ = false;
248 
249         if (!sheetPattern_) {
250             GetParentModalSheet();
251         }
252     }
253     auto overOffsets = GetOverScrollOffset(offset);
254     if (IsAtTop() && (source == SCROLL_FROM_UPDATE) && !isSheetInReactive_ && (axis_ == Axis::VERTICAL)) {
255         isSheetInReactive_ = true;
256         if (sheetPattern_) {
257             sheetPattern_->OnCoordScrollStart();
258         }
259     }
260     if (sheetPattern_ && isSheetInReactive_) {
261         if (!sheetPattern_->OnCoordScrollUpdate(GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset)) {
262             isSheetInReactive_ = false;
263             coordinationMode = ModalSheetCoordinationMode::SCROLLABLE_SCROLL;
264         } else {
265             coordinationMode = ModalSheetCoordinationMode::SHEET_SCROLL;
266         }
267     }
268     return coordinationMode;
269 }
270 
CoordinateWithNavigation(double & offset,int32_t source,bool isAtTop)271 bool ScrollablePattern::CoordinateWithNavigation(double& offset, int32_t source, bool isAtTop)
272 {
273     if (source == SCROLL_FROM_START) {
274         GetParentNavigation();
275         CHECK_NULL_RETURN(navBarPattern_, false);
276         if (isAtTop) {
277             // Starting coordinating scroll at the beginning of scrolling.
278             isReactInParentMovement_ = true;
279             ProcessNavBarReactOnStart();
280         }
281         return false;
282     }
283 
284     CHECK_NULL_RETURN(navBarPattern_ && navBarPattern_->NeedCoordWithScroll(), false);
285 
286     auto overOffsets = GetOverScrollOffset(offset);
287     float offsetRemain = 0.0f;
288     float offsetCoordinate = offset;
289 
290     if (!isReactInParentMovement_ && NeedCoordinateScrollWithNavigation(offset, source, overOffsets)) {
291         // Starting coordinating scroll during sliding or flipping.
292         isReactInParentMovement_ = true;
293         ProcessNavBarReactOnStart();
294         offsetRemain = offset - overOffsets.start;
295         offsetCoordinate = overOffsets.start;
296     }
297 
298     if (isReactInParentMovement_) {
299         float handledByNav = ProcessNavBarReactOnUpdate(offsetCoordinate);
300         if (NearEqual(handledByNav, offsetCoordinate)) {
301             // All offsets are handled by Navigation, list cannot scroll over.
302             SetCanOverScroll(false);
303             offset = offsetRemain;
304         } else {
305             // Not all offsets are handled by Navigation, list still needs to scroll.
306             if (Positive(offset)) {
307                 // When scrolling down, allow list to scroll over.
308                 SetCanOverScroll(true);
309             }
310             offset = offsetRemain + (offsetCoordinate - handledByNav);
311         }
312 
313         if (Negative(offset) && source == SCROLL_FROM_ANIMATION_SPRING) {
314             // When rebounding form scrolling over, trigger the ProcessNavBarReactOnEnd callback.
315             isReactInParentMovement_ = false;
316             ProcessNavBarReactOnEnd();
317         }
318     }
319 
320     return false;
321 }
322 
OnScrollEnd()323 void ScrollablePattern::OnScrollEnd()
324 {
325     // Previous: Sets ScrollablePattern::OnScrollEnd to Scrollable->scrollEndCallback_
326     // Scrollable calls scrollEndCallback_ in HandleOverScroll
327 
328     // Now: HandleOverScroll moved to ScrollablePattern and renamed HandleScrollVelocity, directly
329     // calls OnScrollEnd in ScrollablePattern
330     if (refreshCoordination_) {
331         isRefreshInReactive_ = false;
332         refreshCoordination_->OnScrollEnd(GetVelocity());
333     }
334     if (isSheetInReactive_) {
335         isSheetInReactive_ = false;
336         if (sheetPattern_) {
337             sheetPattern_->OnCoordScrollEnd(GetVelocity());
338         }
339     }
340     if (isReactInParentMovement_) {
341         isReactInParentMovement_ = false;
342         ProcessNavBarReactOnEnd();
343     }
344 
345     OnScrollEndCallback();
346     SelectOverlayScrollNotifier::NotifyOnScrollEnd(WeakClaim(this));
347 }
348 
AttachAnimatableProperty(RefPtr<Scrollable> scrollable)349 void ScrollablePattern::AttachAnimatableProperty(RefPtr<Scrollable> scrollable)
350 {
351     auto host = GetHost();
352     CHECK_NULL_VOID(host);
353     auto renderContext = host->GetRenderContext();
354     CHECK_NULL_VOID(renderContext);
355     auto property = scrollable->GetFrictionProperty();
356     renderContext->AttachNodeAnimatableProperty(property);
357 
358     property = scrollable->GetSpringProperty();
359     renderContext->AttachNodeAnimatableProperty(property);
360     property = scrollable->GetSnapProperty();
361     renderContext->AttachNodeAnimatableProperty(property);
362 }
363 
AddScrollEvent()364 void ScrollablePattern::AddScrollEvent()
365 {
366     auto host = GetHost();
367     CHECK_NULL_VOID(host);
368     auto gestureHub = GetGestureHub();
369     CHECK_NULL_VOID(gestureHub);
370     if (scrollableEvent_) {
371         gestureHub->RemoveScrollableEvent(scrollableEvent_);
372     }
373     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
374         auto pattern = weak.Upgrade();
375         CHECK_NULL_RETURN(pattern, false);
376         return pattern->HandleScrollImpl(static_cast<float>(offset), source);
377     };
378     auto scrollable = MakeRefPtr<Scrollable>(std::move(scrollCallback), GetAxis());
379     scrollable->SetNodeId(host->GetAccessibilityId());
380     scrollable->Initialize(host->GetContext());
381     AttachAnimatableProperty(scrollable);
382 
383     // move HandleScroll and HandleOverScroll to ScrollablePattern by setting callbacks to scrollable
384     auto handleScroll = [weak = AceType::WeakClaim(this)](
385                             float offset, int32_t source, NestedState state) -> ScrollResult {
386         auto pattern = weak.Upgrade();
387         if (pattern) {
388             return pattern->HandleScroll(offset, source, state);
389         }
390         return {};
391     };
392     scrollable->SetHandleScrollCallback(std::move(handleScroll));
393 
394     scrollable->SetOverScrollCallback([weak = WeakClaim(this)](float velocity) {
395         auto pattern = weak.Upgrade();
396         CHECK_NULL_RETURN(pattern, false);
397         return pattern->HandleOverScroll(!pattern->IsReverse() ? velocity : -velocity);
398     });
399 
400     auto scrollStart = [weak = WeakClaim(this)](float position) {
401         auto pattern = weak.Upgrade();
402         CHECK_NULL_VOID(pattern);
403         pattern->FireAndCleanScrollingListener();
404         pattern->OnScrollStartRecursive(position);
405     };
406     scrollable->SetOnScrollStartRec(std::move(scrollStart));
407 
408     auto scrollEndRec = [weak = WeakClaim(this)](const std::optional<float>& velocity) {
409         auto pattern = weak.Upgrade();
410         CHECK_NULL_VOID(pattern);
411         pattern->OnScrollEndRecursive(velocity);
412     };
413     scrollable->SetOnScrollEndRec(std::move(scrollEndRec));
414 
415     auto scrollEnd = [weak = WeakClaim(this)]() {
416         auto pattern = weak.Upgrade();
417         CHECK_NULL_VOID(pattern);
418         pattern->OnScrollEnd();
419     };
420     scrollable->SetScrollEndCallback(std::move(scrollEnd));
421     scrollable->SetUnstaticFriction(friction_);
422     scrollable->SetMaxFlingVelocity(maxFlingVelocity_);
423 
424     auto scrollSnap = [weak = WeakClaim(this)](double targetOffset, double velocity) -> bool {
425         auto pattern = weak.Upgrade();
426         CHECK_NULL_RETURN(pattern, false);
427         return pattern->OnScrollSnapCallback(targetOffset, velocity);
428     };
429     scrollable->SetOnScrollSnapCallback(scrollSnap);
430 
431     auto calePredictSnapOffsetCallback =
432             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
433         auto pattern = weak.Upgrade();
434         std::optional<float> predictSnapOffset;
435         CHECK_NULL_RETURN(pattern, predictSnapOffset);
436         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
437     };
438     scrollable->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
439 
440     auto needScrollSnapToSideCallback = [weak = WeakClaim(this)](float delta) -> bool {
441         auto pattern = weak.Upgrade();
442         CHECK_NULL_RETURN(pattern, false);
443         return pattern->NeedScrollSnapToSide(delta);
444     };
445     scrollable->SetNeedScrollSnapToSideCallback(std::move(needScrollSnapToSideCallback));
446 
447     auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
448         auto pattern = weak.Upgrade();
449         CHECK_NULL_VOID(pattern);
450         return pattern->NotifyFRCSceneInfo(SCROLLABLE_DRAG_SCENE, velocity, sceneStatus);
451     };
452     scrollable->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
453 
454     scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
455     scrollableEvent_->SetScrollable(scrollable);
456     gestureHub->AddScrollableEvent(scrollableEvent_);
457     InitTouchEvent(gestureHub);
458     RegisterWindowStateChangedCallback();
459 }
460 
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)461 void ScrollablePattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
462 {
463     // use TouchEvent to receive next touch down event to stop animation.
464     if (touchEvent_) {
465         return;
466     }
467     auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
468         auto pattern = weak.Upgrade();
469         CHECK_NULL_VOID(pattern && pattern->scrollableEvent_);
470         auto scrollable = pattern->scrollableEvent_->GetScrollable();
471         CHECK_NULL_VOID(scrollable);
472         switch (info.GetTouches().front().GetTouchType()) {
473             case TouchType::DOWN:
474                 scrollable->HandleTouchDown();
475                 break;
476             case TouchType::UP:
477                 scrollable->HandleTouchUp();
478                 break;
479             case TouchType::CANCEL:
480                 scrollable->HandleTouchCancel();
481                 break;
482             default:
483                 break;
484         }
485     };
486     if (touchEvent_) {
487         gestureHub->RemoveTouchEvent(touchEvent_);
488     }
489     touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
490     gestureHub->AddTouchEvent(touchEvent_);
491 }
492 
RegisterWindowStateChangedCallback()493 void ScrollablePattern::RegisterWindowStateChangedCallback()
494 {
495     auto host = GetHost();
496     CHECK_NULL_VOID(host);
497     auto context = NG::PipelineContext::GetCurrentContext();
498     CHECK_NULL_VOID(context);
499     context->AddWindowStateChangedCallback(host->GetId());
500 }
501 
OnDetachFromFrameNode(FrameNode * frameNode)502 void ScrollablePattern::OnDetachFromFrameNode(FrameNode* frameNode)
503 {
504     auto context = NG::PipelineContext::GetCurrentContext();
505     CHECK_NULL_VOID(context);
506     context->RemoveWindowStateChangedCallback(frameNode->GetId());
507 }
508 
OnWindowHide()509 void ScrollablePattern::OnWindowHide()
510 {
511     CHECK_NULL_VOID(scrollableEvent_);
512     auto scrollable = scrollableEvent_->GetScrollable();
513     CHECK_NULL_VOID(scrollable);
514     scrollable->StopFrictionAnimation();
515 }
516 
SetEdgeEffect(EdgeEffect edgeEffect)517 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
518 {
519     auto gestureHub = GetGestureHub();
520     CHECK_NULL_VOID(gestureHub);
521     if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
522         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
523         scrollEffect_.Reset();
524     }
525     if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
526         auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
527         CHECK_NULL_VOID(springEffect);
528         springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
529             auto pattern = weak.Upgrade();
530             CHECK_NULL_RETURN(pattern, false);
531             return pattern->OutBoundaryCallback();
532         });
533         // add callback to springEdgeEffect
534         SetEdgeEffectCallback(springEffect);
535         scrollEffect_ = springEffect;
536         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
537     }
538     if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
539         auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
540         CHECK_NULL_VOID(fadeEdgeEffect);
541         fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
542             auto list = weakScroll.Upgrade();
543             CHECK_NULL_VOID(list);
544             auto host = list->GetHost();
545             CHECK_NULL_VOID(host);
546             host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
547         });
548         SetEdgeEffectCallback(fadeEdgeEffect);
549         fadeEdgeEffect->InitialEdgeEffect();
550         scrollEffect_ = fadeEdgeEffect;
551         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
552     }
553     auto scrollable = scrollableEvent_->GetScrollable();
554     CHECK_NULL_VOID(scrollable);
555     scrollable->SetEdgeEffect(edgeEffect);
556 }
557 
HandleEdgeEffect(float offset,int32_t source,const SizeF & size,bool reverse)558 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size, bool reverse)
559 {
560     bool isAtTop = IsAtTop();
561     bool isAtBottom = IsAtBottom();
562     bool isNotPositiveScrollableDistance = isAtTop && isAtBottom;
563     // check edgeEffect is not springEffect
564     if (scrollEffect_ && scrollEffect_->IsFadeEffect() &&
565         (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION)) { // handle edge effect
566         if ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset))) {
567             auto isScrollFromUpdate = source == SCROLL_FROM_UPDATE;
568             scrollEffect_->HandleOverScroll(GetAxis(), !reverse ? -offset : offset,
569                 size, isScrollFromUpdate, isNotPositiveScrollableDistance);
570         }
571     }
572     if (!(scrollEffect_ && scrollEffect_->IsSpringEffect() &&
573             (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION ||
574                 source == SCROLL_FROM_ANIMATION_SPRING ||
575                 (source == SCROLL_FROM_ANIMATION_CONTROLLER && animateCanOverScroll_)))) {
576         if (isAtTop && Positive(offset)) {
577             animateOverScroll_ = false;
578             return false;
579         }
580         if (isAtBottom && Negative(offset)) {
581             animateOverScroll_ = false;
582             return false;
583         }
584     }
585     animateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && (isAtTop || isAtBottom);
586     isAnimateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && animateCanOverScroll_ &&
587                             ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset)));
588     return true;
589 }
590 
RegisterScrollBarEventTask()591 void ScrollablePattern::RegisterScrollBarEventTask()
592 {
593     CHECK_NULL_VOID(scrollBar_);
594     auto host = GetHost();
595     CHECK_NULL_VOID(host);
596     auto gestureHub = GetGestureHub();
597     auto inputHub = GetInputHub();
598     CHECK_NULL_VOID(gestureHub);
599     CHECK_NULL_VOID(inputHub);
600     scrollBar_->SetGestureEvent();
601     scrollBar_->SetMouseEvent();
602     scrollBar_->SetHoverEvent();
603     scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
604         auto host = weak.Upgrade();
605         CHECK_NULL_VOID(host);
606         host->MarkNeedRenderOnly();
607     });
608     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
609         auto pattern = weak.Upgrade();
610         CHECK_NULL_RETURN(pattern, false);
611         return pattern->OnScrollCallback(static_cast<float>(offset), source);
612     };
613     scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
614     auto scrollEnd = [weak = WeakClaim(this)]() {
615         auto pattern = weak.Upgrade();
616         CHECK_NULL_VOID(pattern);
617         pattern->OnScrollEnd();
618     };
619     scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
620     auto calePredictSnapOffsetCallback =
621             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
622         auto pattern = weak.Upgrade();
623         CHECK_NULL_RETURN(pattern, std::optional<float>());
624         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
625     };
626     scrollBar_->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
627     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
628         auto pattern = weak.Upgrade();
629         CHECK_NULL_VOID(pattern);
630         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
631     };
632     scrollBar_->SetStartScrollSnapMotionCallback(std::move(startScrollSnapMotionCallback));
633 
634     gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
635     inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
636     inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
637     CHECK_NULL_VOID(scrollableEvent_);
638     scrollableEvent_->SetInBarRegionCallback(
639         [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
640             auto scrollBar = weak.Upgrade();
641             CHECK_NULL_RETURN(scrollBar, false);
642             if (source == SourceType::MOUSE) {
643                 return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
644             }
645             return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
646         });
647     scrollableEvent_->SetBarCollectTouchTargetCallback(
648         [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const OffsetF& coordinateOffset,
649             const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
650             const RefPtr<TargetComponent>& targetComponent) {
651             auto scrollBar = weak.Upgrade();
652             CHECK_NULL_VOID(scrollBar);
653             scrollBar->OnCollectTouchTarget(coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent);
654         });
655 
656     auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
657         auto pattern = weak.Upgrade();
658         CHECK_NULL_VOID(pattern);
659         return pattern->NotifyFRCSceneInfo(SCROLL_BAR_DRAG_SCENE, velocity, sceneStatus);
660     };
661     scrollBar_->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
662 }
663 
SetScrollBar(DisplayMode displayMode)664 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
665 {
666     auto host = GetHost();
667     CHECK_NULL_VOID(host);
668     if (displayMode == DisplayMode::OFF) {
669         if (scrollBar_) {
670             auto gestureHub = GetGestureHub();
671             if (gestureHub) {
672                 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
673             }
674             scrollBar_.Reset();
675             if (scrollBarOverlayModifier_) {
676                 scrollBarOverlayModifier_->SetOpacity(0);
677             }
678         }
679         return;
680     }
681     DisplayMode oldDisplayMode = DisplayMode::OFF;
682     if (!scrollBar_) {
683         scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
684         // set the scroll bar style
685         if (GetAxis() == Axis::HORIZONTAL) {
686             scrollBar_->SetPositionMode(PositionMode::BOTTOM);
687             if (scrollBarOverlayModifier_) {
688                 scrollBarOverlayModifier_->SetPositionMode(PositionMode::BOTTOM);
689             }
690         }
691         RegisterScrollBarEventTask();
692         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
693     } else {
694         oldDisplayMode = scrollBar_->GetDisplayMode();
695     }
696 
697     if (oldDisplayMode != displayMode) {
698         scrollBar_->SetDisplayMode(displayMode);
699         if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
700             scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
701         }
702         scrollBar_->ScheduleDisappearDelayTask();
703     }
704     UpdateBorderRadius();
705 }
706 
UpdateBorderRadius()707 void ScrollablePattern::UpdateBorderRadius()
708 {
709     auto host = GetHost();
710     CHECK_NULL_VOID(host);
711     auto renderContext = host->GetRenderContext();
712     CHECK_NULL_VOID(renderContext);
713     if (renderContext->HasBorderRadius()) {
714         auto borderRadius = renderContext->GetBorderRadius().value();
715         if (!(borderRadius == scrollBar_->GetHostBorderRadius())) {
716             scrollBar_->SetHostBorderRadius(borderRadius);
717             scrollBar_->CalcReservedHeight();
718         }
719     }
720 }
721 
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)722 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
723 {
724     if (!property) {
725         SetScrollBar(DisplayMode::AUTO);
726         return;
727     }
728     auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
729     SetScrollBar(displayMode);
730     if (scrollBar_) {
731         auto barWidth = property->GetScrollBarWidth();
732         if (barWidth) {
733             scrollBar_->SetInactiveWidth(barWidth.value());
734             scrollBar_->SetNormalWidth(barWidth.value());
735             scrollBar_->SetActiveWidth(barWidth.value());
736             scrollBar_->SetTouchWidth(barWidth.value());
737             scrollBar_->SetIsUserNormalWidth(true);
738         } else {
739             scrollBar_->SetIsUserNormalWidth(false);
740         }
741         auto barColor = property->GetScrollBarColor();
742         if (barColor) {
743             scrollBar_->SetForegroundColor(barColor.value());
744         } else {
745             auto pipelineContext = PipelineContext::GetCurrentContext();
746             CHECK_NULL_VOID(pipelineContext);
747             auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
748             CHECK_NULL_VOID(theme);
749             scrollBar_->SetForegroundColor(theme->GetForegroundColor());
750             scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
751         }
752     }
753 }
754 
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)755 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
756 {
757     // inner scrollbar, viewOffset is padding offset
758     if (scrollBar_) {
759         auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
760         bool scrollable = GreatNotEqual(estimatedHeight, mainSize) && IsScrollable();
761         if (scrollBar_->IsScrollable() != scrollable) {
762             scrollBar_->SetScrollable(scrollable);
763             if (scrollBarOverlayModifier_) {
764                 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
765             }
766             if (scrollable) {
767                 scrollBar_->ScheduleDisappearDelayTask();
768             }
769         }
770         Offset scrollOffset = { offset, offset }; // fit for w/h switched.
771         UpdateBorderRadius();
772         scrollBar_->SetReverse(IsReverse());
773         scrollBar_->SetIsOutOfBoundary(IsOutOfBoundary());
774         scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
775         scrollBar_->MarkNeedRender();
776     }
777 
778     // outer scrollbar
779     if (scrollBarProxy_) {
780         estimatedHeight_ = estimatedHeight - (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
781         barOffset_ = -offset;
782         scrollBarProxy_->NotifyScrollBar(AceType::WeakClaim(this));
783     }
784 }
785 
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)786 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
787 {
788     CHECK_NULL_VOID(scrollBarProxy);
789     auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source) {
790         if (source != SCROLL_FROM_START) {
791             auto pattern = weak.Upgrade();
792             if (!pattern || pattern->GetAxis() == Axis::NONE) {
793                 return false;
794             }
795             return pattern->UpdateCurrentOffset(offset, SCROLL_FROM_BAR);
796         }
797         return true;
798     };
799     auto scrollStartCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
800         auto pattern = weak.Upgrade();
801         CHECK_NULL_RETURN(pattern, false);
802         // no source == SCROLL_FROM_START for ScrollBar
803         pattern->OnScrollStartCallback();
804         return pattern->OnScrollCallback(static_cast<float>(offset), source);
805     };
806     auto scrollEndCallback = [weak = WeakClaim(this)]() {
807         auto pattern = weak.Upgrade();
808         CHECK_NULL_VOID(pattern);
809         pattern->OnScrollEnd();
810     };
811     auto calePredictSnapOffsetCallback =
812             [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
813         auto pattern = weak.Upgrade();
814         CHECK_NULL_RETURN(pattern, std::optional<float>());
815         return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
816     };
817     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
818         auto pattern = weak.Upgrade();
819         CHECK_NULL_VOID(pattern);
820         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
821     };
822 
823     auto scrollbarFRcallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
824         auto pattern = weak.Upgrade();
825         CHECK_NULL_VOID(pattern);
826         return pattern->NotifyFRCSceneInfo(CUSTOM_SCROLL_BAR_SCENE, velocity, sceneStatus);
827     };
828 
829     ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction), std::move(scrollStartCallback),
830         std::move(scrollEndCallback), std::move(calePredictSnapOffsetCallback),
831         std::move(startScrollSnapMotionCallback), std::move(scrollbarFRcallback) };
832     scrollBarProxy->RegisterScrollableNode(nodeInfo);
833     scrollBarProxy_ = scrollBarProxy;
834 }
835 
CreateScrollBarOverlayModifier()836 void ScrollablePattern::CreateScrollBarOverlayModifier()
837 {
838     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedPaint());
839     CHECK_NULL_VOID(!scrollBarOverlayModifier_);
840     scrollBarOverlayModifier_ = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
841     scrollBarOverlayModifier_->SetRect(scrollBar_->GetActiveRect());
842     scrollBarOverlayModifier_->SetPositionMode(scrollBar_->GetPositionMode());
843 }
844 
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)845 void ScrollablePattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
846 {
847     scrollBarOutBoundaryExtent_ = scrollBarOutBoundaryExtent;
848     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
849     scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
850 }
851 
SetNestedScroll(const NestedScrollOptions & nestedOpt)852 void ScrollablePattern::SetNestedScroll(const NestedScrollOptions& nestedOpt)
853 {
854     nestedScroll_ = nestedOpt;
855 }
856 
SetFriction(double friction)857 void ScrollablePattern::SetFriction(double friction)
858 {
859     if (LessOrEqual(friction, 0.0)) {
860         friction = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
861     }
862     friction_ = friction;
863     CHECK_NULL_VOID(scrollableEvent_);
864     auto scrollable = scrollableEvent_->GetScrollable();
865     scrollable->SetUnstaticFriction(friction_);
866 }
867 
SetMaxFlingVelocity(double max)868 void ScrollablePattern::SetMaxFlingVelocity(double max)
869 {
870     if (LessOrEqual(max, 0.0f)) {
871         max = MAX_VELOCITY;
872     }
873     maxFlingVelocity_ = max;
874     CHECK_NULL_VOID(scrollableEvent_);
875     auto scrollable = scrollableEvent_->GetScrollable();
876     scrollable->SetMaxFlingVelocity(max);
877 }
878 
GetParentNavigation()879 void ScrollablePattern::GetParentNavigation()
880 {
881     if (navBarPattern_) {
882         return;
883     }
884     auto host = GetHost();
885     CHECK_NULL_VOID(host);
886     if ((host->GetTag() != V2::LIST_ETS_TAG) && (host->GetTag() != V2::GRID_ETS_TAG) &&
887         (host->GetTag() != V2::SCROLL_ETS_TAG)) {
888         return;
889     }
890     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
891         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
892         if (!frameNode) {
893             continue;
894         }
895         if ((frameNode->GetTag() == V2::LIST_ETS_TAG) || (frameNode->GetTag() == V2::GRID_ETS_TAG) ||
896             (frameNode->GetTag() == V2::SCROLL_ETS_TAG)) {
897             break;
898         }
899         navBarPattern_ = frameNode->GetPattern<NavBarPattern>();
900         if (!navBarPattern_) {
901             continue;
902         }
903         return;
904     }
905     navBarPattern_ = nullptr;
906     return;
907 }
908 
GetParentModalSheet()909 void ScrollablePattern::GetParentModalSheet()
910 {
911     if (sheetPattern_) {
912         return;
913     }
914     auto host = GetHost();
915     CHECK_NULL_VOID(host);
916 
917     if (host->GetTag() != V2::SCROLL_ETS_TAG) {
918         return;
919     }
920 
921     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
922         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
923         if (!frameNode) {
924             continue;
925         }
926         sheetPattern_ = frameNode->GetPattern<SheetPresentationPattern>();
927         if (!sheetPattern_) {
928             continue;
929         }
930         return;
931     }
932     return;
933 }
934 
SetParentScrollable()935 void ScrollablePattern::SetParentScrollable()
936 {
937     if (nestedScroll_.NeedParent()) {
938         parent_ = SearchParent();
939     } else {
940         parent_ = nullptr;
941     }
942 }
943 
StopAnimate()944 void ScrollablePattern::StopAnimate()
945 {
946     if (!IsScrollableStopped()) {
947         StopScrollable();
948     }
949     if (animator_ && !animator_->IsStopped()) {
950         animator_->Stop();
951     }
952     if (!isAnimationStop_) {
953         StopAnimation(springAnimation_);
954         StopAnimation(curveAnimation_);
955     }
956 }
957 
ScrollTo(float position)958 void ScrollablePattern::ScrollTo(float position)
959 {
960     StopAnimate();
961     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
962 }
963 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll)964 void ScrollablePattern::AnimateTo(
965     float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll)
966 {
967     float currVelocity = 0.0f;
968     if (!IsScrollableStopped()) {
969         CHECK_NULL_VOID(scrollableEvent_);
970         auto scrollable = scrollableEvent_->GetScrollable();
971         CHECK_NULL_VOID(scrollable);
972         currVelocity = -scrollable->GetCurrentVelocity();
973         scrollAbort_ = true;
974         StopScrollable();
975     }
976     if (!isAnimationStop_) {
977         currVelocity = GetCurrentVelocity();
978         scrollAbort_ = true;
979         StopAnimation(springAnimation_);
980         StopAnimation(curveAnimation_);
981     }
982     if (animator_ && !animator_->IsStopped()) {
983         scrollAbort_ = true;
984         animator_->Stop();
985     }
986     if (NearEqual(position, GetTotalOffset())) {
987         return;
988     }
989     ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
990     finalPosition_ = position;
991     if (smooth) {
992         PlaySpringAnimation(position, DEFAULT_SCROLL_TO_VELOCITY, DEFAULT_SCROLL_TO_MASS, DEFAULT_SCROLL_TO_STIFFNESS,
993             DEFAULT_SCROLL_TO_DAMPING);
994     } else {
995         PlayCurveAnimation(position, duration, curve, canOverScroll);
996     }
997     FireOnScrollStart();
998     auto pipeline = PipelineBase::GetCurrentContext();
999     CHECK_NULL_VOID(pipeline);
1000     pipeline->RequestFrame();
1001 }
1002 
PlaySpringAnimation(float position,float velocity,float mass,float stiffness,float damping)1003 void ScrollablePattern::PlaySpringAnimation(float position, float velocity, float mass, float stiffness, float damping)
1004 {
1005     if (!springOffsetProperty_) {
1006         InitSpringOffsetProperty();
1007     }
1008     scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
1009         auto pattern = weakScroll.Upgrade();
1010         CHECK_NULL_RETURN(pattern, 0.0f);
1011         return pattern->GetCurrentVelocity();
1012     });
1013 
1014     AnimationOption option;
1015     auto curve = AceType::MakeRefPtr<InterpolatingSpring>(velocity, mass, stiffness, damping);
1016     InitOption(option, CUSTOM_ANIMATION_DURATION, curve);
1017     isAnimationStop_ = false;
1018     springOffsetProperty_->Set(GetTotalOffset());
1019     springAnimation_ = AnimationUtils::StartAnimation(
1020         option,
1021         [weak = AceType::WeakClaim(this), position]() {
1022             auto pattern = weak.Upgrade();
1023             CHECK_NULL_VOID(pattern);
1024             pattern->springOffsetProperty_->Set(position);
1025         },
1026         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1027             ContainerScope scope(id);
1028             auto pattern = weak.Upgrade();
1029             CHECK_NULL_VOID(pattern);
1030             pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(),
1031                 SceneStatus::END);
1032     });
1033     NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1034 }
1035 
PlayCurveAnimation(float position,float duration,const RefPtr<Curve> & curve,bool canOverScroll)1036 void ScrollablePattern::PlayCurveAnimation(
1037     float position, float duration, const RefPtr<Curve>& curve, bool canOverScroll)
1038 {
1039     AnimationOption option;
1040     InitOption(option, duration, curve);
1041     SetAnimateCanOverScroll(canOverScroll);
1042     if (!curveOffsetProperty_) {
1043         InitCurveOffsetProperty(position);
1044     }
1045     scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
1046         auto pattern = weakScroll.Upgrade();
1047         CHECK_NULL_RETURN(pattern, 0.0f);
1048         return pattern->GetCurrentVelocity();
1049     });
1050     isAnimationStop_ = false;
1051     curveOffsetProperty_->Set(GetTotalOffset());
1052     curveAnimation_ = AnimationUtils::StartAnimation(
1053         option,
1054         [weak = AceType::WeakClaim(this), position]() {
1055             auto pattern = weak.Upgrade();
1056             CHECK_NULL_VOID(pattern);
1057             pattern->curveOffsetProperty_->Set(position);
1058         },
1059         [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1060             ContainerScope scope(id);
1061             auto pattern = weak.Upgrade();
1062             CHECK_NULL_VOID(pattern);
1063             pattern->SetAnimateCanOverScroll(false);
1064             pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(), SceneStatus::END);
1065             ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1066         });
1067     NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1068 }
1069 
InitSpringOffsetProperty()1070 void ScrollablePattern::InitSpringOffsetProperty()
1071 {
1072     auto host = GetHost();
1073     CHECK_NULL_VOID(host);
1074     auto renderContext = host->GetRenderContext();
1075     CHECK_NULL_VOID(renderContext);
1076     auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1077         auto pattern = weak.Upgrade();
1078         CHECK_NULL_VOID(pattern);
1079         if (pattern->isAnimationStop_) {
1080             return;
1081         }
1082         auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1083         CHECK_NULL_VOID(context);
1084         uint64_t currentVsync = context->GetVsyncTime();
1085         uint64_t diff = currentVsync - pattern->lastVsyncTime_;
1086         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1087             pattern->currentVelocity_ = (offset - pattern->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1088             pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->currentVelocity_,
1089                 SceneStatus::RUNNING);
1090         }
1091         auto stopAnimation = NearEqual(pattern->finalPosition_, offset, SPRING_ACCURACY);
1092         if (stopAnimation) {
1093             offset = pattern->finalPosition_;
1094         }
1095         pattern->lastVsyncTime_ = currentVsync;
1096         pattern->lastPosition_ = offset;
1097         if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - offset,
1098             SCROLL_FROM_ANIMATION_CONTROLLER) || stopAnimation) {
1099             pattern->StopAnimation(pattern->springAnimation_);
1100         }
1101     };
1102     springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1103     renderContext->AttachNodeAnimatableProperty(springOffsetProperty_);
1104 }
1105 
InitCurveOffsetProperty(float position)1106 void ScrollablePattern::InitCurveOffsetProperty(float position)
1107 {
1108     auto host = GetHost();
1109     CHECK_NULL_VOID(host);
1110     auto renderContext = host->GetRenderContext();
1111     CHECK_NULL_VOID(renderContext);
1112     auto propertyCallback = [weak = AceType::WeakClaim(this), position](float offset) {
1113         auto pattern = weak.Upgrade();
1114         CHECK_NULL_VOID(pattern);
1115         if (pattern->isAnimationStop_) {
1116             return;
1117         }
1118         auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1119         CHECK_NULL_VOID(context);
1120         uint64_t currentVsync = context->GetVsyncTime();
1121         uint64_t diff = currentVsync - pattern->lastVsyncTime_;
1122         if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1123             pattern->currentVelocity_ = (offset - pattern->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1124             pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->currentVelocity_,
1125                 SceneStatus::RUNNING);
1126         }
1127         auto stopAnimation = NearEqual(pattern->finalPosition_, offset, SPRING_ACCURACY);
1128         if (stopAnimation) {
1129             offset = pattern->finalPosition_;
1130         }
1131         pattern->lastVsyncTime_ = currentVsync;
1132         pattern->lastPosition_ = offset;
1133         pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(),
1134             SceneStatus::RUNNING);
1135         if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - offset, SCROLL_FROM_ANIMATION_CONTROLLER) ||
1136             stopAnimation || pattern->isAnimateOverScroll_) {
1137             if (pattern->isAnimateOverScroll_) {
1138                 pattern->isAnimateOverScroll_ = false;
1139                 auto pauseVelocity = -pattern->currentVelocity_;
1140                 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
1141                 CHECK_NULL_VOID(context);
1142                 context->MarkNeedFlushAnimationStartTime();
1143                 pattern->PauseAnimation(pattern->curveAnimation_);
1144                 pattern->HandleOverScroll(pauseVelocity);
1145             } else if (stopAnimation || (pattern->IsAtTop() && LessOrEqual(position, pattern->GetTotalOffset())) ||
1146                        (pattern->IsAtBottom() && GreatOrEqual(position, pattern->GetTotalOffset()))) {
1147                 pattern->StopAnimation(pattern->curveAnimation_);
1148             }
1149         }
1150     };
1151     curveOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1152     renderContext->AttachNodeAnimatableProperty(curveOffsetProperty_);
1153 }
1154 
InitOption(AnimationOption & option,float duration,const RefPtr<Curve> & curve)1155 void ScrollablePattern::InitOption(AnimationOption &option, float duration, const RefPtr<Curve>& curve)
1156 {
1157     if (!curve) {
1158         option.SetCurve(Curves::EASE); // default curve
1159     } else {
1160         option.SetCurve(curve);
1161     }
1162     if (duration > 0) {
1163         option.SetDuration(duration);
1164     } else {
1165         option.SetDuration(CUSTOM_ANIMATION_DURATION);
1166     }
1167 }
1168 
StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1169 void ScrollablePattern::StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1170 {
1171     isAnimationStop_ = true;
1172     currentVelocity_ = 0.0;
1173     if (!animation) {
1174         return;
1175     }
1176     AnimationUtils::StopAnimation(animation);
1177     OnAnimateStop();
1178 }
1179 
PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1180 void ScrollablePattern::PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1181 {
1182     isAnimationStop_ = true;
1183     currentVelocity_ = 0.0;
1184     if (!animation) {
1185         return;
1186     }
1187     AnimationUtils::StopAnimation(animation);
1188 }
1189 
OnAttachToFrameNode()1190 void ScrollablePattern::OnAttachToFrameNode()
1191 {
1192     auto host = GetHost();
1193     CHECK_NULL_VOID(host);
1194     host->GetRenderContext()->SetClipToBounds(true);
1195     host->GetRenderContext()->UpdateClipEdge(true);
1196 }
1197 
UninitMouseEvent()1198 void ScrollablePattern::UninitMouseEvent()
1199 {
1200     if (!boxSelectPanEvent_) {
1201         return;
1202     }
1203     auto host = GetHost();
1204     CHECK_NULL_VOID(host);
1205     auto gestureHub = host->GetOrCreateGestureEventHub();
1206     CHECK_NULL_VOID(gestureHub);
1207     gestureHub->RemovePanEvent(boxSelectPanEvent_);
1208     boxSelectPanEvent_.Reset();
1209     ClearMultiSelect();
1210     ClearInvisibleItemsSelectedStatus();
1211     isMouseEventInit_ = false;
1212 }
1213 
InitMouseEvent()1214 void ScrollablePattern::InitMouseEvent()
1215 {
1216     auto host = GetHost();
1217     CHECK_NULL_VOID(host);
1218     auto gestureHub = host->GetOrCreateGestureEventHub();
1219     CHECK_NULL_VOID(gestureHub);
1220     if (!boxSelectPanEvent_) {
1221         auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1222             auto pattern = weak.Upgrade();
1223             CHECK_NULL_VOID(pattern);
1224             pattern->HandleDragStart(info);
1225         };
1226 
1227         auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1228             auto pattern = weak.Upgrade();
1229             CHECK_NULL_VOID(pattern);
1230             pattern->HandleDragUpdate(info);
1231         };
1232 
1233         auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1234             auto pattern = weak.Upgrade();
1235             CHECK_NULL_VOID(pattern);
1236             pattern->HandleDragEnd(info);
1237         };
1238         GestureEventNoParameter actionCancelTask;
1239         boxSelectPanEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
1240             std::move(actionEndTask), std::move(actionCancelTask));
1241     }
1242     PanDirection panDirection = { .type = PanDirection::ALL };
1243     gestureHub->AddPanEvent(boxSelectPanEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
1244     gestureHub->SetPanEventType(GestureTypeName::BOXSELECT);
1245     gestureHub->SetOnGestureJudgeNativeBegin([](const RefPtr<NG::GestureInfo>& gestureInfo,
1246                                                  const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1247         if (gestureInfo->GetType() == GestureTypeName::BOXSELECT &&
1248             gestureInfo->GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1249             return GestureJudgeResult::REJECT;
1250         }
1251         return GestureJudgeResult::CONTINUE;
1252     });
1253     isMouseEventInit_ = true;
1254 }
1255 
HandleDragStart(const GestureEvent & info)1256 void ScrollablePattern::HandleDragStart(const GestureEvent& info)
1257 {
1258     auto mouseOffsetX = static_cast<float>(info.GetLocalLocation().GetX());
1259     auto mouseOffsetY = static_cast<float>(info.GetLocalLocation().GetY());
1260     if (!IsItemSelected(info)) {
1261         ClearMultiSelect();
1262         ClearInvisibleItemsSelectedStatus();
1263         mouseStartOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1264         lastMouseStart_ = mouseStartOffset_;
1265         mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1266         mousePressOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1267         totalOffsetOfMousePressed_ = mousePressOffset_.GetMainOffset(axis_) + GetTotalOffset();
1268         canMultiSelect_ = true;
1269     }
1270     mousePressed_ = true;
1271 }
1272 
HandleDragUpdate(const GestureEvent & info)1273 void ScrollablePattern::HandleDragUpdate(const GestureEvent& info)
1274 {
1275     auto mouseOffsetX = static_cast<float>(info.GetLocalLocation().GetX());
1276     auto mouseOffsetY = static_cast<float>(info.GetLocalLocation().GetY());
1277     if (!mousePressed_ || !canMultiSelect_) {
1278         return;
1279     }
1280     if (info.GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1281         HandleDragEnd(info);
1282         return;
1283     }
1284     lastMouseMove_ = info;
1285     auto delta = OffsetF(mouseOffsetX, mouseOffsetY) - mousePressOffset_;
1286     if (Offset(delta.GetX(), delta.GetY()).GetDistance() > DEFAULT_PAN_DISTANCE.ConvertToPx()) {
1287         mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1288         // avoid large select zone
1289         LimitMouseEndOffset();
1290         auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1291         MultiSelectWithoutKeyboard(selectedZone);
1292         HandleInvisibleItemsSelectedStatus(selectedZone);
1293     }
1294     SelectWithScroll();
1295 }
1296 
HandleDragEnd(const GestureEvent & info)1297 void ScrollablePattern::HandleDragEnd(const GestureEvent& info)
1298 {
1299     mouseStartOffset_.Reset();
1300     lastMouseStart_.Reset();
1301     mouseEndOffset_.Reset();
1302     mousePressed_ = false;
1303     canMultiSelect_ = false;
1304     ClearSelectedZone();
1305     itemToBeSelected_.clear();
1306     lastMouseMove_.SetLocalLocation(Offset::Zero());
1307 }
ClearInvisibleItemsSelectedStatus()1308 void ScrollablePattern::ClearInvisibleItemsSelectedStatus()
1309 {
1310     for (auto& item : itemToBeSelected_) {
1311         item.second.FireSelectChangeEvent(false);
1312     }
1313     itemToBeSelected_.clear();
1314 }
1315 
HandleInvisibleItemsSelectedStatus(const RectF & selectedZone)1316 void ScrollablePattern::HandleInvisibleItemsSelectedStatus(const RectF& selectedZone)
1317 {
1318     auto newRect = selectedZone;
1319     auto startMainOffset = mouseStartOffset_.GetMainOffset(axis_);
1320     auto endMainOffset = mouseEndOffset_.GetMainOffset(axis_);
1321     SelectDirection oldDirection = selectDirection_;
1322     if (LessNotEqual(startMainOffset, endMainOffset)) {
1323         selectDirection_ = SELECT_DOWN;
1324         if (axis_ == Axis::VERTICAL) {
1325             newRect.SetOffset(OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_));
1326         } else {
1327             newRect.SetOffset(OffsetF(totalOffsetOfMousePressed_, selectedZone.Top()));
1328         }
1329     } else {
1330         selectDirection_ = SELECT_UP;
1331         if (axis_ == Axis::VERTICAL) {
1332             newRect.SetOffset(
1333                 OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset)));
1334         } else {
1335             newRect.SetOffset(
1336                 OffsetF(totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset), selectedZone.Top()));
1337         }
1338     }
1339     oldDirection = oldDirection == SELECT_NONE ? selectDirection_ : oldDirection;
1340 
1341     for (auto& item : itemToBeSelected_) {
1342         item.second.FireSelectChangeEvent(newRect.IsIntersectWith(item.second.rect));
1343     }
1344 
1345     if (oldDirection != selectDirection_) {
1346         itemToBeSelected_.clear();
1347     }
1348 }
1349 
SelectWithScroll()1350 void ScrollablePattern::SelectWithScroll()
1351 {
1352     if (!IsScrollable()) {
1353         return;
1354     }
1355     auto offset = GetOutOfScrollableOffset();
1356     if (NearZero(offset)) {
1357         return;
1358     }
1359 
1360     if (AnimateRunning()) {
1361         return;
1362     }
1363 
1364     if (!isAnimationStop_) {
1365         StopAnimation(springAnimation_);
1366         StopAnimation(curveAnimation_);
1367     }
1368 
1369     if (!animator_) {
1370         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
1371         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1372             auto pattern = weak.Upgrade();
1373             CHECK_NULL_VOID(pattern);
1374             pattern->OnAnimateStop();
1375         });
1376     } else if (!animator_->IsStopped()) {
1377         scrollAbort_ = true;
1378         animator_->Stop();
1379     }
1380 
1381     if (!selectMotion_) {
1382         selectMotion_ = AceType::MakeRefPtr<SelectMotion>(offset, [weak = WeakClaim(this)]() -> bool {
1383             auto pattern = weak.Upgrade();
1384             CHECK_NULL_RETURN(pattern, true);
1385             return pattern->ShouldSelectScrollBeStopped();
1386         });
1387         selectMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
1388             auto pattern = weakScroll.Upgrade();
1389             CHECK_NULL_VOID(pattern);
1390             offset = pattern->GetOffsetWithLimit(offset);
1391             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
1392             pattern->UpdateMouseStart(offset);
1393         });
1394     } else {
1395         selectMotion_->Reset(offset);
1396     }
1397 
1398     animator_->PlayMotion(selectMotion_);
1399 
1400     FireOnScrollStart();
1401 }
1402 
ClearSelectedZone()1403 void ScrollablePattern::ClearSelectedZone()
1404 {
1405     DrawSelectedZone(RectF());
1406 }
1407 
ComputeSelectedZone(const OffsetF & startOffset,const OffsetF & endOffset)1408 RectF ScrollablePattern::ComputeSelectedZone(const OffsetF& startOffset, const OffsetF& endOffset)
1409 {
1410     RectF selectedZone;
1411     if (startOffset.GetX() <= endOffset.GetX()) {
1412         if (startOffset.GetY() <= endOffset.GetY()) {
1413             // bottom right
1414             selectedZone = RectF(startOffset.GetX(), startOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1415                 endOffset.GetY() - startOffset.GetY());
1416         } else {
1417             // top right
1418             selectedZone = RectF(startOffset.GetX(), endOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1419                 startOffset.GetY() - endOffset.GetY());
1420         }
1421     } else {
1422         if (startOffset.GetY() <= endOffset.GetY()) {
1423             // bottom left
1424             selectedZone = RectF(endOffset.GetX(), startOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1425                 endOffset.GetY() - startOffset.GetY());
1426         } else {
1427             // top left
1428             selectedZone = RectF(endOffset.GetX(), endOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1429                 startOffset.GetY() - endOffset.GetY());
1430         }
1431     }
1432 
1433     return selectedZone;
1434 }
1435 
DrawSelectedZone(const RectF & selectedZone)1436 void ScrollablePattern::DrawSelectedZone(const RectF& selectedZone)
1437 {
1438     auto host = GetHost();
1439     CHECK_NULL_VOID(host);
1440     auto hostContext = host->GetRenderContext();
1441     CHECK_NULL_VOID(hostContext);
1442     hostContext->UpdateMouseSelectWithRect(selectedZone, SELECT_FILL_COLOR, SELECT_STROKE_COLOR);
1443 }
1444 
MarkSelectedItems()1445 void ScrollablePattern::MarkSelectedItems()
1446 {
1447     if (multiSelectable_ && mousePressed_) {
1448         auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1449         if (!selectedZone.IsEmpty()) {
1450             MultiSelectWithoutKeyboard(selectedZone);
1451             HandleInvisibleItemsSelectedStatus(selectedZone);
1452         }
1453     }
1454 }
1455 
ShouldSelectScrollBeStopped()1456 bool ScrollablePattern::ShouldSelectScrollBeStopped()
1457 {
1458     if (!mousePressed_) {
1459         return true;
1460     }
1461     auto offset = GetOutOfScrollableOffset();
1462     if (NearZero(offset)) {
1463         return true;
1464     }
1465 
1466     if (selectMotion_) {
1467         selectMotion_->Reset(offset);
1468     }
1469     return false;
1470 };
1471 
UpdateMouseStart(float offset)1472 void ScrollablePattern::UpdateMouseStart(float offset)
1473 {
1474     if (axis_ == Axis::VERTICAL) {
1475         mouseStartOffset_.AddY(offset);
1476     } else {
1477         mouseStartOffset_.AddX(offset);
1478     }
1479 }
1480 
GetOutOfScrollableOffset() const1481 float ScrollablePattern::GetOutOfScrollableOffset() const
1482 {
1483     auto offset = 0.0f;
1484     auto mouseMainOffset = static_cast<float>(
1485         axis_ == Axis::VERTICAL ? lastMouseMove_.GetLocalLocation().GetY() : lastMouseMove_.GetLocalLocation().GetX());
1486     auto hostSize = GetHostFrameSize();
1487     CHECK_NULL_RETURN(hostSize.has_value(), offset);
1488     auto mainTop = 0.0f;
1489     auto mainBottom = hostSize->MainSize(axis_);
1490     if (GreatOrEqual(mouseMainOffset, mainTop) && LessOrEqual(mouseMainOffset, mainBottom)) {
1491         return offset;
1492     }
1493     if (GreatNotEqual(mouseMainOffset, mainBottom)) {
1494         if (IsAtBottom()) {
1495             return offset;
1496         }
1497         offset = mainBottom - mouseMainOffset;
1498     }
1499     if (LessNotEqual(mouseMainOffset, mainTop)) {
1500         if (IsAtTop()) {
1501             return offset;
1502         }
1503         offset = mainTop - mouseMainOffset;
1504     }
1505     return offset;
1506 }
1507 
1508 // avoid start position move when offset is bigger then item height
GetOffsetWithLimit(float offset) const1509 float ScrollablePattern::GetOffsetWithLimit(float offset) const
1510 {
1511     if (Positive(offset)) {
1512         auto totalOffset = GetTotalOffset();
1513         return std::min(totalOffset, offset);
1514     } else if (Negative(offset)) {
1515         auto hostSize = GetHostFrameSize();
1516         CHECK_NULL_RETURN(hostSize.has_value(), true);
1517         auto remainHeight = GetTotalHeight() - GetTotalOffset() - hostSize->MainSize(axis_);
1518         return std::max(offset, -remainHeight);
1519     }
1520     return 0;
1521 }
1522 
LimitMouseEndOffset()1523 void ScrollablePattern::LimitMouseEndOffset()
1524 {
1525     float limitedMainOffset = -1.0f;
1526     float limitedCrossOffset = -1.0f;
1527     auto hostSize = GetHostFrameSize();
1528     auto mainSize = hostSize->MainSize(axis_);
1529     auto crossSize = hostSize->CrossSize(axis_);
1530     auto mainOffset = mouseEndOffset_.GetMainOffset(axis_);
1531     auto crossOffset = mouseEndOffset_.GetCrossOffset(axis_);
1532     if (LessNotEqual(mainOffset, 0.0f)) {
1533         limitedMainOffset = 0.0f;
1534     }
1535     if (GreatNotEqual(mainOffset, mainSize)) {
1536         limitedMainOffset = mainSize;
1537     }
1538     if (LessNotEqual(crossOffset, 0.0f)) {
1539         limitedCrossOffset = 0.0f;
1540     }
1541     if (GreatNotEqual(crossOffset, crossSize)) {
1542         limitedCrossOffset = crossSize;
1543     }
1544 
1545     if (axis_ == Axis::VERTICAL) {
1546         mouseEndOffset_.SetX(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedCrossOffset);
1547         mouseEndOffset_.SetY(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedMainOffset);
1548     } else {
1549         mouseEndOffset_.SetX(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedMainOffset);
1550         mouseEndOffset_.SetY(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedCrossOffset);
1551     }
1552 }
1553 
HandleScrollImpl(float offset,int32_t source)1554 bool ScrollablePattern::HandleScrollImpl(float offset, int32_t source)
1555 {
1556     // Previous: Set HandleScrollImpl to Scrollable->callback_
1557     // Scrollable::HandleScroll calls callback_ through UpdateScrollPosition
1558 
1559     // Now: HandleScroll moved to ScrollablePattern, directly call HandleScrollImpl in
1560     // ScrollablePattern::HandleScroll
1561     double overOffset = offset;
1562     if (!OnScrollPosition(overOffset, source)) {
1563         return false;
1564     }
1565     auto result = OnScrollCallback(overOffset, source);
1566     SelectOverlayScrollNotifier::NotifyOnScrollCallback(WeakClaim(this), overOffset, source);
1567     return result;
1568 }
1569 
NotifyMoved(bool value)1570 void ScrollablePattern::NotifyMoved(bool value)
1571 {
1572     CHECK_NULL_VOID(scrollableEvent_);
1573     auto&& scroll = scrollableEvent_->GetScrollable();
1574     if (scroll) {
1575         scroll->SetMoved(value);
1576     }
1577 }
1578 
ProcessSpringEffect(float velocity)1579 void ScrollablePattern::ProcessSpringEffect(float velocity)
1580 {
1581     CHECK_NULL_VOID(InstanceOf<ScrollSpringEffect>(scrollEffect_));
1582     if (!OutBoundaryCallback() && !GetCanOverScroll()) {
1583         return;
1584     }
1585     scrollEffect_->ProcessScrollOver(velocity);
1586 }
1587 
SetCanOverScroll(bool val)1588 void ScrollablePattern::SetCanOverScroll(bool val)
1589 {
1590     CHECK_NULL_VOID(scrollableEvent_);
1591     auto&& scrollable = scrollableEvent_->GetScrollable();
1592     if (scrollable) {
1593         scrollable->SetCanOverScroll(val);
1594     }
1595 }
1596 
GetCanOverScroll() const1597 bool ScrollablePattern::GetCanOverScroll() const
1598 {
1599     CHECK_NULL_RETURN(scrollableEvent_, true);
1600     auto&& scrollable = scrollableEvent_->GetScrollable();
1601     if (scrollable) {
1602         return scrollable->CanOverScroll();
1603     }
1604     return true;
1605 }
1606 
GetEdgeEffect() const1607 EdgeEffect ScrollablePattern::GetEdgeEffect() const
1608 {
1609     return edgeEffect_;
1610 }
1611 
GetScrollState() const1612 ScrollState ScrollablePattern::GetScrollState() const
1613 {
1614     return ScrollablePattern::GetScrollState(scrollSource_);
1615 }
1616 
GetScrollState(int32_t scrollSource)1617 ScrollState ScrollablePattern::GetScrollState(int32_t scrollSource)
1618 {
1619     // with event
1620     if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS || scrollSource == SCROLL_FROM_BAR) {
1621         return ScrollState::SCROLL;
1622     }
1623     // without event
1624     if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
1625         scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
1626         return ScrollState::FLING;
1627     }
1628     // SCROLL_FROM_NONE, SCROLL_FROM_JUMP, SCROLL_FROM_CHILD, SCROLL_FROM_FOCUS_JUMP, SCROLL_FROM_ROTATE,
1629     // SCROLL_FROM_INDEXER, SCROLL_FROM_START
1630     return ScrollState::IDLE;
1631 }
1632 
HandleScrollParentFirst(float & offset,int32_t source,NestedState state)1633 ScrollResult ScrollablePattern::HandleScrollParentFirst(float& offset, int32_t source, NestedState state)
1634 {
1635     auto parent = parent_.Upgrade();
1636     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1637     if (state == NestedState::CHILD_OVER_SCROLL) {
1638         if (GetEdgeEffect() == EdgeEffect::NONE) {
1639             return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1640         }
1641         ExecuteScrollFrameBegin(offset, scrollState);
1642         return { 0, true };
1643     }
1644     auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL);
1645     offset = result.remain;
1646     if (NearZero(offset)) {
1647         SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1648         return { 0, false };
1649     }
1650     float allOffset = offset;
1651     ExecuteScrollFrameBegin(offset, scrollState);
1652     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1653     auto overOffsets = GetOverScrollOffset(offset);
1654     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1655     remainOffset += overOffset;
1656     if (NearZero(remainOffset)) {
1657         SetCanOverScroll(false);
1658         return { 0, false };
1659     }
1660     if (state == NestedState::CHILD_SCROLL) {
1661         offset -= overOffset;
1662         SetCanOverScroll(false);
1663         return { remainOffset, !NearZero(overOffset) };
1664     }
1665     if (GetEdgeEffect() == EdgeEffect::NONE) {
1666         result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL);
1667     }
1668     SetCanOverScroll(!NearZero(overOffset) || (NearZero(offset) && result.reachEdge));
1669     return { 0, GetCanOverScroll() };
1670 }
1671 
HandleScrollSelfFirst(float & offset,int32_t source,NestedState state)1672 ScrollResult ScrollablePattern::HandleScrollSelfFirst(float& offset, int32_t source, NestedState state)
1673 {
1674     auto parent = parent_.Upgrade();
1675     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1676     if (state == NestedState::CHILD_OVER_SCROLL) {
1677         auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1678         if (NearZero(result.remain)) {
1679             offset = 0;
1680             return result;
1681         }
1682         ExecuteScrollFrameBegin(offset, scrollState);
1683         if (GetEdgeEffect() == EdgeEffect::NONE) {
1684             return result;
1685         }
1686         return { 0, true };
1687     }
1688     float allOffset = offset;
1689     ExecuteScrollFrameBegin(offset, scrollState);
1690     auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1691     auto overOffsets = GetOverScrollOffset(offset);
1692     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1693     if (NearZero(overOffset) && NearZero(remainOffset)) {
1694         SetCanOverScroll(false);
1695         return { 0, false };
1696     }
1697     offset -= overOffset;
1698     auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL);
1699     if (NearZero(result.remain)) {
1700         SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1701         return { 0, false };
1702     }
1703     if (state == NestedState::CHILD_SCROLL) {
1704         SetCanOverScroll(false);
1705         return result;
1706     }
1707     // triggering overScroll, parent always handle it first
1708     auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
1709     offset += LessNotEqual(std::abs(overOffset), std::abs(result.remain)) ? overOffset : overRes.remain;
1710     SetCanOverScroll((!NearZero(overOffset) || NearZero(offset)) && overRes.reachEdge);
1711     return { 0, GetCanOverScroll() };
1712 }
1713 
HandleScrollSelfOnly(float & offset,int32_t source,NestedState state)1714 ScrollResult ScrollablePattern::HandleScrollSelfOnly(float& offset, int32_t source, NestedState state)
1715 {
1716     float allOffset = offset;
1717     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1718     ExecuteScrollFrameBegin(offset, scrollState);
1719     auto remainOffset = allOffset - offset;
1720     auto overOffsets = GetOverScrollOffset(!IsReverse() ? offset : -offset);
1721     auto overOffset = (!IsReverse() ? offset > 0 : offset < 0) ? overOffsets.start : overOffsets.end;
1722     remainOffset += overOffset;
1723     if (NearZero(remainOffset)) {
1724         SetCanOverScroll(false);
1725         return { 0, false };
1726     }
1727     bool canOverScroll = false;
1728     if (state == NestedState::CHILD_SCROLL) {
1729         offset -= overOffset;
1730     } else if (state == NestedState::GESTURE) {
1731         canOverScroll = !NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE;
1732     } else if (GetEdgeEffect() != EdgeEffect::NONE) {
1733         remainOffset = 0;
1734     }
1735     SetCanOverScroll(canOverScroll);
1736     return { remainOffset, !NearZero(overOffset) };
1737 }
1738 
HandleScrollParallel(float & offset,int32_t source,NestedState state)1739 ScrollResult ScrollablePattern::HandleScrollParallel(float& offset, int32_t source, NestedState state)
1740 {
1741     auto remainOffset = 0.0;
1742     auto parent = parent_.Upgrade();
1743     ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1744     if (state == NestedState::CHILD_OVER_SCROLL) {
1745         if (GetEdgeEffect() == EdgeEffect::NONE) {
1746             auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1747             remainOffset = result.remain;
1748             offset = 0;
1749         } else {
1750             ExecuteScrollFrameBegin(offset, scrollState);
1751         }
1752         return { remainOffset, true };
1753     }
1754 
1755     bool canOverScroll = false;
1756     float parentOffset = offset;
1757     ExecuteScrollFrameBegin(offset, scrollState);
1758     auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL);
1759 
1760     auto overOffsets = GetOverScrollOffset(offset);
1761     auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1762     if (!NearZero(overOffset) && result.reachEdge) {
1763         if (state == NestedState::CHILD_SCROLL) {
1764             remainOffset = overOffset;
1765             offset = offset - overOffset;
1766         } else if (GetEdgeEffect() == EdgeEffect::NONE) {
1767             parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
1768             canOverScroll = true;
1769             offset = offset - overOffset;
1770         } else {
1771             canOverScroll = true;
1772         }
1773     } else if (!NearZero(overOffset)) {
1774         offset = offset - overOffset;
1775     }
1776     SetCanOverScroll(canOverScroll);
1777     return { remainOffset, !NearZero(overOffset) && result.reachEdge };
1778 }
1779 
HandleScroll(float offset,int32_t source,NestedState state)1780 ScrollResult ScrollablePattern::HandleScroll(float offset, int32_t source, NestedState state)
1781 {
1782     ScrollResult result = { 0, false };
1783     auto parent = parent_.Upgrade();
1784     auto overOffsets = GetOverScrollOffset(!IsReverse() ? offset : -offset);
1785     float backOverOffset = (!IsReverse() ? offset > 0 : offset < 0) ? overOffsets.end : overOffsets.start;
1786     if (NearZero(offset) || !NearZero(backOverOffset)) {
1787         ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1788         ExecuteScrollFrameBegin(offset, scrollState);
1789     } else if (parent && !IsScrollSnap() && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::PARENT_FIRST) ||
1790                              (offset > 0 && nestedScroll_.backward == NestedScrollMode::PARENT_FIRST))) {
1791         result = HandleScrollParentFirst(offset, source, state);
1792     } else if (parent && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::SELF_FIRST) ||
1793                              (offset > 0 && nestedScroll_.backward == NestedScrollMode::SELF_FIRST))) {
1794         result = HandleScrollSelfFirst(offset, source, state);
1795     } else if (parent && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::PARALLEL) ||
1796                              (offset > 0 && nestedScroll_.backward == NestedScrollMode::PARALLEL))) {
1797         result = HandleScrollParallel(offset, source, state);
1798     } else {
1799         result = HandleScrollSelfOnly(offset, source, state);
1800     }
1801     bool moved = HandleScrollImpl(offset, source);
1802     NotifyMoved(moved);
1803     return result;
1804 }
1805 
HandleScrollVelocity(float velocity)1806 bool ScrollablePattern::HandleScrollVelocity(float velocity)
1807 {
1808     if ((velocity > 0 && !IsAtTop()) || (velocity < 0 && !IsAtBottom())) {
1809         // trigger scroll animation if edge not reached
1810         if (scrollableEvent_ && scrollableEvent_->GetScrollable()) {
1811             scrollableEvent_->GetScrollable()->StartScrollAnimation(0.0f, velocity);
1812             return true;
1813         }
1814         return false;
1815     }
1816     return HandleOverScroll(velocity) || GetEdgeEffect() == EdgeEffect::FADE;
1817 }
1818 
HandleOverScroll(float velocity)1819 bool ScrollablePattern::HandleOverScroll(float velocity)
1820 {
1821     auto parent = parent_.Upgrade();
1822     if (!parent || !nestedScroll_.NeedParent(velocity < 0)) {
1823         if (GetEdgeEffect() == EdgeEffect::SPRING && AnimateStoped()) {
1824             // trigger onScrollEnd later, when spring animation finishes
1825             ProcessSpringEffect(velocity);
1826             return true;
1827         }
1828         OnScrollEnd();
1829         return false;
1830     }
1831     // parent handle over scroll first
1832     if ((velocity < 0 && (nestedScroll_.forward == NestedScrollMode::SELF_FIRST)) ||
1833         (velocity > 0 && (nestedScroll_.backward == NestedScrollMode::SELF_FIRST)) ||
1834         !InstanceOf<ScrollablePattern>(parent)) {
1835         if (parent->HandleScrollVelocity(velocity)) {
1836             OnScrollEnd();
1837             return true;
1838         }
1839         if (GetEdgeEffect() == EdgeEffect::SPRING) {
1840             ProcessSpringEffect(velocity);
1841             return true;
1842         }
1843     }
1844 
1845     // self handle over scroll first
1846     if (GetEdgeEffect() == EdgeEffect::SPRING) {
1847         ProcessSpringEffect(velocity);
1848         return true;
1849     }
1850     OnScrollEnd();
1851     return parent->HandleScrollVelocity(velocity);
1852 }
1853 
ExecuteScrollFrameBegin(float & mainDelta,ScrollState state)1854 void ScrollablePattern::ExecuteScrollFrameBegin(float& mainDelta, ScrollState state)
1855 {
1856     auto context = PipelineContext::GetCurrentContext();
1857     if (!context || !scrollFrameBeginCallback_) {
1858         return;
1859     }
1860 
1861     auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
1862     auto scrollRes = scrollFrameBeginCallback_(-offset, state);
1863     mainDelta = -context->NormalizeToPx(scrollRes.offset);
1864 }
1865 
OnScrollStartRecursive(float position)1866 void ScrollablePattern::OnScrollStartRecursive(float position)
1867 {
1868     HandleScrollImpl(position, SCROLL_FROM_START);
1869     auto parent = parent_.Upgrade();
1870     if (parent && nestedScroll_.NeedParent()) {
1871         parent->OnScrollStartRecursive(position);
1872     }
1873 }
1874 
OnScrollEndRecursive(const std::optional<float> & velocity)1875 void ScrollablePattern::OnScrollEndRecursive(const std::optional<float>& velocity)
1876 {
1877     OnScrollEnd();
1878     auto parent = parent_.Upgrade();
1879     if (parent && nestedScroll_.NeedParent()) {
1880         parent->OnScrollEndRecursive(velocity);
1881     }
1882 }
1883 
GetVelocity() const1884 float ScrollablePattern::GetVelocity() const
1885 {
1886     float velocity = 0.0f;
1887     CHECK_NULL_RETURN(scrollableEvent_, velocity);
1888     auto scrollable = scrollableEvent_->GetScrollable();
1889     CHECK_NULL_RETURN(scrollable, velocity);
1890     velocity = scrollable->GetCurrentVelocity();
1891     return velocity;
1892 }
1893 
RegisterScrollingListener(const RefPtr<ScrollingListener> listener)1894 void ScrollablePattern::RegisterScrollingListener(const RefPtr<ScrollingListener> listener)
1895 {
1896     CHECK_NULL_VOID(listener);
1897     scrollingListener_.emplace_back(listener);
1898 }
1899 
FireAndCleanScrollingListener()1900 void ScrollablePattern::FireAndCleanScrollingListener()
1901 {
1902     for (auto listener : scrollingListener_) {
1903         CHECK_NULL_VOID(listener);
1904         listener->NotifyScrollingEvent();
1905     }
1906     scrollingListener_.clear();
1907 }
1908 
CleanScrollingListener()1909 void ScrollablePattern::CleanScrollingListener()
1910 {
1911     scrollingListener_.clear();
1912 }
1913 
GetMainContentSize() const1914 float ScrollablePattern::GetMainContentSize() const
1915 {
1916     auto host = GetHost();
1917     CHECK_NULL_RETURN(host, 0.0);
1918     auto geometryNode = host->GetGeometryNode();
1919     CHECK_NULL_RETURN(geometryNode, 0.0);
1920     return geometryNode->GetPaddingSize().MainSize(axis_);
1921 }
1922 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1923 void ScrollablePattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1924 {
1925     if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1926         ScrollToIndex(0, false, ScrollAlign::START);
1927     } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1928         // use LAST_ITEM for children count changed after scrollEdge(Edge.Bottom) and before layout
1929         ScrollToIndex(LAST_ITEM, false, ScrollAlign::END);
1930     }
1931 }
1932 
NotifyFRCSceneInfo(const std::string & scene,double velocity,SceneStatus sceneStatus)1933 void ScrollablePattern::NotifyFRCSceneInfo(const std::string& scene, double velocity, SceneStatus sceneStatus)
1934 {
1935     auto host = GetHost();
1936     CHECK_NULL_VOID(host);
1937     host->AddFRCSceneInfo(scene, velocity, sceneStatus);
1938 }
1939 
FireOnScrollStart()1940 void ScrollablePattern::FireOnScrollStart()
1941 {
1942     PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
1943     if (GetScrollAbort()) {
1944         return;
1945     }
1946     auto scrollBar = GetScrollBar();
1947     if (scrollBar) {
1948         scrollBar->PlayScrollBarAppearAnimation();
1949     }
1950     StopScrollBarAnimatorByProxy();
1951     auto host = GetHost();
1952     CHECK_NULL_VOID(host);
1953     auto hub = host->GetEventHub<ScrollableEventHub>();
1954     CHECK_NULL_VOID(hub);
1955     host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
1956     auto onScrollStart = hub->GetOnScrollStart();
1957     CHECK_NULL_VOID(onScrollStart);
1958     onScrollStart();
1959 }
1960 
OnScrollStartCallback()1961 void ScrollablePattern::OnScrollStartCallback()
1962 {
1963     FireOnScrollStart();
1964 };
1965 
FireOnScroll(float finalOffset,OnScrollEvent & onScroll) const1966 void ScrollablePattern::FireOnScroll(float finalOffset, OnScrollEvent& onScroll) const
1967 {
1968     auto offsetPX = Dimension(finalOffset);
1969     auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
1970     auto scrollState = GetScrollState();
1971     if (!NearZero(finalOffset)) {
1972         onScroll(offsetVP, scrollState);
1973     }
1974     if (scrollStop_ && !GetScrollAbort()) {
1975         if (scrollState != ScrollState::IDLE) {
1976             onScroll(0.0_vp, ScrollState::IDLE);
1977         }
1978     }
1979 }
1980 
OnScrollStop(const OnScrollStopEvent & onScrollStop)1981 void ScrollablePattern::OnScrollStop(const OnScrollStopEvent& onScrollStop)
1982 {
1983     if (scrollStop_) {
1984         if (!GetScrollAbort()) {
1985             auto host = GetHost();
1986             if (host != nullptr) {
1987                 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1988             }
1989             if (onScrollStop) {
1990                 SetScrollSource(SCROLL_FROM_NONE);
1991                 onScrollStop();
1992             }
1993             auto scrollBar = GetScrollBar();
1994             if (scrollBar) {
1995                 scrollBar->ScheduleDisappearDelayTask();
1996             }
1997             StartScrollBarAnimatorByProxy();
1998         }
1999         PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
2000         AceAsyncTraceEnd(0, TRAILING_ANIMATION);
2001         scrollStop_ = false;
2002         SetScrollAbort(false);
2003     }
2004 }
2005 
2006 /**
2007  * @description: Register with the drag drop manager
2008  * @return None
2009  */
Register2DragDropManager()2010 void ScrollablePattern::Register2DragDropManager()
2011 {
2012     auto host = GetHost();
2013     CHECK_NULL_VOID(host);
2014     auto pipeline = PipelineContext::GetCurrentContext();
2015     CHECK_NULL_VOID(pipeline);
2016     auto dragDropManager = pipeline->GetDragDropManager();
2017     CHECK_NULL_VOID(dragDropManager);
2018     dragDropManager->RegisterDragStatusListener(host->GetId(), AceType::WeakClaim(AceType::RawPtr(host)));
2019 }
2020 
2021 /**
2022  * @description: Determine whether it is in the hot zone, then
2023  * 1.Gives the rolling direction according to the location of the hot zone
2024  * 2.Gives the distance from the edge of the hot zone from the drag point
2025  * @param {PointF&} point The drag point
2026  * @return The distance from the edge of the hot zone from the drag point.scroll up:Offset percent is positive, scroll
2027  * down:Offset percent is  negative
2028  */
IsInHotZone(const PointF & point)2029 float ScrollablePattern::IsInHotZone(const PointF& point)
2030 {
2031     auto host = GetHost();
2032     auto offset = 0.f;
2033     auto geometryNode = host->GetGeometryNode();
2034     CHECK_NULL_RETURN(geometryNode, 0.f);
2035 
2036     auto wholeRect = geometryNode->GetFrameRect();
2037     wholeRect.SetOffset(host->GetTransformRelativeOffset());
2038     auto hotZoneHeightPX = HOT_ZONE_HEIGHT_VP_DIM.ConvertToPx();
2039     auto hotZoneWidthPX = HOT_ZONE_WIDTH_VP_DIM.ConvertToPx();
2040     if (isVertical()) {
2041         // create top hot zone,it is a rectangle
2042         auto topHotzone = wholeRect;
2043         topHotzone.SetHeight(hotZoneHeightPX);
2044 
2045         // create bottom hot zone,it is a rectangle
2046         auto bottomHotzone = wholeRect;
2047         auto bottomZoneEdgeY = wholeRect.GetY() + wholeRect.Height();
2048         bottomHotzone.SetTop(bottomZoneEdgeY - hotZoneHeightPX);
2049         bottomHotzone.SetHeight(hotZoneHeightPX);
2050 
2051         // Determines whether the drag point is within the hot zone,
2052         // then gives the scroll component movement direction according to which hot zone the point is in
2053         // top or bottom hot zone
2054         if (topHotzone.IsInRegion(point)) {
2055             offset = hotZoneHeightPX - point.GetY() + topHotzone.GetY();
2056             if (!NearZero(hotZoneHeightPX)) {
2057                 return offset / hotZoneHeightPX;
2058             }
2059         } else if (bottomHotzone.IsInRegion(point)) {
2060             offset = bottomZoneEdgeY - point.GetY() - hotZoneHeightPX;
2061             if (!NearZero(hotZoneHeightPX)) {
2062                 return offset / hotZoneHeightPX;
2063             }
2064         }
2065     } else {
2066         auto leftHotzone = wholeRect;
2067 
2068         // create left hot zone,it is a rectangle
2069         leftHotzone.SetWidth(hotZoneWidthPX);
2070 
2071         // create right hot zone,it is a rectangle
2072         auto rightHotzone = wholeRect;
2073         rightHotzone.SetWidth(hotZoneWidthPX);
2074         auto rightZoneEdgeX = wholeRect.GetX() + wholeRect.Width();
2075         rightHotzone.SetLeft(rightZoneEdgeX - hotZoneWidthPX);
2076 
2077         // Determines whether the drag point is within the hot zone,
2078         // gives the scroll component movement direction according to which hot zone the point is in
2079         // left or right hot zone
2080         if (leftHotzone.IsInRegion(point)) {
2081             offset = hotZoneWidthPX - point.GetX() + wholeRect.GetX();
2082             if (!NearZero(hotZoneWidthPX)) {
2083                 return offset / hotZoneWidthPX;
2084             }
2085         } else if (rightHotzone.IsInRegion(point)) {
2086             offset = rightZoneEdgeX - point.GetX() - hotZoneWidthPX;
2087             if (!NearZero(hotZoneWidthPX)) {
2088                 return offset / hotZoneWidthPX;
2089             }
2090         }
2091     }
2092 
2093     return 0.f;
2094 }
2095 
2096 /**
2097  * @description: Determines whether the scroll component is in the vertical direction
2098  * @return True,If the scrolling component is vertical
2099  */
isVertical() const2100 bool ScrollablePattern::isVertical() const
2101 {
2102     return axis_ == Axis::VERTICAL;
2103 }
2104 
2105 /**
2106  * @description: scroll up or down
2107  * @param {float} offsetPct offset percent.When scrolling in the vertical or horizontal direction, there is a distance
2108  * between the drag point and the outer edge of the hot zone, and the percentage represents the proportion of this
2109  * distance to the height or width of the hot zone
2110  * @return None
2111  */
HotZoneScroll(const float offsetPct)2112 void ScrollablePattern::HotZoneScroll(const float offsetPct)
2113 {
2114     auto host = GetHost();
2115     CHECK_NULL_VOID(IsScrollable());
2116     CHECK_NULL_VOID(!NearZero(offsetPct));
2117 
2118     // There are three types of situations to consider.
2119     // 1. Enter the hot zone for the first time.
2120     // 2. When the drag point leaves the hot zone, it enters the hot zone again
2121     // 3. When the drag point moves within the hot zone, the hot zone offset changes
2122     CHECK_NULL_VOID(!NearEqual(lastHonezoneOffsetPct_, offsetPct));
2123 
2124     if (AnimateRunning()) {
2125         // Variable speed rolling
2126         // When the drag point is in the hot zone, and the hot zone offset changes.
2127         // Then need to modify the offset percent
2128         if (velocityMotion_) {
2129             velocityMotion_->Reset(offsetPct);
2130         }
2131         return;
2132     }
2133 
2134     if (!animator_) {
2135         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
2136         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
2137             auto pattern = weak.Upgrade();
2138             CHECK_NULL_VOID(pattern);
2139             pattern->AddHotZoneSenceInterface(SceneStatus::END);
2140             pattern->OnAnimateStop();
2141         });
2142         animator_->AddStartListener([weak = AceType::WeakClaim(this)]() {
2143             auto pattern = weak.Upgrade();
2144             CHECK_NULL_VOID(pattern);
2145             pattern->AddHotZoneSenceInterface(SceneStatus::START);
2146         });
2147     }
2148 
2149     if (!velocityMotion_) {
2150         // Enter the hot zone for the first time.
2151         velocityMotion_ = AceType::MakeRefPtr<BezierVariableVelocityMotion>(
2152             offsetPct, [weak = WeakClaim(this)](float offset) -> bool {
2153                 auto pattern = weak.Upgrade();
2154                 CHECK_NULL_RETURN(pattern, true);
2155 
2156                 if (LessNotEqual(offset, 0) && pattern->IsAtBottom()) {
2157                     // Stop scrolling when reach the bottom
2158                     return true;
2159                 } else if (GreatNotEqual(offset, 0) && pattern->IsAtTop()) {
2160                     // Stop scrolling when reach the top
2161                     return true;
2162                 }
2163                 return false;
2164             });
2165         velocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
2166             // Get the distance component need to roll from BezierVariableVelocityMotion
2167             // Roll up: negative value, Roll up: positive value
2168             auto pattern = weakScroll.Upgrade();
2169             CHECK_NULL_VOID(pattern);
2170             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
2171             pattern->UpdateMouseStart(offset);
2172             pattern->AddHotZoneSenceInterface(SceneStatus::RUNNING);
2173         });
2174         velocityMotion_->ReInit(offsetPct);
2175     } else {
2176         // When the drag point leaves the hot zone, it enters the hot zone again.Then need to reset offset percent.
2177         velocityMotion_->ReInit(offsetPct);
2178     }
2179     // Save the last offset percent
2180     lastHonezoneOffsetPct_ = offsetPct;
2181     animator_->PlayMotion(velocityMotion_);
2182     FireOnScrollStart();
2183 }
2184 
2185 /**
2186  * @description: When the drag point leaves the hot zone, stop the animation.
2187  * @return None
2188  */
StopHotzoneScroll()2189 void ScrollablePattern::StopHotzoneScroll()
2190 {
2191     if (!AnimateStoped()) {
2192         animator_->Stop();
2193     }
2194 }
2195 
2196 /**
2197  * @description: Handle drag and drop events
2198  * When a drag point enters or moves over a component, determine whether it is within the hot zone.
2199  * When leave the component, stop scrolling
2200  * @param {DragEventType&} dragEventType Drag the event type
2201  * @param {NotifyDragEvent&} notifyDragEvent Drag event
2202  * @return None
2203  */
HandleHotZone(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2204 void ScrollablePattern::HandleHotZone(
2205     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2206 {
2207     // The starting version of the auto-scroll feature is 11
2208     CHECK_NULL_VOID(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN));
2209     PointF point(static_cast<float>(notifyDragEvent->GetX()), static_cast<float>(notifyDragEvent->GetY()));
2210     switch (dragEventType) {
2211         case DragEventType::ENTER: {
2212             HandleMoveEventInComp(point);
2213             break;
2214         }
2215         case DragEventType::MOVE: {
2216             HandleMoveEventInComp(point);
2217             break;
2218         }
2219         case DragEventType::DROP:
2220         case DragEventType::LEAVE: {
2221             HandleLeaveHotzoneEvent();
2222             break;
2223         }
2224         default:
2225             break;
2226     }
2227 }
2228 
2229 /**
2230  * @description:When a drag point is inside the scroll component, it is necessary to handle the events of each moving
2231  * point
2232  * @param {PointF&} point the drag point
2233  * @return None
2234  */
HandleMoveEventInComp(const PointF & point)2235 void ScrollablePattern::HandleMoveEventInComp(const PointF& point)
2236 {
2237     float offsetPct = IsInHotZone(point);
2238     if (NearZero(offsetPct)) {
2239         // Although it entered the rolling component, it is not in the rolling component hot zone.Then stop
2240         // scrolling
2241         HandleLeaveHotzoneEvent();
2242     } else {
2243         // The drag point enters the hot zone
2244         HotZoneScroll(offsetPct);
2245     }
2246 }
2247 
2248 /**
2249  * @description:When the drag point is not in the hot zone, need to stop scrolling, if it exists.
2250  * This function is executed multiple times
2251  * @return None
2252  */
HandleLeaveHotzoneEvent()2253 void ScrollablePattern::HandleLeaveHotzoneEvent()
2254 {
2255     // Stop scrolling up and down
2256     StopHotzoneScroll();
2257 }
2258 
2259 /**
2260  * @description: This is the entry point for handling drag events
2261  * @return None
2262  */
HandleOnDragStatusCallback(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2263 void ScrollablePattern::HandleOnDragStatusCallback(
2264     const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2265 {
2266     HandleHotZone(dragEventType, notifyDragEvent);
2267 }
2268 
2269 /**
2270  * @description: Cancel registration with the drag drop manager
2271  * @return None
2272  */
UnRegister2DragDropManager()2273 void ScrollablePattern::UnRegister2DragDropManager()
2274 {
2275     auto host = GetHost();
2276     CHECK_NULL_VOID(host);
2277     auto pipeline = PipelineContext::GetCurrentContext();
2278     CHECK_NULL_VOID(pipeline);
2279     auto dragDropManager = pipeline->GetDragDropManager();
2280     CHECK_NULL_VOID(dragDropManager);
2281     dragDropManager->UnRegisterDragStatusListener(host->GetId());
2282 }
2283 
NeedCoordinateScrollWithNavigation(double offset,int32_t source,const OverScrollOffset & overOffsets)2284 bool ScrollablePattern::NeedCoordinateScrollWithNavigation(
2285     double offset, int32_t source, const OverScrollOffset& overOffsets)
2286 {
2287     if (!navBarPattern_) {
2288         return false;
2289     }
2290     return (GreatNotEqual(overOffsets.start, 0.0) || navBarPattern_->CanCoordScrollUp(offset)) &&
2291            (axis_ == Axis::VERTICAL) && (source != SCROLL_FROM_ANIMATION_SPRING);
2292 }
2293 
AddHotZoneSenceInterface(SceneStatus scene)2294 void ScrollablePattern::AddHotZoneSenceInterface(SceneStatus scene)
2295 {
2296     CHECK_NULL_VOID(velocityMotion_);
2297     auto velocity = velocityMotion_->GetCurrentVelocity();
2298     NotifyFRCSceneInfo(SCROLL_IN_HOTZONE_SCENE, velocity, scene);
2299 }
2300 } // namespace OHOS::Ace::NG
2301