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