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