• 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/log/dump_log.h"
19 
20 namespace OHOS::Ace::NG {
21 
22 namespace {
23 constexpr float SCROLL_BY_SPEED = 250.0f; // move 250 pixels per second
24 constexpr float UNIT_CONVERT = 1000.0f;   // 1s convert to 1000ms
25 constexpr Dimension SELECT_SCROLL_MIN_WIDTH = 64.0_vp;
26 constexpr int32_t COLUMN_NUM = 2;
27 constexpr float SCROLL_PAGING_SPEED_THRESHOLD = 1200.0f;
28 constexpr int32_t SCROLL_LAYOUT_INFO_COUNT = 30;
29 constexpr int32_t SCROLL_MEASURE_INFO_COUNT = 30;
30 constexpr double SCROLL_SNAP_INTERVAL_SIZE_MIN_VALUE = 1.0;
31 #ifdef SUPPORT_DIGITAL_CROWN
32 constexpr int32_t CROWN_EVENT_NUN_THRESH_MIN = 5;
33 constexpr int64_t CROWN_VIBRATOR_INTERVAL_TIME = 30 * 1000 * 1000;
34 constexpr char CROWN_VIBRATOR_WEAK[] = "watchhaptic.feedback.crown.strength2";
35 #endif
36 } // namespace
37 
OnModifyDone()38 void ScrollPattern::OnModifyDone()
39 {
40     Pattern::OnModifyDone();
41     auto host = GetHost();
42     CHECK_NULL_VOID(host);
43     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
44     CHECK_NULL_VOID(layoutProperty);
45     auto paintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
46     CHECK_NULL_VOID(paintProperty);
47     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
48     if (axis != GetAxis()) {
49         SetAxis(axis);
50         ResetPosition();
51     }
52     if (!GetScrollableEvent()) {
53         AddScrollEvent();
54 #ifdef SUPPORT_DIGITAL_CROWN
55         SetDigitalCrownEvent();
56 #endif
57     }
58     SetEdgeEffect();
59     SetScrollBar(paintProperty->GetScrollBarProperty());
60     SetAccessibilityAction();
61     if (scrollSnapUpdate_) {
62         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
63     }
64     Register2DragDropManager();
65     auto overlayNode = host->GetOverlayNode();
66     if (!overlayNode && paintProperty->GetFadingEdge().value_or(false)) {
67         CreateAnalyzerOverlay(host);
68     }
69 }
70 
CreatePaintProperty()71 RefPtr<PaintProperty> ScrollPattern::CreatePaintProperty()
72 {
73     auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
74     auto property = MakeRefPtr<ScrollPaintProperty>();
75     property->UpdateScrollBarMode(defaultDisplayMode);
76     return property;
77 }
78 
CreateNodePaintMethod()79 RefPtr<NodePaintMethod> ScrollPattern::CreateNodePaintMethod()
80 {
81     auto host = GetHost();
82     CHECK_NULL_RETURN(host, nullptr);
83     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
84     CHECK_NULL_RETURN(layoutProperty, nullptr);
85     auto layoutDirection = layoutProperty->GetNonAutoLayoutDirection();
86     auto drawDirection = (layoutDirection == TextDirection::RTL);
87     auto paint = MakeRefPtr<ScrollPaintMethod>(GetAxis() == Axis::HORIZONTAL, drawDirection);
88     paint->SetScrollBar(GetScrollBar());
89     paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
90     auto scrollEffect = GetScrollEdgeEffect();
91     if (scrollEffect && scrollEffect->IsFadeEffect()) {
92         paint->SetEdgeEffect(scrollEffect);
93     }
94     if (!scrollContentModifier_) {
95         scrollContentModifier_ = AceType::MakeRefPtr<ScrollContentModifier>();
96     }
97     paint->SetContentModifier(scrollContentModifier_);
98     UpdateFadingEdge(paint);
99     return paint;
100 }
101 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)102 bool ScrollPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
103 {
104     if (config.skipMeasure && config.skipLayout) {
105         return false;
106     }
107     if (!SetScrollProperties(dirty)) {
108         return false;
109     }
110     UpdateScrollBarOffset();
111     if (config.frameSizeChange) {
112         if (GetScrollBar() != nullptr) {
113             GetScrollBar()->ScheduleDisappearDelayTask();
114         }
115     }
116     auto host = GetHost();
117     CHECK_NULL_RETURN(host, false);
118     auto eventHub = host->GetEventHub<ScrollEventHub>();
119     CHECK_NULL_RETURN(eventHub, false);
120     PrintOffsetLog(AceLogTag::ACE_SCROLL, host->GetId(), prevOffset_ - currentOffset_);
121     FireOnDidScroll(prevOffset_ - currentOffset_);
122     auto onReachStart = eventHub->GetOnReachStart();
123     FireOnReachStart(onReachStart);
124     auto onReachEnd = eventHub->GetOnReachEnd();
125     FireOnReachEnd(onReachEnd);
126     OnScrollStop(eventHub->GetOnScrollStop());
127     ScrollSnapTrigger();
128     CheckScrollable();
129     prevOffset_ = currentOffset_;
130     auto geometryNode = host->GetGeometryNode();
131     CHECK_NULL_RETURN(geometryNode, false);
132     auto offsetRelativeToWindow = host->GetOffsetRelativeToWindow();
133     auto globalViewPort = RectF(offsetRelativeToWindow, geometryNode->GetFrameRect().GetSize());
134     host->SetViewPort(globalViewPort);
135     isInitialized_ = true;
136     SetScrollSource(SCROLL_FROM_NONE);
137     auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
138     CHECK_NULL_RETURN(paintProperty, false);
139     if (scrollEdgeType_ != ScrollEdgeType::SCROLL_NONE && AnimateStoped()) {
140         scrollEdgeType_ = ScrollEdgeType::SCROLL_NONE;
141     }
142     return paintProperty->GetFadingEdge().value_or(false);
143 }
144 
SetScrollProperties(const RefPtr<LayoutWrapper> & dirty)145 bool ScrollPattern::SetScrollProperties(const RefPtr<LayoutWrapper>& dirty)
146 {
147     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
148     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
149     auto layoutAlgorithm = DynamicCast<ScrollLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
150     CHECK_NULL_RETURN(layoutAlgorithm, false);
151     currentOffset_ = layoutAlgorithm->GetCurrentOffset();
152     auto oldScrollableDistance = scrollableDistance_;
153     scrollableDistance_ = layoutAlgorithm->GetScrollableDistance();
154     if (!NearEqual(oldScrollableDistance, scrollableDistance_)) {
155         CheckScrollToEdge();
156         AddScrollLayoutInfo();
157     }
158 
159     if (LessNotEqual(scrollableDistance_, oldScrollableDistance)) {
160         CheckRestartSpring(true);
161     }
162     auto axis = GetAxis();
163     auto oldMainSize = GetMainAxisSize(viewPort_, axis);
164     auto newMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPort(), axis);
165     auto oldExtentMainSize = GetMainAxisSize(viewPortExtent_, axis);
166     auto newExtentMainSize = GetMainAxisSize(layoutAlgorithm->GetViewPortExtent(), axis);
167     viewPortLength_ = layoutAlgorithm->GetViewPortLength();
168     viewPort_ = layoutAlgorithm->GetViewPort();
169     viewSize_ = layoutAlgorithm->GetViewSize();
170     viewPortExtent_ = layoutAlgorithm->GetViewPortExtent();
171     if (IsEnablePagingValid()) {
172         SetIntervalSize(Dimension(static_cast<double>(viewPortLength_)));
173     }
174     if (scrollSnapUpdate_ || !NearEqual(oldMainSize, newMainSize) || !NearEqual(oldExtentMainSize, newExtentMainSize)) {
175         CaleSnapOffsets();
176         scrollSnapUpdate_ = false;
177     }
178     return true;
179 }
180 
ScrollSnapTrigger()181 bool ScrollPattern::ScrollSnapTrigger()
182 {
183     if (ScrollableIdle() && !AnimateRunning()) {
184         SnapAnimationOptions snapAnimationOptions;
185         if (StartSnapAnimation(snapAnimationOptions)) {
186             if (!IsScrolling()) {
187                 FireOnScrollStart();
188             }
189             return true;
190         }
191     }
192     return false;
193 }
194 
CheckScrollable()195 void ScrollPattern::CheckScrollable()
196 {
197     auto host = GetHost();
198     CHECK_NULL_VOID(host);
199     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
200     CHECK_NULL_VOID(layoutProperty);
201     if (GreatNotEqual(scrollableDistance_, 0.0f)) {
202         SetScrollEnabled(layoutProperty->GetScrollEnabled().value_or(true));
203     } else {
204         SetScrollEnabled(layoutProperty->GetScrollEnabled().value_or(true) && GetAlwaysEnabled());
205     }
206 }
207 
OnScrollCallback(float offset,int32_t source)208 bool ScrollPattern::OnScrollCallback(float offset, int32_t source)
209 {
210     if (source != SCROLL_FROM_START) {
211         if (GetAxis() == Axis::NONE) {
212             return false;
213         }
214         if (!AnimateStoped()) {
215             return false;
216         }
217         auto adjustOffset = static_cast<float>(offset);
218         AdjustOffset(adjustOffset, source);
219         return UpdateCurrentOffset(adjustOffset, source);
220     } else {
221         if (GetSnapType() == SnapType::SCROLL_SNAP) {
222             SetScrollableCurrentPos(currentOffset_);
223         }
224         FireOnScrollStart();
225     }
226     return true;
227 }
228 
OnScrollEndCallback()229 void ScrollPattern::OnScrollEndCallback()
230 {
231     auto host = GetHost();
232     CHECK_NULL_VOID(host);
233     auto eventHub = host->GetEventHub<ScrollEventHub>();
234     CHECK_NULL_VOID(eventHub);
235     auto scrollEndEvent = eventHub->GetScrollEndEvent();
236     if (scrollEndEvent) {
237         scrollEndEvent();
238     }
239     if (AnimateStoped()) {
240         scrollStop_ = true;
241         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
242     }
243 #ifdef SUPPORT_DIGITAL_CROWN
244     crownEventNum_ = 0;
245 #endif
246 }
247 
ResetPosition()248 void ScrollPattern::ResetPosition()
249 {
250     currentOffset_ = 0.0f;
251     lastOffset_ = 0.0f;
252 }
253 
IsAtTop() const254 bool ScrollPattern::IsAtTop() const
255 {
256     return GreatOrEqual(currentOffset_, 0.0);
257 }
258 
IsAtBottom() const259 bool ScrollPattern::IsAtBottom() const
260 {
261     if (LessNotEqual(scrollableDistance_, 0.0f)) {
262         return LessOrEqual(currentOffset_, 0.0f);
263     }
264     return LessOrEqual(currentOffset_, -scrollableDistance_);
265 }
266 
GetOverScrollOffset(double delta) const267 OverScrollOffset ScrollPattern::GetOverScrollOffset(double delta) const
268 {
269     OverScrollOffset offset = { 0, 0 };
270     auto startPos = currentOffset_;
271     auto newStartPos = startPos + delta;
272     if (startPos > 0 && newStartPos > 0) {
273         offset.start = delta;
274     }
275     if (startPos > 0 && newStartPos <= 0) {
276         offset.start = -startPos;
277     }
278     if (startPos <= 0 && newStartPos > 0) {
279         offset.start = newStartPos;
280     }
281 
282     auto endPos = currentOffset_;
283     auto newEndPos = endPos + delta;
284     auto endRefences =  GreatOrEqual(scrollableDistance_, 0.0f) ? -scrollableDistance_ : 0;
285     if (endPos < endRefences && newEndPos < endRefences) {
286         offset.end = delta;
287     }
288     if (endPos < endRefences && newEndPos >= endRefences) {
289         offset.end = endRefences - endPos;
290     }
291     if (endPos >= endRefences && newEndPos < endRefences) {
292         offset.end = newEndPos - endRefences;
293     }
294     return offset;
295 }
296 
IsOutOfBoundary(bool useCurrentDelta)297 bool ScrollPattern::IsOutOfBoundary(bool useCurrentDelta)
298 {
299     if (Positive(scrollableDistance_)) {
300         return Positive(currentOffset_) || LessNotEqual(currentOffset_, -scrollableDistance_);
301     } else {
302         return !NearZero(currentOffset_);
303     }
304 }
305 
ScrollPageCheck(float delta,int32_t source)306 bool ScrollPattern::ScrollPageCheck(float delta, int32_t source)
307 {
308     return true;
309 }
310 
AdjustOffset(float & delta,int32_t source)311 void ScrollPattern::AdjustOffset(float& delta, int32_t source)
312 {
313     if (NearZero(delta) || NearZero(viewPortLength_) || source == SCROLL_FROM_ANIMATION ||
314         source == SCROLL_FROM_ANIMATION_SPRING || source == SCROLL_FROM_FOCUS_JUMP) {
315         return;
316     }
317     // the distance above the top, if lower than top, it is zero
318     float overScrollPastStart = 0.0f;
319     // the distance below the bottom, if higher than bottom, it is zero
320     float overScrollPastEnd = 0.0f;
321     float overScrollPast = 0.0f;
322     // not consider rowReverse or colReverse
323     overScrollPastStart = std::max(currentOffset_, 0.0f);
324     if (Positive(scrollableDistance_)) {
325         overScrollPastEnd = std::max(-scrollableDistance_ - currentOffset_, 0.0f);
326     } else {
327         overScrollPastEnd = std::abs(std::min(currentOffset_, 0.0f));
328     }
329     overScrollPast = std::max(overScrollPastStart, overScrollPastEnd);
330     if (overScrollPast == 0.0f) {
331         return;
332     }
333     float friction = CalculateFriction((overScrollPast - std::abs(delta)) / viewPortLength_);
334     delta = delta * friction;
335 }
336 
ValidateOffset(int32_t source,float willScrollOffset)337 float ScrollPattern::ValidateOffset(int32_t source, float willScrollOffset)
338 {
339     if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) {
340         return willScrollOffset;
341     }
342 
343     // restrict position between top and bottom
344     if (IsRestrictBoundary() || source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING ||
345         source == SCROLL_FROM_ROTATE || source == SCROLL_FROM_AXIS) {
346         if (GetAxis() == Axis::HORIZONTAL) {
347             if (IsRowReverse()) {
348                 willScrollOffset = std::clamp(willScrollOffset, 0.0f, scrollableDistance_);
349             } else {
350                 willScrollOffset = std::clamp(willScrollOffset, -scrollableDistance_, 0.0f);
351             }
352         } else {
353             willScrollOffset = std::clamp(willScrollOffset, -scrollableDistance_, 0.0f);
354         }
355     }
356     return willScrollOffset;
357 }
358 
ValidateOffset(int32_t source)359 void ScrollPattern::ValidateOffset(int32_t source)
360 {
361     if (LessOrEqual(scrollableDistance_, 0.0f) || source == SCROLL_FROM_JUMP) {
362         return;
363     }
364 
365     // restrict position between top and bottom
366     if (IsRestrictBoundary() || source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING ||
367         source == SCROLL_FROM_ROTATE || source == SCROLL_FROM_AXIS) {
368         if (GetAxis() == Axis::HORIZONTAL) {
369             if (IsRowReverse()) {
370                 currentOffset_ = std::clamp(currentOffset_, 0.0f, scrollableDistance_);
371             } else {
372                 currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f);
373             }
374         } else {
375             currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f);
376         }
377     }
378 }
379 
HandleScrollPosition(float scroll)380 void ScrollPattern::HandleScrollPosition(float scroll)
381 {
382     auto eventHub = GetEventHub<ScrollEventHub>();
383     CHECK_NULL_VOID(eventHub);
384     auto onScroll = eventHub->GetOnScrollEvent();
385     CHECK_NULL_VOID(onScroll);
386     // not consider async call
387     Dimension scrollX(0, DimensionUnit::VP);
388     Dimension scrollY(0, DimensionUnit::VP);
389     Dimension scrollPx(scroll, DimensionUnit::PX);
390     auto scrollVpValue = scrollPx.ConvertToVp();
391     if (GetAxis() == Axis::HORIZONTAL) {
392         scrollX.SetValue(scrollVpValue);
393     } else {
394         scrollY.SetValue(scrollVpValue);
395     }
396     onScroll(scrollX, scrollY);
397 }
398 
FireTwoDimensionOnWillScroll(float scroll)399 float ScrollPattern::FireTwoDimensionOnWillScroll(float scroll)
400 {
401     auto eventHub = GetEventHub<ScrollEventHub>();
402     CHECK_NULL_RETURN(eventHub, scroll);
403     auto onScroll = eventHub->GetOnWillScrollEvent();
404     CHECK_NULL_RETURN(onScroll, scroll);
405     Dimension scrollX(0, DimensionUnit::VP);
406     Dimension scrollY(0, DimensionUnit::VP);
407     Dimension scrollPx(scroll, DimensionUnit::PX);
408     auto scrollVpValue = scrollPx.ConvertToVp();
409     if (GetAxis() == Axis::HORIZONTAL) {
410         scrollX.SetValue(scrollVpValue);
411     } else {
412         scrollY.SetValue(scrollVpValue);
413     }
414     auto scrollRes =
415         onScroll(scrollX, scrollY, GetScrollState(), ScrollablePattern::ConvertScrollSource(GetScrollSource()));
416     auto context = GetContext();
417     CHECK_NULL_RETURN(context, scroll);
418     if (GetAxis() == Axis::HORIZONTAL) {
419         return context->NormalizeToPx(scrollRes.xOffset);
420     } else {
421         return context->NormalizeToPx(scrollRes.yOffset);
422     }
423 }
424 
FireOnDidScroll(float scroll)425 void ScrollPattern::FireOnDidScroll(float scroll)
426 {
427     FireObserverOnDidScroll(scroll);
428     auto eventHub = GetEventHub<ScrollEventHub>();
429     CHECK_NULL_VOID(eventHub);
430     auto onScroll = eventHub->GetOnDidScrollEvent();
431     CHECK_NULL_VOID(onScroll);
432     Dimension scrollX(0, DimensionUnit::VP);
433     Dimension scrollY(0, DimensionUnit::VP);
434     Dimension scrollPx(scroll, DimensionUnit::PX);
435     auto scrollVpValue = scrollPx.ConvertToVp();
436     if (GetAxis() == Axis::HORIZONTAL) {
437         scrollX.SetValue(scrollVpValue);
438     } else {
439         scrollY.SetValue(scrollVpValue);
440     }
441     auto scrollState = GetScrollState();
442     bool isTriggered = false;
443     if (!NearZero(scroll)) {
444         onScroll(scrollX, scrollY, scrollState);
445         isTriggered = true;
446     }
447     if (scrollStop_ && !GetScrollAbort()) {
448         if (scrollState != ScrollState::IDLE || !isTriggered) {
449             onScroll(0.0_vp, 0.0_vp, ScrollState::IDLE);
450         }
451     }
452 }
453 
FireOnReachStart(const OnReachEvent & onReachStart)454 void ScrollPattern::FireOnReachStart(const OnReachEvent& onReachStart)
455 {
456     auto host = GetHost();
457     CHECK_NULL_VOID(host);
458     if (ReachStart(!isInitialized_)) {
459         FireObserverOnReachStart();
460         CHECK_NULL_VOID(onReachStart);
461         ACE_SCOPED_TRACE("OnReachStart, id:%d, tag:Scroll", static_cast<int32_t>(host->GetAccessibilityId()));
462         onReachStart();
463         AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
464     }
465 }
466 
FireOnReachEnd(const OnReachEvent & onReachEnd)467 void ScrollPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
468 {
469     auto host = GetHost();
470     CHECK_NULL_VOID(host);
471     if (ReachEnd(false)) {
472         FireObserverOnReachEnd();
473         CHECK_NULL_VOID(onReachEnd);
474         ACE_SCOPED_TRACE("OnReachEnd, id:%d, tag:Scroll", static_cast<int32_t>(host->GetAccessibilityId()));
475         onReachEnd();
476         AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
477     } else if (!isInitialized_ && ReachEnd(true)) {
478         FireObserverOnReachEnd();
479     }
480 }
481 
IsCrashTop() const482 bool ScrollPattern::IsCrashTop() const
483 {
484     bool scrollUpToReachTop = LessNotEqual(lastOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
485     bool scrollDownToReachTop = GreatNotEqual(lastOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
486     return scrollUpToReachTop || scrollDownToReachTop;
487 }
488 
IsCrashBottom() const489 bool ScrollPattern::IsCrashBottom() const
490 {
491     float minExtent = -scrollableDistance_;
492     bool scrollDownToReachEnd = GreatNotEqual(lastOffset_, minExtent) && LessOrEqual(currentOffset_, minExtent);
493     bool scrollUpToReachEnd = LessNotEqual(lastOffset_, minExtent) && GreatOrEqual(currentOffset_, minExtent);
494     return (scrollUpToReachEnd || scrollDownToReachEnd);
495 }
496 
ReachStart(bool firstLayout) const497 bool ScrollPattern::ReachStart(bool firstLayout) const
498 {
499     bool scrollUpToReachTop = (LessNotEqual(prevOffset_, 0.0) || firstLayout) && GreatOrEqual(currentOffset_, 0.0);
500     bool scrollDownToReachTop = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
501     return scrollUpToReachTop || scrollDownToReachTop;
502 }
503 
ReachEnd(bool firstLayout) const504 bool ScrollPattern::ReachEnd(bool firstLayout) const
505 {
506     float minExtent = -scrollableDistance_;
507     bool scrollDownToReachEnd =
508         (GreatNotEqual(prevOffset_, minExtent) || firstLayout) && LessOrEqual(currentOffset_, minExtent);
509     bool scrollUpToReachEnd = LessNotEqual(prevOffset_, minExtent) && GreatOrEqual(currentOffset_, minExtent);
510     return (scrollUpToReachEnd || scrollDownToReachEnd);
511 }
512 
HandleCrashTop()513 void ScrollPattern::HandleCrashTop()
514 {
515     auto frameNode = GetHost();
516     CHECK_NULL_VOID(frameNode);
517     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
518     CHECK_NULL_VOID(eventHub);
519     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
520     CHECK_NULL_VOID(onScrollEdge);
521     // not consider async call
522     if (GetAxis() == Axis::HORIZONTAL) {
523         onScrollEdge(ScrollEdge::LEFT);
524         AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
525         return;
526     }
527     onScrollEdge(ScrollEdge::TOP);
528     AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
529 }
530 
HandleCrashBottom()531 void ScrollPattern::HandleCrashBottom()
532 {
533     auto frameNode = GetHost();
534     CHECK_NULL_VOID(frameNode);
535     auto eventHub = frameNode->GetEventHub<ScrollEventHub>();
536     CHECK_NULL_VOID(eventHub);
537     const auto& onScrollEdge = eventHub->GetScrollEdgeEvent();
538     CHECK_NULL_VOID(onScrollEdge);
539     if (GetAxis() == Axis::HORIZONTAL) {
540         onScrollEdge(ScrollEdge::RIGHT);
541         AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
542         return;
543     }
544     onScrollEdge(ScrollEdge::BOTTOM);
545     AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_EDGE);
546 }
547 
548 #ifdef SUPPORT_DIGITAL_CROWN
StartVibrateFeedback()549 void ScrollPattern::StartVibrateFeedback()
550 {
551     if (!GetCrownEventDragging()) {
552         return;
553     }
554     if (crownEventNum_ < CROWN_EVENT_NUN_THRESH_MIN) {
555         crownEventNum_++;
556     }
557     auto currentTime = GetSysTimestamp();
558     if (!reachBoundary_ &&
559         (crownEventNum_ >= CROWN_EVENT_NUN_THRESH_MIN && currentTime - lastTime_ > CROWN_VIBRATOR_INTERVAL_TIME)) {
560         VibratorUtils::StartVibraFeedback(CROWN_VIBRATOR_WEAK);
561         lastTime_ = GetSysTimestamp();
562     }
563 }
564 #endif
565 
UpdateCurrentOffset(float delta,int32_t source)566 bool ScrollPattern::UpdateCurrentOffset(float delta, int32_t source)
567 {
568 #ifdef SUPPORT_DIGITAL_CROWN
569     if (source == SCROLL_FROM_CROWN && !ReachStart(true) !ReachEnd(true)) {
570         StartVibrateFeedback();
571     }
572 #endif
573 
574     auto host = GetHost();
575     CHECK_NULL_RETURN(host, false);
576     if (source != SCROLL_FROM_JUMP && !HandleEdgeEffect(delta, source, viewSize_)) {
577         if (IsOutOfBoundary()) {
578             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
579         }
580         return false;
581     }
582     SetScrollSource(source);
583     FireAndCleanScrollingListener();
584     lastOffset_ = currentOffset_;
585     auto willScrollPosition = currentOffset_ + delta;
586     willScrollPosition = ValidateOffset(source, willScrollPosition);
587     auto userOffset = FireTwoDimensionOnWillScroll(currentOffset_ - willScrollPosition);
588     currentOffset_ -= userOffset;
589     ValidateOffset(source);
590     HandleScrollPosition(userOffset);
591     if (IsCrashTop()) {
592         TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "UpdateCurrentOffset==>[HandleCrashTop();]");
593 #ifdef SUPPORT_DIGITAL_CROWN
594         SetReachBoundary(true);
595 #endif
596         HandleCrashTop();
597     } else if (IsCrashBottom()) {
598         TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "UpdateCurrentOffset==>[HandleCrashBottom();]");
599 #ifdef SUPPORT_DIGITAL_CROWN
600         SetReachBoundary(true);
601 #endif
602         HandleCrashBottom();
603     }
604 #ifdef SUPPORT_DIGITAL_CROWN
605     if (!IsCrashBottom() && !IsCrashTop()) {
606         SetReachBoundary(false);
607     }
608 #endif
609     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
610     return true;
611 }
612 
OnAnimateStop()613 void ScrollPattern::OnAnimateStop()
614 {
615     if (!GetIsDragging() || GetScrollAbort()) {
616         auto host = GetHost();
617         CHECK_NULL_VOID(host);
618         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
619         scrollStop_ = true;
620     }
621 }
622 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)623 void ScrollPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
624 {
625     if (scrollEdgeType == ScrollEdgeType::SCROLL_NONE) {
626         return;
627     }
628     if (LessOrEqual(scrollableDistance_, 0.0)) {
629         return;
630     }
631     float distance = scrollEdgeType == ScrollEdgeType::SCROLL_TOP ? -currentOffset_ :
632         (-scrollableDistance_ - currentOffset_);
633     auto host = GetHost();
634     CHECK_NULL_VOID(host);
635     ACE_SCOPED_TRACE("Scroll ScrollToEdge scrollEdgeType:%zu, offset:%f, id:%d", scrollEdgeType, distance,
636         static_cast<int32_t>(host->GetAccessibilityId()));
637     ScrollBy(distance, distance, smooth);
638     scrollEdgeType_ = scrollEdgeType;
639 }
640 
CheckScrollToEdge()641 void ScrollPattern::CheckScrollToEdge()
642 {
643     if (scrollEdgeType_ != ScrollEdgeType::SCROLL_NONE) {
644         ScrollToEdge(scrollEdgeType_, true);
645     }
646 }
647 
ScrollBy(float pixelX,float pixelY,bool smooth,const std::function<void ()> & onFinish)648 void ScrollPattern::ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish)
649 {
650     float distance = (GetAxis() == Axis::VERTICAL) ? pixelY : pixelX;
651     if (NearZero(distance)) {
652         return;
653     }
654     float position = currentOffset_ + distance;
655     if (smooth) {
656         AnimateTo(-position, fabs(distance) * UNIT_CONVERT / SCROLL_BY_SPEED, Curves::EASE_OUT, true, false, false);
657         return;
658     }
659     JumpToPosition(position);
660 }
661 
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)662 void ScrollPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
663 {
664     auto host = GetHost();
665     CHECK_NULL_VOID(host);
666     float distance = reverse ? viewPortLength_ : -viewPortLength_;
667     if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
668         distance = distance / 2.f;
669     }
670     ACE_SCOPED_TRACE(
671         "Scroll ScrollPage distance:%f, id:%d", distance, static_cast<int32_t>(host->GetAccessibilityId()));
672     ScrollBy(distance, distance, smooth);
673 }
674 
JumpToPosition(float position,int32_t source)675 void ScrollPattern::JumpToPosition(float position, int32_t source)
676 {
677     // If an animation is playing, stop it.
678     auto lastAnimateRunning = AnimateRunning();
679     StopAnimate();
680     DoJump(position, source);
681     // AccessibilityEventType::SCROLL_END
682     if (lastAnimateRunning) {
683         SetScrollAbort(false);
684     }
685 }
686 
ScrollTo(float position)687 void ScrollPattern::ScrollTo(float position)
688 {
689     JumpToPosition(-position, SCROLL_FROM_JUMP);
690 }
691 
DoJump(float position,int32_t source)692 void ScrollPattern::DoJump(float position, int32_t source)
693 {
694     float setPosition = (GetAxis() == Axis::HORIZONTAL && IsRowReverse()) ? -position : position;
695     if (!NearEqual(currentOffset_, setPosition) && GreatOrEqual(scrollableDistance_, 0.0f)) {
696         UpdateCurrentOffset(setPosition - currentOffset_, source);
697     }
698 }
699 
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)700 void ScrollPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
701 {
702     scrollEffect->SetCurrentPositionCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
703         auto scroll = weakScroll.Upgrade();
704         CHECK_NULL_RETURN(scroll, 0.0);
705         return scroll->GetCurrentPosition();
706     });
707     scrollEffect->SetLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
708         auto scroll = weakScroll.Upgrade();
709         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
710             return -scroll->GetScrollableDistance();
711         }
712         return 0.0;
713     });
714     scrollEffect->SetTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
715         auto scroll = weakScroll.Upgrade();
716         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
717             return scroll->GetScrollableDistance();
718         }
719         return 0.0;
720     });
721     scrollEffect->SetInitLeadingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
722         auto scroll = weakScroll.Upgrade();
723         if (scroll && !scroll->IsRowReverse() && !scroll->IsColReverse() && scroll->GetScrollableDistance() > 0) {
724             return -scroll->GetScrollableDistance();
725         }
726         return 0.0;
727     });
728     scrollEffect->SetInitTrailingCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
729         auto scroll = weakScroll.Upgrade();
730         if (scroll && (scroll->IsRowReverse() || scroll->IsColReverse())) {
731             return scroll->GetScrollableDistance();
732         }
733         return 0.0;
734     });
735 }
736 
UpdateScrollBarOffset()737 void ScrollPattern::UpdateScrollBarOffset()
738 {
739     CheckScrollBarOff();
740     if (!GetScrollBar() && !GetScrollBarProxy()) {
741         return;
742     }
743 
744     float scrollBarOutBoundaryExtent = 0.0f;
745     if (currentOffset_ > 0) {
746         scrollBarOutBoundaryExtent = currentOffset_;
747     } else if ((-currentOffset_) >= (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_))) {
748         scrollBarOutBoundaryExtent = -currentOffset_ - (GetMainSize(viewPortExtent_) - GetMainSize(viewPort_));
749     }
750     HandleScrollBarOutBoundary(scrollBarOutBoundaryExtent);
751 
752     auto host = GetHost();
753     CHECK_NULL_VOID(host);
754     auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
755     CHECK_NULL_VOID(layoutProperty);
756     auto padding = layoutProperty->CreatePaddingAndBorder();
757     auto contentEndOffset = layoutProperty->GetScrollContentEndOffsetValue(.0f);
758     Size size(viewSize_.Width(), viewSize_.Height() - contentEndOffset);
759     auto viewPortExtent = viewPortExtent_;
760     AddPaddingToSize(padding, viewPortExtent);
761     auto estimatedHeight = (GetAxis() == Axis::HORIZONTAL) ? viewPortExtent.Width() : viewPortExtent.Height();
762     UpdateScrollBarRegion(-currentOffset_, estimatedHeight, size, Offset(0.0f, 0.0f));
763 }
764 
SetAccessibilityAction()765 void ScrollPattern::SetAccessibilityAction()
766 {
767     auto host = GetHost();
768     CHECK_NULL_VOID(host);
769     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
770     CHECK_NULL_VOID(accessibilityProperty);
771     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
772         const auto& pattern = weakPtr.Upgrade();
773         CHECK_NULL_VOID(pattern);
774         auto host = pattern->GetHost();
775         CHECK_NULL_VOID(host);
776         ACE_SCOPED_TRACE("accessibility action, scroll forward, isScrollable:%u, IsPositiveScrollableDistance:%u, "
777                          "scrollType:%d, id:%d, tag:Scroll",
778             pattern->IsScrollable(), pattern->IsPositiveScrollableDistance(), scrollType,
779             static_cast<int32_t>(host->GetAccessibilityId()));
780         if (pattern->IsScrollable() && pattern->IsPositiveScrollableDistance()) {
781             pattern->ScrollPage(false, true, scrollType);
782         }
783     });
784 
785     accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
786         const auto& pattern = weakPtr.Upgrade();
787         CHECK_NULL_VOID(pattern);
788         auto host = pattern->GetHost();
789         CHECK_NULL_VOID(host);
790         ACE_SCOPED_TRACE("accessibility action, scroll backward, isScrollable:%u, IsPositiveScrollableDistance:%u, "
791                          "scrollType:%d, id:%d, tag:Scroll",
792             pattern->IsScrollable(), pattern->IsPositiveScrollableDistance(), scrollType,
793             static_cast<int32_t>(host->GetAccessibilityId()));
794         if (pattern->IsScrollable() && pattern->IsPositiveScrollableDistance()) {
795             pattern->ScrollPage(true, true, scrollType);
796         }
797     });
798 }
799 
GetOffsetToScroll(const RefPtr<FrameNode> & childFrame) const800 OffsetF ScrollPattern::GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const
801 {
802     auto frameNode = GetHost();
803     CHECK_NULL_RETURN(frameNode, OffsetF());
804     CHECK_NULL_RETURN(childFrame, OffsetF());
805     auto childGeometryNode = childFrame->GetGeometryNode();
806     CHECK_NULL_RETURN(childGeometryNode, OffsetF());
807     OffsetF result = childGeometryNode->GetFrameOffset();
808     auto parent = childFrame->GetParent();
809     while (parent) {
810         auto parentFrame = AceType::DynamicCast<FrameNode>(parent);
811         if (!parentFrame) {
812             parent = parent->GetParent();
813             continue;
814         }
815         if (parentFrame == frameNode) {
816             return result;
817         }
818         auto parentGeometryNode = parentFrame->GetGeometryNode();
819         if (!parentGeometryNode) {
820             parent = parent->GetParent();
821             continue;
822         }
823         result += parentGeometryNode->GetFrameOffset();
824         parent = parent->GetParent();
825     }
826     return OffsetF(0.0, 0.0);
827 }
828 
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)829 bool ScrollPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
830 {
831     CHECK_NULL_RETURN(focusFrameNode, false);
832     auto focusGeometryNode = focusFrameNode->GetGeometryNode();
833     CHECK_NULL_RETURN(focusGeometryNode, false);
834     auto focusNodeSize = focusGeometryNode->GetFrameSize();
835     auto focusNodeOffsetToScrolll = GetOffsetToScroll(focusFrameNode);
836     auto scrollFrame = GetHost();
837     CHECK_NULL_RETURN(scrollFrame, false);
838     auto scrollGeometry = scrollFrame->GetGeometryNode();
839     CHECK_NULL_RETURN(scrollGeometry, false);
840     auto scrollFrameSize = scrollGeometry->GetFrameSize();
841     float focusNodeDiffToScroll =
842         GetAxis() == Axis::VERTICAL ? focusNodeOffsetToScrolll.GetY() : focusNodeOffsetToScrolll.GetX();
843     if (NearZero(focusNodeDiffToScroll)) {
844         return false;
845     }
846     float focusNodeLength = GetAxis() == Axis::VERTICAL ? focusNodeSize.Height() : focusNodeSize.Width();
847     float scrollFrameLength = GetAxis() == Axis::VERTICAL ? scrollFrameSize.Height() : scrollFrameSize.Width();
848     float moveOffset = 0.0;
849     if (LessNotEqual(focusNodeDiffToScroll, 0)) {
850         moveOffset = -focusNodeDiffToScroll;
851     } else if (GreatNotEqual(focusNodeDiffToScroll + focusNodeLength, scrollFrameLength)) {
852         moveOffset = scrollFrameLength - focusNodeDiffToScroll - focusNodeLength;
853     }
854     if (!NearZero(moveOffset)) {
855         return OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP);
856     }
857     return false;
858 }
859 
GetScrollOffsetAbility()860 ScrollOffsetAbility ScrollPattern::GetScrollOffsetAbility()
861 {
862     return { [wp = WeakClaim(this)](float moveOffset) -> bool {
863                 auto pattern = wp.Upgrade();
864                 CHECK_NULL_RETURN(pattern, false);
865                 return pattern->OnScrollCallback(moveOffset, SCROLL_FROM_FOCUS_JUMP);
866             },
867         GetAxis() };
868 }
869 
CalcPredictSnapOffset(float delta,float dragDistance,float velocity,SnapDirection snapDirection)870 std::optional<float> ScrollPattern::CalcPredictSnapOffset(
871     float delta, float dragDistance, float velocity, SnapDirection snapDirection)
872 {
873     std::optional<float> predictSnapOffset;
874     CHECK_NULL_RETURN(IsScrollSnap(), predictSnapOffset);
875     if (snapDirection != SnapDirection::NONE) {
876         return CalcPredictNextSnapOffset(delta, snapDirection);
877     }
878     float finalPosition = currentOffset_ + delta;
879     if (IsEnablePagingValid()) {
880         finalPosition = GetPagingOffset(delta, dragDistance, velocity);
881     }
882     if (!IsSnapToInterval()) {
883         if (!enableSnapToSide_.first) {
884             CHECK_NULL_RETURN(!(GreatNotEqual(finalPosition, *(snapOffsets_.begin() + 1)) ||
885                                   GreatNotEqual(currentOffset_, *(snapOffsets_.begin() + 1))),
886                 predictSnapOffset);
887         }
888         if (!enableSnapToSide_.second) {
889             CHECK_NULL_RETURN(!(LessNotEqual(finalPosition, *(snapOffsets_.rbegin() + 1)) ||
890                                   LessNotEqual(currentOffset_, *(snapOffsets_.rbegin() + 1))),
891                 predictSnapOffset);
892         }
893     }
894     float head = 0.0f;
895     float tail = -scrollableDistance_;
896     if (GreatOrEqual(finalPosition, head) || LessOrEqual(finalPosition, tail)) {
897         predictSnapOffset = finalPosition;
898     } else if (LessNotEqual(finalPosition, head) && GreatOrEqual(finalPosition, *(snapOffsets_.begin()))) {
899         predictSnapOffset = *(snapOffsets_.begin());
900     } else if (GreatNotEqual(finalPosition, tail) && LessOrEqual(finalPosition, *(snapOffsets_.rbegin()))) {
901         predictSnapOffset = *(snapOffsets_.rbegin());
902     } else {
903         auto iter = snapOffsets_.begin() + 1;
904         float start = *(iter - 1);
905         float end = *(iter);
906         for (; iter != snapOffsets_.end(); ++iter) {
907             if (GreatOrEqual(finalPosition, *iter)) {
908                 start = *(iter - 1);
909                 end = *(iter);
910                 predictSnapOffset = (LessNotEqual(start - finalPosition, finalPosition - end) ? start : end);
911                 break;
912             }
913         }
914     }
915     if (predictSnapOffset.has_value()) {
916         predictSnapOffset = predictSnapOffset.value() - currentOffset_;
917     }
918     return predictSnapOffset;
919 }
920 
CalcPredictNextSnapOffset(float delta,SnapDirection snapDirection)921 std::optional<float> ScrollPattern::CalcPredictNextSnapOffset(float delta, SnapDirection snapDirection)
922 {
923     std::optional<float> predictSnapOffset;
924     int32_t start = 0;
925     int32_t end = static_cast<int32_t>(snapOffsets_.size()) - 1;
926     int32_t mid = 0;
927     auto targetOffset = currentOffset_ + delta;
928     if (LessOrEqual(targetOffset, snapOffsets_[end]) && snapDirection == SnapDirection::BACKWARD) {
929         predictSnapOffset = -scrollableDistance_ - currentOffset_;
930         return predictSnapOffset;
931     } else if (GreatOrEqual(targetOffset, snapOffsets_[start]) && snapDirection == SnapDirection::FORWARD) {
932         predictSnapOffset = -currentOffset_;
933         return predictSnapOffset;
934     }
935     while (start < end) {
936         mid = (start + end) / 2;
937         if (LessNotEqual(snapOffsets_[mid], targetOffset)) {
938             end = mid;
939         } else if (GreatNotEqual(snapOffsets_[mid], targetOffset)) {
940             start = mid + 1;
941         } else {
942             if (snapDirection == SnapDirection::FORWARD && mid > 0) {
943                 predictSnapOffset = snapOffsets_[mid - 1] - currentOffset_;
944             } else if (snapDirection == SnapDirection::BACKWARD &&
945                        (mid + 1) < static_cast<int32_t>(snapOffsets_.size())) {
946                 predictSnapOffset = snapOffsets_[mid + 1] - currentOffset_;
947             }
948             return predictSnapOffset;
949         }
950     }
951     if (snapDirection == SnapDirection::FORWARD) {
952         predictSnapOffset = snapOffsets_[std::max(start - 1, 0)] - currentOffset_;
953     } else if (snapDirection == SnapDirection::BACKWARD) {
954         predictSnapOffset = snapOffsets_[start] - currentOffset_;
955     }
956     return predictSnapOffset;
957 }
958 
CaleSnapOffsets()959 void ScrollPattern::CaleSnapOffsets()
960 {
961     auto scrollSnapAlign = GetScrollSnapAlign();
962     std::vector<float>().swap(snapOffsets_);
963     if (scrollSnapAlign == ScrollSnapAlign::NONE) {
964         CHECK_NULL_VOID(enablePagingStatus_ == ScrollPagingStatus::VALID);
965         scrollSnapAlign = ScrollSnapAlign::START;
966     }
967     if (IsSnapToInterval()) {
968         CaleSnapOffsetsByInterval(scrollSnapAlign);
969     } else {
970         CaleSnapOffsetsByPaginations(scrollSnapAlign);
971     }
972 }
973 
CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)974 void ScrollPattern::CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign)
975 {
976     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
977     auto intervalSize = intervalSize_.Unit() == DimensionUnit::PERCENT ?
978                         intervalSize_.Value() * mainSize : intervalSize_.ConvertToPx();
979     CHECK_NULL_VOID(GreatOrEqual(intervalSize, SCROLL_SNAP_INTERVAL_SIZE_MIN_VALUE));
980     auto extentMainSize = GetMainAxisSize(viewPortExtent_, GetAxis());
981     auto start = 0.0f;
982     auto end = -scrollableDistance_;
983     auto snapOffset = 0.0f;
984     auto sizeDelta = 0.0f;
985     float temp = static_cast<int32_t>(extentMainSize / intervalSize) * intervalSize;
986     switch (scrollSnapAlign) {
987         case ScrollSnapAlign::START:
988             end = -temp;
989             break;
990         case ScrollSnapAlign::CENTER:
991             sizeDelta = (mainSize - intervalSize) / 2;
992             start = Positive(sizeDelta) ? sizeDelta - static_cast<int32_t>(sizeDelta / intervalSize) * intervalSize
993                                         : sizeDelta;
994             end = -temp + (mainSize - extentMainSize + temp) / 2;
995             break;
996         case ScrollSnapAlign::END:
997             sizeDelta = mainSize - intervalSize;
998             start = Positive(sizeDelta) ? mainSize - static_cast<int32_t>(mainSize / intervalSize) * intervalSize
999                                         : sizeDelta;
1000             end = -scrollableDistance_;
1001             break;
1002         default:
1003             break;
1004     }
1005     if (!Positive(start)) {
1006         snapOffsets_.emplace_back(start);
1007     }
1008     snapOffset = start - intervalSize;
1009     while (GreatOrEqual(snapOffset, -scrollableDistance_) && GreatOrEqual(snapOffset, end)) {
1010         snapOffsets_.emplace_back(snapOffset);
1011         snapOffset -= intervalSize;
1012     }
1013     if (GreatNotEqual(end, -scrollableDistance_)) {
1014         snapOffsets_.emplace_back(end);
1015     }
1016     if (IsEnablePagingValid()) {
1017         if (NearEqual(snapOffset + intervalSize, -scrollableDistance_)) {
1018             lastPageLength_ = 0.f;
1019             return;
1020         }
1021         lastPageLength_ = scrollableDistance_ + snapOffset + intervalSize;
1022         snapOffsets_.emplace_back(-scrollableDistance_);
1023     }
1024 }
1025 
CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign)1026 void ScrollPattern::CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign)
1027 {
1028     auto mainSize = GetMainAxisSize(viewPort_, GetAxis());
1029     auto extentMainSize = GetMainAxisSize(viewPortExtent_, GetAxis());
1030     auto start = 0.0f;
1031     auto end = -scrollableDistance_;
1032     auto snapOffset = 0.0f;
1033     snapOffsets_.emplace_back(start);
1034     int32_t length = 0;
1035     auto snapPaginations = snapPaginations_;
1036     snapPaginations.emplace(snapPaginations.begin(), Dimension(0.f));
1037     auto current = 0.0f;
1038     auto next = 0.0f;
1039     auto size = static_cast<int32_t>(snapPaginations.size());
1040     auto element = snapPaginations[length];
1041     auto nextElement = snapPaginations[length + 1];
1042     for (; length < size; length++) {
1043         element = snapPaginations[length];
1044         current = element.Unit() == DimensionUnit::PERCENT ? element.Value() * mainSize : element.ConvertToPx();
1045         if (length == size - 1) {
1046             next = extentMainSize;
1047         } else {
1048             nextElement = snapPaginations[length + 1];
1049             next = nextElement.Unit() == DimensionUnit::PERCENT ? nextElement.Value() * mainSize
1050                                                                 : nextElement.ConvertToPx();
1051         }
1052         switch (scrollSnapAlign) {
1053             case ScrollSnapAlign::START:
1054                 snapOffset = -current;
1055                 break;
1056             case ScrollSnapAlign::CENTER:
1057                 snapOffset = (mainSize - (current + next)) / 2.0f;
1058                 break;
1059             case ScrollSnapAlign::END:
1060                 snapOffset = mainSize - next;
1061                 break;
1062             default:
1063                 break;
1064         }
1065         if (!Negative(snapOffset)) {
1066             continue;
1067         }
1068         if (GreatNotEqual(snapOffset, -scrollableDistance_)) {
1069             snapOffsets_.emplace_back(snapOffset);
1070         } else {
1071             break;
1072         }
1073     }
1074     snapOffsets_.emplace_back(end);
1075 }
1076 
NeedScrollSnapToSide(float delta)1077 bool ScrollPattern::NeedScrollSnapToSide(float delta)
1078 {
1079     CHECK_NULL_RETURN(IsScrollSnap(), false);
1080     auto finalPosition = currentOffset_ + delta;
1081     if (IsEnablePagingValid() && GetEdgeEffect() != EdgeEffect::SPRING) {
1082         return Positive(finalPosition) || LessNotEqual(finalPosition, -scrollableDistance_);
1083     }
1084     CHECK_NULL_RETURN(!IsSnapToInterval(), false);
1085     CHECK_NULL_RETURN(static_cast<int32_t>(snapOffsets_.size()) > 2, false);
1086     if (!enableSnapToSide_.first) {
1087         if (GreatOrEqual(currentOffset_, *(snapOffsets_.begin() + 1)) &&
1088             LessOrEqual(finalPosition, *(snapOffsets_.begin() + 1))) {
1089             return true;
1090         }
1091     }
1092     if (!enableSnapToSide_.second) {
1093         if (LessOrEqual(currentOffset_, *(snapOffsets_.rbegin() + 1)) &&
1094             GreatOrEqual(finalPosition, *(snapOffsets_.rbegin() + 1))) {
1095             return true;
1096         }
1097     }
1098     return false;
1099 }
1100 
ProvideRestoreInfo()1101 std::string ScrollPattern::ProvideRestoreInfo()
1102 {
1103     Dimension dimension(currentOffset_);
1104     return StringUtils::DoubleToString(dimension.ConvertToVp());
1105 }
1106 
OnRestoreInfo(const std::string & restoreInfo)1107 void ScrollPattern::OnRestoreInfo(const std::string& restoreInfo)
1108 {
1109     Dimension dimension = StringUtils::StringToDimension(restoreInfo, true);
1110     currentOffset_ = dimension.ConvertToPx();
1111 }
1112 
GetItemRect(int32_t index) const1113 Rect ScrollPattern::GetItemRect(int32_t index) const
1114 {
1115     auto host = GetHost();
1116     CHECK_NULL_RETURN(host, Rect());
1117     if (index != 0 || host->TotalChildCount() != 1) {
1118         return Rect();
1119     }
1120     auto item = host->GetChildByIndex(index);
1121     CHECK_NULL_RETURN(item, Rect());
1122     auto itemGeometry = item->GetGeometryNode();
1123     CHECK_NULL_RETURN(itemGeometry, Rect());
1124     return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1125         itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1126 }
1127 
GetSelectScrollWidth()1128 float ScrollPattern::GetSelectScrollWidth()
1129 {
1130     RefPtr<GridColumnInfo> columnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::MENU);
1131     auto parent = columnInfo->GetParent();
1132     CHECK_NULL_RETURN(parent, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1133     parent->BuildColumnWidth();
1134     auto defaultWidth = static_cast<float>(columnInfo->GetWidth(COLUMN_NUM));
1135     auto scrollNode = GetHost();
1136     CHECK_NULL_RETURN(scrollNode, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1137     float finalWidth = SELECT_SCROLL_MIN_WIDTH.ConvertToPx();
1138 
1139     if (IsWidthModifiedBySelect()) {
1140         auto scrollLayoutProperty = scrollNode->GetLayoutProperty<ScrollLayoutProperty>();
1141         CHECK_NULL_RETURN(scrollLayoutProperty, SELECT_SCROLL_MIN_WIDTH.ConvertToPx());
1142         auto selectModifiedWidth = scrollLayoutProperty->GetScrollWidth();
1143         finalWidth = selectModifiedWidth.value();
1144     } else {
1145         finalWidth = defaultWidth;
1146     }
1147 
1148     if (finalWidth < SELECT_SCROLL_MIN_WIDTH.ConvertToPx()) {
1149         finalWidth = defaultWidth;
1150     }
1151 
1152     return finalWidth;
1153 }
1154 
GetPagingOffset(float delta,float dragDistance,float velocity) const1155 float ScrollPattern::GetPagingOffset(float delta, float dragDistance, float velocity)  const
1156 {
1157     // handle last page
1158     auto currentOffset = currentOffset_;
1159     if (GreatNotEqual(lastPageLength_, 0.f) &&
1160         LessNotEqual(currentOffset - dragDistance, -scrollableDistance_ + lastPageLength_)) {
1161         if (LessOrEqual(dragDistance, lastPageLength_)) {
1162             return currentOffset - dragDistance + GetPagingDelta(dragDistance, velocity, lastPageLength_);
1163         }
1164         if (GreatNotEqual(dragDistance, lastPageLength_)) {
1165             dragDistance -= lastPageLength_;
1166         }
1167     }
1168     // handle other pages
1169     float head = 0.0f;
1170     float tail = -scrollableDistance_;
1171     dragDistance = fmod(dragDistance, viewPortLength_);
1172     auto pagingPosition = currentOffset - dragDistance + GetPagingDelta(dragDistance, velocity, viewPortLength_);
1173     auto finalPosition = currentOffset + delta;
1174     auto useFinalPosition = (GreatOrEqual(pagingPosition, head) && !GreatOrEqual(finalPosition, head)) ||
1175                       (LessOrEqual(pagingPosition, tail) && !LessOrEqual(finalPosition, tail));
1176     return useFinalPosition ? finalPosition : pagingPosition;
1177 }
1178 
GetPagingDelta(float dragDistance,float velocity,float pageLength) const1179 float ScrollPattern::GetPagingDelta(float dragDistance, float velocity, float pageLength)  const
1180 {
1181     auto dragDistanceThreshold = pageLength * 0.5f;
1182     // dragDistance and velocity have not reached the threshold
1183     if (LessNotEqual(std::abs(dragDistance), dragDistanceThreshold) &&
1184         LessNotEqual(std::abs(velocity), SCROLL_PAGING_SPEED_THRESHOLD)) {
1185         return 0.f;
1186     }
1187     // The direction of dragDistance is the same as the direction of velocity
1188     if (GreatOrEqual(dragDistance * velocity, 0.f)) {
1189         auto direction = NearZero(dragDistance) ? velocity : dragDistance;
1190         return GreatNotEqual(direction, 0.f) ? pageLength : -pageLength;
1191     }
1192     // The direction of dragDistance is opposite to the direction of velocity
1193     if (GreatOrEqual(std::abs(dragDistance), dragDistanceThreshold) &&
1194         LessNotEqual(std::abs(velocity), SCROLL_PAGING_SPEED_THRESHOLD)) {
1195         return GreatNotEqual(dragDistance, 0.f) ? pageLength : -pageLength;
1196     }
1197     return 0.f;
1198 }
1199 
TriggerModifyDone()1200 void ScrollPattern::TriggerModifyDone()
1201 {
1202     OnModifyDone();
1203 }
1204 
AddScrollMeasureInfo(const std::optional<LayoutConstraintF> & parentConstraint,const std::optional<LayoutConstraintF> & childConstraint,const SizeF & selfSize,const SizeF & childSize)1205 void ScrollPattern::AddScrollMeasureInfo(const std::optional<LayoutConstraintF>& parentConstraint,
1206     const std::optional<LayoutConstraintF>& childConstraint, const SizeF& selfSize, const SizeF& childSize)
1207 {
1208     if (scrollMeasureInfos_.size() >= SCROLL_MEASURE_INFO_COUNT) {
1209         scrollMeasureInfos_.pop_front();
1210     }
1211     scrollMeasureInfos_.push_back(ScrollMeasureInfo({
1212         .changedTime_ = GetSysTimestamp(),
1213         .parentConstraint_ = parentConstraint,
1214         .childConstraint_ = childConstraint,
1215         .selfSize_ = selfSize,
1216         .childSize_ = childSize,
1217     }));
1218 }
1219 
AddScrollLayoutInfo()1220 void ScrollPattern::AddScrollLayoutInfo()
1221 {
1222     if (scrollLayoutInfos_.size() >= SCROLL_LAYOUT_INFO_COUNT) {
1223         scrollLayoutInfos_.pop_front();
1224     }
1225     scrollLayoutInfos_.push_back(ScrollLayoutInfo({
1226         .changedTime_ = GetSysTimestamp(),
1227         .scrollableDistance_ = scrollableDistance_,
1228         .scrollSize_ = viewSize_,
1229         .viewPort_ = viewPort_,
1230         .childSize_ = viewPortExtent_,
1231     }));
1232 }
1233 
GetScrollSnapAlignDumpInfo()1234 void ScrollPattern::GetScrollSnapAlignDumpInfo()
1235 {
1236     switch (GetScrollSnapAlign()) {
1237         case ScrollSnapAlign::NONE: {
1238             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::NONE");
1239             break;
1240         }
1241         case ScrollSnapAlign::START: {
1242             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::START");
1243             break;
1244         }
1245         case ScrollSnapAlign::CENTER: {
1246             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::CENTER");
1247             break;
1248         }
1249         case ScrollSnapAlign::END: {
1250             DumpLog::GetInstance().AddDesc("snapAlign: ScrollSnapAlign::END");
1251             break;
1252         }
1253         default: {
1254             break;
1255         }
1256     }
1257 }
1258 
GetScrollPagingStatusDumpInfo()1259 void ScrollPattern::GetScrollPagingStatusDumpInfo()
1260 {
1261     switch (enablePagingStatus_) {
1262         case ScrollPagingStatus::NONE: {
1263             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::NONE");
1264             break;
1265         }
1266         case ScrollPagingStatus::INVALID: {
1267             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::INVALID");
1268             break;
1269         }
1270         case ScrollPagingStatus::VALID: {
1271             DumpLog::GetInstance().AddDesc("enablePaging: ScrollPagingStatus::VALID");
1272             break;
1273         }
1274         default: {
1275             break;
1276         }
1277     }
1278 }
1279 
DumpAdvanceInfo()1280 void ScrollPattern::DumpAdvanceInfo()
1281 {
1282     auto host = GetHost();
1283     CHECK_NULL_VOID(host);
1284     auto hub = host->GetEventHub<ScrollEventHub>();
1285     CHECK_NULL_VOID(hub);
1286     ScrollablePattern::DumpAdvanceInfo();
1287     DumpLog::GetInstance().AddDesc(std::string("currentOffset: ").append(std::to_string(currentOffset_)));
1288     GetScrollSnapAlignDumpInfo();
1289     auto snapPaginationStr = std::string("snapPagination: ");
1290     DumpLog::GetInstance().AddDesc(snapPaginationStr.append(GetScrollSnapPagination()));
1291     enableSnapToSide_.first ? DumpLog::GetInstance().AddDesc("enableSnapToStart: true")
1292                             : DumpLog::GetInstance().AddDesc("enableSnapToStart: false");
1293     enableSnapToSide_.second ? DumpLog::GetInstance().AddDesc("enableSnapToEnd: true")
1294                              : DumpLog::GetInstance().AddDesc("enableSnapToEnd: false");
1295     GetScrollPagingStatusDumpInfo();
1296     auto snapOffsetsStr = std::string("snapOffsets: [");
1297     for (const auto& iter : snapPaginations_) {
1298         snapOffsetsStr = snapOffsetsStr.append(iter.ToString()).append(" ");
1299     }
1300     DumpLog::GetInstance().AddDesc(snapOffsetsStr.append("]"));
1301     initialOffset_.has_value() ? DumpLog::GetInstance().AddDesc(std::string("initialOffset: ")
1302         .append(initialOffset_->GetMainOffset(GetAxis()).ToString()))
1303         : DumpLog::GetInstance().AddDesc("initialOffset: None");
1304     auto onScrollEdge = hub->GetScrollEdgeEvent();
1305     onScrollEdge ? DumpLog::GetInstance().AddDesc("hasOnScrollEdge: true")
1306                  : DumpLog::GetInstance().AddDesc("hasOnScrollEdge: false");
1307     DumpLog::GetInstance().AddDesc("==========================scrollLayoutInfos==========================");
1308     for (const auto& info : scrollLayoutInfos_) {
1309         DumpLog::GetInstance().AddDesc(info.ToString());
1310     }
1311     DumpLog::GetInstance().AddDesc("==========================scrollLayoutInfos==========================");
1312     DumpLog::GetInstance().AddDesc("==========================scrollMeasureInfos==========================");
1313     for (const auto& info : scrollMeasureInfos_) {
1314         DumpLog::GetInstance().AddDesc(info.ToString());
1315     }
1316     DumpLog::GetInstance().AddDesc("==========================scrollMeasureInfos==========================");
1317 }
1318 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const1319 void ScrollPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
1320 {
1321     ScrollablePattern::ToJsonValue(json, filter);
1322     /* no fixed attr below, just return */
1323     if (filter.IsFastFilter()) {
1324         return;
1325     }
1326     auto initialOffset = JsonUtil::Create(true);
1327     initialOffset->Put("xOffset", GetInitialOffset().GetX().ToString().c_str());
1328     initialOffset->Put("yOffset", GetInitialOffset().GetY().ToString().c_str());
1329     json->PutExtAttr("initialOffset", initialOffset, filter);
1330     if (enablePagingStatus_ != ScrollPagingStatus::NONE) {
1331         json->PutExtAttr("enablePaging", enablePagingStatus_ == ScrollPagingStatus::VALID, filter);
1332     }
1333 
1334     auto scrollSnapOptions = JsonUtil::Create(true);
1335     if (IsSnapToInterval()) {
1336         scrollSnapOptions->Put("snapPagination", intervalSize_.ToString().c_str());
1337     } else {
1338         auto snapPaginationArr = JsonUtil::CreateArray(true);
1339         auto iter = snapPaginations_.begin();
1340         for (auto i = 0; iter != snapPaginations_.end(); ++iter, ++i) {
1341             snapPaginationArr->Put(std::to_string(i).c_str(), (*iter).ToString().c_str());
1342         }
1343         scrollSnapOptions->Put("snapPagination", snapPaginationArr);
1344     }
1345     scrollSnapOptions->Put("enableSnapToStart", enableSnapToSide_.first);
1346     scrollSnapOptions->Put("enableSnapToEnd", enableSnapToSide_.second);
1347     json->PutExtAttr("scrollSnap", scrollSnapOptions, filter);
1348 }
1349 
GetScrollSnapPagination() const1350 std::string ScrollPattern::GetScrollSnapPagination() const
1351 {
1352     auto snapPaginationStr = std::string("");
1353     if (IsSnapToInterval()) {
1354         snapPaginationStr = intervalSize_.ToString();
1355     } else {
1356         snapPaginationStr.append("[");
1357         auto iter = snapPaginations_.begin();
1358         for (; iter != snapPaginations_.end(); ++iter) {
1359             snapPaginationStr = snapPaginationStr.append((*iter).ToString()).append(" ");
1360         }
1361         snapPaginationStr.append("]");
1362     }
1363     return snapPaginationStr;
1364 }
1365 
StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)1366 bool ScrollPattern::StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)
1367 {
1368     auto scrollBar = GetScrollBar();
1369     auto scrollBarProxy = GetScrollBarProxy();
1370     auto fromScrollBar = snapAnimationOptions.fromScrollBar;
1371     if (!fromScrollBar && scrollBar && scrollBar->IsDriving()) {
1372         return false;
1373     }
1374     if (!fromScrollBar && scrollBarProxy && scrollBarProxy->IsScrollSnapTrigger()) {
1375         return false;
1376     }
1377     auto predictSnapOffset = CalcPredictSnapOffset(snapAnimationOptions.snapDelta, snapAnimationOptions.dragDistance,
1378         snapAnimationOptions.animationVelocity, snapAnimationOptions.snapDirection);
1379     if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value(), SPRING_ACCURACY)) {
1380         StartScrollSnapAnimation(predictSnapOffset.value(), snapAnimationOptions.animationVelocity, fromScrollBar);
1381         return true;
1382     }
1383     return false;
1384 }
1385 
StartScrollSnapAnimation(float scrollSnapDelta,float scrollSnapVelocity,bool fromScrollBar)1386 void ScrollPattern::StartScrollSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity, bool fromScrollBar)
1387 {
1388     auto scrollableEvent = GetScrollableEvent();
1389     CHECK_NULL_VOID(scrollableEvent);
1390     auto scrollable = scrollableEvent->GetScrollable();
1391     CHECK_NULL_VOID(scrollable);
1392     if (scrollable->IsSnapAnimationRunning()) {
1393         scrollable->UpdateScrollSnapEndWithOffset(
1394             -(scrollSnapDelta + scrollable->GetCurrentPos() - scrollable->GetSnapFinalPosition()));
1395     } else {
1396         scrollable->StartScrollSnapAnimation(scrollSnapDelta, scrollSnapVelocity, fromScrollBar);
1397         if (!IsScrolling()) {
1398             FireOnScrollStart();
1399         }
1400     }
1401 }
1402 
GetScrollPagingStatusDumpInfo(std::unique_ptr<JsonValue> & json)1403 void ScrollPattern::GetScrollPagingStatusDumpInfo(std::unique_ptr<JsonValue>& json)
1404 {
1405     switch (enablePagingStatus_) {
1406         case ScrollPagingStatus::NONE: {
1407             json->Put("enablePaging", "ScrollPagingStatus::NONE");
1408             break;
1409         }
1410         case ScrollPagingStatus::INVALID: {
1411             json->Put("enablePaging", "ScrollPagingStatus::INVALID");
1412             break;
1413         }
1414         case ScrollPagingStatus::VALID: {
1415             json->Put("enablePaging", "ScrollPagingStatus::VALID");
1416             break;
1417         }
1418         default: {
1419             break;
1420         }
1421     }
1422 }
1423 
GetScrollSnapAlignDumpInfo(std::unique_ptr<JsonValue> & json)1424 void ScrollPattern::GetScrollSnapAlignDumpInfo(std::unique_ptr<JsonValue>& json)
1425 {
1426     switch (GetScrollSnapAlign()) {
1427         case ScrollSnapAlign::NONE: {
1428             json->Put("snapAlign", "ScrollSnapAlign::NONE");
1429             break;
1430         }
1431         case ScrollSnapAlign::START: {
1432             json->Put("snapAlign", "ScrollSnapAlign::START");
1433             break;
1434         }
1435         case ScrollSnapAlign::CENTER: {
1436             json->Put("snapAlign", "ScrollSnapAlign::CENTER");
1437             break;
1438         }
1439         case ScrollSnapAlign::END: {
1440             json->Put("snapAlign", "ScrollSnapAlign::END");
1441             break;
1442         }
1443         default: {
1444             break;
1445         }
1446     }
1447 }
1448 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)1449 void ScrollPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
1450 {
1451     auto host = GetHost();
1452     CHECK_NULL_VOID(host);
1453     auto hub = host->GetEventHub<ScrollEventHub>();
1454     CHECK_NULL_VOID(hub);
1455     ScrollablePattern::DumpAdvanceInfo(json);
1456     json->Put("currentOffset", std::to_string(currentOffset_).c_str());
1457 
1458     GetScrollSnapAlignDumpInfo(json);
1459     auto snapPaginationStr = std::string("snapPagination: ");
1460     json->Put("snapPagination", GetScrollSnapPagination().c_str());
1461     json->Put("enableSnapToStart", enableSnapToSide_.first ? "true" : "false");
1462     json->Put("enableSnapToEnd", enableSnapToSide_.second ? "true" : "false");
1463 
1464     GetScrollPagingStatusDumpInfo(json);
1465     std::string snapOffsetsStr = "";
1466     for (const auto& iter : snapPaginations_) {
1467         snapOffsetsStr = snapOffsetsStr.append(iter.ToString()).append(" ");
1468     }
1469     json->Put("snapOffsets", snapOffsetsStr.c_str());
1470     json->Put("initialOffset",
1471         initialOffset_.has_value() ? initialOffset_->GetMainOffset(GetAxis()).ToString().c_str() : "None");
1472     auto onScrollEdge = hub->GetScrollEdgeEvent();
1473     json->Put("hasOnScrollEdge", onScrollEdge ? "true" : "false");
1474 
1475     std::unique_ptr<JsonValue> children = JsonUtil::CreateArray(true);
1476     for (const auto& info : scrollLayoutInfos_) {
1477         std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
1478         info.ToJson(child);
1479         children->Put(child);
1480     }
1481     json->Put("scrollLayoutInfos", children);
1482     std::unique_ptr<JsonValue> infochildren = JsonUtil::CreateArray(true);
1483     for (const auto& info : scrollMeasureInfos_) {
1484         std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
1485         info.ToJson(child);
1486         infochildren->Put(child);
1487     }
1488     json->Put("scrollMeasureInfos", infochildren);
1489 }
1490 
GetChildrenExpandedSize()1491 SizeF ScrollPattern::GetChildrenExpandedSize()
1492 {
1493     auto axis = GetAxis();
1494     if (axis == Axis::VERTICAL) {
1495         return SizeF(viewPort_.Width(), viewPortExtent_.Height());
1496     } else if (axis == Axis::HORIZONTAL) {
1497         return SizeF(viewPortExtent_.Width(), viewPort_.Height());
1498     }
1499     return SizeF();
1500 }
1501 } // namespace OHOS::Ace::NG
1502