1 /*
2 * Copyright (c) 2021-2022 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/scroll/render_scroll.h"
17
18 #include <chrono>
19
20 #include "base/geometry/axis.h"
21 #include "core/animation/curve_animation.h"
22 #include "core/common/event_manager.h"
23 #include "core/components/scroll/scrollable.h"
24 #include "core/event/ace_event_helper.h"
25 #include "core/gestures/timeout_recognizer.h"
26 #include "core/pipeline/base/composed_element.h"
27
28 namespace OHOS::Ace {
29 namespace {
30
31 constexpr int32_t SCROLL_NONE = 0;
32 constexpr int32_t SCROLL_TOUCH_DOWN = 1;
33 constexpr int32_t SCROLL_TOUCH_UP = 2;
34 constexpr double SCROLL_RATIO = 0.52;
35 constexpr float SCROLL_BY_SPEED = 250.0f; // move 250 pixels per second
36 constexpr double UNIT_CONVERT = 1000.0; // 1s convert to 1000ms
37 constexpr double ROTATE_FACTOR = -1.0; // pixels factor per angle
38
39 } // namespace
40
RenderScroll()41 RenderScroll::RenderScroll() : RenderNode(true)
42 {
43 Initialize();
44 }
45
~RenderScroll()46 RenderScroll::~RenderScroll()
47 {
48 if (scrollBarProxy_) {
49 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
50 }
51 }
52
Initialize()53 void RenderScroll::Initialize()
54 {
55 touchRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
56 touchRecognizer_->SetOnTouchCancel([weakItem = AceType::WeakClaim(this)](const TouchEventInfo&) {
57 auto item = weakItem.Upgrade();
58 if (!item) {
59 return;
60 }
61 // check out of boundary
62 if (!item->IsOutOfBoundary()) {
63 return;
64 }
65 auto scrollEffect = item->scrollEffect_;
66 if (scrollEffect) {
67 scrollEffect->ProcessScrollOver(0.0);
68 }
69 });
70 }
71
ValidateOffset(int32_t source)72 bool RenderScroll::ValidateOffset(int32_t source)
73 {
74 if (mainScrollExtent_ <= GetMainSize(viewPort_)) {
75 return true;
76 }
77
78 outBoundaryExtent_ = 0.0;
79 scrollBarOutBoundaryExtent_ = 0.0;
80
81 // restrict position between top and bottom
82 if (!scrollEffect_ || scrollEffect_->IsRestrictBoundary() || source == SCROLL_FROM_JUMP ||
83 source == SCROLL_FROM_BAR || source == SCROLL_FROM_ROTATE || refreshParent_.Upgrade()) {
84 if (axis_ == Axis::HORIZONTAL) {
85 currentOffset_.SetX(std::clamp(currentOffset_.GetX(), 0.0, mainScrollExtent_ - viewPort_.Width()));
86 } else {
87 // Refresh support spring when pulling up.
88 #ifdef WEARABLE_PRODUCT
89 if (refreshParent_.Upgrade() &&
90 GetMainOffset(currentOffset_) >= (mainScrollExtent_ - GetMainSize(viewPort_)) && ReachMaxCount()) {
91 scrollBarOutBoundaryExtent_ = GetMainOffset(currentOffset_) -
92 (mainScrollExtent_ - GetMainSize(viewPort_));
93 } else {
94 currentOffset_.SetY(std::clamp(currentOffset_.GetY(), 0.0, mainScrollExtent_ - viewPort_.Height()));
95 }
96 #else
97 currentOffset_.SetY(std::clamp(currentOffset_.GetY(), 0.0, mainScrollExtent_ - viewPort_.Height()));
98 #endif
99 }
100 } else {
101 if (GetMainOffset(currentOffset_) < 0) {
102 outBoundaryExtent_ = -GetMainOffset(currentOffset_);
103 scrollBarOutBoundaryExtent_ = -GetMainOffset(currentOffset_);
104 } else if (GetMainOffset(currentOffset_) >= (mainScrollExtent_ - GetMainSize(viewPort_)) &&
105 ReachMaxCount()) {
106 scrollBarOutBoundaryExtent_ =
107 GetMainOffset(currentOffset_) - (mainScrollExtent_ - GetMainSize(viewPort_));
108 }
109 HandleScrollBarOutBoundary();
110 }
111
112 axis_ == Axis::HORIZONTAL ? currentOffset_.SetY(0.0) : currentOffset_.SetX(0.0);
113 return true;
114 }
115
HandleScrollBarOutBoundary()116 void RenderScroll::HandleScrollBarOutBoundary()
117 {
118 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
119 scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
120 }
121 }
122
HandleScrollPosition(double scrollX,double scrollY,int32_t scrollState) const123 void RenderScroll::HandleScrollPosition(double scrollX, double scrollY, int32_t scrollState) const
124 {
125 if (!positionController_) {
126 LOGW("positionController is null");
127 return;
128 }
129
130 if (GetMainOffset(currentOffset_) > 0.0 &&
131 GetMainOffset(currentOffset_) < mainScrollExtent_ - GetMainSize(viewPort_)) {
132 positionController_->SetMiddle();
133 }
134
135 positionController_->HandleScrollEvent(
136 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_POSITION, scrollX, scrollY, scrollState));
137 }
138
HandleCrashBottom()139 bool RenderScroll::HandleCrashBottom()
140 {
141 if (positionController_) {
142 positionController_->SetBottom();
143 positionController_->HandleScrollEvent(
144 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_BOTTOM, 0.0, 0.0, -1));
145 }
146 if (axis_ == Axis::HORIZONTAL) {
147 OnReachEnd();
148 } else {
149 OnReachBottom();
150 }
151 return false;
152 }
153
HandleCrashTop()154 bool RenderScroll::HandleCrashTop()
155 {
156 if (positionController_) {
157 positionController_->SetTop();
158 positionController_->HandleScrollEvent(
159 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_TOP, 0.0, 0.0, -1));
160 }
161 if (axis_ == Axis::HORIZONTAL) {
162 OnReachStart();
163 } else {
164 OnReachTop();
165 }
166 return false;
167 }
168
UpdateOffset(Offset & delta,int32_t source)169 bool RenderScroll::UpdateOffset(Offset& delta, int32_t source)
170 {
171 if (source == SCROLL_FROM_ROTATE) {
172 isFromRotate_ = true;
173 } else {
174 isFromRotate_ = false;
175 }
176 if (!scrollable_->Available()) {
177 return false;
178 }
179 if (delta.IsZero()) {
180 return false;
181 }
182 if (IsAtTop() && HandleRefreshEffect(-delta.GetY(), source, currentOffset_.GetY())) {
183 return true;
184 }
185 if ((IsAtBottom() && GetMainOffset(delta) > 0.0) || (IsAtTop() && GetMainOffset(delta) < 0.0)) {
186 if (!scrollEffect_) {
187 return false;
188 }
189 if (scrollEffect_->IsNoneEffect()) {
190 return false;
191 }
192 }
193 if (!ScrollPageCheck(delta, source)) {
194 return false;
195 }
196 if (GetMainOffset(currentOffset_) <= 0.0) {
197 if (scrollable_->RelatedScrollEventDoing(delta)) {
198 return false;
199 }
200 }
201 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
202 scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
203 scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
204 }
205 currentOffset_ += delta;
206 currentDeltaInMain_ += GetMainOffset(delta);
207 // handle edge effect
208 HandleScrollEffect();
209
210 if (!ValidateOffset(source)) {
211 currentOffset_ = currentOffset_ - delta;
212 return false;
213 }
214
215 bool next = true;
216 Offset correctedDelta = currentOffset_ - lastOffset_;
217 if (!correctedDelta.IsZero()) {
218 int32_t touchState = SCROLL_NONE;
219 if (source == SCROLL_FROM_UPDATE) {
220 touchState = SCROLL_TOUCH_DOWN;
221 } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
222 touchState = SCROLL_TOUCH_UP;
223 }
224 HandleScrollPosition(correctedDelta.GetX(), correctedDelta.GetY(), touchState);
225
226 if (IsCrashTop()) {
227 // scroll to top
228 next = HandleCrashTop();
229 } else if (IsCrashBottom()) {
230 // scroll to bottom
231 next = HandleCrashBottom();
232 }
233 if (scrollEffect_ && !scrollEffect_->IsRestrictBoundary()) {
234 next = true;
235 MarkNeedPredictLayout();
236 }
237 } else {
238 next = false;
239 }
240
241 lastOffset_ = currentOffset_;
242 currentBottomOffset_ = axis_ == Axis::VERTICAL ? currentOffset_ + Offset(0.0, viewPort_.Height())
243 : currentOffset_ + Offset(viewPort_.Width(), 0.0);
244 correctedDelta_ = correctedDelta;
245 MarkNeedLayout(true);
246 return next;
247 }
248
HandleScrollEffect()249 void RenderScroll::HandleScrollEffect()
250 {
251 // handle edge effect
252 if (scrollEffect_) {
253 overScroll_ = scrollEffect_->CalculateOverScroll(GetMainOffset(lastOffset_), ReachMaxCount());
254 if (!NearZero(overScroll_)) {
255 scrollEffect_->HandleOverScroll(axis_, overScroll_, viewPort_);
256 }
257 }
258 }
259
IsCrashTop()260 bool RenderScroll::IsCrashTop()
261 {
262 double current = GetMainOffset(currentOffset_);
263 double last = GetMainOffset(lastOffset_);
264 bool scrollUpToReachTop = GreatNotEqual(last, 0.0) && LessOrEqual(current, 0.0);
265 bool scrollDownToReachTop = LessNotEqual(last, 0.0) && GreatOrEqual(current, 0.0);
266 return scrollUpToReachTop || scrollDownToReachTop;
267 }
268
IsCrashBottom()269 bool RenderScroll::IsCrashBottom()
270 {
271 double maxExtent = mainScrollExtent_ - GetMainSize(viewPort_);
272 double current = GetMainOffset(currentOffset_);
273 double last = GetMainOffset(lastOffset_);
274 bool scrollDownToReachEnd = LessNotEqual(last, maxExtent) && GreatOrEqual(current, maxExtent);
275 bool scrollUpToReachEnd = GreatNotEqual(last, maxExtent) && LessOrEqual(current, maxExtent);
276 return (scrollUpToReachEnd || scrollDownToReachEnd) && ReachMaxCount();
277 }
278
CanScrollVertically(const Offset & delta)279 bool RenderScroll::CanScrollVertically(const Offset& delta)
280 {
281 if (axis_ != Axis::VERTICAL) {
282 return false;
283 }
284
285 if (delta.IsZero()) {
286 return false;
287 }
288 Offset currentOffset = currentOffset_ + delta;
289
290 if (currentOffset.GetY() < 0.0) {
291 currentOffset.SetY(0.0);
292 } else if (currentOffset.GetY() > mainScrollExtent_ - viewPort_.Height()) {
293 currentOffset.SetY(mainScrollExtent_ - viewPort_.Height());
294 }
295
296 if (mainScrollExtent_ <= GetMainSize(viewPort_)) {
297 return false;
298 }
299
300 bool next = true;
301 Offset correctedDelta = currentOffset - lastOffset_;
302 if (!correctedDelta.IsZero()) {
303 double mainOffset = GetMainOffset(currentOffset);
304 if (NearZero(mainOffset)) {
305 // scroll to top
306 next = false;
307 } else if (NearEqual(mainOffset, mainScrollExtent_ - GetMainSize(viewPort_)) && ReachMaxCount()) {
308 // scroll to bottom
309 next = false;
310 }
311 } else {
312 next = false;
313 }
314 return next;
315 }
316
ScrollPageCheck(Offset & delta,int32_t source)317 bool RenderScroll::ScrollPageCheck(Offset& delta, int32_t source)
318 {
319 if (source == SCROLL_FROM_ANIMATION_SPRING || source == SCROLL_FROM_BAR) {
320 return true;
321 }
322 if (axis_ != Axis::VERTICAL) {
323 return true;
324 }
325 if (scrollPage_) {
326 if (delta.GetY() > 0.0) {
327 // scroll up
328 bool selfCanScroll = RenderNode::ScrollPageByChild(delta, SCROLL_FROM_CHILD);
329 if (!selfCanScroll) {
330 return false;
331 }
332 } else {
333 // scroll down
334 if (!CanScrollVertically(delta)) {
335 bool selfCanScroll = RenderNode::ScrollPageByChild(delta, SCROLL_FROM_CHILD);
336 if (!selfCanScroll) {
337 return false;
338 }
339 }
340 }
341 }
342 return true;
343 }
344
ScrollPageByChild(Offset & delta,int32_t source)345 bool RenderScroll::ScrollPageByChild(Offset& delta, int32_t source)
346 {
347 // scroll up
348 if (delta.GetY() > 0.0) {
349 bool selfCanScroll = RenderNode::ScrollPageByChild(delta, source);
350 if (selfCanScroll) {
351 AdjustOffset(delta, source);
352 return !UpdateOffset(delta, source);
353 }
354 return false;
355 } else {
356 // scroll down
357 AdjustOffset(delta, source);
358 if (UpdateOffset(delta, source)) {
359 return false;
360 } else {
361 return RenderNode::ScrollPageByChild(delta, source);
362 }
363 }
364 }
365
IsOutOfBottomBoundary()366 bool RenderScroll::IsOutOfBottomBoundary()
367 {
368 return GreatOrEqual(GetMainOffset(currentOffset_), (mainScrollExtent_ - GetMainSize(viewPort_))) &&
369 ReachMaxCount();
370 }
371
IsOutOfTopBoundary()372 bool RenderScroll::IsOutOfTopBoundary()
373 {
374 return LessOrEqual(GetMainOffset(currentOffset_), 0.0);
375 }
376
IsOutOfBoundary()377 bool RenderScroll::IsOutOfBoundary()
378 {
379 return (IsOutOfTopBoundary() || IsOutOfBottomBoundary());
380 }
381
AdjustOffset(Offset & delta,int32_t source)382 void RenderScroll::AdjustOffset(Offset& delta, int32_t source)
383 {
384 if (delta.IsZero() || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
385 return;
386 }
387
388 double viewPortSize = GetMainSize(viewPort_);
389 double offset = GetMainOffset(delta);
390 if (NearZero(viewPortSize) || NearZero(offset)) {
391 return;
392 }
393
394 double maxScrollExtent = mainScrollExtent_ - viewPortSize;
395 double overscrollPastStart = 0.0;
396 double overscrollPastEnd = 0.0;
397 double overscrollPast = 0.0;
398 bool easing = false;
399 overscrollPastStart = std::max(-GetCurrentPosition(), 0.0);
400 overscrollPastEnd = std::max(GetCurrentPosition() - maxScrollExtent, 0.0);
401 // do not adjust offset if direction oppsite from the overScroll direction when out of boundary
402 if ((overscrollPastStart > 0.0 && offset > 0.0) || (overscrollPastEnd > 0.0 && offset < 0.0)) {
403 return;
404 }
405 easing = (overscrollPastStart > 0.0 && offset < 0.0) || (overscrollPastEnd > 0.0 && offset > 0.0);
406 overscrollPast = std::max(overscrollPastStart, overscrollPastEnd);
407 double friction = easing ? CalculateFriction((overscrollPast - std::abs(offset)) / viewPortSize)
408 : CalculateFriction(overscrollPast / viewPortSize);
409 double direction = offset / std::abs(offset);
410 offset = direction * CalculateOffsetByFriction(overscrollPast, std::abs(offset), friction);
411 axis_ == Axis::VERTICAL ? delta.SetY(offset) : delta.SetX(offset);
412 }
413
CalculateFriction(double gamma)414 double RenderScroll::CalculateFriction(double gamma)
415 {
416 return SCROLL_RATIO * std::pow(1.0 - gamma, SQUARE);
417 }
418
CalculateOffsetByFriction(double extentOffset,double delta,double friction)419 double RenderScroll::CalculateOffsetByFriction(double extentOffset, double delta, double friction)
420 {
421 double offset = 0.0;
422 if (extentOffset > 0.0 && !NearZero(friction)) {
423 double deltaToLimit = extentOffset / friction;
424 if (delta < deltaToLimit) {
425 return delta * friction;
426 }
427 offset += extentOffset;
428 delta -= deltaToLimit;
429 }
430 return offset + delta;
431 }
432
ResetEdgeEffect()433 void RenderScroll::ResetEdgeEffect()
434 {
435 if (scrollEffect_) {
436 scrollEffect_->SetCurrentPositionCallback([weakScroll = AceType::WeakClaim(this)]() {
437 auto scroll = weakScroll.Upgrade();
438 if (scroll) {
439 return -scroll->GetCurrentPosition();
440 }
441 return 0.0;
442 });
443 scrollEffect_->SetLeadingCallback([weakScroll = AceType::WeakClaim(this)]() {
444 auto scroll = weakScroll.Upgrade();
445 if (scroll) {
446 if (!scroll->IsRowReverse() && !scroll->IsColReverse()) {
447 return scroll->GetMainSize(scroll->viewPort_) - scroll->mainScrollExtent_;
448 }
449 }
450 return 0.0;
451 });
452 scrollEffect_->SetTrailingCallback([weakScroll = AceType::WeakClaim(this)]() {
453 auto scroll = weakScroll.Upgrade();
454 if (scroll) {
455 if (scroll->IsRowReverse() || scroll->IsColReverse()) {
456 return scroll->mainScrollExtent_ - scroll->GetMainSize(scroll->viewPort_);
457 }
458 }
459 return 0.0;
460 });
461 scrollEffect_->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() {
462 auto scroll = weakScroll.Upgrade();
463 if (scroll) {
464 if (!scroll->IsRowReverse() && !scroll->IsColReverse()) {
465 return scroll->GetMainSize(scroll->viewPort_) - scroll->GetMainScrollExtent();
466 }
467 }
468 return 0.0;
469 });
470 scrollEffect_->SetInitTrailingCallback([weakScroll = AceType::WeakClaim(this)]() {
471 auto scroll = weakScroll.Upgrade();
472 if (scroll) {
473 if (scroll->IsRowReverse() || scroll->IsColReverse()) {
474 return scroll->GetMainScrollExtent() - scroll->GetMainSize(scroll->viewPort_);
475 }
476 }
477 return 0.0;
478 });
479 scrollEffect_->SetScrollNode(AceType::WeakClaim(this));
480
481 SetEdgeEffectAttribute();
482 scrollEffect_->InitialEdgeEffect();
483 }
484 }
485
ResetScrollEventCallBack()486 void RenderScroll::ResetScrollEventCallBack()
487 {
488 scrollable_->SetScrollEndCallback([weakScroll = AceType::WeakClaim(this)]() {
489 auto scroll = weakScroll.Upgrade();
490 if (scroll) {
491 if (scroll->positionController_) {
492 scroll->positionController_->HandleScrollEvent(
493 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_END, 0.0, 0.0, -1));
494 }
495 // Send scroll none when scroll is end.
496 auto context = scroll->GetContext().Upgrade();
497 if (!(context && context->GetIsDeclarative())) {
498 scroll->HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
499 }
500 scroll->HandleScrollBarEnd();
501
502 auto proxy = scroll->scrollBarProxy_;
503 if (proxy) {
504 proxy->StartScrollBarAnimator();
505 }
506 }
507 });
508 scrollable_->SetScrollTouchUpCallback([weakScroll = AceType::WeakClaim(this)]() {
509 auto scroll = weakScroll.Upgrade();
510 if (scroll && scroll->positionController_) {
511 scroll->positionController_->HandleScrollEvent(
512 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_TOUCHUP, 0.0, 0.0, -1));
513 }
514 });
515 if (positionController_) {
516 positionController_->SetMiddle();
517 double mainOffset = GetMainOffset(currentOffset_);
518 // No need to set bottom, because if scrollable, it must not be at the bottom.
519 if (NearZero(mainOffset)) {
520 positionController_->SetTop();
521 }
522 }
523 }
524
InitScrollBar(const RefPtr<ScrollBar> & scrollBar)525 void RenderScroll::InitScrollBar(const RefPtr<ScrollBar>& scrollBar)
526 {
527 if (scrollBar_ == scrollBar) {
528 return;
529 }
530
531 scrollBar_ = scrollBar;
532 if (!scrollBar_) {
533 scrollBar_ = AceType::MakeRefPtr<ScrollBar>(DisplayMode::OFF);
534 }
535 if (axis_ == Axis::HORIZONTAL) {
536 scrollBar_->SetPositionMode(PositionMode::BOTTOM);
537 }
538 if (axis_ == Axis::VERTICAL) {
539 if (rightToLeft_) {
540 scrollBar_->SetPositionMode(PositionMode::LEFT);
541 }
542 }
543 scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
544 SetBarCallBack(axis_ == Axis::VERTICAL);
545 }
546
ResetScrollable()547 void RenderScroll::ResetScrollable()
548 {
549 const auto isVertical = (axis_ == Axis::VERTICAL);
550 auto&& callback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
551 auto scroll = weakScroll.Upgrade();
552 if (!scroll) {
553 LOGE("render scroll is released");
554 return false;
555 }
556 if (source == SCROLL_FROM_START) {
557 scroll->NotifyDragStart(value);
558 scroll->currentDeltaInMain_ = 0.0;
559 return true;
560 }
561 Offset delta;
562 if (isVertical) {
563 delta.SetX(0.0);
564 delta.SetY(-value);
565 } else {
566 delta.SetX(-value);
567 delta.SetY(0.0);
568 }
569 scroll->AdjustOffset(delta, source);
570 scroll->NotifyDragUpdate(scroll->GetMainOffset(delta), source);
571
572 // Stop animator of scroll bar.
573 auto scrollBarProxy = scroll->scrollBarProxy_;
574 if (scrollBarProxy) {
575 scrollBarProxy->StopScrollBarAnimator();
576 }
577 return scroll->UpdateOffset(delta, source);
578 };
579 // Initializes scrollable with different direction.
580 if (scrollable_) {
581 scrollable_->SetAxis(axis_ == Axis::VERTICAL ? Axis::VERTICAL : Axis::HORIZONTAL);
582 scrollable_->SetCallback(callback);
583 } else {
584 if (isVertical) {
585 scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, Axis::VERTICAL);
586 scrollable_->InitRelatedParent(GetParent());
587 } else {
588 scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, Axis::HORIZONTAL);
589 }
590 scrollable_->SetScrollableNode(AceType::WeakClaim(this));
591 }
592 scrollable_->SetOnScrollBegin(onScrollBegin_);
593 scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
594 auto scroll = weak.Upgrade();
595 if (!scroll) {
596 return;
597 }
598 scroll->ProcessScrollOverCallback(velocity);
599 });
600 scrollable_->SetWatchFixCallback([weak = AceType::WeakClaim(this)](double final, double current) {
601 auto scroll = weak.Upgrade();
602 if (scroll) {
603 return scroll->GetFixPositionOnWatch(final, current);
604 }
605 return final;
606 });
607 scrollable_->Initialize(GetContext());
608 scrollable_->SetNodeId(GetAccessibilityNodeId());
609 scrollable_->SetScrollEnd([weakScroll = AceType::WeakClaim(this)]() {
610 auto scroll = weakScroll.Upgrade();
611 if (scroll) {
612 scroll->MarkNeedLayout(true);
613 }
614 });
615 InitializeScrollable(scrollable_);
616
617 currentBottomOffset_ = Offset::Zero();
618 currentOffset_ = Offset::Zero();
619 lastOffset_ = Offset::Zero();
620 ResetScrollEventCallBack();
621 SetEdgeEffectAttribute();
622 }
623
SetEdgeEffectAttribute()624 void RenderScroll::SetEdgeEffectAttribute()
625 {
626 if (scrollEffect_ && scrollable_) {
627 scrollEffect_->SetScrollable(scrollable_);
628 scrollEffect_->RegisterSpringCallback();
629 if (scrollEffect_->IsSpringEffect()) {
630 scrollable_->SetOutBoundaryCallback([weakScroll = AceType::WeakClaim(this)]() {
631 auto scroll = weakScroll.Upgrade();
632 if (scroll) {
633 return scroll->IsOutOfBoundary();
634 }
635 return false;
636 });
637 }
638 }
639 }
640
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)641 void RenderScroll::OnTouchTestHit(
642 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
643 {
644 if (!GetVisible() || axis_ == Axis::NONE) {
645 return;
646 }
647 if (!scrollable_) {
648 return;
649 }
650 if (scrollable_->Available() && scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
651 scrollBar_->AddScrollBarController(coordinateOffset, result);
652 } else {
653 scrollable_->SetCoordinateOffset(coordinateOffset);
654 auto newTouchRestrict = touchRestrict;
655 AdjustTouchRestrict(newTouchRestrict);
656 scrollable_->SetDragTouchRestrict(newTouchRestrict);
657 result.emplace_back(scrollable_);
658 }
659 touchRecognizer_->SetCoordinateOffset(coordinateOffset);
660 result.emplace_back(touchRecognizer_);
661 }
662
HandleMouseHoverEvent(const MouseState mouseState)663 void RenderScroll::HandleMouseHoverEvent(const MouseState mouseState)
664 {
665 LOGI("scroll hover state: %{public}d", mouseState);
666 if (scrollBar_ && mouseState == MouseState::NONE) {
667 scrollBar_->SetIsHover(false);
668 }
669 }
670
HandleMouseEvent(const MouseEvent & event)671 bool RenderScroll::HandleMouseEvent(const MouseEvent& event)
672 {
673 if (!scrollBar_) {
674 return RenderNode::HandleMouseEvent(event);
675 }
676 auto globalOffset = GetGlobalOffset();
677 auto localPoint = Point(event.x - globalOffset.GetX(), event.y - globalOffset.GetY());
678 bool isInBarRegion = scrollBar_->InBarRegion(localPoint);
679 scrollBar_->SetIsHover(isInBarRegion);
680 return isInBarRegion;
681 }
682
JumpToIndex(int32_t index,int32_t source)683 void RenderScroll::JumpToIndex(int32_t index, int32_t source)
684 {
685 LOGE("Do not support in base RenderScroll");
686 }
687
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)688 void RenderScroll::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
689 {
690 if ((IsRowReverse() && scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) ||
691 (!IsRowReverse() && scrollEdgeType == ScrollEdgeType::SCROLL_TOP)) {
692 double distance = -GetMainOffset(currentOffset_);
693 ScrollBy(distance, distance, smooth);
694 } else {
695 double distance = mainScrollExtent_ - GetMainOffset(currentOffset_);
696 ScrollBy(distance, distance, smooth);
697 }
698 }
699
ScrollPage(bool reverse,bool smooth,const std::function<void ()> & onFinish)700 bool RenderScroll::ScrollPage(bool reverse, bool smooth, const std::function<void()>& onFinish)
701 {
702 if ((IsRowReverse() && !reverse) || (!IsRowReverse() && reverse)) {
703 double distance = -GetMainSize(viewPort_);
704 ScrollBy(distance, distance, smooth, onFinish);
705 } else {
706 double distance = GetMainSize(viewPort_);
707 ScrollBy(distance, distance, smooth, onFinish);
708 }
709 return true;
710 }
711
JumpToPosition(double position,int32_t source)712 void RenderScroll::JumpToPosition(double position, int32_t source)
713 {
714 // If an animation is playing, stop it.
715 if (animator_) {
716 if (!animator_->IsStopped()) {
717 animator_->Stop();
718 }
719 animator_->ClearInterpolators();
720 }
721 DoJump(position, source);
722 HandleScrollBarEnd();
723 }
724
DoJump(double position,int32_t source)725 void RenderScroll::DoJump(double position, int32_t source)
726 {
727 if (!NearEqual(GetCurrentPosition(), position)) {
728 Offset delta;
729 if (axis_ == Axis::VERTICAL) {
730 delta.SetY(position - GetMainOffset(currentOffset_));
731 } else {
732 delta.SetX(position - GetMainOffset(currentOffset_));
733 }
734
735 UpdateOffset(delta, source);
736 }
737 }
738
AnimateTo(double position,float duration,const RefPtr<Curve> & curve,bool limitDuration,const std::function<void ()> & onFinish)739 void RenderScroll::AnimateTo(double position, float duration, const RefPtr<Curve>& curve, bool limitDuration,
740 const std::function<void()>& onFinish)
741 {
742 if (!animator_) {
743 animator_ = CREATE_ANIMATOR(GetContext());
744 CHECK_NULL_VOID(animator_);
745 }
746 if (!animator_->IsStopped()) {
747 animator_->Stop();
748 }
749 animator_->ClearInterpolators();
750
751 // Send event to accessibility when scroll start.
752 auto context = GetContext().Upgrade();
753 if (context) {
754 AccessibilityEvent scrollEvent;
755 scrollEvent.nodeId = GetAccessibilityNodeId();
756 scrollEvent.eventType = "scrollstart";
757 context->SendEventToAccessibility(scrollEvent);
758 }
759 auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentPosition(), position, curve);
760 animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
761 auto scroll = weakScroll.Upgrade();
762 if (scroll) {
763 scroll->DoJump(value);
764 }
765 });
766 animator_->AddInterpolator(animation);
767 animator_->SetDuration(duration);
768 animator_->ClearStopListeners();
769 animator_->Play();
770 auto weakScroll = AceType::WeakClaim(this);
771 auto weakScrollBar = AceType::WeakClaim(AceType::RawPtr(scrollBar_));
772 animator_->AddStopListener([weakScroll, weakScrollBar, onFinish, context = context_]() {
773 auto scrollBar = weakScrollBar.Upgrade();
774 if (scrollBar) {
775 scrollBar->HandleScrollBarEnd();
776 }
777 // Send event to accessibility when scroll end.
778 auto scroll = weakScroll.Upgrade();
779 if (scroll) {
780 auto context = scroll->GetContext().Upgrade();
781 if (context) {
782 AccessibilityEvent scrollEvent;
783 scrollEvent.nodeId = scroll->GetAccessibilityNodeId();
784 scrollEvent.eventType = "scrollend";
785 context->SendEventToAccessibility(scrollEvent);
786 if (context->GetIsDeclarative() && scroll->scrollable_) {
787 scroll->scrollable_->ChangeMoveStatus(true);
788 scroll->scrollable_->ProcessScrollMotionStop();
789 }
790 }
791 }
792
793 if (onFinish) {
794 onFinish();
795 }
796 });
797 }
798
AnimateToTarget(const ComposeId & targetId,float duration,const RefPtr<Curve> & curve,bool limitDuration,const std::function<void ()> & onFinish)799 bool RenderScroll::AnimateToTarget(const ComposeId& targetId, float duration, const RefPtr<Curve>& curve,
800 bool limitDuration, const std::function<void()>& onFinish)
801 {
802 auto context = GetContext().Upgrade();
803 if (!context) {
804 return false;
805 }
806 auto targetElement = context->GetComposedElementById(targetId);
807 if (!targetElement) {
808 return false;
809 }
810 auto targetRender = targetElement->GetRenderNode();
811 if (!targetRender) {
812 return false;
813 }
814
815 auto globalOffset = targetRender->GetGlobalOffset() - GetPosition();
816 double distance = ((axis_ == Axis::VERTICAL) ? globalOffset.GetY() : globalOffset.GetX()) + GetCurrentPosition();
817 AnimateTo(distance, duration, curve, limitDuration, onFinish);
818 return true;
819 }
820
ScrollBy(double pixelX,double pixelY,bool smooth,const std::function<void ()> & onFinish)821 void RenderScroll::ScrollBy(double pixelX, double pixelY, bool smooth, const std::function<void()>& onFinish)
822 {
823 double distance = (axis_ == Axis::VERTICAL) ? pixelY : pixelX;
824 if (NearZero(distance)) {
825 return;
826 }
827 double position = GetMainOffset(currentOffset_) + distance;
828 if (smooth) {
829 AnimateTo(position, fabs(distance) * UNIT_CONVERT / SCROLL_BY_SPEED, Curves::EASE_OUT, true, onFinish);
830 } else {
831 JumpToPosition(position);
832 }
833 }
834
GetCurrentPosition() const835 double RenderScroll::GetCurrentPosition() const
836 {
837 double position = 0.0;
838 if (axis_ == Axis::VERTICAL) {
839 position = currentOffset_.GetY();
840 } else {
841 position = currentOffset_.GetX();
842 }
843 return position;
844 }
845
Update(const RefPtr<Component> & component)846 void RenderScroll::Update(const RefPtr<Component>& component)
847 {
848 auto scroll = AceType::DynamicCast<ScrollComponent>(component);
849 if (scroll == nullptr) {
850 LOGI("scroll component is null, which it is multi scroll, not single scroll");
851 return;
852 }
853 hasHeight_ = scroll->GetHasHeight();
854 hasWidth_ = scroll->GetHasWidth();
855 if (!animator_) {
856 animator_ = CREATE_ANIMATOR(GetContext());
857 }
858 // ApplyRestoreInfo maybe change currentOffset_
859 ApplyRestoreInfo();
860 lastOffset_ = currentOffset_;
861 // Send scroll none when first build.
862 HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
863 MarkNeedLayout();
864 onReachStart_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachStart(), context_);
865 onReachEnd_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachEnd(), context_);
866 onReachTop_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachTop(), context_);
867 onReachBottom_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachBottom(), context_);
868 scrollBarProxy_ = scroll->GetScrollBarProxy();
869 InitScrollBarProxy();
870 }
871
InitScrollBarProxy()872 void RenderScroll::InitScrollBarProxy()
873 {
874 if (!scrollBarProxy_) {
875 return;
876 }
877 auto isVertical = (axis_ == Axis::VERTICAL);
878 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
879 auto scroll = weakScroll.Upgrade();
880 if (!scroll) {
881 LOGE("render scroll is released");
882 return false;
883 }
884 Offset delta;
885 if (isVertical) {
886 delta.SetX(0.0);
887 delta.SetY(-value);
888 } else {
889 delta.SetX(-value);
890 delta.SetY(0.0);
891 }
892 scroll->AdjustOffset(delta, source);
893 return scroll->UpdateOffset(delta, source);
894 };
895 scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
896 scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
897 }
898
SetBarCallBack(bool isVertical)899 void RenderScroll::SetBarCallBack(bool isVertical)
900 {
901 if (scrollBar_ && scrollBar_->NeedScrollBar()) {
902 auto&& barEndCallback = [weakScroll = AceType::WeakClaim(this), isVertical](int32_t value) {
903 auto scroll = weakScroll.Upgrade();
904 if (!scroll) {
905 LOGE("render scroll is released");
906 return;
907 }
908 scroll->scrollBarOpacity_ = value;
909 scroll->MarkNeedRender();
910 };
911 auto&& scrollEndCallback = [weakScroll = AceType::WeakClaim(this), isVertical]() {
912 auto scroll = weakScroll.Upgrade();
913 if (!scroll) {
914 LOGE("render scroll is released");
915 return;
916 }
917 if (scroll->positionController_) {
918 scroll->positionController_->HandleScrollEvent(
919 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_END, 0.0, 0.0, -1));
920 }
921 // Send scroll none when scroll is end.
922 scroll->HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
923 };
924 auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
925 auto scroll = weakScroll.Upgrade();
926 if (!scroll) {
927 LOGE("render scroll is released");
928 return false;
929 }
930 Offset delta;
931 if (isVertical) {
932 delta.SetX(0.0);
933 delta.SetY(-value);
934 } else {
935 delta.SetX(-value);
936 delta.SetY(0.0);
937 }
938 scroll->AdjustOffset(delta, source);
939
940 return scroll->UpdateOffset(delta, source);
941 };
942 scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
943 }
944 }
945
GetEstimatedHeight()946 double RenderScroll::GetEstimatedHeight()
947 {
948 if (ReachMaxCount()) {
949 estimatedHeight_ = scrollBarExtent_;
950 } else {
951 estimatedHeight_ = std::max(estimatedHeight_, scrollBarExtent_);
952 }
953 return estimatedHeight_;
954 }
955
HandleScrollOverByBar(double velocity)956 void RenderScroll::HandleScrollOverByBar(double velocity)
957 {
958 // the direction of bar and scroll is opposite, so it need be negative
959 if (scrollEffect_) {
960 scrollEffect_->ProcessScrollOver(-velocity);
961 }
962 }
963
HandleScrollBarEnd()964 void RenderScroll::HandleScrollBarEnd()
965 {
966 if (scrollBar_) {
967 scrollBar_->HandleScrollBarEnd();
968 }
969 }
970
HandleRotate(double rotateValue,bool isVertical)971 void RenderScroll::HandleRotate(double rotateValue, bool isVertical)
972 {
973 auto context = GetContext().Upgrade();
974 if (!context) {
975 LOGE("context is null");
976 return;
977 }
978 double value = context->NormalizeToPx(Dimension(rotateValue, DimensionUnit::VP)) * ROTATE_FACTOR;
979
980 // Vertical or horizontal, different axis
981 Offset delta;
982 if (isVertical) {
983 delta.SetX(0.0);
984 delta.SetY(value);
985 } else {
986 delta.SetX(value);
987 delta.SetY(0.0);
988 }
989 UpdateOffset(delta, SCROLL_FROM_ROTATE);
990 }
991
OnChildAdded(const RefPtr<RenderNode> & child)992 void RenderScroll::OnChildAdded(const RefPtr<RenderNode>& child)
993 {
994 child->SetSlipFactorSetting([weakScroll = AceType::WeakClaim(this)](double slipFactor) {
995 auto scroll = weakScroll.Upgrade();
996 if (scroll) {
997 scroll->scrollable_->SetSlipFactor(slipFactor);
998 }
999 });
1000 }
1001
OnReachStart() const1002 void RenderScroll::OnReachStart() const
1003 {
1004 if (onReachStart_) {
1005 onReachStart_(std::string("\"reachstart\",null"));
1006 }
1007 }
1008
OnReachEnd() const1009 void RenderScroll::OnReachEnd() const
1010 {
1011 if (onReachEnd_) {
1012 onReachEnd_(std::string("\"reachend\",null"));
1013 }
1014 }
1015
OnReachTop() const1016 void RenderScroll::OnReachTop() const
1017 {
1018 if (onReachTop_) {
1019 onReachTop_(std::string("\"reachtop\",null"));
1020 }
1021 }
1022
OnReachBottom() const1023 void RenderScroll::OnReachBottom() const
1024 {
1025 if (onReachBottom_) {
1026 onReachBottom_(std::string("\"reachbottom\",null"));
1027 }
1028 }
1029
UpdateTouchRect()1030 void RenderScroll::UpdateTouchRect()
1031 {
1032 RenderNode::UpdateTouchRect();
1033 if (!scrollBar_) {
1034 return;
1035 }
1036 double scrollBarPosition = NormalizeToPx(scrollBar_->GetPosition());
1037 if (!NearZero(scrollBarPosition)) {
1038 touchRect_.SetWidth(touchRect_.Width() + scrollBarPosition);
1039 if (scrollBar_->GetPositionMode() == PositionMode::LEFT) {
1040 touchRect_.SetLeft(touchRect_.Left() - scrollBarPosition);
1041 }
1042 }
1043 }
1044
IsAxisScrollable(AxisDirection direction)1045 bool RenderScroll::IsAxisScrollable(AxisDirection direction)
1046 {
1047 return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !IsAtTop()) ||
1048 ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !IsAtBottom()));
1049 }
1050
HandleAxisEvent(const AxisEvent & event)1051 void RenderScroll::HandleAxisEvent(const AxisEvent& event)
1052 {
1053 }
1054
ProvideRestoreInfo()1055 std::string RenderScroll::ProvideRestoreInfo()
1056 {
1057 if (!NearZero(currentOffset_.GetY()) && axis_ == Axis::VERTICAL) {
1058 return std::to_string(currentOffset_.GetY());
1059 } else if (!NearZero(currentOffset_.GetX()) && axis_ == Axis::HORIZONTAL) {
1060 return std::to_string(currentOffset_.GetX());
1061 }
1062 return "";
1063 }
1064
ApplyRestoreInfo()1065 void RenderScroll::ApplyRestoreInfo()
1066 {
1067 if (GetRestoreInfo().empty()) {
1068 return;
1069 }
1070 if (axis_ == Axis::VERTICAL) {
1071 currentOffset_.SetY(StringUtils::StringToDouble(GetRestoreInfo()));
1072 } else {
1073 currentOffset_.SetX(StringUtils::StringToDouble(GetRestoreInfo()));
1074 }
1075 SetRestoreInfo("");
1076 }
1077
1078 } // namespace OHOS::Ace
1079