• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     } else {
484         LOGD("scrollEffect_ is null");
485     }
486 }
487 
ResetScrollEventCallBack()488 void RenderScroll::ResetScrollEventCallBack()
489 {
490     scrollable_->SetScrollEndCallback([weakScroll = AceType::WeakClaim(this)]() {
491         auto scroll = weakScroll.Upgrade();
492         if (scroll) {
493             if (scroll->positionController_) {
494                 scroll->positionController_->HandleScrollEvent(
495                     std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_END, 0.0, 0.0, -1));
496             }
497             // Send scroll none when scroll is end.
498             auto context = scroll->GetContext().Upgrade();
499             if (!(context && context->GetIsDeclarative())) {
500                 scroll->HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
501             }
502             scroll->HandleScrollBarEnd();
503 
504             auto proxy = scroll->scrollBarProxy_;
505             if (proxy) {
506                 proxy->StartScrollBarAnimator();
507             }
508         }
509     });
510     scrollable_->SetScrollTouchUpCallback([weakScroll = AceType::WeakClaim(this)]() {
511         auto scroll = weakScroll.Upgrade();
512         if (scroll && scroll->positionController_) {
513             scroll->positionController_->HandleScrollEvent(
514                 std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_TOUCHUP, 0.0, 0.0, -1));
515         }
516     });
517     if (positionController_) {
518         positionController_->SetMiddle();
519         double mainOffset = GetMainOffset(currentOffset_);
520         // No need to set bottom, because if scrollable, it must not be at the bottom.
521         if (NearZero(mainOffset)) {
522             positionController_->SetTop();
523         }
524     }
525 }
526 
InitScrollBar(const RefPtr<ScrollBar> & scrollBar)527 void RenderScroll::InitScrollBar(const RefPtr<ScrollBar>& scrollBar)
528 {
529     if (scrollBar_ == scrollBar) {
530         return;
531     }
532 
533     scrollBar_ = scrollBar;
534     if (!scrollBar_) {
535         scrollBar_ = AceType::MakeRefPtr<ScrollBar>(DisplayMode::OFF);
536     }
537     if (axis_ == Axis::HORIZONTAL) {
538         scrollBar_->SetPositionMode(PositionMode::BOTTOM);
539     }
540     if (axis_ == Axis::VERTICAL) {
541         if (rightToLeft_) {
542             scrollBar_->SetPositionMode(PositionMode::LEFT);
543         }
544     }
545     scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
546     SetBarCallBack(axis_ == Axis::VERTICAL);
547 }
548 
ResetScrollable()549 void RenderScroll::ResetScrollable()
550 {
551     const auto isVertical = (axis_ == Axis::VERTICAL);
552     auto&& callback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
553         auto scroll = weakScroll.Upgrade();
554         if (!scroll) {
555             LOGE("render scroll is released");
556             return false;
557         }
558         if (source == SCROLL_FROM_START) {
559             scroll->NotifyDragStart(value);
560             scroll->currentDeltaInMain_ = 0.0;
561             return true;
562         }
563         Offset delta;
564         if (isVertical) {
565             delta.SetX(0.0);
566             delta.SetY(-value);
567         } else {
568             delta.SetX(-value);
569             delta.SetY(0.0);
570         }
571         scroll->AdjustOffset(delta, source);
572         scroll->NotifyDragUpdate(scroll->GetMainOffset(delta), source);
573 
574         // Stop animator of scroll bar.
575         auto scrollBarProxy = scroll->scrollBarProxy_;
576         if (scrollBarProxy) {
577             scrollBarProxy->StopScrollBarAnimator();
578         }
579         return scroll->UpdateOffset(delta, source);
580     };
581     // Initializes scrollable with different direction.
582     if (scrollable_) {
583         scrollable_->SetAxis(axis_ == Axis::VERTICAL ? Axis::VERTICAL : Axis::HORIZONTAL);
584         scrollable_->SetCallback(callback);
585     } else {
586         if (isVertical) {
587             scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, Axis::VERTICAL);
588             scrollable_->InitRelatedParent(GetParent());
589         } else {
590             scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, Axis::HORIZONTAL);
591         }
592         scrollable_->SetScrollableNode(AceType::WeakClaim(this));
593     }
594     scrollable_->SetOnScrollBegin(onScrollBegin_);
595     scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
596         auto scroll = weak.Upgrade();
597         if (!scroll) {
598             return;
599         }
600         scroll->ProcessScrollOverCallback(velocity);
601     });
602     scrollable_->SetWatchFixCallback([weak = AceType::WeakClaim(this)](double final, double current) {
603         auto scroll = weak.Upgrade();
604         if (scroll) {
605             return scroll->GetFixPositionOnWatch(final, current);
606         }
607         return final;
608     });
609     scrollable_->Initialize(GetContext());
610     scrollable_->SetNodeId(GetAccessibilityNodeId());
611     scrollable_->SetScrollEnd([weakScroll = AceType::WeakClaim(this)]() {
612         auto scroll = weakScroll.Upgrade();
613         if (scroll) {
614             scroll->MarkNeedLayout(true);
615         }
616     });
617     InitializeScrollable(scrollable_);
618 
619     currentBottomOffset_ = Offset::Zero();
620     currentOffset_ = Offset::Zero();
621     lastOffset_ = Offset::Zero();
622     ResetScrollEventCallBack();
623     SetEdgeEffectAttribute();
624 }
625 
SetEdgeEffectAttribute()626 void RenderScroll::SetEdgeEffectAttribute()
627 {
628     if (scrollEffect_ && scrollable_) {
629         scrollEffect_->SetScrollable(scrollable_);
630         scrollEffect_->RegisterSpringCallback();
631         if (scrollEffect_->IsSpringEffect()) {
632             scrollable_->SetOutBoundaryCallback([weakScroll = AceType::WeakClaim(this)]() {
633                 auto scroll = weakScroll.Upgrade();
634                 if (scroll) {
635                     return scroll->IsOutOfBoundary();
636                 }
637                 return false;
638             });
639         }
640     }
641 }
642 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)643 void RenderScroll::OnTouchTestHit(
644     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
645 {
646     if (!GetVisible() || axis_ == Axis::NONE) {
647         return;
648     }
649     if (!scrollable_) {
650         return;
651     }
652     if (scrollable_->Available() && scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
653         scrollBar_->AddScrollBarController(coordinateOffset, result);
654     } else {
655         scrollable_->SetCoordinateOffset(coordinateOffset);
656         auto newTouchRestrict = touchRestrict;
657         AdjustTouchRestrict(newTouchRestrict);
658         scrollable_->SetDragTouchRestrict(newTouchRestrict);
659         result.emplace_back(scrollable_);
660     }
661     touchRecognizer_->SetCoordinateOffset(coordinateOffset);
662     result.emplace_back(touchRecognizer_);
663 }
664 
HandleMouseHoverEvent(const MouseState mouseState)665 void RenderScroll::HandleMouseHoverEvent(const MouseState mouseState)
666 {
667     LOGI("scroll hover state: %{public}d", mouseState);
668     if (scrollBar_ && mouseState == MouseState::NONE) {
669         scrollBar_->SetIsHover(false);
670     }
671 }
672 
HandleMouseEvent(const MouseEvent & event)673 bool RenderScroll::HandleMouseEvent(const MouseEvent& event)
674 {
675     if (!scrollBar_) {
676         return RenderNode::HandleMouseEvent(event);
677     }
678     auto globalOffset = GetGlobalOffset();
679     auto localPoint = Point(event.x - globalOffset.GetX(), event.y - globalOffset.GetY());
680     bool isInBarRegion = scrollBar_->InBarRegion(localPoint);
681     scrollBar_->SetIsHover(isInBarRegion);
682     return isInBarRegion;
683 }
684 
JumpToIndex(int32_t index,int32_t source)685 void RenderScroll::JumpToIndex(int32_t index, int32_t source)
686 {
687     LOGE("Do not support in base RenderScroll");
688 }
689 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)690 void RenderScroll::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
691 {
692     if ((IsRowReverse() && scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) ||
693         (!IsRowReverse() && scrollEdgeType == ScrollEdgeType::SCROLL_TOP)) {
694         double distance = -GetMainOffset(currentOffset_);
695         ScrollBy(distance, distance, smooth);
696     } else {
697         double distance = mainScrollExtent_ - GetMainOffset(currentOffset_);
698         ScrollBy(distance, distance, smooth);
699     }
700 }
701 
ScrollPage(bool reverse,bool smooth,const std::function<void ()> & onFinish)702 bool RenderScroll::ScrollPage(bool reverse, bool smooth, const std::function<void()>& onFinish)
703 {
704     if ((IsRowReverse() && !reverse) || (!IsRowReverse() && reverse)) {
705         double distance = -GetMainSize(viewPort_);
706         ScrollBy(distance, distance, smooth, onFinish);
707     } else {
708         double distance = GetMainSize(viewPort_);
709         ScrollBy(distance, distance, smooth, onFinish);
710     }
711     return true;
712 }
713 
JumpToPosition(double position,int32_t source)714 void RenderScroll::JumpToPosition(double position, int32_t source)
715 {
716     // If an animation is playing, stop it.
717     if (animator_) {
718         if (!animator_->IsStopped()) {
719             animator_->Stop();
720         }
721         animator_->ClearInterpolators();
722     }
723     DoJump(position, source);
724     HandleScrollBarEnd();
725 }
726 
DoJump(double position,int32_t source)727 void RenderScroll::DoJump(double position, int32_t source)
728 {
729     LOGD("jump to position: %{public}lf", position);
730     if (!NearEqual(GetCurrentPosition(), position)) {
731         Offset delta;
732         if (axis_ == Axis::VERTICAL) {
733             delta.SetY(position - GetMainOffset(currentOffset_));
734         } else {
735             delta.SetX(position - GetMainOffset(currentOffset_));
736         }
737 
738         UpdateOffset(delta, source);
739     }
740 }
741 
AnimateTo(double position,float duration,const RefPtr<Curve> & curve,bool limitDuration,const std::function<void ()> & onFinish)742 void RenderScroll::AnimateTo(double position, float duration, const RefPtr<Curve>& curve, bool limitDuration,
743     const std::function<void()>& onFinish)
744 {
745     LOGD("animate from position %{public}lf to %{public}lf, duration: %{public}f", GetCurrentPosition(), position,
746         duration);
747     if (!animator_) {
748         animator_ = CREATE_ANIMATOR(GetContext());
749         CHECK_NULL_VOID(animator_);
750     }
751     if (!animator_->IsStopped()) {
752         animator_->Stop();
753     }
754     animator_->ClearInterpolators();
755 
756     // Send event to accessibility when scroll start.
757     auto context = GetContext().Upgrade();
758     if (context) {
759         AccessibilityEvent scrollEvent;
760         scrollEvent.nodeId = GetAccessibilityNodeId();
761         scrollEvent.eventType = "scrollstart";
762         context->SendEventToAccessibility(scrollEvent);
763     }
764     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(GetCurrentPosition(), position, curve);
765     animation->AddListener([weakScroll = AceType::WeakClaim(this)](double value) {
766         auto scroll = weakScroll.Upgrade();
767         if (scroll) {
768             scroll->DoJump(value);
769         }
770     });
771     animator_->AddInterpolator(animation);
772     animator_->SetDuration(duration);
773     animator_->ClearStopListeners();
774     animator_->Play();
775     auto weakScroll = AceType::WeakClaim(this);
776     auto weakScrollBar = AceType::WeakClaim(AceType::RawPtr(scrollBar_));
777     animator_->AddStopListener([weakScroll, weakScrollBar, onFinish, context = context_]() {
778         auto scrollBar = weakScrollBar.Upgrade();
779         if (scrollBar) {
780             scrollBar->HandleScrollBarEnd();
781         }
782         // Send event to accessibility when scroll end.
783         auto scroll = weakScroll.Upgrade();
784         if (scroll) {
785             auto context = scroll->GetContext().Upgrade();
786             if (context) {
787                 AccessibilityEvent scrollEvent;
788                 scrollEvent.nodeId = scroll->GetAccessibilityNodeId();
789                 scrollEvent.eventType = "scrollend";
790                 context->SendEventToAccessibility(scrollEvent);
791                 if (context->GetIsDeclarative() && scroll->scrollable_) {
792                     scroll->scrollable_->ChangeMoveStatus(true);
793                     scroll->scrollable_->ProcessScrollMotionStop();
794                 }
795             }
796         }
797 
798         if (onFinish) {
799             onFinish();
800         }
801     });
802 }
803 
AnimateToTarget(const ComposeId & targetId,float duration,const RefPtr<Curve> & curve,bool limitDuration,const std::function<void ()> & onFinish)804 bool RenderScroll::AnimateToTarget(const ComposeId& targetId, float duration, const RefPtr<Curve>& curve,
805     bool limitDuration, const std::function<void()>& onFinish)
806 {
807     auto context = GetContext().Upgrade();
808     if (!context) {
809         return false;
810     }
811     auto targetElement = context->GetComposedElementById(targetId);
812     if (!targetElement) {
813         return false;
814     }
815     auto targetRender = targetElement->GetRenderNode();
816     if (!targetRender) {
817         return false;
818     }
819 
820     auto globalOffset = targetRender->GetGlobalOffset() - GetPosition();
821     double distance = ((axis_ == Axis::VERTICAL) ? globalOffset.GetY() : globalOffset.GetX()) + GetCurrentPosition();
822     AnimateTo(distance, duration, curve, limitDuration, onFinish);
823     return true;
824 }
825 
ScrollBy(double pixelX,double pixelY,bool smooth,const std::function<void ()> & onFinish)826 void RenderScroll::ScrollBy(double pixelX, double pixelY, bool smooth, const std::function<void()>& onFinish)
827 {
828     double distance = (axis_ == Axis::VERTICAL) ? pixelY : pixelX;
829     if (NearZero(distance)) {
830         return;
831     }
832     double position = GetMainOffset(currentOffset_) + distance;
833     if (smooth) {
834         AnimateTo(position, fabs(distance) * UNIT_CONVERT / SCROLL_BY_SPEED, Curves::EASE_OUT, true, onFinish);
835     } else {
836         JumpToPosition(position);
837     }
838 }
839 
GetCurrentPosition() const840 double RenderScroll::GetCurrentPosition() const
841 {
842     double position = 0.0;
843     if (axis_ == Axis::VERTICAL) {
844         position = currentOffset_.GetY();
845     } else {
846         position = currentOffset_.GetX();
847     }
848     return position;
849 }
850 
Update(const RefPtr<Component> & component)851 void RenderScroll::Update(const RefPtr<Component>& component)
852 {
853     auto scroll = AceType::DynamicCast<ScrollComponent>(component);
854     if (scroll == nullptr) {
855         LOGI("scroll component is null, which it is multi scroll, not single scroll");
856         return;
857     }
858     hasHeight_ = scroll->GetHasHeight();
859     hasWidth_ = scroll->GetHasWidth();
860     if (!animator_) {
861         animator_ = CREATE_ANIMATOR(GetContext());
862     }
863     // ApplyRestoreInfo maybe change currentOffset_
864     ApplyRestoreInfo();
865     lastOffset_ = currentOffset_;
866     // Send scroll none when first build.
867     HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
868     MarkNeedLayout();
869     onReachStart_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachStart(), context_);
870     onReachEnd_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachEnd(), context_);
871     onReachTop_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachTop(), context_);
872     onReachBottom_ = AceAsyncEvent<void(const std::string&)>::Create(scroll->GetOnReachBottom(), context_);
873     scrollBarProxy_ = scroll->GetScrollBarProxy();
874     InitScrollBarProxy();
875 }
876 
InitScrollBarProxy()877 void RenderScroll::InitScrollBarProxy()
878 {
879     if (!scrollBarProxy_) {
880         return;
881     }
882     auto isVertical = (axis_ == Axis::VERTICAL);
883     auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
884         auto scroll = weakScroll.Upgrade();
885         if (!scroll) {
886             LOGE("render scroll is released");
887             return false;
888         }
889         Offset delta;
890         if (isVertical) {
891             delta.SetX(0.0);
892             delta.SetY(-value);
893         } else {
894             delta.SetX(-value);
895             delta.SetY(0.0);
896         }
897         scroll->AdjustOffset(delta, source);
898         return scroll->UpdateOffset(delta, source);
899     };
900     scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
901     scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), scrollCallback });
902 }
903 
SetBarCallBack(bool isVertical)904 void RenderScroll::SetBarCallBack(bool isVertical)
905 {
906     if (scrollBar_ && scrollBar_->NeedScrollBar()) {
907         auto&& barEndCallback = [weakScroll = AceType::WeakClaim(this), isVertical](int32_t value) {
908             auto scroll = weakScroll.Upgrade();
909             if (!scroll) {
910                 LOGE("render scroll is released");
911                 return;
912             }
913             scroll->scrollBarOpacity_ = value;
914             scroll->MarkNeedRender();
915         };
916         auto&& scrollEndCallback = [weakScroll = AceType::WeakClaim(this), isVertical]() {
917             auto scroll = weakScroll.Upgrade();
918             if (!scroll) {
919                 LOGE("render scroll is released");
920                 return;
921             }
922             if (scroll->positionController_) {
923                 scroll->positionController_->HandleScrollEvent(
924                     std::make_shared<ScrollEventInfo>(ScrollEvent::SCROLL_END, 0.0, 0.0, -1));
925             }
926             // Send scroll none when scroll is end.
927             scroll->HandleScrollPosition(0.0, 0.0, SCROLL_NONE);
928         };
929         auto&& scrollCallback = [weakScroll = AceType::WeakClaim(this), isVertical](double value, int32_t source) {
930             auto scroll = weakScroll.Upgrade();
931             if (!scroll) {
932                 LOGE("render scroll is released");
933                 return false;
934             }
935             Offset delta;
936             if (isVertical) {
937                 delta.SetX(0.0);
938                 delta.SetY(-value);
939             } else {
940                 delta.SetX(-value);
941                 delta.SetY(0.0);
942             }
943             scroll->AdjustOffset(delta, source);
944 
945             return scroll->UpdateOffset(delta, source);
946         };
947         scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
948     }
949 }
950 
GetEstimatedHeight()951 double RenderScroll::GetEstimatedHeight()
952 {
953     if (ReachMaxCount()) {
954         estimatedHeight_ = scrollBarExtent_;
955     } else {
956         estimatedHeight_ = std::max(estimatedHeight_, scrollBarExtent_);
957     }
958     return estimatedHeight_;
959 }
960 
HandleScrollOverByBar(double velocity)961 void RenderScroll::HandleScrollOverByBar(double velocity)
962 {
963     // the direction of bar and scroll is opposite, so it need be negative
964     if (scrollEffect_) {
965         scrollEffect_->ProcessScrollOver(-velocity);
966     }
967 }
968 
HandleScrollBarEnd()969 void RenderScroll::HandleScrollBarEnd()
970 {
971     if (scrollBar_) {
972         scrollBar_->HandleScrollBarEnd();
973     }
974 }
975 
HandleRotate(double rotateValue,bool isVertical)976 void RenderScroll::HandleRotate(double rotateValue, bool isVertical)
977 {
978     auto context = GetContext().Upgrade();
979     if (!context) {
980         LOGE("context is null");
981         return;
982     }
983     double value = context->NormalizeToPx(Dimension(rotateValue, DimensionUnit::VP)) * ROTATE_FACTOR;
984 
985     // Vertical or horizontal, different axis
986     Offset delta;
987     if (isVertical) {
988         delta.SetX(0.0);
989         delta.SetY(value);
990     } else {
991         delta.SetX(value);
992         delta.SetY(0.0);
993     }
994     UpdateOffset(delta, SCROLL_FROM_ROTATE);
995 }
996 
OnChildAdded(const RefPtr<RenderNode> & child)997 void RenderScroll::OnChildAdded(const RefPtr<RenderNode>& child)
998 {
999     child->SetSlipFactorSetting([weakScroll = AceType::WeakClaim(this)](double slipFactor) {
1000         auto scroll = weakScroll.Upgrade();
1001         if (scroll) {
1002             scroll->scrollable_->SetSlipFactor(slipFactor);
1003         }
1004     });
1005 }
1006 
OnReachStart() const1007 void RenderScroll::OnReachStart() const
1008 {
1009     if (onReachStart_) {
1010         onReachStart_(std::string("\"reachstart\",null"));
1011     }
1012 }
1013 
OnReachEnd() const1014 void RenderScroll::OnReachEnd() const
1015 {
1016     if (onReachEnd_) {
1017         onReachEnd_(std::string("\"reachend\",null"));
1018     }
1019 }
1020 
OnReachTop() const1021 void RenderScroll::OnReachTop() const
1022 {
1023     if (onReachTop_) {
1024         onReachTop_(std::string("\"reachtop\",null"));
1025     }
1026 }
1027 
OnReachBottom() const1028 void RenderScroll::OnReachBottom() const
1029 {
1030     if (onReachBottom_) {
1031         onReachBottom_(std::string("\"reachbottom\",null"));
1032     }
1033 }
1034 
UpdateTouchRect()1035 void RenderScroll::UpdateTouchRect()
1036 {
1037     RenderNode::UpdateTouchRect();
1038     if (!scrollBar_) {
1039         return;
1040     }
1041     double scrollBarPosition = NormalizeToPx(scrollBar_->GetPosition());
1042     if (!NearZero(scrollBarPosition)) {
1043         touchRect_.SetWidth(touchRect_.Width() + scrollBarPosition);
1044         if (scrollBar_->GetPositionMode() == PositionMode::LEFT) {
1045             touchRect_.SetLeft(touchRect_.Left() - scrollBarPosition);
1046         }
1047     }
1048 }
1049 
IsAxisScrollable(AxisDirection direction)1050 bool RenderScroll::IsAxisScrollable(AxisDirection direction)
1051 {
1052     return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !IsAtTop()) ||
1053             ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !IsAtBottom()));
1054 }
1055 
HandleAxisEvent(const AxisEvent & event)1056 void RenderScroll::HandleAxisEvent(const AxisEvent& event)
1057 {
1058 }
1059 
ProvideRestoreInfo()1060 std::string RenderScroll::ProvideRestoreInfo()
1061 {
1062     if (!NearZero(currentOffset_.GetY()) && axis_ == Axis::VERTICAL) {
1063         return std::to_string(currentOffset_.GetY());
1064     } else if (!NearZero(currentOffset_.GetX()) && axis_ == Axis::HORIZONTAL) {
1065         return std::to_string(currentOffset_.GetX());
1066     }
1067     return "";
1068 }
1069 
ApplyRestoreInfo()1070 void RenderScroll::ApplyRestoreInfo()
1071 {
1072     if (GetRestoreInfo().empty()) {
1073         return;
1074     }
1075     if (axis_ == Axis::VERTICAL) {
1076         currentOffset_.SetY(StringUtils::StringToDouble(GetRestoreInfo()));
1077     } else {
1078         currentOffset_.SetX(StringUtils::StringToDouble(GetRestoreInfo()));
1079     }
1080     SetRestoreInfo("");
1081 }
1082 
1083 } // namespace OHOS::Ace
1084