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/perfmonitor/perf_constants.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/ressched/ressched_report.h"
23 #include "base/utils/utils.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/pattern/scrollable/scrollable.h"
26 #include "core/components_ng/manager/select_overlay/select_overlay_scroll_notifier.h"
27 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
28 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
29 #include "core/components_ng/pattern/scrollable/nestable_scroll_container.h"
30 #include "core/components_ng/pattern/scrollable/scrollable_event_hub.h"
31 #include "core/pipeline/pipeline_base.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33
34 namespace OHOS::Ace::NG {
35 namespace {
36 constexpr Color SELECT_FILL_COLOR = Color(0x1A000000);
37 constexpr Color SELECT_STROKE_COLOR = Color(0x33FFFFFF);
38 constexpr float CUSTOM_ANIMATION_DURATION = 1000.0;
39 constexpr uint32_t MILLOS_PER_NANO_SECONDS = 1000 * 1000 * 1000;
40 constexpr uint64_t MIN_DIFF_VSYNC = 1000 * 1000; // min is 1ms
41 constexpr uint32_t MAX_VSYNC_DIFF_TIME = 100 * 1000 * 1000; //max 100ms
42 constexpr float SPRING_ACCURACY = 0.1;
43 const std::string SCROLLABLE_DRAG_SCENE = "scrollable_drag_scene";
44 const std::string SCROLL_BAR_DRAG_SCENE = "scrollBar_drag_scene";
45 const std::string SCROLLABLE_MOTION_SCENE = "scrollable_motion_scene";
46 const std::string SCROLLABLE_MULTI_TASK_SCENE = "scrollable_multi_task_scene";
47 const std::string SCROLL_IN_HOTZONE_SCENE = "scroll_in_hotzone_scene";
48 const std::string CUSTOM_SCROLL_BAR_SCENE = "custom_scroll_bar_scene";
49 } // namespace
50 using std::chrono::high_resolution_clock;
51 using std::chrono::milliseconds;
52
ScrollablePattern()53 ScrollablePattern::ScrollablePattern()
54 {
55 friction_ = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
56 }
57
ScrollablePattern(EdgeEffect edgeEffect,bool alwaysEnabled)58 ScrollablePattern::ScrollablePattern(EdgeEffect edgeEffect, bool alwaysEnabled)
59 : edgeEffect_(edgeEffect), edgeEffectAlwaysEnabled_(alwaysEnabled)
60 {
61 friction_ = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
62 }
63
CreatePaintProperty()64 RefPtr<PaintProperty> ScrollablePattern::CreatePaintProperty()
65 {
66 auto defaultDisplayMode = GetDefaultScrollBarDisplayMode();
67 auto property = MakeRefPtr<ScrollablePaintProperty>();
68 property->UpdateScrollBarMode(defaultDisplayMode);
69 return property;
70 }
71
ToJsonValue(std::unique_ptr<JsonValue> & json) const72 void ScrollablePattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
73 {
74 json->Put("friction", GetFriction());
75 if (edgeEffect_ == EdgeEffect::SPRING) {
76 json->Put("edgeEffect", "EdgeEffect.Spring");
77 } else if (edgeEffect_ == EdgeEffect::FADE) {
78 json->Put("edgeEffect", "EdgeEffect.Fade");
79 } else {
80 json->Put("edgeEffect", "EdgeEffect.None");
81 }
82 json->Put("flingSpeedLimit", Dimension(maxFlingVelocity_, DimensionUnit::PX).ToString().c_str());
83 auto JsonEdgeEffectOptions = JsonUtil::Create(true);
84 JsonEdgeEffectOptions->Put("alwaysEnabled", GetAlwaysEnabled());
85 json->Put("edgeEffectOptions", JsonEdgeEffectOptions);
86 }
87
SetAxis(Axis axis)88 void ScrollablePattern::SetAxis(Axis axis)
89 {
90 if (axis_ == axis) {
91 return;
92 }
93 axis_ = axis;
94 if (scrollBar_) {
95 scrollBar_->SetPositionMode(axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
96 }
97 if (scrollBarOverlayModifier_) {
98 scrollBarOverlayModifier_->SetPositionMode(
99 axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
100 }
101 auto gestureHub = GetGestureHub();
102 CHECK_NULL_VOID(gestureHub);
103 if (scrollableEvent_) {
104 gestureHub->RemoveScrollableEvent(scrollableEvent_);
105 scrollableEvent_->SetAxis(axis);
106 gestureHub->AddScrollableEvent(scrollableEvent_);
107 }
108 if (scrollEffect_) {
109 gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
110 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
111 }
112 }
113
GetGestureHub()114 RefPtr<GestureEventHub> ScrollablePattern::GetGestureHub()
115 {
116 auto host = GetHost();
117 CHECK_NULL_RETURN(host, nullptr);
118 auto hub = host->GetEventHub<EventHub>();
119 CHECK_NULL_RETURN(hub, nullptr);
120 return hub->GetOrCreateGestureEventHub();
121 }
122
GetInputHub()123 RefPtr<InputEventHub> ScrollablePattern::GetInputHub()
124 {
125 auto host = GetHost();
126 CHECK_NULL_RETURN(host, nullptr);
127 auto hub = host->GetEventHub<EventHub>();
128 CHECK_NULL_RETURN(host, nullptr);
129 return hub->GetOrCreateInputEventHub();
130 }
131
OnScrollCallback(float offset,int32_t source)132 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
133 {
134 if (source == SCROLL_FROM_START) {
135 FireOnScrollStart();
136 return true;
137 }
138 return UpdateCurrentOffset(offset, source);
139 }
140
ProcessNavBarReactOnStart()141 void ScrollablePattern::ProcessNavBarReactOnStart()
142 {
143 CHECK_NULL_VOID(navBarPattern_);
144 navBarPattern_->OnCoordScrollStart();
145 }
146
ProcessNavBarReactOnUpdate(float offset)147 float ScrollablePattern::ProcessNavBarReactOnUpdate(float offset)
148 {
149 CHECK_NULL_RETURN(navBarPattern_, false);
150 return navBarPattern_->OnCoordScrollUpdate(offset);
151 }
152
ProcessNavBarReactOnEnd()153 void ScrollablePattern::ProcessNavBarReactOnEnd()
154 {
155 CHECK_NULL_VOID(navBarPattern_);
156 navBarPattern_->OnCoordScrollEnd();
157 }
158
OnScrollPosition(double & offset,int32_t source)159 bool ScrollablePattern::OnScrollPosition(double& offset, int32_t source)
160 {
161 if (needLinked_) {
162 auto isAtTop = (IsAtTop() && Positive(offset));
163 auto refreshCoordinateMode = CoordinateWithRefresh(offset, source, isAtTop);
164 auto navigationInCoordination = CoordinateWithNavigation(offset, source, isAtTop);
165 auto modalSheetCoordinationMode = CoordinateWithSheet(offset, source, isAtTop);
166 if ((refreshCoordinateMode == RefreshCoordinationMode::REFRESH_SCROLL) || navigationInCoordination ||
167 (modalSheetCoordinationMode == ModalSheetCoordinationMode::SHEET_SCROLL)) {
168 return false;
169 }
170 }
171
172 if (source == SCROLL_FROM_START) {
173 SetParentScrollable();
174 StopScrollBarAnimatorByProxy();
175 AbortScrollAnimator();
176 } else if (!AnimateStoped()) {
177 return false;
178 }
179 return true;
180 }
181
NeedSplitScroll(OverScrollOffset & overOffsets,int32_t source)182 bool ScrollablePattern::NeedSplitScroll(OverScrollOffset& overOffsets, int32_t source)
183 {
184 return GreatNotEqual(overOffsets.start, 0.0) && refreshCoordination_ && refreshCoordination_->InCoordination() &&
185 !isRefreshInReactive_ &&
186 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_ANIMATION_SPRING ||
187 source == SCROLL_FROM_ANIMATION) &&
188 (axis_ == Axis::VERTICAL);
189 }
190
CoordinateWithRefresh(double & offset,int32_t source,bool isAtTop)191 RefreshCoordinationMode ScrollablePattern::CoordinateWithRefresh(double& offset, int32_t source, bool isAtTop)
192 {
193 auto coordinationMode = RefreshCoordinationMode::UNKNOWN;
194 if (!refreshCoordination_) {
195 CreateRefreshCoordination();
196 }
197 auto overOffsets = GetOverScrollOffset(offset);
198 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && !IsAtTop() && Positive(offset) &&
199 NeedSplitScroll(overOffsets, source)) {
200 offset = offset - overOffsets.start;
201 OnScrollCallback(offset, source);
202 isRefreshInReactive_ = true;
203 if (refreshCoordination_) {
204 refreshCoordination_->OnScrollStart(
205 source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS, GetVelocity());
206 }
207 }
208 if (IsAtTop() &&
209 (Positive(offset) || (Negative(offset) && refreshCoordination_ && refreshCoordination_->IsRefreshInScroll())) &&
210 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_ANIMATION) &&
211 !isRefreshInReactive_ && (axis_ == Axis::VERTICAL)) {
212 isRefreshInReactive_ = true;
213 if (refreshCoordination_) {
214 refreshCoordination_->OnScrollStart(
215 source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS, GetVelocity());
216 }
217 }
218 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_ELEVEN) &&
219 (refreshCoordination_ && refreshCoordination_->InCoordination()) && source != SCROLL_FROM_UPDATE &&
220 source != SCROLL_FROM_AXIS && isRefreshInReactive_) {
221 isRefreshInReactive_ = false;
222 refreshCoordination_->OnScrollEnd(GetVelocity());
223 }
224 if (refreshCoordination_ && refreshCoordination_->InCoordination() && isRefreshInReactive_) {
225 if (!refreshCoordination_->OnScroll(
226 GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset, GetVelocity())) {
227 isRefreshInReactive_ = false;
228 coordinationMode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
229 }
230 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
231 coordinationMode = RefreshCoordinationMode::REFRESH_SCROLL;
232 } else {
233 if (scrollEffect_ && scrollEffect_->IsSpringEffect()) {
234 coordinationMode = RefreshCoordinationMode::SCROLLABLE_SCROLL;
235 } else {
236 coordinationMode = RefreshCoordinationMode::REFRESH_SCROLL;
237 }
238 }
239 }
240 return coordinationMode;
241 }
242
CoordinateWithSheet(double & offset,int32_t source,bool isAtTop)243 ModalSheetCoordinationMode ScrollablePattern::CoordinateWithSheet(double& offset, int32_t source, bool isAtTop)
244 {
245 auto coordinationMode = ModalSheetCoordinationMode::UNKNOWN;
246 if (source == SCROLL_FROM_START) {
247 isSheetInReactive_ = false;
248
249 if (!sheetPattern_) {
250 GetParentModalSheet();
251 }
252 }
253 auto overOffsets = GetOverScrollOffset(offset);
254 if (IsAtTop() && (source == SCROLL_FROM_UPDATE) && !isSheetInReactive_ && (axis_ == Axis::VERTICAL)) {
255 isSheetInReactive_ = true;
256 if (sheetPattern_) {
257 sheetPattern_->OnCoordScrollStart();
258 }
259 }
260 if (sheetPattern_ && isSheetInReactive_) {
261 if (!sheetPattern_->OnCoordScrollUpdate(GreatNotEqual(overOffsets.start, 0.0) ? overOffsets.start : offset)) {
262 isSheetInReactive_ = false;
263 coordinationMode = ModalSheetCoordinationMode::SCROLLABLE_SCROLL;
264 } else {
265 coordinationMode = ModalSheetCoordinationMode::SHEET_SCROLL;
266 }
267 }
268 return coordinationMode;
269 }
270
CoordinateWithNavigation(double & offset,int32_t source,bool isAtTop)271 bool ScrollablePattern::CoordinateWithNavigation(double& offset, int32_t source, bool isAtTop)
272 {
273 if (source == SCROLL_FROM_START) {
274 GetParentNavigation();
275 CHECK_NULL_RETURN(navBarPattern_, false);
276 if (isAtTop) {
277 // Starting coordinating scroll at the beginning of scrolling.
278 isReactInParentMovement_ = true;
279 ProcessNavBarReactOnStart();
280 }
281 return false;
282 }
283
284 CHECK_NULL_RETURN(navBarPattern_ && navBarPattern_->NeedCoordWithScroll(), false);
285
286 auto overOffsets = GetOverScrollOffset(offset);
287 float offsetRemain = 0.0f;
288 float offsetCoordinate = offset;
289
290 if (!isReactInParentMovement_ && NeedCoordinateScrollWithNavigation(offset, source, overOffsets)) {
291 // Starting coordinating scroll during sliding or flipping.
292 isReactInParentMovement_ = true;
293 ProcessNavBarReactOnStart();
294 offsetRemain = offset - overOffsets.start;
295 offsetCoordinate = overOffsets.start;
296 }
297
298 if (isReactInParentMovement_) {
299 float handledByNav = ProcessNavBarReactOnUpdate(offsetCoordinate);
300 if (NearEqual(handledByNav, offsetCoordinate)) {
301 // All offsets are handled by Navigation, list cannot scroll over.
302 SetCanOverScroll(false);
303 offset = offsetRemain;
304 } else {
305 // Not all offsets are handled by Navigation, list still needs to scroll.
306 if (Positive(offset)) {
307 // When scrolling down, allow list to scroll over.
308 SetCanOverScroll(true);
309 }
310 offset = offsetRemain + (offsetCoordinate - handledByNav);
311 }
312
313 if (Negative(offset) && source == SCROLL_FROM_ANIMATION_SPRING) {
314 // When rebounding form scrolling over, trigger the ProcessNavBarReactOnEnd callback.
315 isReactInParentMovement_ = false;
316 ProcessNavBarReactOnEnd();
317 }
318 }
319
320 return false;
321 }
322
OnScrollEnd()323 void ScrollablePattern::OnScrollEnd()
324 {
325 // Previous: Sets ScrollablePattern::OnScrollEnd to Scrollable->scrollEndCallback_
326 // Scrollable calls scrollEndCallback_ in HandleOverScroll
327
328 // Now: HandleOverScroll moved to ScrollablePattern and renamed HandleScrollVelocity, directly
329 // calls OnScrollEnd in ScrollablePattern
330 if (refreshCoordination_) {
331 isRefreshInReactive_ = false;
332 refreshCoordination_->OnScrollEnd(GetVelocity());
333 }
334 if (isSheetInReactive_) {
335 isSheetInReactive_ = false;
336 if (sheetPattern_) {
337 sheetPattern_->OnCoordScrollEnd(GetVelocity());
338 }
339 }
340 if (isReactInParentMovement_) {
341 isReactInParentMovement_ = false;
342 ProcessNavBarReactOnEnd();
343 }
344
345 OnScrollEndCallback();
346 SelectOverlayScrollNotifier::NotifyOnScrollEnd(WeakClaim(this));
347 }
348
AttachAnimatableProperty(RefPtr<Scrollable> scrollable)349 void ScrollablePattern::AttachAnimatableProperty(RefPtr<Scrollable> scrollable)
350 {
351 auto host = GetHost();
352 CHECK_NULL_VOID(host);
353 auto renderContext = host->GetRenderContext();
354 CHECK_NULL_VOID(renderContext);
355 auto property = scrollable->GetFrictionProperty();
356 renderContext->AttachNodeAnimatableProperty(property);
357
358 property = scrollable->GetSpringProperty();
359 renderContext->AttachNodeAnimatableProperty(property);
360 property = scrollable->GetSnapProperty();
361 renderContext->AttachNodeAnimatableProperty(property);
362 }
363
AddScrollEvent()364 void ScrollablePattern::AddScrollEvent()
365 {
366 auto host = GetHost();
367 CHECK_NULL_VOID(host);
368 auto gestureHub = GetGestureHub();
369 CHECK_NULL_VOID(gestureHub);
370 if (scrollableEvent_) {
371 gestureHub->RemoveScrollableEvent(scrollableEvent_);
372 }
373 auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
374 auto pattern = weak.Upgrade();
375 CHECK_NULL_RETURN(pattern, false);
376 return pattern->HandleScrollImpl(static_cast<float>(offset), source);
377 };
378 auto scrollable = MakeRefPtr<Scrollable>(std::move(scrollCallback), GetAxis());
379 scrollable->SetNodeId(host->GetAccessibilityId());
380 scrollable->Initialize(host->GetContext());
381 AttachAnimatableProperty(scrollable);
382
383 // move HandleScroll and HandleOverScroll to ScrollablePattern by setting callbacks to scrollable
384 auto handleScroll = [weak = AceType::WeakClaim(this)](
385 float offset, int32_t source, NestedState state) -> ScrollResult {
386 auto pattern = weak.Upgrade();
387 if (pattern) {
388 return pattern->HandleScroll(offset, source, state);
389 }
390 return {};
391 };
392 scrollable->SetHandleScrollCallback(std::move(handleScroll));
393
394 scrollable->SetOverScrollCallback([weak = WeakClaim(this)](float velocity) {
395 auto pattern = weak.Upgrade();
396 CHECK_NULL_RETURN(pattern, false);
397 return pattern->HandleOverScroll(!pattern->IsReverse() ? velocity : -velocity);
398 });
399
400 auto scrollStart = [weak = WeakClaim(this)](float position) {
401 auto pattern = weak.Upgrade();
402 CHECK_NULL_VOID(pattern);
403 pattern->FireAndCleanScrollingListener();
404 pattern->OnScrollStartRecursive(position);
405 };
406 scrollable->SetOnScrollStartRec(std::move(scrollStart));
407
408 auto scrollEndRec = [weak = WeakClaim(this)](const std::optional<float>& velocity) {
409 auto pattern = weak.Upgrade();
410 CHECK_NULL_VOID(pattern);
411 pattern->OnScrollEndRecursive(velocity);
412 };
413 scrollable->SetOnScrollEndRec(std::move(scrollEndRec));
414
415 auto scrollEnd = [weak = WeakClaim(this)]() {
416 auto pattern = weak.Upgrade();
417 CHECK_NULL_VOID(pattern);
418 pattern->OnScrollEnd();
419 };
420 scrollable->SetScrollEndCallback(std::move(scrollEnd));
421 scrollable->SetUnstaticFriction(friction_);
422 scrollable->SetMaxFlingVelocity(maxFlingVelocity_);
423
424 auto scrollSnap = [weak = WeakClaim(this)](double targetOffset, double velocity) -> bool {
425 auto pattern = weak.Upgrade();
426 CHECK_NULL_RETURN(pattern, false);
427 return pattern->OnScrollSnapCallback(targetOffset, velocity);
428 };
429 scrollable->SetOnScrollSnapCallback(scrollSnap);
430
431 auto calePredictSnapOffsetCallback =
432 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
433 auto pattern = weak.Upgrade();
434 std::optional<float> predictSnapOffset;
435 CHECK_NULL_RETURN(pattern, predictSnapOffset);
436 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
437 };
438 scrollable->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
439
440 auto needScrollSnapToSideCallback = [weak = WeakClaim(this)](float delta) -> bool {
441 auto pattern = weak.Upgrade();
442 CHECK_NULL_RETURN(pattern, false);
443 return pattern->NeedScrollSnapToSide(delta);
444 };
445 scrollable->SetNeedScrollSnapToSideCallback(std::move(needScrollSnapToSideCallback));
446
447 auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
448 auto pattern = weak.Upgrade();
449 CHECK_NULL_VOID(pattern);
450 return pattern->NotifyFRCSceneInfo(SCROLLABLE_DRAG_SCENE, velocity, sceneStatus);
451 };
452 scrollable->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
453
454 scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
455 scrollableEvent_->SetScrollable(scrollable);
456 gestureHub->AddScrollableEvent(scrollableEvent_);
457 InitTouchEvent(gestureHub);
458 RegisterWindowStateChangedCallback();
459 }
460
InitTouchEvent(const RefPtr<GestureEventHub> & gestureHub)461 void ScrollablePattern::InitTouchEvent(const RefPtr<GestureEventHub>& gestureHub)
462 {
463 // use TouchEvent to receive next touch down event to stop animation.
464 if (touchEvent_) {
465 return;
466 }
467 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
468 auto pattern = weak.Upgrade();
469 CHECK_NULL_VOID(pattern && pattern->scrollableEvent_);
470 auto scrollable = pattern->scrollableEvent_->GetScrollable();
471 CHECK_NULL_VOID(scrollable);
472 switch (info.GetTouches().front().GetTouchType()) {
473 case TouchType::DOWN:
474 scrollable->HandleTouchDown();
475 break;
476 case TouchType::UP:
477 scrollable->HandleTouchUp();
478 break;
479 case TouchType::CANCEL:
480 scrollable->HandleTouchCancel();
481 break;
482 default:
483 break;
484 }
485 };
486 if (touchEvent_) {
487 gestureHub->RemoveTouchEvent(touchEvent_);
488 }
489 touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
490 gestureHub->AddTouchEvent(touchEvent_);
491 }
492
RegisterWindowStateChangedCallback()493 void ScrollablePattern::RegisterWindowStateChangedCallback()
494 {
495 auto host = GetHost();
496 CHECK_NULL_VOID(host);
497 auto context = NG::PipelineContext::GetCurrentContext();
498 CHECK_NULL_VOID(context);
499 context->AddWindowStateChangedCallback(host->GetId());
500 }
501
OnDetachFromFrameNode(FrameNode * frameNode)502 void ScrollablePattern::OnDetachFromFrameNode(FrameNode* frameNode)
503 {
504 auto context = NG::PipelineContext::GetCurrentContext();
505 CHECK_NULL_VOID(context);
506 context->RemoveWindowStateChangedCallback(frameNode->GetId());
507 }
508
OnWindowHide()509 void ScrollablePattern::OnWindowHide()
510 {
511 CHECK_NULL_VOID(scrollableEvent_);
512 auto scrollable = scrollableEvent_->GetScrollable();
513 CHECK_NULL_VOID(scrollable);
514 scrollable->StopFrictionAnimation();
515 }
516
SetEdgeEffect(EdgeEffect edgeEffect)517 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
518 {
519 auto gestureHub = GetGestureHub();
520 CHECK_NULL_VOID(gestureHub);
521 if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
522 gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
523 scrollEffect_.Reset();
524 }
525 if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
526 auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
527 CHECK_NULL_VOID(springEffect);
528 springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
529 auto pattern = weak.Upgrade();
530 CHECK_NULL_RETURN(pattern, false);
531 return pattern->OutBoundaryCallback();
532 });
533 // add callback to springEdgeEffect
534 SetEdgeEffectCallback(springEffect);
535 scrollEffect_ = springEffect;
536 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
537 }
538 if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
539 auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
540 CHECK_NULL_VOID(fadeEdgeEffect);
541 fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
542 auto list = weakScroll.Upgrade();
543 CHECK_NULL_VOID(list);
544 auto host = list->GetHost();
545 CHECK_NULL_VOID(host);
546 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
547 });
548 SetEdgeEffectCallback(fadeEdgeEffect);
549 fadeEdgeEffect->InitialEdgeEffect();
550 scrollEffect_ = fadeEdgeEffect;
551 gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
552 }
553 auto scrollable = scrollableEvent_->GetScrollable();
554 CHECK_NULL_VOID(scrollable);
555 scrollable->SetEdgeEffect(edgeEffect);
556 }
557
HandleEdgeEffect(float offset,int32_t source,const SizeF & size,bool reverse)558 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size, bool reverse)
559 {
560 bool isAtTop = IsAtTop();
561 bool isAtBottom = IsAtBottom();
562 bool isNotPositiveScrollableDistance = isAtTop && isAtBottom;
563 // check edgeEffect is not springEffect
564 if (scrollEffect_ && scrollEffect_->IsFadeEffect() &&
565 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION)) { // handle edge effect
566 if ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset))) {
567 auto isScrollFromUpdate = source == SCROLL_FROM_UPDATE;
568 scrollEffect_->HandleOverScroll(GetAxis(), !reverse ? -offset : offset,
569 size, isScrollFromUpdate, isNotPositiveScrollableDistance);
570 }
571 }
572 if (!(scrollEffect_ && scrollEffect_->IsSpringEffect() &&
573 (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION ||
574 source == SCROLL_FROM_ANIMATION_SPRING ||
575 (source == SCROLL_FROM_ANIMATION_CONTROLLER && animateCanOverScroll_)))) {
576 if (isAtTop && Positive(offset)) {
577 animateOverScroll_ = false;
578 return false;
579 }
580 if (isAtBottom && Negative(offset)) {
581 animateOverScroll_ = false;
582 return false;
583 }
584 }
585 animateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && (isAtTop || isAtBottom);
586 isAnimateOverScroll_ = (source == SCROLL_FROM_ANIMATION_CONTROLLER) && animateCanOverScroll_ &&
587 ((isAtTop && Positive(offset)) || (isAtBottom && Negative(offset)));
588 return true;
589 }
590
RegisterScrollBarEventTask()591 void ScrollablePattern::RegisterScrollBarEventTask()
592 {
593 CHECK_NULL_VOID(scrollBar_);
594 auto host = GetHost();
595 CHECK_NULL_VOID(host);
596 auto gestureHub = GetGestureHub();
597 auto inputHub = GetInputHub();
598 CHECK_NULL_VOID(gestureHub);
599 CHECK_NULL_VOID(inputHub);
600 scrollBar_->SetGestureEvent();
601 scrollBar_->SetMouseEvent();
602 scrollBar_->SetHoverEvent();
603 scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
604 auto host = weak.Upgrade();
605 CHECK_NULL_VOID(host);
606 host->MarkNeedRenderOnly();
607 });
608 auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
609 auto pattern = weak.Upgrade();
610 CHECK_NULL_RETURN(pattern, false);
611 return pattern->OnScrollCallback(static_cast<float>(offset), source);
612 };
613 scrollBar_->SetScrollPositionCallback(std::move(scrollCallback));
614 auto scrollEnd = [weak = WeakClaim(this)]() {
615 auto pattern = weak.Upgrade();
616 CHECK_NULL_VOID(pattern);
617 pattern->OnScrollEnd();
618 };
619 scrollBar_->SetScrollEndCallback(std::move(scrollEnd));
620 auto calePredictSnapOffsetCallback =
621 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
622 auto pattern = weak.Upgrade();
623 CHECK_NULL_RETURN(pattern, std::optional<float>());
624 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
625 };
626 scrollBar_->SetCalePredictSnapOffsetCallback(std::move(calePredictSnapOffsetCallback));
627 auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
628 auto pattern = weak.Upgrade();
629 CHECK_NULL_VOID(pattern);
630 pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
631 };
632 scrollBar_->SetStartScrollSnapMotionCallback(std::move(startScrollSnapMotionCallback));
633
634 gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
635 inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
636 inputHub->AddOnHoverEvent(scrollBar_->GetHoverEvent());
637 CHECK_NULL_VOID(scrollableEvent_);
638 scrollableEvent_->SetInBarRegionCallback(
639 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const PointF& point, SourceType source) {
640 auto scrollBar = weak.Upgrade();
641 CHECK_NULL_RETURN(scrollBar, false);
642 if (source == SourceType::MOUSE) {
643 return scrollBar->InBarHoverRegion(Point(point.GetX(), point.GetY()));
644 }
645 return scrollBar->InBarTouchRegion(Point(point.GetX(), point.GetY()));
646 });
647 scrollableEvent_->SetBarCollectTouchTargetCallback(
648 [weak = AceType::WeakClaim(AceType::RawPtr(scrollBar_))](const OffsetF& coordinateOffset,
649 const GetEventTargetImpl& getEventTargetImpl, TouchTestResult& result, const RefPtr<FrameNode>& frameNode,
650 const RefPtr<TargetComponent>& targetComponent) {
651 auto scrollBar = weak.Upgrade();
652 CHECK_NULL_VOID(scrollBar);
653 scrollBar->OnCollectTouchTarget(coordinateOffset, getEventTargetImpl, result, frameNode, targetComponent);
654 });
655
656 auto dragFRCSceneCallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
657 auto pattern = weak.Upgrade();
658 CHECK_NULL_VOID(pattern);
659 return pattern->NotifyFRCSceneInfo(SCROLL_BAR_DRAG_SCENE, velocity, sceneStatus);
660 };
661 scrollBar_->SetDragFRCSceneCallback(std::move(dragFRCSceneCallback));
662 }
663
SetScrollBar(DisplayMode displayMode)664 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
665 {
666 auto host = GetHost();
667 CHECK_NULL_VOID(host);
668 if (displayMode == DisplayMode::OFF) {
669 if (scrollBar_) {
670 auto gestureHub = GetGestureHub();
671 if (gestureHub) {
672 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
673 }
674 scrollBar_.Reset();
675 if (scrollBarOverlayModifier_) {
676 scrollBarOverlayModifier_->SetOpacity(0);
677 }
678 }
679 return;
680 }
681 DisplayMode oldDisplayMode = DisplayMode::OFF;
682 if (!scrollBar_) {
683 scrollBar_ = AceType::MakeRefPtr<ScrollBar>();
684 // set the scroll bar style
685 if (GetAxis() == Axis::HORIZONTAL) {
686 scrollBar_->SetPositionMode(PositionMode::BOTTOM);
687 if (scrollBarOverlayModifier_) {
688 scrollBarOverlayModifier_->SetPositionMode(PositionMode::BOTTOM);
689 }
690 }
691 RegisterScrollBarEventTask();
692 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
693 } else {
694 oldDisplayMode = scrollBar_->GetDisplayMode();
695 }
696
697 if (oldDisplayMode != displayMode) {
698 scrollBar_->SetDisplayMode(displayMode);
699 if (scrollBarOverlayModifier_ && scrollBar_->IsScrollable()) {
700 scrollBarOverlayModifier_->SetOpacity(UINT8_MAX);
701 }
702 scrollBar_->ScheduleDisappearDelayTask();
703 }
704 UpdateBorderRadius();
705 }
706
UpdateBorderRadius()707 void ScrollablePattern::UpdateBorderRadius()
708 {
709 auto host = GetHost();
710 CHECK_NULL_VOID(host);
711 auto renderContext = host->GetRenderContext();
712 CHECK_NULL_VOID(renderContext);
713 if (renderContext->HasBorderRadius()) {
714 auto borderRadius = renderContext->GetBorderRadius().value();
715 if (!(borderRadius == scrollBar_->GetHostBorderRadius())) {
716 scrollBar_->SetHostBorderRadius(borderRadius);
717 scrollBar_->CalcReservedHeight();
718 }
719 }
720 }
721
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)722 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
723 {
724 if (!property) {
725 SetScrollBar(DisplayMode::AUTO);
726 return;
727 }
728 auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
729 SetScrollBar(displayMode);
730 if (scrollBar_) {
731 auto barWidth = property->GetScrollBarWidth();
732 if (barWidth) {
733 scrollBar_->SetInactiveWidth(barWidth.value());
734 scrollBar_->SetNormalWidth(barWidth.value());
735 scrollBar_->SetActiveWidth(barWidth.value());
736 scrollBar_->SetTouchWidth(barWidth.value());
737 scrollBar_->SetIsUserNormalWidth(true);
738 } else {
739 scrollBar_->SetIsUserNormalWidth(false);
740 }
741 auto barColor = property->GetScrollBarColor();
742 if (barColor) {
743 scrollBar_->SetForegroundColor(barColor.value());
744 } else {
745 auto pipelineContext = PipelineContext::GetCurrentContext();
746 CHECK_NULL_VOID(pipelineContext);
747 auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
748 CHECK_NULL_VOID(theme);
749 scrollBar_->SetForegroundColor(theme->GetForegroundColor());
750 scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
751 }
752 }
753 }
754
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort,Offset viewOffset)755 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort, Offset viewOffset)
756 {
757 // inner scrollbar, viewOffset is padding offset
758 if (scrollBar_) {
759 auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
760 bool scrollable = GreatNotEqual(estimatedHeight, mainSize) && IsScrollable();
761 if (scrollBar_->IsScrollable() != scrollable) {
762 scrollBar_->SetScrollable(scrollable);
763 if (scrollBarOverlayModifier_) {
764 scrollBarOverlayModifier_->SetOpacity(scrollable ? UINT8_MAX : 0);
765 }
766 if (scrollable) {
767 scrollBar_->ScheduleDisappearDelayTask();
768 }
769 }
770 Offset scrollOffset = { offset, offset }; // fit for w/h switched.
771 UpdateBorderRadius();
772 scrollBar_->SetReverse(IsReverse());
773 scrollBar_->SetIsOutOfBoundary(IsOutOfBoundary());
774 scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
775 scrollBar_->MarkNeedRender();
776 }
777
778 // outer scrollbar
779 if (scrollBarProxy_) {
780 estimatedHeight_ = estimatedHeight - (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
781 barOffset_ = -offset;
782 scrollBarProxy_->NotifyScrollBar(AceType::WeakClaim(this));
783 }
784 }
785
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)786 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
787 {
788 CHECK_NULL_VOID(scrollBarProxy);
789 auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source) {
790 if (source != SCROLL_FROM_START) {
791 auto pattern = weak.Upgrade();
792 if (!pattern || pattern->GetAxis() == Axis::NONE) {
793 return false;
794 }
795 return pattern->UpdateCurrentOffset(offset, SCROLL_FROM_BAR);
796 }
797 return true;
798 };
799 auto scrollStartCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
800 auto pattern = weak.Upgrade();
801 CHECK_NULL_RETURN(pattern, false);
802 // no source == SCROLL_FROM_START for ScrollBar
803 pattern->OnScrollStartCallback();
804 return pattern->OnScrollCallback(static_cast<float>(offset), source);
805 };
806 auto scrollEndCallback = [weak = WeakClaim(this)]() {
807 auto pattern = weak.Upgrade();
808 CHECK_NULL_VOID(pattern);
809 pattern->OnScrollEnd();
810 };
811 auto calePredictSnapOffsetCallback =
812 [weak = WeakClaim(this)](float delta, float dragDistance, float velocity) -> std::optional<float> {
813 auto pattern = weak.Upgrade();
814 CHECK_NULL_RETURN(pattern, std::optional<float>());
815 return pattern->CalePredictSnapOffset(delta, dragDistance, velocity);
816 };
817 auto startScrollSnapMotionCallback = [weak = WeakClaim(this)](float scrollSnapDelta, float scrollSnapVelocity) {
818 auto pattern = weak.Upgrade();
819 CHECK_NULL_VOID(pattern);
820 pattern->StartScrollSnapMotion(scrollSnapDelta, scrollSnapVelocity);
821 };
822
823 auto scrollbarFRcallback = [weak = WeakClaim(this)](double velocity, SceneStatus sceneStatus) {
824 auto pattern = weak.Upgrade();
825 CHECK_NULL_VOID(pattern);
826 return pattern->NotifyFRCSceneInfo(CUSTOM_SCROLL_BAR_SCENE, velocity, sceneStatus);
827 };
828
829 ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction), std::move(scrollStartCallback),
830 std::move(scrollEndCallback), std::move(calePredictSnapOffsetCallback),
831 std::move(startScrollSnapMotionCallback), std::move(scrollbarFRcallback) };
832 scrollBarProxy->RegisterScrollableNode(nodeInfo);
833 scrollBarProxy_ = scrollBarProxy;
834 }
835
CreateScrollBarOverlayModifier()836 void ScrollablePattern::CreateScrollBarOverlayModifier()
837 {
838 CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedPaint());
839 CHECK_NULL_VOID(!scrollBarOverlayModifier_);
840 scrollBarOverlayModifier_ = AceType::MakeRefPtr<ScrollBarOverlayModifier>();
841 scrollBarOverlayModifier_->SetRect(scrollBar_->GetActiveRect());
842 scrollBarOverlayModifier_->SetPositionMode(scrollBar_->GetPositionMode());
843 }
844
HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)845 void ScrollablePattern::HandleScrollBarOutBoundary(float scrollBarOutBoundaryExtent)
846 {
847 scrollBarOutBoundaryExtent_ = scrollBarOutBoundaryExtent;
848 CHECK_NULL_VOID(scrollBar_ && scrollBar_->NeedScrollBar());
849 scrollBar_->SetOutBoundary(std::abs(scrollBarOutBoundaryExtent_));
850 }
851
SetNestedScroll(const NestedScrollOptions & nestedOpt)852 void ScrollablePattern::SetNestedScroll(const NestedScrollOptions& nestedOpt)
853 {
854 nestedScroll_ = nestedOpt;
855 }
856
SetFriction(double friction)857 void ScrollablePattern::SetFriction(double friction)
858 {
859 if (LessOrEqual(friction, 0.0)) {
860 friction = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? NEW_FRICTION : FRICTION;
861 }
862 friction_ = friction;
863 CHECK_NULL_VOID(scrollableEvent_);
864 auto scrollable = scrollableEvent_->GetScrollable();
865 scrollable->SetUnstaticFriction(friction_);
866 }
867
SetMaxFlingVelocity(double max)868 void ScrollablePattern::SetMaxFlingVelocity(double max)
869 {
870 if (LessOrEqual(max, 0.0f)) {
871 max = MAX_VELOCITY;
872 }
873 maxFlingVelocity_ = max;
874 CHECK_NULL_VOID(scrollableEvent_);
875 auto scrollable = scrollableEvent_->GetScrollable();
876 scrollable->SetMaxFlingVelocity(max);
877 }
878
GetParentNavigation()879 void ScrollablePattern::GetParentNavigation()
880 {
881 if (navBarPattern_) {
882 return;
883 }
884 auto host = GetHost();
885 CHECK_NULL_VOID(host);
886 if ((host->GetTag() != V2::LIST_ETS_TAG) && (host->GetTag() != V2::GRID_ETS_TAG) &&
887 (host->GetTag() != V2::SCROLL_ETS_TAG)) {
888 return;
889 }
890 for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
891 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
892 if (!frameNode) {
893 continue;
894 }
895 if ((frameNode->GetTag() == V2::LIST_ETS_TAG) || (frameNode->GetTag() == V2::GRID_ETS_TAG) ||
896 (frameNode->GetTag() == V2::SCROLL_ETS_TAG)) {
897 break;
898 }
899 navBarPattern_ = frameNode->GetPattern<NavBarPattern>();
900 if (!navBarPattern_) {
901 continue;
902 }
903 return;
904 }
905 navBarPattern_ = nullptr;
906 return;
907 }
908
GetParentModalSheet()909 void ScrollablePattern::GetParentModalSheet()
910 {
911 if (sheetPattern_) {
912 return;
913 }
914 auto host = GetHost();
915 CHECK_NULL_VOID(host);
916
917 if (host->GetTag() != V2::SCROLL_ETS_TAG) {
918 return;
919 }
920
921 for (auto parent = host->GetParent(); parent != nullptr; parent = parent->GetParent()) {
922 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
923 if (!frameNode) {
924 continue;
925 }
926 sheetPattern_ = frameNode->GetPattern<SheetPresentationPattern>();
927 if (!sheetPattern_) {
928 continue;
929 }
930 return;
931 }
932 return;
933 }
934
SetParentScrollable()935 void ScrollablePattern::SetParentScrollable()
936 {
937 if (nestedScroll_.NeedParent()) {
938 parent_ = SearchParent();
939 } else {
940 parent_ = nullptr;
941 }
942 }
943
StopAnimate()944 void ScrollablePattern::StopAnimate()
945 {
946 if (!IsScrollableStopped()) {
947 StopScrollable();
948 }
949 if (animator_ && !animator_->IsStopped()) {
950 animator_->Stop();
951 }
952 if (!isAnimationStop_) {
953 StopAnimation(springAnimation_);
954 StopAnimation(curveAnimation_);
955 }
956 }
957
ScrollTo(float position)958 void ScrollablePattern::ScrollTo(float position)
959 {
960 StopAnimate();
961 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
962 }
963
AnimateTo(float position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll)964 void ScrollablePattern::AnimateTo(
965 float position, float duration, const RefPtr<Curve>& curve, bool smooth, bool canOverScroll)
966 {
967 float currVelocity = 0.0f;
968 if (!IsScrollableStopped()) {
969 CHECK_NULL_VOID(scrollableEvent_);
970 auto scrollable = scrollableEvent_->GetScrollable();
971 CHECK_NULL_VOID(scrollable);
972 currVelocity = -scrollable->GetCurrentVelocity();
973 scrollAbort_ = true;
974 StopScrollable();
975 }
976 if (!isAnimationStop_) {
977 currVelocity = GetCurrentVelocity();
978 scrollAbort_ = true;
979 StopAnimation(springAnimation_);
980 StopAnimation(curveAnimation_);
981 }
982 if (animator_ && !animator_->IsStopped()) {
983 scrollAbort_ = true;
984 animator_->Stop();
985 }
986 if (NearEqual(position, GetTotalOffset())) {
987 return;
988 }
989 ResSchedReport::GetInstance().ResSchedDataReport("slide_on");
990 finalPosition_ = position;
991 if (smooth) {
992 PlaySpringAnimation(position, DEFAULT_SCROLL_TO_VELOCITY, DEFAULT_SCROLL_TO_MASS, DEFAULT_SCROLL_TO_STIFFNESS,
993 DEFAULT_SCROLL_TO_DAMPING);
994 } else {
995 PlayCurveAnimation(position, duration, curve, canOverScroll);
996 }
997 FireOnScrollStart();
998 auto pipeline = PipelineBase::GetCurrentContext();
999 CHECK_NULL_VOID(pipeline);
1000 pipeline->RequestFrame();
1001 }
1002
PlaySpringAnimation(float position,float velocity,float mass,float stiffness,float damping)1003 void ScrollablePattern::PlaySpringAnimation(float position, float velocity, float mass, float stiffness, float damping)
1004 {
1005 if (!springOffsetProperty_) {
1006 InitSpringOffsetProperty();
1007 }
1008 scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
1009 auto pattern = weakScroll.Upgrade();
1010 CHECK_NULL_RETURN(pattern, 0.0f);
1011 return pattern->GetCurrentVelocity();
1012 });
1013
1014 AnimationOption option;
1015 auto curve = AceType::MakeRefPtr<InterpolatingSpring>(velocity, mass, stiffness, damping);
1016 InitOption(option, CUSTOM_ANIMATION_DURATION, curve);
1017 isAnimationStop_ = false;
1018 springOffsetProperty_->Set(GetTotalOffset());
1019 springAnimation_ = AnimationUtils::StartAnimation(
1020 option,
1021 [weak = AceType::WeakClaim(this), position]() {
1022 auto pattern = weak.Upgrade();
1023 CHECK_NULL_VOID(pattern);
1024 pattern->springOffsetProperty_->Set(position);
1025 },
1026 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1027 ContainerScope scope(id);
1028 auto pattern = weak.Upgrade();
1029 CHECK_NULL_VOID(pattern);
1030 pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(),
1031 SceneStatus::END);
1032 });
1033 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1034 }
1035
PlayCurveAnimation(float position,float duration,const RefPtr<Curve> & curve,bool canOverScroll)1036 void ScrollablePattern::PlayCurveAnimation(
1037 float position, float duration, const RefPtr<Curve>& curve, bool canOverScroll)
1038 {
1039 AnimationOption option;
1040 InitOption(option, duration, curve);
1041 SetAnimateCanOverScroll(canOverScroll);
1042 if (!curveOffsetProperty_) {
1043 InitCurveOffsetProperty(position);
1044 }
1045 scrollableEvent_->SetAnimateVelocityCallback([weakScroll = AceType::WeakClaim(this)]() -> double {
1046 auto pattern = weakScroll.Upgrade();
1047 CHECK_NULL_RETURN(pattern, 0.0f);
1048 return pattern->GetCurrentVelocity();
1049 });
1050 isAnimationStop_ = false;
1051 curveOffsetProperty_->Set(GetTotalOffset());
1052 curveAnimation_ = AnimationUtils::StartAnimation(
1053 option,
1054 [weak = AceType::WeakClaim(this), position]() {
1055 auto pattern = weak.Upgrade();
1056 CHECK_NULL_VOID(pattern);
1057 pattern->curveOffsetProperty_->Set(position);
1058 },
1059 [weak = AceType::WeakClaim(this), id = Container::CurrentId()]() {
1060 ContainerScope scope(id);
1061 auto pattern = weak.Upgrade();
1062 CHECK_NULL_VOID(pattern);
1063 pattern->SetAnimateCanOverScroll(false);
1064 pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(), SceneStatus::END);
1065 ResSchedReport::GetInstance().ResSchedDataReport("slide_off");
1066 });
1067 NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, GetCurrentVelocity(), SceneStatus::START);
1068 }
1069
InitSpringOffsetProperty()1070 void ScrollablePattern::InitSpringOffsetProperty()
1071 {
1072 auto host = GetHost();
1073 CHECK_NULL_VOID(host);
1074 auto renderContext = host->GetRenderContext();
1075 CHECK_NULL_VOID(renderContext);
1076 auto propertyCallback = [weak = AceType::WeakClaim(this)](float offset) {
1077 auto pattern = weak.Upgrade();
1078 CHECK_NULL_VOID(pattern);
1079 if (pattern->isAnimationStop_) {
1080 return;
1081 }
1082 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1083 CHECK_NULL_VOID(context);
1084 uint64_t currentVsync = context->GetVsyncTime();
1085 uint64_t diff = currentVsync - pattern->lastVsyncTime_;
1086 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1087 pattern->currentVelocity_ = (offset - pattern->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1088 pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->currentVelocity_,
1089 SceneStatus::RUNNING);
1090 }
1091 auto stopAnimation = NearEqual(pattern->finalPosition_, offset, SPRING_ACCURACY);
1092 if (stopAnimation) {
1093 offset = pattern->finalPosition_;
1094 }
1095 pattern->lastVsyncTime_ = currentVsync;
1096 pattern->lastPosition_ = offset;
1097 if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - offset,
1098 SCROLL_FROM_ANIMATION_CONTROLLER) || stopAnimation) {
1099 pattern->StopAnimation(pattern->springAnimation_);
1100 }
1101 };
1102 springOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1103 renderContext->AttachNodeAnimatableProperty(springOffsetProperty_);
1104 }
1105
InitCurveOffsetProperty(float position)1106 void ScrollablePattern::InitCurveOffsetProperty(float position)
1107 {
1108 auto host = GetHost();
1109 CHECK_NULL_VOID(host);
1110 auto renderContext = host->GetRenderContext();
1111 CHECK_NULL_VOID(renderContext);
1112 auto propertyCallback = [weak = AceType::WeakClaim(this), position](float offset) {
1113 auto pattern = weak.Upgrade();
1114 CHECK_NULL_VOID(pattern);
1115 if (pattern->isAnimationStop_) {
1116 return;
1117 }
1118 auto context = OHOS::Ace::PipelineContext::GetCurrentContext();
1119 CHECK_NULL_VOID(context);
1120 uint64_t currentVsync = context->GetVsyncTime();
1121 uint64_t diff = currentVsync - pattern->lastVsyncTime_;
1122 if (diff < MAX_VSYNC_DIFF_TIME && diff > MIN_DIFF_VSYNC) {
1123 pattern->currentVelocity_ = (offset - pattern->lastPosition_) / diff * MILLOS_PER_NANO_SECONDS;
1124 pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->currentVelocity_,
1125 SceneStatus::RUNNING);
1126 }
1127 auto stopAnimation = NearEqual(pattern->finalPosition_, offset, SPRING_ACCURACY);
1128 if (stopAnimation) {
1129 offset = pattern->finalPosition_;
1130 }
1131 pattern->lastVsyncTime_ = currentVsync;
1132 pattern->lastPosition_ = offset;
1133 pattern->NotifyFRCSceneInfo(SCROLLABLE_MULTI_TASK_SCENE, pattern->GetCurrentVelocity(),
1134 SceneStatus::RUNNING);
1135 if (!pattern->UpdateCurrentOffset(pattern->GetTotalOffset() - offset, SCROLL_FROM_ANIMATION_CONTROLLER) ||
1136 stopAnimation || pattern->isAnimateOverScroll_) {
1137 if (pattern->isAnimateOverScroll_) {
1138 pattern->isAnimateOverScroll_ = false;
1139 auto pauseVelocity = -pattern->currentVelocity_;
1140 auto context = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
1141 CHECK_NULL_VOID(context);
1142 context->MarkNeedFlushAnimationStartTime();
1143 pattern->PauseAnimation(pattern->curveAnimation_);
1144 pattern->HandleOverScroll(pauseVelocity);
1145 } else if (stopAnimation || (pattern->IsAtTop() && LessOrEqual(position, pattern->GetTotalOffset())) ||
1146 (pattern->IsAtBottom() && GreatOrEqual(position, pattern->GetTotalOffset()))) {
1147 pattern->StopAnimation(pattern->curveAnimation_);
1148 }
1149 }
1150 };
1151 curveOffsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
1152 renderContext->AttachNodeAnimatableProperty(curveOffsetProperty_);
1153 }
1154
InitOption(AnimationOption & option,float duration,const RefPtr<Curve> & curve)1155 void ScrollablePattern::InitOption(AnimationOption &option, float duration, const RefPtr<Curve>& curve)
1156 {
1157 if (!curve) {
1158 option.SetCurve(Curves::EASE); // default curve
1159 } else {
1160 option.SetCurve(curve);
1161 }
1162 if (duration > 0) {
1163 option.SetDuration(duration);
1164 } else {
1165 option.SetDuration(CUSTOM_ANIMATION_DURATION);
1166 }
1167 }
1168
StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1169 void ScrollablePattern::StopAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1170 {
1171 isAnimationStop_ = true;
1172 currentVelocity_ = 0.0;
1173 if (!animation) {
1174 return;
1175 }
1176 AnimationUtils::StopAnimation(animation);
1177 OnAnimateStop();
1178 }
1179
PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)1180 void ScrollablePattern::PauseAnimation(std::shared_ptr<AnimationUtils::Animation> animation)
1181 {
1182 isAnimationStop_ = true;
1183 currentVelocity_ = 0.0;
1184 if (!animation) {
1185 return;
1186 }
1187 AnimationUtils::StopAnimation(animation);
1188 }
1189
OnAttachToFrameNode()1190 void ScrollablePattern::OnAttachToFrameNode()
1191 {
1192 auto host = GetHost();
1193 CHECK_NULL_VOID(host);
1194 host->GetRenderContext()->SetClipToBounds(true);
1195 host->GetRenderContext()->UpdateClipEdge(true);
1196 }
1197
UninitMouseEvent()1198 void ScrollablePattern::UninitMouseEvent()
1199 {
1200 if (!boxSelectPanEvent_) {
1201 return;
1202 }
1203 auto host = GetHost();
1204 CHECK_NULL_VOID(host);
1205 auto gestureHub = host->GetOrCreateGestureEventHub();
1206 CHECK_NULL_VOID(gestureHub);
1207 gestureHub->RemovePanEvent(boxSelectPanEvent_);
1208 boxSelectPanEvent_.Reset();
1209 ClearMultiSelect();
1210 ClearInvisibleItemsSelectedStatus();
1211 isMouseEventInit_ = false;
1212 }
1213
InitMouseEvent()1214 void ScrollablePattern::InitMouseEvent()
1215 {
1216 auto host = GetHost();
1217 CHECK_NULL_VOID(host);
1218 auto gestureHub = host->GetOrCreateGestureEventHub();
1219 CHECK_NULL_VOID(gestureHub);
1220 if (!boxSelectPanEvent_) {
1221 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1222 auto pattern = weak.Upgrade();
1223 CHECK_NULL_VOID(pattern);
1224 pattern->HandleDragStart(info);
1225 };
1226
1227 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1228 auto pattern = weak.Upgrade();
1229 CHECK_NULL_VOID(pattern);
1230 pattern->HandleDragUpdate(info);
1231 };
1232
1233 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1234 auto pattern = weak.Upgrade();
1235 CHECK_NULL_VOID(pattern);
1236 pattern->HandleDragEnd(info);
1237 };
1238 GestureEventNoParameter actionCancelTask;
1239 boxSelectPanEvent_ = MakeRefPtr<PanEvent>(std::move(actionStartTask), std::move(actionUpdateTask),
1240 std::move(actionEndTask), std::move(actionCancelTask));
1241 }
1242 PanDirection panDirection = { .type = PanDirection::ALL };
1243 gestureHub->AddPanEvent(boxSelectPanEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
1244 gestureHub->SetPanEventType(GestureTypeName::BOXSELECT);
1245 gestureHub->SetOnGestureJudgeNativeBegin([](const RefPtr<NG::GestureInfo>& gestureInfo,
1246 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1247 if (gestureInfo->GetType() == GestureTypeName::BOXSELECT &&
1248 gestureInfo->GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1249 return GestureJudgeResult::REJECT;
1250 }
1251 return GestureJudgeResult::CONTINUE;
1252 });
1253 isMouseEventInit_ = true;
1254 }
1255
HandleDragStart(const GestureEvent & info)1256 void ScrollablePattern::HandleDragStart(const GestureEvent& info)
1257 {
1258 auto mouseOffsetX = static_cast<float>(info.GetLocalLocation().GetX());
1259 auto mouseOffsetY = static_cast<float>(info.GetLocalLocation().GetY());
1260 if (!IsItemSelected(info)) {
1261 ClearMultiSelect();
1262 ClearInvisibleItemsSelectedStatus();
1263 mouseStartOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1264 lastMouseStart_ = mouseStartOffset_;
1265 mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1266 mousePressOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1267 totalOffsetOfMousePressed_ = mousePressOffset_.GetMainOffset(axis_) + GetTotalOffset();
1268 canMultiSelect_ = true;
1269 }
1270 mousePressed_ = true;
1271 }
1272
HandleDragUpdate(const GestureEvent & info)1273 void ScrollablePattern::HandleDragUpdate(const GestureEvent& info)
1274 {
1275 auto mouseOffsetX = static_cast<float>(info.GetLocalLocation().GetX());
1276 auto mouseOffsetY = static_cast<float>(info.GetLocalLocation().GetY());
1277 if (!mousePressed_ || !canMultiSelect_) {
1278 return;
1279 }
1280 if (info.GetInputEventType() != InputEventType::MOUSE_BUTTON) {
1281 HandleDragEnd(info);
1282 return;
1283 }
1284 lastMouseMove_ = info;
1285 auto delta = OffsetF(mouseOffsetX, mouseOffsetY) - mousePressOffset_;
1286 if (Offset(delta.GetX(), delta.GetY()).GetDistance() > DEFAULT_PAN_DISTANCE.ConvertToPx()) {
1287 mouseEndOffset_ = OffsetF(mouseOffsetX, mouseOffsetY);
1288 // avoid large select zone
1289 LimitMouseEndOffset();
1290 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1291 MultiSelectWithoutKeyboard(selectedZone);
1292 HandleInvisibleItemsSelectedStatus(selectedZone);
1293 }
1294 SelectWithScroll();
1295 }
1296
HandleDragEnd(const GestureEvent & info)1297 void ScrollablePattern::HandleDragEnd(const GestureEvent& info)
1298 {
1299 mouseStartOffset_.Reset();
1300 lastMouseStart_.Reset();
1301 mouseEndOffset_.Reset();
1302 mousePressed_ = false;
1303 canMultiSelect_ = false;
1304 ClearSelectedZone();
1305 itemToBeSelected_.clear();
1306 lastMouseMove_.SetLocalLocation(Offset::Zero());
1307 }
ClearInvisibleItemsSelectedStatus()1308 void ScrollablePattern::ClearInvisibleItemsSelectedStatus()
1309 {
1310 for (auto& item : itemToBeSelected_) {
1311 item.second.FireSelectChangeEvent(false);
1312 }
1313 itemToBeSelected_.clear();
1314 }
1315
HandleInvisibleItemsSelectedStatus(const RectF & selectedZone)1316 void ScrollablePattern::HandleInvisibleItemsSelectedStatus(const RectF& selectedZone)
1317 {
1318 auto newRect = selectedZone;
1319 auto startMainOffset = mouseStartOffset_.GetMainOffset(axis_);
1320 auto endMainOffset = mouseEndOffset_.GetMainOffset(axis_);
1321 SelectDirection oldDirection = selectDirection_;
1322 if (LessNotEqual(startMainOffset, endMainOffset)) {
1323 selectDirection_ = SELECT_DOWN;
1324 if (axis_ == Axis::VERTICAL) {
1325 newRect.SetOffset(OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_));
1326 } else {
1327 newRect.SetOffset(OffsetF(totalOffsetOfMousePressed_, selectedZone.Top()));
1328 }
1329 } else {
1330 selectDirection_ = SELECT_UP;
1331 if (axis_ == Axis::VERTICAL) {
1332 newRect.SetOffset(
1333 OffsetF(selectedZone.Left(), totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset)));
1334 } else {
1335 newRect.SetOffset(
1336 OffsetF(totalOffsetOfMousePressed_ - (startMainOffset - endMainOffset), selectedZone.Top()));
1337 }
1338 }
1339 oldDirection = oldDirection == SELECT_NONE ? selectDirection_ : oldDirection;
1340
1341 for (auto& item : itemToBeSelected_) {
1342 item.second.FireSelectChangeEvent(newRect.IsIntersectWith(item.second.rect));
1343 }
1344
1345 if (oldDirection != selectDirection_) {
1346 itemToBeSelected_.clear();
1347 }
1348 }
1349
SelectWithScroll()1350 void ScrollablePattern::SelectWithScroll()
1351 {
1352 if (!IsScrollable()) {
1353 return;
1354 }
1355 auto offset = GetOutOfScrollableOffset();
1356 if (NearZero(offset)) {
1357 return;
1358 }
1359
1360 if (AnimateRunning()) {
1361 return;
1362 }
1363
1364 if (!isAnimationStop_) {
1365 StopAnimation(springAnimation_);
1366 StopAnimation(curveAnimation_);
1367 }
1368
1369 if (!animator_) {
1370 animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
1371 animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
1372 auto pattern = weak.Upgrade();
1373 CHECK_NULL_VOID(pattern);
1374 pattern->OnAnimateStop();
1375 });
1376 } else if (!animator_->IsStopped()) {
1377 scrollAbort_ = true;
1378 animator_->Stop();
1379 }
1380
1381 if (!selectMotion_) {
1382 selectMotion_ = AceType::MakeRefPtr<SelectMotion>(offset, [weak = WeakClaim(this)]() -> bool {
1383 auto pattern = weak.Upgrade();
1384 CHECK_NULL_RETURN(pattern, true);
1385 return pattern->ShouldSelectScrollBeStopped();
1386 });
1387 selectMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
1388 auto pattern = weakScroll.Upgrade();
1389 CHECK_NULL_VOID(pattern);
1390 offset = pattern->GetOffsetWithLimit(offset);
1391 pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
1392 pattern->UpdateMouseStart(offset);
1393 });
1394 } else {
1395 selectMotion_->Reset(offset);
1396 }
1397
1398 animator_->PlayMotion(selectMotion_);
1399
1400 FireOnScrollStart();
1401 }
1402
ClearSelectedZone()1403 void ScrollablePattern::ClearSelectedZone()
1404 {
1405 DrawSelectedZone(RectF());
1406 }
1407
ComputeSelectedZone(const OffsetF & startOffset,const OffsetF & endOffset)1408 RectF ScrollablePattern::ComputeSelectedZone(const OffsetF& startOffset, const OffsetF& endOffset)
1409 {
1410 RectF selectedZone;
1411 if (startOffset.GetX() <= endOffset.GetX()) {
1412 if (startOffset.GetY() <= endOffset.GetY()) {
1413 // bottom right
1414 selectedZone = RectF(startOffset.GetX(), startOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1415 endOffset.GetY() - startOffset.GetY());
1416 } else {
1417 // top right
1418 selectedZone = RectF(startOffset.GetX(), endOffset.GetY(), endOffset.GetX() - startOffset.GetX(),
1419 startOffset.GetY() - endOffset.GetY());
1420 }
1421 } else {
1422 if (startOffset.GetY() <= endOffset.GetY()) {
1423 // bottom left
1424 selectedZone = RectF(endOffset.GetX(), startOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1425 endOffset.GetY() - startOffset.GetY());
1426 } else {
1427 // top left
1428 selectedZone = RectF(endOffset.GetX(), endOffset.GetY(), startOffset.GetX() - endOffset.GetX(),
1429 startOffset.GetY() - endOffset.GetY());
1430 }
1431 }
1432
1433 return selectedZone;
1434 }
1435
DrawSelectedZone(const RectF & selectedZone)1436 void ScrollablePattern::DrawSelectedZone(const RectF& selectedZone)
1437 {
1438 auto host = GetHost();
1439 CHECK_NULL_VOID(host);
1440 auto hostContext = host->GetRenderContext();
1441 CHECK_NULL_VOID(hostContext);
1442 hostContext->UpdateMouseSelectWithRect(selectedZone, SELECT_FILL_COLOR, SELECT_STROKE_COLOR);
1443 }
1444
MarkSelectedItems()1445 void ScrollablePattern::MarkSelectedItems()
1446 {
1447 if (multiSelectable_ && mousePressed_) {
1448 auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
1449 if (!selectedZone.IsEmpty()) {
1450 MultiSelectWithoutKeyboard(selectedZone);
1451 HandleInvisibleItemsSelectedStatus(selectedZone);
1452 }
1453 }
1454 }
1455
ShouldSelectScrollBeStopped()1456 bool ScrollablePattern::ShouldSelectScrollBeStopped()
1457 {
1458 if (!mousePressed_) {
1459 return true;
1460 }
1461 auto offset = GetOutOfScrollableOffset();
1462 if (NearZero(offset)) {
1463 return true;
1464 }
1465
1466 if (selectMotion_) {
1467 selectMotion_->Reset(offset);
1468 }
1469 return false;
1470 };
1471
UpdateMouseStart(float offset)1472 void ScrollablePattern::UpdateMouseStart(float offset)
1473 {
1474 if (axis_ == Axis::VERTICAL) {
1475 mouseStartOffset_.AddY(offset);
1476 } else {
1477 mouseStartOffset_.AddX(offset);
1478 }
1479 }
1480
GetOutOfScrollableOffset() const1481 float ScrollablePattern::GetOutOfScrollableOffset() const
1482 {
1483 auto offset = 0.0f;
1484 auto mouseMainOffset = static_cast<float>(
1485 axis_ == Axis::VERTICAL ? lastMouseMove_.GetLocalLocation().GetY() : lastMouseMove_.GetLocalLocation().GetX());
1486 auto hostSize = GetHostFrameSize();
1487 CHECK_NULL_RETURN(hostSize.has_value(), offset);
1488 auto mainTop = 0.0f;
1489 auto mainBottom = hostSize->MainSize(axis_);
1490 if (GreatOrEqual(mouseMainOffset, mainTop) && LessOrEqual(mouseMainOffset, mainBottom)) {
1491 return offset;
1492 }
1493 if (GreatNotEqual(mouseMainOffset, mainBottom)) {
1494 if (IsAtBottom()) {
1495 return offset;
1496 }
1497 offset = mainBottom - mouseMainOffset;
1498 }
1499 if (LessNotEqual(mouseMainOffset, mainTop)) {
1500 if (IsAtTop()) {
1501 return offset;
1502 }
1503 offset = mainTop - mouseMainOffset;
1504 }
1505 return offset;
1506 }
1507
1508 // avoid start position move when offset is bigger then item height
GetOffsetWithLimit(float offset) const1509 float ScrollablePattern::GetOffsetWithLimit(float offset) const
1510 {
1511 if (Positive(offset)) {
1512 auto totalOffset = GetTotalOffset();
1513 return std::min(totalOffset, offset);
1514 } else if (Negative(offset)) {
1515 auto hostSize = GetHostFrameSize();
1516 CHECK_NULL_RETURN(hostSize.has_value(), true);
1517 auto remainHeight = GetTotalHeight() - GetTotalOffset() - hostSize->MainSize(axis_);
1518 return std::max(offset, -remainHeight);
1519 }
1520 return 0;
1521 }
1522
LimitMouseEndOffset()1523 void ScrollablePattern::LimitMouseEndOffset()
1524 {
1525 float limitedMainOffset = -1.0f;
1526 float limitedCrossOffset = -1.0f;
1527 auto hostSize = GetHostFrameSize();
1528 auto mainSize = hostSize->MainSize(axis_);
1529 auto crossSize = hostSize->CrossSize(axis_);
1530 auto mainOffset = mouseEndOffset_.GetMainOffset(axis_);
1531 auto crossOffset = mouseEndOffset_.GetCrossOffset(axis_);
1532 if (LessNotEqual(mainOffset, 0.0f)) {
1533 limitedMainOffset = 0.0f;
1534 }
1535 if (GreatNotEqual(mainOffset, mainSize)) {
1536 limitedMainOffset = mainSize;
1537 }
1538 if (LessNotEqual(crossOffset, 0.0f)) {
1539 limitedCrossOffset = 0.0f;
1540 }
1541 if (GreatNotEqual(crossOffset, crossSize)) {
1542 limitedCrossOffset = crossSize;
1543 }
1544
1545 if (axis_ == Axis::VERTICAL) {
1546 mouseEndOffset_.SetX(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedCrossOffset);
1547 mouseEndOffset_.SetY(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedMainOffset);
1548 } else {
1549 mouseEndOffset_.SetX(LessNotEqual(limitedMainOffset, 0.0f) ? mouseEndOffset_.GetX() : limitedMainOffset);
1550 mouseEndOffset_.SetY(LessNotEqual(limitedCrossOffset, 0.0f) ? mouseEndOffset_.GetY() : limitedCrossOffset);
1551 }
1552 }
1553
HandleScrollImpl(float offset,int32_t source)1554 bool ScrollablePattern::HandleScrollImpl(float offset, int32_t source)
1555 {
1556 // Previous: Set HandleScrollImpl to Scrollable->callback_
1557 // Scrollable::HandleScroll calls callback_ through UpdateScrollPosition
1558
1559 // Now: HandleScroll moved to ScrollablePattern, directly call HandleScrollImpl in
1560 // ScrollablePattern::HandleScroll
1561 double overOffset = offset;
1562 if (!OnScrollPosition(overOffset, source)) {
1563 return false;
1564 }
1565 auto result = OnScrollCallback(overOffset, source);
1566 SelectOverlayScrollNotifier::NotifyOnScrollCallback(WeakClaim(this), overOffset, source);
1567 return result;
1568 }
1569
NotifyMoved(bool value)1570 void ScrollablePattern::NotifyMoved(bool value)
1571 {
1572 CHECK_NULL_VOID(scrollableEvent_);
1573 auto&& scroll = scrollableEvent_->GetScrollable();
1574 if (scroll) {
1575 scroll->SetMoved(value);
1576 }
1577 }
1578
ProcessSpringEffect(float velocity)1579 void ScrollablePattern::ProcessSpringEffect(float velocity)
1580 {
1581 CHECK_NULL_VOID(InstanceOf<ScrollSpringEffect>(scrollEffect_));
1582 if (!OutBoundaryCallback() && !GetCanOverScroll()) {
1583 return;
1584 }
1585 scrollEffect_->ProcessScrollOver(velocity);
1586 }
1587
SetCanOverScroll(bool val)1588 void ScrollablePattern::SetCanOverScroll(bool val)
1589 {
1590 CHECK_NULL_VOID(scrollableEvent_);
1591 auto&& scrollable = scrollableEvent_->GetScrollable();
1592 if (scrollable) {
1593 scrollable->SetCanOverScroll(val);
1594 }
1595 }
1596
GetCanOverScroll() const1597 bool ScrollablePattern::GetCanOverScroll() const
1598 {
1599 CHECK_NULL_RETURN(scrollableEvent_, true);
1600 auto&& scrollable = scrollableEvent_->GetScrollable();
1601 if (scrollable) {
1602 return scrollable->CanOverScroll();
1603 }
1604 return true;
1605 }
1606
GetEdgeEffect() const1607 EdgeEffect ScrollablePattern::GetEdgeEffect() const
1608 {
1609 return edgeEffect_;
1610 }
1611
GetScrollState() const1612 ScrollState ScrollablePattern::GetScrollState() const
1613 {
1614 return ScrollablePattern::GetScrollState(scrollSource_);
1615 }
1616
GetScrollState(int32_t scrollSource)1617 ScrollState ScrollablePattern::GetScrollState(int32_t scrollSource)
1618 {
1619 // with event
1620 if (scrollSource == SCROLL_FROM_UPDATE || scrollSource == SCROLL_FROM_AXIS || scrollSource == SCROLL_FROM_BAR) {
1621 return ScrollState::SCROLL;
1622 }
1623 // without event
1624 if (scrollSource == SCROLL_FROM_ANIMATION || scrollSource == SCROLL_FROM_ANIMATION_SPRING ||
1625 scrollSource == SCROLL_FROM_ANIMATION_CONTROLLER || scrollSource == SCROLL_FROM_BAR_FLING) {
1626 return ScrollState::FLING;
1627 }
1628 // SCROLL_FROM_NONE, SCROLL_FROM_JUMP, SCROLL_FROM_CHILD, SCROLL_FROM_FOCUS_JUMP, SCROLL_FROM_ROTATE,
1629 // SCROLL_FROM_INDEXER, SCROLL_FROM_START
1630 return ScrollState::IDLE;
1631 }
1632
HandleScrollParentFirst(float & offset,int32_t source,NestedState state)1633 ScrollResult ScrollablePattern::HandleScrollParentFirst(float& offset, int32_t source, NestedState state)
1634 {
1635 auto parent = parent_.Upgrade();
1636 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1637 if (state == NestedState::CHILD_OVER_SCROLL) {
1638 if (GetEdgeEffect() == EdgeEffect::NONE) {
1639 return parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1640 }
1641 ExecuteScrollFrameBegin(offset, scrollState);
1642 return { 0, true };
1643 }
1644 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL);
1645 offset = result.remain;
1646 if (NearZero(offset)) {
1647 SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1648 return { 0, false };
1649 }
1650 float allOffset = offset;
1651 ExecuteScrollFrameBegin(offset, scrollState);
1652 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1653 auto overOffsets = GetOverScrollOffset(offset);
1654 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1655 remainOffset += overOffset;
1656 if (NearZero(remainOffset)) {
1657 SetCanOverScroll(false);
1658 return { 0, false };
1659 }
1660 if (state == NestedState::CHILD_SCROLL) {
1661 offset -= overOffset;
1662 SetCanOverScroll(false);
1663 return { remainOffset, !NearZero(overOffset) };
1664 }
1665 if (GetEdgeEffect() == EdgeEffect::NONE) {
1666 result = parent->HandleScroll(remainOffset, source, NestedState::CHILD_OVER_SCROLL);
1667 }
1668 SetCanOverScroll(!NearZero(overOffset) || (NearZero(offset) && result.reachEdge));
1669 return { 0, GetCanOverScroll() };
1670 }
1671
HandleScrollSelfFirst(float & offset,int32_t source,NestedState state)1672 ScrollResult ScrollablePattern::HandleScrollSelfFirst(float& offset, int32_t source, NestedState state)
1673 {
1674 auto parent = parent_.Upgrade();
1675 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1676 if (state == NestedState::CHILD_OVER_SCROLL) {
1677 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1678 if (NearZero(result.remain)) {
1679 offset = 0;
1680 return result;
1681 }
1682 ExecuteScrollFrameBegin(offset, scrollState);
1683 if (GetEdgeEffect() == EdgeEffect::NONE) {
1684 return result;
1685 }
1686 return { 0, true };
1687 }
1688 float allOffset = offset;
1689 ExecuteScrollFrameBegin(offset, scrollState);
1690 auto remainOffset = std::abs(offset) < std::abs(allOffset) ? allOffset - offset : 0;
1691 auto overOffsets = GetOverScrollOffset(offset);
1692 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1693 if (NearZero(overOffset) && NearZero(remainOffset)) {
1694 SetCanOverScroll(false);
1695 return { 0, false };
1696 }
1697 offset -= overOffset;
1698 auto result = parent->HandleScroll(overOffset + remainOffset, source, NestedState::CHILD_SCROLL);
1699 if (NearZero(result.remain)) {
1700 SetCanOverScroll(!InstanceOf<ScrollablePattern>(parent));
1701 return { 0, false };
1702 }
1703 if (state == NestedState::CHILD_SCROLL) {
1704 SetCanOverScroll(false);
1705 return result;
1706 }
1707 // triggering overScroll, parent always handle it first
1708 auto overRes = parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
1709 offset += LessNotEqual(std::abs(overOffset), std::abs(result.remain)) ? overOffset : overRes.remain;
1710 SetCanOverScroll((!NearZero(overOffset) || NearZero(offset)) && overRes.reachEdge);
1711 return { 0, GetCanOverScroll() };
1712 }
1713
HandleScrollSelfOnly(float & offset,int32_t source,NestedState state)1714 ScrollResult ScrollablePattern::HandleScrollSelfOnly(float& offset, int32_t source, NestedState state)
1715 {
1716 float allOffset = offset;
1717 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1718 ExecuteScrollFrameBegin(offset, scrollState);
1719 auto remainOffset = allOffset - offset;
1720 auto overOffsets = GetOverScrollOffset(!IsReverse() ? offset : -offset);
1721 auto overOffset = (!IsReverse() ? offset > 0 : offset < 0) ? overOffsets.start : overOffsets.end;
1722 remainOffset += overOffset;
1723 if (NearZero(remainOffset)) {
1724 SetCanOverScroll(false);
1725 return { 0, false };
1726 }
1727 bool canOverScroll = false;
1728 if (state == NestedState::CHILD_SCROLL) {
1729 offset -= overOffset;
1730 } else if (state == NestedState::GESTURE) {
1731 canOverScroll = !NearZero(overOffset) && GetEdgeEffect() != EdgeEffect::NONE;
1732 } else if (GetEdgeEffect() != EdgeEffect::NONE) {
1733 remainOffset = 0;
1734 }
1735 SetCanOverScroll(canOverScroll);
1736 return { remainOffset, !NearZero(overOffset) };
1737 }
1738
HandleScrollParallel(float & offset,int32_t source,NestedState state)1739 ScrollResult ScrollablePattern::HandleScrollParallel(float& offset, int32_t source, NestedState state)
1740 {
1741 auto remainOffset = 0.0;
1742 auto parent = parent_.Upgrade();
1743 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1744 if (state == NestedState::CHILD_OVER_SCROLL) {
1745 if (GetEdgeEffect() == EdgeEffect::NONE) {
1746 auto result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL);
1747 remainOffset = result.remain;
1748 offset = 0;
1749 } else {
1750 ExecuteScrollFrameBegin(offset, scrollState);
1751 }
1752 return { remainOffset, true };
1753 }
1754
1755 bool canOverScroll = false;
1756 float parentOffset = offset;
1757 ExecuteScrollFrameBegin(offset, scrollState);
1758 auto result = parent->HandleScroll(parentOffset, source, NestedState::CHILD_SCROLL);
1759
1760 auto overOffsets = GetOverScrollOffset(offset);
1761 auto overOffset = offset > 0 ? overOffsets.start : overOffsets.end;
1762 if (!NearZero(overOffset) && result.reachEdge) {
1763 if (state == NestedState::CHILD_SCROLL) {
1764 remainOffset = overOffset;
1765 offset = offset - overOffset;
1766 } else if (GetEdgeEffect() == EdgeEffect::NONE) {
1767 parent->HandleScroll(result.remain, source, NestedState::CHILD_OVER_SCROLL);
1768 canOverScroll = true;
1769 offset = offset - overOffset;
1770 } else {
1771 canOverScroll = true;
1772 }
1773 } else if (!NearZero(overOffset)) {
1774 offset = offset - overOffset;
1775 }
1776 SetCanOverScroll(canOverScroll);
1777 return { remainOffset, !NearZero(overOffset) && result.reachEdge };
1778 }
1779
HandleScroll(float offset,int32_t source,NestedState state)1780 ScrollResult ScrollablePattern::HandleScroll(float offset, int32_t source, NestedState state)
1781 {
1782 ScrollResult result = { 0, false };
1783 auto parent = parent_.Upgrade();
1784 auto overOffsets = GetOverScrollOffset(!IsReverse() ? offset : -offset);
1785 float backOverOffset = (!IsReverse() ? offset > 0 : offset < 0) ? overOffsets.end : overOffsets.start;
1786 if (NearZero(offset) || !NearZero(backOverOffset)) {
1787 ScrollState scrollState = source == SCROLL_FROM_ANIMATION ? ScrollState::FLING : ScrollState::SCROLL;
1788 ExecuteScrollFrameBegin(offset, scrollState);
1789 } else if (parent && !IsScrollSnap() && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::PARENT_FIRST) ||
1790 (offset > 0 && nestedScroll_.backward == NestedScrollMode::PARENT_FIRST))) {
1791 result = HandleScrollParentFirst(offset, source, state);
1792 } else if (parent && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::SELF_FIRST) ||
1793 (offset > 0 && nestedScroll_.backward == NestedScrollMode::SELF_FIRST))) {
1794 result = HandleScrollSelfFirst(offset, source, state);
1795 } else if (parent && ((offset < 0 && nestedScroll_.forward == NestedScrollMode::PARALLEL) ||
1796 (offset > 0 && nestedScroll_.backward == NestedScrollMode::PARALLEL))) {
1797 result = HandleScrollParallel(offset, source, state);
1798 } else {
1799 result = HandleScrollSelfOnly(offset, source, state);
1800 }
1801 bool moved = HandleScrollImpl(offset, source);
1802 NotifyMoved(moved);
1803 return result;
1804 }
1805
HandleScrollVelocity(float velocity)1806 bool ScrollablePattern::HandleScrollVelocity(float velocity)
1807 {
1808 if ((velocity > 0 && !IsAtTop()) || (velocity < 0 && !IsAtBottom())) {
1809 // trigger scroll animation if edge not reached
1810 if (scrollableEvent_ && scrollableEvent_->GetScrollable()) {
1811 scrollableEvent_->GetScrollable()->StartScrollAnimation(0.0f, velocity);
1812 return true;
1813 }
1814 return false;
1815 }
1816 return HandleOverScroll(velocity) || GetEdgeEffect() == EdgeEffect::FADE;
1817 }
1818
HandleOverScroll(float velocity)1819 bool ScrollablePattern::HandleOverScroll(float velocity)
1820 {
1821 auto parent = parent_.Upgrade();
1822 if (!parent || !nestedScroll_.NeedParent(velocity < 0)) {
1823 if (GetEdgeEffect() == EdgeEffect::SPRING && AnimateStoped()) {
1824 // trigger onScrollEnd later, when spring animation finishes
1825 ProcessSpringEffect(velocity);
1826 return true;
1827 }
1828 OnScrollEnd();
1829 return false;
1830 }
1831 // parent handle over scroll first
1832 if ((velocity < 0 && (nestedScroll_.forward == NestedScrollMode::SELF_FIRST)) ||
1833 (velocity > 0 && (nestedScroll_.backward == NestedScrollMode::SELF_FIRST)) ||
1834 !InstanceOf<ScrollablePattern>(parent)) {
1835 if (parent->HandleScrollVelocity(velocity)) {
1836 OnScrollEnd();
1837 return true;
1838 }
1839 if (GetEdgeEffect() == EdgeEffect::SPRING) {
1840 ProcessSpringEffect(velocity);
1841 return true;
1842 }
1843 }
1844
1845 // self handle over scroll first
1846 if (GetEdgeEffect() == EdgeEffect::SPRING) {
1847 ProcessSpringEffect(velocity);
1848 return true;
1849 }
1850 OnScrollEnd();
1851 return parent->HandleScrollVelocity(velocity);
1852 }
1853
ExecuteScrollFrameBegin(float & mainDelta,ScrollState state)1854 void ScrollablePattern::ExecuteScrollFrameBegin(float& mainDelta, ScrollState state)
1855 {
1856 auto context = PipelineContext::GetCurrentContext();
1857 if (!context || !scrollFrameBeginCallback_) {
1858 return;
1859 }
1860
1861 auto offset = Dimension(mainDelta / context->GetDipScale(), DimensionUnit::VP);
1862 auto scrollRes = scrollFrameBeginCallback_(-offset, state);
1863 mainDelta = -context->NormalizeToPx(scrollRes.offset);
1864 }
1865
OnScrollStartRecursive(float position)1866 void ScrollablePattern::OnScrollStartRecursive(float position)
1867 {
1868 HandleScrollImpl(position, SCROLL_FROM_START);
1869 auto parent = parent_.Upgrade();
1870 if (parent && nestedScroll_.NeedParent()) {
1871 parent->OnScrollStartRecursive(position);
1872 }
1873 }
1874
OnScrollEndRecursive(const std::optional<float> & velocity)1875 void ScrollablePattern::OnScrollEndRecursive(const std::optional<float>& velocity)
1876 {
1877 OnScrollEnd();
1878 auto parent = parent_.Upgrade();
1879 if (parent && nestedScroll_.NeedParent()) {
1880 parent->OnScrollEndRecursive(velocity);
1881 }
1882 }
1883
GetVelocity() const1884 float ScrollablePattern::GetVelocity() const
1885 {
1886 float velocity = 0.0f;
1887 CHECK_NULL_RETURN(scrollableEvent_, velocity);
1888 auto scrollable = scrollableEvent_->GetScrollable();
1889 CHECK_NULL_RETURN(scrollable, velocity);
1890 velocity = scrollable->GetCurrentVelocity();
1891 return velocity;
1892 }
1893
RegisterScrollingListener(const RefPtr<ScrollingListener> listener)1894 void ScrollablePattern::RegisterScrollingListener(const RefPtr<ScrollingListener> listener)
1895 {
1896 CHECK_NULL_VOID(listener);
1897 scrollingListener_.emplace_back(listener);
1898 }
1899
FireAndCleanScrollingListener()1900 void ScrollablePattern::FireAndCleanScrollingListener()
1901 {
1902 for (auto listener : scrollingListener_) {
1903 CHECK_NULL_VOID(listener);
1904 listener->NotifyScrollingEvent();
1905 }
1906 scrollingListener_.clear();
1907 }
1908
CleanScrollingListener()1909 void ScrollablePattern::CleanScrollingListener()
1910 {
1911 scrollingListener_.clear();
1912 }
1913
GetMainContentSize() const1914 float ScrollablePattern::GetMainContentSize() const
1915 {
1916 auto host = GetHost();
1917 CHECK_NULL_RETURN(host, 0.0);
1918 auto geometryNode = host->GetGeometryNode();
1919 CHECK_NULL_RETURN(geometryNode, 0.0);
1920 return geometryNode->GetPaddingSize().MainSize(axis_);
1921 }
1922
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1923 void ScrollablePattern::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1924 {
1925 if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1926 ScrollToIndex(0, false, ScrollAlign::START);
1927 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1928 // use LAST_ITEM for children count changed after scrollEdge(Edge.Bottom) and before layout
1929 ScrollToIndex(LAST_ITEM, false, ScrollAlign::END);
1930 }
1931 }
1932
NotifyFRCSceneInfo(const std::string & scene,double velocity,SceneStatus sceneStatus)1933 void ScrollablePattern::NotifyFRCSceneInfo(const std::string& scene, double velocity, SceneStatus sceneStatus)
1934 {
1935 auto host = GetHost();
1936 CHECK_NULL_VOID(host);
1937 host->AddFRCSceneInfo(scene, velocity, sceneStatus);
1938 }
1939
FireOnScrollStart()1940 void ScrollablePattern::FireOnScrollStart()
1941 {
1942 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
1943 if (GetScrollAbort()) {
1944 return;
1945 }
1946 auto scrollBar = GetScrollBar();
1947 if (scrollBar) {
1948 scrollBar->PlayScrollBarAppearAnimation();
1949 }
1950 StopScrollBarAnimatorByProxy();
1951 auto host = GetHost();
1952 CHECK_NULL_VOID(host);
1953 auto hub = host->GetEventHub<ScrollableEventHub>();
1954 CHECK_NULL_VOID(hub);
1955 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
1956 auto onScrollStart = hub->GetOnScrollStart();
1957 CHECK_NULL_VOID(onScrollStart);
1958 onScrollStart();
1959 }
1960
OnScrollStartCallback()1961 void ScrollablePattern::OnScrollStartCallback()
1962 {
1963 FireOnScrollStart();
1964 };
1965
FireOnScroll(float finalOffset,OnScrollEvent & onScroll) const1966 void ScrollablePattern::FireOnScroll(float finalOffset, OnScrollEvent& onScroll) const
1967 {
1968 auto offsetPX = Dimension(finalOffset);
1969 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
1970 auto scrollState = GetScrollState();
1971 if (!NearZero(finalOffset)) {
1972 onScroll(offsetVP, scrollState);
1973 }
1974 if (scrollStop_ && !GetScrollAbort()) {
1975 if (scrollState != ScrollState::IDLE) {
1976 onScroll(0.0_vp, ScrollState::IDLE);
1977 }
1978 }
1979 }
1980
OnScrollStop(const OnScrollStopEvent & onScrollStop)1981 void ScrollablePattern::OnScrollStop(const OnScrollStopEvent& onScrollStop)
1982 {
1983 if (scrollStop_) {
1984 if (!GetScrollAbort()) {
1985 auto host = GetHost();
1986 if (host != nullptr) {
1987 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1988 }
1989 if (onScrollStop) {
1990 SetScrollSource(SCROLL_FROM_NONE);
1991 onScrollStop();
1992 }
1993 auto scrollBar = GetScrollBar();
1994 if (scrollBar) {
1995 scrollBar->ScheduleDisappearDelayTask();
1996 }
1997 StartScrollBarAnimatorByProxy();
1998 }
1999 PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
2000 AceAsyncTraceEnd(0, TRAILING_ANIMATION);
2001 scrollStop_ = false;
2002 SetScrollAbort(false);
2003 }
2004 }
2005
2006 /**
2007 * @description: Register with the drag drop manager
2008 * @return None
2009 */
Register2DragDropManager()2010 void ScrollablePattern::Register2DragDropManager()
2011 {
2012 auto host = GetHost();
2013 CHECK_NULL_VOID(host);
2014 auto pipeline = PipelineContext::GetCurrentContext();
2015 CHECK_NULL_VOID(pipeline);
2016 auto dragDropManager = pipeline->GetDragDropManager();
2017 CHECK_NULL_VOID(dragDropManager);
2018 dragDropManager->RegisterDragStatusListener(host->GetId(), AceType::WeakClaim(AceType::RawPtr(host)));
2019 }
2020
2021 /**
2022 * @description: Determine whether it is in the hot zone, then
2023 * 1.Gives the rolling direction according to the location of the hot zone
2024 * 2.Gives the distance from the edge of the hot zone from the drag point
2025 * @param {PointF&} point The drag point
2026 * @return The distance from the edge of the hot zone from the drag point.scroll up:Offset percent is positive, scroll
2027 * down:Offset percent is negative
2028 */
IsInHotZone(const PointF & point)2029 float ScrollablePattern::IsInHotZone(const PointF& point)
2030 {
2031 auto host = GetHost();
2032 auto offset = 0.f;
2033 auto geometryNode = host->GetGeometryNode();
2034 CHECK_NULL_RETURN(geometryNode, 0.f);
2035
2036 auto wholeRect = geometryNode->GetFrameRect();
2037 wholeRect.SetOffset(host->GetTransformRelativeOffset());
2038 auto hotZoneHeightPX = HOT_ZONE_HEIGHT_VP_DIM.ConvertToPx();
2039 auto hotZoneWidthPX = HOT_ZONE_WIDTH_VP_DIM.ConvertToPx();
2040 if (isVertical()) {
2041 // create top hot zone,it is a rectangle
2042 auto topHotzone = wholeRect;
2043 topHotzone.SetHeight(hotZoneHeightPX);
2044
2045 // create bottom hot zone,it is a rectangle
2046 auto bottomHotzone = wholeRect;
2047 auto bottomZoneEdgeY = wholeRect.GetY() + wholeRect.Height();
2048 bottomHotzone.SetTop(bottomZoneEdgeY - hotZoneHeightPX);
2049 bottomHotzone.SetHeight(hotZoneHeightPX);
2050
2051 // Determines whether the drag point is within the hot zone,
2052 // then gives the scroll component movement direction according to which hot zone the point is in
2053 // top or bottom hot zone
2054 if (topHotzone.IsInRegion(point)) {
2055 offset = hotZoneHeightPX - point.GetY() + topHotzone.GetY();
2056 if (!NearZero(hotZoneHeightPX)) {
2057 return offset / hotZoneHeightPX;
2058 }
2059 } else if (bottomHotzone.IsInRegion(point)) {
2060 offset = bottomZoneEdgeY - point.GetY() - hotZoneHeightPX;
2061 if (!NearZero(hotZoneHeightPX)) {
2062 return offset / hotZoneHeightPX;
2063 }
2064 }
2065 } else {
2066 auto leftHotzone = wholeRect;
2067
2068 // create left hot zone,it is a rectangle
2069 leftHotzone.SetWidth(hotZoneWidthPX);
2070
2071 // create right hot zone,it is a rectangle
2072 auto rightHotzone = wholeRect;
2073 rightHotzone.SetWidth(hotZoneWidthPX);
2074 auto rightZoneEdgeX = wholeRect.GetX() + wholeRect.Width();
2075 rightHotzone.SetLeft(rightZoneEdgeX - hotZoneWidthPX);
2076
2077 // Determines whether the drag point is within the hot zone,
2078 // gives the scroll component movement direction according to which hot zone the point is in
2079 // left or right hot zone
2080 if (leftHotzone.IsInRegion(point)) {
2081 offset = hotZoneWidthPX - point.GetX() + wholeRect.GetX();
2082 if (!NearZero(hotZoneWidthPX)) {
2083 return offset / hotZoneWidthPX;
2084 }
2085 } else if (rightHotzone.IsInRegion(point)) {
2086 offset = rightZoneEdgeX - point.GetX() - hotZoneWidthPX;
2087 if (!NearZero(hotZoneWidthPX)) {
2088 return offset / hotZoneWidthPX;
2089 }
2090 }
2091 }
2092
2093 return 0.f;
2094 }
2095
2096 /**
2097 * @description: Determines whether the scroll component is in the vertical direction
2098 * @return True,If the scrolling component is vertical
2099 */
isVertical() const2100 bool ScrollablePattern::isVertical() const
2101 {
2102 return axis_ == Axis::VERTICAL;
2103 }
2104
2105 /**
2106 * @description: scroll up or down
2107 * @param {float} offsetPct offset percent.When scrolling in the vertical or horizontal direction, there is a distance
2108 * between the drag point and the outer edge of the hot zone, and the percentage represents the proportion of this
2109 * distance to the height or width of the hot zone
2110 * @return None
2111 */
HotZoneScroll(const float offsetPct)2112 void ScrollablePattern::HotZoneScroll(const float offsetPct)
2113 {
2114 auto host = GetHost();
2115 CHECK_NULL_VOID(IsScrollable());
2116 CHECK_NULL_VOID(!NearZero(offsetPct));
2117
2118 // There are three types of situations to consider.
2119 // 1. Enter the hot zone for the first time.
2120 // 2. When the drag point leaves the hot zone, it enters the hot zone again
2121 // 3. When the drag point moves within the hot zone, the hot zone offset changes
2122 CHECK_NULL_VOID(!NearEqual(lastHonezoneOffsetPct_, offsetPct));
2123
2124 if (AnimateRunning()) {
2125 // Variable speed rolling
2126 // When the drag point is in the hot zone, and the hot zone offset changes.
2127 // Then need to modify the offset percent
2128 if (velocityMotion_) {
2129 velocityMotion_->Reset(offsetPct);
2130 }
2131 return;
2132 }
2133
2134 if (!animator_) {
2135 animator_ = CREATE_ANIMATOR(PipelineBase::GetCurrentContext());
2136 animator_->AddStopListener([weak = AceType::WeakClaim(this)]() {
2137 auto pattern = weak.Upgrade();
2138 CHECK_NULL_VOID(pattern);
2139 pattern->AddHotZoneSenceInterface(SceneStatus::END);
2140 pattern->OnAnimateStop();
2141 });
2142 animator_->AddStartListener([weak = AceType::WeakClaim(this)]() {
2143 auto pattern = weak.Upgrade();
2144 CHECK_NULL_VOID(pattern);
2145 pattern->AddHotZoneSenceInterface(SceneStatus::START);
2146 });
2147 }
2148
2149 if (!velocityMotion_) {
2150 // Enter the hot zone for the first time.
2151 velocityMotion_ = AceType::MakeRefPtr<BezierVariableVelocityMotion>(
2152 offsetPct, [weak = WeakClaim(this)](float offset) -> bool {
2153 auto pattern = weak.Upgrade();
2154 CHECK_NULL_RETURN(pattern, true);
2155
2156 if (LessNotEqual(offset, 0) && pattern->IsAtBottom()) {
2157 // Stop scrolling when reach the bottom
2158 return true;
2159 } else if (GreatNotEqual(offset, 0) && pattern->IsAtTop()) {
2160 // Stop scrolling when reach the top
2161 return true;
2162 }
2163 return false;
2164 });
2165 velocityMotion_->AddListener([weakScroll = AceType::WeakClaim(this)](double offset) {
2166 // Get the distance component need to roll from BezierVariableVelocityMotion
2167 // Roll up: negative value, Roll up: positive value
2168 auto pattern = weakScroll.Upgrade();
2169 CHECK_NULL_VOID(pattern);
2170 pattern->UpdateCurrentOffset(offset, SCROLL_FROM_AXIS);
2171 pattern->UpdateMouseStart(offset);
2172 pattern->AddHotZoneSenceInterface(SceneStatus::RUNNING);
2173 });
2174 velocityMotion_->ReInit(offsetPct);
2175 } else {
2176 // When the drag point leaves the hot zone, it enters the hot zone again.Then need to reset offset percent.
2177 velocityMotion_->ReInit(offsetPct);
2178 }
2179 // Save the last offset percent
2180 lastHonezoneOffsetPct_ = offsetPct;
2181 animator_->PlayMotion(velocityMotion_);
2182 FireOnScrollStart();
2183 }
2184
2185 /**
2186 * @description: When the drag point leaves the hot zone, stop the animation.
2187 * @return None
2188 */
StopHotzoneScroll()2189 void ScrollablePattern::StopHotzoneScroll()
2190 {
2191 if (!AnimateStoped()) {
2192 animator_->Stop();
2193 }
2194 }
2195
2196 /**
2197 * @description: Handle drag and drop events
2198 * When a drag point enters or moves over a component, determine whether it is within the hot zone.
2199 * When leave the component, stop scrolling
2200 * @param {DragEventType&} dragEventType Drag the event type
2201 * @param {NotifyDragEvent&} notifyDragEvent Drag event
2202 * @return None
2203 */
HandleHotZone(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2204 void ScrollablePattern::HandleHotZone(
2205 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2206 {
2207 // The starting version of the auto-scroll feature is 11
2208 CHECK_NULL_VOID(Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN));
2209 PointF point(static_cast<float>(notifyDragEvent->GetX()), static_cast<float>(notifyDragEvent->GetY()));
2210 switch (dragEventType) {
2211 case DragEventType::ENTER: {
2212 HandleMoveEventInComp(point);
2213 break;
2214 }
2215 case DragEventType::MOVE: {
2216 HandleMoveEventInComp(point);
2217 break;
2218 }
2219 case DragEventType::DROP:
2220 case DragEventType::LEAVE: {
2221 HandleLeaveHotzoneEvent();
2222 break;
2223 }
2224 default:
2225 break;
2226 }
2227 }
2228
2229 /**
2230 * @description:When a drag point is inside the scroll component, it is necessary to handle the events of each moving
2231 * point
2232 * @param {PointF&} point the drag point
2233 * @return None
2234 */
HandleMoveEventInComp(const PointF & point)2235 void ScrollablePattern::HandleMoveEventInComp(const PointF& point)
2236 {
2237 float offsetPct = IsInHotZone(point);
2238 if (NearZero(offsetPct)) {
2239 // Although it entered the rolling component, it is not in the rolling component hot zone.Then stop
2240 // scrolling
2241 HandleLeaveHotzoneEvent();
2242 } else {
2243 // The drag point enters the hot zone
2244 HotZoneScroll(offsetPct);
2245 }
2246 }
2247
2248 /**
2249 * @description:When the drag point is not in the hot zone, need to stop scrolling, if it exists.
2250 * This function is executed multiple times
2251 * @return None
2252 */
HandleLeaveHotzoneEvent()2253 void ScrollablePattern::HandleLeaveHotzoneEvent()
2254 {
2255 // Stop scrolling up and down
2256 StopHotzoneScroll();
2257 }
2258
2259 /**
2260 * @description: This is the entry point for handling drag events
2261 * @return None
2262 */
HandleOnDragStatusCallback(const DragEventType & dragEventType,const RefPtr<NotifyDragEvent> & notifyDragEvent)2263 void ScrollablePattern::HandleOnDragStatusCallback(
2264 const DragEventType& dragEventType, const RefPtr<NotifyDragEvent>& notifyDragEvent)
2265 {
2266 HandleHotZone(dragEventType, notifyDragEvent);
2267 }
2268
2269 /**
2270 * @description: Cancel registration with the drag drop manager
2271 * @return None
2272 */
UnRegister2DragDropManager()2273 void ScrollablePattern::UnRegister2DragDropManager()
2274 {
2275 auto host = GetHost();
2276 CHECK_NULL_VOID(host);
2277 auto pipeline = PipelineContext::GetCurrentContext();
2278 CHECK_NULL_VOID(pipeline);
2279 auto dragDropManager = pipeline->GetDragDropManager();
2280 CHECK_NULL_VOID(dragDropManager);
2281 dragDropManager->UnRegisterDragStatusListener(host->GetId());
2282 }
2283
NeedCoordinateScrollWithNavigation(double offset,int32_t source,const OverScrollOffset & overOffsets)2284 bool ScrollablePattern::NeedCoordinateScrollWithNavigation(
2285 double offset, int32_t source, const OverScrollOffset& overOffsets)
2286 {
2287 if (!navBarPattern_) {
2288 return false;
2289 }
2290 return (GreatNotEqual(overOffsets.start, 0.0) || navBarPattern_->CanCoordScrollUp(offset)) &&
2291 (axis_ == Axis::VERTICAL) && (source != SCROLL_FROM_ANIMATION_SPRING);
2292 }
2293
AddHotZoneSenceInterface(SceneStatus scene)2294 void ScrollablePattern::AddHotZoneSenceInterface(SceneStatus scene)
2295 {
2296 CHECK_NULL_VOID(velocityMotion_);
2297 auto velocity = velocityMotion_->GetCurrentVelocity();
2298 NotifyFRCSceneInfo(SCROLL_IN_HOTZONE_SCENE, velocity, scene);
2299 }
2300 } // namespace OHOS::Ace::NG
2301