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