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