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