1 /*
2 * Copyright (c) 2023-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/scrollable/scrollable_pattern.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/geometry/point.h"
20 #include "base/log/dump_log.h"
21 #include "base/perfmonitor/perf_constants.h"
22 #include "base/perfmonitor/perf_monitor.h"
23 #include "base/ressched/ressched_report.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/components_ng/base/inspector_filter.h"
27 #include "core/components_ng/base/observer_handler.h"
28 #include "core/components_ng/manager/select_overlay/select_overlay_scroll_notifier.h"
29 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
30 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
31 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
32 #include "core/components_ng/pattern/scrollable/scrollable.h"
33 #include "core/components_ng/pattern/scrollable/scrollable_event_hub.h"
34 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
35 #include "core/pipeline/pipeline_base.h"
36 #include "core/pipeline_ng/pipeline_context.h"
37 #include "core/components_ng/pattern/swiper/swiper_pattern.h"
38
39 namespace OHOS::Ace::NG {
40 namespace {
41 constexpr Color SELECT_FILL_COLOR = Color(0x1A000000);
42 constexpr Color SELECT_STROKE_COLOR = Color(0x33FFFFFF);
43 constexpr float CUSTOM_ANIMATION_DURATION = 1000.0;
44 constexpr uint32_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
45 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
46 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; //max 100ms
47 constexpr uint32_t DEFAULT_VSYNC_DIFF_TIME = 16 * 1000 * 1000; // default is 16 ms
48 constexpr uint32_t EVENTS_FIRED_INFO_COUNT = 50;
49 constexpr uint32_t SCROLLABLE_FRAME_INFO_COUNT = 50;
50 const std::string SCROLLABLE_DRAG_SCENE = "scrollable_drag_scene";
51 const std::string SCROLL_BAR_DRAG_SCENE = "scrollBar_drag_scene";
52 const std::string SCROLLABLE_MOTION_SCENE = "scrollable_motion_scene";
53 const std::string SCROLLABLE_MULTI_TASK_SCENE = "scrollable_multi_task_scene";
54 const std::string SCROLL_IN_HOTZONE_SCENE = "scroll_in_hotzone_scene";
55 const std::string CUSTOM_SCROLL_BAR_SCENE = "custom_scroll_bar_scene";
56 } // namespace
57 using std::chrono::high_resolution_clock;
58 using std::chrono::milliseconds;
59
ScrollablePattern()60 ScrollablePattern::ScrollablePattern()
61 {
62 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
63 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
64 }
65
ScrollablePattern(EdgeEffect edgeEffect,bool alwaysEnabled)66 ScrollablePattern::ScrollablePattern(EdgeEffect edgeEffect, bool alwaysEnabled)
67 : edgeEffect_(edgeEffect), edgeEffectAlwaysEnabled_(alwaysEnabled)
68 {
69 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
70 friction_ = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction_;
71 }
72
CreatePaintProperty()73 RefPtr<PaintProperty> ScrollablePattern::CreatePaintProperty()
74 {
75 auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
76 auto property = MakeRefPtr<ScrollablePaintProperty>();
77 property->UpdateScrollBarMode(defaultDisplayMode);
78 return property;
79 }
80
CreateAnalyzerOverlay(const RefPtr<FrameNode> node)81 void ScrollablePattern::CreateAnalyzerOverlay(const RefPtr<FrameNode> node)
82 {
83 auto builderFunc = []() -> RefPtr<UINode> {
84 auto uiNode = FrameNode::GetOrCreateFrameNode(V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
85 []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
86 return uiNode;
87 };
88 auto overlayNode = AceType::DynamicCast<FrameNode>(builderFunc());
89 CHECK_NULL_VOID(overlayNode);
90 node->SetOverlayNode(overlayNode);
91 overlayNode->SetParent(AceType::WeakClaim(AceType::RawPtr(node)));
92 overlayNode->SetActive(true);
93 overlayNode->SetHitTestMode(HitTestMode::HTMTRANSPARENT);
94 auto overlayProperty = AceType::DynamicCast<LayoutProperty>(overlayNode->GetLayoutProperty());
95 CHECK_NULL_VOID(overlayProperty);
96 overlayProperty->SetIsOverlayNode(true);
97 overlayProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
98 overlayProperty->UpdateAlignment(Alignment::TOP_LEFT);
99 auto overlayOffsetX = std::make_optional<Dimension>(Dimension::FromString("0px"));
100 auto overlayOffsetY = std::make_optional<Dimension>(Dimension::FromString("0px"));
101 overlayProperty->SetOverlayOffset(overlayOffsetX, overlayOffsetY);
102 auto focusHub = overlayNode->GetOrCreateFocusHub();
103 CHECK_NULL_VOID(focusHub);
104 focusHub->SetFocusable(false);
105 overlayNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
106 }
107
UpdateFadingEdge(const RefPtr<ScrollablePaintMethod> & paint)108 void ScrollablePattern::UpdateFadingEdge(const RefPtr<ScrollablePaintMethod>& paint)
109 {
110 if (NearZero(GetMainContentSize())) {
111 return;
112 }
113 auto host = GetHost();
114 CHECK_NULL_VOID(host);
115 auto overlayNode = host->GetOverlayNode();
116 CHECK_NULL_VOID(overlayNode);
117 auto geometryNode = host->GetGeometryNode();
118 CHECK_NULL_VOID(geometryNode);
119 auto frameSize = geometryNode->GetFrameRect();
120 auto overlayRenderContext = overlayNode->GetRenderContext();
121 CHECK_NULL_VOID(overlayRenderContext);
122 auto fadeFrameSize = GetAxis() == Axis::HORIZONTAL ? frameSize.Width() : frameSize.Height();
123 if (fadeFrameSize == 0) {
124 return;
125 }
126 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
127 CHECK_NULL_VOID(paintProperty);
128 bool hasFadingEdge = paintProperty->GetFadingEdge().value_or(false);
129 if (!hasFadingEdge) {
130 paint->SetOverlayRenderContext(overlayRenderContext);
131 paint->SetFadingInfo(false, false, prevHasFadingEdge_);
132 prevHasFadingEdge_ = hasFadingEdge;
133 return;
134 }
135 prevHasFadingEdge_ = hasFadingEdge;
136 auto isFadingTop = !IsAtTop();
137 auto isFadingBottom = IsFadingBottom();
138 float paddingBeforeContent = 0.0f;
139 float paddingAfterContent = 0.0f;
140 auto& padding = geometryNode->GetPadding();
141 if (padding) {
142 paddingBeforeContent = GetAxis() == Axis::HORIZONTAL ? *padding->left : *padding->top;
143 paddingAfterContent = GetAxis() == Axis::HORIZONTAL ? *padding->right : *padding->bottom;
144 }
145 startPercent_ = (paddingBeforeContent) / fadeFrameSize;
146 endPercent_ = (fadeFrameSize - paddingAfterContent) / fadeFrameSize;
147 paint->SetOverlayRenderContext(overlayRenderContext);
148 UpdateFadeInfo(isFadingTop, isFadingBottom, fadeFrameSize, paint);
149 }
150
UpdateFadeInfo(bool isFadingTop,bool isFadingBottom,float fadeFrameSize,const RefPtr<ScrollablePaintMethod> & paint)151 void ScrollablePattern::UpdateFadeInfo(
152 bool isFadingTop, bool isFadingBottom, float fadeFrameSize, const RefPtr<ScrollablePaintMethod>& paint)
153 {
154 if (fadeFrameSize == 0) {
155 return;
156 }
157 float percentFading = 0.0f;
158 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
159 CHECK_NULL_VOID(paintProperty);
160 auto fadingEdgeLength = paintProperty->GetFadingEdgeLength().value();
161 if (fadingEdgeLength.Unit() == DimensionUnit::PERCENT) {
162 percentFading = fadingEdgeLength.Value() / 100.0f; // One hundred percent
163 } else {
164 percentFading = fadingEdgeLength.ConvertToPx() / fadeFrameSize;
165 }
166 paint->SetFadingInfo(isFadingTop, isFadingBottom, true, (percentFading > 0.5f ? 0.5f : percentFading),
167 startPercent_, endPercent_); // 0.5: Half
168 }
169
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const170 void ScrollablePattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
171 {
172 /* no fixed attr below, just return */
173 if (filter.IsFastFilter()) {
174 return;
175 }
176 json->PutExtAttr("friction", GetFriction(), filter);
177 if (edgeEffect_ == EdgeEffect::SPRING) {
178 json->PutExtAttr("edgeEffect", "EdgeEffect.Spring", filter);
179 } else if (edgeEffect_ == EdgeEffect::FADE) {
180 json->PutExtAttr("edgeEffect", "EdgeEffect.Fade", filter);
181 } else {
182 json->PutExtAttr("edgeEffect", "EdgeEffect.None", filter);
183 }
184 json->PutExtAttr("flingSpeedLimit",
185 Dimension(maxFlingVelocity_, DimensionUnit::VP).ToString().c_str(), filter);
186 auto JsonEdgeEffectOptions = JsonUtil::Create(true);
187 JsonEdgeEffectOptions->Put("alwaysEnabled", GetAlwaysEnabled());
188 json->PutExtAttr("edgeEffectOptions", JsonEdgeEffectOptions, filter);
189
190 auto nestedScrollOptions = JsonUtil::Create(true);
191 auto nestedScroll = GetNestedScroll();
192 nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
193 nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
194 json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
195 }
196
SetAxis(Axis axis)197 void ScrollablePattern::SetAxis(Axis axis)
198 {
199 if (axis_ == axis) {
200 return;
201 }
202 axis_ = axis;
203 SetParentScrollable();
204 if (scrollBar_) {
205 auto positionMode = GetPositionMode();
206 scrollBar_->SetPositionMode(positionMode);
207 if (scrollBarOverlayModifier_) {
208 scrollBarOverlayModifier_->SetPositionMode(positionMode);
209 }
210 }
211 auto gestureHub = GetGestureHub();
212 CHECK_NULL_VOID(gestureHub);
213 if (scrollableEvent_) {
214 gestureHub->RemoveScrollableEvent(scrollableEvent_);
215 scrollableEvent_->SetAxis(axis);
216 gestureHub->AddScrollableEvent(scrollableEvent_);
217 }
218 if (scrollEffect_) {
219 gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
220 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
221 }
222 }
223
GetGestureHub()224 RefPtr<GestureEventHub> ScrollablePattern::GetGestureHub()
225 {
226 auto host = GetHost();
227 CHECK_NULL_RETURN(host, nullptr);
228 auto hub = host->GetEventHub<EventHub>();
229 CHECK_NULL_RETURN(hub, nullptr);
230 return hub->GetOrCreateGestureEventHub();
231 }
232
GetInputHub()233 RefPtr<InputEventHub> ScrollablePattern::GetInputHub()
234 {
235 auto host = GetHost();
236 CHECK_NULL_RETURN(host, nullptr);
237 auto hub = host->GetEventHub<EventHub>();
238 CHECK_NULL_RETURN(host, nullptr);
239 return hub->GetOrCreateInputEventHub();
240 }
241
OnScrollCallback(float offset,int32_t source)242 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
243 {
244 if (source == SCROLL_FROM_START) {
245 FireOnScrollStart();
246 return true;
247 }
248 SuggestOpIncGroup(true);
249 return UpdateCurrentOffset(offset, source);
250 }
251
ProcessNavBarReactOnStart()252 void ScrollablePattern::ProcessNavBarReactOnStart()
253 {
254 CHECK_NULL_VOID(navBarPattern_);
255 navBarPattern_->OnCoordScrollStart();
256 }
257
ProcessNavBarReactOnUpdate(float offset)258 float ScrollablePattern::ProcessNavBarReactOnUpdate(float offset)
259 {
260 CHECK_NULL_RETURN(navBarPattern_, false);
261 return navBarPattern_->OnCoordScrollUpdate(offset);
262 }
263
ProcessNavBarReactOnEnd()264 void ScrollablePattern::ProcessNavBarReactOnEnd()
265 {
266 CHECK_NULL_VOID(navBarPattern_);
267 navBarPattern_->OnCoordScrollEnd();
268 }
269
OnScrollPosition(double & offset,int32_t source)270 bool ScrollablePattern::OnScrollPosition(double& offset, int32_t source)
271 {
272 auto isSearchRefresh = GetIsSearchRefresh();
273 if (needLinked_) {
274 bool isAtTop = IsAtTop();
275 auto isAtTopAndPositive = (isAtTop && Positive(offset));
276 auto refreshCoordinateMode = RefreshCoordinationMode::UNKNOWN;
277 auto modalSheetCoordinationMode = ModalSheetCoordinationMode::UNKNOWN;
278 if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
279 modalSheetCoordinationMode = CoordinateWithSheet(offset, source, isAtTopAndPositive);
280 }
281 if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE) ||
282 !isSearchRefresh) {
283 refreshCoordinateMode = CoordinateWithRefresh(offset, source, isAtTopAndPositive);
284 }
285 auto navigationInCoordination = CoordinateWithNavigation(offset, source, isAtTop);
286 if ((refreshCoordinateMode == RefreshCoordinationMode::REFRESH_SCROLL) || navigationInCoordination ||
287 (modalSheetCoordinationMode == ModalSheetCoordinationMode::SHEET_SCROLL)) {
288 return false;
289 }
290 }
291
292 if (source == SCROLL_FROM_START) {
293 SetParentScrollable();
294 StopScrollBarAnimatorByProxy();
295 AbortScrollAnimator();
296 } else if (!AnimateStoped()) {
297 return false;
298 }
299 return true;
300 }
301
302 namespace {
FromDrag(int32_t source)303 inline bool FromDrag(int32_t source)
304 {
305 return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS;
306 }
307 } // namespace
308
NeedSplitScroll(OverScrollOffset & overOffsets,int32_t source)309 bool ScrollablePattern::NeedSplitScroll(OverScrollOffset& overOffsets, int32_t source)
310 {
311 return GreatNotEqual(overOffsets.start, 0.0) && refreshCoordination_ && refreshCoordination_->InCoordination() &&
312 !isRefreshInReactive_ &&
313 (FromDrag(source) || source == SCROLL_FROM_ANIMATION_SPRING ||
314 source == SCROLL_FROM_ANIMATION) &&
315 axis_ == Axis::VERTICAL;
316 }
317
CoordinateWithRefresh(double & offset,int32_t source,bool isAtTop)318 RefreshCoordinationMode ScrollablePattern::CoordinateWithRefresh(double& offset, int32_t source, bool isAtTop)
319 {
320 // use first scroll update to trigger scrollStart. Ignore SCROLL_FROM_START.
321 if (source == SCROLL_FROM_START) {
322 return RefreshCoordinationMode::UNKNOWN;
323 }
324 if (!refreshCoordination_) {
325 CreateRefreshCoordination();
326 }
327 auto overOffsets = GetOverScrollOffset(offset);
328 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !IsAtTop() && Positive(offset) &&
329 NeedSplitScroll(overOffsets, source)) {
330 offset = offset - overOffsets.start;
331 OnScrollCallback(offset, source);
332 isRefreshInReactive_ = true;
333 refreshCoordination_->OnScrollStart(FromDrag(source), GetVelocity());
334 }
335 bool hasScrollSpace = Positive(offset) || (Negative(offset) && refreshCoordination_->IsRefreshInScroll());
336 if (IsAtTop() && hasScrollSpace &&
337 (FromDrag(source) || source == SCROLL_FROM_ANIMATION) &&
338 !isRefreshInReactive_ && (axis_ == Axis::VERTICAL)) {
339 isRefreshInReactive_ = true;
340 refreshCoordination_->OnScrollStart(FromDrag(source), GetVelocity());
341 }
342 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
343 refreshCoordination_->InCoordination() && source != SCROLL_FROM_UPDATE &&
344 source != SCROLL_FROM_AXIS && isRefreshInReactive_) {
345 isRefreshInReactive_ = false;
346 refreshCoordination_->OnScrollEnd(GetVelocity());
347 }
348 auto mode = RefreshCoordinationMode::UNKNOWN;
349 if (refreshCoordination_->InCoordination() && isRefreshInReactive_) {
350 if (!refreshCoordination_->OnScroll(
351 GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset, GetVelocity())) {
352 isRefreshInReactive_ = false;
353 }
354 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
355 mode = RefreshCoordinationMode::REFRESH_SCROLL;
356 } else {
357 if (scrollEffect_ && scrollEffect_->IsSpringEffect()) {
358 mode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
359 } else {
360 mode = RefreshCoordinationMode::REFRESH_SCROLL;
361 }
362 }
363 }
364 return mode;
365 }
366
CoordinateWithSheet(double & offset,int32_t source,bool isAtTop)367 ModalSheetCoordinationMode ScrollablePattern::CoordinateWithSheet(double& offset, int32_t source, bool isAtTop)
368 {
369 auto coordinationMode = ModalSheetCoordinationMode::UNKNOWN;
370 if (source == SCROLL_FROM_START) {
371 isSheetInReactive_ = false;
372
373 if (!sheetPattern_) {
374 GetParentModalSheet();
375 }
376 }
377 auto overOffsets = GetOverScrollOffset(offset);
378 if (IsAtTop() && (source == SCROLL_FROM_UPDATE) && !isSheetInReactive_ && (axis_ == Axis::VERTICAL)) {
379 isSheetInReactive_ = true;
380 if (sheetPattern_) {
381 sheetPattern_->OnCoordScrollStart();
382 }
383 }
384 if (sheetPattern_ && isSheetInReactive_) {
385 if (!sheetPattern_->OnCoordScrollUpdate(GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset)) {
386 isSheetInReactive_ = false;
387 coordinationMode = ModalSheetCoordinationMode::SCROLLABLE_SCROLL;
388 } else {
389 coordinationMode = ModalSheetCoordinationMode::SHEET_SCROLL;
390 }
391 }
392 return coordinationMode;
393 }
394
CoordinateWithNavigation(double & offset,int32_t source,bool isAtTop)395 bool ScrollablePattern::CoordinateWithNavigation(double& offset, int32_t source, bool isAtTop)
396 {
397 if (source == SCROLL_FROM_START) {
398 GetParentNavigation();
399 CHECK_NULL_RETURN(navBarPattern_, false);
400 if (isAtTop && Positive(offset)) {
401 // Starting coordinating scroll at the beginning of scrolling.
402 isReactInParentMovement_ = true;
403 ProcessNavBarReactOnStart();
404 }
405 return false;
406 }
407
408 CHECK_NULL_RETURN(navBarPattern_ && navBarPattern_->NeedCoordWithScroll(), false);
409
410 float diff = navBarPattern_->GetTitleBarHeightLessThanMaxBarHeight();
411 auto overOffsets = GetOverScrollOffset(offset + std::max(diff, 0.0f));
412 overOffsets.start = Positive(offset) ? std::min(offset, overOffsets.start) : overOffsets.start;
413 float offsetRemain = 0.0f;
414 float offsetCoordinate = offset;
415
416 if (!isReactInParentMovement_ && NeedCoordinateScrollWithNavigation(offset, source, overOffsets)) {
417 // Starting coordinating scroll during sliding or flipping.
418 isReactInParentMovement_ = true;
419 ProcessNavBarReactOnStart();
420 }
421
422 if (isReactInParentMovement_) {
423 if (Positive(offset)) {
424 offsetRemain = offset - overOffsets.start;
425 offsetCoordinate = overOffsets.start;
426 }
427 float handledByNav = ProcessNavBarReactOnUpdate(offsetCoordinate);
428 if (NearEqual(handledByNav, offsetCoordinate) && !NearZero(offset)) {
429 // All offsets are handled by Navigation, list cannot scroll over.
430 SetCanOverScroll(false);
431 offset = offsetRemain;
432 } else {
433 offset = offsetRemain + (offsetCoordinate - handledByNav);
434 }
435 if (Negative(diff) && Negative(offset)) {
436 offset = overOffsets.start;
437 }
438
439 if (Negative(offset) && (source == SCROLL_FROM_ANIMATION_SPRING || !navBarPattern_->CanCoordScrollUp(offset))) {
440 // When rebounding form scrolling over, trigger the ProcessNavBarReactOnEnd callback.
441 isReactInParentMovement_ = false;
442 ProcessNavBarReactOnEnd();
443 }
444 }
445
446 return false;
447 }
448
SetUiDvsyncSwitch(bool on)449 void ScrollablePattern::SetUiDvsyncSwitch(bool on)
450 {
451 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
452 CHECK_NULL_VOID(context);
453 if (on && inScrollingStatus_) {
454 inScrollingStatus_ = false;
455 context->SetUiDvsyncSwitch(true);
456 switchOnStatus_ = true;
457 } else if (!on && switchOnStatus_) {
458 context->SetUiDvsyncSwitch(false);
459 switchOnStatus_ = false;
460 }
461 }
462
OnScrollEnd()463 void ScrollablePattern::OnScrollEnd()
464 {
465 // Previous: Sets ScrollablePattern::OnScrollEnd to Scrollable->scrollEndCallback_
466 // Scrollable calls scrollEndCallback_ in HandleOverScroll
467
468 // Now: HandleOverScroll moved to ScrollablePattern and renamed HandleScrollVelocity, directly
469 // calls OnScrollEnd in ScrollablePattern
470 if (refreshCoordination_) {
471 isRefreshInReactive_ = false;
472 refreshCoordination_->OnScrollEnd(GetVelocity());
473 }
474 if (isSheetInReactive_) {
475 isSheetInReactive_ = false;
476 if (sheetPattern_) {
477 sheetPattern_->OnCoordScrollEnd(GetVelocity());
478 }
479 }
480 if (isReactInParentMovement_) {
481 isReactInParentMovement_ = false;
482 ProcessNavBarReactOnEnd();
483 }
484 if (isAnimationStop_) {
485 SetUiDvsyncSwitch(false);
486 }
487 if (scrollStop_) {
488 scrollAbort_ = false;
489 }
490 OnScrollEndCallback();
491 SelectOverlayScrollNotifier::NotifyOnScrollEnd(WeakClaim(this));
492 }
493
AttachAnimatableProperty(RefPtr<Scrollable> scrollable)494 void ScrollablePattern::AttachAnimatableProperty(RefPtr<Scrollable> scrollable)
495 {
496 auto host = GetHost();
497 CHECK_NULL_VOID(host);
498 auto renderContext = host->GetRenderContext();
499 CHECK_NULL_VOID(renderContext);
500 auto property = scrollable->GetFrictionProperty();
501 renderContext->AttachNodeAnimatableProperty(property);
502
503 property = scrollable->GetSpringProperty();
504 renderContext->AttachNodeAnimatableProperty(property);
505 property = scrollable->GetSnapProperty();
506 renderContext->AttachNodeAnimatableProperty(property);
507 }
508
AddScrollEvent()509 void ScrollablePattern::AddScrollEvent()
510 {
511 auto host = GetHost();
512 CHECK_NULL_VOID(host);
513 auto gestureHub = GetGestureHub();
514 CHECK_NULL_VOID(gestureHub);
515 if (scrollableEvent_) {
516 gestureHub->RemoveScrollableEvent(scrollableEvent_);
517 }
518 auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
519 auto pattern = weak.Upgrade();
520 CHECK_NULL_RETURN(pattern, false);
521 return pattern->HandleScrollImpl(static_cast<float>(offset), source);
522 };
523 auto scrollable = MakeRefPtr<Scrollable>(std::move(scrollCallback), GetAxis());
524 scrollable->SetNodeId(host->GetAccessibilityId());
525 scrollable->SetNodeTag(host->GetTag());
526 scrollable->Initialize(host->GetContextRefPtr());
527 AttachAnimatableProperty(scrollable);
528
529 // move HandleScroll and HandleOverScroll to ScrollablePattern by setting callbacks to scrollable
530 auto handleScroll = [weak = AceType::WeakClaim(this)](
531 float offset, int32_t source, NestedState state) -> ScrollResult {
532 auto pattern = weak.Upgrade();
533 if (pattern) {
534 return pattern->HandleScroll(offset, source, state, pattern->GetVelocity());
535 }
536 return {};
537 };
538 scrollable->SetHandleScrollCallback(std::move(handleScroll));
539
540 scrollable->SetOverScrollCallback([weak = WeakClaim(this)](float velocity) {
541 auto pattern = weak.Upgrade();
542 CHECK_NULL_RETURN(pattern, false);
543 return pattern->HandleOverScroll(velocity);
544 });
545
546 scrollable->SetIsReverseCallback([weak = WeakClaim(this)]() {
547 auto pattern = weak.Upgrade();
548 CHECK_NULL_RETURN(pattern, false);
549 return pattern->IsReverse();
550 });
551
552 auto scrollStart = [weak = WeakClaim(this)](float position) {
553 auto pattern = weak.Upgrade();
554 CHECK_NULL_VOID(pattern);
555 pattern->FireAndCleanScrollingListener();
556 pattern->OnScrollStartRecursiveInner(weak, position, pattern->GetVelocity());
557 };
558 scrollable->SetOnScrollStartRec(std::move(scrollStart));
559
560 auto scrollEndRec = [weak = WeakClaim(this)](const std::optional<float>& velocity) {
561 auto pattern = weak.Upgrade();
562 CHECK_NULL_VOID(pattern);
563 pattern->OnScrollEndRecursiveInner(velocity);
564 };
565 scrollable->SetOnScrollEndRec(std::move(scrollEndRec));
566
567 auto scrollEnd = [weak = WeakClaim(this)]() {
568 auto pattern = weak.Upgrade();
569 CHECK_NULL_VOID(pattern);
570 pattern->OnScrollEnd();
571 };
572 scrollable->SetScrollEndCallback(std::move(scrollEnd));
573
574 auto RemainVelocityToChild = [weak = WeakClaim(this)](float remainVelocity) -> bool {
575 auto pattern = weak.Upgrade();
576 CHECK_NULL_RETURN(pattern, false);
577 auto child = pattern->GetScrollOriginChild();
578 if (child) {
579 child->RemainVelocityToChild(remainVelocity);
580 return true;
581 }
582 return false;
583 };
584 scrollable->SetRemainVelocityCallback(std::move(RemainVelocityToChild));
585
586 auto dragEnd = [weak = WeakClaim(this)]() {
587 auto pattern = weak.Upgrade();
588 CHECK_NULL_VOID(pattern);
589 pattern->OnScrollDragEndRecursive();
590 };
591 scrollable->SetDragEndCallback(std::move(dragEnd));
592
593 scrollable->SetUnstaticFriction(friction_);
594 scrollable->SetMaxFlingVelocity(maxFlingVelocity_);
595
596 auto scrollSnap = [weak = WeakClaim(this)](double targetOffset, double velocity) -> bool {
597 auto pattern = weak.Upgrade();
598 CHECK_NULL_RETURN(pattern, false);
599 return pattern->OnScrollSnapCallback(targetOffset, velocity);
600 };
601 scrollable->SetOnScrollSnapCallback(scrollSnap);
602
603 auto calePredictSnapOffsetCallback =
604 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
605 auto pattern = weak.Upgrade();
606 std::optional<float> predictSnapOffset;
607 CHECK_NULL_RETURN(pattern, predictSnapOffset);
608 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
609 };
610 scrollable->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
611
612 auto needScrollSnapToSideCallback = [weak = WeakClaim(this)](float delta) -> bool {
613 auto pattern = weak.Upgrade();
614 CHECK_NULL_RETURN(pattern, false);
615 return pattern->NeedScrollSnapToSide(delta);
616 };
617 scrollable->SetNeedScrollSnapToSideCallback(std::move(needScrollSnapToSideCallback));
618
619 auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
620 auto pattern = weak.Upgrade();
621 CHECK_NULL_VOID(pattern);
622 if (sceneStatus == NG::SceneStatus::START) {
623 pattern->inScrollingStatus_ = true;
624 pattern->SetUiDvsyncSwitch(false);
625 } else if (sceneStatus == NG::SceneStatus::END) {
626 pattern->SetUiDvsyncSwitch(true);
627 }
628 return pattern->NotifyFRCSceneInfo(SCROLLABLE_DRAG_SCENE, velocity, sceneStatus);
629 };
630 scrollable->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
631
632 scrollable->SetOnContinuousSliding([weak = WeakClaim(this)]() -> double {
633 auto pattern = weak.Upgrade();
634 CHECK_NULL_RETURN(pattern, 0.0);
635 return pattern->GetMainContentSize();
636 });
637
638 scrollable->AddPanActionEndEvent([weak = WeakClaim(this)](GestureEvent& info) {
639 auto pattern = weak.Upgrade();
640 CHECK_NULL_VOID(pattern);
641 pattern->FireObserverOnPanActionEnd(info);
642 });
643
644 scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
645 scrollableEvent_->SetScrollable(scrollable);
646 scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
647 auto pattern = weakScroll.Upgrade();
648 CHECK_NULL_RETURN(pattern, 0.0f);
649 float nestedVelocity = pattern->GetNestedScrollVelocity();
650 if (std::abs(nestedVelocity) > std::abs(pattern->GetCurrentVelocity())) {
651 return nestedVelocity;
652 }
653 return pattern->GetCurrentVelocity();
654 });
655 gestureHub->AddScrollableEvent(scrollableEvent_);
656 InitTouchEvent(gestureHub);
657 RegisterWindowStateChangedCallback();
658 if (!clickRecognizer_) {
659 InitScrollBarClickEvent();
660 }
661 }
662
StopScrollAnimation()663 void ScrollablePattern::StopScrollAnimation()
664 {
665 StopScrollable();
666 }
667
OnTouchDown(const TouchEventInfo & info)668 void ScrollablePattern::OnTouchDown(const TouchEventInfo& info)
669 {
670 if (GetNestedScrolling() && !NearZero(GetNestedScrollVelocity())) {
671 auto child = GetScrollOriginChild();
672 CHECK_NULL_VOID(child);
673 child->StopScrollAnimation();
674 }
675 }
676
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)677 void ScrollablePattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
678 {
679 // use TouchEvent to receive next touch down event to stop animation.
680 if (touchEvent_) {
681 return;
682 }
683 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
684 auto pattern = weak.Upgrade();
685 CHECK_NULL_VOID(pattern);
686 pattern->FireObserverOnTouch(info);
687 CHECK_NULL_VOID(pattern->scrollableEvent_);
688 auto scrollable = pattern->scrollableEvent_->GetScrollable();
689 CHECK_NULL_VOID(scrollable);
690 switch (info.GetTouches().front().GetTouchType()) {
691 case TouchType::DOWN:
692 scrollable->HandleTouchDown();
693 pattern->OnTouchDown(info);
694 break;
695 case TouchType::UP:
696 scrollable->HandleTouchUp();
697 break;
698 case TouchType::CANCEL:
699 scrollable->HandleTouchCancel();
700 break;
701 default:
702 break;
703 }
704 };
705 if (touchEvent_) {
706 gestureHub->RemoveTouchEvent(touchEvent_);
707 }
708 touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
709 gestureHub->AddTouchEvent(touchEvent_);
710 }
711
RegisterWindowStateChangedCallback()712 void ScrollablePattern::RegisterWindowStateChangedCallback()
713 {
714 auto host = GetHost();
715 CHECK_NULL_VOID(host);
716 auto context = NG::PipelineContext::GetCurrentContext();
717 CHECK_NULL_VOID(context);
718 context->AddWindowStateChangedCallback(host->GetId());
719 }
720
OnDetachFromFrameNode(FrameNode * frameNode)721 void ScrollablePattern::OnDetachFromFrameNode(FrameNode* frameNode)
722 {
723 auto context = NG::PipelineContext::GetCurrentContext();
724 CHECK_NULL_VOID(context);
725 context->RemoveWindowStateChangedCallback(frameNode->GetId());
726 }
727
OnWindowHide()728 void ScrollablePattern::OnWindowHide()
729 {
730 CHECK_NULL_VOID(scrollableEvent_);
731 auto scrollable = scrollableEvent_->GetScrollable();
732 CHECK_NULL_VOID(scrollable);
733 scrollable->StopFrictionAnimation();
734 }
735
SetEdgeEffect(EdgeEffect edgeEffect)736 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
737 {
738 auto gestureHub = GetGestureHub();
739 CHECK_NULL_VOID(gestureHub);
740 if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
741 gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
742 scrollEffect_.Reset();
743 }
744 if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
745 auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
746 CHECK_NULL_VOID(springEffect);
747 springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
748 auto pattern = weak.Upgrade();
749 CHECK_NULL_RETURN(pattern, false);
750 return pattern->OutBoundaryCallback();
751 });
752 // add callback to springEdgeEffect
753 SetEdgeEffectCallback(springEffect);
754 scrollEffect_ = springEffect;
755 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
756 }
757 if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
758 auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
759 CHECK_NULL_VOID(fadeEdgeEffect);
760 fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
761 auto pattern = weakScroll.Upgrade();
762 CHECK_NULL_VOID(pattern);
763 auto host = pattern->GetHost();
764 CHECK_NULL_VOID(host);
765 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
766 });
767 SetEdgeEffectCallback(fadeEdgeEffect);
768 fadeEdgeEffect->InitialEdgeEffect();
769 scrollEffect_ = fadeEdgeEffect;
770 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
771 }
772 CHECK_NULL_VOID(scrollableEvent_);
773 auto scrollable = scrollableEvent_->GetScrollable();
774 CHECK_NULL_VOID(scrollable);
775 scrollable->SetEdgeEffect(edgeEffect);
776 if (edgeEffect != EdgeEffect::SPRING) {
777 scrollable->StopSpringAnimation(true);
778 }
779 }
780
HandleFadeEffect(float offset,int32_t source,const SizeF & size,bool isNotPositiveScrollableDistance)781 void ScrollablePattern::HandleFadeEffect(float offset, int32_t source, const SizeF& size,
782 bool isNotPositiveScrollableDistance)
783 {
784 auto isScrollFromUpdate = source == SCROLL_FROM_UPDATE;
785 scrollEffect_->HandleOverScroll(GetAxis(), IsReverse() ? offset : -offset,
786 size, isScrollFromUpdate, isNotPositiveScrollableDistance);
787 }
788
HandleEdgeEffect(float offset,int32_t source,const SizeF & size)789 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size)
790 {
791 bool isAtTop = IsAtTop();
792 bool isAtBottom = IsAtBottom();
793 bool isNotPositiveScrollableDistance = isAtTop && isAtBottom;
794 // check edgeEffect is not springEffect
795 if (scrollEffect_ && scrollEffect_->IsFadeEffect() &&
796 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION)) { // handle edge effect
797 if ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset))) {
798 HandleFadeEffect(offset, source, size, isNotPositiveScrollableDistance);
799 }
800 }
801 if (!(scrollEffect_ && scrollEffect_->IsSpringEffect() &&
802 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION ||
803 source == SCROLL_FROM_ANIMATION_SPRING ||
804 (source == SCROLL_FROM_ANIMATION_CONTROLLER && animateCanOverScroll_)))) {
805 if (isAtTop && Positive(offset)) {
806 animateOverScroll_ = false;
807 return false;
808 }
809 if (isAtBottom && Negative(offset)) {
810 animateOverScroll_ = false;
811 return false;
812 }
813 }
814 animateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && (isAtTop || isAtBottom);
815 isAnimateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && animateCanOverScroll_ &&
816 ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset)));
817 return true;
818 }
819
RegisterScrollBarEventTask()820 void ScrollablePattern::RegisterScrollBarEventTask()
821 {
822 CHECK_NULL_VOID(scrollBar_);
823 auto host = GetHost();
824 CHECK_NULL_VOID(host);
825 scrollBar_->SetAxis(axis_);
826 scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
827 auto host = weak.Upgrade();
828 CHECK_NULL_VOID(host);
829 host->MarkNeedRenderOnly();
830 });
831 auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
832 auto pattern = weak.Upgrade();
833 CHECK_NULL_RETURN(pattern, false);
834 return pattern->OnScrollCallback(static_cast<float>(offset), source);
835 };
836 scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
837 auto scrollEnd = [weak = WeakClaim(this)]() {
838 auto pattern = weak.Upgrade();
839 CHECK_NULL_VOID(pattern);
840 pattern->OnScrollEnd();
841 };
842 scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
843 auto calePredictSnapOffsetCallback =
844 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
845 auto pattern = weak.Upgrade();
846 CHECK_NULL_RETURN(pattern, std::optional<float>());
847 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
848 };
849 scrollBar_->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
850 auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
851 auto pattern = weak.Upgrade();
852 CHECK_NULL_VOID(pattern);
853 pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
854 };
855 scrollBar_->SetStartScrollSnapMotionCallback(std::move(startScrollSnapMotionCallback));
856 auto scrollPageCallback = [weak = WeakClaim(this)](bool reverse, bool smooth) {
857 auto pattern = weak.Upgrade();
858 CHECK_NULL_VOID(pattern);
859 pattern->ScrollPage(reverse, smooth);
860 };
861 scrollBar_->SetScrollPageCallback(std::move(scrollPageCallback));
862 auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
863 auto pattern = weak.Upgrade();
864 CHECK_NULL_VOID(pattern);
865 return pattern->NotifyFRCSceneInfo(SCROLL_BAR_DRAG_SCENE, velocity, sceneStatus);
866 };
867 scrollBar_->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
868 InitScrollBarGestureEvent();
869 InitScrollBarMouseEvent();
870 }
871
InitScrollBarGestureEvent()872 void ScrollablePattern::InitScrollBarGestureEvent()
873 {
874 auto gestureHub = GetGestureHub();
875 CHECK_NULL_VOID(gestureHub);
876 auto inputHub = GetInputHub();
877 CHECK_NULL_VOID(inputHub);
878 scrollBar_->SetGestureEvent();
879 scrollBar_->SetMouseEvent();
880 scrollBar_->SetHoverEvent();
881 gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
882 inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
883 inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
884 CHECK_NULL_VOID(scrollableEvent_);
885 scrollableEvent_->SetInBarRegionCallback(
886 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
887 auto scrollBar = weak.Upgrade();
888 CHECK_NULL_RETURN(scrollBar, false);
889 if (source == SourceType::MOUSE) {
890 return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
891 }
892 return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
893 });
894 scrollableEvent_->SetBarCollectTouchTargetCallback(
895 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const OffsetF& coordinateOffset,
896 const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
897 const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult) {
898 auto scrollBar = weak.Upgrade();
899 CHECK_NULL_VOID(scrollBar);
900 scrollBar->OnCollectTouchTarget(
901 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
902 });
903 scrollableEvent_->SetBarCollectClickAndLongPressTargetCallback(
904 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_)), this](const OffsetF& coordinateOffset,
905 const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
906 const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult) {
907 auto scrollBar = weak.Upgrade();
908 CHECK_NULL_VOID(scrollBar);
909 scrollBar->OnCollectLongPressTarget(
910 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
911 OnCollectClickTarget(
912 coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent, responseLinkResult);
913 });
914 scrollableEvent_->SetInBarRectRegionCallback(
915 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
916 auto scrollBar = weak.Upgrade();
917 CHECK_NULL_RETURN(scrollBar, false);
918 return scrollBar->InBarRectRegion(Point(point.GetX(), point.GetY()));
919 });
920 }
921
SetScrollBar(DisplayMode displayMode)922 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
923 {
924 auto host = GetHost();
925 CHECK_NULL_VOID(host);
926 if (displayMode == DisplayMode::OFF) {
927 if (scrollBar_) {
928 auto gestureHub = GetGestureHub();
929 if (gestureHub) {
930 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
931 }
932 scrollBar_.Reset();
933 if (scrollBarOverlayModifier_) {
934 scrollBarOverlayModifier_->SetOpacity(0);
935 }
936 }
937 return;
938 }
939 DisplayMode oldDisplayMode = DisplayMode::OFF;
940 if (!scrollBar_) {
941 scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
942 RegisterScrollBarEventTask();
943 } else {
944 oldDisplayMode = scrollBar_->GetDisplayMode();
945 }
946 // set the scroll bar style
947 auto positionMode = GetPositionMode();
948 scrollBar_->SetPositionMode(positionMode);
949 if (scrollBarOverlayModifier_) {
950 scrollBarOverlayModifier_->SetPositionMode(positionMode);
951 }
952
953 if (oldDisplayMode != displayMode) {
954 scrollBar_->SetDisplayMode(displayMode);
955 if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
956 scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
957 }
958 scrollBar_->ScheduleDisappearDelayTask();
959 if (isInitialized_ && !host->CheckNeedForceMeasureAndLayout()) {
960 UpdateScrollBarOffset();
961 }
962 }
963 UpdateBorderRadius();
964 }
965
UpdateBorderRadius()966 void ScrollablePattern::UpdateBorderRadius()
967 {
968 auto host = GetHost();
969 CHECK_NULL_VOID(host);
970 auto renderContext = host->GetRenderContext();
971 CHECK_NULL_VOID(renderContext);
972 if (renderContext->HasBorderRadius()) {
973 auto borderRadius = renderContext->GetBorderRadius().value();
974 if (!(borderRadius == scrollBar_->GetHostBorderRadius())) {
975 scrollBar_->SetHostBorderRadius(borderRadius);
976 scrollBar_->CalcReservedHeight();
977 }
978 }
979 }
980
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)981 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
982 {
983 if (!property) {
984 SetScrollBar(DisplayMode::AUTO);
985 return;
986 }
987 auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
988 SetScrollBar(displayMode);
989 if (scrollBar_) {
990 auto barWidth = property->GetScrollBarWidth();
991 if (barWidth) {
992 scrollBar_->SetInactiveWidth(barWidth.value());
993 scrollBar_->SetNormalWidth(barWidth.value());
994 scrollBar_->SetActiveWidth(barWidth.value());
995 scrollBar_->SetTouchWidth(barWidth.value());
996 scrollBar_->SetIsUserNormalWidth(true);
997 } else {
998 scrollBar_->SetIsUserNormalWidth(false);
999 }
1000 auto barColor = property->GetScrollBarColor();
1001 if (barColor) {
1002 scrollBar_->SetForegroundColor(barColor.value());
1003 } else {
1004 auto pipelineContext = GetContext();
1005 CHECK_NULL_VOID(pipelineContext);
1006 auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
1007 CHECK_NULL_VOID(theme);
1008 scrollBar_->SetForegroundColor(theme->GetForegroundColor());
1009 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
1010 }
1011 }
1012 }
1013
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)1014 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
1015 {
1016 // inner scrollbar, viewOffset is padding offset
1017 if (scrollBar_) {
1018 auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
1019 bool scrollable = GreatNotEqual(estimatedHeight, mainSize) && IsScrollable();
1020 if (scrollBar_->IsScrollable() != scrollable) {
1021 scrollBar_->SetScrollable(scrollable);
1022 if (scrollBarOverlayModifier_) {
1023 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
1024 scrollBarOverlayModifier_->SetScrollable(scrollable);
1025 }
1026 if (scrollable) {
1027 scrollBar_->ScheduleDisappearDelayTask();
1028 }
1029 }
1030 Offset scrollOffset = { offset, offset }; // fit for w/h switched.
1031 UpdateBorderRadius();
1032 scrollBar_->SetReverse(IsReverse());
1033 scrollBar_->SetIsOutOfBoundary(IsOutOfBoundary());
1034 scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
1035 scrollBar_->MarkNeedRender();
1036 }
1037
1038 // outer scrollbar
1039 if (scrollBarProxy_) {
1040 auto height = (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
1041 auto estimatedHeightItem = estimatedHeight - height;
1042 estimatedHeight_ = (estimatedHeightItem < 0 ? 0 : estimatedHeightItem);
1043 barOffset_ = -offset;
1044 scrollBarProxy_->NotifyScrollBar();
1045 }
1046
1047 for (auto nestbar : nestScrollBarProxy_) {
1048 auto scrollBarProxy = nestbar.Upgrade();
1049 if (!scrollBarProxy) {
1050 continue;
1051 }
1052 scrollBarProxy->NotifyScrollBar();
1053 }
1054 }
1055
ScrollEndCallback(bool nestedScroll,float velocity)1056 void ScrollablePattern::ScrollEndCallback(bool nestedScroll, float velocity)
1057 {
1058 if (nestedScroll) {
1059 OnScrollEndRecursiveInner(velocity);
1060 } else {
1061 OnScrollEnd();
1062 }
1063 }
1064
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)1065 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
1066 {
1067 CHECK_NULL_VOID(scrollBarProxy);
1068 auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source, bool nestedScroll) {
1069 if (source != SCROLL_FROM_START) {
1070 auto pattern = weak.Upgrade();
1071 if (!pattern || pattern->GetAxis() == Axis::NONE) {
1072 return false;
1073 }
1074 if (!nestedScroll) {
1075 return pattern->UpdateCurrentOffset(offset, source);
1076 }
1077 pattern->HandleScroll(offset, source, NestedState::GESTURE, 0.0f);
1078 }
1079 return true;
1080 };
1081 auto scrollStartCallback = [weak = WeakClaim(this)](double offset, int32_t source, bool nestedScroll) {
1082 auto pattern = weak.Upgrade();
1083 CHECK_NULL_RETURN(pattern, false);
1084 // no source == SCROLL_FROM_START for ScrollBar
1085 if (nestedScroll) {
1086 pattern->OnScrollStartRecursiveInner(weak, offset, pattern->GetVelocity());
1087 } else {
1088 pattern->OnScrollStartCallback();
1089 }
1090 return pattern->OnScrollCallback(static_cast<float>(offset), source);
1091 };
1092 auto scrollEndCallback = [weak = WeakClaim(this)](bool nestedScroll) {
1093 auto pattern = weak.Upgrade();
1094 CHECK_NULL_VOID(pattern);
1095 pattern->ScrollEndCallback(nestedScroll, pattern->GetVelocity());
1096 };
1097 auto calePredictSnapOffsetCallback =
1098 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
1099 auto pattern = weak.Upgrade();
1100 CHECK_NULL_RETURN(pattern, std::optional<float>());
1101 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
1102 };
1103 auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
1104 auto pattern = weak.Upgrade();
1105 CHECK_NULL_VOID(pattern);
1106 pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
1107 };
1108
1109 auto scrollbarFRcallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
1110 auto pattern = weak.Upgrade();
1111 CHECK_NULL_VOID(pattern);
1112 return pattern->NotifyFRCSceneInfo(CUSTOM_SCROLL_BAR_SCENE, velocity, sceneStatus);
1113 };
1114 auto scrollPageCallback = [weak = WeakClaim(this)](bool reverse, bool smooth) {
1115 auto pattern = weak.Upgrade();
1116 CHECK_NULL_VOID(pattern);
1117 return pattern->ScrollPage(reverse, smooth);
1118 };
1119 ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction), std::move(scrollStartCallback),
1120 std::move(scrollEndCallback), std::move(calePredictSnapOffsetCallback),
1121 std::move(startScrollSnapMotionCallback), std::move(scrollbarFRcallback),
1122 std::move(scrollPageCallback) };
1123 scrollBarProxy->RegisterScrollableNode(nodeInfo);
1124 scrollBarProxy_ = scrollBarProxy;
1125 }
1126
CreateScrollBarOverlayModifier()1127 void ScrollablePattern::CreateScrollBarOverlayModifier()
1128 {
1129 CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedPaint());
1130 CHECK_NULL_VOID(!scrollBarOverlayModifier_);
1131 scrollBarOverlayModifier_ = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
1132 scrollBarOverlayModifier_->SetRect(scrollBar_->GetActiveRect());
1133 scrollBarOverlayModifier_->SetPositionMode(scrollBar_->GetPositionMode());
1134 }
1135
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)1136 void ScrollablePattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
1137 {
1138 scrollBarOutBoundaryExtent_ = scrollBarOutBoundaryExtent;
1139 CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
1140 scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
1141 }
1142
SetFriction(double friction)1143 void ScrollablePattern::SetFriction(double friction)
1144 {
1145 if (LessOrEqual(friction, 0.0)) {
1146 friction =
1147 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN) ? API11_FRICTION : FRICTION;
1148 friction =
1149 Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) ? API12_FRICTION : friction;
1150 }
1151 friction_ = friction;
1152 CHECK_NULL_VOID(scrollableEvent_);
1153 auto scrollable = scrollableEvent_->GetScrollable();
1154 scrollable->SetUnstaticFriction(friction_);
1155 }
1156
SetMaxFlingVelocity(double max)1157 void ScrollablePattern::SetMaxFlingVelocity(double max)
1158 {
1159 if (LessOrEqual(max, 0.0f)) {
1160 max = MAX_VELOCITY;
1161 }
1162 maxFlingVelocity_ = max;
1163 CHECK_NULL_VOID(scrollableEvent_);
1164 auto scrollable = scrollableEvent_->GetScrollable();
1165 scrollable->SetMaxFlingVelocity(max);
1166 }
1167
GetParentNavigation()1168 void ScrollablePattern::GetParentNavigation()
1169 {
1170 if (navBarPattern_) {
1171 return;
1172 }
1173 auto host = GetHost();
1174 CHECK_NULL_VOID(host);
1175 if ((host->GetTag() != V2::LIST_ETS_TAG) && (host->GetTag() != V2::GRID_ETS_TAG) &&
1176 (host->GetTag() != V2::SCROLL_ETS_TAG)) {
1177 return;
1178 }
1179 for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
1180 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
1181 if (!frameNode) {
1182 continue;
1183 }
1184 if ((frameNode->GetTag() == V2::LIST_ETS_TAG) || (frameNode->GetTag() == V2::GRID_ETS_TAG) ||
1185 (frameNode->GetTag() == V2::SCROLL_ETS_TAG)) {
1186 break;
1187 }
1188 navBarPattern_ = frameNode->GetPattern<NavBarPattern>();
1189 if (!navBarPattern_) {
1190 continue;
1191 }
1192 return;
1193 }
1194 navBarPattern_ = nullptr;
1195 return;
1196 }
1197
GetParentModalSheet()1198 void ScrollablePattern::GetParentModalSheet()
1199 {
1200 if (sheetPattern_) {
1201 return;
1202 }
1203 auto host = GetHost();
1204 CHECK_NULL_VOID(host);
1205
1206 if (host->GetTag() != V2::SCROLL_ETS_TAG) {
1207 return;
1208 }
1209
1210 for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
1211 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
1212 if (!frameNode) {
1213 continue;
1214 }
1215 sheetPattern_ = frameNode->GetPattern<SheetPresentationPattern>();
1216 if (!sheetPattern_) {
1217 continue;
1218 }
1219 return;
1220 }
1221 return;
1222 }
1223
StopAnimate()1224 void ScrollablePattern::StopAnimate()
1225 {
1226 if (!IsScrollableStopped()) {
1227 StopScrollable();
1228 }
1229 if (animator_ && !animator_->IsStopped()) {
1230 animator_->Stop();
1231 }
1232 if (!isAnimationStop_) {
1233 StopAnimation(springAnimation_);
1234 StopAnimation(curveAnimation_);
1235 }
1236 if (scrollBar_) {
1237 scrollBar_->StopFlingAnimation();
1238 }
1239 }
1240
ScrollTo(float position)1241 void ScrollablePattern::ScrollTo(float position)
1242 {
1243 StopAnimate();
1244 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1245 }
1246
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll,bool useTotalOffset)1247 void ScrollablePattern::AnimateTo(
1248 float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll, bool useTotalOffset)
1249 {
1250 float currVelocity = 0.0f;
1251 if (!IsScrollableStopped()) {
1252 CHECK_NULL_VOID(scrollableEvent_);
1253 auto scrollable = scrollableEvent_->GetScrollable();
1254 CHECK_NULL_VOID(scrollable);
1255 currVelocity = -scrollable->GetCurrentVelocity();
1256 scrollAbort_ = true;
1257 StopScrollable();
1258 }
1259 if (!isAnimationStop_) {
1260 currVelocity = GetCurrentVelocity();
1261 scrollAbort_ = true;
1262 StopAnimation(springAnimation_);
1263 StopAnimation(curveAnimation_);
1264 }
1265 if (animator_ && !animator_->IsStopped()) {
1266 scrollAbort_ = true;
1267 animator_->Stop();
1268 }
1269 if (NearEqual(position, GetTotalOffset())) {
1270 return;
1271 }
1272 finalPosition_ = position;
1273 auto host = GetHost();
1274 CHECK_NULL_VOID(host);
1275 if (smooth) {
1276 if (!useTotalOffset) {
1277 lastPosition_ = GetTotalOffset();
1278 }
1279 PlaySpringAnimation(position, DEFAULT_SCROLL_TO_VELOCITY, DEFAULT_SCROLL_TO_MASS, DEFAULT_SCROLL_TO_STIFFNESS,
1280 DEFAULT_SCROLL_TO_DAMPING, useTotalOffset);
1281 } else {
1282 PlayCurveAnimation(position, duration, curve, canOverScroll);
1283 }
1284 if (!GetIsDragging()) {
1285 FireOnScrollStart();
1286 }
1287 PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
1288 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::SCROLLER_ANIMATION, PerfActionType::FIRST_MOVE, "");
1289 auto pipeline = PipelineBase::GetCurrentContext();
1290 CHECK_NULL_VOID(pipeline);
1291 pipeline->RequestFrame();
1292 }
1293
OnAnimateFinish()1294 void ScrollablePattern::OnAnimateFinish()
1295 {
1296 useTotalOffset_ = true;
1297 auto host = GetHost();
1298 CHECK_NULL_VOID(host);
1299 if (isAnimationStop_) {
1300 SetUiDvsyncSwitch(false);
1301 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::END);
1302 PerfMonitor::GetPerfMonitor()->End(PerfConstants::SCROLLER_ANIMATION, false);
1303 }
1304 if (animateToTraceFlag_) {
1305 animateToTraceFlag_ = false;
1306 AceAsyncTraceEnd(host->GetId(), TRAILING_ANIMATION);
1307 }
1308 }
1309
PlaySpringAnimation(float position,float velocity,float mass,float stiffness,float damping,bool useTotalOffset)1310 void ScrollablePattern::PlaySpringAnimation(float position, float velocity, float mass, float stiffness, float damping,
1311 bool useTotalOffset)
1312 {
1313 if (!springOffsetProperty_) {
1314 InitSpringOffsetProperty();
1315 CHECK_NULL_VOID(springOffsetProperty_);
1316 }
1317
1318 AnimationOption option;
1319 auto curve = AceType::MakeRefPtr<InterpolatingSpring>(velocity, mass, stiffness, damping);
1320 InitOption(option, CUSTOM_ANIMATION_DURATION, curve);
1321 isAnimationStop_ = false;
1322 useTotalOffset_ = useTotalOffset;
1323 AnimationUtils::ExecuteWithoutAnimation([this]() { springOffsetProperty_->Set(GetTotalOffset()); });
1324 springAnimation_ = AnimationUtils::StartAnimation(
1325 option,
1326 [weak = AceType::WeakClaim(this), position]() {
1327 auto pattern = weak.Upgrade();
1328 CHECK_NULL_VOID(pattern);
1329 pattern->SetUiDvsyncSwitch(true);
1330 pattern->springOffsetProperty_->Set(position);
1331 },
1332 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1333 ContainerScope scope(id);
1334 auto pattern = weak.Upgrade();
1335 CHECK_NULL_VOID(pattern);
1336 pattern->OnAnimateFinish();
1337 pattern->SetScrollEdgeType(ScrollEdgeType::SCROLL_NONE);
1338 });
1339 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1340 }
1341
PlayCurveAnimation(float position,float duration,const RefPtr<Curve> & curve,bool canOverScroll)1342 void ScrollablePattern::PlayCurveAnimation(
1343 float position, float duration, const RefPtr<Curve>& curve, bool canOverScroll)
1344 {
1345 AnimationOption option;
1346 InitOption(option, duration, curve);
1347 if (!curveOffsetProperty_) {
1348 InitCurveOffsetProperty();
1349 CHECK_NULL_VOID(curveOffsetProperty_);
1350 }
1351 isAnimationStop_ = false;
1352 SetAnimateCanOverScroll(canOverScroll);
1353 curveOffsetProperty_->Set(GetTotalOffset());
1354 curveAnimation_ = AnimationUtils::StartAnimation(
1355 option,
1356 [weak = AceType::WeakClaim(this), position]() {
1357 auto pattern = weak.Upgrade();
1358 CHECK_NULL_VOID(pattern);
1359 pattern->SetUiDvsyncSwitch(true);
1360 pattern->curveOffsetProperty_->Set(position);
1361 },
1362 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1363 ContainerScope scope(id);
1364 auto pattern = weak.Upgrade();
1365 CHECK_NULL_VOID(pattern);
1366 pattern->OnAnimateFinish();
1367 });
1368 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1369 }
1370
GetScrollDelta(float offset,bool & stopAnimation)1371 float ScrollablePattern::GetScrollDelta(float offset, bool& stopAnimation)
1372 {
1373 auto context = GetContext();
1374 CHECK_NULL_RETURN(context, 0.0f);
1375 uint64_t currentVsync = context->GetVsyncTime();
1376 uint64_t diff = currentVsync - lastVsyncTime_;
1377 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1378 currentVelocity_ = (offset - lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1379 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, currentVelocity_, SceneStatus::RUNNING);
1380 }
1381 stopAnimation = NearEqual(finalPosition_, offset, SPRING_ACCURACY);
1382 if (stopAnimation) {
1383 offset = finalPosition_;
1384 }
1385 if (NearEqual(offset, lastPosition_, 1.0) && !animateToTraceFlag_) {
1386 animateToTraceFlag_ = true;
1387 auto host = GetHost();
1388 auto id = host ? host->GetId() : 0;
1389 AceAsyncTraceBegin(id, TRAILING_ANIMATION);
1390 }
1391 auto delta = useTotalOffset_ ? GetTotalOffset() - offset : lastPosition_ - offset;
1392 lastVsyncTime_ = currentVsync;
1393 lastPosition_ = offset;
1394 return delta;
1395 }
1396
InitSpringOffsetProperty()1397 void ScrollablePattern::InitSpringOffsetProperty()
1398 {
1399 auto host = GetHost();
1400 CHECK_NULL_VOID(host);
1401 auto renderContext = host->GetRenderContext();
1402 CHECK_NULL_VOID(renderContext);
1403 auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1404 auto pattern = weak.Upgrade();
1405 CHECK_NULL_VOID(pattern);
1406 if (pattern->isAnimationStop_) {
1407 return;
1408 }
1409 bool stopAnimation = false;
1410 auto delta = pattern->GetScrollDelta(offset, stopAnimation);
1411 if (!pattern->UpdateCurrentOffset(delta, SCROLL_FROM_ANIMATION_CONTROLLER) || stopAnimation) {
1412 pattern->StopAnimation(pattern->springAnimation_);
1413 }
1414 };
1415 springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1416 renderContext->AttachNodeAnimatableProperty(springOffsetProperty_);
1417 }
1418
InitCurveOffsetProperty()1419 void ScrollablePattern::InitCurveOffsetProperty()
1420 {
1421 auto host = GetHost();
1422 CHECK_NULL_VOID(host);
1423 auto renderContext = host->GetRenderContext();
1424 CHECK_NULL_VOID(renderContext);
1425 auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1426 auto pattern = weak.Upgrade();
1427 CHECK_NULL_VOID(pattern);
1428 if (pattern->isAnimationStop_) {
1429 return;
1430 }
1431 bool stopAnimation = false;
1432 auto delta = pattern->GetScrollDelta(offset, stopAnimation);
1433 if (!pattern->UpdateCurrentOffset(delta, SCROLL_FROM_ANIMATION_CONTROLLER) ||
1434 stopAnimation || pattern->isAnimateOverScroll_) {
1435 if (pattern->isAnimateOverScroll_) {
1436 pattern->isAnimateOverScroll_ = false;
1437 auto pauseVelocity = -pattern->currentVelocity_;
1438 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
1439 CHECK_NULL_VOID(context);
1440 context->MarkNeedFlushAnimationStartTime();
1441 pattern->PauseAnimation(pattern->curveAnimation_);
1442 pattern->HandleOverScroll(pauseVelocity);
1443 } else if (stopAnimation ||
1444 (pattern->IsAtTop() && LessOrEqual(pattern->finalPosition_, pattern->GetTotalOffset())) ||
1445 (pattern->IsAtBottom() && GreatOrEqual(pattern->finalPosition_, pattern->GetTotalOffset()))) {
1446 pattern->StopAnimation(pattern->curveAnimation_);
1447 }
1448 }
1449 };
1450 curveOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1451 renderContext->AttachNodeAnimatableProperty(curveOffsetProperty_);
1452 }
1453
InitOption(AnimationOption & option,float duration,const RefPtr<Curve> & curve)1454 void ScrollablePattern::InitOption(AnimationOption &option, float duration, const RefPtr<Curve>& curve)
1455 {
1456 if (!curve) {
1457 option.SetCurve(Curves::EASE); // default curve
1458 } else {
1459 option.SetCurve(curve);
1460 }
1461 if (duration > 0) {
1462 option.SetDuration(duration);
1463 } else {
1464 option.SetDuration(CUSTOM_ANIMATION_DURATION);
1465 }
1466 }
1467
StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1468 void ScrollablePattern::StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1469 {
1470 SetAnimateCanOverScroll(false);
1471 isAnimationStop_ = true;
1472 currentVelocity_ = 0.0;
1473 if (!animation) {
1474 return;
1475 }
1476 AnimationUtils::StopAnimation(animation);
1477 OnAnimateStop();
1478 }
1479
PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1480 void ScrollablePattern::PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1481 {
1482 SetAnimateCanOverScroll(false);
1483 isAnimationStop_ = true;
1484 currentVelocity_ = 0.0;
1485 if (!animation) {
1486 return;
1487 }
1488 AnimationUtils::StopAnimation(animation);
1489 }
1490
OnAttachToFrameNode()1491 void ScrollablePattern::OnAttachToFrameNode()
1492 {
1493 auto host = GetHost();
1494 CHECK_NULL_VOID(host);
1495 host->GetRenderContext()->SetClipToBounds(true);
1496 host->GetRenderContext()->UpdateClipEdge(true);
1497 }
1498
UninitMouseEvent()1499 void ScrollablePattern::UninitMouseEvent()
1500 {
1501 if (!boxSelectPanEvent_) {
1502 return;
1503 }
1504 auto host = GetHost();
1505 CHECK_NULL_VOID(host);
1506 auto gestureHub = host->GetOrCreateGestureEventHub();
1507 CHECK_NULL_VOID(gestureHub);
1508 gestureHub->RemovePanEvent(boxSelectPanEvent_);
1509 boxSelectPanEvent_.Reset();
1510 ClearMultiSelect();
1511 ClearInvisibleItemsSelectedStatus();
1512 isMouseEventInit_ = false;
1513 }
1514
InitMouseEvent()1515 void ScrollablePattern::InitMouseEvent()
1516 {
1517 auto host = GetHost();
1518 CHECK_NULL_VOID(host);
1519 auto gestureHub = host->GetOrCreateGestureEventHub();
1520 CHECK_NULL_VOID(gestureHub);
1521 if (!boxSelectPanEvent_) {
1522 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1523 auto pattern = weak.Upgrade();
1524 CHECK_NULL_VOID(pattern);
1525 pattern->HandleDragStart(info);
1526 };
1527
1528 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1529 auto pattern = weak.Upgrade();
1530 CHECK_NULL_VOID(pattern);
1531 pattern->HandleDragUpdate(info);
1532 };
1533
1534 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1535 auto pattern = weak.Upgrade();
1536 CHECK_NULL_VOID(pattern);
1537 pattern->HandleDragEnd();
1538 };
1539 GestureEventNoParameter actionCancelTask = [weak = WeakClaim(this)]() {
1540 auto pattern = weak.Upgrade();
1541 CHECK_NULL_VOID(pattern);
1542 pattern->HandleDragEnd();
1543 };
1544 boxSelectPanEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
1545 std::move(actionEndTask), std::move(actionCancelTask));
1546 }
1547 PanDirection panDirection = { .type = PanDirection::ALL };
1548 gestureHub->AddPanEvent(boxSelectPanEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
1549 gestureHub->SetPanEventType(GestureTypeName::BOXSELECT);
1550 gestureHub->SetOnGestureJudgeNativeBegin([](const RefPtr<NG::GestureInfo>& gestureInfo,
1551 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1552 if (gestureInfo->GetType() == GestureTypeName::BOXSELECT &&
1553 gestureInfo->GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1554 return GestureJudgeResult::REJECT;
1555 }
1556 return GestureJudgeResult::CONTINUE;
1557 });
1558 isMouseEventInit_ = true;
1559 }
1560
HandleDragStart(const GestureEvent & info)1561 void ScrollablePattern::HandleDragStart(const GestureEvent& info)
1562 {
1563 TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Box select start");
1564 auto mouseOffsetX = static_cast<float>(info.GetRawGlobalLocation().GetX());
1565 auto mouseOffsetY = static_cast<float>(info.GetRawGlobalLocation().GetY());
1566 mouseOffsetX -= info.GetOffsetX();
1567 mouseOffsetY -= info.GetOffsetY();
1568 SuggestOpIncGroup(true);
1569 if (!IsItemSelected(info)) {
1570 ClearMultiSelect();
1571 ClearInvisibleItemsSelectedStatus();
1572 mouseStartOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1573 lastMouseStart_ = mouseStartOffset_;
1574 mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1575 mousePressOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1576 totalOffsetOfMousePressed_ = mousePressOffset_.GetMainOffset(axis_) + GetTotalOffset();
1577 canMultiSelect_ = true;
1578 }
1579 mousePressed_ = true;
1580 }
1581
HandleDragUpdate(const GestureEvent & info)1582 void ScrollablePattern::HandleDragUpdate(const GestureEvent& info)
1583 {
1584 auto mouseOffsetX = static_cast<float>(info.GetRawGlobalLocation().GetX());
1585 auto mouseOffsetY = static_cast<float>(info.GetRawGlobalLocation().GetY());
1586 if (!mousePressed_ || !canMultiSelect_) {
1587 return;
1588 }
1589 if (info.GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1590 HandleDragEnd();
1591 return;
1592 }
1593 lastMouseMove_ = info;
1594 auto delta = OffsetF(mouseOffsetX, mouseOffsetY) - mousePressOffset_;
1595 if (Offset(delta.GetX(), delta.GetY()).GetDistance() > DEFAULT_PAN_DISTANCE.ConvertToPx()) {
1596 mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1597 // avoid large select zone
1598 LimitMouseEndOffset();
1599 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1600 MultiSelectWithoutKeyboard(selectedZone);
1601 HandleInvisibleItemsSelectedStatus(selectedZone);
1602 }
1603 SelectWithScroll();
1604 }
1605
HandleDragEnd()1606 void ScrollablePattern::HandleDragEnd()
1607 {
1608 TAG_LOGI(AceLogTag::ACE_SCROLLABLE, "Box select end");
1609 mouseStartOffset_.Reset();
1610 lastMouseStart_.Reset();
1611 mouseEndOffset_.Reset();
1612 mousePressed_ = false;
1613 canMultiSelect_ = false;
1614 ClearSelectedZone();
1615 itemToBeSelected_.clear();
1616 lastMouseMove_.SetLocalLocation(Offset::Zero());
1617 }
ClearInvisibleItemsSelectedStatus()1618 void ScrollablePattern::ClearInvisibleItemsSelectedStatus()
1619 {
1620 for (auto& item : itemToBeSelected_) {
1621 item.second.FireSelectChangeEvent(false);
1622 }
1623 itemToBeSelected_.clear();
1624 }
1625
HandleInvisibleItemsSelectedStatus(const RectF & selectedZone)1626 void ScrollablePattern::HandleInvisibleItemsSelectedStatus(const RectF& selectedZone)
1627 {
1628 auto newRect = selectedZone;
1629 auto startMainOffset = mouseStartOffset_.GetMainOffset(axis_);
1630 auto endMainOffset = mouseEndOffset_.GetMainOffset(axis_);
1631 SelectDirection oldDirection = selectDirection_;
1632 if (LessNotEqual(startMainOffset, endMainOffset)) {
1633 selectDirection_ = SELECT_DOWN;
1634 if (axis_ == Axis::VERTICAL) {
1635 newRect.SetOffset(OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_));
1636 } else {
1637 newRect.SetOffset(OffsetF(totalOffsetOfMousePressed_, selectedZone.Top()));
1638 }
1639 } else {
1640 selectDirection_ = SELECT_UP;
1641 if (axis_ == Axis::VERTICAL) {
1642 newRect.SetOffset(
1643 OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset)));
1644 } else {
1645 newRect.SetOffset(
1646 OffsetF(totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset), selectedZone.Top()));
1647 }
1648 }
1649 oldDirection = oldDirection == SELECT_NONE ? selectDirection_ : oldDirection;
1650
1651 for (auto& item : itemToBeSelected_) {
1652 item.second.FireSelectChangeEvent(newRect.IsIntersectWith(item.second.rect));
1653 }
1654
1655 if (oldDirection != selectDirection_) {
1656 itemToBeSelected_.clear();
1657 }
1658 }
1659
SelectWithScroll()1660 void ScrollablePattern::SelectWithScroll()
1661 {
1662 if (!IsScrollable()) {
1663 return;
1664 }
1665 auto offset = GetOutOfScrollableOffset();
1666 if (NearZero(offset)) {
1667 return;
1668 }
1669
1670 if (AnimateRunning()) {
1671 return;
1672 }
1673
1674 if (!isAnimationStop_) {
1675 StopAnimation(springAnimation_);
1676 StopAnimation(curveAnimation_);
1677 }
1678
1679 if (!animator_) {
1680 animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
1681 animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1682 auto pattern = weak.Upgrade();
1683 CHECK_NULL_VOID(pattern);
1684 pattern->OnAnimateStop();
1685 });
1686 } else if (!animator_->IsStopped()) {
1687 scrollAbort_ = true;
1688 animator_->Stop();
1689 }
1690
1691 if (!selectMotion_) {
1692 selectMotion_ = AceType::MakeRefPtr<SelectMotion>(offset, [weak = WeakClaim(this)]() -> bool {
1693 auto pattern = weak.Upgrade();
1694 CHECK_NULL_RETURN(pattern, true);
1695 return pattern->ShouldSelectScrollBeStopped();
1696 });
1697 selectMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
1698 auto pattern = weakScroll.Upgrade();
1699 CHECK_NULL_VOID(pattern);
1700 offset = pattern->GetOffsetWithLimit(offset);
1701 pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
1702 pattern->UpdateMouseStart(offset);
1703 });
1704 } else {
1705 selectMotion_->Reset(offset);
1706 }
1707
1708 animator_->PlayMotion(selectMotion_);
1709
1710 FireOnScrollStart();
1711 }
1712
ClearSelectedZone()1713 void ScrollablePattern::ClearSelectedZone()
1714 {
1715 DrawSelectedZone(RectF());
1716 }
1717
ComputeSelectedZone(const OffsetF & startOffset,const OffsetF & endOffset)1718 RectF ScrollablePattern::ComputeSelectedZone(const OffsetF& startOffset, const OffsetF& endOffset)
1719 {
1720 RectF selectedZone;
1721 if (startOffset.GetX() <= endOffset.GetX()) {
1722 if (startOffset.GetY() <= endOffset.GetY()) {
1723 // bottom right
1724 selectedZone = RectF(startOffset.GetX(), startOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1725 endOffset.GetY() - startOffset.GetY());
1726 } else {
1727 // top right
1728 selectedZone = RectF(startOffset.GetX(), endOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1729 startOffset.GetY() - endOffset.GetY());
1730 }
1731 } else {
1732 if (startOffset.GetY() <= endOffset.GetY()) {
1733 // bottom left
1734 selectedZone = RectF(endOffset.GetX(), startOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1735 endOffset.GetY() - startOffset.GetY());
1736 } else {
1737 // top left
1738 selectedZone = RectF(endOffset.GetX(), endOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1739 startOffset.GetY() - endOffset.GetY());
1740 }
1741 }
1742
1743 return selectedZone;
1744 }
1745
DrawSelectedZone(const RectF & selectedZone)1746 void ScrollablePattern::DrawSelectedZone(const RectF& selectedZone)
1747 {
1748 auto host = GetHost();
1749 CHECK_NULL_VOID(host);
1750 auto hostContext = host->GetRenderContext();
1751 CHECK_NULL_VOID(hostContext);
1752 hostContext->UpdateMouseSelectWithRect(selectedZone, SELECT_FILL_COLOR, SELECT_STROKE_COLOR);
1753 }
1754
MarkSelectedItems()1755 void ScrollablePattern::MarkSelectedItems()
1756 {
1757 if (multiSelectable_ && mousePressed_) {
1758 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1759 if (!selectedZone.IsEmpty()) {
1760 MultiSelectWithoutKeyboard(selectedZone);
1761 HandleInvisibleItemsSelectedStatus(selectedZone);
1762 }
1763 }
1764 }
1765
ShouldSelectScrollBeStopped()1766 bool ScrollablePattern::ShouldSelectScrollBeStopped()
1767 {
1768 if (!mousePressed_) {
1769 return true;
1770 }
1771 auto offset = GetOutOfScrollableOffset();
1772 if (NearZero(offset)) {
1773 return true;
1774 }
1775
1776 if (selectMotion_) {
1777 selectMotion_->Reset(offset);
1778 }
1779 return false;
1780 };
1781
UpdateMouseStart(float offset)1782 void ScrollablePattern::UpdateMouseStart(float offset)
1783 {
1784 if (axis_ == Axis::VERTICAL) {
1785 mouseStartOffset_.AddY(offset);
1786 } else {
1787 mouseStartOffset_.AddX(offset);
1788 }
1789 }
1790
GetOutOfScrollableOffset() const1791 float ScrollablePattern::GetOutOfScrollableOffset() const
1792 {
1793 auto offset = 0.0f;
1794 auto mouseMainOffset = static_cast<float>(
1795 axis_ == Axis::VERTICAL ? lastMouseMove_.GetLocalLocation().GetY() : lastMouseMove_.GetLocalLocation().GetX());
1796 auto hostSize = GetHostFrameSize();
1797 CHECK_NULL_RETURN(hostSize.has_value(), offset);
1798 auto mainTop = 0.0f;
1799 auto mainBottom = hostSize->MainSize(axis_);
1800 if (GreatOrEqual(mouseMainOffset, mainTop) && LessOrEqual(mouseMainOffset, mainBottom)) {
1801 return offset;
1802 }
1803 if (GreatNotEqual(mouseMainOffset, mainBottom)) {
1804 if (IsAtBottom()) {
1805 return offset;
1806 }
1807 offset = mainBottom - mouseMainOffset;
1808 }
1809 if (LessNotEqual(mouseMainOffset, mainTop)) {
1810 if (IsAtTop()) {
1811 return offset;
1812 }
1813 offset = mainTop - mouseMainOffset;
1814 }
1815 return offset;
1816 }
1817
1818 // avoid start position move when offset is bigger then item height
GetOffsetWithLimit(float offset) const1819 float ScrollablePattern::GetOffsetWithLimit(float offset) const
1820 {
1821 if (Positive(offset)) {
1822 auto totalOffset = GetTotalOffset();
1823 return std::min(totalOffset, offset);
1824 } else if (Negative(offset)) {
1825 auto frameNode = GetHost();
1826 CHECK_NULL_RETURN(frameNode, true);
1827 auto hostSize = frameNode->GetGeometryNode()->GetFrameSize();
1828 auto remainHeight = GetTotalHeight() - GetTotalOffset() - hostSize.MainSize(axis_);
1829 return std::max(offset, -remainHeight);
1830 }
1831 return 0;
1832 }
1833
LimitMouseEndOffset()1834 void ScrollablePattern::LimitMouseEndOffset()
1835 {
1836 float limitedMainOffset = -1.0f;
1837 float limitedCrossOffset = -1.0f;
1838 auto frameNode = GetHost();
1839 CHECK_NULL_VOID(frameNode);
1840 auto hostSize = frameNode->GetGeometryNode()->GetFrameSize();
1841 auto mainSize = hostSize.MainSize(axis_);
1842 auto crossSize = hostSize.CrossSize(axis_);
1843 auto mainOffset = mouseEndOffset_.GetMainOffset(axis_);
1844 auto crossOffset = mouseEndOffset_.GetCrossOffset(axis_);
1845 if (LessNotEqual(mainOffset, 0.0f)) {
1846 limitedMainOffset = 0.0f;
1847 }
1848 if (GreatNotEqual(mainOffset, mainSize)) {
1849 limitedMainOffset = mainSize;
1850 }
1851 if (LessNotEqual(crossOffset, 0.0f)) {
1852 limitedCrossOffset = 0.0f;
1853 }
1854 if (GreatNotEqual(crossOffset, crossSize)) {
1855 limitedCrossOffset = crossSize;
1856 }
1857
1858 if (axis_ == Axis::VERTICAL) {
1859 mouseEndOffset_.SetX(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedCrossOffset);
1860 mouseEndOffset_.SetY(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedMainOffset);
1861 } else {
1862 mouseEndOffset_.SetX(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedMainOffset);
1863 mouseEndOffset_.SetY(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedCrossOffset);
1864 }
1865 }
1866
HandleScrollImpl(float offset,int32_t source)1867 bool ScrollablePattern::HandleScrollImpl(float offset, int32_t source)
1868 {
1869 // Previous: Set HandleScrollImpl to Scrollable->callback_
1870 // Scrollable::HandleScroll calls callback_ through UpdateScrollPosition
1871
1872 // Now: HandleScroll moved to ScrollablePattern, directly call HandleScrollImpl in
1873 // ScrollablePattern::HandleScroll
1874 double overOffset = offset;
1875 if (!OnScrollPosition(overOffset, source)) {
1876 return false;
1877 }
1878 auto result = OnScrollCallback(overOffset, source);
1879 SelectOverlayScrollNotifier::NotifyOnScrollCallback(WeakClaim(this), overOffset, source);
1880 return result;
1881 }
1882
NotifyMoved(bool value)1883 void ScrollablePattern::NotifyMoved(bool value)
1884 {
1885 CHECK_NULL_VOID(scrollableEvent_);
1886 auto&& scroll = scrollableEvent_->GetScrollable();
1887 if (scroll) {
1888 scroll->SetMoved(value);
1889 }
1890 }
1891
ProcessSpringEffect(float velocity,bool needRestart)1892 void ScrollablePattern::ProcessSpringEffect(float velocity, bool needRestart)
1893 {
1894 CHECK_NULL_VOID(InstanceOf<ScrollSpringEffect>(scrollEffect_));
1895 auto isOutOfBoundary = OutBoundaryCallback();
1896 if (!isOutOfBoundary && !GetCanOverScroll()) {
1897 OnScrollEnd();
1898 return;
1899 }
1900 CHECK_NULL_VOID(scrollableEvent_);
1901 auto scrollable = scrollableEvent_->GetScrollable();
1902 // HandleTouchUp may be triggered before HandleDragEnd when scrollable nested scrollable,
1903 // so need to update spring motion.
1904 if (needRestart || !(scrollable && scrollable->IsSpringMotionRunning())) {
1905 StopScrollable();
1906 scrollEffect_->ProcessScrollOver(velocity);
1907 } else {
1908 scrollEffect_->ProcessSpringUpdate();
1909 }
1910 }
1911
SetCanOverScroll(bool val)1912 void ScrollablePattern::SetCanOverScroll(bool val)
1913 {
1914 CHECK_NULL_VOID(scrollableEvent_);
1915 auto&& scrollable = scrollableEvent_->GetScrollable();
1916 if (scrollable) {
1917 scrollable->SetCanOverScroll(val);
1918 }
1919 }
1920
GetCanOverScroll() const1921 bool ScrollablePattern::GetCanOverScroll() const
1922 {
1923 CHECK_NULL_RETURN(scrollableEvent_, true);
1924 auto&& scrollable = scrollableEvent_->GetScrollable();
1925 if (scrollable) {
1926 return scrollable->CanOverScroll();
1927 }
1928 return true;
1929 }
1930
GetEdgeEffect() const1931 EdgeEffect ScrollablePattern::GetEdgeEffect() const
1932 {
1933 return edgeEffect_;
1934 }
1935
GetScrollState() const1936 ScrollState ScrollablePattern::GetScrollState() const
1937 {
1938 return ScrollablePattern::GetScrollState(scrollSource_);
1939 }
1940
GetScrollState(int32_t scrollSource)1941 ScrollState ScrollablePattern::GetScrollState(int32_t scrollSource)
1942 {
1943 // with event
1944 if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS || scrollSource == SCROLL_FROM_BAR) {
1945 return ScrollState::SCROLL;
1946 }
1947 // without event
1948 if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
1949 scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
1950 return ScrollState::FLING;
1951 }
1952 // SCROLL_FROM_NONE, SCROLL_FROM_JUMP, SCROLL_FROM_CHILD, SCROLL_FROM_FOCUS_JUMP, SCROLL_FROM_ROTATE,
1953 // SCROLL_FROM_INDEXER, SCROLL_FROM_START
1954 return ScrollState::IDLE;
1955 }
1956
ConvertScrollSource(int32_t source)1957 ScrollSource ScrollablePattern::ConvertScrollSource(int32_t source)
1958 {
1959 // static linear map must be sorted by key.
1960 static const LinearEnumMapNode<int32_t, ScrollSource> scrollSourceMap[] = {
1961 { SCROLL_FROM_UPDATE, ScrollSource::DRAG },
1962 { SCROLL_FROM_ANIMATION, ScrollSource::FLING },
1963 { SCROLL_FROM_JUMP, ScrollSource::SCROLLER },
1964 { SCROLL_FROM_ANIMATION_SPRING, ScrollSource::EDGE_EFFECT },
1965 { SCROLL_FROM_BAR, ScrollSource::SCROLL_BAR },
1966 { SCROLL_FROM_FOCUS_JUMP, ScrollSource::OTHER_USER_INPUT },
1967 { SCROLL_FROM_AXIS, ScrollSource::OTHER_USER_INPUT },
1968 { SCROLL_FROM_ANIMATION_CONTROLLER, ScrollSource::SCROLLER_ANIMATION },
1969 { SCROLL_FROM_BAR_FLING, ScrollSource::SCROLL_BAR_FLING },
1970 };
1971 ScrollSource sourceType = ScrollSource::OTHER_USER_INPUT;
1972 int64_t idx = BinarySearchFindIndex(scrollSourceMap, ArraySize(scrollSourceMap), source);
1973 if (idx >= 0) {
1974 sourceType = scrollSourceMap[idx].value;
1975 }
1976 return sourceType;
1977 }
1978
HandleScrollParentFirst(float & offset,int32_t source,NestedState state)1979 ScrollResult ScrollablePattern::HandleScrollParentFirst(float& offset, int32_t source, NestedState state)
1980 {
1981 auto parent = GetNestedScrollParent();
1982 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1983 if (state == NestedState::CHILD_OVER_SCROLL) {
1984 if (GetEdgeEffect() == EdgeEffect::NONE) {
1985 return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
1986 }
1987 ExecuteScrollFrameBegin(offset, scrollState);
1988 return { 0, true };
1989 }
1990 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
1991 offset = IsReverse() ? -result.remain : result.remain;
1992 if (NearZero(offset)) {
1993 SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1994 return { 0, false };
1995 }
1996 float allOffset = offset;
1997 ExecuteScrollFrameBegin(offset, scrollState);
1998 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1999 auto overOffsets = GetOverScrollOffset(offset);
2000 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
2001 remainOffset += overOffset;
2002 if (NearZero(remainOffset)) {
2003 SetCanOverScroll(false);
2004 return { 0, false };
2005 }
2006 if (state == NestedState::CHILD_SCROLL) {
2007 offset -= overOffset;
2008 SetCanOverScroll(false);
2009 return { remainOffset, !NearZero(overOffset) };
2010 }
2011 bool parentEdgeEffect = false;
2012 if (GetEdgeEffect() == EdgeEffect::NONE) {
2013 result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2014 if (NearZero(result.remain)) {
2015 offset -= overOffset;
2016 parentEdgeEffect = NearZero(offset) && result.reachEdge;
2017 }
2018 }
2019 SetCanOverScroll((!NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE) || parentEdgeEffect);
2020 return { 0, GetCanOverScroll() };
2021 }
2022
HandleScrollSelfFirst(float & offset,int32_t source,NestedState state)2023 ScrollResult ScrollablePattern::HandleScrollSelfFirst(float& offset, int32_t source, NestedState state)
2024 {
2025 auto parent = GetNestedScrollParent();
2026 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
2027 if (state == NestedState::CHILD_OVER_SCROLL) {
2028 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2029 if (NearZero(result.remain)) {
2030 offset = 0;
2031 return result;
2032 }
2033 ExecuteScrollFrameBegin(offset, scrollState);
2034 if (GetEdgeEffect() == EdgeEffect::NONE) {
2035 return result;
2036 }
2037 return { 0, true };
2038 }
2039 float allOffset = offset;
2040 ExecuteScrollFrameBegin(offset, scrollState);
2041 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
2042 auto overOffsets = GetOverScrollOffset(offset);
2043 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
2044 if (NearZero(overOffset) && NearZero(remainOffset)) {
2045 SetCanOverScroll(false);
2046 return { 0, false };
2047 }
2048 offset -= overOffset;
2049 auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
2050 if (NearZero(result.remain)) {
2051 SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
2052 return { 0, false };
2053 }
2054 if (state == NestedState::CHILD_SCROLL) {
2055 SetCanOverScroll(false);
2056 return result;
2057 }
2058 // triggering overScroll, parent always handle it first
2059 auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2060 offset += LessNotEqual(std::abs(overOffset), std::abs(result.remain)) ? overOffset : overRes.remain;
2061 bool parentEdgeEffect = result.reachEdge && NearZero(offset);
2062 SetCanOverScroll((!NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE) || parentEdgeEffect);
2063 return { 0, GetCanOverScroll() };
2064 }
2065
HandleScrollSelfOnly(float & offset,int32_t source,NestedState state)2066 ScrollResult ScrollablePattern::HandleScrollSelfOnly(float& offset, int32_t source, NestedState state)
2067 {
2068 float allOffset = offset;
2069 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
2070 ExecuteScrollFrameBegin(offset, scrollState);
2071 auto remainOffset = allOffset - offset;
2072 auto overOffsets = GetOverScrollOffset(offset);
2073 auto overOffset = (offset > 0) ? overOffsets.start : overOffsets.end;
2074 remainOffset += overOffset;
2075 if (NearZero(remainOffset)) {
2076 SetCanOverScroll(false);
2077 return { 0, false };
2078 }
2079 bool canOverScroll = false;
2080 if (state == NestedState::CHILD_SCROLL) {
2081 offset -= overOffset;
2082 } else if (state == NestedState::GESTURE) {
2083 canOverScroll = !NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE;
2084 } else if (GetEdgeEffect() != EdgeEffect::NONE) {
2085 remainOffset = 0;
2086 }
2087 SetCanOverScroll(canOverScroll);
2088 return { remainOffset, !NearZero(overOffset) };
2089 }
2090
HandleScrollParallel(float & offset,int32_t source,NestedState state)2091 ScrollResult ScrollablePattern::HandleScrollParallel(float& offset, int32_t source, NestedState state)
2092 {
2093 auto remainOffset = 0.0;
2094 auto parent = GetNestedScrollParent();
2095 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
2096 if (state == NestedState::CHILD_OVER_SCROLL) {
2097 if (GetEdgeEffect() == EdgeEffect::NONE) {
2098 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2099 remainOffset = result.remain;
2100 offset = 0;
2101 } else {
2102 ExecuteScrollFrameBegin(offset, scrollState);
2103 }
2104 return { remainOffset, true };
2105 }
2106
2107 bool canOverScroll = false;
2108 float parentOffset = offset;
2109 ExecuteScrollFrameBegin(offset, scrollState);
2110 auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
2111
2112 auto overOffsets = GetOverScrollOffset(offset);
2113 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
2114 if (!NearZero(overOffset) && result.reachEdge) {
2115 if (state == NestedState::CHILD_SCROLL) {
2116 remainOffset = overOffset;
2117 offset = offset - overOffset;
2118 } else if (GetEdgeEffect() == EdgeEffect::NONE) {
2119 parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2120 canOverScroll = true;
2121 offset = offset - overOffset;
2122 } else {
2123 canOverScroll = true;
2124 }
2125 } else if (!NearZero(overOffset)) {
2126 offset = offset - overOffset;
2127 }
2128 SetCanOverScroll(canOverScroll);
2129 return { remainOffset, !NearZero(overOffset) && result.reachEdge };
2130 }
2131
HandleOutBoundary(float & offset,int32_t source,NestedState state,ScrollResult & result)2132 bool ScrollablePattern::HandleOutBoundary(float& offset, int32_t source, NestedState state, ScrollResult& result)
2133 {
2134 auto overOffsets = GetOverScrollOffset(offset);
2135 auto backOverOffset = Negative(offset) ? overOffsets.start : overOffsets.end;
2136 auto oppositeOverOffset = Negative(offset) ? overOffsets.end : overOffsets.start;
2137 if (state != NestedState::GESTURE) {
2138 if (NearZero(backOverOffset)) {
2139 return false;
2140 }
2141 result = {offset - backOverOffset, true};
2142 offset = backOverOffset;
2143 return true;
2144 }
2145 auto nestedScroll = GetNestedScroll();
2146 auto isAtTopOrBottom = !NearZero(backOverOffset) || !NearZero(oppositeOverOffset);
2147 if (!NestedScrollOutOfBoundary() && nestedScroll.NeedParent()) {
2148 for (auto ancestor = GetNestedScrollParent(); ancestor != nullptr;
2149 ancestor = ancestor->GetNestedScrollParent()) {
2150 if (ancestor->NestedScrollOutOfBoundary()) {
2151 auto ancestorResult = ancestor->HandleScroll(offset, source,
2152 isAtTopOrBottom ? NestedState::CHILD_OVER_SCROLL : NestedState::CHILD_SCROLL,
2153 GetVelocity());
2154 offset = ancestorResult.remain;
2155 SetCanOverScroll(NearZero(offset));
2156 return true;
2157 }
2158 auto ancestorNestedScroll = ancestor->GetNestedScroll();
2159 if (!ancestorNestedScroll.NeedParent()) {
2160 break;
2161 }
2162 }
2163 return false;
2164 }
2165 return HandleSelfOutBoundary(offset, source, backOverOffset, oppositeOverOffset);
2166 }
2167
HandleSelfOutBoundary(float & offset,int32_t source,const float backOverOffset,const float oppositeOverOffset)2168 bool ScrollablePattern::HandleSelfOutBoundary(float& offset, int32_t source, const float backOverOffset,
2169 const float oppositeOverOffset)
2170 {
2171 if (NearZero(backOverOffset)) {
2172 return false;
2173 }
2174 offset -= backOverOffset;
2175 ScrollResult result = { 0.f, false};
2176 auto parent = GetNestedScrollParent();
2177 if (!NearZero(offset) && parent) {
2178 auto nestedScrollOptions = GetNestedScroll();
2179 auto nestedScroll = Positive(offset) ? nestedScrollOptions.backward : nestedScrollOptions.forward;
2180 switch (nestedScroll) {
2181 case NestedScrollMode::SELF_FIRST: {
2182 offset -= oppositeOverOffset;
2183 result = parent->HandleScroll(oppositeOverOffset, source, NestedState::CHILD_SCROLL, GetVelocity());
2184 if (!NearZero(result.remain)) {
2185 result = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL, GetVelocity());
2186 }
2187 break;
2188 }
2189 case NestedScrollMode::PARENT_FIRST: {
2190 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
2191 offset = 0.f;
2192 break;
2193 }
2194 case NestedScrollMode::PARALLEL: {
2195 parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, GetVelocity());
2196 break;
2197 }
2198 default:
2199 break;
2200 }
2201 }
2202 offset += result.remain;
2203 SetCanOverScroll(NearZero(offset));
2204 offset += backOverOffset;
2205 return true;
2206 }
2207
HandleScroll(float offset,int32_t source,NestedState state,float velocity)2208 ScrollResult ScrollablePattern::HandleScroll(float offset, int32_t source, NestedState state, float velocity)
2209 {
2210 ScrollResult result = { 0, false };
2211 auto host = GetHost();
2212 CHECK_NULL_RETURN(host, result);
2213 auto nestedScroll = GetNestedScroll();
2214 auto parent = GetNestedScrollParent();
2215 auto initOffset = offset;
2216 if (NearZero(offset)) {
2217 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
2218 ExecuteScrollFrameBegin(offset, scrollState);
2219 } else if (!HandleOutBoundary(offset, source, state, result)) {
2220 if (parent && !IsScrollSnap() &&
2221 ((offset < 0 && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) ||
2222 (offset > 0 && nestedScroll.backward == NestedScrollMode::PARENT_FIRST))) {
2223 result = HandleScrollParentFirst(offset, source, state);
2224 } else if (parent && ((offset < 0 && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
2225 (offset > 0 && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
2226 result = HandleScrollSelfFirst(offset, source, state);
2227 } else if (parent && ((offset < 0 && nestedScroll.forward == NestedScrollMode::PARALLEL) ||
2228 (offset > 0 && nestedScroll.backward == NestedScrollMode::PARALLEL))) {
2229 result = HandleScrollParallel(offset, source, state);
2230 } else {
2231 result = HandleScrollSelfOnly(offset, source, state);
2232 }
2233 }
2234 ACE_SCOPED_TRACE("HandleScroll, initOffset:%f, processedOffset:%f, "
2235 "source:%d, nestedState:%d, canOverScroll:%u, id:%d, tag:%s",
2236 initOffset, offset, source, state, GetCanOverScroll(),
2237 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2238 UpdateNestedScrollVelocity(offset, state);
2239 bool moved = HandleScrollImpl(offset, source);
2240 NotifyMoved(moved);
2241 return result;
2242 }
2243
HandleScrollVelocity(float velocity,const RefPtr<NestableScrollContainer> & child)2244 bool ScrollablePattern::HandleScrollVelocity(float velocity, const RefPtr<NestableScrollContainer>& child)
2245 {
2246 // if scrollable try to over scroll when it is at the boundary,
2247 // scrollable does not start fling animation.
2248 SetNestedScrolling(false);
2249 SetScrollOriginChild(AceType::WeakClaim(AceType::RawPtr(child)));
2250 auto edgeEffect = GetEdgeEffect();
2251 auto needFlingAtEdge = !(((IsAtTop() && Positive(velocity)) || (IsAtBottom() && Negative(velocity))));
2252 auto isOutOfBoundary = OutBoundaryCallback();
2253 auto host = GetHost();
2254 CHECK_NULL_RETURN(host, false);
2255 ACE_SCOPED_TRACE("HandleScrollVelocity, IsOutOfBoundary:%u, needFlingAtEdge:%u, edgeEffect:%d, IsAtTop:%u, "
2256 "IsAtBottom:%u, velocity:%f, id:%d, tag:%s",
2257 isOutOfBoundary, needFlingAtEdge, edgeEffect, IsAtTop(), IsAtBottom(), velocity,
2258 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2259 if (!isOutOfBoundary && needFlingAtEdge) {
2260 // trigger scroll animation if edge not reached
2261 if (scrollableEvent_ && scrollableEvent_->GetScrollable()) {
2262 scrollableEvent_->GetScrollable()->StartScrollAnimation(0.0f, velocity);
2263 return true;
2264 }
2265 return false;
2266 }
2267 SetCanOverScroll(true);
2268 return HandleOverScroll(velocity) || GetEdgeEffect() == EdgeEffect::FADE;
2269 }
2270
RemainVelocityToChild(float remainVelocity)2271 void ScrollablePattern::RemainVelocityToChild(float remainVelocity)
2272 {
2273 auto host = GetHost();
2274 CHECK_NULL_VOID(host);
2275 ACE_SCOPED_TRACE("RemainVelocityToChild, remainVelocity:%f id:%d, tag:%s",
2276 remainVelocity, static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2277 Fling(remainVelocity);
2278 }
2279
HandleScrollableOverScroll(float velocity)2280 bool ScrollablePattern::HandleScrollableOverScroll(float velocity)
2281 {
2282 bool result = false;
2283 for (auto ancestor = GetNestedScrollParent(); ancestor != nullptr; ancestor = ancestor->GetNestedScrollParent()) {
2284 if (ancestor->NestedScrollOutOfBoundary()) {
2285 result = ancestor->HandleScrollVelocity(velocity, Claim(this));
2286 break;
2287 }
2288 auto ancestorNestedScroll = ancestor->GetNestedScroll();
2289 if (!ancestorNestedScroll.NeedParent()) {
2290 break;
2291 }
2292 }
2293 if (result) {
2294 OnScrollEndRecursiveInner(velocity);
2295 return true;
2296 }
2297 OnScrollEnd();
2298 auto parent = GetNestedScrollParent();
2299 auto nestedScroll = GetNestedScroll();
2300 if (!result && parent && nestedScroll.NeedParent()) {
2301 result = parent->HandleScrollVelocity(velocity, Claim(this));
2302 }
2303 return result;
2304 }
2305
HandleOverScroll(float velocity)2306 bool ScrollablePattern::HandleOverScroll(float velocity)
2307 {
2308 auto parent = GetNestedScrollParent();
2309 auto nestedScroll = GetNestedScroll();
2310 auto host = GetHost();
2311 CHECK_NULL_RETURN(host, false);
2312 auto isOutOfBoundary = IsOutOfBoundary();
2313 ACE_SCOPED_TRACE("HandleOverScroll, IsOutOfBoundary:%u, id:%d, tag:%s", isOutOfBoundary,
2314 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2315 if (!parent || !nestedScroll.NeedParent(velocity < 0) || isOutOfBoundary) {
2316 if (GetEdgeEffect() == EdgeEffect::SPRING && AnimateStoped()) {
2317 // trigger onScrollEnd later, when spring animation finishes
2318 ProcessSpringEffect(velocity, true);
2319 return true;
2320 }
2321 OnScrollEnd();
2322 return false;
2323 }
2324 if (parent && InstanceOf<ScrollablePattern>(parent)) {
2325 // Components that are not ScrollablePattern do not implement NestedScrollOutOfBoundary and
2326 // handleScroll is handled differently, so isolate the implementation of handleOverScroll
2327 return HandleScrollableOverScroll(velocity);
2328 }
2329 // parent handle over scroll first
2330 if ((velocity < 0 && (nestedScroll.forward == NestedScrollMode::SELF_FIRST)) ||
2331 (velocity > 0 && (nestedScroll.backward == NestedScrollMode::SELF_FIRST)) ||
2332 (!InstanceOf<ScrollablePattern>(parent) && !isOutOfBoundary)) {
2333 if (parent->HandleScrollVelocity(velocity)) {
2334 OnScrollEnd();
2335 return true;
2336 }
2337 if (GetEdgeEffect() == EdgeEffect::SPRING) {
2338 ProcessSpringEffect(velocity);
2339 return true;
2340 }
2341 }
2342
2343 // self handle over scroll first
2344 if (GetEdgeEffect() == EdgeEffect::SPRING) {
2345 ProcessSpringEffect(velocity);
2346 return true;
2347 }
2348 OnScrollEnd();
2349 return parent->HandleScrollVelocity(velocity);
2350 }
2351
ExecuteScrollFrameBegin(float & mainDelta,ScrollState state)2352 void ScrollablePattern::ExecuteScrollFrameBegin(float& mainDelta, ScrollState state)
2353 {
2354 auto context = PipelineContext::GetCurrentContextSafely();
2355 auto eventHub = GetEventHub<ScrollableEventHub>();
2356 CHECK_NULL_VOID(eventHub);
2357 auto scrollFrameBeginCallback = eventHub->GetOnScrollFrameBegin();
2358 if (!context || !scrollFrameBeginCallback) {
2359 return;
2360 }
2361
2362 auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
2363 auto scrollRes = scrollFrameBeginCallback(-offset, state);
2364 mainDelta = -context->NormalizeToPx(scrollRes.offset);
2365 }
2366
OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child,float position,float velocity)2367 void ScrollablePattern::OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child, float position, float velocity)
2368 {
2369 OnScrollStartRecursiveInner(child, position, velocity);
2370 SetNestedScrolling(true);
2371 SetScrollOriginChild(child);
2372 }
2373
OnScrollStartRecursiveInner(WeakPtr<NestableScrollContainer> child,float position,float velocity)2374 void ScrollablePattern::OnScrollStartRecursiveInner(
2375 WeakPtr<NestableScrollContainer> child, float position, float velocity)
2376 {
2377 SetIsNestedInterrupt(false);
2378 HandleScrollImpl(position, SCROLL_FROM_START);
2379 auto parent = GetNestedScrollParent();
2380 auto nestedScroll = GetNestedScroll();
2381 if (parent && nestedScroll.NeedParent()) {
2382 parent->OnScrollStartRecursive(child, position, GetVelocity());
2383 }
2384 }
2385
OnScrollEndRecursive(const std::optional<float> & velocity)2386 void ScrollablePattern::OnScrollEndRecursive(const std::optional<float>& velocity)
2387 {
2388 OnScrollEndRecursiveInner(velocity);
2389 SetNestedScrolling(false);
2390 CheckRestartSpring(false);
2391 }
2392
SetNestedScrolling(bool nestedScrolling)2393 void ScrollablePattern::SetNestedScrolling(bool nestedScrolling)
2394 {
2395 CHECK_NULL_VOID(scrollableEvent_);
2396 auto scrollable = scrollableEvent_->GetScrollable();
2397 CHECK_NULL_VOID(scrollable);
2398 scrollable->SetNestedScrolling(nestedScrolling);
2399 // Sliding the touchPad is an axis event, and the parent of the nested scroll cannot respond to TouchDown,
2400 // so the scrollable animation stops when the nested scroll scroll start.
2401 if (nestedScrolling) {
2402 scrollable->StopScrollable();
2403 }
2404 }
2405
OnScrollEndRecursiveInner(const std::optional<float> & velocity)2406 void ScrollablePattern::OnScrollEndRecursiveInner(const std::optional<float>& velocity)
2407 {
2408 if (!IsScrollableStopped() && !GetNestedScrolling()) {
2409 return;
2410 }
2411 OnScrollEnd();
2412 auto parent = GetNestedScrollParent();
2413 auto nestedScroll = GetNestedScroll();
2414 if (parent && (nestedScroll.NeedParent() || GetIsNestedInterrupt())) {
2415 parent->OnScrollEndRecursive(velocity);
2416 }
2417 SetIsNestedInterrupt(false);
2418 }
2419
OnScrollDragEndRecursive()2420 void ScrollablePattern::OnScrollDragEndRecursive()
2421 {
2422 auto parent = GetNestedScrollParent();
2423 auto nestedScroll = GetNestedScroll();
2424 if (parent && nestedScroll.NeedParent()) {
2425 parent->OnScrollDragEndRecursive();
2426 }
2427 CheckRestartSpring(false, false);
2428 }
2429
GetVelocity() const2430 float ScrollablePattern::GetVelocity() const
2431 {
2432 float velocity = 0.0f;
2433 CHECK_NULL_RETURN(scrollableEvent_, velocity);
2434 auto scrollable = scrollableEvent_->GetScrollable();
2435 CHECK_NULL_RETURN(scrollable, velocity);
2436 velocity = scrollable->GetCurrentVelocity();
2437 return velocity;
2438 }
2439
RegisterScrollingListener(const RefPtr<ScrollingListener> listener)2440 void ScrollablePattern::RegisterScrollingListener(const RefPtr<ScrollingListener> listener)
2441 {
2442 CHECK_NULL_VOID(listener);
2443 scrollingListener_.emplace_back(listener);
2444 }
2445
FireAndCleanScrollingListener()2446 void ScrollablePattern::FireAndCleanScrollingListener()
2447 {
2448 for (auto listener : scrollingListener_) {
2449 CHECK_NULL_VOID(listener);
2450 listener->NotifyScrollingEvent();
2451 }
2452 scrollingListener_.clear();
2453 }
2454
CleanScrollingListener()2455 void ScrollablePattern::CleanScrollingListener()
2456 {
2457 scrollingListener_.clear();
2458 }
2459
GetMainContentSize() const2460 float ScrollablePattern::GetMainContentSize() const
2461 {
2462 auto host = GetHost();
2463 CHECK_NULL_RETURN(host, 0.0);
2464 auto geometryNode = host->GetGeometryNode();
2465 CHECK_NULL_RETURN(geometryNode, 0.0);
2466 return geometryNode->GetPaddingSize().MainSize(axis_);
2467 }
2468
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)2469 void ScrollablePattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
2470 {
2471 auto host = GetHost();
2472 CHECK_NULL_VOID(host);
2473 ACE_SCOPED_TRACE("ScrollToEdge scrollEdgeType:%zu, id:%d, tag:%s", scrollEdgeType,
2474 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2475 if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
2476 ScrollToIndex(0, false, ScrollAlign::START);
2477 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
2478 // use LAST_ITEM for children count changed after scrollEdge(Edge.Bottom) and before layout
2479 ScrollToIndex(LAST_ITEM, false, ScrollAlign::END);
2480 }
2481 }
2482
Fling(double flingVelocity)2483 void ScrollablePattern::Fling(double flingVelocity)
2484 {
2485 if (!IsScrollableStopped()) {
2486 scrollAbort_ = true;
2487 StopScrollable();
2488 }
2489 if (!isAnimationStop_) {
2490 scrollAbort_ = true;
2491 StopAnimation(springAnimation_);
2492 StopAnimation(curveAnimation_);
2493 }
2494 if (animator_ && !animator_->IsStopped()) {
2495 scrollAbort_ = true;
2496 animator_->Stop();
2497 }
2498 CHECK_NULL_VOID(scrollableEvent_);
2499 auto scrollable = scrollableEvent_->GetScrollable();
2500 CHECK_NULL_VOID(scrollable);
2501 if (IsOutOfBoundary()) {
2502 scrollable->HandleOverScroll(flingVelocity);
2503 } else {
2504 FireOnScrollStart();
2505 scrollable->StartScrollAnimation(0.0f, flingVelocity);
2506 }
2507 auto pipeline = GetContext();
2508 CHECK_NULL_VOID(pipeline);
2509 pipeline->RequestFrame();
2510 }
2511
NotifyFRCSceneInfo(const std::string & scene,double velocity,SceneStatus sceneStatus)2512 void ScrollablePattern::NotifyFRCSceneInfo(const std::string& scene, double velocity, SceneStatus sceneStatus)
2513 {
2514 auto host = GetHost();
2515 CHECK_NULL_VOID(host);
2516 host->AddFRCSceneInfo(scene, velocity, sceneStatus);
2517 }
2518
FireOnScrollStart()2519 void ScrollablePattern::FireOnScrollStart()
2520 {
2521 auto host = GetHost();
2522 CHECK_NULL_VOID(host);
2523 auto hub = host->GetEventHub<ScrollableEventHub>();
2524 CHECK_NULL_VOID(hub);
2525 SuggestOpIncGroup(true);
2526 if (scrollStop_ && !GetScrollAbort()) {
2527 OnScrollStop(hub->GetOnScrollStop());
2528 }
2529 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
2530 AceType::WeakClaim(this), ScrollEventType::SCROLL_START);
2531 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
2532 if (GetScrollAbort()) {
2533 ACE_SCOPED_TRACE("ScrollAbort, no OnScrollStart, id:%d, tag:%s",
2534 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2535 return;
2536 }
2537 auto scrollBar = GetScrollBar();
2538 if (scrollBar) {
2539 scrollBar->PlayScrollBarAppearAnimation();
2540 }
2541 StopScrollBarAnimatorByProxy();
2542 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
2543 FireObserverOnScrollStart();
2544 auto onScrollStart = hub->GetOnScrollStart();
2545 CHECK_NULL_VOID(onScrollStart);
2546 ACE_SCOPED_TRACE(
2547 "OnScrollStart, id:%d, tag:%s", static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2548 onScrollStart();
2549 AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_START);
2550 }
2551
OnScrollStartCallback()2552 void ScrollablePattern::OnScrollStartCallback()
2553 {
2554 FireOnScrollStart();
2555 };
2556
FireOnScroll(float finalOffset,OnScrollEvent & onScroll) const2557 void ScrollablePattern::FireOnScroll(float finalOffset, OnScrollEvent& onScroll) const
2558 {
2559 auto offsetPX = Dimension(finalOffset);
2560 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
2561 auto scrollState = GetScrollState();
2562 bool isTriggered = false;
2563 if (!NearZero(finalOffset)) {
2564 onScroll(offsetVP, scrollState);
2565 isTriggered = true;
2566 }
2567 if (scrollStop_ && !GetScrollAbort()) {
2568 if (scrollState != ScrollState::IDLE || !isTriggered) {
2569 onScroll(0.0_vp, ScrollState::IDLE);
2570 }
2571 }
2572 }
2573
FireObserverOnTouch(const TouchEventInfo & info)2574 void ScrollablePattern::FireObserverOnTouch(const TouchEventInfo& info)
2575 {
2576 CHECK_NULL_VOID(positionController_);
2577 auto touchInfo = info;
2578 auto observer = positionController_->GetObserver();
2579 if (observer.onTouchEvent) {
2580 (*observer.onTouchEvent)(touchInfo);
2581 }
2582 auto obsMgr = positionController_->GetObserverManager();
2583 if (obsMgr) {
2584 obsMgr->HandleOnTouchEvent(touchInfo);
2585 }
2586 }
2587
FireObserverOnPanActionEnd(GestureEvent & info)2588 void ScrollablePattern::FireObserverOnPanActionEnd(GestureEvent& info)
2589 {
2590 if (positionController_) {
2591 auto observer = positionController_->GetObserver();
2592 if (observer.onPanActionEndEvent) {
2593 observer.onPanActionEndEvent(info);
2594 }
2595 }
2596 }
2597
FireObserverOnReachStart()2598 void ScrollablePattern::FireObserverOnReachStart()
2599 {
2600 CHECK_NULL_VOID(positionController_);
2601 auto observer = positionController_->GetObserver();
2602 if (observer.onReachStartEvent) {
2603 observer.onReachStartEvent();
2604 }
2605 auto obsMgr = positionController_->GetObserverManager();
2606 if (obsMgr) {
2607 obsMgr->HandleOnReachEvent(false);
2608 }
2609 }
2610
FireObserverOnReachEnd()2611 void ScrollablePattern::FireObserverOnReachEnd()
2612 {
2613 CHECK_NULL_VOID(positionController_);
2614 auto observer = positionController_->GetObserver();
2615 if (observer.onReachEndEvent) {
2616 observer.onReachEndEvent();
2617 }
2618 auto obsMgr = positionController_->GetObserverManager();
2619 if (obsMgr) {
2620 obsMgr->HandleOnReachEvent(true);
2621 }
2622 }
2623
FireObserverOnScrollStart()2624 void ScrollablePattern::FireObserverOnScrollStart()
2625 {
2626 CHECK_NULL_VOID(positionController_);
2627 auto observer = positionController_->GetObserver();
2628 if (observer.onScrollStartEvent) {
2629 observer.onScrollStartEvent();
2630 }
2631 auto obsMgr = positionController_->GetObserverManager();
2632 if (obsMgr) {
2633 obsMgr->HandleOnScrollStartEvent();
2634 }
2635 }
2636
FireObserverOnScrollStop()2637 void ScrollablePattern::FireObserverOnScrollStop()
2638 {
2639 CHECK_NULL_VOID(positionController_);
2640 auto observer = positionController_->GetObserver();
2641 if (observer.onScrollStopEvent) {
2642 observer.onScrollStopEvent();
2643 }
2644 auto obsMgr = positionController_->GetObserverManager();
2645 if (obsMgr) {
2646 obsMgr->HandleOnScrollStopEvent();
2647 }
2648 }
2649
FireObserverOnDidScroll(float finalOffset)2650 void ScrollablePattern::FireObserverOnDidScroll(float finalOffset)
2651 {
2652 OnScrollEvent onScroll = [weak = WeakClaim(this)](Dimension dimension, ScrollState state) {
2653 auto pattern = weak.Upgrade();
2654 CHECK_NULL_VOID(pattern && pattern->positionController_);
2655 auto source = pattern->ConvertScrollSource(pattern->scrollSource_);
2656 bool isAtTop = pattern->IsAtTop();
2657 bool isAtBottom = pattern->IsAtBottom();
2658 auto observer = pattern->positionController_->GetObserver();
2659 if (observer.onDidScrollEvent) {
2660 observer.onDidScrollEvent(dimension, state, source, isAtTop, isAtBottom);
2661 }
2662 auto obsMgr = pattern->positionController_->GetObserverManager();
2663 if (obsMgr) {
2664 obsMgr->HandleOnDidScrollEvent(dimension, state, source, isAtTop, isAtBottom);
2665 }
2666 };
2667 FireOnScroll(finalOffset, onScroll);
2668 }
2669
SuggestOpIncGroup(bool flag)2670 void ScrollablePattern::SuggestOpIncGroup(bool flag)
2671 {
2672 if (!SystemProperties::IsOpIncEnable()) {
2673 return;
2674 }
2675 auto host = GetHost();
2676 CHECK_NULL_VOID(host);
2677 if (host->GetSuggestOpIncActivatedOnce()) {
2678 return;
2679 }
2680 flag = flag && isVertical();
2681 if (flag) {
2682 ACE_SCOPED_TRACE("SuggestOpIncGroup %s", host->GetHostTag().c_str());
2683 auto parent = host->GetAncestorNodeOfFrame();
2684 CHECK_NULL_VOID(parent);
2685 parent->SetSuggestOpIncActivatedOnce();
2686 host->SetSuggestOpIncActivatedOnce();
2687 // get 1st layer
2688 for (auto child : host->GetAllChildren()) {
2689 if (!child) {
2690 continue;
2691 }
2692 auto frameNode = AceType::DynamicCast<FrameNode>(child);
2693 if (!frameNode || frameNode->GetSuggestOpIncActivatedOnce()) {
2694 continue;
2695 }
2696 std::string path(host->GetHostTag());
2697 frameNode->FindSuggestOpIncNode(path, host->GetGeometryNode()->GetFrameSize(), 1);
2698 }
2699 }
2700 }
2701
OnScrollStop(const OnScrollStopEvent & onScrollStop)2702 void ScrollablePattern::OnScrollStop(const OnScrollStopEvent& onScrollStop)
2703 {
2704 auto host = GetHost();
2705 CHECK_NULL_VOID(host);
2706 if (!scrollStop_) {
2707 return;
2708 }
2709 UIObserverHandler::GetInstance().NotifyScrollEventStateChange(
2710 AceType::WeakClaim(this), ScrollEventType::SCROLL_STOP);
2711 if (!GetScrollAbort()) {
2712 if (host != nullptr) {
2713 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
2714 }
2715 FireObserverOnScrollStop();
2716 if (onScrollStop) {
2717 ACE_SCOPED_TRACE("OnScrollStop, id:%d, tag:%s", static_cast<int32_t>(host->GetAccessibilityId()),
2718 host->GetTag().c_str());
2719 onScrollStop();
2720 AddEventsFiredInfo(ScrollableEventType::ON_SCROLL_STOP);
2721 SetScrollSource(SCROLL_FROM_NONE);
2722 }
2723 auto scrollBar = GetScrollBar();
2724 if (scrollBar) {
2725 scrollBar->ScheduleDisappearDelayTask();
2726 }
2727 StartScrollBarAnimatorByProxy();
2728 auto pipeline = host->GetContext();
2729 if (pipeline && pipeline->GetTaskExecutor() && pipeline->GetTHPExtraManager()) {
2730 auto taskExecutor = pipeline->GetTaskExecutor();
2731 const uint32_t delay = 100; // 100: ms
2732 taskExecutor->RemoveTask(TaskExecutor::TaskType::UI, "NotifyResponseRegionChanged");
2733 auto task = [weak = WeakClaim(pipeline)]() {
2734 auto pipeline = weak.Upgrade();
2735 CHECK_NULL_VOID(pipeline);
2736 pipeline->NotifyResponseRegionChanged(pipeline->GetRootElement());
2737 };
2738 taskExecutor->PostDelayedTask(task, TaskExecutor::TaskType::UI, delay, "NotifyResponseRegionChanged");
2739 }
2740 } else {
2741 ACE_SCOPED_TRACE("ScrollAbort, no OnScrollStop, id:%d, tag:%s",
2742 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
2743 }
2744 PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
2745 AceAsyncTraceEnd(
2746 0, (TRAILING_ANIMATION + std::to_string(host->GetAccessibilityId()) + std::string(" ") + host->GetTag())
2747 .c_str());
2748 scrollStop_ = false;
2749 SetScrollAbort(false);
2750 }
2751
FireOnWillScroll(float offset) const2752 float ScrollablePattern::FireOnWillScroll(float offset) const
2753 {
2754 auto eventHub = GetEventHub<ScrollableEventHub>();
2755 CHECK_NULL_RETURN(eventHub, offset);
2756 auto onScroll = eventHub->GetOnWillScroll();
2757 CHECK_NULL_RETURN(onScroll, offset);
2758 auto offsetPX = Dimension(offset);
2759 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
2760 auto scrollRes = onScroll(offsetVP, GetScrollState(), ConvertScrollSource(scrollSource_));
2761 auto context = PipelineContext::GetCurrentContextSafely();
2762 CHECK_NULL_RETURN(context, offset);
2763 return context->NormalizeToPx(scrollRes.offset);
2764 }
2765
2766 /**
2767 * @description: Register with the drag drop manager
2768 * @return None
2769 */
Register2DragDropManager()2770 void ScrollablePattern::Register2DragDropManager()
2771 {
2772 auto host = GetHost();
2773 CHECK_NULL_VOID(host);
2774 auto pipeline = PipelineContext::GetCurrentContext();
2775 CHECK_NULL_VOID(pipeline);
2776 auto dragDropManager = pipeline->GetDragDropManager();
2777 CHECK_NULL_VOID(dragDropManager);
2778 dragDropManager->RegisterDragStatusListener(host->GetId(), AceType::WeakClaim(AceType::RawPtr(host)));
2779 }
2780
2781 /**
2782 * @description: Determine whether it is in the hot zone, then
2783 * 1.Gives the rolling direction according to the location of the hot zone
2784 * 2.Gives the distance from the edge of the hot zone from the drag point
2785 * @param {PointF&} point The drag point
2786 * @return The distance from the edge of the hot zone from the drag point.scroll up:Offset percent is positive, scroll
2787 * down:Offset percent is negative
2788 */
IsInHotZone(const PointF & point)2789 float ScrollablePattern::IsInHotZone(const PointF& point)
2790 {
2791 auto host = GetHost();
2792 auto offset = 0.f;
2793 auto geometryNode = host->GetGeometryNode();
2794 CHECK_NULL_RETURN(geometryNode, 0.f);
2795
2796 auto wholeRect = geometryNode->GetFrameRect();
2797 wholeRect.SetOffset(host->GetTransformRelativeOffset());
2798 auto hotZoneHeightPX = HOT_ZONE_HEIGHT_VP_DIM.ConvertToPx();
2799 auto hotZoneWidthPX = HOT_ZONE_WIDTH_VP_DIM.ConvertToPx();
2800 if (isVertical()) {
2801 // create top hot zone,it is a rectangle
2802 auto topHotzone = wholeRect;
2803 topHotzone.SetHeight(hotZoneHeightPX);
2804
2805 // create bottom hot zone,it is a rectangle
2806 auto bottomHotzone = wholeRect;
2807 auto bottomZoneEdgeY = wholeRect.GetY() + wholeRect.Height();
2808 bottomHotzone.SetTop(bottomZoneEdgeY - hotZoneHeightPX);
2809 bottomHotzone.SetHeight(hotZoneHeightPX);
2810
2811 // Determines whether the drag point is within the hot zone,
2812 // then gives the scroll component movement direction according to which hot zone the point is in
2813 // top or bottom hot zone
2814 if (topHotzone.IsInRegion(point)) {
2815 offset = hotZoneHeightPX - point.GetY() + topHotzone.GetY();
2816 if (!NearZero(hotZoneHeightPX)) {
2817 return offset / hotZoneHeightPX;
2818 }
2819 } else if (bottomHotzone.IsInRegion(point)) {
2820 offset = bottomZoneEdgeY - point.GetY() - hotZoneHeightPX;
2821 if (!NearZero(hotZoneHeightPX)) {
2822 return offset / hotZoneHeightPX;
2823 }
2824 }
2825 } else {
2826 auto leftHotzone = wholeRect;
2827
2828 // create left hot zone,it is a rectangle
2829 leftHotzone.SetWidth(hotZoneWidthPX);
2830
2831 // create right hot zone,it is a rectangle
2832 auto rightHotzone = wholeRect;
2833 rightHotzone.SetWidth(hotZoneWidthPX);
2834 auto rightZoneEdgeX = wholeRect.GetX() + wholeRect.Width();
2835 rightHotzone.SetLeft(rightZoneEdgeX - hotZoneWidthPX);
2836
2837 // Determines whether the drag point is within the hot zone,
2838 // gives the scroll component movement direction according to which hot zone the point is in
2839 // left or right hot zone
2840 if (leftHotzone.IsInRegion(point)) {
2841 offset = hotZoneWidthPX - point.GetX() + wholeRect.GetX();
2842 if (!NearZero(hotZoneWidthPX)) {
2843 return offset / hotZoneWidthPX;
2844 }
2845 } else if (rightHotzone.IsInRegion(point)) {
2846 offset = rightZoneEdgeX - point.GetX() - hotZoneWidthPX;
2847 if (!NearZero(hotZoneWidthPX)) {
2848 return offset / hotZoneWidthPX;
2849 }
2850 }
2851 }
2852
2853 return 0.f;
2854 }
2855
2856 /**
2857 * @description: Determines whether the scroll component is in the vertical direction
2858 * @return True,If the scrolling component is vertical
2859 */
isVertical() const2860 bool ScrollablePattern::isVertical() const
2861 {
2862 return axis_ == Axis::VERTICAL;
2863 }
2864
2865 /**
2866 * @description: scroll up or down
2867 * @param {float} offsetPct offset percent.When scrolling in the vertical or horizontal direction, there is a distance
2868 * between the drag point and the outer edge of the hot zone, and the percentage represents the proportion of this
2869 * distance to the height or width of the hot zone
2870 * @return None
2871 */
HotZoneScroll(const float offsetPct)2872 void ScrollablePattern::HotZoneScroll(const float offsetPct)
2873 {
2874 auto host = GetHost();
2875 CHECK_NULL_VOID(IsScrollable());
2876 CHECK_NULL_VOID(!NearZero(offsetPct));
2877
2878 // There are three types of situations to consider.
2879 // 1. Enter the hot zone for the first time.
2880 // 2. When the drag point leaves the hot zone, it enters the hot zone again
2881 // 3. When the drag point moves within the hot zone, the hot zone offset changes
2882 CHECK_NULL_VOID(!NearEqual(lastHonezoneOffsetPct_, offsetPct));
2883
2884 if (AnimateRunning()) {
2885 // Variable speed rolling
2886 // When the drag point is in the hot zone, and the hot zone offset changes.
2887 // Then need to modify the offset percent
2888 if (velocityMotion_) {
2889 velocityMotion_->Reset(offsetPct);
2890 }
2891 return;
2892 }
2893
2894 if (!animator_) {
2895 animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
2896 animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
2897 auto pattern = weak.Upgrade();
2898 CHECK_NULL_VOID(pattern);
2899 pattern->OnAnimateStop();
2900 });
2901 }
2902
2903 if (!velocityMotion_) {
2904 // Enter the hot zone for the first time.
2905 velocityMotion_ = AceType::MakeRefPtr<BezierVariableVelocityMotion>(
2906 offsetPct, [weak = WeakClaim(this)](float offset) -> bool {
2907 auto pattern = weak.Upgrade();
2908 CHECK_NULL_RETURN(pattern, true);
2909 // Stop scrolling when reach the bottom or top
2910 return ((LessNotEqual(offset, 0) && pattern->IsAtBottom()) ||
2911 (GreatNotEqual(offset, 0) && pattern->IsAtTop()));
2912 });
2913 velocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
2914 // Get the distance component need to roll from BezierVariableVelocityMotion
2915 // Roll up: negative value, Roll up: positive value
2916 auto pattern = weakScroll.Upgrade();
2917 CHECK_NULL_VOID(pattern);
2918 pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
2919 pattern->UpdateMouseStart(offset);
2920 if (pattern->hotZoneScrollCallback_) {
2921 pattern->hotZoneScrollCallback_();
2922 }
2923 });
2924 velocityMotion_->ReInit(offsetPct);
2925 } else {
2926 // When the drag point leaves the hot zone, it enters the hot zone again.Then need to reset offset percent.
2927 velocityMotion_->ReInit(offsetPct);
2928 }
2929 // Save the last offset percent
2930 lastHonezoneOffsetPct_ = offsetPct;
2931 animator_->PlayMotion(velocityMotion_);
2932 FireOnScrollStart();
2933 }
2934
2935 /**
2936 * @description: When the drag point leaves the hot zone, stop the animation.
2937 * @return None
2938 */
StopHotzoneScroll()2939 void ScrollablePattern::StopHotzoneScroll()
2940 {
2941 if (!AnimateStoped()) {
2942 animator_->Stop();
2943 }
2944 }
2945
2946 /**
2947 * @description: Handle drag and drop events
2948 * When a drag point enters or moves over a component, determine whether it is within the hot zone.
2949 * When leave the component, stop scrolling
2950 * @param {DragEventType&} dragEventType Drag the event type
2951 * @param {NotifyDragEvent&} notifyDragEvent Drag event
2952 * @return None
2953 */
HandleHotZone(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2954 void ScrollablePattern::HandleHotZone(
2955 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2956 {
2957 // The starting version of the auto-scroll feature is 11
2958 CHECK_NULL_VOID(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN));
2959 PointF point(static_cast<float>(notifyDragEvent->GetX()), static_cast<float>(notifyDragEvent->GetY()));
2960 switch (dragEventType) {
2961 case DragEventType::ENTER: {
2962 HandleMoveEventInComp(point);
2963 break;
2964 }
2965 case DragEventType::MOVE: {
2966 HandleMoveEventInComp(point);
2967 break;
2968 }
2969 case DragEventType::DROP:
2970 case DragEventType::LEAVE: {
2971 HandleLeaveHotzoneEvent();
2972 break;
2973 }
2974 default:
2975 break;
2976 }
2977 }
2978
2979 /**
2980 * @description:When a drag point is inside the scroll component, it is necessary to handle the events of each moving
2981 * point
2982 * @param {PointF&} point the drag point
2983 * @return None
2984 */
HandleMoveEventInComp(const PointF & point)2985 void ScrollablePattern::HandleMoveEventInComp(const PointF& point)
2986 {
2987 float offsetPct = IsInHotZone(point);
2988 if ((Positive(offsetPct) && !IsAtTop()) || (Negative(offsetPct) && !IsAtBottom())) {
2989 // The drag point enters the hot zone
2990 HotZoneScroll(offsetPct);
2991 } else {
2992 // Although it entered the rolling component, it is not in the rolling component hot zone.Then stop
2993 // scrolling
2994 HandleLeaveHotzoneEvent();
2995 }
2996 }
2997
2998 /**
2999 * @description:When the drag point is not in the hot zone, need to stop scrolling, if it exists.
3000 * This function is executed multiple times
3001 * @return None
3002 */
HandleLeaveHotzoneEvent()3003 void ScrollablePattern::HandleLeaveHotzoneEvent()
3004 {
3005 // Stop scrolling up and down
3006 StopHotzoneScroll();
3007 }
3008
3009 /**
3010 * @description: This is the entry point for handling drag events
3011 * @return None
3012 */
HandleOnDragStatusCallback(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)3013 void ScrollablePattern::HandleOnDragStatusCallback(
3014 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
3015 {
3016 HandleHotZone(dragEventType, notifyDragEvent);
3017 }
3018
3019 /**
3020 * @description: Cancel registration with the drag drop manager
3021 * @return None
3022 */
UnRegister2DragDropManager()3023 void ScrollablePattern::UnRegister2DragDropManager()
3024 {
3025 auto host = GetHost();
3026 CHECK_NULL_VOID(host);
3027 auto pipeline = PipelineContext::GetCurrentContext();
3028 CHECK_NULL_VOID(pipeline);
3029 auto dragDropManager = pipeline->GetDragDropManager();
3030 CHECK_NULL_VOID(dragDropManager);
3031 dragDropManager->UnRegisterDragStatusListener(host->GetId());
3032 }
3033
NeedCoordinateScrollWithNavigation(double offset,int32_t source,const OverScrollOffset & overOffsets)3034 bool ScrollablePattern::NeedCoordinateScrollWithNavigation(
3035 double offset, int32_t source, const OverScrollOffset& overOffsets)
3036 {
3037 if (!navBarPattern_) {
3038 return false;
3039 }
3040 return (GreatNotEqual(overOffsets.start, 0.0) || navBarPattern_->CanCoordScrollUp(offset)) &&
3041 (axis_ == Axis::VERTICAL) && (source != SCROLL_FROM_ANIMATION_SPRING);
3042 }
3043
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)3044 void ScrollablePattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
3045 {
3046 float distance = reverse ? GetMainContentSize() : -GetMainContentSize();
3047 if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
3048 distance = distance / 2.f;
3049 }
3050 if (smooth) {
3051 float position = -GetTotalOffset() + distance;
3052 AnimateTo(-position, -1, nullptr, true);
3053 } else {
3054 StopAnimate();
3055 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
3056 }
3057 }
3058
OnCollectClickTarget(const OffsetF & coordinateOffset,const GetEventTargetImpl & getEventTargetImpl,TouchTestResult & result,const RefPtr<FrameNode> & frameNode,const RefPtr<TargetComponent> & targetComponent,ResponseLinkResult & responseLinkResult)3059 void ScrollablePattern::OnCollectClickTarget(const OffsetF& coordinateOffset,
3060 const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
3061 const RefPtr<TargetComponent>& targetComponent, ResponseLinkResult& responseLinkResult)
3062 {
3063 CHECK_NULL_VOID(GetScrollBar());
3064 if (clickRecognizer_) {
3065 clickRecognizer_->SetCoordinateOffset(Offset(coordinateOffset.GetX(), coordinateOffset.GetY()));
3066 clickRecognizer_->SetGetEventTargetImpl(getEventTargetImpl);
3067 clickRecognizer_->SetNodeId(frameNode->GetId());
3068 clickRecognizer_->AttachFrameNode(frameNode);
3069 clickRecognizer_->SetTargetComponent(targetComponent);
3070 clickRecognizer_->SetIsSystemGesture(true);
3071 clickRecognizer_->SetRecognizerType(GestureTypeName::CLICK);
3072 clickRecognizer_->SetSysGestureJudge(
3073 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_)), this](const RefPtr<GestureInfo>& gestureInfo,
3074 const std::shared_ptr<BaseGestureEvent>&) -> GestureJudgeResult {
3075 const auto& inputEventType = gestureInfo->GetInputEventType();
3076 TAG_LOGI(AceLogTag::ACE_SCROLL_BAR, "input event type:%{public}d", inputEventType);
3077 auto scrollBar = weak.Upgrade();
3078 CHECK_NULL_RETURN(scrollBar, GestureJudgeResult::REJECT);
3079 Point point(locationInfo_.GetX(), locationInfo_.GetY());
3080 if (inputEventType == InputEventType::MOUSE_BUTTON && scrollBar->InBarRectRegion(point)) {
3081 return GestureJudgeResult::CONTINUE;
3082 }
3083 return GestureJudgeResult::REJECT;
3084 });
3085 result.emplace_front(clickRecognizer_);
3086 responseLinkResult.emplace_back(clickRecognizer_);
3087 }
3088 }
3089
InitScrollBarClickEvent()3090 void ScrollablePattern::InitScrollBarClickEvent()
3091 {
3092 clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
3093 clickRecognizer_->SetOnClick([weakBar = AceType::WeakClaim(this)](const ClickInfo&) {
3094 auto scrollBar = weakBar.Upgrade();
3095 if (scrollBar) {
3096 scrollBar->HandleClickEvent();
3097 }
3098 });
3099 }
3100
HandleClickEvent()3101 void ScrollablePattern::HandleClickEvent()
3102 {
3103 auto host = GetHost();
3104 CHECK_NULL_VOID(host);
3105 CHECK_NULL_VOID(GetScrollBar());
3106 Point point(locationInfo_.GetX(), locationInfo_.GetY());
3107 bool reverse = false;
3108 if (scrollBar_->AnalysisUpOrDown(point, reverse) && isMousePressed_) {
3109 ScrollPage(reverse, true);
3110 }
3111 }
3112
InitScrollBarMouseEvent()3113 void ScrollablePattern::InitScrollBarMouseEvent()
3114 {
3115 CHECK_NULL_VOID(!mouseEvent_);
3116 auto host = GetHost();
3117 CHECK_NULL_VOID(host);
3118 auto inputHub = GetInputHub();
3119 CHECK_NULL_VOID(inputHub);
3120 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
3121 auto pattern = weak.Upgrade();
3122 CHECK_NULL_VOID(pattern);
3123 if (info.GetButton() == MouseButton::LEFT_BUTTON &&
3124 (info.GetAction() == MouseAction::PRESS || info.GetAction() == MouseAction::MOVE)) {
3125 pattern->isMousePressed_ = true;
3126 } else {
3127 pattern->isMousePressed_ = false;
3128 }
3129 pattern->locationInfo_ = info.GetLocalLocation();
3130 };
3131 mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
3132 inputHub->AddOnMouseEvent(mouseEvent_);
3133 }
3134
PrintOffsetLog(AceLogTag tag,int32_t id,double finalOffset)3135 void ScrollablePattern::PrintOffsetLog(AceLogTag tag, int32_t id, double finalOffset)
3136 {
3137 if (SystemProperties::GetDebugOffsetLogEnabled() && !NearZero(finalOffset)) {
3138 TAG_LOGD(tag, "Scrollable id:%{public}d, scrollSource:%{public}d, scrollOffset:%{public}f",
3139 id, scrollSource_, finalOffset);
3140 }
3141 }
3142
CheckRestartSpring(bool sizeDiminished,bool needNestedScrolling)3143 void ScrollablePattern::CheckRestartSpring(bool sizeDiminished, bool needNestedScrolling)
3144 {
3145 auto host = GetHost();
3146 CHECK_NULL_VOID(host);
3147 auto edgeEffect = GetScrollEdgeEffect();
3148 if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
3149 return;
3150 }
3151 // Check if need update Spring when itemTotalSize diminishes.
3152 if (IsScrollableSpringMotionRunning() && sizeDiminished) {
3153 ACE_SCOPED_TRACE("CheckRestartSpring, do ProcessSpringUpdate, id:%d, tag:%s",
3154 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
3155 edgeEffect->ProcessSpringUpdate();
3156 return;
3157 }
3158 if (AnimateRunning() || !IsOutOfBoundary()) {
3159 return;
3160 }
3161 if (needNestedScrolling && !ScrollableIdle()) {
3162 return;
3163 } else if (!needNestedScrolling && !IsScrollableAnimationNotRunning()) {
3164 return;
3165 }
3166 FireOnScrollStart();
3167 ACE_SCOPED_TRACE("CheckRestartSpring, do ProcessScrollOver, id:%d, tag:%s",
3168 static_cast<int32_t>(host->GetAccessibilityId()), host->GetTag().c_str());
3169 edgeEffect->ProcessScrollOver(0);
3170 }
3171
AddEventsFiredInfo(ScrollableEventType eventType)3172 void ScrollablePattern::AddEventsFiredInfo(ScrollableEventType eventType)
3173 {
3174 if (eventsFiredInfos_.size() >= EVENTS_FIRED_INFO_COUNT) {
3175 eventsFiredInfos_.pop_front();
3176 }
3177 eventsFiredInfos_.push_back(ScrollableEventsFiredInfo({
3178 .eventFiredTime_ = GetSysTimestamp(),
3179 .eventType_ = eventType,
3180 .scrollSource_ = scrollSource_,
3181 }));
3182 }
3183
AddScrollableFrameInfo(int32_t scrollSource)3184 void ScrollablePattern::AddScrollableFrameInfo(int32_t scrollSource)
3185 {
3186 if (scrollableFrameInfos_.size() >= SCROLLABLE_FRAME_INFO_COUNT) {
3187 scrollableFrameInfos_.pop_front();
3188 }
3189 uint32_t canOverScrollInfo = IsScrollableSpringEffect();
3190 canOverScrollInfo = (canOverScrollInfo << 1) | IsScrollable();
3191 canOverScrollInfo = (canOverScrollInfo << 1) | ScrollableIdle();
3192 canOverScrollInfo = (canOverScrollInfo << 1) | animateOverScroll_;
3193 canOverScrollInfo = (canOverScrollInfo << 1) | animateCanOverScroll_;
3194 scrollableFrameInfos_.push_back(ScrollableFrameInfo({
3195 .scrollStateTime_ = GetSysTimestamp(),
3196 .scrollState_ = scrollSource,
3197 .canOverScroll_ = lastCanOverScroll_,
3198 .canOverScrollInfo_ = canOverScrollInfo,
3199 }));
3200 }
3201
GetEdgeEffectDumpInfo()3202 void ScrollablePattern::GetEdgeEffectDumpInfo()
3203 {
3204 switch (edgeEffect_) {
3205 case EdgeEffect::NONE: {
3206 DumpLog::GetInstance().AddDesc("edgeEffect: NONE");
3207 break;
3208 }
3209 case EdgeEffect::SPRING: {
3210 DumpLog::GetInstance().AddDesc("edgeEffect: SPRING");
3211 break;
3212 }
3213 case EdgeEffect::FADE: {
3214 DumpLog::GetInstance().AddDesc("edgeEffect: FADE");
3215 break;
3216 }
3217 default: {
3218 break;
3219 }
3220 }
3221 }
3222
GetAxisDumpInfo()3223 void ScrollablePattern::GetAxisDumpInfo()
3224 {
3225 switch (axis_) {
3226 case Axis::NONE: {
3227 DumpLog::GetInstance().AddDesc("Axis: NONE");
3228 break;
3229 }
3230 case Axis::VERTICAL: {
3231 DumpLog::GetInstance().AddDesc("Axis: VERTICAL");
3232 break;
3233 }
3234 case Axis::HORIZONTAL: {
3235 DumpLog::GetInstance().AddDesc("Axis: HORIZONTAL");
3236 break;
3237 }
3238 case Axis::FREE: {
3239 DumpLog::GetInstance().AddDesc("Axis: FREE");
3240 break;
3241 }
3242 default: {
3243 break;
3244 }
3245 }
3246 }
3247
GetPanDirectionDumpInfo()3248 void ScrollablePattern::GetPanDirectionDumpInfo()
3249 {
3250 switch (GetScrollablePanDirection()) {
3251 case Axis::NONE: {
3252 DumpLog::GetInstance().AddDesc("ScrollablePanDirection:NONE");
3253 break;
3254 }
3255 case Axis::VERTICAL: {
3256 DumpLog::GetInstance().AddDesc("ScrollablePanDirection:VERTICAL");
3257 break;
3258 }
3259 case Axis::HORIZONTAL: {
3260 DumpLog::GetInstance().AddDesc("ScrollablePanDirection:HORIZONTAL");
3261 break;
3262 }
3263 case Axis::FREE: {
3264 DumpLog::GetInstance().AddDesc("ScrollablePanDirection:FREE");
3265 break;
3266 }
3267 default: {
3268 DumpLog::GetInstance().AddDesc("ScrollablePanDirection is null");
3269 break;
3270 }
3271 }
3272 }
3273
GetPaintPropertyDumpInfo()3274 void ScrollablePattern::GetPaintPropertyDumpInfo()
3275 {
3276 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3277 if (paintProperty) {
3278 switch (paintProperty->GetScrollBarMode().value_or(DisplayMode::OFF)) {
3279 case DisplayMode::OFF: {
3280 DumpLog::GetInstance().AddDesc("innerScrollBarState: OFF");
3281 break;
3282 }
3283 case DisplayMode::AUTO: {
3284 DumpLog::GetInstance().AddDesc("innerScrollBarState: AUTO");
3285 break;
3286 }
3287 case DisplayMode::ON: {
3288 DumpLog::GetInstance().AddDesc("innerScrollBarState: ON");
3289 break;
3290 }
3291 default: {
3292 break;
3293 }
3294 }
3295 auto scrollBarWidth = paintProperty->GetScrollBarWidth();
3296 scrollBarWidth.has_value() ? DumpLog::GetInstance().AddDesc(std::string("scrollBarWidth: ")
3297 .append(paintProperty->GetScrollBarWidth().value().ToString()))
3298 : DumpLog::GetInstance().AddDesc("scrollBarWidth: None");
3299 }
3300 }
3301
GetEventDumpInfo()3302 void ScrollablePattern::GetEventDumpInfo()
3303 {
3304 auto host = GetHost();
3305 CHECK_NULL_VOID(host);
3306 auto hub = host->GetEventHub<ScrollableEventHub>();
3307 CHECK_NULL_VOID(hub);
3308 auto onScrollStart = hub->GetOnScrollStart();
3309 onScrollStart ? DumpLog::GetInstance().AddDesc("hasOnScrollStart: true")
3310 : DumpLog::GetInstance().AddDesc("hasOnScrollStart: false");
3311 auto onScrollStop = hub->GetOnScrollStop();
3312 onScrollStop ? DumpLog::GetInstance().AddDesc("hasOnScrollStop: true")
3313 : DumpLog::GetInstance().AddDesc("hasOnScrollStop: false");
3314 auto scrollHub = host->GetEventHub<ScrollEventHub>();
3315 if (scrollHub) {
3316 auto onWillScroll = scrollHub->GetOnWillScrollEvent();
3317 onWillScroll ? DumpLog::GetInstance().AddDesc("hasOnWillScroll: true")
3318 : DumpLog::GetInstance().AddDesc("hasOnWillScroll: false");
3319 auto onDidScroll = scrollHub->GetOnDidScrollEvent();
3320 onDidScroll ? DumpLog::GetInstance().AddDesc("hasOnDidScroll: true")
3321 : DumpLog::GetInstance().AddDesc("hasOnDidScroll: false");
3322 } else {
3323 auto onWillScroll = hub->GetOnWillScroll();
3324 onWillScroll ? DumpLog::GetInstance().AddDesc("hasOnWillScroll: true")
3325 : DumpLog::GetInstance().AddDesc("hasOnWillScroll: false");
3326 auto onDidScroll = hub->GetOnDidScroll();
3327 onDidScroll ? DumpLog::GetInstance().AddDesc("hasOnDidScroll: true")
3328 : DumpLog::GetInstance().AddDesc("hasOnDidScroll: false");
3329 }
3330 auto onScrollFrameBegin = hub->GetOnScrollFrameBegin();
3331 onScrollFrameBegin ? DumpLog::GetInstance().AddDesc("hasOnScrollFrameBegin: true")
3332 : DumpLog::GetInstance().AddDesc("hasOnScrollFrameBegin: false");
3333 auto onReachStart = hub->GetOnReachStart();
3334 onReachStart ? DumpLog::GetInstance().AddDesc("hasOnReachStart: true")
3335 : DumpLog::GetInstance().AddDesc("hasOnReachStart: false");
3336 auto onReachEnd = hub->GetOnReachEnd();
3337 onReachEnd ? DumpLog::GetInstance().AddDesc("hasOnReachEnd: true")
3338 : DumpLog::GetInstance().AddDesc("hasOnReachEnd: false");
3339 }
3340
DumpAdvanceInfo()3341 void ScrollablePattern::DumpAdvanceInfo()
3342 {
3343 GetEdgeEffectDumpInfo();
3344 edgeEffectAlwaysEnabled_ ? DumpLog::GetInstance().AddDesc("edgeEffectAlwaysEnabled: true")
3345 : DumpLog::GetInstance().AddDesc("edgeEffectAlwaysEnabled: false");
3346 IsScrollable() ? DumpLog::GetInstance().AddDesc("isScrollable: true")
3347 : DumpLog::GetInstance().AddDesc("isScrollable: false");
3348 GetEventDumpInfo();
3349 DumpLog::GetInstance().AddDesc(GetNestedScroll().ToString().c_str());
3350 GetIsSearchRefresh() ? DumpLog::GetInstance().AddDesc(std::string("isSearchRefresh: true"))
3351 : DumpLog::GetInstance().AddDesc(std::string("isSearchRefresh: false"));
3352 GetIsFixedNestedScrollMode() ? DumpLog::GetInstance().AddDesc(std::string("isFixedNestedScrollMode: true"))
3353 : DumpLog::GetInstance().AddDesc(std::string("isFixedNestedScrollMode: false"));
3354 auto parent = GetNestedScrollParent();
3355 parent && parent->GetHost() ? DumpLog::GetInstance().AddDesc(std::string("nestedScrollParent id: ")
3356 .append(std::to_string(parent->GetHost()->GetId()))
3357 .append(" tag: ")
3358 .append(parent->GetHost()->GetTag()))
3359 : DumpLog::GetInstance().AddDesc("nestedScrollParent is null");
3360 GetAxisDumpInfo();
3361 GetPanDirectionDumpInfo();
3362 GetPaintPropertyDumpInfo();
3363 GetScrollEnabled() ? DumpLog::GetInstance().AddDesc("enableScrollInteraction: true")
3364 : DumpLog::GetInstance().AddDesc("enableScrollInteraction: false");
3365 DumpLog::GetInstance().AddDesc(std::string("friction: ").append(std::to_string(friction_)));
3366 DumpLog::GetInstance().AddDesc(std::string("flingSpeedLimit: ").append(std::to_string(GetMaxFlingVelocity())));
3367 DumpLog::GetInstance().AddDesc("==========================eventsFiredInfos==============================");
3368 for (const auto& info : eventsFiredInfos_) {
3369 DumpLog::GetInstance().AddDesc(info.ToString());
3370 }
3371 DumpLog::GetInstance().AddDesc("==========================eventsFiredInfos==============================");
3372 DumpLog::GetInstance().AddDesc("==========================scrollableFrameInfos==========================");
3373 for (const auto& info : scrollableFrameInfos_) {
3374 DumpLog::GetInstance().AddDesc(info.ToString());
3375 }
3376 DumpLog::GetInstance().AddDesc("==========================scrollableFrameInfos==========================");
3377 DumpLog::GetInstance().AddDesc("==========================inner ScrollBar===============================");
3378 if (scrollBar_) {
3379 scrollBar_->DumpAdvanceInfo();
3380 } else {
3381 DumpLog::GetInstance().AddDesc("inner ScrollBar is null");
3382 }
3383 DumpLog::GetInstance().AddDesc("==========================inner ScrollBar===============================");
3384 }
3385
SetAccessibilityAction()3386 void ScrollablePattern::SetAccessibilityAction()
3387 {
3388 auto host = GetHost();
3389 CHECK_NULL_VOID(host);
3390 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3391 CHECK_NULL_VOID(accessibilityProperty);
3392 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
3393 const auto& pattern = weakPtr.Upgrade();
3394 CHECK_NULL_VOID(pattern);
3395 auto host = pattern->GetHost();
3396 CHECK_NULL_VOID(host);
3397 ACE_SCOPED_TRACE("accessibility action, scroll forward, isScrollable:%u, scrollType:%d, id:%d, tag:%s",
3398 pattern->IsScrollable(), scrollType, static_cast<int32_t>(host->GetAccessibilityId()),
3399 host->GetTag().c_str());
3400 CHECK_NULL_VOID(pattern->IsScrollable());
3401 pattern->ScrollPage(false, true, scrollType);
3402 });
3403
3404 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)](AccessibilityScrollType scrollType) {
3405 const auto& pattern = weakPtr.Upgrade();
3406 CHECK_NULL_VOID(pattern);
3407 auto host = pattern->GetHost();
3408 CHECK_NULL_VOID(host);
3409 ACE_SCOPED_TRACE("accessibility action, scroll backward, isScrollable:%u, scrollType:%d, id:%d, tag:%s",
3410 pattern->IsScrollable(), scrollType, static_cast<int32_t>(host->GetAccessibilityId()),
3411 host->GetTag().c_str());
3412 CHECK_NULL_VOID(pattern->IsScrollable());
3413 pattern->ScrollPage(true, true, scrollType);
3414 });
3415 }
3416
ScrollAtFixedVelocity(float velocity)3417 void ScrollablePattern::ScrollAtFixedVelocity(float velocity)
3418 {
3419 auto host = GetHost();
3420 CHECK_NULL_VOID(host);
3421 if (AnimateRunning()) {
3422 StopAnimate();
3423 }
3424
3425 if (!animator_) {
3426 animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
3427 animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
3428 auto pattern = weak.Upgrade();
3429 CHECK_NULL_VOID(pattern);
3430 pattern->OnAnimateStop();
3431 auto host = pattern->GetHost();
3432 CHECK_NULL_VOID(host);
3433 AceAsyncTraceEnd(
3434 host->GetId(), (SCROLLER_FIX_VELOCITY_ANIMATION + std::to_string(host->GetAccessibilityId()) +
3435 std::string(" ") + host->GetTag()).c_str());
3436 });
3437 }
3438
3439 if (!fixedVelocityMotion_) {
3440 fixedVelocityMotion_ = AceType::MakeRefPtr<VelocityMotion>([weak = WeakClaim(this)](float offset) -> bool {
3441 auto pattern = weak.Upgrade();
3442 CHECK_NULL_RETURN(pattern, true);
3443 if (LessNotEqual(offset, 0) && pattern->IsAtBottom()) {
3444 // Stop scrolling when reach the bottom
3445 pattern->fixedVelocityMotion_->Init();
3446 return true;
3447 } else if (GreatNotEqual(offset, 0) && pattern->IsAtTop()) {
3448 // Stop scrolling when reach the top
3449 pattern->fixedVelocityMotion_->Init();
3450 return true;
3451 }
3452 return false;
3453 });
3454 fixedVelocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
3455 auto pattern = weakScroll.Upgrade();
3456 CHECK_NULL_VOID(pattern);
3457 pattern->UpdateCurrentOffset(offset, SCROLL_FROM_ANIMATION_CONTROLLER);
3458 });
3459 fixedVelocityMotion_->SetVelocity(velocity);
3460 } else {
3461 fixedVelocityMotion_->Init();
3462 fixedVelocityMotion_->SetVelocity(velocity);
3463 }
3464 AceAsyncTraceBegin(host->GetId(), (SCROLLER_FIX_VELOCITY_ANIMATION + std::to_string(host->GetAccessibilityId()) +
3465 std::string(" ") + host->GetTag()).c_str());
3466 animator_->PlayMotion(fixedVelocityMotion_);
3467 FireOnScrollStart();
3468 }
3469
GetPositionMode()3470 PositionMode ScrollablePattern::GetPositionMode()
3471 {
3472 auto host = GetHost();
3473 CHECK_NULL_RETURN(host, PositionMode::RIGHT);
3474 auto positionMode = PositionMode::RIGHT;
3475 if (axis_ == Axis::HORIZONTAL) {
3476 positionMode = PositionMode::BOTTOM;
3477 } else {
3478 auto isRtl = host->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
3479 if (isRtl) {
3480 positionMode = PositionMode::LEFT;
3481 }
3482 }
3483 return positionMode;
3484 }
3485
CheckScrollBarOff()3486 void ScrollablePattern::CheckScrollBarOff()
3487 {
3488 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3489 CHECK_NULL_VOID(paintProperty);
3490 auto displayMode = paintProperty->GetScrollBarMode().value_or(GetDefaultScrollBarDisplayMode());
3491 if (displayMode == DisplayMode::OFF) {
3492 SetScrollBar(DisplayMode::OFF);
3493 }
3494 }
3495
UpdateNestedScrollVelocity(float offset,NestedState state)3496 void ScrollablePattern::UpdateNestedScrollVelocity(float offset, NestedState state)
3497 {
3498 if (state == NestedState::GESTURE) {
3499 return;
3500 }
3501 auto pipeline = GetContext();
3502 CHECK_NULL_VOID(pipeline);
3503 uint64_t currentVsync = pipeline->GetVsyncTime();
3504 uint64_t diff = currentVsync - nestedScrollTimestamp_;
3505 if (diff >= MAX_VSYNC_DIFF_TIME || diff <= MIN_DIFF_VSYNC) {
3506 diff = DEFAULT_VSYNC_DIFF_TIME;
3507 }
3508 nestedScrollVelocity_ = (offset / diff) * MILLOS_PER_NANO_SECONDS;
3509 nestedScrollTimestamp_ = currentVsync;
3510 }
3511
GetNestedScrollVelocity()3512 float ScrollablePattern::GetNestedScrollVelocity()
3513 {
3514 if (NearZero(nestedScrollVelocity_)) {
3515 return 0.0f;
3516 }
3517 uint64_t currentVsync = static_cast<uint64_t>(GetSysTimestamp());
3518 uint64_t diff = currentVsync > nestedScrollTimestamp_ ? currentVsync - nestedScrollTimestamp_ : 0;
3519 if (diff >= MAX_VSYNC_DIFF_TIME) {
3520 nestedScrollVelocity_ = 0.0f;
3521 }
3522 return nestedScrollVelocity_;
3523 }
3524
AddNestScrollBarProxy(const WeakPtr<ScrollBarProxy> & scrollBarProxy)3525 void ScrollablePattern::AddNestScrollBarProxy(const WeakPtr<ScrollBarProxy>& scrollBarProxy)
3526 {
3527 if (std::find(nestScrollBarProxy_.begin(), nestScrollBarProxy_.end(), scrollBarProxy) !=
3528 nestScrollBarProxy_.end()) {
3529 return;
3530 }
3531 nestScrollBarProxy_.emplace_back(scrollBarProxy);
3532 }
3533
DeleteNestScrollBarProxy(const WeakPtr<ScrollBarProxy> & scrollBarProxy)3534 void ScrollablePattern::DeleteNestScrollBarProxy(const WeakPtr<ScrollBarProxy>& scrollBarProxy)
3535 {
3536 auto iter = std::find(nestScrollBarProxy_.begin(), nestScrollBarProxy_.end(), scrollBarProxy);
3537 if (iter != nestScrollBarProxy_.end()) {
3538 nestScrollBarProxy_.erase(iter);
3539 }
3540 }
3541
SetParentNestedScroll(RefPtr<ScrollablePattern> & parentPattern)3542 void ScrollablePattern::SetParentNestedScroll(RefPtr<ScrollablePattern>& parentPattern)
3543 {
3544 CHECK_NULL_VOID(parentPattern);
3545 auto parentScrollBarProxy = parentPattern->GetScrollBarProxy();
3546 auto scrollBarProxy = scrollBarProxy_;
3547 CHECK_NULL_VOID(scrollBarProxy);
3548 if (!parentScrollBarProxy) {
3549 auto proxy = AceType::MakeRefPtr<ScrollBarProxy>();
3550 parentPattern->SetScrollBarProxy(proxy);
3551 auto parentNodeInfo = proxy->GetScrollableNodeInfo();
3552 scrollBarProxy->RegisterNestScrollableNode(parentNodeInfo);
3553 parentPattern->AddNestScrollBarProxy(scrollBarProxy);
3554 } else {
3555 auto parentNodeInfo = parentScrollBarProxy->GetScrollableNodeInfo();
3556 scrollBarProxy->RegisterNestScrollableNode(parentNodeInfo);
3557 parentPattern->AddNestScrollBarProxy(scrollBarProxy);
3558 }
3559 }
3560
SearchAndSetParentNestedScroll(const RefPtr<FrameNode> & node)3561 void ScrollablePattern::SearchAndSetParentNestedScroll(const RefPtr<FrameNode>& node)
3562 {
3563 CHECK_NULL_VOID(node);
3564 auto scrollBarAxis = GetAxis();
3565 for (auto parent = node->GetParent(); parent != nullptr; parent = parent->GetParent()) {
3566 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
3567 if (!frameNode) {
3568 continue;
3569 }
3570 auto parentPattern = frameNode->GetPattern<NestableScrollContainer>();
3571 if (!parentPattern) {
3572 continue;
3573 }
3574
3575 if (AceType::InstanceOf<SwiperPattern>(parentPattern)) {
3576 auto swiper = AceType::DynamicCast<SwiperPattern>(parentPattern);
3577 CHECK_NULL_VOID(swiper);
3578 auto direction = swiper->GetDirection();
3579 CHECK_EQUAL_VOID(scrollBarAxis, direction);
3580 }
3581 auto ScrollPattern = AceType::DynamicCast<ScrollablePattern>(parentPattern);
3582 SetParentNestedScroll(ScrollPattern);
3583 }
3584 }
3585
OnAttachToMainTree()3586 void ScrollablePattern::OnAttachToMainTree()
3587 {
3588 auto host = GetHost();
3589 CHECK_NULL_VOID(host);
3590 auto scrollBarProxy = scrollBarProxy_;
3591 CHECK_NULL_VOID(scrollBarProxy);
3592 auto enableNestScroll = scrollBarProxy->IsNestScroller();
3593 if (enableNestScroll) {
3594 SearchAndSetParentNestedScroll(host);
3595 }
3596 }
3597
UnsetParentNestedScroll(RefPtr<ScrollablePattern> & parentPattern)3598 void ScrollablePattern::UnsetParentNestedScroll(RefPtr<ScrollablePattern>& parentPattern)
3599 {
3600 CHECK_NULL_VOID(parentPattern);
3601 auto parentScrollBarProxy = parentPattern->GetScrollBarProxy();
3602 CHECK_NULL_VOID(parentScrollBarProxy);
3603 auto scrollBarProxy = scrollBarProxy_;
3604 CHECK_NULL_VOID(scrollBarProxy);
3605 auto parentNodeInfo = parentScrollBarProxy->GetScrollableNodeInfo();
3606 auto pattern = parentNodeInfo.scrollableNode.Upgrade();
3607 CHECK_NULL_VOID(pattern);
3608 scrollBarProxy->UnRegisterNestScrollableNode(pattern);
3609 parentPattern->DeleteNestScrollBarProxy(scrollBarProxy);
3610 }
3611
SearchAndUnsetParentNestedScroll(const RefPtr<FrameNode> & node)3612 void ScrollablePattern::SearchAndUnsetParentNestedScroll(const RefPtr<FrameNode>& node)
3613 {
3614 CHECK_NULL_VOID(node);
3615 auto scrollBarAxis = GetAxis();
3616 for (auto parent = node->GetParent(); parent != nullptr; parent = parent->GetParent()) {
3617 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
3618 if (!frameNode) {
3619 continue;
3620 }
3621
3622 auto parentPattern = frameNode->GetPattern<NestableScrollContainer>();
3623 if (!parentPattern) {
3624 continue;
3625 }
3626
3627 if (AceType::InstanceOf<SwiperPattern>(parentPattern)) {
3628 auto swiper = AceType::DynamicCast<SwiperPattern>(parentPattern);
3629 CHECK_NULL_VOID(swiper);
3630 auto direction = swiper->GetDirection();
3631 CHECK_EQUAL_VOID(scrollBarAxis, direction);
3632 }
3633
3634 auto ScrollPattern = AceType::DynamicCast<ScrollablePattern>(parentPattern);
3635 UnsetParentNestedScroll(ScrollPattern);
3636 }
3637 }
3638
GetViewSizeMinusPadding()3639 SizeF ScrollablePattern::GetViewSizeMinusPadding()
3640 {
3641 auto host = GetHost();
3642 CHECK_NULL_RETURN(host, SizeF());
3643 auto layoutProperty = host->GetLayoutProperty();
3644 CHECK_NULL_RETURN(layoutProperty, SizeF());
3645 auto padding = layoutProperty->CreatePaddingAndBorder();
3646 auto geometryNode = host->GetGeometryNode();
3647 CHECK_NULL_RETURN(geometryNode, SizeF());
3648 auto viewSize = geometryNode->GetFrameSize();
3649 MinusPaddingToSize(padding, viewSize);
3650 return viewSize;
3651 }
3652 } // namespace OHOS::Ace::NG
3653