1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scroll_bar/scroll_bar_pattern.h"
17 
18 #include "base/log/dump_log.h"
19 #include "core/components/common/layout/constants.h"
20 #include "core/components_ng/event/event_hub.h"
21 #include "core/components_ng/property/measure_utils.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 
24 namespace OHOS::Ace::NG {
25 namespace {
26 constexpr int32_t BAR_DISAPPEAR_DELAY_DURATION = 2000; // 2000ms
27 constexpr int32_t BAR_DISAPPEAR_DURATION = 300;        // 300ms
28 constexpr int32_t BAR_APPEAR_DURATION = 100;           // 100ms
29 constexpr int32_t BAR_DISAPPEAR_FRAME_RATE = 15;       // 15fps, the expected frame rate of opacity animation
30 constexpr int32_t BAR_DISAPPEAR_MIN_FRAME_RATE = 0;
31 constexpr int32_t BAR_DISAPPEAR_MAX_FRAME_RATE = 90;
32 constexpr int32_t SCROLL_BAR_LAYOUT_INFO_COUNT = 120;
33 constexpr int32_t LONG_PRESS_PAGE_INTERVAL_MS = 100;
34 constexpr int32_t LONG_PRESS_TIME_THRESHOLD_MS = 500;
35 } // namespace
36 
OnAttachToFrameNode()37 void ScrollBarPattern::OnAttachToFrameNode()
38 {
39     auto host = GetHost();
40     CHECK_NULL_VOID(host);
41 
42     host->GetRenderContext()->SetClipToFrame(true);
43 }
44 
SendAccessibilityEvent(AccessibilityEventType eventType)45 void ScrollBarPattern::SendAccessibilityEvent(AccessibilityEventType eventType)
46 {
47     auto frameNode = GetHost();
48     CHECK_NULL_VOID(frameNode);
49     frameNode->OnAccessibilityEvent(eventType);
50 }
51 
OnModifyDone()52 void ScrollBarPattern::OnModifyDone()
53 {
54     Pattern::OnModifyDone();
55     auto host = GetHost();
56     CHECK_NULL_VOID(host);
57     auto layoutProperty = host->GetLayoutProperty<ScrollBarLayoutProperty>();
58     CHECK_NULL_VOID(layoutProperty);
59 
60     auto oldDisplayMode = displayMode_;
61     displayMode_ = layoutProperty->GetDisplayMode().value_or(DisplayMode::AUTO);
62     if (oldDisplayMode != displayMode_ && scrollBarProxy_) {
63         if (displayMode_ == DisplayMode::ON) {
64             StopDisappearAnimator();
65         } else if (displayMode_ == DisplayMode::AUTO) {
66             StartDisappearAnimator();
67         }
68     }
69     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
70     if (axis_ == axis && scrollableEvent_) {
71         return;
72     }
73     axis_ = axis;
74     // scrollPosition callback
75     scrollPositionCallback_ = [weak = WeakClaim(this)](double offset, int32_t source) {
76         auto pattern = weak.Upgrade();
77         CHECK_NULL_RETURN(pattern, false);
78         if (source == SCROLL_FROM_START) {
79             pattern->StopDisappearAnimator();
80             // AccessibilityEventType::SCROLL_START
81             return true;
82         }
83         return pattern->UpdateCurrentOffset(offset, source);
84     };
85     scrollEndCallback_ = [weak = WeakClaim(this)]() {
86         auto pattern = weak.Upgrade();
87         CHECK_NULL_VOID(pattern);
88         if (pattern->GetDisplayMode() == DisplayMode::AUTO) {
89             pattern->StartDisappearAnimator();
90         }
91         // AccessibilityEventType::SCROLL_END
92     };
93 
94     auto hub = host->GetEventHub<EventHub>();
95     CHECK_NULL_VOID(hub);
96     auto gestureHub = hub->GetOrCreateGestureEventHub();
97     CHECK_NULL_VOID(gestureHub);
98     if (scrollableEvent_) {
99         gestureHub->RemoveScrollableEvent(scrollableEvent_);
100     }
101     scrollableEvent_ = MakeRefPtr<ScrollableEvent>(axis);
102     scrollableEvent_->SetInBarRegionCallback([weak = AceType::WeakClaim(this)]
103         (const PointF& point, SourceType source) {
104             auto scrollBarPattern = weak.Upgrade();
105             CHECK_NULL_RETURN(scrollBarPattern, false);
106             if (!scrollBarPattern->HasChild()
107                 && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
108                 auto scrollBar = scrollBarPattern->scrollBar_;
109                 CHECK_NULL_RETURN(scrollBar, false);
110                 if (source == SourceType::MOUSE) {
111                     return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
112                 }
113                 return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
114             } else {
115                 return scrollBarPattern->childRect_.IsInRegion(point);
116             }
117         }
118     );
119     scrollableEvent_->SetBarCollectTouchTargetCallback(
120         [weak = AceType::WeakClaim(this)](const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl,
121             TouchTestResult& result, const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
122             ResponseLinkResult& responseLinkResult) {
123             auto scrollBarPattern = weak.Upgrade();
124             CHECK_NULL_VOID(scrollBarPattern);
125             if (!scrollBarPattern->HasChild()
126                 && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
127                 auto scrollBar = scrollBarPattern->scrollBar_;
128                 CHECK_NULL_VOID(scrollBar);
129                 scrollBar->OnCollectTouchTarget(
130                     coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
131             } else {
132                 scrollBarPattern->OnCollectTouchTarget(
133                     coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
134             }
135         });
136 
137     SetBarCollectClickAndLongPressTargetCallback();
138     SetInBarRectRegionCallback();
139     gestureHub->AddScrollableEvent(scrollableEvent_);
140     SetAccessibilityAction();
141     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
142         SetScrollBar(DisplayMode::ON);
143     }
144     if (!panRecognizer_) {
145         InitPanRecognizer();
146     }
147     InitMouseEvent();
148     if (!clickRecognizer_) {
149         InitClickEvent();
150     }
151     if (!longPressRecognizer_) {
152         InitLongPressEvent();
153     }
154 }
155 
SetBarCollectClickAndLongPressTargetCallback()156 void ScrollBarPattern::SetBarCollectClickAndLongPressTargetCallback()
157 {
158     CHECK_NULL_VOID(scrollableEvent_);
159     scrollableEvent_->SetBarCollectClickAndLongPressTargetCallback(
160         [weak = AceType::WeakClaim(this)](const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl,
161             TouchTestResult& result, const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
162             ResponseLinkResult& responseLinkResult) {
163             auto scrollBar = weak.Upgrade();
164             CHECK_NULL_VOID(scrollBar);
165             scrollBar->OnCollectClickTarget(
166                 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
167             scrollBar->OnCollectLongPressTarget(
168                 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
169         });
170 }
171 
SetInBarRectRegionCallback()172 void ScrollBarPattern::SetInBarRectRegionCallback()
173 {
174     CHECK_NULL_VOID(scrollableEvent_);
175     scrollableEvent_->SetInBarRectRegionCallback(
176         [weak = AceType::WeakClaim(this)](const PointF& point, SourceType source) {
177             auto scrollBar = weak.Upgrade();
178             CHECK_NULL_RETURN(scrollBar, false);
179             return scrollBar->IsInScrollBar();
180         });
181 }
182 
SetScrollBar(DisplayMode displayMode)183 void ScrollBarPattern::SetScrollBar(DisplayMode displayMode)
184 {
185     auto host = GetHost();
186     CHECK_NULL_VOID(host);
187     if (displayMode == DisplayMode::OFF) {
188         if (scrollBar_) {
189             auto gestureHub = GetGestureHub();
190             if (gestureHub) {
191                 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
192             }
193             scrollBar_.Reset();
194             if (scrollBarOverlayModifier_) {
195                 scrollBarOverlayModifier_->SetOpacity(0);
196             }
197         }
198         return;
199     }
200     DisplayMode oldDisplayMode = DisplayMode::OFF;
201     if (!scrollBar_) {
202         scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
203         // set the scroll bar style
204         if (GetAxis() == Axis::HORIZONTAL) {
205             scrollBar_->SetPositionMode(PositionMode::BOTTOM);
206             if (scrollBarOverlayModifier_) {
207                 scrollBarOverlayModifier_->SetPositionMode(PositionMode::BOTTOM);
208             }
209         }
210         RegisterScrollBarEventTask();
211         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
212     } else {
213         oldDisplayMode = scrollBar_->GetDisplayMode();
214     }
215 
216     if (oldDisplayMode != displayMode) {
217         scrollBar_->SetDisplayMode(displayMode);
218         if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
219             scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
220         }
221         scrollBar_->ScheduleDisappearDelayTask();
222     }
223 }
224 
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)225 void ScrollBarPattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
226 {
227     CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
228     scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent));
229 }
230 
UpdateScrollBarOffset()231 void ScrollBarPattern::UpdateScrollBarOffset()
232 {
233     CHECK_NULL_VOID(scrollBar_);
234     auto host = GetHost();
235     CHECK_NULL_VOID(host);
236     auto geometryNode = host->GetGeometryNode();
237     auto viewSize = geometryNode->GetFrameSize();
238 
239     auto layoutProperty = host->GetLayoutProperty<ScrollBarLayoutProperty>();
240     CHECK_NULL_VOID(layoutProperty);
241     auto estimatedHeight = GetControlDistance() + (GetAxis() == Axis::VERTICAL ? viewSize.Height() : viewSize.Width());
242 
243     UpdateScrollBarRegion(scrollableNodeOffset_, estimatedHeight,
244         Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f));
245 }
246 
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)247 void ScrollBarPattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
248 {
249     // outer scrollbar, viewOffset is padding offset
250     if (scrollBar_) {
251         auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
252         bool scrollable = GreatNotEqual(estimatedHeight, mainSize);
253         if (scrollBar_->IsScrollable() != scrollable) {
254             scrollBar_->SetScrollable(scrollable);
255             if (scrollBarOverlayModifier_) {
256                 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
257             }
258             if (scrollable) {
259                 scrollBar_->ScheduleDisappearDelayTask();
260             }
261         }
262         Offset scrollOffset = { offset, offset };
263         scrollBar_->SetReverse(IsReverse());
264         scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
265         scrollBar_->MarkNeedRender();
266     }
267 }
268 
RegisterScrollBarEventTask()269 void ScrollBarPattern::RegisterScrollBarEventTask()
270 {
271     CHECK_NULL_VOID(scrollBar_);
272     auto host = GetHost();
273     CHECK_NULL_VOID(host);
274     auto gestureHub = GetGestureHub();
275     auto inputHub = GetInputHub();
276     CHECK_NULL_VOID(gestureHub);
277     CHECK_NULL_VOID(inputHub);
278     scrollBar_->SetGestureEvent();
279     scrollBar_->SetMouseEvent();
280     scrollBar_->SetHoverEvent();
281     scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
282         auto host = weak.Upgrade();
283         CHECK_NULL_VOID(host);
284         host->MarkNeedRenderOnly();
285     });
286 
287     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
288         auto pattern = weak.Upgrade();
289         CHECK_NULL_RETURN(pattern, false);
290         pattern->scrollBarProxy_->NotifyScrollBarNode(offset, source);
291         pattern->scrollPositionCallback_(0.0, SCROLL_FROM_START);
292         return true;
293     };
294     scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
295 
296     auto scrollEnd = [weak = WeakClaim(this)]() {
297         auto pattern = weak.Upgrade();
298         CHECK_NULL_VOID(pattern);
299         pattern->scrollBarProxy_->NotifyScrollStop();
300     };
301     scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
302 
303     gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
304     inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
305     inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
306 }
307 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)308 bool ScrollBarPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
309 {
310     if (config.skipMeasure && config.skipLayout) {
311         return false;
312     }
313     bool updateFlag = false;
314     if (!HasChild() && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
315         updateFlag = true;
316     } else {
317         auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
318         CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
319         auto layoutAlgorithm = DynamicCast<ScrollBarLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
320         CHECK_NULL_RETURN(layoutAlgorithm, false);
321         scrollableDistance_ = layoutAlgorithm->GetScrollableDistance();
322     }
323     if (displayMode_ != DisplayMode::OFF) {
324         updateFlag = UpdateScrollBarDisplay() || updateFlag;
325     }
326     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
327         updateFlag = CheckChildState() || updateFlag;
328     }
329     return updateFlag;
330 }
331 
OnColorConfigurationUpdate()332 void ScrollBarPattern::OnColorConfigurationUpdate()
333 {
334     CHECK_NULL_VOID(scrollBar_);
335     auto pipelineContext = GetContext();
336     CHECK_NULL_VOID(pipelineContext);
337     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
338     CHECK_NULL_VOID(theme);
339     scrollBar_->SetForegroundColor(theme->GetForegroundColor());
340     scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
341 }
342 
UpdateScrollBarDisplay()343 bool ScrollBarPattern::UpdateScrollBarDisplay()
344 {
345     auto host = GetHost();
346     CHECK_NULL_RETURN(host, false);
347     auto renderContext = host->GetRenderContext();
348     CHECK_NULL_RETURN(renderContext, false);
349     if (controlDistanceChanged_) {
350         controlDistanceChanged_ = false;
351         if (!Positive(controlDistance_)) {
352             SetOpacity(0);
353             return true;
354         }
355         SetOpacity(UINT8_MAX);
356         if (displayMode_ == DisplayMode::AUTO) {
357             StartDisappearAnimator();
358         }
359         return true;
360     }
361     if (!Positive(controlDistance_)) {
362         SetOpacity(0);
363         return true;
364     }
365     return false;
366 }
367 
IsInScrollBar()368 bool ScrollBarPattern::IsInScrollBar()
369 {
370     auto scrollBar = GetHost();
371     CHECK_NULL_RETURN(scrollBar, false);
372     auto scrollBarSize = scrollBar->GetGeometryNode()->GetFrameSize();
373     const bool isInVerticalScrollBar = (locationInfo_.GetX() >= 0 && locationInfo_.GetX() <= scrollBarSize.Width()) &&
374                                        (locationInfo_.GetY() >= 0 && locationInfo_.GetY() <= scrollBarSize.Height());
375 
376     return isInVerticalScrollBar;
377 }
378 
IsAtTop() const379 bool ScrollBarPattern::IsAtTop() const
380 {
381     return LessOrEqual(currentOffset_, 0.0);
382 }
383 
IsAtBottom() const384 bool ScrollBarPattern::IsAtBottom() const
385 {
386     return GreatOrEqual(currentOffset_, scrollableDistance_);
387 }
388 
ValidateOffset()389 void ScrollBarPattern::ValidateOffset()
390 {
391     if (scrollableDistance_ <= 0.0f) {
392         return;
393     }
394     currentOffset_ = std::clamp(currentOffset_, 0.0f, scrollableDistance_);
395 }
396 
UpdateCurrentOffset(float delta,int32_t source)397 bool ScrollBarPattern::UpdateCurrentOffset(float delta, int32_t source)
398 {
399     auto host = GetHost();
400     CHECK_NULL_RETURN(host, false);
401     if (NearZero(delta) || axis_ == Axis::NONE) {
402         return false;
403     }
404 
405     lastOffset_ = currentOffset_;
406     currentOffset_ += delta;
407     if (scrollBarProxy_ && lastOffset_ != currentOffset_) {
408         scrollBarProxy_->NotifyScrollableNode(-delta, source, AceType::WeakClaim(this));
409     }
410     AddScrollBarLayoutInfo();
411     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
412         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
413     } else {
414         host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
415     }
416     return true;
417 }
418 
AddScrollBarLayoutInfo()419 void ScrollBarPattern::AddScrollBarLayoutInfo()
420 {
421     if (outerScrollBarLayoutInfos_.size() >= SCROLL_BAR_LAYOUT_INFO_COUNT) {
422         outerScrollBarLayoutInfos_.pop_front();
423     }
424     outerScrollBarLayoutInfos_.push_back(OuterScrollBarLayoutInfo({
425         .layoutTime_ = GetSysTimestamp(),
426         .currentOffset_ = currentOffset_,
427         .scrollableNodeOffset_ = scrollableNodeOffset_,
428     }));
429 }
430 
GetAxisDumpInfo()431 void ScrollBarPattern::GetAxisDumpInfo()
432 {
433     switch (axis_) {
434         case Axis::NONE: {
435             DumpLog::GetInstance().AddDesc("Axis: NONE");
436             break;
437         }
438         case Axis::VERTICAL: {
439             DumpLog::GetInstance().AddDesc("Axis: VERTICAL");
440             break;
441         }
442         case Axis::HORIZONTAL: {
443             DumpLog::GetInstance().AddDesc("Axis: HORIZONTAL");
444             break;
445         }
446         case Axis::FREE: {
447             DumpLog::GetInstance().AddDesc("Axis: FREE");
448             break;
449         }
450         default: {
451             break;
452         }
453     }
454 }
455 
GetDisplayModeDumpInfo()456 void ScrollBarPattern::GetDisplayModeDumpInfo()
457 {
458     switch (displayMode_) {
459         case DisplayMode::OFF: {
460             DumpLog::GetInstance().AddDesc("outerScrollBarState: OFF");
461             break;
462         }
463         case DisplayMode::AUTO: {
464             DumpLog::GetInstance().AddDesc("outerScrollBarState: AUTO");
465             break;
466         }
467         case DisplayMode::ON: {
468             DumpLog::GetInstance().AddDesc("outerScrollBarState: ON");
469             break;
470         }
471         default: {
472             break;
473         }
474     }
475 }
476 
GetPanDirectionDumpInfo()477 void ScrollBarPattern::GetPanDirectionDumpInfo()
478 {
479     if (panRecognizer_) {
480         switch (panRecognizer_->GetAxisDirection()) {
481             case Axis::NONE: {
482                 DumpLog::GetInstance().AddDesc("panDirection: NONE");
483                 break;
484             }
485             case Axis::VERTICAL: {
486                 DumpLog::GetInstance().AddDesc("panDirection: VERTICAL");
487                 break;
488             }
489             case Axis::HORIZONTAL: {
490                 DumpLog::GetInstance().AddDesc("panDirection: HORIZONTAL");
491                 break;
492             }
493             case Axis::FREE: {
494                 DumpLog::GetInstance().AddDesc("panDirection: FREE");
495                 break;
496             }
497             default: {
498                 break;
499             }
500         }
501     } else {
502         DumpLog::GetInstance().AddDesc("panDirection is null");
503     }
504 }
505 
DumpAdvanceInfo()506 void ScrollBarPattern::DumpAdvanceInfo()
507 {
508     GetAxisDumpInfo();
509     GetDisplayModeDumpInfo();
510     GetPanDirectionDumpInfo();
511     hasChild_ ? DumpLog::GetInstance().AddDesc("hasChild: true") : DumpLog::GetInstance().AddDesc("hasChild: false");
512     preFrameChildState_ ? DumpLog::GetInstance().AddDesc("preFrameChildState: true")
513                         : DumpLog::GetInstance().AddDesc("preFrameChildState: false");
514     enableNestedSorll_ ? DumpLog::GetInstance().AddDesc("enableNestedSorll: true")
515                        : DumpLog::GetInstance().AddDesc("enableNestedSorll: false");
516     if (!hasChild_ && scrollBar_) {
517         scrollBar_->DumpAdvanceInfo();
518     }
519     DumpLog::GetInstance().AddDesc(std::string("childRect: ").append(childRect_.ToString()));
520     DumpLog::GetInstance().AddDesc(std::string("scrollableDistance: ").append(std::to_string(scrollableDistance_)));
521     DumpLog::GetInstance().AddDesc(std::string("controlDistance_: ").append(std::to_string(controlDistance_)));
522     DumpLog::GetInstance().AddDesc("==========================outerScrollBarLayoutInfos==========================");
523     for (const auto& info : outerScrollBarLayoutInfos_) {
524         DumpLog::GetInstance().AddDesc(info.ToString());
525     }
526     DumpLog::GetInstance().AddDesc("==========================outerScrollBarLayoutInfos==========================");
527 }
528 
StartDisappearAnimator()529 void ScrollBarPattern::StartDisappearAnimator()
530 {
531     if (!Positive(controlDistance_)) {
532         return;
533     }
534     if (disapplearDelayTask_) {
535         disapplearDelayTask_.Cancel();
536     }
537     auto context = GetContext();
538     CHECK_NULL_VOID(context);
539     auto taskExecutor = context->GetTaskExecutor();
540     CHECK_NULL_VOID(taskExecutor);
541     SetOpacity(UINT8_MAX);
542     disapplearDelayTask_.Reset([weak = WeakClaim(this)] {
543         auto scrollBar = weak.Upgrade();
544         CHECK_NULL_VOID(scrollBar);
545         AnimationOption option;
546         if (!scrollBar->HasChild()
547             && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
548             option.SetCurve(Curves::SHARP);
549         } else {
550             option.SetCurve(Curves::FRICTION);
551         }
552         option.SetDuration(BAR_DISAPPEAR_DURATION);
553         option.SetFrameRateRange(AceType::MakeRefPtr<FrameRateRange>(
554             BAR_DISAPPEAR_MIN_FRAME_RATE, BAR_DISAPPEAR_MAX_FRAME_RATE, BAR_DISAPPEAR_FRAME_RATE));
555         auto disappearAnimation = AnimationUtils::StartAnimation(option, [weak]() {
556             auto scrollBar = weak.Upgrade();
557             CHECK_NULL_VOID(scrollBar);
558             scrollBar->SetOpacity(0);
559         });
560         scrollBar->SetDisappearAnimation(disappearAnimation);
561     });
562     taskExecutor->PostDelayedTask(disapplearDelayTask_, TaskExecutor::TaskType::UI, BAR_DISAPPEAR_DELAY_DURATION,
563         "ArkUIScrollBarDisappearAnimation");
564 }
565 
StopDisappearAnimator()566 void ScrollBarPattern::StopDisappearAnimator()
567 {
568     if (!Positive(controlDistance_)) {
569         return;
570     }
571     if (disapplearDelayTask_) {
572         disapplearDelayTask_.Cancel();
573     }
574     if (disappearAnimation_) {
575         AnimationUtils::StopAnimation(disappearAnimation_);
576     }
577     if (!HasChild()
578         && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
579         AnimationOption option;
580         option.SetCurve(Curves::SHARP);
581         option.SetDuration(BAR_APPEAR_DURATION);
582         option.SetFrameRateRange(AceType::MakeRefPtr<FrameRateRange>(
583         BAR_DISAPPEAR_MIN_FRAME_RATE, BAR_DISAPPEAR_MAX_FRAME_RATE, BAR_DISAPPEAR_FRAME_RATE));
584         AnimationUtils::StartAnimation(option, [weak = WeakClaim(this)]() {
585             auto scrollBar = weak.Upgrade();
586             CHECK_NULL_VOID(scrollBar);
587             scrollBar->SetOpacity(UINT8_MAX);
588         });
589     } else {
590         SetOpacity(UINT8_MAX);
591     }
592 }
593 
SetOpacity(uint8_t value)594 void ScrollBarPattern::SetOpacity(uint8_t value)
595 {
596     auto host = GetHost();
597     CHECK_NULL_VOID(host);
598     auto renderContext = host->GetRenderContext();
599     CHECK_NULL_VOID(renderContext);
600     opacity_ = value;
601     renderContext->UpdateOpacity(static_cast<double>(value) / UINT8_MAX);
602     host->MarkNeedRenderOnly();
603 }
604 
SetAccessibilityAction()605 void ScrollBarPattern::SetAccessibilityAction()
606 {
607     auto host = GetHost();
608     CHECK_NULL_VOID(host);
609     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
610     CHECK_NULL_VOID(accessibilityProperty);
611     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
612         const auto& pattern = weakPtr.Upgrade();
613         CHECK_NULL_VOID(pattern);
614         if (pattern->GetAxis() == Axis::NONE || pattern->GetScrollableDistance() == 0.0f) {
615             return;
616         }
617         pattern->UpdateCurrentOffset(pattern->GetChildOffset(), SCROLL_FROM_BAR);
618         // AccessibilityEventType::SCROLL_END
619     });
620 
621     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
622         const auto& pattern = weakPtr.Upgrade();
623         CHECK_NULL_VOID(pattern);
624         if (pattern->GetAxis() == Axis::NONE || pattern->GetScrollableDistance() == 0.0f) {
625             return;
626         }
627         pattern->UpdateCurrentOffset(-pattern->GetChildOffset(), SCROLL_FROM_BAR);
628         // AccessibilityEventType::SCROLL_END
629     });
630 }
631 
InitPanRecognizer()632 void ScrollBarPattern::InitPanRecognizer()
633 {
634     PanDirection panDirection;
635     panDirection.type = axis_ == Axis::HORIZONTAL ? PanDirection::HORIZONTAL : PanDirection::VERTICAL;
636     const static int32_t PLATFORM_VERSION_TEN = 10;
637     float distance = DEFAULT_PAN_DISTANCE.Value();
638     auto context = PipelineContext::GetCurrentContext();
639     if (context && (context->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN)) {
640         distance = DEFAULT_PAN_DISTANCE.ConvertToPx();
641     }
642     panRecognizer_ = MakeRefPtr<PanRecognizer>(1, panDirection, distance);
643     panRecognizer_->SetOnActionUpdate([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
644         auto scrollBar = weakBar.Upgrade();
645         if (scrollBar) {
646             scrollBar->HandleDragUpdate(info);
647         }
648     });
649     panRecognizer_->SetOnActionEnd([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
650         auto scrollBar = weakBar.Upgrade();
651         if (scrollBar) {
652             scrollBar->HandleDragEnd(info);
653         }
654     });
655     panRecognizer_->SetOnActionStart([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
656         auto scrollBar = weakBar.Upgrade();
657         if (scrollBar) {
658             scrollBar->HandleDragStart(info);
659         }
660     });
661     panRecognizer_->SetOnActionCancel([weakBar = AceType::WeakClaim(this)]() {
662         auto scrollBar = weakBar.Upgrade();
663         if (scrollBar) {
664             GestureEvent info;
665             scrollBar->HandleDragEnd(info);
666         }
667     });
668 }
669 
HandleDragStart(const GestureEvent & info)670 void ScrollBarPattern::HandleDragStart(const GestureEvent& info)
671 {
672     StopMotion();
673     SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
674     TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "outer scrollBar drag start");
675     ACE_SCOPED_TRACE("outer scrollBar HandleDragStart");
676     if (scrollPositionCallback_) {
677         if (scrollBarProxy_) {
678             scrollBarProxy_->NotifyScrollStart();
679             scrollBarProxy_->SetScrollSnapTrigger_(true);
680         }
681         scrollPositionCallback_(0, SCROLL_FROM_START);
682     }
683 }
684 
HandleDragUpdate(const GestureEvent & info)685 void ScrollBarPattern::HandleDragUpdate(const GestureEvent& info)
686 {
687     if (scrollPositionCallback_) {
688         auto offset = info.GetMainDelta();
689         if (IsReverse()) {
690             offset = -offset;
691         }
692         // The offset of the mouse wheel and gesture is opposite.
693         if (info.GetInputEventType() == InputEventType::AXIS && !NearZero(controlDistance_)) {
694             offset = - offset * scrollableDistance_ / controlDistance_;
695         }
696         ACE_SCOPED_TRACE("outer scrollBar HandleDragUpdate offset:%f", offset);
697         scrollPositionCallback_(offset, SCROLL_FROM_BAR);
698     }
699 }
700 
HandleDragEnd(const GestureEvent & info)701 void ScrollBarPattern::HandleDragEnd(const GestureEvent& info)
702 {
703     auto velocity = IsReverse() ? -info.GetMainVelocity() : info.GetMainVelocity();
704     TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "outer scrollBar drag end, velocity is %{public}f", velocity);
705     ACE_SCOPED_TRACE("outer scrollBar HandleDragEnd velocity:%f", velocity);
706     SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
707     if (NearZero(velocity) || info.GetInputEventType() == InputEventType::AXIS) {
708         if (scrollEndCallback_) {
709             if (scrollBarProxy_) {
710                 scrollBarProxy_->NotifyScrollStop();
711                 scrollBarProxy_->SetScrollSnapTrigger_(false);
712             }
713             scrollEndCallback_();
714         }
715         return;
716     }
717     frictionPosition_ = 0.0;
718     if (frictionMotion_) {
719         frictionMotion_->Reset(friction_, 0, velocity);
720     } else {
721         frictionMotion_ = AceType::MakeRefPtr<FrictionMotion>(friction_, 0, velocity);
722         frictionMotion_->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
723             auto scrollBar = weakBar.Upgrade();
724             CHECK_NULL_VOID(scrollBar);
725             scrollBar->ProcessFrictionMotion(value);
726         });
727     }
728     CHECK_NULL_VOID(!scrollBarProxy_ || !scrollBarProxy_->NotifySnapScroll(-(frictionMotion_->GetFinalPosition()),
729         velocity, GetScrollableDistance(), static_cast<float>(GetDragOffset())));
730     scrollBarProxy_->SetScrollSnapTrigger_(false);
731     if (!frictionController_) {
732         frictionController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
733         frictionController_->AddStopListener([weakBar = AceType::WeakClaim(this)]() {
734             auto scrollBar = weakBar.Upgrade();
735             CHECK_NULL_VOID(scrollBar);
736             scrollBar->ProcessFrictionMotionStop();
737         });
738     }
739     frictionController_->PlayMotion(frictionMotion_);
740 }
741 
ProcessFrictionMotion(double value)742 void ScrollBarPattern::ProcessFrictionMotion(double value)
743 {
744     if (scrollPositionCallback_) {
745         auto offset = value - frictionPosition_;
746         scrollPositionCallback_(offset, SCROLL_FROM_BAR_FLING);
747     }
748     frictionPosition_ = value;
749 }
750 
ProcessFrictionMotionStop()751 void ScrollBarPattern::ProcessFrictionMotionStop()
752 {
753     if (scrollEndCallback_) {
754         if (scrollBarProxy_) {
755             scrollBarProxy_->NotifyScrollStop();
756         }
757         scrollEndCallback_();
758     }
759 }
760 
OnCollectTouchTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)761 void ScrollBarPattern::OnCollectTouchTarget(const OffsetF& coordinateOffset,
762     const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
763     const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
764 {
765     if (panRecognizer_) {
766         panRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
767         panRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
768         panRecognizer_->SetNodeId(frameNode->GetId());
769         panRecognizer_->AttachFrameNode(frameNode);
770         panRecognizer_->SetTargetComponent(targetComponent);
771         panRecognizer_->SetIsSystemGesture(true);
772         panRecognizer_->SetRecognizerType(GestureTypeName::PAN_GESTURE);
773         result.emplace_front(panRecognizer_);
774         responseLinkResult.emplace_back(panRecognizer_);
775     }
776 }
777 
IsReverse() const778 bool ScrollBarPattern::IsReverse() const
779 {
780     return isReverse_;
781 }
782 
SetReverse(bool reverse)783 void ScrollBarPattern::SetReverse(bool reverse)
784 {
785     isReverse_ = reverse;
786 }
787 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const788 void ScrollBarPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
789 {
790     /* no fixed attr below, just return */
791     if (filter.IsFastFilter()) {
792         return;
793     }
794 
795     json->PutExtAttr("enableNestedScroll", enableNestedSorll_ ? "true" : "false", filter);
796 }
797 
OnCollectClickTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)798 void ScrollBarPattern::OnCollectClickTarget(const OffsetF& coordinateOffset,
799     const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
800     const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
801 {
802     if (clickRecognizer_) {
803         clickRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
804         clickRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
805         clickRecognizer_->SetNodeId(frameNode->GetId());
806         clickRecognizer_->AttachFrameNode(frameNode);
807         clickRecognizer_->SetTargetComponent(targetComponent);
808         clickRecognizer_->SetIsSystemGesture(true);
809         clickRecognizer_->SetRecognizerType(GestureTypeName::BOXSELECT);
810         clickRecognizer_->SetSysGestureJudge([](const RefPtr<GestureInfo>& gestureInfo,
811                                                  const std::shared_ptr<BaseGestureEvent>&) -> GestureJudgeResult {
812             auto inputEventType = gestureInfo->GetInputEventType();
813             TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "input event type:%{public}d", inputEventType);
814             return inputEventType == InputEventType::MOUSE_BUTTON ? GestureJudgeResult::CONTINUE
815                                                                   : GestureJudgeResult::REJECT;
816         });
817         result.emplace_front(clickRecognizer_);
818         responseLinkResult.emplace_back(clickRecognizer_);
819     }
820 }
821 
OnCollectLongPressTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)822 void ScrollBarPattern::OnCollectLongPressTarget(const OffsetF& coordinateOffset,
823     const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result,
824     const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
825     ResponseLinkResult& responseLinkResult)
826 {
827     if (longPressRecognizer_) {
828         longPressRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
829         longPressRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
830         longPressRecognizer_->SetNodeId(frameNode->GetId());
831         longPressRecognizer_->AttachFrameNode(frameNode);
832         longPressRecognizer_->SetTargetComponent(targetComponent);
833         longPressRecognizer_->SetIsSystemGesture(true);
834         longPressRecognizer_->SetRecognizerType(GestureTypeName::LONG_PRESS_GESTURE);
835         longPressRecognizer_->SetSysGestureJudge([](const RefPtr<GestureInfo>& gestureInfo,
836                                                      const std::shared_ptr<BaseGestureEvent>&) -> GestureJudgeResult {
837             const auto &inputEventType = gestureInfo->GetInputEventType();
838             TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "input event type:%{public}d", inputEventType);
839             return inputEventType == InputEventType::MOUSE_BUTTON ? GestureJudgeResult::CONTINUE
840                                                                   : GestureJudgeResult::REJECT;
841         });
842         result.emplace_front(longPressRecognizer_);
843         responseLinkResult.emplace_back(longPressRecognizer_);
844     }
845 }
846 
InitClickEvent()847 void ScrollBarPattern::InitClickEvent()
848 {
849     clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
850     clickRecognizer_->SetOnClick([weakBar = AceType::WeakClaim(this)](const ClickInfo&) {
851         auto scrollBar = weakBar.Upgrade();
852         if (scrollBar) {
853             scrollBar->HandleClickEvent();
854         }
855     });
856 }
857 
HandleClickEvent()858 void ScrollBarPattern::HandleClickEvent()
859 {
860     auto host = GetHost();
861     CHECK_NULL_VOID(host);
862     auto infoOffset = OffsetF(locationInfo_.GetX(), locationInfo_.GetY());
863     auto scrollBarTopOffset = OffsetF(childRect_.Left(), childRect_.Top());
864     auto scrollBarBottomOffset = OffsetF(childRect_.Right(), childRect_.Bottom());
865     if (infoOffset.GetMainOffset(axis_) < scrollBarTopOffset.GetMainOffset(axis_)) {
866         scrollBarProxy_->ScrollPage(true, true);
867     } else if (infoOffset.GetMainOffset(axis_) > scrollBarBottomOffset.GetMainOffset(axis_)) {
868         scrollBarProxy_->ScrollPage(false, true);
869     }
870 }
871 
InitLongPressEvent()872 void ScrollBarPattern::InitLongPressEvent()
873 {
874     longPressRecognizer_ = AceType::MakeRefPtr<LongPressRecognizer>(LONG_PRESS_TIME_THRESHOLD_MS, 1, false, false);
875     longPressRecognizer_->SetOnAction([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
876         auto scrollBar = weakBar.Upgrade();
877         if (scrollBar) {
878             scrollBar->HandleLongPress(true);
879         }
880     });
881 }
882 
HandleLongPress(bool smooth)883 void ScrollBarPattern::HandleLongPress(bool smooth)
884 {
885     bool reverse = false;
886     auto infoOffset = OffsetF(locationInfo_.GetX(), locationInfo_.GetY());
887     auto scrollBarTopOffset = OffsetF(childRect_.Left(), childRect_.Top());
888     auto scrollBarBottomOffset = OffsetF(childRect_.Right(), childRect_.Bottom());
889     if (infoOffset.GetMainOffset(axis_) < scrollBarTopOffset.GetMainOffset(axis_)) {
890         reverse = true;
891         if (scrollingDown_) {
892             return;
893         }
894         scrollingUp_ = true;
895         scrollingDown_ = false;
896     } else if (infoOffset.GetMainOffset(axis_) > scrollBarBottomOffset.GetMainOffset(axis_)) {
897         reverse = false;
898         if (scrollingUp_) {
899             return;
900         }
901         scrollingUp_ = false;
902         scrollingDown_ = true;
903     } else {
904         isMousePressed_ = false;
905         scrollingUp_ = false;
906         scrollingDown_ = false;
907     }
908     if (isMousePressed_ && IsInScrollBar()) {
909         scrollBarProxy_->ScrollPage(reverse, smooth);
910         StartLongPressEventTimer();
911     }
912 }
913 
ScheduleCaretLongPress()914 void ScrollBarPattern::ScheduleCaretLongPress()
915 {
916     auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
917     CHECK_NULL_VOID(context);
918     if (!context->GetTaskExecutor()) {
919         return;
920     }
921     auto taskExecutor = context->GetTaskExecutor();
922     CHECK_NULL_VOID(taskExecutor);
923     taskExecutor->PostDelayedTask(
924         [weak = WeakClaim(this)]() {
925             auto pattern = weak.Upgrade();
926             CHECK_NULL_VOID(pattern);
927             pattern->HandleLongPress(true);
928         },
929         TaskExecutor::TaskType::UI, LONG_PRESS_PAGE_INTERVAL_MS, "ArkUIScrollBarHandleLongPress");
930 }
931 
StartLongPressEventTimer()932 void ScrollBarPattern::StartLongPressEventTimer()
933 {
934     auto tmpHost = GetHost();
935     CHECK_NULL_VOID(tmpHost);
936     tmpHost->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
937     ScheduleCaretLongPress();
938 }
939 
InitMouseEvent()940 void ScrollBarPattern::InitMouseEvent()
941 {
942     CHECK_NULL_VOID(!mouseEvent_);
943     auto host = GetHost();
944     CHECK_NULL_VOID(host);
945     auto inputHub = host->GetOrCreateInputEventHub();
946     CHECK_NULL_VOID(inputHub);
947     auto mouseCallback = [weak = WeakClaim(this)](MouseInfo& info) {
948         auto pattern = weak.Upgrade();
949         CHECK_NULL_VOID(pattern);
950         if (info.GetButton() == MouseButton::LEFT_BUTTON && info.GetAction() == MouseAction::PRESS) {
951             pattern->isMousePressed_ = true;
952         } else {
953             pattern->isMousePressed_ = false;
954             pattern->scrollingUp_ = false;
955             pattern->scrollingDown_ = false;
956         }
957         pattern->locationInfo_ = info.GetLocalLocation();
958     };
959     mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseCallback));
960     inputHub->AddOnMouseEvent(mouseEvent_);
961 }
962 } // namespace OHOS::Ace::NG
963