• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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/memory/ace_type.h"
21 #include "base/utils/utils.h"
22 #include "core/components/scroll/scrollable.h"
23 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
24 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr Color SELECT_FILL_COLOR = Color(0x1A000000);
30 constexpr Color SELECT_STROKE_COLOR = Color(0x33FFFFFF);
31 } // namespace
32 
SetAxis(Axis axis)33 void ScrollablePattern::SetAxis(Axis axis)
34 {
35     if (axis_ == axis) {
36         return;
37     }
38     axis_ = axis;
39     if (scrollBar_) {
40         scrollBar_->SetPositionMode(axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
41     }
42     auto gestureHub = GetGestureHub();
43     CHECK_NULL_VOID(gestureHub);
44     if (scrollableEvent_) {
45         gestureHub->RemoveScrollableEvent(scrollableEvent_);
46         scrollableEvent_->SetAxis(axis);
47         gestureHub->AddScrollableEvent(scrollableEvent_);
48     }
49     if (scrollEffect_) {
50         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
51         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
52     }
53 }
54 
GetGestureHub()55 RefPtr<GestureEventHub> ScrollablePattern::GetGestureHub()
56 {
57     auto host = GetHost();
58     CHECK_NULL_RETURN(host, nullptr);
59     auto hub = host->GetEventHub<EventHub>();
60     CHECK_NULL_RETURN(hub, nullptr);
61     return hub->GetOrCreateGestureEventHub();
62 }
63 
GetInputHub()64 RefPtr<InputEventHub> ScrollablePattern::GetInputHub()
65 {
66     auto host = GetHost();
67     CHECK_NULL_RETURN(host, nullptr);
68     auto hub = host->GetEventHub<EventHub>();
69     CHECK_NULL_RETURN(host, nullptr);
70     return hub->GetOrCreateInputEventHub();
71 }
72 
OnScrollCallback(float offset,int32_t source)73 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
74 {
75     if (source == SCROLL_FROM_START) {
76         return true;
77     }
78     return UpdateCurrentOffset(offset, source);
79 }
80 
DraggedDownScrollEndProcess()81 void ScrollablePattern::DraggedDownScrollEndProcess()
82 {
83     CHECK_NULL_VOID_NOLOG(navBarPattern_);
84     if (!navBarPattern_->GetDraggedDown() && isReactInParentMovement_) {
85         isReactInParentMovement_ = false;
86         navBarPattern_->OnCoordScrollEnd();
87     }
88 }
89 
ProcessNavBarReactOnStart()90 void ScrollablePattern::ProcessNavBarReactOnStart()
91 {
92     CHECK_NULL_VOID_NOLOG(navBarPattern_);
93     navBarPattern_->OnCoordScrollStart();
94 }
95 
ProcessNavBarReactOnUpdate(bool isDraggedDown,float offset)96 bool ScrollablePattern::ProcessNavBarReactOnUpdate(bool isDraggedDown, float offset)
97 {
98     CHECK_NULL_RETURN_NOLOG(navBarPattern_, true);
99     auto minTitle = navBarPattern_ ? navBarPattern_->GetIsMinTitle() : false;
100     navBarPattern_->OnCoordScrollUpdate(offset);
101     DraggedDownScrollEndProcess();
102     if (minTitle) {
103         return scrollEffect_ && scrollEffect_->IsNoneEffect();
104     }
105     return scrollEffect_ && scrollEffect_->IsSpringEffect();
106 }
107 
ProcessNavBarReactOnEnd()108 void ScrollablePattern::ProcessNavBarReactOnEnd()
109 {
110     CHECK_NULL_VOID_NOLOG(navBarPattern_);
111     navBarPattern_->OnCoordScrollEnd();
112 }
113 
OnScrollPosition(double offset,int32_t source)114 bool ScrollablePattern::OnScrollPosition(double offset, int32_t source)
115 {
116     auto isAtTop = (IsAtTop() && Positive(offset));
117     auto isDraggedDown = navBarPattern_ ? navBarPattern_->GetDraggedDown() : false;
118     auto isFullStatus = navBarPattern_ ? navBarPattern_->GetFullStatus() : false;
119     if (isAtTop && (source == SCROLL_FROM_ANIMATION_SPRING) && !isFullStatus) {
120         SetNavBarVelocity();
121     }
122     if ((isAtTop || isDraggedDown) && (source == SCROLL_FROM_UPDATE) && !isReactInParentMovement_ &&
123         (axis_ == Axis::VERTICAL)) {
124         isReactInParentMovement_ = true;
125         if (coordinationEvent_) {
126             auto onScrollStart = coordinationEvent_->GetOnScrollStartEvent();
127             if (onScrollStart) {
128                 onScrollStart();
129             }
130         }
131         ProcessNavBarReactOnStart();
132     }
133     if ((coordinationEvent_ || navBarPattern_) && source != SCROLL_FROM_UPDATE && isReactInParentMovement_) {
134         isReactInParentMovement_ = false;
135         if (coordinationEvent_) {
136             auto onScrollEnd = coordinationEvent_->GetOnScrollEndEvent();
137             if (onScrollEnd) {
138                 onScrollEnd();
139             }
140         }
141         ProcessNavBarReactOnEnd();
142     }
143     if (isReactInParentMovement_) {
144         ProcessNavBarReactOnUpdate(isDraggedDown, offset);
145         if (coordinationEvent_) {
146             auto onScroll = coordinationEvent_->GetOnScroll();
147             CHECK_NULL_RETURN(onScroll, false);
148             if (!onScroll(offset)) {
149                 isReactInParentMovement_ = false;
150                 return true;
151             }
152             return scrollEffect_ && scrollEffect_->IsSpringEffect();
153         }
154     }
155     if (source == SCROLL_FROM_START) {
156         SetParentScrollable();
157         GetParentNavigition();
158         StopScrollBarAnimatorByProxy();
159         AbortScrollAnimator();
160     } else if (!AnimateStoped()) {
161         return false;
162     }
163     return true;
164 }
165 
OnScrollEnd()166 void ScrollablePattern::OnScrollEnd()
167 {
168     if (isReactInParentMovement_) {
169         ProcessNavBarReactOnEnd();
170         if (coordinationEvent_) {
171             auto onScrollEnd = coordinationEvent_->GetOnScrollEndEvent();
172             if (onScrollEnd) {
173                 onScrollEnd();
174             }
175             isReactInParentMovement_ = false;
176             return;
177         }
178     }
179 }
180 
AddScrollEvent()181 void ScrollablePattern::AddScrollEvent()
182 {
183     auto host = GetHost();
184     CHECK_NULL_VOID(host);
185     auto gestureHub = GetGestureHub();
186     CHECK_NULL_VOID(gestureHub);
187     if (scrollableEvent_) {
188         gestureHub->RemoveScrollableEvent(scrollableEvent_);
189     }
190     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
191         auto pattern = weak.Upgrade();
192         CHECK_NULL_RETURN(pattern, false);
193         if (!pattern->OnScrollPosition(offset, source)) {
194             return false;
195         }
196         return pattern->OnScrollCallback(static_cast<float>(offset), source);
197     };
198     auto scrollable = MakeRefPtr<Scrollable>(std::move(scrollCallback), GetAxis());
199     scrollable->SetNodeId(host->GetAccessibilityId());
200     scrollable->Initialize(host->GetContext());
201 
202     auto scrollEnd = [weak = WeakClaim(this)]() {
203         auto pattern = weak.Upgrade();
204         CHECK_NULL_VOID(pattern);
205         pattern->OnScrollEnd();
206         pattern->OnScrollEndCallback();
207     };
208     scrollable->SetScrollEndCallback(std::move(scrollEnd));
209     scrollable->SetUnstaticFriction(friction_);
210 
211     auto func = [weak = AceType::WeakClaim(this)](double offset) -> OverScrollOffset {
212         auto pattern = weak.Upgrade();
213         if (pattern) {
214             return pattern->GetOverScrollOffset(offset);
215         }
216         return { 0, 0 };
217     };
218     scrollable->SetOverScrollOffsetCallback(std::move(func));
219     scrollable->SetNestedScrollOptions(nestedScroll_);
220 
221     auto scrollSnap = [weak = WeakClaim(this)](double targetOffset, double velocity) -> bool {
222         auto pattern = weak.Upgrade();
223         CHECK_NULL_RETURN(pattern, false);
224         return pattern->OnScrollSnapCallback(targetOffset, velocity);
225     };
226     scrollable->SetOnScrollSnapCallback(scrollSnap);
227 
228     auto calePredictSnapOffsetCallback = [weak = WeakClaim(this)](float delta) -> std::optional<float> {
229         auto pattern = weak.Upgrade();
230         std::optional<float> predictSnapOffset;
231         CHECK_NULL_RETURN_NOLOG(pattern, predictSnapOffset);
232         return pattern->CalePredictSnapOffset(delta);
233     };
234     scrollable->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
235 
236     auto needScrollSnapToSideCallback = [weak = WeakClaim(this)](float delta) -> bool {
237         auto pattern = weak.Upgrade();
238         CHECK_NULL_RETURN_NOLOG(pattern, false);
239         return pattern->NeedScrollSnapToSide(delta);
240     };
241     scrollable->SetNeedScrollSnapToSideCallback(std::move(needScrollSnapToSideCallback));
242 
243     scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
244     scrollableEvent_->SetScrollable(scrollable);
245     gestureHub->AddScrollableEvent(scrollableEvent_);
246 }
247 
SetEdgeEffect(EdgeEffect edgeEffect)248 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
249 {
250     auto gestureHub = GetGestureHub();
251     CHECK_NULL_VOID(gestureHub);
252     if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
253         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
254         scrollEffect_.Reset();
255     }
256     if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
257         auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
258         CHECK_NULL_VOID(springEffect);
259         springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
260             auto pattern = weak.Upgrade();
261             CHECK_NULL_RETURN_NOLOG(pattern, false);
262             return pattern->OutBoundaryCallback();
263         });
264         // add callback to springEdgeEffect
265         SetEdgeEffectCallback(springEffect);
266         scrollEffect_ = springEffect;
267         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
268     }
269     if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
270         auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
271         CHECK_NULL_VOID(fadeEdgeEffect);
272         fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
273             auto list = weakScroll.Upgrade();
274             CHECK_NULL_VOID_NOLOG(list);
275             auto host = list->GetHost();
276             CHECK_NULL_VOID_NOLOG(host);
277             host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
278         });
279         SetEdgeEffectCallback(fadeEdgeEffect);
280         fadeEdgeEffect->InitialEdgeEffect();
281         scrollEffect_ = fadeEdgeEffect;
282         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
283     }
284     auto scrollable = scrollableEvent_->GetScrollable();
285     CHECK_NULL_VOID_NOLOG(scrollable);
286     scrollable->SetEdgeEffect(edgeEffect);
287 }
288 
HandleEdgeEffect(float offset,int32_t source,const SizeF & size)289 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size)
290 {
291     bool isAtTop = IsAtTop();
292     bool isAtBottom = IsAtBottom();
293     // check edgeEffect is not springEffect
294     if (scrollEffect_ && scrollEffect_->IsFadeEffect() && (source == SCROLL_FROM_UPDATE ||
295         source == SCROLL_FROM_ANIMATION)) {    // handle edge effect
296         if ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset))) {
297             scrollEffect_->HandleOverScroll(GetAxis(), -offset, size);
298         }
299     }
300     if (!(scrollEffect_ && scrollEffect_->IsSpringEffect() && (source == SCROLL_FROM_UPDATE ||
301         source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING))) {
302         if (isAtTop && Positive(offset)) {
303             animateOverScroll_ = false;
304             return false;
305         }
306         if (isAtBottom && Negative(offset)) {
307             animateOverScroll_ = false;
308             return false;
309         }
310     }
311     animateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && (isAtTop || isAtBottom);
312     return true;
313 }
314 
RegisterScrollBarEventTask()315 void ScrollablePattern::RegisterScrollBarEventTask()
316 {
317     CHECK_NULL_VOID(scrollBar_);
318     auto host = GetHost();
319     CHECK_NULL_VOID(host);
320     auto gestureHub = GetGestureHub();
321     auto inputHub = GetInputHub();
322     CHECK_NULL_VOID(gestureHub);
323     CHECK_NULL_VOID(inputHub);
324     scrollBar_->SetGestureEvent();
325     scrollBar_->SetMouseEvent();
326     scrollBar_->SetHoverEvent();
327     scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
328         auto host = weak.Upgrade();
329         CHECK_NULL_VOID(host);
330         host->MarkNeedRenderOnly();
331     });
332     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
333         auto pattern = weak.Upgrade();
334         CHECK_NULL_RETURN(pattern, false);
335         return pattern->OnScrollCallback(static_cast<float>(offset), source);
336     };
337     scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
338     auto scrollEnd = [weak = WeakClaim(this)]() {
339         auto pattern = weak.Upgrade();
340         CHECK_NULL_VOID(pattern);
341         pattern->OnScrollEnd();
342         pattern->OnScrollEndCallback();
343     };
344     scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
345     auto calePredictSnapOffsetCallback = [weak = WeakClaim(this)](float delta) {
346         auto pattern = weak.Upgrade();
347         CHECK_NULL_RETURN(pattern, std::optional<float>());
348         return pattern->CalePredictSnapOffset(delta);
349     };
350     scrollBar_->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
351     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
352         auto pattern = weak.Upgrade();
353         CHECK_NULL_VOID(pattern);
354         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
355     };
356     scrollBar_->SetStartScrollSnapMotionCallback(std::move(startScrollSnapMotionCallback));
357 
358     gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
359     inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
360     inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
361     CHECK_NULL_VOID(scrollableEvent_);
362     scrollableEvent_->SetInBarRegionCallback(
363         [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
364             auto scrollBar = weak.Upgrade();
365             CHECK_NULL_RETURN_NOLOG(scrollBar, false);
366             if (source == SourceType::MOUSE) {
367                 return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
368             }
369             return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
370         }
371     );
372     scrollableEvent_->SetBarCollectTouchTargetCallback([weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))]
373         (const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result) {
374             auto scrollBar = weak.Upgrade();
375             CHECK_NULL_VOID_NOLOG(scrollBar);
376             scrollBar->OnCollectTouchTarget(coordinateOffset, getEventTargetImpl, result);
377         }
378     );
379 }
380 
SetScrollBar(DisplayMode displayMode)381 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
382 {
383     auto host = GetHost();
384     CHECK_NULL_VOID_NOLOG(host);
385     if (displayMode == DisplayMode::OFF) {
386         if (scrollBar_) {
387             auto gestureHub = GetGestureHub();
388             if (gestureHub) {
389                 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
390             }
391             scrollBar_.Reset();
392             if (scrollBarOverlayModifier_) {
393                 scrollBarOverlayModifier_->SetOpacity(0);
394             }
395         }
396         return;
397     }
398     DisplayMode oldDisplayMode = DisplayMode::OFF;
399     if (!scrollBar_) {
400         scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
401         // set the scroll bar style
402         if (GetAxis() == Axis::HORIZONTAL) {
403             scrollBar_->SetPositionMode(PositionMode::BOTTOM);
404         }
405         RegisterScrollBarEventTask();
406         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
407     } else {
408         oldDisplayMode = scrollBar_->GetDisplayMode();
409     }
410 
411     if (oldDisplayMode != displayMode) {
412         scrollBar_->SetDisplayMode(displayMode);
413         if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
414             scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
415         }
416         scrollBar_->ScheduleDisapplearDelayTask();
417     }
418     auto renderContext = host->GetRenderContext();
419     CHECK_NULL_VOID_NOLOG(renderContext);
420     if (renderContext->HasBorderRadius()) {
421         auto borderRadius = renderContext->GetBorderRadius().value();
422         if (!(borderRadius == scrollBar_->GetHostBorderRadius())) {
423             scrollBar_->SetHostBorderRadius(borderRadius);
424             scrollBar_->CalcReservedHeight();
425         }
426     }
427 }
428 
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)429 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
430 {
431     if (!property) {
432         SetScrollBar(DisplayMode::AUTO);
433         return;
434     }
435     auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
436     SetScrollBar(displayMode);
437     if (scrollBar_) {
438         auto barColor = property->GetScrollBarColor();
439         if (barColor) {
440             scrollBar_->SetForegroundColor(barColor.value());
441         }
442         auto barWidth = property->GetScrollBarWidth();
443         if (barWidth) {
444             scrollBar_->SetInactiveWidth(barWidth.value());
445             scrollBar_->SetNormalWidth(barWidth.value());
446             scrollBar_->SetActiveWidth(barWidth.value());
447             scrollBar_->SetTouchWidth(barWidth.value());
448             scrollBar_->SetIsUserNormalWidth(true);
449         } else {
450             scrollBar_->SetIsUserNormalWidth(false);
451         }
452     }
453 }
454 
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)455 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
456 {
457     // inner scrollbar, viewOffset is padding offset
458     if (scrollBar_) {
459         auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
460         bool scrollable = GreatNotEqual(estimatedHeight, mainSize) && IsScrollable();
461         if (scrollBar_->IsScrollable() != scrollable) {
462             scrollBar_->SetScrollable(scrollable);
463             if (scrollBarOverlayModifier_) {
464                 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
465             }
466             if (scrollable) {
467                 scrollBar_->ScheduleDisapplearDelayTask();
468             }
469         }
470         Offset scrollOffset = { offset, offset }; // fit for w/h switched.
471         scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
472         scrollBar_->MarkNeedRender();
473     }
474 
475     // outer scrollbar
476     if (scrollBarProxy_) {
477         estimatedHeight_ = estimatedHeight - (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
478         barOffset_ = -offset;
479         scrollBarProxy_->NotifyScrollBar(AceType::WeakClaim(this));
480     }
481 }
482 
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)483 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
484 {
485     CHECK_NULL_VOID(scrollBarProxy);
486     auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source) {
487         if (source != SCROLL_FROM_START) {
488             auto pattern = weak.Upgrade();
489             if (!pattern || pattern->GetAxis() == Axis::NONE) {
490                 return false;
491             }
492             return pattern->UpdateCurrentOffset(offset, SCROLL_FROM_BAR);
493         }
494         return true;
495     };
496     auto scrollStartCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
497         auto pattern = weak.Upgrade();
498         CHECK_NULL_RETURN(pattern, false);
499         pattern->OnScrollStartCallback();
500         return pattern->OnScrollCallback(static_cast<float>(offset), source);
501     };
502     auto scrollEndCallback = [weak = WeakClaim(this)]() {
503         auto pattern = weak.Upgrade();
504         CHECK_NULL_VOID(pattern);
505         pattern->OnScrollEnd();
506         pattern->OnScrollEndCallback();
507     };
508     auto calePredictSnapOffsetCallback = [weak = WeakClaim(this)](float delta) {
509         auto pattern = weak.Upgrade();
510         CHECK_NULL_RETURN(pattern, std::optional<float>());
511         return pattern->CalePredictSnapOffset(delta);
512     };
513     auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
514         auto pattern = weak.Upgrade();
515         CHECK_NULL_VOID(pattern);
516         pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
517     };
518     ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction), std::move(scrollStartCallback),
519         std::move(scrollEndCallback), std::move(calePredictSnapOffsetCallback),
520         std::move(startScrollSnapMotionCallback) };
521     scrollBarProxy->RegisterScrollableNode(nodeInfo);
522     scrollBarProxy_ = scrollBarProxy;
523 }
524 
CreateScrollBarOverlayModifier()525 void ScrollablePattern::CreateScrollBarOverlayModifier()
526 {
527     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedPaint());
528     CHECK_NULL_VOID(!scrollBarOverlayModifier_);
529     scrollBarOverlayModifier_ = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
530     scrollBarOverlayModifier_->SetRect(scrollBar_->GetActiveRect(), scrollBar_->GetBarRect());
531 }
532 
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)533 void ScrollablePattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
534 {
535     scrollBarOutBoundaryExtent_ = scrollBarOutBoundaryExtent;
536     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
537     scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
538 }
539 
SetNestedScroll(const NestedScrollOptions & nestedOpt)540 void ScrollablePattern::SetNestedScroll(const NestedScrollOptions& nestedOpt)
541 {
542     nestedScroll_ = nestedOpt;
543     CHECK_NULL_VOID_NOLOG(scrollableEvent_);
544     auto scrollable = scrollableEvent_->GetScrollable();
545     CHECK_NULL_VOID_NOLOG(scrollable);
546     scrollable->SetNestedScrollOptions(nestedScroll_);
547 }
548 
SetFriction(double friction)549 void ScrollablePattern::SetFriction(double friction)
550 {
551     if (LessOrEqual(friction, 0.0)) {
552         friction = FRICTION;
553     }
554     friction_ = friction;
555     CHECK_NULL_VOID_NOLOG(scrollableEvent_);
556     auto scrollable = scrollableEvent_->GetScrollable();
557     scrollable->SetUnstaticFriction(friction_);
558 }
559 
GetParentScrollable()560 RefPtr<ScrollablePattern> ScrollablePattern::GetParentScrollable()
561 {
562     auto host = GetHost();
563     CHECK_NULL_RETURN(host, nullptr);
564     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
565         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
566         if (!frameNode) {
567             continue;
568         }
569         auto pattern = frameNode->GetPattern<ScrollablePattern>();
570         if (!pattern) {
571             continue;
572         }
573         if (pattern->GetAxis() != GetAxis()) {
574             continue;
575         }
576         return pattern;
577     }
578     return nullptr;
579 }
580 
GetParentNavigition()581 void ScrollablePattern::GetParentNavigition()
582 {
583     if (navBarPattern_) {
584         return;
585     }
586     auto host = GetHost();
587     CHECK_NULL_VOID(host);
588     if ((host->GetTag() != V2::LIST_ETS_TAG) && (host->GetTag() != V2::GRID_ETS_TAG) &&
589         (host->GetTag() != V2::SCROLL_ETS_TAG)) {
590         return;
591     }
592     for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
593         RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
594         if (!frameNode) {
595             continue;
596         }
597         if ((frameNode->GetTag() == V2::LIST_ETS_TAG) || (frameNode->GetTag() == V2::GRID_ETS_TAG) ||
598             (frameNode->GetTag() == V2::SCROLL_ETS_TAG)) {
599             break;
600         }
601         navBarPattern_ = frameNode->GetPattern<NavBarPattern>();
602         if (!navBarPattern_) {
603             continue;
604         }
605         return;
606     }
607     navBarPattern_ = nullptr;
608     return;
609 }
610 
SetNavBarVelocity()611 void ScrollablePattern::SetNavBarVelocity()
612 {
613     CHECK_NULL_VOID(scrollableEvent_);
614     auto scrollable = scrollableEvent_->GetScrollable();
615     CHECK_NULL_VOID(scrollable);
616     auto currVelocity = scrollable->GetCurrentVelocity();
617     if (Positive(currVelocity)) {
618         CHECK_NULL_VOID(navBarPattern_);
619         navBarPattern_->NavBarMotion(currVelocity, FRICTION);
620     }
621 }
622 
SetParentScrollable()623 void ScrollablePattern::SetParentScrollable()
624 {
625     CHECK_NULL_VOID_NOLOG(scrollableEvent_);
626     CHECK_NULL_VOID_NOLOG(scrollableEvent_->GetScrollable());
627     if (nestedScroll_.NeedParent()) {
628         auto parent = GetParentScrollable();
629         CHECK_NULL_VOID_NOLOG(parent);
630         CHECK_NULL_VOID_NOLOG(parent->scrollableEvent_);
631         auto parentScrollable = parent->scrollableEvent_->GetScrollable();
632         scrollableEvent_->GetScrollable()->SetParent(parentScrollable);
633     } else {
634         scrollableEvent_->GetScrollable()->SetParent(nullptr);
635     }
636 }
637 
StopAnimate()638 void ScrollablePattern::StopAnimate()
639 {
640     if (!IsScrollableStopped()) {
641         StopScrollable();
642     }
643     if (animator_ && !animator_->IsStopped()) {
644         animator_->Stop();
645     }
646 }
647 
ScrollTo(float position)648 void ScrollablePattern::ScrollTo(float position)
649 {
650     StopAnimate();
651     UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
652 }
653 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth)654 void ScrollablePattern::AnimateTo(float position, float duration, const RefPtr<Curve>& curve, bool smooth)
655 {
656     LOGI("AnimateTo:%{public}f, duration:%{public}f", position, duration);
657     float currVelocity = 0.0f;
658     if (!IsScrollableStopped()) {
659         CHECK_NULL_VOID(scrollableEvent_);
660         auto scrollable = scrollableEvent_->GetScrollable();
661         CHECK_NULL_VOID(scrollable);
662         currVelocity = -scrollable->GetCurrentVelocity();
663         scrollAbort_ = true;
664         StopScrollable();
665     }
666     if (!animator_) {
667         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
668         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
669             auto pattern = weak.Upgrade();
670             CHECK_NULL_VOID_NOLOG(pattern);
671             pattern->OnAnimateStop();
672         });
673     } else if (!animator_->IsStopped()) {
674         if (springMotion_) {
675             currVelocity = springMotion_->GetCurrentVelocity();
676         }
677         scrollAbort_ = true;
678         animator_->Stop();
679     }
680     animator_->ClearInterpolators();
681 
682     if (smooth) {
683         PlaySpringAnimation(position, DEFAULT_SCROLL_TO_VELOCITY, DEFAULT_SCROLL_TO_MASS,
684             DEFAULT_SCROLL_TO_STIFFNESS, DEFAULT_SCROLL_TO_DAMPING);
685     } else if (AceType::InstanceOf<InterpolatingSpring>(curve)) {
686         auto springCurve = AceType::DynamicCast<InterpolatingSpring>(curve);
687         float velocity = springCurve->GetVelocity();
688         float mass = springCurve->GetMass();
689         float stiffness = springCurve->GetStiffness();
690         float damping = springCurve->GetDamping();
691         PlaySpringAnimation(position, velocity, mass, stiffness, damping);
692     } else if (AceType::InstanceOf<ResponsiveSpringMotion>(curve)) {
693         auto springCurve = AceType::DynamicCast<ResponsiveSpringMotion>(curve);
694         constexpr float PI = 3.1415926f;
695         float tmpStiffness = (2 * PI / springCurve->GetResponse());
696         float stiffness = tmpStiffness * tmpStiffness;
697         float damping = 4 * PI * springCurve->GetDampingRatio() / springCurve->GetResponse();
698         PlaySpringAnimation(position, currVelocity, 1.0f, stiffness, damping);
699     } else {
700         auto animation = AceType::MakeRefPtr<CurveAnimation<float>>(GetTotalOffset(), position, curve);
701         animation->AddListener([weakScroll = AceType::WeakClaim(this), position](float value) {
702             auto pattern = weakScroll.Upgrade();
703             CHECK_NULL_VOID_NOLOG(pattern);
704             if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - value, SCROLL_FROM_ANIMATION_CONTROLLER)) {
705                 if ((pattern->IsAtTop() && LessOrEqual(position, pattern->GetTotalOffset())) ||
706                     (pattern->IsAtBottom() && GreatOrEqual(position, pattern->GetTotalOffset()))) {
707                     pattern->animator_->Stop();
708                 }
709             }
710         });
711         animator_->AddInterpolator(animation);
712         animator_->SetDuration(static_cast<int32_t>(duration));
713         animator_->Play();
714     }
715 }
716 
PlaySpringAnimation(float position,float velocity,float mass,float stiffness,float damping)717 void ScrollablePattern::PlaySpringAnimation(
718     float position, float velocity, float mass, float stiffness, float damping)
719 {
720     auto start = GetTotalOffset();
721     const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
722     AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping);
723     if (!springMotion_) {
724         const RefPtr<SpringProperty> DEFAULT_OVER_SPRING_PROPERTY =
725             AceType::MakeRefPtr<SpringProperty>(mass, stiffness, damping);
726         springMotion_ = AceType::MakeRefPtr<SpringMotion>(start, position, velocity, DEFAULT_OVER_SPRING_PROPERTY);
727         CHECK_NULL_VOID(scrollableEvent_);
728         scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
729             auto pattern = weakScroll.Upgrade();
730             CHECK_NULL_RETURN_NOLOG(pattern, 0.0f);
731             CHECK_NULL_RETURN_NOLOG(pattern->springMotion_, 0.0f);
732             CHECK_NULL_RETURN_NOLOG(pattern->animator_, 0.0f);
733             auto velocity = pattern->animator_->IsStopped() ? 0.0f : pattern->springMotion_->GetCurrentVelocity();
734             return velocity;
735         });
736     } else {
737         springMotion_->Reset(start, position, velocity, DEFAULT_OVER_SPRING_PROPERTY);
738         springMotion_->ClearListeners();
739     }
740     springMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double position) {
741         auto pattern = weakScroll.Upgrade();
742         CHECK_NULL_VOID(pattern);
743         if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - position, SCROLL_FROM_ANIMATION_CONTROLLER)) {
744             pattern->animator_->Stop();
745         }
746     });
747     animator_->PlayMotion(springMotion_);
748 }
749 
OnAttachToFrameNode()750 void ScrollablePattern::OnAttachToFrameNode()
751 {
752     auto host = GetHost();
753     CHECK_NULL_VOID(host);
754     host->GetRenderContext()->SetClipToBounds(true);
755     host->GetRenderContext()->UpdateClipEdge(true);
756 }
757 
UninitMouseEvent()758 void ScrollablePattern::UninitMouseEvent()
759 {
760     if (!mouseEvent_) {
761         return;
762     }
763     auto host = GetHost();
764     CHECK_NULL_VOID(host);
765     auto mouseEventHub = host->GetOrCreateInputEventHub();
766     CHECK_NULL_VOID(mouseEventHub);
767     mouseEventHub->RemoveOnMouseEvent(mouseEvent_);
768     ClearMultiSelect();
769     ClearInvisibleItemsSelectedStatus();
770     isMouseEventInit_ = false;
771 }
772 
InitMouseEvent()773 void ScrollablePattern::InitMouseEvent()
774 {
775     auto host = GetHost();
776     CHECK_NULL_VOID(host);
777     auto mouseEventHub = host->GetOrCreateInputEventHub();
778     CHECK_NULL_VOID(mouseEventHub);
779     if (!mouseEvent_) {
780         auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
781             auto pattern = weak.Upgrade();
782             if (pattern) {
783                 pattern->HandleMouseEventWithoutKeyboard(info);
784             }
785         };
786         mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
787     }
788     mouseEventHub->AddOnMouseEvent(mouseEvent_);
789     isMouseEventInit_ = true;
790     auto pipeline = PipelineContext::GetCurrentContext();
791     CHECK_NULL_VOID(pipeline);
792     auto dragDropManager = pipeline->GetDragDropManager();
793     CHECK_NULL_VOID(dragDropManager);
794     dragDropManager->SetNotifyInDraggedCallback([wp = WeakClaim(this)]() {
795         auto pattern = wp.Upgrade();
796         if (pattern && pattern->mousePressed_) {
797             pattern->OnMouseRelease();
798         }
799     });
800 }
801 
ClearInvisibleItemsSelectedStatus()802 void ScrollablePattern::ClearInvisibleItemsSelectedStatus()
803 {
804     for (auto& item : itemToBeSelected_) {
805         item.second.FireSelectChangeEvent(false);
806     }
807     itemToBeSelected_.clear();
808 }
809 
HandleInvisibleItemsSelectedStatus(const RectF & selectedZone)810 void ScrollablePattern::HandleInvisibleItemsSelectedStatus(const RectF& selectedZone)
811 {
812     auto newRect = selectedZone;
813     auto startMainOffset = mouseStartOffset_.GetMainOffset(axis_);
814     auto endMainOffset = mouseEndOffset_.GetMainOffset(axis_);
815     if (LessNotEqual(startMainOffset, endMainOffset)) {
816         if (axis_ == Axis::VERTICAL) {
817             newRect.SetOffset(OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_));
818         } else {
819             newRect.SetOffset(OffsetF(totalOffsetOfMousePressed_, selectedZone.Top()));
820         }
821     } else {
822         if (axis_ == Axis::VERTICAL) {
823             newRect.SetOffset(
824                 OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset)));
825         } else {
826             newRect.SetOffset(
827                 OffsetF(totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset), selectedZone.Top()));
828         }
829     }
830 
831     for (auto& item : itemToBeSelected_) {
832         item.second.FireSelectChangeEvent(newRect.IsIntersectWith(item.second.rect));
833     }
834 }
835 
HandleMouseEventWithoutKeyboard(const MouseInfo & info)836 void ScrollablePattern::HandleMouseEventWithoutKeyboard(const MouseInfo& info)
837 {
838     if (info.GetButton() != MouseButton::LEFT_BUTTON) {
839         return;
840     }
841 
842     auto pipeline = PipelineContext::GetCurrentContext();
843     CHECK_NULL_VOID(pipeline);
844     auto manager = pipeline->GetDragDropManager();
845     CHECK_NULL_VOID(manager);
846     if (manager->IsDragged()) {
847         if (mousePressed_) {
848             OnMouseRelease();
849         }
850         return;
851     }
852 
853     auto mouseOffsetX = static_cast<float>(info.GetLocalLocation().GetX());
854     auto mouseOffsetY = static_cast<float>(info.GetLocalLocation().GetY());
855     if (info.GetAction() == MouseAction::PRESS) {
856         if (!IsItemSelected(info)) {
857             ClearMultiSelect();
858             ClearInvisibleItemsSelectedStatus();
859         }
860         mouseStartOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
861         mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
862         mousePressOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
863         totalOffsetOfMousePressed_ = mousePressOffset_.GetMainOffset(axis_) + GetTotalOffset();
864         mousePressed_ = true;
865         // do not select when click
866     } else if (info.GetAction() == MouseAction::MOVE) {
867         if (!mousePressed_) {
868             return;
869         }
870         lastMouseMove_ = info;
871         auto delta = OffsetF(mouseOffsetX, mouseOffsetY) - mousePressOffset_;
872         if (Offset(delta.GetX(), delta.GetY()).GetDistance() > DEFAULT_PAN_DISTANCE.ConvertToPx()) {
873             mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
874             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
875             MultiSelectWithoutKeyboard(selectedZone);
876             HandleInvisibleItemsSelectedStatus(selectedZone);
877         }
878         SelectWithScroll();
879     } else if (info.GetAction() == MouseAction::RELEASE) {
880         OnMouseRelease();
881     }
882 }
883 
SelectWithScroll()884 void ScrollablePattern::SelectWithScroll()
885 {
886     if (!IsScrollable()) {
887         return;
888     }
889     auto offset = GetOutOfScrollableOffset();
890     if (NearZero(offset)) {
891         return;
892     }
893 
894     if (AnimateRunning()) {
895         return;
896     }
897     if (!animator_) {
898         animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
899         animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
900             auto pattern = weak.Upgrade();
901             CHECK_NULL_VOID_NOLOG(pattern);
902             pattern->OnAnimateStop();
903         });
904     } else if (!animator_->IsStopped()) {
905         scrollAbort_ = true;
906         animator_->Stop();
907     }
908 
909     if (!selectMotion_) {
910         selectMotion_ = AceType::MakeRefPtr<SelectMotion>(offset, [weak = WeakClaim(this)]() -> bool {
911             auto pattern = weak.Upgrade();
912             CHECK_NULL_RETURN_NOLOG(pattern, true);
913             return pattern->ShouldSelectScrollBeStopped();
914         });
915         selectMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
916             auto pattern = weakScroll.Upgrade();
917             CHECK_NULL_VOID(pattern);
918             pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
919         });
920     } else {
921         selectMotion_->Reset(offset);
922     }
923 
924     animator_->PlayMotion(selectMotion_);
925 }
926 
OnMouseRelease()927 void ScrollablePattern::OnMouseRelease()
928 {
929     mouseStartOffset_.Reset();
930     mouseEndOffset_.Reset();
931     mousePressed_ = false;
932     ClearSelectedZone();
933     lastMouseMove_.SetLocalLocation(Offset::Zero());
934 }
935 
ClearSelectedZone()936 void ScrollablePattern::ClearSelectedZone()
937 {
938     DrawSelectedZone(RectF());
939 }
940 
ComputeSelectedZone(const OffsetF & startOffset,const OffsetF & endOffset)941 RectF ScrollablePattern::ComputeSelectedZone(const OffsetF& startOffset, const OffsetF& endOffset)
942 {
943     RectF selectedZone;
944     if (startOffset.GetX() <= endOffset.GetX()) {
945         if (startOffset.GetY() <= endOffset.GetY()) {
946             // bottom right
947             selectedZone = RectF(startOffset.GetX(), startOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
948                 endOffset.GetY() - startOffset.GetY());
949         } else {
950             // top right
951             selectedZone = RectF(startOffset.GetX(), endOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
952                 startOffset.GetY() - endOffset.GetY());
953         }
954     } else {
955         if (startOffset.GetY() <= endOffset.GetY()) {
956             // bottom left
957             selectedZone = RectF(endOffset.GetX(), startOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
958                 endOffset.GetY() - startOffset.GetY());
959         } else {
960             // top left
961             selectedZone = RectF(endOffset.GetX(), endOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
962                 startOffset.GetY() - endOffset.GetY());
963         }
964     }
965 
966     return selectedZone;
967 }
968 
DrawSelectedZone(const RectF & selectedZone)969 void ScrollablePattern::DrawSelectedZone(const RectF& selectedZone)
970 {
971     auto host = GetHost();
972     CHECK_NULL_VOID(host);
973     auto hostContext = host->GetRenderContext();
974     CHECK_NULL_VOID(hostContext);
975     hostContext->UpdateMouseSelectWithRect(selectedZone, SELECT_FILL_COLOR, SELECT_STROKE_COLOR);
976 }
977 
MarkSelectedItems()978 void ScrollablePattern::MarkSelectedItems()
979 {
980     if (multiSelectable_ && mousePressed_) {
981         auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
982         if (!selectedZone.IsEmpty()) {
983             MultiSelectWithoutKeyboard(selectedZone);
984             HandleInvisibleItemsSelectedStatus(selectedZone);
985         }
986     }
987 }
988 
ShouldSelectScrollBeStopped()989 bool ScrollablePattern::ShouldSelectScrollBeStopped()
990 {
991     if (!mousePressed_) {
992         return true;
993     }
994     auto offset = GetOutOfScrollableOffset();
995     if (NearZero(offset)) {
996         return true;
997     }
998 
999     // avoid start position move when offset is bigger then item height
1000     auto currentMainStartOffset = mouseStartOffset_.GetMainOffset(axis_);
1001     if (Positive(offset)) {
1002         if (LessNotEqual(totalOffsetOfMousePressed_, currentMainStartOffset + offset)) {
1003             offset = totalOffsetOfMousePressed_ - currentMainStartOffset;
1004         }
1005     } else {
1006         auto hostSize = GetHostFrameSize();
1007         CHECK_NULL_RETURN_NOLOG(hostSize.has_value(), true);
1008         auto minStartOffset = -(GetTotalHeight() - totalOffsetOfMousePressed_ - hostSize->MainSize(axis_));
1009         if (GreatNotEqual(minStartOffset, currentMainStartOffset + offset)) {
1010             offset = minStartOffset - currentMainStartOffset;
1011         }
1012     }
1013 
1014     if (axis_ == Axis::VERTICAL) {
1015         mouseStartOffset_.AddY(offset);
1016     } else {
1017         mouseStartOffset_.AddX(offset);
1018     }
1019     if (selectMotion_) {
1020         selectMotion_->Reset(offset);
1021     }
1022     return false;
1023 };
1024 
GetOutOfScrollableOffset() const1025 float ScrollablePattern::GetOutOfScrollableOffset() const
1026 {
1027     auto offset = 0.0f;
1028     auto mouseMainOffset = static_cast<float>(
1029         axis_ == Axis::VERTICAL ? lastMouseMove_.GetLocalLocation().GetY() : lastMouseMove_.GetLocalLocation().GetX());
1030     auto hostSize = GetHostFrameSize();
1031     CHECK_NULL_RETURN_NOLOG(hostSize.has_value(), offset);
1032     auto mainTop = 0.0f;
1033     auto mainBottom = hostSize->MainSize(axis_);
1034     if (GreatOrEqual(mouseMainOffset, mainTop) && LessOrEqual(mouseMainOffset, mainBottom)) {
1035         return offset;
1036     }
1037     if (GreatNotEqual(mouseMainOffset, mainBottom)) {
1038         if (IsAtBottom()) {
1039             return offset;
1040         }
1041         offset = mainBottom - mouseMainOffset;
1042     }
1043     if (LessNotEqual(mouseMainOffset, mainTop)) {
1044         if (IsAtTop()) {
1045             return offset;
1046         }
1047         offset = mainTop - mouseMainOffset;
1048     }
1049     return offset;
1050 }
1051 } // namespace OHOS::Ace::NG
1052