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