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