• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scroll/inner/scroll_bar.h"
17 
18 #include <cmath>
19 
20 #include "base/utils/utils.h"
21 #include "core/animation/curve_animation.h"
22 #include "core/animation/curves.h"
23 #include "core/pipeline_ng/pipeline_context.h"
24 
25 namespace OHOS::Ace::NG {
26 namespace {
27 constexpr int32_t BAR_DISAPPRAE_DELAY_DURATION = 2000; // 2000ms
28 constexpr int32_t BAR_ADAPT_DURATION = 400;  // 400ms, scroll bar adapts to the size changes of components
29 constexpr double BAR_ADAPT_EPSLION = 1.0;
30 constexpr double FRICTION_VELOCITY_THRESHOLD = 100.0;
31 } // namespace
32 
ScrollBar()33 ScrollBar::ScrollBar()
34 {
35     InitTheme();
36 }
37 
ScrollBar(DisplayMode displayMode,ShapeMode shapeMode,PositionMode positionMode)38 ScrollBar::ScrollBar(DisplayMode displayMode,
39     ShapeMode shapeMode, PositionMode positionMode) : ScrollBar()
40 {
41     displayMode_ = displayMode;
42     shapeMode_ = shapeMode;
43     positionMode_ = positionMode;
44 }
45 
InitTheme()46 void ScrollBar::InitTheme()
47 {
48     auto pipelineContext = PipelineContext::GetCurrentContext();
49     CHECK_NULL_VOID(pipelineContext);
50     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
51     CHECK_NULL_VOID(theme);
52     SetInactiveWidth(theme->GetNormalWidth());
53     SetNormalWidth(theme->GetNormalWidth());
54     SetActiveWidth(theme->GetActiveWidth());
55     SetTouchWidth(theme->GetTouchWidth());
56     SetMinHeight(theme->GetMinHeight());
57     SetMinDynamicHeight(theme->GetMinDynamicHeight());
58     SetBackgroundColor(theme->GetBackgroundColor());
59     SetForegroundColor(theme->GetForegroundColor());
60     SetPadding(theme->GetPadding());
61     SetHoverWidth(theme);
62 }
63 
InBarTouchRegion(const Point & point) const64 bool ScrollBar::InBarTouchRegion(const Point& point) const
65 {
66     if (NeedScrollBar() && shapeMode_ == ShapeMode::RECT) {
67         return touchRegion_.IsInRegion(point);
68     }
69     return false;
70 }
71 
InBarHoverRegion(const Point & point) const72 bool ScrollBar::InBarHoverRegion(const Point& point) const
73 {
74     if (NeedScrollBar() && shapeMode_ == ShapeMode::RECT) {
75         return hoverRegion_.IsInRegion(point);
76     }
77     return false;
78 }
79 
FlushBarWidth()80 void ScrollBar::FlushBarWidth()
81 {
82     SetBarRegion(paintOffset_, viewPortSize_);
83     if (shapeMode_ == ShapeMode::RECT) {
84         SetRectTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
85     } else {
86         SetRoundTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
87     }
88 }
89 
UpdateScrollBarRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)90 void ScrollBar::UpdateScrollBarRegion(
91     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
92 {
93     // return if nothing changes to avoid changing opacity
94     if (!positionModeUpdate_ && !normalWidthUpdate_ && paintOffset_ == offset && viewPortSize_ == size &&
95         lastOffset_ == lastOffset && NearEqual(estimatedHeight_, estimatedHeight, 0.000001f)) {
96         return;
97     }
98     if (!NearZero(estimatedHeight)) {
99         paintOffset_ = offset;
100         viewPortSize_ = size;
101         lastOffset_ = lastOffset;
102         estimatedHeight_ = estimatedHeight;
103         opacity_ = UINT8_MAX;
104         SetBarRegion(offset, size);
105         if (shapeMode_ == ShapeMode::RECT) {
106             SetRectTrickRegion(offset, size, lastOffset, estimatedHeight);
107         } else {
108             SetRoundTrickRegion(offset, size, lastOffset, estimatedHeight);
109         }
110         positionModeUpdate_ = false;
111         normalWidthUpdate_ = false;
112     }
113 }
114 
UpdateActiveRectSize(double activeSize)115 void ScrollBar::UpdateActiveRectSize(double activeSize)
116 {
117     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
118         activeRect_.SetHeight(activeSize);
119         touchRegion_.SetHeight(activeSize);
120         hoverRegion_.SetHeight(activeSize);
121     } else if (positionMode_ == PositionMode::BOTTOM) {
122         activeRect_.SetWidth(activeSize);
123         touchRegion_.SetWidth(activeSize);
124         hoverRegion_.SetWidth(activeSize);
125     }
126 }
127 
UpdateActiveRectOffset(double activeMainOffset)128 void ScrollBar::UpdateActiveRectOffset(double activeMainOffset)
129 {
130     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
131         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Height());
132         activeRect_.SetTop(activeMainOffset);
133         touchRegion_.SetTop(activeMainOffset);
134         hoverRegion_.SetTop(activeMainOffset);
135     } else if (positionMode_ == PositionMode::BOTTOM) {
136         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Width());
137         activeRect_.SetLeft(activeMainOffset);
138         touchRegion_.SetLeft(activeMainOffset);
139         hoverRegion_.SetLeft(activeMainOffset);
140     }
141 }
142 
SetBarRegion(const Offset & offset,const Size & size)143 void ScrollBar::SetBarRegion(const Offset& offset, const Size& size)
144 {
145     double normalWidth = NormalizeToPx(normalWidth_);
146     if (shapeMode_ == ShapeMode::RECT) {
147         double height =
148             std::max(size.Height() - NormalizeToPx(startReservedHeight_) - NormalizeToPx(endReservedHeight_), 0.0);
149         if (positionMode_ == PositionMode::LEFT) {
150             barRect_ = Rect(0.0, 0.0, normalWidth, height) + offset;
151         } else if (positionMode_ == PositionMode::RIGHT) {
152             barRect_ =
153                 Rect(size.Width() - normalWidth - NormalizeToPx(padding_.Right()), 0.0, normalWidth, height) + offset;
154         } else if (positionMode_ == PositionMode::BOTTOM) {
155             auto scrollBarWidth =
156                 std::max(size.Width() - NormalizeToPx(startReservedHeight_) - NormalizeToPx(endReservedHeight_), 0.0);
157             barRect_ =
158                 Rect(0.0, size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()), scrollBarWidth, normalWidth) +
159                 offset;
160         }
161     }
162 }
163 
SetRectTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)164 void ScrollBar::SetRectTrickRegion(
165     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
166 {
167     double mainSize = (positionMode_ == PositionMode::BOTTOM ? size.Width() : size.Height());
168     barRegionSize_ = std::max(mainSize - NormalizeToPx(endReservedHeight_), 0.0);
169     if (LessOrEqual(estimatedHeight, 0.0)) {
170         return;
171     }
172     double activeSize = barRegionSize_ * mainSize / estimatedHeight - outBoundary_;
173     if (!NearEqual(mainSize, estimatedHeight)) {
174         if (!NearZero(outBoundary_)) {
175             activeSize = std::max(
176                 std::max(activeSize, NormalizeToPx(minHeight_) - outBoundary_), NormalizeToPx(minDynamicHeight_));
177         } else {
178             activeSize = std::max(activeSize, NormalizeToPx(minHeight_));
179         }
180         double normalWidth = NormalizeToPx(normalWidth_);
181         if (LessOrEqual(activeSize, normalWidth)) {
182             if (!inSpring_) {
183                 auto pipelineContext = PipelineContext::GetCurrentContext();
184                 CHECK_NULL_VOID(pipelineContext);
185                 auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
186                 CHECK_NULL_VOID(theme);
187                 normalWidth_ = theme->GetNormalWidth();
188                 normalWidth = NormalizeToPx(normalWidth_);
189             } else {
190                 activeSize = normalWidth;
191             }
192         }
193         double lastMainOffset =
194             std::max(positionMode_ == PositionMode::BOTTOM ? lastOffset.GetX() : lastOffset.GetY(), 0.0);
195         offsetScale_ = (barRegionSize_ - activeSize) / (estimatedHeight - mainSize);
196         double activeMainOffset = offsetScale_ * lastMainOffset + NormalizeToPx(startReservedHeight_);
197         bool canUseAnimation = !inSpring_ && !positionModeUpdate_;
198         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeSize);
199         double inactiveSize = 0.0;
200         double inactiveMainOffset = 0.0;
201         scrollableOffset_ = activeMainOffset;
202         if (positionMode_ == PositionMode::LEFT) {
203             inactiveSize = activeRect_.Height();
204             inactiveMainOffset = activeRect_.Top();
205             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
206                 activeRect_ =
207                     Rect(-NormalizeToPx(position_), activeRect_.Top(), normalWidth, activeRect_.Height()) + offset;
208             } else {
209                 activeRect_ = Rect(-NormalizeToPx(position_), activeMainOffset, normalWidth, activeSize) + offset;
210             }
211             if (isUserNormalWidth_) {
212                 touchRegion_ = activeRect_;
213                 hoverRegion_ = activeRect_;
214             } else {
215                 touchRegion_ = activeRect_ + Size(NormalizeToPx(touchWidth_), 0);
216                 hoverRegion_ = activeRect_ + Size(NormalizeToPx(hoverWidth_), 0);
217             }
218         } else if (positionMode_ == PositionMode::RIGHT) {
219             inactiveSize = activeRect_.Height();
220             inactiveMainOffset = activeRect_.Top();
221             double x = size.Width() - normalWidth - NormalizeToPx(padding_.Right()) + NormalizeToPx(position_);
222             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
223                 activeRect_ = Rect(x, activeRect_.Top(), normalWidth, activeRect_.Height()) + offset;
224             } else {
225                 activeRect_ = Rect(x, activeMainOffset, normalWidth, activeSize) + offset;
226             }
227             // Update the hot region
228             if (isUserNormalWidth_) {
229                 touchRegion_ = activeRect_;
230                 hoverRegion_ = activeRect_;
231             } else {
232                 touchRegion_ =
233                     activeRect_ -
234                     Offset(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()),
235                         0.0) +
236                     Size(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_), 0);
237                 hoverRegion_ =
238                     activeRect_ -
239                     Offset(NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()),
240                         0.0) +
241                     Size(NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_), 0);
242             }
243         } else if (positionMode_ == PositionMode::BOTTOM) {
244             inactiveSize = activeRect_.Width();
245             inactiveMainOffset = activeRect_.Left();
246             auto positionY = size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()) + NormalizeToPx(position_);
247             if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
248                 activeRect_ = Rect(activeRect_.Left(), positionY, activeRect_.Width(), normalWidth) + offset;
249             } else {
250                 activeRect_ = Rect(activeMainOffset, positionY, activeSize, normalWidth) + offset;
251             }
252             if (isUserNormalWidth_) {
253                 touchRegion_ = activeRect_;
254                 hoverRegion_ = activeRect_;
255             } else {
256                 auto hotRegionOffset = Offset(
257                     0.0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Bottom()));
258                 auto hotRegionSize = Size(0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_));
259                 touchRegion_ = activeRect_ - hotRegionOffset + hotRegionSize;
260 
261                 auto hoverRegionOffset = Offset(
262                     0.0, NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Bottom()));
263                 auto hoverRegionSize = Size(0, NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_));
264                 hoverRegion_ = activeRect_ - hoverRegionOffset + hoverRegionSize;
265             }
266         }
267         // If the scrollBar length changes, start the adaptation animation
268         if (!NearZero(inactiveSize) && !NearEqual(activeSize, inactiveSize, BAR_ADAPT_EPSLION) && canUseAnimation &&
269             !Negative(inactiveMainOffset)) {
270             PlayAdaptAnimation(activeSize, activeMainOffset, inactiveSize, inactiveMainOffset);
271         }
272     }
273 }
274 
SetRoundTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)275 void ScrollBar::SetRoundTrickRegion(
276     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
277 {
278     double diameter = std::min(size.Width(), size.Height());
279     if (!NearEqual(estimatedHeight, diameter)) {
280         double maxAngle = bottomAngle_ - topAngle_;
281         trickSweepAngle_ = std::max(diameter * maxAngle / estimatedHeight, minAngle_);
282         double lastOffsetY = std::max(lastOffset.GetY(), 0.0);
283         double trickStartAngle = (maxAngle - trickSweepAngle_) * lastOffsetY / (estimatedHeight - diameter);
284         trickStartAngle = std::clamp(0.0, trickStartAngle, maxAngle) - maxAngle * FACTOR_HALF;
285         if (positionMode_ == PositionMode::LEFT) {
286             if (trickStartAngle > 0.0) {
287                 trickStartAngle_ = STRAIGHT_ANGLE - trickStartAngle;
288             } else {
289                 trickStartAngle_ = -(trickStartAngle + STRAIGHT_ANGLE);
290             }
291             trickSweepAngle_ = -trickSweepAngle_;
292         } else {
293             trickStartAngle_ = trickStartAngle;
294         }
295     }
296 }
297 
NeedScrollBar() const298 bool ScrollBar::NeedScrollBar() const
299 {
300     return displayMode_ == DisplayMode::AUTO || displayMode_ == DisplayMode::ON;
301 }
302 
NeedPaint() const303 bool ScrollBar::NeedPaint() const
304 {
305     return NeedScrollBar() && isScrollable_;
306 }
307 
GetNormalWidthToPx() const308 double ScrollBar::GetNormalWidthToPx() const
309 {
310     return NormalizeToPx(normalWidth_);
311 }
312 
CalcPatternOffset(float scrollBarOffset) const313 float ScrollBar::CalcPatternOffset(float scrollBarOffset) const
314 {
315     if (!isDriving_ || NearZero(offsetScale_)) {
316         return scrollBarOffset;
317     }
318     return -scrollBarOffset / offsetScale_;
319 }
320 
NormalizeToPx(const Dimension & dimension) const321 double ScrollBar::NormalizeToPx(const Dimension& dimension) const
322 {
323     auto pipelineContext = PipelineContext::GetCurrentContext();
324     CHECK_NULL_RETURN(pipelineContext, 0.0);
325     return pipelineContext->NormalizeToPx(dimension);
326 }
327 
SetGestureEvent()328 void ScrollBar::SetGestureEvent()
329 {
330     if (!touchEvent_) {
331         touchEvent_ = MakeRefPtr<TouchEventImpl>([weak = WeakClaim(this)](const TouchEventInfo& info) {
332             auto scrollBar = weak.Upgrade();
333             CHECK_NULL_VOID_NOLOG(scrollBar && scrollBar->IsScrollable());
334             if (info.GetTouches().empty()) {
335                 return;
336             }
337             auto touch = info.GetTouches().front();
338             if (touch.GetTouchType() == TouchType::DOWN) {
339                 Point point(touch.GetLocalLocation().GetX(), touch.GetLocalLocation().GetY());
340                 bool inRegion = false;
341                 if (info.GetSourceDevice() == SourceType::TOUCH) {
342                     inRegion = scrollBar->InBarTouchRegion(point);
343                 } else if (info.GetSourceDevice() == SourceType::MOUSE) {
344                     inRegion = scrollBar->InBarHoverRegion(point);
345                 }
346                 scrollBar->SetPressed(inRegion);
347                 if (inRegion && !scrollBar->IsHover()) {
348                     scrollBar->PlayScrollBarGrowAnimation();
349                 }
350             }
351             if (info.GetTouches().front().GetTouchType() == TouchType::UP ||
352                 info.GetTouches().front().GetTouchType() == TouchType::CANCEL) {
353                 if (scrollBar->IsPressed() && !scrollBar->IsHover()) {
354                     scrollBar->PlayScrollBarShrinkAnimation();
355                     scrollBar->ScheduleDisapplearDelayTask();
356                 }
357                 scrollBar->SetPressed(false);
358             }
359         });
360     }
361     if (!panRecognizer_) {
362         InitPanRecognizer();
363     }
364 }
365 
SetMouseEvent()366 void ScrollBar::SetMouseEvent()
367 {
368     if (mouseEvent_) {
369         return;
370     }
371     mouseEvent_ = MakeRefPtr<InputEvent>([weak = WeakClaim(this)](MouseInfo& info) {
372         auto scrollBar = weak.Upgrade();
373         CHECK_NULL_VOID_NOLOG(scrollBar && scrollBar->IsScrollable());
374         Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
375         bool inRegion = scrollBar->InBarHoverRegion(point);
376         if (inRegion && !scrollBar->IsHover()) {
377             if (!scrollBar->IsPressed()) {
378                 scrollBar->PlayScrollBarGrowAnimation();
379             }
380             scrollBar->SetHover(true);
381         }
382         if (scrollBar->IsHover() && !inRegion) {
383             scrollBar->SetHover(false);
384             if (!scrollBar->IsPressed()) {
385                 scrollBar->PlayScrollBarShrinkAnimation();
386                 scrollBar->ScheduleDisapplearDelayTask();
387             }
388         }
389     });
390 }
391 
SetHoverEvent()392 void ScrollBar::SetHoverEvent()
393 {
394     CHECK_NULL_VOID_NOLOG(!hoverEvent_);
395     hoverEvent_ = MakeRefPtr<InputEvent>([weak = WeakClaim(this)](bool isHover) {
396         auto scrollBar = weak.Upgrade();
397         CHECK_NULL_VOID_NOLOG(scrollBar && scrollBar->IsScrollable());
398         if (scrollBar->IsHover() && !isHover) {
399             scrollBar->SetHover(false);
400             if (!scrollBar->IsPressed()) {
401                 scrollBar->PlayScrollBarShrinkAnimation();
402                 scrollBar->ScheduleDisapplearDelayTask();
403             }
404         }
405     });
406 }
407 
PlayAdaptAnimation(double activeSize,double activeMainOffset,double inactiveSize,double inactiveMainOffset)408 void ScrollBar::PlayAdaptAnimation(
409     double activeSize, double activeMainOffset, double inactiveSize, double inactiveMainOffset)
410 {
411     if (adaptAnimator_ && adaptAnimator_->IsRunning()) {
412         return;
413     }
414     if (!adaptAnimator_) {
415         adaptAnimator_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
416     }
417     adaptAnimator_->ClearInterpolators();
418     // Animate the mainSize of the ScrollBar
419     auto sizeAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(inactiveSize, activeSize, Curves::FRICTION);
420     sizeAnimation->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
421         auto scrollBar = weakBar.Upgrade();
422         if (scrollBar) {
423             scrollBar->UpdateActiveRectSize(value);
424             scrollBar->MarkNeedRender();
425         }
426     });
427     // Animate the mainOffset of the ScrollBar
428     auto offsetAnimation =
429         AceType::MakeRefPtr<CurveAnimation<double>>(inactiveMainOffset, activeMainOffset, Curves::FRICTION);
430     offsetAnimation->AddListener(
431         [weakBar = AceType::WeakClaim(this), inactiveMainOffset, activeMainOffset](double value) {
432             auto scrollBar = weakBar.Upgrade();
433             if (scrollBar) {
434                 auto top = scrollBar->GetPositionMode() == PositionMode::BOTTOM ? scrollBar->activeRect_.Left() :
435                     scrollBar->activeRect_.Top();
436                 if (NearEqual(top, activeMainOffset, 0.000001f) || NearEqual(top, inactiveMainOffset, 0.000001f)) {
437                     scrollBar->UpdateActiveRectOffset(value);
438                 } else {
439                     scrollBar->UpdateActiveRectOffset(value + scrollBar->scrollableOffset_ - activeMainOffset);
440                 }
441                 scrollBar->MarkNeedRender();
442             }
443         });
444     adaptAnimator_->AddInterpolator(sizeAnimation);
445     adaptAnimator_->AddInterpolator(offsetAnimation);
446     adaptAnimator_->SetDuration(BAR_ADAPT_DURATION);
447 
448     UpdateActiveRectSize(inactiveSize);
449     UpdateActiveRectOffset(inactiveMainOffset);
450 
451     adaptAnimator_->Play();
452 }
453 
CalcReservedHeight()454 void ScrollBar::CalcReservedHeight()
455 {
456     auto pipelineContext = PipelineContext::GetCurrentContext();
457     CHECK_NULL_VOID_NOLOG(pipelineContext);
458     const static int32_t PLATFORM_VERSION_TEN = 10;
459     if (pipelineContext->GetMinPlatformVersion() < PLATFORM_VERSION_TEN) {
460         auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
461         CHECK_NULL_VOID_NOLOG(theme);
462         startReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
463         endReservedHeight_ = theme->GetReservedHeight();
464         LOGD("scrollBar set reservedHeight by theme");
465         FlushBarWidth();
466         return;
467     }
468     float startRadius = 0.0;
469     float endRadius = 0.0;
470     float barMargin = 0.0;
471     float padding = 0.0;
472     float startRadiusHeight = 0.0;
473     float endRadiusHeight = 0.0;
474     switch (positionMode_) {
475         case PositionMode::LEFT:
476             startRadius = hostBorderRadius_.radiusTopLeft->ConvertToPx();
477             endRadius = hostBorderRadius_.radiusBottomLeft->ConvertToPx();
478             padding = NormalizeToPx(padding_.Left());
479             break;
480         case PositionMode::RIGHT:
481             startRadius = hostBorderRadius_.radiusTopRight->ConvertToPx();
482             endRadius = hostBorderRadius_.radiusBottomRight->ConvertToPx();
483             padding = NormalizeToPx(padding_.Right());
484             break;
485         case PositionMode::BOTTOM:
486             startRadius = hostBorderRadius_.radiusBottomLeft->ConvertToPx();
487             endRadius = hostBorderRadius_.radiusBottomRight->ConvertToPx();
488             padding = NormalizeToPx(padding_.Bottom());
489             break;
490         default:
491             break;
492     }
493     if (std::isnan(startRadius)) {
494         startRadius = 0.0f;
495     }
496     if (std::isnan(endRadius)) {
497         endRadius = 0.0f;
498     }
499     barMargin = padding + NormalizeToPx(normalWidth_) / 2;
500     if (LessOrEqual(startRadius, barMargin)) {
501         startReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
502     } else {
503         startRadiusHeight = startRadius - std::sqrt(2 * padding * startRadius - padding * padding);
504         startReservedHeight_ = Dimension(startRadiusHeight + (startRadius / barMargin), DimensionUnit::PX);
505     }
506 
507     if (LessOrEqual(endRadius, barMargin)) {
508         endReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
509     } else {
510         endRadiusHeight = endRadius - std::sqrt(2 * padding * endRadius - padding * padding);
511         endReservedHeight_ = Dimension(endRadiusHeight + (endRadius / barMargin), DimensionUnit::PX);
512     }
513     LOGD("scrollBar calculate reservedHeight, startReservedHeight_:%{public}f, endReservedHeight_:%{public}f",
514         startReservedHeight_.Value(), endReservedHeight_.Value());
515     FlushBarWidth();
516 }
517 
InitPanRecognizer()518 void ScrollBar::InitPanRecognizer()
519 {
520     PanDirection panDirection;
521     panDirection.type = positionMode_ == PositionMode::BOTTOM ? PanDirection::HORIZONTAL : PanDirection::VERTICAL;
522     panRecognizer_ = MakeRefPtr<PanRecognizer>(1, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
523     panRecognizer_->SetMouseDistance(DRAG_PAN_DISTANCE_MOUSE);
524     panRecognizer_->SetOnActionUpdate([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
525         auto scrollBar = weakBar.Upgrade();
526         if (scrollBar) {
527             scrollBar->HandleDragUpdate(info);
528         }
529     });
530     panRecognizer_->SetOnActionEnd([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
531         auto scrollBar = weakBar.Upgrade();
532         if (scrollBar) {
533             scrollBar->HandleDragEnd(info);
534         }
535     });
536     panRecognizer_->SetOnActionStart([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
537         auto scrollBar = weakBar.Upgrade();
538         if (scrollBar) {
539             scrollBar->HandleDragStart(info);
540         }
541     });
542 }
543 
HandleDragStart(const GestureEvent & info)544 void ScrollBar::HandleDragStart(const GestureEvent& info)
545 {
546     if (frictionController_ && frictionController_->IsRunning()) {
547         frictionController_->Stop();
548     }
549     if (scrollPositionCallback_) {
550         scrollPositionCallback_(0, SCROLL_FROM_START);
551     }
552     isDriving_ = true;
553 }
554 
HandleDragUpdate(const GestureEvent & info)555 void ScrollBar::HandleDragUpdate(const GestureEvent& info)
556 {
557     if (scrollPositionCallback_) {
558         auto offset = CalcPatternOffset(info.GetMainDelta());
559         scrollPositionCallback_(offset, SCROLL_FROM_BAR);
560     }
561 }
562 
HandleDragEnd(const GestureEvent & info)563 void ScrollBar::HandleDragEnd(const GestureEvent& info)
564 {
565     auto velocity = info.GetMainVelocity();
566     if (NearZero(velocity)) {
567         if (scrollEndCallback_) {
568             scrollEndCallback_();
569         }
570         isDriving_ = false;
571         return;
572     }
573     frictionPosition_ = 0.0;
574     if (frictionMotion_) {
575         frictionMotion_->Reset(friction_, 0, velocity, FRICTION_VELOCITY_THRESHOLD);
576     } else {
577         frictionMotion_ = AceType::MakeRefPtr<FrictionMotion>(friction_, 0, velocity, FRICTION_VELOCITY_THRESHOLD);
578         frictionMotion_->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
579             auto scrollBar = weakBar.Upgrade();
580             CHECK_NULL_VOID_NOLOG(scrollBar);
581             scrollBar->ProcessFrictionMotion(value);
582         });
583     }
584     if (calePredictSnapOffsetCallback_ && startScrollSnapMotionCallback_) {
585         auto predictSnapOffset = calePredictSnapOffsetCallback_(CalcPatternOffset(frictionMotion_->GetFinalPosition()));
586         // If snap scrolling, predictSnapOffset will has a value.
587         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
588             LOGD("ScrollBar::HandleDragEnd predictSnapOffset:%{public}f", predictSnapOffset.value());
589             startScrollSnapMotionCallback_(predictSnapOffset.value(), velocity);
590             return;
591         }
592     }
593 
594     if (!frictionController_) {
595         frictionController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
596         frictionController_->AddStopListener([weakBar = AceType::WeakClaim(this)]() {
597             auto scrollBar = weakBar.Upgrade();
598             CHECK_NULL_VOID_NOLOG(scrollBar);
599             scrollBar->ProcessFrictionMotionStop();
600         });
601     }
602     frictionController_->PlayMotion(frictionMotion_);
603 }
604 
ProcessFrictionMotion(double value)605 void ScrollBar::ProcessFrictionMotion(double value)
606 {
607     if (scrollPositionCallback_) {
608         auto offset = CalcPatternOffset(value - frictionPosition_);
609         if (!scrollPositionCallback_(offset, SCROLL_FROM_BAR_FLING)) {
610             if (frictionController_ && frictionController_->IsRunning()) {
611                 frictionController_->Stop();
612             }
613         }
614     }
615     frictionPosition_ = value;
616 }
617 
ProcessFrictionMotionStop()618 void ScrollBar::ProcessFrictionMotionStop()
619 {
620     if (scrollEndCallback_) {
621         scrollEndCallback_();
622     }
623     isDriving_ = false;
624 }
625 
OnCollectTouchTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result)626 void ScrollBar::OnCollectTouchTarget(const OffsetF& coordinateOffset,
627     const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result)
628 {
629     if (panRecognizer_) {
630         panRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
631         panRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
632         result.emplace_front(panRecognizer_);
633     }
634 }
635 
ScheduleDisapplearDelayTask()636 void ScrollBar::ScheduleDisapplearDelayTask()
637 {
638     if (displayMode_ == DisplayMode::AUTO && isScrollable_ && !isHover_ && !isPressed_) {
639         disapplearDelayTask_.Cancel();
640         auto context = PipelineContext::GetCurrentContext();
641         CHECK_NULL_VOID_NOLOG(context);
642         auto taskExecutor = context->GetTaskExecutor();
643         CHECK_NULL_VOID_NOLOG(taskExecutor);
644         disapplearDelayTask_.Reset([weak = WeakClaim(this)] {
645             auto scrollBar = weak.Upgrade();
646             CHECK_NULL_VOID_NOLOG(scrollBar);
647             scrollBar->PlayScrollBarEndAnimation();
648         });
649         taskExecutor->PostDelayedTask(disapplearDelayTask_, TaskExecutor::TaskType::UI, BAR_DISAPPRAE_DELAY_DURATION);
650     }
651 }
652 } // namespace OHOS::Ace::NG
653