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