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