• 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     }
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