• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/geometry/dimension.h"
20 #include "base/utils/utils.h"
21 #include "core/components/scroll/scrollable.h"
22 #include "core/components_ng/pattern/scroll/scroll_edge_effect.h"
23 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
24 #include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h"
25 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
26 #include "core/components_ng/pattern/scroll/scroll_paint_property.h"
27 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
28 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
29 #include "core/components_ng/property/measure_utils.h"
30 #include "core/components_ng/property/property.h"
31 #include "core/pipeline/pipeline_base.h"
32 
33 namespace OHOS::Ace::NG {
34 
35 namespace {
36 
37 constexpr int32_t SCROLL_NONE = 0;
38 constexpr int32_t SCROLL_TOUCH_DOWN = 1;
39 constexpr int32_t SCROLL_TOUCH_UP = 2;
40 constexpr float SCROLL_RATIO = 0.52f;
41 constexpr float SCROLL_BY_SPEED = 250.0f; // move 250 pixels per second
42 constexpr float UNIT_CONVERT = 1000.0f;   // 1s convert to 1000ms
43 
CalculateFriction(float gamma)44 float CalculateFriction(float gamma)
45 {
46     return static_cast<float>(SCROLL_RATIO * std::pow(1.0 - gamma, SQUARE));
47 }
48 
CalculateOffsetByFriction(float extentOffset,float delta,float friction)49 float CalculateOffsetByFriction(float extentOffset, float delta, float friction)
50 {
51     if (NearZero(friction)) {
52         return delta;
53     }
54     float deltaToLimit = extentOffset / friction;
55     if (delta < deltaToLimit) {
56         return delta * friction;
57     }
58     return extentOffset + delta - deltaToLimit;
59 }
60 
61 } // namespace
62 
OnModifyDone()63 void ScrollPattern::OnModifyDone()
64 {
65     auto host = GetHost();
66     CHECK_NULL_VOID_NOLOG(host);
67     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
68     CHECK_NULL_VOID_NOLOG(layoutProperty);
69     auto paintProperty = host->GetPaintProperty<ScrollPaintProperty>();
70     CHECK_NULL_VOID_NOLOG(paintProperty);
71     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
72     if (axis != GetAxis()) {
73         SetAxis(axis);
74         ResetPosition();
75     }
76     if (!GetScrollableEvent()) {
77         AddScrollEvent();
78         RegisterScrollEventTask();
79     }
80     SetEdgeEffect(layoutProperty->GetEdgeEffect().value_or(EdgeEffect::NONE));
81     SetScrollBar(paintProperty->GetScrollBarProperty());
82     SetAccessibilityAction();
83     if (scrollSnapUpdate_) {
84         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
85     }
86 }
87 
RegisterScrollEventTask()88 void ScrollPattern::RegisterScrollEventTask()
89 {
90     auto eventHub = GetHost()->GetEventHub<ScrollEventHub>();
91     CHECK_NULL_VOID(eventHub);
92     auto scrollFrameBeginEvent = eventHub->GetScrollFrameBeginEvent();
93     SetScrollFrameBeginCallback(scrollFrameBeginEvent);
94 }
95 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)96 bool ScrollPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
97 {
98     if (config.skipMeasure && config.skipLayout) {
99         return false;
100     }
101     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
102     CHECK_NULL_RETURN_NOLOG(layoutAlgorithmWrapper, false);
103     auto layoutAlgorithm = DynamicCast<ScrollLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
104     CHECK_NULL_RETURN_NOLOG(layoutAlgorithm, false);
105     currentOffset_ = layoutAlgorithm->GetCurrentOffset();
106     scrollableDistance_ = layoutAlgorithm->GetScrollableDistance();
107     auto axis = GetAxis();
108     auto oldMainSize = GetMainAxisSize(viewPort_, axis);
109     auto newMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPort(), axis);
110     auto oldExtentMainSize = GetMainAxisSize(viewPortExtent_, axis);
111     auto newExtentMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPortExtent(), axis);
112     viewPortLength_ = layoutAlgorithm->GetViewPortLength();
113     viewPort_ = layoutAlgorithm->GetViewPort();
114     viewSize_ = layoutAlgorithm->GetViewSize();
115     viewPortExtent_ = layoutAlgorithm->GetViewPortExtent();
116     if (scrollSnapUpdate_ || !NearEqual(oldMainSize, newMainSize) || !NearEqual(oldExtentMainSize, newExtentMainSize)) {
117         CaleSnapOffsets();
118         scrollSnapUpdate_ = false;
119     }
120     UpdateScrollBarOffset();
121     if (config.frameSizeChange) {
122         if (GetScrollBar() != nullptr) {
123             GetScrollBar()->ScheduleDisapplearDelayTask();
124         }
125     }
126     if (scrollStop_) {
127         FireOnScrollStop();
128         scrollStop_ = false;
129     }
130     if (ScrollableIdle() && !AnimateRunning()) {
131         auto predictSnapOffset = CalePredictSnapOffset(0.0);
132         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
133             StartScrollSnapMotion(predictSnapOffset.value(), 0.0f);
134             FireOnScrollStart();
135         }
136     }
137     CheckScrollable();
138     auto host = GetHost();
139     CHECK_NULL_RETURN(host, false);
140     auto geometryNode = host->GetGeometryNode();
141     CHECK_NULL_RETURN(geometryNode, false);
142     auto offsetRelativeToWindow = host->GetOffsetRelativeToWindow();
143     auto globalViewPort = RectF(offsetRelativeToWindow, geometryNode->GetFrameRect().GetSize());
144     host->SetViewPort(globalViewPort);
145     return false;
146 }
147 
CheckScrollable()148 void ScrollPattern::CheckScrollable()
149 {
150     auto host = GetHost();
151     CHECK_NULL_VOID(host);
152     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
153     CHECK_NULL_VOID(layoutProperty);
154     SetScrollEnable(layoutProperty->GetScrollEnabled().value_or(true));
155 }
156 
FireOnScrollStart()157 void ScrollPattern::FireOnScrollStart()
158 {
159     if (GetScrollAbort()) {
160         return;
161     }
162     auto scrollBar = GetScrollBar();
163     if (scrollBar) {
164         scrollBar->PlayScrollBarStartAnimation();
165     }
166     StopScrollBarAnimatorByProxy();
167     auto host = GetHost();
168     CHECK_NULL_VOID(host);
169     auto hub = host->GetEventHub<ScrollEventHub>();
170     CHECK_NULL_VOID_NOLOG(hub);
171     auto onScrollStart = hub->GetScrollStartEvent();
172     CHECK_NULL_VOID_NOLOG(onScrollStart);
173     onScrollStart();
174 }
175 
FireOnScrollStop()176 void ScrollPattern::FireOnScrollStop()
177 {
178     if (GetScrollAbort()) {
179         SetScrollAbort(false);
180         return;
181     }
182     auto scrollBar = GetScrollBar();
183     if (scrollBar) {
184         scrollBar->ScheduleDisapplearDelayTask();
185     }
186     StartScrollBarAnimatorByProxy();
187     auto host = GetHost();
188     CHECK_NULL_VOID(host);
189     auto hub = host->GetEventHub<ScrollEventHub>();
190     CHECK_NULL_VOID_NOLOG(hub);
191     auto onScrollStop = hub->GetScrollStopEvent();
192     CHECK_NULL_VOID_NOLOG(onScrollStop);
193     onScrollStop();
194 }
195 
OnScrollCallback(float offset,int32_t source)196 bool ScrollPattern::OnScrollCallback(float offset, int32_t source)
197 {
198     if (source != SCROLL_FROM_START) {
199         if (GetAxis() == Axis::NONE) {
200             return false;
201         }
202         if (!AnimateStoped()) {
203             return false;
204         }
205         auto adjustOffset = static_cast<float>(offset);
206         AdjustOffset(adjustOffset, source);
207         return UpdateCurrentOffset(adjustOffset, source);
208     } else {
209         FireOnScrollStart();
210     }
211     return true;
212 }
213 
ToJsonValue(std::unique_ptr<JsonValue> & json) const214 void ScrollPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
215 {
216     json->Put("friction", GetFriction());
217 }
218 
OnScrollEndCallback()219 void ScrollPattern::OnScrollEndCallback()
220 {
221     auto host = GetHost();
222     CHECK_NULL_VOID(host);
223     auto eventHub = host->GetEventHub<ScrollEventHub>();
224     CHECK_NULL_VOID(eventHub);
225     auto scrollEndEvent = eventHub->GetScrollEndEvent();
226     if (scrollEndEvent) {
227         scrollEndEvent();
228     }
229     scrollStop_ = true;
230     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
231 }
232 
ResetPosition()233 void ScrollPattern::ResetPosition()
234 {
235     currentOffset_ = 0.0f;
236     lastOffset_ = 0.0f;
237 }
238 
IsAtTop() const239 bool ScrollPattern::IsAtTop() const
240 {
241     return GreatOrEqual(currentOffset_, 0.0);
242 }
243 
IsAtBottom() const244 bool ScrollPattern::IsAtBottom() const
245 {
246     bool atBottom = LessOrEqual(currentOffset_, -scrollableDistance_);
247     // TODO: ignore ReachMaxCount
248     return atBottom;
249 }
250 
GetOverScrollOffset(double delta) const251 OverScrollOffset ScrollPattern::GetOverScrollOffset(double delta) const
252 {
253     OverScrollOffset offset = { 0, 0 };
254     auto startPos = currentOffset_;
255     auto newStartPos = startPos + delta;
256     if (startPos > 0 && newStartPos > 0) {
257         offset.start = delta;
258     }
259     if (startPos > 0 && newStartPos <= 0) {
260         offset.start = -startPos;
261     }
262     if (startPos <= 0 && newStartPos > 0) {
263         offset.start = newStartPos;
264     }
265 
266     auto endPos = currentOffset_;
267     auto newEndPos = endPos + delta;
268     auto endRefences = -scrollableDistance_;
269     if (endPos < endRefences && newEndPos < endRefences) {
270         offset.end = delta;
271     }
272     if (endPos < endRefences && newEndPos >= endRefences) {
273         offset.end = endRefences - endPos;
274     }
275     if (endPos >= endRefences && newEndPos < endRefences) {
276         offset.end = newEndPos - endRefences;
277     }
278     return offset;
279 }
280 
IsOutOfBoundary() const281 bool ScrollPattern::IsOutOfBoundary() const
282 {
283     return Positive(currentOffset_) || LessNotEqual(currentOffset_, -scrollableDistance_);
284 }
285 
ScrollPageCheck(float delta,int32_t source)286 bool ScrollPattern::ScrollPageCheck(float delta, int32_t source)
287 {
288     return true;
289 }
290 
AdjustOffset(float & delta,int32_t source)291 void ScrollPattern::AdjustOffset(float& delta, int32_t source)
292 {
293     if (NearZero(delta) || NearZero(viewPortLength_) || source == SCROLL_FROM_ANIMATION ||
294         source == SCROLL_FROM_ANIMATION_SPRING) {
295         return;
296     }
297     // the distance above the top, if lower than top, it is zero
298     float overScrollPastStart = 0.0f;
299     // the distance below the bottom, if higher than bottom, it is zero
300     float overScrollPastEnd = 0.0f;
301     float overScrollPast = 0.0f;
302     // TODO: not consider rowReverse or colReverse
303     overScrollPastStart = std::max(currentOffset_, 0.0f);
304     if (Positive(scrollableDistance_)) {
305         overScrollPastEnd = std::max(-scrollableDistance_ - currentOffset_, 0.0f);
306     } else {
307         overScrollPastEnd = std::abs(std::min(currentOffset_, 0.0f));
308     }
309     // do not adjust offset if direction opposite from the overScroll direction when out of boundary
310     if ((overScrollPastStart > 0.0f && delta < 0.0f) || (overScrollPastEnd > 0.0f && delta > 0.0f)) {
311         return;
312     }
313     overScrollPast = std::max(overScrollPastStart, overScrollPastEnd);
314     if (overScrollPast == 0.0f) {
315         return;
316     }
317     float friction = CalculateFriction((overScrollPast - std::abs(delta)) / viewPortLength_);
318     float direction = delta > 0.0f ? 1.0f : -1.0f;
319     delta = direction * CalculateOffsetByFriction(overScrollPast, std::abs(delta), friction);
320 }
321 
ValidateOffset(int32_t source)322 void ScrollPattern::ValidateOffset(int32_t source)
323 {
324     if (scrollableDistance_ <= 0.0f || source == SCROLL_FROM_JUMP) {
325         return;
326     }
327 
328     // restrict position between top and bottom
329     if (IsRestrictBoundary() || source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING ||
330         source == SCROLL_FROM_ROTATE) {
331         if (GetAxis() == Axis::HORIZONTAL) {
332             if (IsRowReverse()) {
333                 currentOffset_ = std::clamp(currentOffset_, 0.0f, scrollableDistance_);
334             } else {
335                 currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f);
336             }
337         } else {
338             currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f);
339         }
340     } else {
341         float scrollBarOutBoundaryExtent = 0.0f;
342         if (currentOffset_ > 0) {
343             scrollBarOutBoundaryExtent = currentOffset_;
344         } else if ((-currentOffset_) >= (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_)) && ReachMaxCount()) {
345             scrollBarOutBoundaryExtent = -currentOffset_ - (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_));
346         }
347         HandleScrollBarOutBoundary(scrollBarOutBoundaryExtent);
348     }
349 }
350 
HandleScrollPosition(float scroll,int32_t scrollState)351 void ScrollPattern::HandleScrollPosition(float scroll, int32_t scrollState)
352 {
353     auto eventHub = GetEventHub<ScrollEventHub>();
354     CHECK_NULL_VOID(eventHub);
355     auto onScroll = eventHub->GetOnScrollEvent();
356     CHECK_NULL_VOID_NOLOG(onScroll);
357     // not consider async call
358     Dimension scrollX(0, DimensionUnit::VP);
359     Dimension scrollY(0, DimensionUnit::VP);
360     Dimension scrollPx(scroll, DimensionUnit::PX);
361     auto scrollVpValue = scrollPx.ConvertToVp();
362     if (GetAxis() == Axis::HORIZONTAL) {
363         scrollX.SetValue(scrollVpValue);
364     } else {
365         scrollY.SetValue(scrollVpValue);
366     }
367     onScroll(scrollX, scrollY);
368 }
369 
IsCrashTop() const370 bool ScrollPattern::IsCrashTop() const
371 {
372     bool scrollUpToReachTop = LessNotEqual(lastOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
373     bool scrollDownToReachTop = GreatNotEqual(lastOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
374     return scrollUpToReachTop || scrollDownToReachTop;
375 }
376 
IsCrashBottom() const377 bool ScrollPattern::IsCrashBottom() const
378 {
379     float minExtent = -scrollableDistance_;
380     bool scrollDownToReachEnd = GreatNotEqual(lastOffset_, minExtent) && LessOrEqual(currentOffset_, minExtent);
381     bool scrollUpToReachEnd = LessNotEqual(lastOffset_, minExtent) && GreatOrEqual(currentOffset_, minExtent);
382     return (scrollUpToReachEnd || scrollDownToReachEnd) && ReachMaxCount();
383 }
384 
HandleCrashTop() const385 void ScrollPattern::HandleCrashTop() const
386 {
387     auto frameNode = GetHost();
388     CHECK_NULL_VOID(frameNode);
389     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
390     CHECK_NULL_VOID(eventHub);
391     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
392     CHECK_NULL_VOID_NOLOG(onScrollEdge);
393     // not consider async call
394     if (GetAxis() == Axis::HORIZONTAL) {
395         onScrollEdge(ScrollEdge::LEFT);
396         return;
397     }
398     onScrollEdge(ScrollEdge::TOP);
399 }
400 
HandleCrashBottom() const401 void ScrollPattern::HandleCrashBottom() const
402 {
403     auto frameNode = GetHost();
404     CHECK_NULL_VOID(frameNode);
405     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
406     CHECK_NULL_VOID(eventHub);
407     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
408     CHECK_NULL_VOID_NOLOG(onScrollEdge);
409     if (GetAxis() == Axis::HORIZONTAL) {
410         onScrollEdge(ScrollEdge::RIGHT);
411         return;
412     }
413     onScrollEdge(ScrollEdge::BOTTOM);
414 }
415 
UpdateCurrentOffset(float delta,int32_t source)416 bool ScrollPattern::UpdateCurrentOffset(float delta, int32_t source)
417 {
418     SetScrollSource(source);
419     auto host = GetHost();
420     CHECK_NULL_RETURN(host, false);
421     // TODO: ignore handle refresh
422     if (source != SCROLL_FROM_JUMP && !HandleEdgeEffect(delta, source, viewSize_)) {
423         if (IsOutOfBoundary()) {
424             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
425         }
426         return false;
427     }
428     // TODO: scrollBar effect!!
429     lastOffset_ = currentOffset_;
430     currentOffset_ += delta;
431     ValidateOffset(source);
432     int32_t touchState = SCROLL_NONE;
433     if (source == SCROLL_FROM_UPDATE) {
434         touchState = SCROLL_TOUCH_DOWN;
435     } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
436         touchState = SCROLL_TOUCH_UP;
437     }
438     HandleScrollPosition(-delta, touchState);
439     if (IsCrashTop()) {
440         HandleCrashTop();
441     } else if (IsCrashBottom()) {
442         HandleCrashBottom();
443     }
444     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
445     return true;
446 }
447 
OnAnimateStop()448 void ScrollPattern::OnAnimateStop()
449 {
450     auto host = GetHost();
451     CHECK_NULL_VOID_NOLOG(host);
452     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
453     host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
454     scrollStop_ = true;
455 }
456 
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth)457 void ScrollPattern::AnimateTo(float position, float duration, const RefPtr<Curve>& curve, bool smooth)
458 {
459     auto host = GetHost();
460     CHECK_NULL_VOID(host);
461     host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
462     ScrollablePattern::AnimateTo(position, duration, curve, smooth);
463     FireOnScrollStart();
464 }
465 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)466 void ScrollPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
467 {
468     if (scrollEdgeType == ScrollEdgeType::SCROLL_NONE) {
469         return;
470     }
471     float distance = scrollEdgeType == ScrollEdgeType::SCROLL_TOP ? -currentOffset_ :
472         (-scrollableDistance_ - currentOffset_);
473     ScrollBy(distance, distance, smooth);
474 }
475 
ScrollBy(float pixelX,float pixelY,bool smooth,const std::function<void ()> & onFinish)476 void ScrollPattern::ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish)
477 {
478     float distance = (GetAxis() == Axis::VERTICAL) ? pixelY : pixelX;
479     if (NearZero(distance)) {
480         return;
481     }
482     float position = currentOffset_ + distance;
483     if (smooth) {
484         AnimateTo(-position, fabs(distance) * UNIT_CONVERT / SCROLL_BY_SPEED, Curves::EASE_OUT, true);
485         return;
486     }
487     JumpToPosition(position);
488 }
489 
ScrollPage(bool reverse,bool smooth,const std::function<void ()> & onFinish)490 bool ScrollPattern::ScrollPage(bool reverse, bool smooth, const std::function<void()>& onFinish)
491 {
492     float distance = reverse ? viewPortLength_ : -viewPortLength_;
493     ScrollBy(distance, distance, smooth, onFinish);
494     return true;
495 }
496 
JumpToPosition(float position,int32_t source)497 void ScrollPattern::JumpToPosition(float position, int32_t source)
498 {
499     // If an animation is playing, stop it.
500     StopAnimate();
501     float cachePosition = currentOffset_;
502     DoJump(position, source);
503     if (cachePosition != currentOffset_) {
504         auto host = GetHost();
505         CHECK_NULL_VOID(host);
506         host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
507     }
508 }
509 
ScrollTo(float position)510 void ScrollPattern::ScrollTo(float position)
511 {
512     JumpToPosition(-position, SCROLL_FROM_JUMP);
513 }
514 
DoJump(float position,int32_t source)515 void ScrollPattern::DoJump(float position, int32_t source)
516 {
517     float setPosition = (GetAxis() == Axis::HORIZONTAL && IsRowReverse()) ? -position : position;
518     if (!NearEqual(currentOffset_, setPosition)) {
519         UpdateCurrentOffset(setPosition - currentOffset_, source);
520     }
521 }
522 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)523 void ScrollPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
524 {
525     scrollEffect->SetCurrentPositionCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
526         auto scroll = weakScroll.Upgrade();
527         CHECK_NULL_RETURN_NOLOG(scroll, 0.0);
528         return scroll->GetCurrentPosition();
529     });
530     scrollEffect->SetLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
531         auto scroll = weakScroll.Upgrade();
532         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
533             return -scroll->GetScrollableDistance();
534         }
535         return 0.0;
536     });
537     scrollEffect->SetTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
538         auto scroll = weakScroll.Upgrade();
539         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
540             return scroll->GetScrollableDistance();
541         }
542         return 0.0;
543     });
544     scrollEffect->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
545         auto scroll = weakScroll.Upgrade();
546         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
547             return -scroll->GetScrollableDistance();
548         }
549         return 0.0;
550     });
551     scrollEffect->SetInitTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
552         auto scroll = weakScroll.Upgrade();
553         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
554             return scroll->GetScrollableDistance();
555         }
556         return 0.0;
557     });
558 }
559 
UpdateScrollBarOffset()560 void ScrollPattern::UpdateScrollBarOffset()
561 {
562     if (!GetScrollBar() && !GetScrollBarProxy()) {
563         return;
564     }
565     auto host = GetHost();
566     CHECK_NULL_VOID_NOLOG(host);
567     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
568     CHECK_NULL_VOID_NOLOG(layoutProperty);
569     auto padding = layoutProperty->CreatePaddingAndBorder();
570     Size size(viewSize_.Width(), viewSize_.Height());
571     auto viewPortExtent = viewPortExtent_;
572     AddPaddingToSize(padding, viewPortExtent);
573     auto estimatedHeight = (GetAxis() == Axis::HORIZONTAL) ? viewPortExtent.Width() : viewPortExtent.Height();
574     UpdateScrollBarRegion(-currentOffset_, estimatedHeight, size, Offset(0.0f, 0.0f));
575 }
576 
SetAccessibilityAction()577 void ScrollPattern::SetAccessibilityAction()
578 {
579     auto host = GetHost();
580     CHECK_NULL_VOID(host);
581     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
582     CHECK_NULL_VOID(accessibilityProperty);
583     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
584         const auto& pattern = weakPtr.Upgrade();
585         CHECK_NULL_VOID(pattern);
586         if (pattern->IsScrollable() && pattern->GetScrollableDistance() > 0.0f) {
587             pattern->ScrollPage(false, true);
588         }
589     });
590 
591     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
592         const auto& pattern = weakPtr.Upgrade();
593         CHECK_NULL_VOID(pattern);
594         if (pattern->IsScrollable() && pattern->GetScrollableDistance() > 0.0f) {
595             pattern->ScrollPage(true, true);
596         }
597     });
598 }
599 
GetOffsetToScroll(const RefPtr<FrameNode> & childFrame) const600 OffsetF ScrollPattern::GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const
601 {
602     auto frameNode = GetHost();
603     CHECK_NULL_RETURN(frameNode, OffsetF());
604     CHECK_NULL_RETURN(childFrame, OffsetF());
605     auto childGeometryNode = childFrame->GetGeometryNode();
606     CHECK_NULL_RETURN(childGeometryNode, OffsetF());
607     OffsetF result = childGeometryNode->GetFrameOffset();
608     auto parent = childFrame->GetParent();
609     while (parent) {
610         auto parentFrame = AceType::DynamicCast<FrameNode>(parent);
611         if (!parentFrame) {
612             parent = parent->GetParent();
613             continue;
614         }
615         if (parentFrame == frameNode) {
616             return result;
617         }
618         auto parentGeometryNode = parentFrame->GetGeometryNode();
619         if (!parentGeometryNode) {
620             parent = parent->GetParent();
621             continue;
622         }
623         result += parentGeometryNode->GetFrameOffset();
624         parent = parent->GetParent();
625     }
626     return OffsetF(0.0, 0.0);
627 }
628 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)629 bool ScrollPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
630 {
631     CHECK_NULL_RETURN(focusFrameNode, false);
632     auto focusGeometryNode = focusFrameNode->GetGeometryNode();
633     CHECK_NULL_RETURN(focusGeometryNode, false);
634     auto focusNodeSize = focusGeometryNode->GetFrameSize();
635     auto focusNodeOffsetToScrolll = GetOffsetToScroll(focusFrameNode);
636     auto scrollFrame = GetHost();
637     CHECK_NULL_RETURN(scrollFrame, false);
638     auto scrollGeometry = scrollFrame->GetGeometryNode();
639     CHECK_NULL_RETURN(scrollGeometry, false);
640     auto scrollFrameSize = scrollGeometry->GetFrameSize();
641     LOGD("Child: %{public}s/%{public}d on focus. Size is (%{public}f,%{public}f). Offset to Scroll is "
642          "(%{public}f,%{public}f). Scroll size is (%{public}f,%{public}f)",
643         focusFrameNode->GetTag().c_str(), focusFrameNode->GetId(), focusNodeSize.Width(), focusNodeSize.Height(),
644         focusNodeOffsetToScrolll.GetX(), focusNodeOffsetToScrolll.GetY(), scrollFrameSize.Width(),
645         scrollFrameSize.Height());
646 
647     float focusNodeDiffToScroll =
648         GetAxis() == Axis::VERTICAL ? focusNodeOffsetToScrolll.GetY() : focusNodeOffsetToScrolll.GetX();
649     if (NearZero(focusNodeDiffToScroll)) {
650         return false;
651     }
652     float focusNodeLength = GetAxis() == Axis::VERTICAL ? focusNodeSize.Height() : focusNodeSize.Width();
653     float scrollFrameLength = GetAxis() == Axis::VERTICAL ? scrollFrameSize.Height() : scrollFrameSize.Width();
654     float moveOffset = 0.0;
655     if (LessNotEqual(focusNodeDiffToScroll, 0)) {
656         moveOffset = -focusNodeDiffToScroll;
657     } else if (GreatNotEqual(focusNodeDiffToScroll + focusNodeLength, scrollFrameLength)) {
658         moveOffset = scrollFrameLength - focusNodeDiffToScroll - focusNodeLength;
659     }
660     if (!NearZero(moveOffset)) {
661         LOGD("Scroll offset: %{public}f on axis: %{public}d", moveOffset, GetAxis());
662         return OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP);
663     }
664     return false;
665 }
666 
CalePredictSnapOffset(float delta)667 std::optional<float> ScrollPattern::CalePredictSnapOffset(float delta)
668 {
669     std::optional<float> predictSnapOffset;
670     CHECK_NULL_RETURN_NOLOG(!snapOffsets_.empty(), predictSnapOffset);
671     CHECK_NULL_RETURN_NOLOG(GetScrollSnapAlign() != ScrollSnapAlign::NONE, predictSnapOffset);
672     float finalPosition = currentOffset_ + delta;
673     if (!IsSnapToInterval()) {
674         if (!enableSnapToSide_.first) {
675             if (GreatNotEqual(finalPosition, *(snapOffsets_.begin() + 1)) ||
676                 GreatNotEqual(currentOffset_, *(snapOffsets_.begin() + 1))) {
677                 return predictSnapOffset;
678             }
679         }
680         if (!enableSnapToSide_.second) {
681             if (LessNotEqual(finalPosition, *(snapOffsets_.rbegin() + 1)) ||
682                 LessNotEqual(currentOffset_, *(snapOffsets_.rbegin() + 1))) {
683                 return predictSnapOffset;
684             }
685         }
686     }
687     float head = 0.0f;
688     float tail = -scrollableDistance_;
689     if (GreatOrEqual(finalPosition, head) || LessOrEqual(finalPosition, tail)) {
690         return predictSnapOffset;
691     } else if (LessNotEqual(finalPosition, head) && GreatOrEqual(finalPosition, *(snapOffsets_.begin()))) {
692         predictSnapOffset = *(snapOffsets_.begin());
693     } else if (GreatNotEqual(finalPosition, tail) && LessOrEqual(finalPosition, *(snapOffsets_.rbegin()))) {
694         predictSnapOffset = *(snapOffsets_.rbegin());
695     } else {
696         auto iter = snapOffsets_.begin() + 1;
697         float start = *(iter - 1);
698         float end = *(iter);
699         for (; iter != snapOffsets_.end(); ++iter) {
700             if (GreatOrEqual(finalPosition, *iter)) {
701                 start = *(iter - 1);
702                 end = *(iter);
703                 predictSnapOffset = (LessNotEqual(start - finalPosition, finalPosition - end) ? start : end);
704                 break;
705             }
706         }
707     }
708     if (predictSnapOffset.has_value()) {
709         predictSnapOffset = predictSnapOffset.value() - currentOffset_;
710         LOGD("CalePredictSnapOffset predictSnapOffset:%{public}f", predictSnapOffset.value());
711     }
712     return predictSnapOffset;
713 }
714 
CaleSnapOffsets()715 void ScrollPattern::CaleSnapOffsets()
716 {
717     auto scrollSnapAlign = GetScrollSnapAlign();
718     std::vector<float>().swap(snapOffsets_);
719     CHECK_NULL_VOID_NOLOG(scrollSnapAlign != ScrollSnapAlign::NONE);
720     if (IsSnapToInterval()) {
721         CaleSnapOffsetsByInterval(scrollSnapAlign);
722     } else {
723         CaleSnapOffsetsByPaginations();
724     }
725 }
726 
CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)727 void ScrollPattern::CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)
728 {
729     CHECK_NULL_VOID_NOLOG(Positive(intervalSize_.Value()));
730     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
731     auto extentMainSize = GetMainAxisSize(viewPortExtent_, GetAxis());
732     auto start = 0.0f;
733     auto end = -scrollableDistance_;
734     auto snapOffset = 0.0f;
735     auto intervalSize = 0.0f;
736     auto sizeDelta = 0.0f;
737     if (intervalSize_.Unit() == DimensionUnit::PERCENT) {
738         intervalSize = intervalSize_.Value() * mainSize;
739     } else {
740         intervalSize = intervalSize_.ConvertToPx();
741     }
742     float temp = static_cast<int32_t>(extentMainSize / intervalSize) * intervalSize;
743     switch (scrollSnapAlign) {
744         case ScrollSnapAlign::START:
745             start = 0.0f;
746             end = -temp;
747             break;
748         case ScrollSnapAlign::CENTER:
749             sizeDelta = (mainSize - intervalSize) / 2;
750             start = Positive(sizeDelta) ? sizeDelta - static_cast<int32_t>(sizeDelta / intervalSize) * intervalSize
751                                         : sizeDelta;
752             end = -temp + (mainSize - extentMainSize + temp) / 2;
753             break;
754         case ScrollSnapAlign::END:
755             sizeDelta = mainSize - intervalSize;
756             start = Positive(sizeDelta) ? mainSize - static_cast<int32_t>(mainSize / intervalSize) * intervalSize
757                                         : sizeDelta;
758             end = -scrollableDistance_;
759             break;
760         default:
761             break;
762     }
763     if (!Positive(start)) {
764         snapOffsets_.emplace_back(start);
765     }
766     snapOffset = start - intervalSize;
767     while (GreatOrEqual(snapOffset, -scrollableDistance_) && GreatOrEqual(snapOffset, end)) {
768         snapOffsets_.emplace_back(snapOffset);
769         snapOffset -= intervalSize;
770     }
771     if (GreatNotEqual(end, -scrollableDistance_)) {
772         snapOffsets_.emplace_back(end);
773     }
774 }
775 
CaleSnapOffsetsByPaginations()776 void ScrollPattern::CaleSnapOffsetsByPaginations()
777 {
778     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
779     auto start = 0.0f;
780     auto end = -scrollableDistance_;
781     auto snapOffset = 0.0f;
782     snapOffsets_.emplace_back(start);
783     int32_t length = 0;
784     if (static_cast<int32_t>(snapPaginations_.size()) > 0 && NearZero(snapPaginations_[length].Value())) {
785         length++;
786     }
787     for (; length < static_cast<int32_t>(snapPaginations_.size()); length++) {
788         if (snapPaginations_[length].Unit() == DimensionUnit::PERCENT) {
789             snapOffset = -(snapPaginations_[length].Value() * mainSize);
790         } else {
791             snapOffset = -snapPaginations_[length].ConvertToPx();
792         }
793         if (GreatNotEqual(snapOffset, -scrollableDistance_)) {
794             snapOffsets_.emplace_back(snapOffset);
795         } else {
796             break;
797         }
798     }
799     snapOffsets_.emplace_back(end);
800 }
801 
NeedScrollSnapToSide(float delta)802 bool ScrollPattern::NeedScrollSnapToSide(float delta)
803 {
804     CHECK_NULL_RETURN_NOLOG(GetScrollSnapAlign() != ScrollSnapAlign::NONE, false);
805     CHECK_NULL_RETURN_NOLOG(!IsSnapToInterval(), false);
806     auto finalPosition = currentOffset_ + delta;
807     CHECK_NULL_RETURN_NOLOG(static_cast<int32_t>(snapOffsets_.size()) > 2, false);
808     if (!enableSnapToSide_.first) {
809         if (GreatOrEqual(currentOffset_, *(snapOffsets_.begin() + 1)) &&
810             LessOrEqual(finalPosition, *(snapOffsets_.begin() + 1))) {
811             return true;
812         }
813     }
814     if (!enableSnapToSide_.second) {
815         if (LessOrEqual(currentOffset_, *(snapOffsets_.rbegin() + 1)) &&
816             GreatOrEqual(finalPosition, *(snapOffsets_.rbegin() + 1))) {
817             return true;
818         }
819     }
820     return false;
821 }
822 } // namespace OHOS::Ace::NG
823