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