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