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