• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/inner/scroll_bar.h"
17 
18 #include <cmath>
19 
20 #include "base/log/dump_log.h"
21 #include "base/utils/utils.h"
22 #include "core/animation/curve_animation.h"
23 #include "core/animation/curves.h"
24 #include "core/common/container.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr int32_t BAR_DISAPPRAE_DELAY_DURATION = 2000; // 2000ms
30 constexpr double BAR_ADAPT_EPSLION = 1.0;
31 constexpr int32_t SCROLL_BAR_LAYOUT_INFO_COUNT = 30;
32 } // namespace
33 
ScrollBar()34 ScrollBar::ScrollBar()
35 {
36     InitTheme();
37 }
38 
ScrollBar(DisplayMode displayMode,ShapeMode shapeMode,PositionMode positionMode)39 ScrollBar::ScrollBar(DisplayMode displayMode, 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::GetCurrentContextSafely();
49     CHECK_NULL_VOID(pipelineContext);
50     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
51     CHECK_NULL_VOID(theme);
52     themeNormalWidth_ = theme->GetNormalWidth();
53     SetInactiveWidth(themeNormalWidth_);
54     SetNormalWidth(themeNormalWidth_);
55     SetActiveWidth(theme->GetActiveWidth());
56     SetTouchWidth(theme->GetTouchWidth());
57     SetMinHeight(theme->GetMinHeight());
58     SetMinDynamicHeight(theme->GetMinDynamicHeight());
59     SetBackgroundColor(theme->GetBackgroundColor());
60     SetForegroundColor(theme->GetForegroundColor());
61     SetPadding(theme->GetPadding());
62     SetHoverWidth(theme);
63 }
64 
InBarTouchRegion(const Point & point) const65 bool ScrollBar::InBarTouchRegion(const Point& point) const
66 {
67     if (NeedPaint() && shapeMode_ == ShapeMode::RECT) {
68         return touchRegion_.IsInRegion(point);
69     }
70     return false;
71 }
72 
InBarHoverRegion(const Point & point) const73 bool ScrollBar::InBarHoverRegion(const Point& point) const
74 {
75     if (NeedPaint() && shapeMode_ == ShapeMode::RECT) {
76         return hoverRegion_.IsInRegion(point);
77     }
78     return false;
79 }
80 
InBarRectRegion(const Point & point) const81 bool ScrollBar::InBarRectRegion(const Point& point) const
82 {
83     if (NeedPaint() && shapeMode_ == ShapeMode::RECT) {
84         return barRect_.IsInRegion(point);
85     }
86     return false;
87 }
88 
FlushBarWidth()89 void ScrollBar::FlushBarWidth()
90 {
91     SetBarRegion(paintOffset_, viewPortSize_);
92     if (shapeMode_ == ShapeMode::RECT) {
93         SetRectTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
94     } else {
95         SetRoundTrickRegion(paintOffset_, viewPortSize_, lastOffset_, estimatedHeight_);
96     }
97 }
98 
UpdateScrollBarRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)99 void ScrollBar::UpdateScrollBarRegion(
100     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
101 {
102     // return if nothing changes to avoid changing opacity
103     if (!positionModeUpdate_ && !normalWidthUpdate_ && paintOffset_ == offset && viewPortSize_ == size &&
104         lastOffset_ == lastOffset && NearEqual(estimatedHeight_, estimatedHeight, 0.000001f) && !isReverseUpdate_) {
105         return;
106     }
107     if (!NearEqual(estimatedHeight_, estimatedHeight, 0.000001f) || viewPortSize_ != size) {
108         needAddLayoutInfo = true;
109     }
110     if (!NearZero(estimatedHeight)) {
111         paintOffset_ = offset;
112         viewPortSize_ = size;
113         lastOffset_ = lastOffset;
114         estimatedHeight_ = estimatedHeight;
115         SetBarRegion(offset, size);
116         if (shapeMode_ == ShapeMode::RECT) {
117             SetRectTrickRegion(offset, size, lastOffset, estimatedHeight);
118         } else {
119             SetRoundTrickRegion(offset, size, lastOffset, estimatedHeight);
120         }
121         positionModeUpdate_ = false;
122         normalWidthUpdate_ = false;
123         isReverseUpdate_ = false;
124     }
125     needAddLayoutInfo =false;
126 }
127 
UpdateActiveRectSize(double activeSize)128 void ScrollBar::UpdateActiveRectSize(double activeSize)
129 {
130     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
131         activeRect_.SetHeight(activeSize);
132         touchRegion_.SetHeight(activeSize);
133         hoverRegion_.SetHeight(activeSize);
134     } else if (positionMode_ == PositionMode::BOTTOM) {
135         activeRect_.SetWidth(activeSize);
136         touchRegion_.SetWidth(activeSize);
137         hoverRegion_.SetWidth(activeSize);
138     }
139 }
140 
UpdateActiveRectOffset(double activeMainOffset)141 void ScrollBar::UpdateActiveRectOffset(double activeMainOffset)
142 {
143     if (positionMode_ == PositionMode::LEFT || positionMode_ == PositionMode::RIGHT) {
144         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Height());
145         activeRect_.SetTop(activeMainOffset);
146         touchRegion_.SetTop(activeMainOffset);
147         hoverRegion_.SetTop(activeMainOffset);
148     } else if (positionMode_ == PositionMode::BOTTOM) {
149         activeMainOffset = std::min(activeMainOffset, barRegionSize_ - activeRect_.Width());
150         activeRect_.SetLeft(activeMainOffset);
151         touchRegion_.SetLeft(activeMainOffset);
152         hoverRegion_.SetLeft(activeMainOffset);
153     }
154 }
155 
SetBarRegion(const Offset & offset,const Size & size)156 void ScrollBar::SetBarRegion(const Offset& offset, const Size& size)
157 {
158     double normalWidth = NormalizeToPx(normalWidth_);
159     if (shapeMode_ == ShapeMode::RECT) {
160         double height =
161             std::max(size.Height() - NormalizeToPx(startReservedHeight_) - NormalizeToPx(endReservedHeight_), 0.0);
162         if (positionMode_ == PositionMode::LEFT) {
163             barRect_ = Rect(NormalizeToPx(padding_.Left()), 0.0, normalWidth, height) + offset;
164         } else if (positionMode_ == PositionMode::RIGHT) {
165             barRect_ =
166                 Rect(size.Width() - normalWidth - NormalizeToPx(padding_.Right()), 0.0, normalWidth, height) + offset;
167         } else if (positionMode_ == PositionMode::BOTTOM) {
168             auto scrollBarWidth =
169                 std::max(size.Width() - NormalizeToPx(startReservedHeight_) - NormalizeToPx(endReservedHeight_), 0.0);
170             barRect_ =
171                 Rect(0.0, size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()), scrollBarWidth, normalWidth) +
172                 offset;
173         }
174     }
175 }
176 
SetRectTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)177 void ScrollBar::SetRectTrickRegion(
178     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
179 {
180     double mainSize = (positionMode_ == PositionMode::BOTTOM ? size.Width() : size.Height());
181     barRegionSize_ = std::max(mainSize - NormalizeToPx(endReservedHeight_) - NormalizeToPx(startReservedHeight_), 0.0);
182     if (LessOrEqual(estimatedHeight, 0.0)) {
183         return;
184     }
185     double activeSize = barRegionSize_ * mainSize / estimatedHeight - outBoundary_;
186 
187     if (!NearZero(outBoundary_)) {
188         activeSize = std::max(
189             std::max(activeSize, NormalizeToPx(minHeight_) - outBoundary_), NormalizeToPx(minDynamicHeight_));
190     } else {
191         activeSize = std::max(activeSize, NormalizeToPx(minHeight_));
192     }
193     double normalWidth = NormalizeToPx(normalWidth_);
194     if (LessOrEqual(activeSize, normalWidth)) {
195         if (GreatNotEqual(normalWidth, mainSize)) {
196             normalWidth = NormalizeToPx(themeNormalWidth_);
197         } else {
198             activeSize = normalWidth;
199         }
200     }
201     double lastMainOffset =
202         std::max(positionMode_ == PositionMode::BOTTOM ? lastOffset.GetX() : lastOffset.GetY(), 0.0);
203     if (NearEqual(mainSize, estimatedHeight)) {
204         offsetScale_ = 0.0;
205     } else {
206         offsetScale_ = (barRegionSize_ - activeSize) / (estimatedHeight - mainSize);
207     }
208     // Avoid crossing the top or bottom boundary.
209     double activeMainOffset = std::min(offsetScale_ * lastMainOffset, barRegionSize_ - activeSize)
210                                 + NormalizeToPx(startReservedHeight_);
211     activeMainOffset = !isReverse_ ? activeMainOffset : barRegionSize_ - activeSize - activeMainOffset;
212     bool canUseAnimation = !isOutOfBoundary_ && !positionModeUpdate_;
213     double inactiveSize = 0.0;
214     double inactiveMainOffset = 0.0;
215     scrollableOffset_ = activeMainOffset;
216     if (positionMode_ == PositionMode::LEFT) {
217         inactiveSize = activeRect_.Height();
218         inactiveMainOffset = activeRect_.Top();
219         activeRect_ = Rect(-NormalizeToPx(position_) + NormalizeToPx(padding_.Left()),
220             activeMainOffset, normalWidth, activeSize) + offset;
221         if (isUserNormalWidth_) {
222             touchRegion_ = activeRect_;
223             hoverRegion_ = activeRect_;
224         } else {
225             touchRegion_ = activeRect_ + Size(NormalizeToPx(touchWidth_), 0);
226             hoverRegion_ = activeRect_ + Size(NormalizeToPx(hoverWidth_), 0);
227         }
228     } else if (positionMode_ == PositionMode::RIGHT) {
229         inactiveSize = activeRect_.Height();
230         inactiveMainOffset = activeRect_.Top();
231         double x = size.Width() - normalWidth - NormalizeToPx(padding_.Right()) + NormalizeToPx(position_);
232         activeRect_ = Rect(x, activeMainOffset, normalWidth, activeSize) + offset;
233         // Update the hot region
234         if (isUserNormalWidth_) {
235             touchRegion_ = activeRect_;
236             hoverRegion_ = activeRect_;
237         } else {
238             touchRegion_ =
239                 activeRect_ -
240                 Offset(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()),
241                     0.0) +
242                 Size(NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_), 0);
243             hoverRegion_ =
244                 activeRect_ -
245                 Offset(NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Right()),
246                     0.0) +
247                 Size(NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_), 0);
248         }
249     } else if (positionMode_ == PositionMode::BOTTOM) {
250         inactiveSize = activeRect_.Width();
251         inactiveMainOffset = activeRect_.Left();
252         auto positionY = size.Height() - normalWidth - NormalizeToPx(padding_.Bottom()) + NormalizeToPx(position_);
253         activeRect_ = Rect(activeMainOffset, positionY, activeSize, normalWidth) + offset;
254         if (isUserNormalWidth_) {
255             touchRegion_ = activeRect_;
256             hoverRegion_ = activeRect_;
257         } else {
258             auto hotRegionOffset = Offset(
259                 0.0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Bottom()));
260             auto hotRegionSize = Size(0, NormalizeToPx(touchWidth_) - NormalizeToPx(normalWidth_));
261             touchRegion_ = activeRect_ - hotRegionOffset + hotRegionSize;
262 
263             auto hoverRegionOffset = Offset(
264                 0.0, NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_) - NormalizeToPx(padding_.Bottom()));
265             auto hoverRegionSize = Size(0, NormalizeToPx(hoverWidth_) - NormalizeToPx(normalWidth_));
266             hoverRegion_ = activeRect_ - hoverRegionOffset + hoverRegionSize;
267         }
268     }
269     AddScrollBarLayoutInfo();
270     // If the scrollBar length changes, start the adaptation animation
271     if (!NearZero(inactiveSize) && !NearEqual(activeSize, inactiveSize, BAR_ADAPT_EPSLION) && canUseAnimation &&
272         !Negative(inactiveMainOffset) && !normalWidthUpdate_) {
273         PlayScrollBarAdaptAnimation();
274     } else {
275         needAdaptAnimation_ = false;
276     }
277 }
278 
SetRoundTrickRegion(const Offset & offset,const Size & size,const Offset & lastOffset,double estimatedHeight)279 void ScrollBar::SetRoundTrickRegion(
280     const Offset& offset, const Size& size, const Offset& lastOffset, double estimatedHeight)
281 {
282     double diameter = std::min(size.Width(), size.Height());
283     if (!NearEqual(estimatedHeight, diameter)) {
284         double maxAngle = bottomAngle_ - topAngle_;
285         trickSweepAngle_ = std::max(diameter * maxAngle / estimatedHeight, minAngle_);
286         double lastOffsetY = std::max(lastOffset.GetY(), 0.0);
287         double trickStartAngle = (maxAngle - trickSweepAngle_) * lastOffsetY / (estimatedHeight - diameter);
288         trickStartAngle = std::clamp(0.0, trickStartAngle, maxAngle) - maxAngle * FACTOR_HALF;
289         if (positionMode_ == PositionMode::LEFT) {
290             if (trickStartAngle > 0.0) {
291                 trickStartAngle_ = STRAIGHT_ANGLE - trickStartAngle;
292             } else {
293                 trickStartAngle_ = -(trickStartAngle + STRAIGHT_ANGLE);
294             }
295             trickSweepAngle_ = -trickSweepAngle_;
296         } else {
297             trickStartAngle_ = trickStartAngle;
298         }
299     }
300 }
301 
NeedScrollBar() const302 bool ScrollBar::NeedScrollBar() const
303 {
304     return displayMode_ == DisplayMode::AUTO || displayMode_ == DisplayMode::ON;
305 }
306 
NeedPaint() const307 bool ScrollBar::NeedPaint() const
308 {
309     return NeedScrollBar() && isScrollable_;
310 }
311 
GetNormalWidthToPx() const312 double ScrollBar::GetNormalWidthToPx() const
313 {
314     return NormalizeToPx(normalWidth_);
315 }
316 
CalcPatternOffset(float scrollBarOffset) const317 float ScrollBar::CalcPatternOffset(float scrollBarOffset) const
318 {
319     auto activeRectLength = positionMode_ == PositionMode::BOTTOM ? activeRect_.Width() : activeRect_.Height();
320     if (!isDriving_ || NearZero(barRegionSize_ - activeRectLength)) {
321         return scrollBarOffset;
322     }
323     auto mainSize = (positionMode_ == PositionMode::BOTTOM ? viewPortSize_.Width() : viewPortSize_.Height());
324     return -scrollBarOffset * (estimatedHeight_ - mainSize) / (barRegionSize_ - activeRectLength);
325 }
326 
NormalizeToPx(const Dimension & dimension) const327 double ScrollBar::NormalizeToPx(const Dimension& dimension) const
328 {
329     auto pipelineContext = PipelineContext::GetCurrentContext();
330     CHECK_NULL_RETURN(pipelineContext, 0.0);
331     return pipelineContext->NormalizeToPx(dimension);
332 }
333 
SetGestureEvent()334 void ScrollBar::SetGestureEvent()
335 {
336     if (!touchEvent_) {
337         touchEvent_ = MakeRefPtr<TouchEventImpl>([weak = WeakClaim(this)](const TouchEventInfo& info) {
338             auto scrollBar = weak.Upgrade();
339             CHECK_NULL_VOID(scrollBar && scrollBar->IsScrollable());
340             if (info.GetTouches().empty()) {
341                 return;
342             }
343             auto touch = info.GetTouches().front();
344             if (touch.GetTouchType() == TouchType::DOWN) {
345                 TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "inner scrollBar touch down, panDirection: %{public}u",
346                     scrollBar->GetPanDirection());
347                 Point point(touch.GetLocalLocation().GetX(), touch.GetLocalLocation().GetY());
348                 bool inRegion = false;
349                 if (info.GetSourceDevice() == SourceType::TOUCH) {
350                     inRegion = scrollBar->InBarTouchRegion(point);
351                 } else if (info.GetSourceDevice() == SourceType::MOUSE) {
352                     inRegion = scrollBar->InBarHoverRegion(point);
353                     scrollBar->MarkNeedRender();
354                 }
355                 if (!scrollBar->IsPressed()) {
356                     scrollBar->SetPressed(inRegion);
357                 }
358                 if (inRegion && !scrollBar->IsHover()) {
359                     scrollBar->PlayScrollBarGrowAnimation();
360                 }
361             }
362             if ((info.GetTouches().front().GetTouchType() == TouchType::UP ||
363                     info.GetTouches().front().GetTouchType() == TouchType::CANCEL) &&
364                     (info.GetTouches().size() <= 1)) {
365                 if (scrollBar->IsPressed() && !scrollBar->IsHover()) {
366                     scrollBar->PlayScrollBarShrinkAnimation();
367                     scrollBar->ScheduleDisappearDelayTask();
368                 }
369                 scrollBar->SetPressed(false);
370                 scrollBar->MarkNeedRender();
371             }
372         });
373     }
374     if (!panRecognizer_) {
375         InitPanRecognizer();
376     }
377 }
378 
SetMouseEvent()379 void ScrollBar::SetMouseEvent()
380 {
381     if (mouseEvent_) {
382         return;
383     }
384     mouseEvent_ = MakeRefPtr<InputEvent>([weak = WeakClaim(this)](MouseInfo& info) {
385         auto scrollBar = weak.Upgrade();
386         CHECK_NULL_VOID(scrollBar && scrollBar->IsScrollable());
387         Point point(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY());
388         bool inBarRegion = scrollBar->InBarRectRegion(point);
389         bool inHoverRegion = scrollBar->InBarHoverRegion(point);
390         if (inBarRegion) {
391             scrollBar->PlayScrollBarAppearAnimation();
392         } else if (!scrollBar->IsPressed()) {
393             scrollBar->ScheduleDisappearDelayTask();
394         }
395         if (inHoverRegion && !scrollBar->IsHover()) {
396             if (!scrollBar->IsPressed()) {
397                 scrollBar->PlayScrollBarGrowAnimation();
398             }
399             scrollBar->SetHover(true);
400         }
401         if (scrollBar->IsHover() && !inHoverRegion) {
402             scrollBar->SetHover(false);
403             if (!scrollBar->IsPressed()) {
404                 scrollBar->PlayScrollBarShrinkAnimation();
405             }
406         }
407     });
408 }
409 
SetHoverEvent()410 void ScrollBar::SetHoverEvent()
411 {
412     CHECK_NULL_VOID(!hoverEvent_);
413     hoverEvent_ = MakeRefPtr<InputEvent>([weak = WeakClaim(this)](bool isHover) {
414         auto scrollBar = weak.Upgrade();
415         CHECK_NULL_VOID(scrollBar && scrollBar->IsScrollable());
416         if (scrollBar->IsHover() && !isHover) {
417             scrollBar->SetHover(false);
418             if (!scrollBar->IsPressed()) {
419                 scrollBar->PlayScrollBarShrinkAnimation();
420                 scrollBar->ScheduleDisappearDelayTask();
421             }
422         }
423     });
424 }
425 
CalcReservedHeight()426 void ScrollBar::CalcReservedHeight()
427 {
428     auto pipelineContext = PipelineContext::GetCurrentContext();
429     CHECK_NULL_VOID(pipelineContext);
430     if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
431         auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
432         CHECK_NULL_VOID(theme);
433         startReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
434         endReservedHeight_ = theme->GetReservedHeight();
435         FlushBarWidth();
436         return;
437     }
438     float startRadius = 0.0;
439     float endRadius = 0.0;
440     float barMargin = 0.0;
441     float padding = 0.0;
442     float startRadiusHeight = 0.0;
443     float endRadiusHeight = 0.0;
444     switch (positionMode_) {
445         case PositionMode::LEFT:
446             startRadius = hostBorderRadius_.radiusTopLeft.value_or(Dimension()).ConvertToPx();
447             endRadius = hostBorderRadius_.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
448             padding = NormalizeToPx(padding_.Left());
449             break;
450         case PositionMode::RIGHT:
451             startRadius = hostBorderRadius_.radiusTopRight.value_or(Dimension()).ConvertToPx();
452             endRadius = hostBorderRadius_.radiusBottomRight.value_or(Dimension()).ConvertToPx();
453             padding = NormalizeToPx(padding_.Right());
454             break;
455         case PositionMode::BOTTOM:
456             startRadius = hostBorderRadius_.radiusBottomLeft.value_or(Dimension()).ConvertToPx();
457             endRadius = hostBorderRadius_.radiusBottomRight.value_or(Dimension()).ConvertToPx();
458             padding = NormalizeToPx(padding_.Bottom());
459             break;
460         default:
461             break;
462     }
463     if (std::isnan(startRadius)) {
464         startRadius = 0.0f;
465     }
466     if (std::isnan(endRadius)) {
467         endRadius = 0.0f;
468     }
469     barMargin = padding + NormalizeToPx(normalWidth_) / 2;
470     if (LessOrEqual(startRadius, barMargin)) {
471         startReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
472     } else {
473         startRadiusHeight = startRadius - std::sqrt(2 * padding * startRadius - padding * padding);
474         startReservedHeight_ = Dimension(startRadiusHeight + (startRadius / barMargin), DimensionUnit::PX);
475     }
476 
477     if (LessOrEqual(endRadius, barMargin)) {
478         endReservedHeight_ = Dimension(0.0, DimensionUnit::PX);
479     } else {
480         endRadiusHeight = endRadius - std::sqrt(2 * padding * endRadius - padding * padding);
481         endReservedHeight_ = Dimension(endRadiusHeight + (endRadius / barMargin), DimensionUnit::PX);
482     }
483     FlushBarWidth();
484 }
485 
InitPanRecognizer()486 void ScrollBar::InitPanRecognizer()
487 {
488     PanDirection panDirection;
489     panDirection.type = positionMode_ == PositionMode::BOTTOM ? PanDirection::HORIZONTAL : PanDirection::VERTICAL;
490     panRecognizer_ = MakeRefPtr<PanRecognizer>(1, panDirection, DEFAULT_PAN_DISTANCE.ConvertToPx());
491     panRecognizer_->SetMouseDistance(DRAG_PAN_DISTANCE_MOUSE.ConvertToPx());
492     panRecognizer_->SetOnActionUpdate([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
493         auto scrollBar = weakBar.Upgrade();
494         if (scrollBar) {
495             scrollBar->HandleDragUpdate(info);
496         }
497     });
498     panRecognizer_->SetOnActionEnd([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
499         auto scrollBar = weakBar.Upgrade();
500         if (scrollBar) {
501             scrollBar->HandleDragEnd(info);
502         }
503     });
504     panRecognizer_->SetOnActionStart([weakBar = AceType::WeakClaim(this)](const GestureEvent& info) {
505         auto scrollBar = weakBar.Upgrade();
506         if (scrollBar) {
507             scrollBar->HandleDragStart(info);
508         }
509     });
510     panRecognizer_->SetOnActionCancel([weakBar = AceType::WeakClaim(this)]() {
511         auto scrollBar = weakBar.Upgrade();
512         if (scrollBar) {
513             GestureEvent info;
514             scrollBar->HandleDragEnd(info);
515         }
516     });
517 }
518 
StopFlingAnimation()519 void ScrollBar::StopFlingAnimation()
520 {
521     if (frictionController_ && frictionController_->IsRunning()) {
522         frictionController_->Stop();
523     }
524 }
525 
HandleDragStart(const GestureEvent & info)526 void ScrollBar::HandleDragStart(const GestureEvent& info)
527 {
528     StopFlingAnimation();
529     TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "inner scrollBar drag start");
530     ACE_SCOPED_TRACE("inner scrollBar HandleDragStart");
531     if (scrollPositionCallback_) {
532         scrollPositionCallback_(0, SCROLL_FROM_START);
533         if (dragFRCSceneCallback_) {
534             dragFRCSceneCallback_(0, NG::SceneStatus::START);
535         }
536     }
537     SetDragStartPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
538     isDriving_ = true;
539 }
540 
HandleDragUpdate(const GestureEvent & info)541 void ScrollBar::HandleDragUpdate(const GestureEvent& info)
542 {
543     // if historical touch point slope is zero but delta is not zero, no need to update.
544     auto mainDelta = info.GetMainDelta();
545     if (info.IsInterpolated()) {
546         if (GetPanDirection() == Axis::VERTICAL && NearZero(info.GetInputYDeltaSlope()) && !NearZero(mainDelta)) {
547             return;
548         } else if (GetPanDirection() == Axis::HORIZONTAL && NearZero(info.GetInputXDeltaSlope()) &&
549                    !NearZero(mainDelta)) {
550             return;
551         }
552     }
553     if (scrollPositionCallback_) {
554         // The offset of the mouse wheel and gesture is opposite.
555         auto offset = info.GetInputEventType() == InputEventType::AXIS ?
556                       info.GetMainDelta() : CalcPatternOffset(info.GetMainDelta());
557         if (IsReverse()) {
558             offset = -offset;
559         }
560         ACE_SCOPED_TRACE("inner scrollBar HandleDragUpdate offset:%f", offset);
561         scrollPositionCallback_(offset, SCROLL_FROM_BAR);
562         if (dragFRCSceneCallback_) {
563             dragFRCSceneCallback_(NearZero(info.GetMainDelta()) ? info.GetMainVelocity()
564                                                                 : info.GetMainVelocity() / info.GetMainDelta() * offset,
565                 NG::SceneStatus::RUNNING);
566         }
567     }
568 }
569 
HandleDragEnd(const GestureEvent & info)570 void ScrollBar::HandleDragEnd(const GestureEvent& info)
571 {
572     if (dragFRCSceneCallback_) {
573         dragFRCSceneCallback_(0, NG::SceneStatus::END);
574     }
575     auto velocity = IsReverse() ? -info.GetMainVelocity() : info.GetMainVelocity();
576     TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "inner scrollBar drag end, velocity is %{public}f", velocity);
577     ACE_SCOPED_TRACE("inner scrollBar HandleDragEnd velocity:%f", velocity);
578     if (NearZero(velocity) || info.GetInputEventType() == InputEventType::AXIS) {
579         if (scrollEndCallback_) {
580             scrollEndCallback_();
581         }
582         isDriving_ = false;
583         return;
584     }
585     SetDragEndPosition(GetMainOffset(Offset(info.GetGlobalPoint().GetX(), info.GetGlobalPoint().GetY())));
586     frictionPosition_ = 0.0;
587     if (frictionMotion_) {
588         frictionMotion_->Reset(friction_, 0, velocity);
589     } else {
590         frictionMotion_ = AceType::MakeRefPtr<FrictionMotion>(friction_, 0, velocity);
591         frictionMotion_->AddListener([weakBar = AceType::WeakClaim(this)](double value) {
592             auto scrollBar = weakBar.Upgrade();
593             CHECK_NULL_VOID(scrollBar);
594             scrollBar->ProcessFrictionMotion(value);
595         });
596     }
597     if (calePredictSnapOffsetCallback_ && startScrollSnapMotionCallback_) {
598         auto predictSnapOffset = calePredictSnapOffsetCallback_(CalcPatternOffset(frictionMotion_->GetFinalPosition()),
599                                                                 CalcPatternOffset(GetDragOffset()), -velocity);
600         // If snap scrolling, predictSnapOffset will has a value.
601         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
602             startScrollSnapMotionCallback_(predictSnapOffset.value(), velocity);
603             return;
604         }
605     }
606 
607     if (!frictionController_) {
608         frictionController_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
609         frictionController_->AddStopListener([weakBar = AceType::WeakClaim(this)]() {
610             auto scrollBar = weakBar.Upgrade();
611             CHECK_NULL_VOID(scrollBar);
612             scrollBar->ProcessFrictionMotionStop();
613         });
614     }
615     frictionController_->PlayMotion(frictionMotion_);
616 }
617 
ProcessFrictionMotion(double value)618 void ScrollBar::ProcessFrictionMotion(double value)
619 {
620     if (scrollPositionCallback_) {
621         auto offset = CalcPatternOffset(value - frictionPosition_);
622         if (!scrollPositionCallback_(offset, SCROLL_FROM_BAR_FLING)) {
623             if (frictionController_ && frictionController_->IsRunning()) {
624                 frictionController_->Stop();
625             }
626         }
627     }
628     frictionPosition_ = value;
629 }
630 
ProcessFrictionMotionStop()631 void ScrollBar::ProcessFrictionMotionStop()
632 {
633     if (scrollEndCallback_) {
634         scrollEndCallback_();
635     }
636     isDriving_ = false;
637 }
638 
OnCollectTouchTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)639 void ScrollBar::OnCollectTouchTarget(const OffsetF& coordinateOffset, const GetEventTargetImpl& getEventTargetImpl,
640     TouchTestResult& result, const RefPtr<FrameNode>& frameNode, const RefPtr<TargetComponent>& targetComponent,
641     ResponseLinkResult& responseLinkResult)
642 {
643     if (panRecognizer_ && isScrollable_) {
644         panRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
645         panRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
646         panRecognizer_->SetNodeId(frameNode->GetId());
647         panRecognizer_->AttachFrameNode(frameNode);
648         panRecognizer_->SetTargetComponent(targetComponent);
649         panRecognizer_->SetIsSystemGesture(true);
650         panRecognizer_->SetRecognizerType(GestureTypeName::PAN_GESTURE);
651         result.emplace_front(panRecognizer_);
652         responseLinkResult.emplace_back(panRecognizer_);
653     }
654 }
655 
ScheduleDisappearDelayTask()656 void ScrollBar::ScheduleDisappearDelayTask()
657 {
658     if (displayMode_ == DisplayMode::AUTO && isScrollable_ && !isHover_) {
659         disappearDelayTask_.Cancel();
660         auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
661         CHECK_NULL_VOID(context);
662         auto taskExecutor = context->GetTaskExecutor();
663         CHECK_NULL_VOID(taskExecutor);
664         disappearDelayTask_.Reset([weak = WeakClaim(this)] {
665             auto scrollBar = weak.Upgrade();
666             CHECK_NULL_VOID(scrollBar);
667             scrollBar->PlayScrollBarDisappearAnimation();
668         });
669         taskExecutor->PostDelayedTask(disappearDelayTask_, TaskExecutor::TaskType::UI, BAR_DISAPPRAE_DELAY_DURATION,
670             "ArkUIScrollBarInnerDisappearAnimation");
671     }
672 }
673 
AddScrollBarLayoutInfo()674 void ScrollBar::AddScrollBarLayoutInfo()
675 {
676     CHECK_NULL_VOID(needAddLayoutInfo);
677     if (innerScrollBarLayoutInfos_.size() >= SCROLL_BAR_LAYOUT_INFO_COUNT) {
678         innerScrollBarLayoutInfos_.pop_front();
679     }
680     innerScrollBarLayoutInfos_.push_back(InnerScrollBarLayoutInfo({
681         .layoutTime_ = GetSysTimestamp(),
682         .viewPortSize_ = viewPortSize_,
683         .lastOffset_ = lastOffset_,
684         .estimatedHeight_ = estimatedHeight_,
685         .outBoundary_ = outBoundary_,
686         .activeRect_ = activeRect_,
687     }));
688 }
689 
GetShapeModeDumpInfo()690 void ScrollBar::GetShapeModeDumpInfo()
691 {
692     switch (shapeMode_) {
693         case ShapeMode::RECT: {
694             DumpLog::GetInstance().AddDesc("shapeMode: RECT");
695             break;
696         }
697         case ShapeMode::ROUND: {
698             DumpLog::GetInstance().AddDesc("shapeMode: ROUND");
699             break;
700         }
701         case ShapeMode::DEFAULT: {
702             DumpLog::GetInstance().AddDesc("shapeMode: DEFAULT");
703             break;
704         }
705         default: {
706             break;
707         }
708     }
709 }
710 
GetPositionModeDumpInfo()711 void ScrollBar::GetPositionModeDumpInfo()
712 {
713     switch (positionMode_) {
714         case PositionMode::RIGHT: {
715             DumpLog::GetInstance().AddDesc("positionMode: RIGHT");
716             DumpLog::GetInstance().AddDesc(std::string("padding.right: ").append(padding_.Right().ToString()));
717             break;
718         }
719         case PositionMode::LEFT: {
720             DumpLog::GetInstance().AddDesc("positionMode: LEFT");
721             DumpLog::GetInstance().AddDesc(std::string("padding.left: ").append(padding_.Left().ToString()));
722             break;
723         }
724         case PositionMode::BOTTOM: {
725             DumpLog::GetInstance().AddDesc("positionMode: BOTTOM");
726             DumpLog::GetInstance().AddDesc(std::string("padding.bottom: ").append(padding_.Bottom().ToString()));
727             break;
728         }
729         default: {
730             break;
731         }
732     }
733 }
734 
GetAxisDumpInfo()735 void ScrollBar::GetAxisDumpInfo()
736 {
737     switch (axis_) {
738         case Axis::NONE: {
739             DumpLog::GetInstance().AddDesc("axis: NONE");
740             break;
741         }
742         case Axis::VERTICAL: {
743             DumpLog::GetInstance().AddDesc("axis: VERTICAL");
744             break;
745         }
746         case Axis::HORIZONTAL: {
747             DumpLog::GetInstance().AddDesc("axis: HORIZONTAL");
748             break;
749         }
750         case Axis::FREE: {
751             DumpLog::GetInstance().AddDesc("axis: FREE");
752             break;
753         }
754         default: {
755             break;
756         }
757     }
758 }
759 
GetPanDirectionDumpInfo()760 void ScrollBar::GetPanDirectionDumpInfo()
761 {
762     if (panRecognizer_) {
763         switch (panRecognizer_->GetAxisDirection()) {
764             case Axis::NONE: {
765                 DumpLog::GetInstance().AddDesc("panDirection: NONE");
766                 break;
767             }
768             case Axis::VERTICAL: {
769                 DumpLog::GetInstance().AddDesc("panDirection: VERTICAL");
770                 break;
771             }
772             case Axis::HORIZONTAL: {
773                 DumpLog::GetInstance().AddDesc("panDirection: HORIZONTAL");
774                 break;
775             }
776             case Axis::FREE: {
777                 DumpLog::GetInstance().AddDesc("panDirection: FREE");
778                 break;
779             }
780             default: {
781                 break;
782             }
783         }
784     } else {
785         DumpLog::GetInstance().AddDesc("panDirection is null");
786     }
787 }
788 
DumpAdvanceInfo()789 void ScrollBar::DumpAdvanceInfo()
790 {
791     DumpLog::GetInstance().AddDesc(std::string("activeRect: ").append(activeRect_.ToString()));
792     DumpLog::GetInstance().AddDesc(std::string("touchRegion: ").append(touchRegion_.ToString()));
793     DumpLog::GetInstance().AddDesc(std::string("hoverRegion: ").append(hoverRegion_.ToString()));
794     DumpLog::GetInstance().AddDesc(std::string("normalWidth: ").append(normalWidth_.ToString()));
795     DumpLog::GetInstance().AddDesc(std::string("activeWidth: ").append(activeWidth_.ToString()));
796     DumpLog::GetInstance().AddDesc(std::string("touchWidth: ").append(touchWidth_.ToString()));
797     DumpLog::GetInstance().AddDesc(std::string("hoverWidth: ").append(hoverWidth_.ToString()));
798     GetShapeModeDumpInfo();
799     GetPositionModeDumpInfo();
800     GetAxisDumpInfo();
801     GetPanDirectionDumpInfo();
802     DumpLog::GetInstance().AddDesc(std::string("hostBorderRadius: ").append(hostBorderRadius_.ToString()));
803     DumpLog::GetInstance().AddDesc(std::string("startReservedHeight: ").append(startReservedHeight_.ToString()));
804     DumpLog::GetInstance().AddDesc(std::string("endReservedHeight: ").append(endReservedHeight_.ToString()));
805     DumpLog::GetInstance().AddDesc(std::string("isScrollable: ").append(std::to_string(isScrollable_)));
806     DumpLog::GetInstance().AddDesc(std::string("isReverse: ").append(std::to_string(isReverse_)));
807     DumpLog::GetInstance().AddDesc("==========================innerScrollBarLayoutInfos==========================");
808     for (const auto& info : innerScrollBarLayoutInfos_) {
809         DumpLog::GetInstance().AddDesc(info.ToString());
810     }
811     DumpLog::GetInstance().AddDesc("==========================innerScrollBarLayoutInfos==========================");
812 }
813 
GetForegroundColor() const814 Color ScrollBar::GetForegroundColor() const
815 {
816     return IsPressed() ? foregroundColor_.BlendColor(PRESSED_BLEND_COLOR) : foregroundColor_;
817 }
818 
SetHoverWidth(const RefPtr<ScrollBarTheme> & theme)819 void ScrollBar::SetHoverWidth(const RefPtr<ScrollBarTheme>& theme)
820 {
821     hoverWidth_ = theme->GetActiveWidth() + theme->GetScrollBarMargin() * 2;
822 }
823 
SetNormalWidth(const Dimension & normalWidth)824 void ScrollBar::SetNormalWidth(const Dimension& normalWidth)
825 {
826     if (normalWidth_ != normalWidth) {
827         normalWidthUpdate_ = true;
828         normalWidth_ = normalWidth;
829         CalcReservedHeight();
830         MarkNeedRender();
831     }
832 }
833 
SetScrollable(bool isScrollable)834 void ScrollBar::SetScrollable(bool isScrollable)
835 {
836     CHECK_NULL_VOID(isScrollable_ != isScrollable);
837     isScrollable_ = isScrollable;
838 }
839 
SetPositionMode(PositionMode positionMode)840 void ScrollBar::SetPositionMode(PositionMode positionMode)
841 {
842     if (positionMode_ != positionMode) {
843         positionModeUpdate_ = true;
844         positionMode_ = positionMode;
845         if (panRecognizer_) {
846             PanDirection panDirection;
847             panDirection.type =
848                 positionMode_ == PositionMode::BOTTOM ? PanDirection::HORIZONTAL : PanDirection::VERTICAL;
849             panRecognizer_->SetDirection(panDirection);
850         }
851     }
852 }
853 
SetDisplayMode(DisplayMode displayMode)854 void ScrollBar::SetDisplayMode(DisplayMode displayMode)
855 {
856     CHECK_NULL_VOID(displayMode_ != displayMode);
857     displayMode_ = displayMode;
858 }
859 
PlayScrollBarDisappearAnimation()860 void ScrollBar::PlayScrollBarDisappearAnimation()
861 {
862     if (displayMode_ == DisplayMode::AUTO && isScrollable_ && !isHover_ && !isPressed_) {
863         opacityAnimationType_ = OpacityAnimationType::DISAPPEAR;
864         MarkNeedRender();
865     }
866 }
867 
PlayScrollBarAppearAnimation()868 void ScrollBar::PlayScrollBarAppearAnimation()
869 {
870     if (displayMode_ == DisplayMode::AUTO && isScrollable_) {
871         disappearDelayTask_.Cancel();
872         opacityAnimationType_ = OpacityAnimationType::APPEAR;
873         MarkNeedRender();
874     }
875 }
876 
PlayScrollBarGrowAnimation()877 void ScrollBar::PlayScrollBarGrowAnimation()
878 {
879     PlayScrollBarAppearAnimation();
880     normalWidth_ = activeWidth_;
881     FlushBarWidth();
882     hoverAnimationType_ = HoverAnimationType::GROW;
883     MarkNeedRender();
884 }
885 
PlayScrollBarShrinkAnimation()886 void ScrollBar::PlayScrollBarShrinkAnimation()
887 {
888     normalWidth_ = inactiveWidth_;
889     FlushBarWidth();
890     hoverAnimationType_ = HoverAnimationType::SHRINK;
891     MarkNeedRender();
892 }
893 
PlayScrollBarAdaptAnimation()894 void ScrollBar::PlayScrollBarAdaptAnimation()
895 {
896     needAdaptAnimation_ = true;
897     MarkNeedRender();
898 }
899 
MarkNeedRender()900 void ScrollBar::MarkNeedRender()
901 {
902     if (markNeedRenderFunc_) {
903         markNeedRenderFunc_();
904     }
905 }
906 
GetMainOffset(const Offset & offset) const907 float ScrollBar::GetMainOffset(const Offset& offset) const
908 {
909     return positionMode_ == PositionMode::BOTTOM ? offset.GetX() : offset.GetY();
910 }
911 
SetReverse(bool reverse)912 void ScrollBar::SetReverse(bool reverse)
913 {
914     if (isReverse_ != reverse) {
915         isReverse_ = reverse;
916         isReverseUpdate_ = true;
917     }
918 }
919 
GetPanDirection() const920 Axis ScrollBar::GetPanDirection() const
921 {
922     CHECK_NULL_RETURN(panRecognizer_, Axis::NONE);
923     return panRecognizer_->GetAxisDirection();
924 }
925 } // namespace OHOS::Ace::NG
926