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