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