• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/geometry/dimension.h"
20 #include "base/log/dump_log.h"
21 #include "base/utils/utils.h"
22 #include "core/components_ng/base/inspector_filter.h"
23 #include "core/components_ng/pattern/scrollable/scrollable.h"
24 #include "core/components_ng/pattern/scroll/scroll_edge_effect.h"
25 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
26 #include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h"
27 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
28 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
29 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
30 #include "core/components_ng/property/measure_utils.h"
31 #include "core/components_ng/property/property.h"
32 #include "core/pipeline/pipeline_base.h"
33 
34 namespace OHOS::Ace::NG {
35 
36 namespace {
37 constexpr float SCROLL_BY_SPEED = 250.0f; // move 250 pixels per second
38 constexpr float UNIT_CONVERT = 1000.0f;   // 1s convert to 1000ms
39 constexpr Dimension SELECT_SCROLL_MIN_WIDTH = 64.0_vp;
40 constexpr int32_t COLUMN_NUM = 2;
41 constexpr float SCROLL_PAGING_SPEED_THRESHOLD = 1200.0f;
42 constexpr int32_t SCROLL_LAYOUT_INFO_COUNT = 30;
43 constexpr int32_t SCROLL_MEASURE_INFO_COUNT = 30;
44 } // namespace
45 
OnModifyDone()46 void ScrollPattern::OnModifyDone()
47 {
48     Pattern::OnModifyDone();
49     auto host = GetHost();
50     CHECK_NULL_VOID(host);
51     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
52     CHECK_NULL_VOID(layoutProperty);
53     auto paintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
54     CHECK_NULL_VOID(paintProperty);
55     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
56     if (axis != GetAxis()) {
57         SetAxis(axis);
58         ResetPosition();
59     }
60     if (!GetScrollableEvent()) {
61         AddScrollEvent();
62     }
63     SetEdgeEffect();
64     SetScrollBar(paintProperty->GetScrollBarProperty());
65     SetAccessibilityAction();
66     if (scrollSnapUpdate_) {
67         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
68     }
69     Register2DragDropManager();
70 }
71 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)72 bool ScrollPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
73 {
74     if (config.skipMeasure && config.skipLayout) {
75         return false;
76     }
77     if (!SetScrollProperties(dirty)) {
78         return false;
79     }
80     UpdateScrollBarOffset();
81     if (config.frameSizeChange) {
82         if (GetScrollBar() != nullptr) {
83             GetScrollBar()->ScheduleDisappearDelayTask();
84         }
85     }
86     auto host = GetHost();
87     CHECK_NULL_RETURN(host, false);
88     auto eventHub = host->GetEventHub<ScrollEventHub>();
89     CHECK_NULL_RETURN(eventHub, false);
90     PrintOffsetLog(AceLogTag::ACE_SCROLL, host->GetId(), prevOffset_ - currentOffset_);
91     FireOnDidScroll(prevOffset_ - currentOffset_);
92     auto onReachStart = eventHub->GetOnReachStart();
93     FireOnReachStart(onReachStart);
94     auto onReachEnd = eventHub->GetOnReachEnd();
95     FireOnReachEnd(onReachEnd);
96     OnScrollStop(eventHub->GetOnScrollStop());
97     ScrollSnapTrigger();
98     CheckScrollable();
99     prevOffset_ = currentOffset_;
100     auto geometryNode = host->GetGeometryNode();
101     CHECK_NULL_RETURN(geometryNode, false);
102     auto offsetRelativeToWindow = host->GetOffsetRelativeToWindow();
103     auto globalViewPort = RectF(offsetRelativeToWindow, geometryNode->GetFrameRect().GetSize());
104     host->SetViewPort(globalViewPort);
105     isInitialized_ = true;
106     SetScrollSource(SCROLL_FROM_NONE);
107     return false;
108 }
109 
SetScrollProperties(const RefPtr<LayoutWrapper> & dirty)110 bool ScrollPattern::SetScrollProperties(const RefPtr<LayoutWrapper>& dirty)
111 {
112     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
113     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
114     auto layoutAlgorithm = DynamicCast<ScrollLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
115     CHECK_NULL_RETURN(layoutAlgorithm, false);
116     currentOffset_ = layoutAlgorithm->GetCurrentOffset();
117     auto oldScrollableDistance = scrollableDistance_;
118     scrollableDistance_ = layoutAlgorithm->GetScrollableDistance();
119     if (!NearEqual(oldScrollableDistance, scrollableDistance_)) {
120         CheckScrollToEdge();
121         AddScrollLayoutInfo();
122     }
123 
124     if (LessNotEqual(scrollableDistance_, oldScrollableDistance)) {
125         CheckRestartSpring(true);
126     }
127     auto axis = GetAxis();
128     auto oldMainSize = GetMainAxisSize(viewPort_, axis);
129     auto newMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPort(), axis);
130     auto oldExtentMainSize = GetMainAxisSize(viewPortExtent_, axis);
131     auto newExtentMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPortExtent(), axis);
132     viewPortLength_ = layoutAlgorithm->GetViewPortLength();
133     viewPort_ = layoutAlgorithm->GetViewPort();
134     viewSize_ = layoutAlgorithm->GetViewSize();
135     viewPortExtent_ = layoutAlgorithm->GetViewPortExtent();
136     if (IsEnablePagingValid()) {
137         SetIntervalSize(Dimension(static_cast<double>(viewPortLength_)));
138     }
139     if (scrollSnapUpdate_ || !NearEqual(oldMainSize, newMainSize) || !NearEqual(oldExtentMainSize, newExtentMainSize)) {
140         CaleSnapOffsets();
141         scrollSnapUpdate_ = false;
142     }
143     return true;
144 }
145 
ScrollSnapTrigger()146 bool ScrollPattern::ScrollSnapTrigger()
147 {
148     auto scrollBar = GetScrollBar();
149     auto scrollBarProxy = GetScrollBarProxy();
150     if (scrollBar && scrollBar->IsPressed()) {
151         return false;
152     }
153     if (scrollBarProxy && scrollBarProxy->IsScrollSnapTrigger()) {
154         return false;
155     }
156     if (ScrollableIdle() && !AnimateRunning()) {
157         auto predictSnapOffset = CalePredictSnapOffset(0.0);
158         if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value(), SPRING_ACCURACY)) {
159             StartScrollSnapMotion(predictSnapOffset.value(), 0.0f);
160             FireOnScrollStart();
161             return true;
162         }
163     }
164     return false;
165 }
166 
CheckScrollable()167 void ScrollPattern::CheckScrollable()
168 {
169     auto host = GetHost();
170     CHECK_NULL_VOID(host);
171     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
172     CHECK_NULL_VOID(layoutProperty);
173     if (GreatNotEqual(scrollableDistance_, 0.0f)) {
174         SetScrollEnabled(layoutProperty->GetScrollEnabled().value_or(true));
175     } else {
176         SetScrollEnabled(layoutProperty->GetScrollEnabled().value_or(true) && GetAlwaysEnabled());
177     }
178 }
179 
OnScrollCallback(float offset,int32_t source)180 bool ScrollPattern::OnScrollCallback(float offset, int32_t source)
181 {
182     if (source != SCROLL_FROM_START) {
183         if (GetAxis() == Axis::NONE) {
184             return false;
185         }
186         if (!AnimateStoped()) {
187             return false;
188         }
189         auto adjustOffset = static_cast<float>(offset);
190         AdjustOffset(adjustOffset, source);
191         return UpdateCurrentOffset(adjustOffset, source);
192     } else {
193         FireOnScrollStart();
194     }
195     return true;
196 }
197 
OnScrollEndCallback()198 void ScrollPattern::OnScrollEndCallback()
199 {
200     auto host = GetHost();
201     CHECK_NULL_VOID(host);
202     auto eventHub = host->GetEventHub<ScrollEventHub>();
203     CHECK_NULL_VOID(eventHub);
204     auto scrollEndEvent = eventHub->GetScrollEndEvent();
205     if (scrollEndEvent) {
206         scrollEndEvent();
207     }
208     if (AnimateStoped()) {
209         scrollStop_ = true;
210         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
211     }
212 }
213 
ResetPosition()214 void ScrollPattern::ResetPosition()
215 {
216     currentOffset_ = 0.0f;
217     lastOffset_ = 0.0f;
218 }
219 
IsAtTop() const220 bool ScrollPattern::IsAtTop() const
221 {
222     return GreatOrEqual(currentOffset_, 0.0);
223 }
224 
IsAtBottom() const225 bool ScrollPattern::IsAtBottom() const
226 {
227     return LessOrEqual(currentOffset_, -scrollableDistance_);
228 }
229 
GetOverScrollOffset(double delta) const230 OverScrollOffset ScrollPattern::GetOverScrollOffset(double delta) const
231 {
232     OverScrollOffset offset = { 0, 0 };
233     auto startPos = currentOffset_;
234     auto newStartPos = startPos + delta;
235     if (startPos > 0 && newStartPos > 0) {
236         offset.start = delta;
237     }
238     if (startPos > 0 && newStartPos <= 0) {
239         offset.start = -startPos;
240     }
241     if (startPos <= 0 && newStartPos > 0) {
242         offset.start = newStartPos;
243     }
244 
245     auto endPos = currentOffset_;
246     auto newEndPos = endPos + delta;
247     auto endRefences =  GreatOrEqual(scrollableDistance_, 0.0f) ? -scrollableDistance_ : 0;
248     if (endPos < endRefences && newEndPos < endRefences) {
249         offset.end = delta;
250     }
251     if (endPos < endRefences && newEndPos >= endRefences) {
252         offset.end = endRefences - endPos;
253     }
254     if (endPos >= endRefences && newEndPos < endRefences) {
255         offset.end = newEndPos - endRefences;
256     }
257     return offset;
258 }
259 
IsOutOfBoundary(bool useCurrentDelta)260 bool ScrollPattern::IsOutOfBoundary(bool useCurrentDelta)
261 {
262     if (Positive(scrollableDistance_)) {
263         return Positive(currentOffset_) || LessNotEqual(currentOffset_, -scrollableDistance_);
264     } else {
265         return !NearZero(currentOffset_);
266     }
267 }
268 
ScrollPageCheck(float delta,int32_t source)269 bool ScrollPattern::ScrollPageCheck(float delta, int32_t source)
270 {
271     return true;
272 }
273 
AdjustOffset(float & delta,int32_t source)274 void ScrollPattern::AdjustOffset(float& delta, int32_t source)
275 {
276     if (NearZero(delta) || NearZero(viewPortLength_) || source == SCROLL_FROM_ANIMATION ||
277         source == SCROLL_FROM_ANIMATION_SPRING) {
278         return;
279     }
280     // the distance above the top, if lower than top, it is zero
281     float overScrollPastStart = 0.0f;
282     // the distance below the bottom, if higher than bottom, it is zero
283     float overScrollPastEnd = 0.0f;
284     float overScrollPast = 0.0f;
285     // not consider rowReverse or colReverse
286     overScrollPastStart = std::max(currentOffset_, 0.0f);
287     if (Positive(scrollableDistance_)) {
288         overScrollPastEnd = std::max(-scrollableDistance_ - currentOffset_, 0.0f);
289     } else {
290         overScrollPastEnd = std::abs(std::min(currentOffset_, 0.0f));
291     }
292     overScrollPast = std::max(overScrollPastStart, overScrollPastEnd);
293     if (overScrollPast == 0.0f) {
294         return;
295     }
296     float friction = ScrollablePattern::CalculateFriction(overScrollPast / viewPortLength_);
297     delta = delta * friction;
298 }
299 
ValidateOffset(int32_t source,float willScrollOffset)300 float ScrollPattern::ValidateOffset(int32_t source, float willScrollOffset)
301 {
302     if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) {
303         return willScrollOffset;
304     }
305 
306     // restrict position between top and bottom
307     if (IsRestrictBoundary() || source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING ||
308         source == SCROLL_FROM_ROTATE || source == SCROLL_FROM_AXIS) {
309         if (GetAxis() == Axis::HORIZONTAL) {
310             if (IsRowReverse()) {
311                 willScrollOffset = std::clamp(willScrollOffset, 0.0f, scrollableDistance_);
312             } else {
313                 willScrollOffset = std::clamp(willScrollOffset, -scrollableDistance_, 0.0f);
314             }
315         } else {
316             willScrollOffset = std::clamp(willScrollOffset, -scrollableDistance_, 0.0f);
317         }
318     }
319     return willScrollOffset;
320 }
321 
ValidateOffset(int32_t source)322 void ScrollPattern::ValidateOffset(int32_t source)
323 {
324     if (LessOrEqual(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 || source == SCROLL_FROM_AXIS) {
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     }
341 }
342 
HandleScrollPosition(float scroll)343 void ScrollPattern::HandleScrollPosition(float scroll)
344 {
345     auto eventHub = GetEventHub<ScrollEventHub>();
346     CHECK_NULL_VOID(eventHub);
347     auto onScroll = eventHub->GetOnScrollEvent();
348     CHECK_NULL_VOID(onScroll);
349     // not consider async call
350     Dimension scrollX(0, DimensionUnit::VP);
351     Dimension scrollY(0, DimensionUnit::VP);
352     Dimension scrollPx(scroll, DimensionUnit::PX);
353     auto scrollVpValue = scrollPx.ConvertToVp();
354     if (GetAxis() == Axis::HORIZONTAL) {
355         scrollX.SetValue(scrollVpValue);
356     } else {
357         scrollY.SetValue(scrollVpValue);
358     }
359     onScroll(scrollX, scrollY);
360 }
361 
FireTwoDimensionOnWillScroll(float scroll)362 float ScrollPattern::FireTwoDimensionOnWillScroll(float scroll)
363 {
364     auto eventHub = GetEventHub<ScrollEventHub>();
365     CHECK_NULL_RETURN(eventHub, scroll);
366     auto onScroll = eventHub->GetOnWillScrollEvent();
367     CHECK_NULL_RETURN(onScroll, scroll);
368     Dimension scrollX(0, DimensionUnit::VP);
369     Dimension scrollY(0, DimensionUnit::VP);
370     Dimension scrollPx(scroll, DimensionUnit::PX);
371     auto scrollVpValue = scrollPx.ConvertToVp();
372     if (GetAxis() == Axis::HORIZONTAL) {
373         scrollX.SetValue(scrollVpValue);
374     } else {
375         scrollY.SetValue(scrollVpValue);
376     }
377     auto scrollRes =
378         onScroll(scrollX, scrollY, GetScrollState(), ScrollablePattern::ConvertScrollSource(GetScrollSource()));
379     auto context = PipelineContext::GetCurrentContextSafely();
380     CHECK_NULL_RETURN(context, scroll);
381     if (GetAxis() == Axis::HORIZONTAL) {
382         return context->NormalizeToPx(scrollRes.xOffset);
383     } else {
384         return context->NormalizeToPx(scrollRes.yOffset);
385     }
386 }
387 
FireOnDidScroll(float scroll)388 void ScrollPattern::FireOnDidScroll(float scroll)
389 {
390     FireObserverOnDidScroll(scroll);
391     auto eventHub = GetEventHub<ScrollEventHub>();
392     CHECK_NULL_VOID(eventHub);
393     auto onScroll = eventHub->GetOnDidScrollEvent();
394     CHECK_NULL_VOID(onScroll);
395     Dimension scrollX(0, DimensionUnit::VP);
396     Dimension scrollY(0, DimensionUnit::VP);
397     Dimension scrollPx(scroll, DimensionUnit::PX);
398     auto scrollVpValue = scrollPx.ConvertToVp();
399     if (GetAxis() == Axis::HORIZONTAL) {
400         scrollX.SetValue(scrollVpValue);
401     } else {
402         scrollY.SetValue(scrollVpValue);
403     }
404     auto scrollState = GetScrollState();
405     bool isTriggered = false;
406     if (!NearZero(scroll)) {
407         onScroll(scrollX, scrollY, scrollState);
408         isTriggered = true;
409     }
410     if (scrollStop_ && !GetScrollAbort()) {
411         if (scrollState != ScrollState::IDLE || !isTriggered) {
412             onScroll(0.0_vp, 0.0_vp, ScrollState::IDLE);
413         }
414     }
415 }
416 
FireOnReachStart(const OnReachEvent & onReachStart)417 void ScrollPattern::FireOnReachStart(const OnReachEvent& onReachStart)
418 {
419     auto host = GetHost();
420     CHECK_NULL_VOID(host);
421     if (ReachStart(!isInitialized_)) {
422         FireObserverOnReachStart();
423         CHECK_NULL_VOID(onReachStart);
424         ACE_SCOPED_TRACE("OnReachStart, id:%d, tag:Scroll", static_cast<int32_t>(host->GetAccessibilityId()));
425         onReachStart();
426         AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
427     }
428 }
429 
FireOnReachEnd(const OnReachEvent & onReachEnd)430 void ScrollPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
431 {
432     auto host = GetHost();
433     CHECK_NULL_VOID(host);
434     if (ReachEnd(false)) {
435         FireObserverOnReachEnd();
436         CHECK_NULL_VOID(onReachEnd);
437         ACE_SCOPED_TRACE("OnReachEnd, id:%d, tag:Scroll", static_cast<int32_t>(host->GetAccessibilityId()));
438         onReachEnd();
439         AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
440     } else if (!isInitialized_ && ReachEnd(true)) {
441         FireObserverOnReachEnd();
442     }
443 }
444 
IsCrashTop() const445 bool ScrollPattern::IsCrashTop() const
446 {
447     bool scrollUpToReachTop = LessNotEqual(lastOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
448     bool scrollDownToReachTop = GreatNotEqual(lastOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
449     return scrollUpToReachTop || scrollDownToReachTop;
450 }
451 
IsCrashBottom() const452 bool ScrollPattern::IsCrashBottom() const
453 {
454     float minExtent = -scrollableDistance_;
455     bool scrollDownToReachEnd = GreatNotEqual(lastOffset_, minExtent) && LessOrEqual(currentOffset_, minExtent);
456     bool scrollUpToReachEnd = LessNotEqual(lastOffset_, minExtent) && GreatOrEqual(currentOffset_, minExtent);
457     return (scrollUpToReachEnd || scrollDownToReachEnd);
458 }
459 
ReachStart(bool firstLayout) const460 bool ScrollPattern::ReachStart(bool firstLayout) const
461 {
462     bool scrollUpToReachTop = (LessNotEqual(prevOffset_, 0.0) || firstLayout) && GreatOrEqual(currentOffset_, 0.0);
463     bool scrollDownToReachTop = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
464     return scrollUpToReachTop || scrollDownToReachTop;
465 }
466 
ReachEnd(bool firstLayout) const467 bool ScrollPattern::ReachEnd(bool firstLayout) const
468 {
469     float minExtent = -scrollableDistance_;
470     bool scrollDownToReachEnd =
471         (GreatNotEqual(prevOffset_, minExtent) || firstLayout) && LessOrEqual(currentOffset_, minExtent);
472     bool scrollUpToReachEnd = LessNotEqual(prevOffset_, minExtent) && GreatOrEqual(currentOffset_, minExtent);
473     return (scrollUpToReachEnd || scrollDownToReachEnd);
474 }
475 
HandleCrashTop()476 void ScrollPattern::HandleCrashTop()
477 {
478     auto frameNode = GetHost();
479     CHECK_NULL_VOID(frameNode);
480     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
481     CHECK_NULL_VOID(eventHub);
482     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
483     CHECK_NULL_VOID(onScrollEdge);
484     // not consider async call
485     if (GetAxis() == Axis::HORIZONTAL) {
486         onScrollEdge(ScrollEdge::LEFT);
487         AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
488         return;
489     }
490     onScrollEdge(ScrollEdge::TOP);
491     AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
492 }
493 
HandleCrashBottom()494 void ScrollPattern::HandleCrashBottom()
495 {
496     auto frameNode = GetHost();
497     CHECK_NULL_VOID(frameNode);
498     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
499     CHECK_NULL_VOID(eventHub);
500     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
501     CHECK_NULL_VOID(onScrollEdge);
502     if (GetAxis() == Axis::HORIZONTAL) {
503         onScrollEdge(ScrollEdge::RIGHT);
504         AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
505         return;
506     }
507     onScrollEdge(ScrollEdge::BOTTOM);
508     AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
509 }
510 
UpdateCurrentOffset(float delta,int32_t source)511 bool ScrollPattern::UpdateCurrentOffset(float delta, int32_t source)
512 {
513     auto host = GetHost();
514     CHECK_NULL_RETURN(host, false);
515     if (source != SCROLL_FROM_JUMP && !HandleEdgeEffect(delta, source, viewSize_)) {
516         if (IsOutOfBoundary()) {
517             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
518         }
519         return false;
520     }
521     SetScrollSource(source);
522     FireAndCleanScrollingListener();
523     lastOffset_ = currentOffset_;
524     auto willScrollPosition = currentOffset_ + delta;
525     willScrollPosition = ValidateOffset(source, willScrollPosition);
526     auto userOffset = FireTwoDimensionOnWillScroll(currentOffset_ - willScrollPosition);
527     currentOffset_ -= userOffset;
528     ValidateOffset(source);
529     HandleScrollPosition(userOffset);
530     if (IsCrashTop()) {
531         HandleCrashTop();
532     } else if (IsCrashBottom()) {
533         HandleCrashBottom();
534     }
535     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
536     return true;
537 }
538 
OnAnimateStop()539 void ScrollPattern::OnAnimateStop()
540 {
541     if (!GetIsDragging() || GetScrollAbort()) {
542         auto host = GetHost();
543         CHECK_NULL_VOID(host);
544         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
545         scrollStop_ = true;
546     }
547 }
548 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)549 void ScrollPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
550 {
551     if (scrollEdgeType == ScrollEdgeType::SCROLL_NONE) {
552         return;
553     }
554     if (LessOrEqual(scrollableDistance_, 0.0)) {
555         return;
556     }
557     float distance = scrollEdgeType == ScrollEdgeType::SCROLL_TOP ? -currentOffset_ :
558         (-scrollableDistance_ - currentOffset_);
559     auto host = GetHost();
560     CHECK_NULL_VOID(host);
561     ACE_SCOPED_TRACE("Scroll ScrollToEdge scrollEdgeType:%zu, offset:%f, id:%d", scrollEdgeType, distance,
562         static_cast<int32_t>(host->GetAccessibilityId()));
563     if (!NearZero(distance)) {
564         ScrollBy(distance, distance, smooth);
565         scrollEdgeType_ = scrollEdgeType;
566     }
567 }
568 
CheckScrollToEdge()569 void ScrollPattern::CheckScrollToEdge()
570 {
571     if (scrollEdgeType_ != ScrollEdgeType::SCROLL_NONE) {
572         ScrollToEdge(scrollEdgeType_, true);
573     }
574 }
575 
ScrollBy(float pixelX,float pixelY,bool smooth,const std::function<void ()> & onFinish)576 void ScrollPattern::ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish)
577 {
578     float distance = (GetAxis() == Axis::VERTICAL) ? pixelY : pixelX;
579     if (NearZero(distance)) {
580         return;
581     }
582     float position = currentOffset_ + distance;
583     if (smooth) {
584         AnimateTo(-position, fabs(distance) * UNIT_CONVERT / SCROLL_BY_SPEED, Curves::EASE_OUT, true);
585         return;
586     }
587     JumpToPosition(position);
588 }
589 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType,const std::function<void ()> & onFinish)590 bool ScrollPattern::ScrollPage(
591     bool reverse, bool smooth, AccessibilityScrollType scrollType, const std::function<void()>& onFinish)
592 {
593     auto host = GetHost();
594     CHECK_NULL_RETURN(host, true);
595     float distance = reverse ? viewPortLength_ : -viewPortLength_;
596     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
597         distance = distance / 2.f;
598     }
599     ACE_SCOPED_TRACE(
600         "Scroll ScrollPage distance:%f, id:%d", distance, static_cast<int32_t>(host->GetAccessibilityId()));
601     ScrollBy(distance, distance, smooth, onFinish);
602     return true;
603 }
604 
JumpToPosition(float position,int32_t source)605 void ScrollPattern::JumpToPosition(float position, int32_t source)
606 {
607     // If an animation is playing, stop it.
608     auto lastAnimateRunning = AnimateRunning();
609     StopAnimate();
610     DoJump(position, source);
611     // AccessibilityEventType::SCROLL_END
612     if (lastAnimateRunning) {
613         SetScrollAbort(false);
614     }
615 }
616 
ScrollTo(float position)617 void ScrollPattern::ScrollTo(float position)
618 {
619     JumpToPosition(-position, SCROLL_FROM_JUMP);
620 }
621 
DoJump(float position,int32_t source)622 void ScrollPattern::DoJump(float position, int32_t source)
623 {
624     float setPosition = (GetAxis() == Axis::HORIZONTAL && IsRowReverse()) ? -position : position;
625     if (!NearEqual(currentOffset_, setPosition) && GreatOrEqual(scrollableDistance_, 0.0f)) {
626         UpdateCurrentOffset(setPosition - currentOffset_, source);
627     }
628 }
629 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)630 void ScrollPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
631 {
632     scrollEffect->SetCurrentPositionCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
633         auto scroll = weakScroll.Upgrade();
634         CHECK_NULL_RETURN(scroll, 0.0);
635         return scroll->GetCurrentPosition();
636     });
637     scrollEffect->SetLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
638         auto scroll = weakScroll.Upgrade();
639         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
640             return -scroll->GetScrollableDistance();
641         }
642         return 0.0;
643     });
644     scrollEffect->SetTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
645         auto scroll = weakScroll.Upgrade();
646         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
647             return scroll->GetScrollableDistance();
648         }
649         return 0.0;
650     });
651     scrollEffect->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
652         auto scroll = weakScroll.Upgrade();
653         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
654             return -scroll->GetScrollableDistance();
655         }
656         return 0.0;
657     });
658     scrollEffect->SetInitTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
659         auto scroll = weakScroll.Upgrade();
660         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
661             return scroll->GetScrollableDistance();
662         }
663         return 0.0;
664     });
665 }
666 
UpdateScrollBarOffset()667 void ScrollPattern::UpdateScrollBarOffset()
668 {
669     CheckScrollBarOff();
670     if (!GetScrollBar() && !GetScrollBarProxy()) {
671         return;
672     }
673 
674     float scrollBarOutBoundaryExtent = 0.0f;
675     if (currentOffset_ > 0) {
676         scrollBarOutBoundaryExtent = currentOffset_;
677     } else if ((-currentOffset_) >= (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_))) {
678         scrollBarOutBoundaryExtent = -currentOffset_ - (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_));
679     }
680     HandleScrollBarOutBoundary(scrollBarOutBoundaryExtent);
681 
682     auto host = GetHost();
683     CHECK_NULL_VOID(host);
684     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
685     CHECK_NULL_VOID(layoutProperty);
686     auto padding = layoutProperty->CreatePaddingAndBorder();
687     auto contentEndOffset = layoutProperty->GetScrollContentEndOffsetValue(.0f);
688     Size size(viewSize_.Width(), viewSize_.Height() - contentEndOffset);
689     auto viewPortExtent = viewPortExtent_;
690     AddPaddingToSize(padding, viewPortExtent);
691     auto estimatedHeight = (GetAxis() == Axis::HORIZONTAL) ? viewPortExtent.Width() : viewPortExtent.Height();
692     UpdateScrollBarRegion(-currentOffset_, estimatedHeight, size, Offset(0.0f, 0.0f));
693 }
694 
SetAccessibilityAction()695 void ScrollPattern::SetAccessibilityAction()
696 {
697     auto host = GetHost();
698     CHECK_NULL_VOID(host);
699     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
700     CHECK_NULL_VOID(accessibilityProperty);
701     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
702         const auto& pattern = weakPtr.Upgrade();
703         CHECK_NULL_VOID(pattern);
704         auto host = pattern->GetHost();
705         CHECK_NULL_VOID(host);
706         ACE_SCOPED_TRACE("accessibility action, scroll forward, isScrollable:%u, IsPositiveScrollableDistance:%u, "
707                          "scrollType:%d, id:%d, tag:Scroll",
708             pattern->IsScrollable(), pattern->IsPositiveScrollableDistance(), scrollType,
709             static_cast<int32_t>(host->GetAccessibilityId()));
710         if (pattern->IsScrollable() && pattern->IsPositiveScrollableDistance()) {
711             pattern->ScrollPage(false, true, scrollType);
712         }
713     });
714 
715     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
716         const auto& pattern = weakPtr.Upgrade();
717         CHECK_NULL_VOID(pattern);
718         auto host = pattern->GetHost();
719         CHECK_NULL_VOID(host);
720         ACE_SCOPED_TRACE("accessibility action, scroll backward, isScrollable:%u, IsPositiveScrollableDistance:%u, "
721                          "scrollType:%d, id:%d, tag:Scroll",
722             pattern->IsScrollable(), pattern->IsPositiveScrollableDistance(), scrollType,
723             static_cast<int32_t>(host->GetAccessibilityId()));
724         if (pattern->IsScrollable() && pattern->IsPositiveScrollableDistance()) {
725             pattern->ScrollPage(true, true, scrollType);
726         }
727     });
728 }
729 
GetOffsetToScroll(const RefPtr<FrameNode> & childFrame) const730 OffsetF ScrollPattern::GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const
731 {
732     auto frameNode = GetHost();
733     CHECK_NULL_RETURN(frameNode, OffsetF());
734     CHECK_NULL_RETURN(childFrame, OffsetF());
735     auto childGeometryNode = childFrame->GetGeometryNode();
736     CHECK_NULL_RETURN(childGeometryNode, OffsetF());
737     OffsetF result = childGeometryNode->GetFrameOffset();
738     auto parent = childFrame->GetParent();
739     while (parent) {
740         auto parentFrame = AceType::DynamicCast<FrameNode>(parent);
741         if (!parentFrame) {
742             parent = parent->GetParent();
743             continue;
744         }
745         if (parentFrame == frameNode) {
746             return result;
747         }
748         auto parentGeometryNode = parentFrame->GetGeometryNode();
749         if (!parentGeometryNode) {
750             parent = parent->GetParent();
751             continue;
752         }
753         result += parentGeometryNode->GetFrameOffset();
754         parent = parent->GetParent();
755     }
756     return OffsetF(0.0, 0.0);
757 }
758 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)759 bool ScrollPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
760 {
761     CHECK_NULL_RETURN(focusFrameNode, false);
762     auto focusGeometryNode = focusFrameNode->GetGeometryNode();
763     CHECK_NULL_RETURN(focusGeometryNode, false);
764     auto focusNodeSize = focusGeometryNode->GetFrameSize();
765     auto focusNodeOffsetToScrolll = GetOffsetToScroll(focusFrameNode);
766     auto scrollFrame = GetHost();
767     CHECK_NULL_RETURN(scrollFrame, false);
768     auto scrollGeometry = scrollFrame->GetGeometryNode();
769     CHECK_NULL_RETURN(scrollGeometry, false);
770     auto scrollFrameSize = scrollGeometry->GetFrameSize();
771     float focusNodeDiffToScroll =
772         GetAxis() == Axis::VERTICAL ? focusNodeOffsetToScrolll.GetY() : focusNodeOffsetToScrolll.GetX();
773     if (NearZero(focusNodeDiffToScroll)) {
774         return false;
775     }
776     float focusNodeLength = GetAxis() == Axis::VERTICAL ? focusNodeSize.Height() : focusNodeSize.Width();
777     float scrollFrameLength = GetAxis() == Axis::VERTICAL ? scrollFrameSize.Height() : scrollFrameSize.Width();
778     float moveOffset = 0.0;
779     if (LessNotEqual(focusNodeDiffToScroll, 0)) {
780         moveOffset = -focusNodeDiffToScroll;
781     } else if (GreatNotEqual(focusNodeDiffToScroll + focusNodeLength, scrollFrameLength)) {
782         moveOffset = scrollFrameLength - focusNodeDiffToScroll - focusNodeLength;
783     }
784     if (!NearZero(moveOffset)) {
785         return OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP);
786     }
787     return false;
788 }
789 
GetScrollOffsetAbility()790 std::pair<std::function<bool(float)>, Axis> ScrollPattern::GetScrollOffsetAbility()
791 {
792     return { [wp = WeakClaim(this)](float moveOffset) -> bool {
793                 auto pattern = wp.Upgrade();
794                 CHECK_NULL_RETURN(pattern, false);
795                 return pattern->OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP);
796             },
797         GetAxis() };
798 }
799 
CalePredictSnapOffset(float delta,float dragDistance,float velocity)800 std::optional<float> ScrollPattern::CalePredictSnapOffset(float delta, float dragDistance, float velocity)
801 {
802     std::optional<float> predictSnapOffset;
803     CHECK_NULL_RETURN(IsScrollSnap(), predictSnapOffset);
804     float finalPosition = currentOffset_ + delta;
805     if (IsEnablePagingValid()) {
806         finalPosition = GetPagingOffset(delta, dragDistance, velocity);
807     }
808     if (!IsSnapToInterval()) {
809         if (!enableSnapToSide_.first) {
810             if (GreatNotEqual(finalPosition, *(snapOffsets_.begin() + 1)) ||
811                 GreatNotEqual(currentOffset_, *(snapOffsets_.begin() + 1))) {
812                 return predictSnapOffset;
813             }
814         }
815         if (!enableSnapToSide_.second) {
816             if (LessNotEqual(finalPosition, *(snapOffsets_.rbegin() + 1)) ||
817                 LessNotEqual(currentOffset_, *(snapOffsets_.rbegin() + 1))) {
818                 return predictSnapOffset;
819             }
820         }
821     }
822     float head = 0.0f;
823     float tail = -scrollableDistance_;
824     if (GreatOrEqual(finalPosition, head) || LessOrEqual(finalPosition, tail)) {
825         return predictSnapOffset;
826     } else if (LessNotEqual(finalPosition, head) && GreatOrEqual(finalPosition, *(snapOffsets_.begin()))) {
827         predictSnapOffset = *(snapOffsets_.begin());
828     } else if (GreatNotEqual(finalPosition, tail) && LessOrEqual(finalPosition, *(snapOffsets_.rbegin()))) {
829         predictSnapOffset = *(snapOffsets_.rbegin());
830     } else {
831         auto iter = snapOffsets_.begin() + 1;
832         float start = *(iter - 1);
833         float end = *(iter);
834         for (; iter != snapOffsets_.end(); ++iter) {
835             if (GreatOrEqual(finalPosition, *iter)) {
836                 start = *(iter - 1);
837                 end = *(iter);
838                 predictSnapOffset = (LessNotEqual(start - finalPosition, finalPosition - end) ? start : end);
839                 break;
840             }
841         }
842     }
843     if (predictSnapOffset.has_value()) {
844         predictSnapOffset = predictSnapOffset.value() - currentOffset_;
845     }
846     return predictSnapOffset;
847 }
848 
CaleSnapOffsets()849 void ScrollPattern::CaleSnapOffsets()
850 {
851     auto scrollSnapAlign = GetScrollSnapAlign();
852     std::vector<float>().swap(snapOffsets_);
853     if (scrollSnapAlign == ScrollSnapAlign::NONE) {
854         CHECK_NULL_VOID(enablePagingStatus_ == ScrollPagingStatus::VALID);
855         scrollSnapAlign = ScrollSnapAlign::START;
856     }
857     if (IsSnapToInterval()) {
858         CaleSnapOffsetsByInterval(scrollSnapAlign);
859     } else {
860         CaleSnapOffsetsByPaginations(scrollSnapAlign);
861     }
862 }
863 
CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)864 void ScrollPattern::CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)
865 {
866     CHECK_NULL_VOID(Positive(intervalSize_.Value()));
867     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
868     auto extentMainSize = GetMainAxisSize(viewPortExtent_, GetAxis());
869     auto start = 0.0f;
870     auto end = -scrollableDistance_;
871     auto snapOffset = 0.0f;
872     auto sizeDelta = 0.0f;
873     auto intervalSize = intervalSize_.Unit() == DimensionUnit::PERCENT ?
874                         intervalSize_.Value() * mainSize : intervalSize_.ConvertToPx();
875     float temp = static_cast<int32_t>(extentMainSize / intervalSize) * intervalSize;
876     switch (scrollSnapAlign) {
877         case ScrollSnapAlign::START:
878             end = -temp;
879             break;
880         case ScrollSnapAlign::CENTER:
881             sizeDelta = (mainSize - intervalSize) / 2;
882             start = Positive(sizeDelta) ? sizeDelta - static_cast<int32_t>(sizeDelta / intervalSize) * intervalSize
883                                         : sizeDelta;
884             end = -temp + (mainSize - extentMainSize + temp) / 2;
885             break;
886         case ScrollSnapAlign::END:
887             sizeDelta = mainSize - intervalSize;
888             start = Positive(sizeDelta) ? mainSize - static_cast<int32_t>(mainSize / intervalSize) * intervalSize
889                                         : sizeDelta;
890             end = -scrollableDistance_;
891             break;
892         default:
893             break;
894     }
895     if (!Positive(start)) {
896         snapOffsets_.emplace_back(start);
897     }
898     snapOffset = start - intervalSize;
899     while (GreatOrEqual(snapOffset, -scrollableDistance_) && GreatOrEqual(snapOffset, end)) {
900         snapOffsets_.emplace_back(snapOffset);
901         snapOffset -= intervalSize;
902     }
903     if (GreatNotEqual(end, -scrollableDistance_)) {
904         snapOffsets_.emplace_back(end);
905     }
906     if (IsEnablePagingValid()) {
907         if (NearEqual(snapOffset + intervalSize, -scrollableDistance_)) {
908             lastPageLength_ = 0.f;
909             return;
910         }
911         lastPageLength_ = scrollableDistance_ + snapOffset + intervalSize;
912         snapOffsets_.emplace_back(-scrollableDistance_);
913     }
914 }
915 
CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign)916 void ScrollPattern::CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign)
917 {
918     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
919     auto extentMainSize = GetMainAxisSize(viewPortExtent_, GetAxis());
920     auto start = 0.0f;
921     auto end = -scrollableDistance_;
922     auto snapOffset = 0.0f;
923     snapOffsets_.emplace_back(start);
924     int32_t length = 0;
925     auto snapPaginations = snapPaginations_;
926     snapPaginations.emplace(snapPaginations.begin(), Dimension(0.f));
927     auto current = 0.0f;
928     auto next = 0.0f;
929     auto size = static_cast<int32_t>(snapPaginations.size());
930     auto element = snapPaginations[length];
931     auto nextElement = snapPaginations[length + 1];
932     for (; length < size; length++) {
933         element = snapPaginations[length];
934         nextElement = snapPaginations[length + 1];
935         current = element.Unit() == DimensionUnit::PERCENT ? element.Value() * mainSize : element.ConvertToPx();
936         if (length == size - 1) {
937             next = extentMainSize;
938         } else {
939             next = nextElement.Unit() == DimensionUnit::PERCENT ? nextElement.Value() * mainSize
940                                                                 : nextElement.ConvertToPx();
941         }
942         switch (scrollSnapAlign) {
943             case ScrollSnapAlign::START:
944                 snapOffset = -current;
945                 break;
946             case ScrollSnapAlign::CENTER:
947                 snapOffset = (mainSize - (current + next)) / 2.0f;
948                 break;
949             case ScrollSnapAlign::END:
950                 snapOffset = mainSize - next;
951                 break;
952             default:
953                 break;
954         }
955         if (!Negative(snapOffset)) {
956             continue;
957         }
958         if (GreatNotEqual(snapOffset, -scrollableDistance_)) {
959             snapOffsets_.emplace_back(snapOffset);
960         } else {
961             break;
962         }
963     }
964     snapOffsets_.emplace_back(end);
965 }
966 
NeedScrollSnapToSide(float delta)967 bool ScrollPattern::NeedScrollSnapToSide(float delta)
968 {
969     CHECK_NULL_RETURN(GetScrollSnapAlign() != ScrollSnapAlign::NONE, false);
970     CHECK_NULL_RETURN(!IsSnapToInterval(), false);
971     auto finalPosition = currentOffset_ + delta;
972     CHECK_NULL_RETURN(static_cast<int32_t>(snapOffsets_.size()) > 2, false);
973     if (!enableSnapToSide_.first) {
974         if (GreatOrEqual(currentOffset_, *(snapOffsets_.begin() + 1)) &&
975             LessOrEqual(finalPosition, *(snapOffsets_.begin() + 1))) {
976             return true;
977         }
978     }
979     if (!enableSnapToSide_.second) {
980         if (LessOrEqual(currentOffset_, *(snapOffsets_.rbegin() + 1)) &&
981             GreatOrEqual(finalPosition, *(snapOffsets_.rbegin() + 1))) {
982             return true;
983         }
984     }
985     return false;
986 }
987 
ProvideRestoreInfo()988 std::string ScrollPattern::ProvideRestoreInfo()
989 {
990     Dimension dimension(currentOffset_);
991     return StringUtils::DoubleToString(dimension.ConvertToVp());
992 }
993 
OnRestoreInfo(const std::string & restoreInfo)994 void ScrollPattern::OnRestoreInfo(const std::string& restoreInfo)
995 {
996     Dimension dimension = StringUtils::StringToDimension(restoreInfo, true);
997     currentOffset_ = dimension.ConvertToPx();
998 }
999 
GetItemRect(int32_t index) const1000 Rect ScrollPattern::GetItemRect(int32_t index) const
1001 {
1002     auto host = GetHost();
1003     CHECK_NULL_RETURN(host, Rect());
1004     if (index != 0 || host->TotalChildCount() != 1) {
1005         return Rect();
1006     }
1007     auto item = host->GetChildByIndex(index);
1008     CHECK_NULL_RETURN(item, Rect());
1009     auto itemGeometry = item->GetGeometryNode();
1010     CHECK_NULL_RETURN(itemGeometry, Rect());
1011     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1012         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1013 }
1014 
GetSelectScrollWidth()1015 float ScrollPattern::GetSelectScrollWidth()
1016 {
1017     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1018     auto parent = columnInfo->GetParent();
1019     CHECK_NULL_RETURN(parent, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1020     parent->BuildColumnWidth();
1021     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM));
1022     auto scrollNode = GetHost();
1023     CHECK_NULL_RETURN(scrollNode, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1024     float finalWidth = SELECT_SCROLL_MIN_WIDTH.ConvertToPx();
1025 
1026     if (IsWidthModifiedBySelect()) {
1027         auto scrollLayoutProperty = scrollNode->GetLayoutProperty<ScrollLayoutProperty>();
1028         CHECK_NULL_RETURN(scrollLayoutProperty, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1029         auto selectModifiedWidth = scrollLayoutProperty->GetScrollWidth();
1030         finalWidth = selectModifiedWidth.value();
1031     } else {
1032         finalWidth = defaultWidth;
1033     }
1034 
1035     if (finalWidth < SELECT_SCROLL_MIN_WIDTH.ConvertToPx()) {
1036         finalWidth = defaultWidth;
1037     }
1038 
1039     return finalWidth;
1040 }
1041 
GetPagingOffset(float delta,float dragDistance,float velocity) const1042 float ScrollPattern::GetPagingOffset(float delta, float dragDistance, float velocity)  const
1043 {
1044     // handle last page
1045     auto currentOffset = currentOffset_;
1046     if (GreatNotEqual(lastPageLength_, 0.f) &&
1047         LessNotEqual(currentOffset - dragDistance, -scrollableDistance_ + lastPageLength_)) {
1048         if (LessOrEqual(dragDistance, lastPageLength_)) {
1049             return currentOffset - dragDistance + GetPagingDelta(dragDistance, velocity, lastPageLength_);
1050         }
1051         if (GreatNotEqual(dragDistance, lastPageLength_)) {
1052             dragDistance -= lastPageLength_;
1053         }
1054     }
1055     // handle other pages
1056     float head = 0.0f;
1057     float tail = -scrollableDistance_;
1058     dragDistance = fmod(dragDistance, viewPortLength_);
1059     auto pagingPosition = currentOffset - dragDistance + GetPagingDelta(dragDistance, velocity, viewPortLength_);
1060     auto finalPosition = currentOffset + delta;
1061     auto useFinalPosition = (GreatOrEqual(pagingPosition, head) && !GreatOrEqual(finalPosition, head)) ||
1062                       (LessOrEqual(pagingPosition, tail) && !LessOrEqual(finalPosition, tail));
1063     return useFinalPosition ? finalPosition : pagingPosition;
1064 }
1065 
GetPagingDelta(float dragDistance,float velocity,float pageLength) const1066 float ScrollPattern::GetPagingDelta(float dragDistance, float velocity, float pageLength)  const
1067 {
1068     auto dragDistanceThreshold = pageLength * 0.5f;
1069     // dragDistance and velocity have not reached the threshold
1070     if (LessNotEqual(std::abs(dragDistance), dragDistanceThreshold) &&
1071         LessNotEqual(std::abs(velocity), SCROLL_PAGING_SPEED_THRESHOLD)) {
1072         return 0.f;
1073     }
1074     // The direction of dragDistance is the same as the direction of velocity
1075     if (GreatOrEqual(dragDistance * velocity, 0.f)) {
1076         auto direction = NearZero(dragDistance) ? velocity : dragDistance;
1077         return GreatNotEqual(direction, 0.f) ? pageLength : -pageLength;
1078     }
1079     // The direction of dragDistance is opposite to the direction of velocity
1080     if (GreatOrEqual(std::abs(dragDistance), dragDistanceThreshold) &&
1081         LessNotEqual(std::abs(velocity), SCROLL_PAGING_SPEED_THRESHOLD)) {
1082         return GreatNotEqual(dragDistance, 0.f) ? pageLength : -pageLength;
1083     }
1084     return 0.f;
1085 }
1086 
TriggerModifyDone()1087 void ScrollPattern::TriggerModifyDone()
1088 {
1089     OnModifyDone();
1090 }
1091 
AddScrollMeasureInfo(const std::optional<LayoutConstraintF> & parentConstraint,const std::optional<LayoutConstraintF> & childConstraint,const SizeF & selfSize,const SizeF & childSize)1092 void ScrollPattern::AddScrollMeasureInfo(const std::optional<LayoutConstraintF>& parentConstraint,
1093     const std::optional<LayoutConstraintF>& childConstraint, const SizeF& selfSize, const SizeF& childSize)
1094 {
1095     if (scrollMeasureInfos_.size() >= SCROLL_MEASURE_INFO_COUNT) {
1096         scrollMeasureInfos_.pop_front();
1097     }
1098     scrollMeasureInfos_.push_back(ScrollMeasureInfo({
1099         .changedTime_ = GetSysTimestamp(),
1100         .parentConstraint_ = parentConstraint,
1101         .childConstraint_ = childConstraint,
1102         .selfSize_ = selfSize,
1103         .childSize_ = childSize,
1104     }));
1105 }
1106 
AddScrollLayoutInfo()1107 void ScrollPattern::AddScrollLayoutInfo()
1108 {
1109     if (scrollLayoutInfos_.size() >= SCROLL_LAYOUT_INFO_COUNT) {
1110         scrollLayoutInfos_.pop_front();
1111     }
1112     scrollLayoutInfos_.push_back(ScrollLayoutInfo({
1113         .changedTime_ = GetSysTimestamp(),
1114         .scrollableDistance_ = scrollableDistance_,
1115         .scrollSize_ = viewSize_,
1116         .viewPort_ = viewPort_,
1117         .childSize_ = viewPortExtent_,
1118     }));
1119 }
1120 
GetScrollSnapAlignDumpInfo()1121 void ScrollPattern::GetScrollSnapAlignDumpInfo()
1122 {
1123     switch (GetScrollSnapAlign()) {
1124         case ScrollSnapAlign::NONE: {
1125             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::NONE");
1126             break;
1127         }
1128         case ScrollSnapAlign::START: {
1129             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::START");
1130             break;
1131         }
1132         case ScrollSnapAlign::CENTER: {
1133             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::CENTER");
1134             break;
1135         }
1136         case ScrollSnapAlign::END: {
1137             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::END");
1138             break;
1139         }
1140         default: {
1141             break;
1142         }
1143     }
1144 }
1145 
GetScrollPagingStatusDumpInfo()1146 void ScrollPattern::GetScrollPagingStatusDumpInfo()
1147 {
1148     switch (enablePagingStatus_) {
1149         case ScrollPagingStatus::NONE: {
1150             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::NONE");
1151             break;
1152         }
1153         case ScrollPagingStatus::INVALID: {
1154             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::INVALID");
1155             break;
1156         }
1157         case ScrollPagingStatus::VALID: {
1158             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::VALID");
1159             break;
1160         }
1161         default: {
1162             break;
1163         }
1164     }
1165 }
1166 
DumpAdvanceInfo()1167 void ScrollPattern::DumpAdvanceInfo()
1168 {
1169     auto host = GetHost();
1170     CHECK_NULL_VOID(host);
1171     auto hub = host->GetEventHub<ScrollEventHub>();
1172     CHECK_NULL_VOID(hub);
1173     ScrollablePattern::DumpAdvanceInfo();
1174     DumpLog::GetInstance().AddDesc(std::string("currentOffset: ").append(std::to_string(currentOffset_)));
1175     GetScrollSnapAlignDumpInfo();
1176     auto snapPaginationStr = std::string("snapPagination: ");
1177     DumpLog::GetInstance().AddDesc(snapPaginationStr.append(GetScrollSnapPagination()));
1178     enableSnapToSide_.first ? DumpLog::GetInstance().AddDesc("enableSnapToStart: true")
1179                             : DumpLog::GetInstance().AddDesc("enableSnapToStart: false");
1180     enableSnapToSide_.second ? DumpLog::GetInstance().AddDesc("enableSnapToEnd: true")
1181                              : DumpLog::GetInstance().AddDesc("enableSnapToEnd: false");
1182     GetScrollPagingStatusDumpInfo();
1183     auto snapOffsetsStr = std::string("snapOffsets: [");
1184     for (const auto& iter : snapPaginations_) {
1185         snapOffsetsStr = snapOffsetsStr.append(iter.ToString()).append(" ");
1186     }
1187     DumpLog::GetInstance().AddDesc(snapOffsetsStr.append("]"));
1188     initialOffset_.has_value() ? DumpLog::GetInstance().AddDesc(std::string("initialOffset: ")
1189         .append(initialOffset_->GetMainOffset(GetAxis()).ToString()))
1190         : DumpLog::GetInstance().AddDesc("initialOffset: None");
1191     auto onScrollEdge = hub->GetScrollEdgeEvent();
1192     onScrollEdge ? DumpLog::GetInstance().AddDesc("hasOnScrollEdge: true")
1193                  : DumpLog::GetInstance().AddDesc("hasOnScrollEdge: false");
1194     DumpLog::GetInstance().AddDesc("==========================scrollLayoutInfos==========================");
1195     for (const auto& info : scrollLayoutInfos_) {
1196         DumpLog::GetInstance().AddDesc(info.ToString());
1197     }
1198     DumpLog::GetInstance().AddDesc("==========================scrollLayoutInfos==========================");
1199     DumpLog::GetInstance().AddDesc("==========================scrollMeasureInfos==========================");
1200     for (const auto& info : scrollMeasureInfos_) {
1201         DumpLog::GetInstance().AddDesc(info.ToString());
1202     }
1203     DumpLog::GetInstance().AddDesc("==========================scrollMeasureInfos==========================");
1204 }
1205 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1206 void ScrollPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1207 {
1208     ScrollablePattern::ToJsonValue(json, filter);
1209     /* no fixed attr below, just return */
1210     if (filter.IsFastFilter()) {
1211         return;
1212     }
1213     auto initialOffset = JsonUtil::Create(true);
1214     initialOffset->Put("xOffset", GetInitialOffset().GetX().ToString().c_str());
1215     initialOffset->Put("yOffset", GetInitialOffset().GetY().ToString().c_str());
1216     json->PutExtAttr("initialOffset", initialOffset, filter);
1217     if (enablePagingStatus_ != ScrollPagingStatus::NONE) {
1218         json->PutExtAttr("enablePaging", enablePagingStatus_ == ScrollPagingStatus::VALID, filter);
1219     }
1220 
1221     auto scrollSnapOptions = JsonUtil::Create(true);
1222     if (IsSnapToInterval()) {
1223         scrollSnapOptions->Put("snapPagination", intervalSize_.ToString().c_str());
1224     } else {
1225         auto snapPaginationArr = JsonUtil::CreateArray(true);
1226         auto iter = snapPaginations_.begin();
1227         for (auto i = 0; iter != snapPaginations_.end(); ++iter, ++i) {
1228             snapPaginationArr->Put(std::to_string(i).c_str(), (*iter).ToString().c_str());
1229         }
1230         scrollSnapOptions->Put("snapPagination", snapPaginationArr);
1231     }
1232     scrollSnapOptions->Put("enableSnapToStart", enableSnapToSide_.first);
1233     scrollSnapOptions->Put("enableSnapToEnd", enableSnapToSide_.second);
1234     json->PutExtAttr("scrollSnap", scrollSnapOptions, filter);
1235 }
1236 
GetScrollSnapPagination() const1237 std::string ScrollPattern::GetScrollSnapPagination() const
1238 {
1239     auto snapPaginationStr = std::string("");
1240     if (IsSnapToInterval()) {
1241         snapPaginationStr = intervalSize_.ToString();
1242     } else {
1243         snapPaginationStr.append("[");
1244         auto iter = snapPaginations_.begin();
1245         for (; iter != snapPaginations_.end(); ++iter) {
1246             snapPaginationStr = snapPaginationStr.append((*iter).ToString()).append(" ");
1247         }
1248         snapPaginationStr.append("]");
1249     }
1250     return snapPaginationStr;
1251 }
1252 
OnScrollSnapCallback(double targetOffset,double velocity)1253 bool ScrollPattern::OnScrollSnapCallback(double targetOffset, double velocity)
1254 {
1255     return ScrollSnapTrigger();
1256 }
1257 } // namespace OHOS::Ace::NG
1258