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