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