• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H
18 
19 #include "base/geometry/axis.h"
20 #include "core/components/common/layout/constants.h"
21 #include "core/components_ng/gestures/recognizers/pan_recognizer.h"
22 #include "core/components_ng/gestures/recognizers/parallel_recognizer.h"
23 #include "core/components_ng/pattern/scroll/free_scroll_controller.h"
24 #include "core/components_ng/pattern/scroll/inner/scroll_bar_2d.h"
25 #include "core/components_ng/pattern/scroll/scroll_accessibility_property.h"
26 #include "core/components_ng/pattern/scroll/scroll_content_modifier.h"
27 #include "core/components_ng/pattern/scroll/scroll_edge_effect.h"
28 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
29 #include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h"
30 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
31 #include "core/components_ng/pattern/scroll/scroll_paint_method.h"
32 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h"
33 #include "core/components_ng/pattern/scroll/zoom_controller.h"
34 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
35 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
36 
37 #ifdef SUPPORT_DIGITAL_CROWN
38 #include "core/common/vibrator/vibrator_utils.h"
39 #endif
40 
41 namespace OHOS::Ace::NG {
42 class InspectorFilter;
43 
44 class ScrollPattern : public ScrollablePattern {
45     DECLARE_ACE_TYPE(ScrollPattern, ScrollablePattern);
46 
47 public:
ScrollPattern()48     ScrollPattern() : ScrollablePattern(EdgeEffect::NONE, true) {}
49 
50     ~ScrollPattern() override = default;
51 
UsResRegion()52     bool UsResRegion() override
53     {
54         return false;
55     }
56 
CreateLayoutProperty()57     RefPtr<LayoutProperty> CreateLayoutProperty() override
58     {
59         return MakeRefPtr<ScrollLayoutProperty>();
60     }
61 
CreateAccessibilityProperty()62     RefPtr<AccessibilityProperty> CreateAccessibilityProperty() override
63     {
64         return MakeRefPtr<ScrollAccessibilityProperty>();
65     }
66 
CreateLayoutAlgorithm()67     RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm() override
68     {
69         if (freeScroll_) {
70             return MakeRefPtr<ScrollLayoutAlgorithm>(freeScroll_->GetOffset().GetX(), freeScroll_->GetOffset().GetY());
71         }
72         return MakeRefPtr<ScrollLayoutAlgorithm>(currentOffset_);
73     }
74 
75     RefPtr<PaintProperty> CreatePaintProperty() override;
76 
77     RefPtr<NodePaintMethod> CreateNodePaintMethod() override;
78 
OpIncType()79     OPINC_TYPE_E OpIncType() override
80     {
81         return OPINC_PARENT_POSSIBLE;
82     }
83 
CreateEventHub()84     RefPtr<EventHub> CreateEventHub() override
85     {
86         return MakeRefPtr<ScrollEventHub>();
87     }
88 
IsScrollable()89     bool IsScrollable() const override
90     {
91         return GetAxis() != Axis::NONE;
92     }
93 
IsPositiveScrollableDistance()94     bool IsPositiveScrollableDistance()
95     {
96         return Positive(scrollableDistance_);
97     }
98 
99     bool OnScrollCallback(float offset, int32_t source) override;
100 
101     void OnScrollEndCallback() override;
102 
GetCurrentPosition()103     double GetCurrentPosition() const
104     {
105         return currentOffset_;
106     }
107 
GetTotalOffset()108     double GetTotalOffset() const override
109     {
110         return -currentOffset_;
111     }
112 
113     void ResetPosition();
114 
GetCurrentOffset()115     Offset GetCurrentOffset() const
116     {
117         if (GetAxis() == Axis::HORIZONTAL) {
118             return Offset { currentOffset_, 0 };
119         }
120         return Offset { 0, currentOffset_ };
121     }
122 
GetScrollableDistance()123     float GetScrollableDistance() const
124     {
125         return scrollableDistance_;
126     }
127 
IsRowReverse()128     bool IsRowReverse() const
129     {
130         return direction_ == FlexDirection::ROW_REVERSE;
131     }
132 
IsColReverse()133     bool IsColReverse() const
134     {
135         return direction_ == FlexDirection::COLUMN_REVERSE;
136     }
137 
GetScrollPositionController()138     RefPtr<ScrollableController> GetScrollPositionController() const
139     {
140         return positionController_;
141     }
142 
SetDirection(FlexDirection direction)143     void SetDirection(FlexDirection direction)
144     {
145         direction_ = direction;
146     }
147 
GetFocusPattern()148     FocusPattern GetFocusPattern() const override
149     {
150         return { FocusType::SCOPE, true };
151     }
152 
153     bool ScrollToNode(const RefPtr<FrameNode>& focusFrameNode) override;
154     ScrollOffsetAbility GetScrollOffsetAbility() override;
155 
156     bool IsAtTop() const override;
157     bool IsAtBottom(bool considerRepeat = false) const override;
158     bool IsOutOfBoundary(bool useCurrentDelta = true) override;
159     OverScrollOffset GetOverScrollOffset(double delta) const override;
160 
161     void OnAnimateStop() override;
162     bool UpdateCurrentOffset(float offset, int32_t source) override;
163     void ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth) override;
164 
165     void CheckScrollToEdge();
166 
GetScrollEdgeType()167     ScrollEdgeType GetScrollEdgeType() const override
168     {
169         return scrollEdgeType_;
170     }
171 
SetScrollEdgeType(ScrollEdgeType scrollEdgeType)172     void SetScrollEdgeType(ScrollEdgeType scrollEdgeType) override
173     {
174         scrollEdgeType_ = scrollEdgeType;
175     }
176 
177     void ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish = nullptr);
178     void ScrollPage(bool reverse, bool smooth = false,
179         AccessibilityScrollType scrollType = AccessibilityScrollType::SCROLL_FULL) override;
180     void ScrollTo(float position) override;
181     void JumpToPosition(float position, int32_t source = SCROLL_FROM_JUMP);
GetMainContentSize()182     float GetMainContentSize() const override
183     {
184         return viewPortLength_;
185     }
SupportScrollToIndex()186     bool SupportScrollToIndex() const override
187     {
188         return false;
189     }
190     bool ScrollPageCheck(float delta, int32_t source);
191     void AdjustOffset(float& delta, int32_t source);
192     Rect GetItemRect(int32_t index) const override;
193 
194     // scrollSnap
195     std::optional<float> CalcPredictSnapOffset(float delta, float dragDistance = 0.f, float velocity = 0.f,
196         SnapDirection snapDirection = SnapDirection::NONE) override;
197     std::optional<float> CalcPredictNextSnapOffset(float delta, SnapDirection snapDirection);
198     bool NeedScrollSnapToSide(float delta) override;
199     void CaleSnapOffsets(const RefPtr<FrameNode>& host);
200     void CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign, const RefPtr<FrameNode>& host);
201     void CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign);
202 
203     float GetSelectScrollWidth();
204 
IsSnapToInterval()205     bool IsSnapToInterval() const
206     {
207         return snapPaginations_.empty();
208     }
209 
GetSnapOffsets()210     std::vector<float> GetSnapOffsets() const
211     {
212         return snapOffsets_;
213     }
214 
SetSnapOffsets(const std::vector<float> & snapOffset)215     void SetSnapOffsets(const std::vector<float>& snapOffset)
216     {
217         snapOffsets_ = snapOffset;
218     }
219 
SetIntervalSize(const Dimension & intervalSize)220     void SetIntervalSize(const Dimension& intervalSize)
221     {
222         if (intervalSize_ != intervalSize) {
223             intervalSize_ = intervalSize;
224             TAG_LOGI(AceLogTag::ACE_SCROLL, "scroll setIntervalSize:%{public}f", intervalSize.Value());
225             scrollSnapUpdate_ = true;
226         }
227     }
228 
229 #ifdef SUPPORT_DIGITAL_CROWN
230     void StartVibrateFeedback();
231 
SetReachBoundary(bool flag)232     void SetReachBoundary(bool flag)
233     {
234         reachBoundary_ = flag;
235     }
236 #endif
237 
GetIntervalSize()238     Dimension GetIntervalSize() const
239     {
240         return intervalSize_;
241     }
242 
SetSnapPaginations(const std::vector<Dimension> & snapPaginations)243     void SetSnapPaginations(const std::vector<Dimension>& snapPaginations)
244     {
245         if (snapPaginations_ != snapPaginations) {
246             snapPaginations_ = snapPaginations;
247             scrollSnapUpdate_ = true;
248         }
249     }
250 
GetSnapPaginations()251     std::vector<Dimension> GetSnapPaginations() const
252     {
253         return snapPaginations_;
254     }
255 
SetEnableSnapToSide(const std::pair<bool,bool> & enableSnapToSide)256     void SetEnableSnapToSide(const std::pair<bool, bool>& enableSnapToSide)
257     {
258         enableSnapToSide_ = enableSnapToSide;
259     }
260 
GetEnableSnapToSide()261     std::pair<bool, bool> GetEnableSnapToSide() const
262     {
263         return enableSnapToSide_;
264     }
265 
SetScrollSnapUpdate(bool scrollSnapUpdate)266     void SetScrollSnapUpdate(bool scrollSnapUpdate)
267     {
268         scrollSnapUpdate_ = scrollSnapUpdate;
269     }
270 
GetScrollSnapUpdate()271     bool GetScrollSnapUpdate() const
272     {
273         return scrollSnapUpdate_;
274     }
275 
GetScrollSnapAlign()276     ScrollSnapAlign GetScrollSnapAlign() const
277     {
278         auto host = GetHost();
279         CHECK_NULL_RETURN(host, ScrollSnapAlign::NONE);
280         auto scrollLayoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
281         CHECK_NULL_RETURN(scrollLayoutProperty, ScrollSnapAlign::NONE);
282         return scrollLayoutProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
283     }
284 
285     ScrollSnapAlign GetScrollSnapAlign(const RefPtr<FrameNode>& host) const;
286 
287     std::string ProvideRestoreInfo() override;
288     void OnRestoreInfo(const std::string& restoreInfo) override;
289 
SetIsWidthModifiedBySelect(bool isModified)290     void SetIsWidthModifiedBySelect(bool isModified)
291     {
292         isWidthModifiedBySelect_ = isModified;
293     }
294 
IsWidthModifiedBySelect()295     bool IsWidthModifiedBySelect() const
296     {
297         return isWidthModifiedBySelect_;
298     }
299 
SetIsSelectScroll(bool isSelect)300     void SetIsSelectScroll(bool isSelect)
301     {
302         isSelectScroll_ = isSelect;
303     }
304 
IsSelectScroll()305     bool IsSelectScroll() const
306     {
307         return isSelectScroll_;
308     }
309 
SetHasOptionWidth(bool hasOptionWidth)310     void SetHasOptionWidth(bool hasOptionWidth)
311     {
312         hasOptionWidth_ = hasOptionWidth;
313     }
314 
GetHasOptionWidth()315     bool GetHasOptionWidth()
316     {
317         return hasOptionWidth_;
318     }
319 
SetEnablePaging(ScrollPagingStatus status)320     void SetEnablePaging(ScrollPagingStatus status)
321     {
322         enablePagingStatus_ = status;
323     }
324 
GetEnablePaging()325     ScrollPagingStatus GetEnablePaging()
326     {
327         return enablePagingStatus_;
328     }
329 
IsScrollSnap()330     bool IsScrollSnap() override
331     {
332         return !snapOffsets_.empty() &&
333                (GetScrollSnapAlign() != ScrollSnapAlign::NONE || enablePagingStatus_ == ScrollPagingStatus::VALID);
334     }
335 
GetSnapType()336     SnapType GetSnapType() override
337     {
338         return (!snapOffsets_.empty() &&
339                    (GetScrollSnapAlign() != ScrollSnapAlign::NONE || enablePagingStatus_ == ScrollPagingStatus::VALID))
340                    ? SnapType::SCROLL_SNAP
341                    : SnapType::NONE_SNAP;
342     }
343 
344     void TriggerModifyDone();
345 
SetInitialOffset(const OffsetT<CalcDimension> & offset)346     void SetInitialOffset(const OffsetT<CalcDimension>& offset)
347     {
348         initialOffset_ = offset;
349     }
350 
GetInitialOffset()351     OffsetT<CalcDimension> GetInitialOffset() const
352     {
353         return initialOffset_.has_value() ? initialOffset_.value() : OffsetT(CalcDimension(), CalcDimension());
354     }
355 
NeedSetInitialOffset()356     bool NeedSetInitialOffset()
357     {
358         return !isInitialized_ && initialOffset_.has_value();
359     }
360 
361     void AddScrollMeasureInfo(const std::optional<LayoutConstraintF>& parentConstraint,
362         const std::optional<LayoutConstraintF>& childConstraint, const SizeF& selfSize, const SizeF& childSize);
363 
364     void AddScrollLayoutInfo();
365 
366     void GetScrollSnapAlignDumpInfo();
367     void GetScrollSnapAlignDumpInfo(std::unique_ptr<JsonValue>& json);
368 
369     void GetScrollPagingStatusDumpInfo();
370     void GetScrollPagingStatusDumpInfo(std::unique_ptr<JsonValue>& json);
371     void DumpAdvanceInfo() override;
372     void DumpAdvanceInfo(std::unique_ptr<JsonValue>& json) override;
373 
GetViewSize()374     const SizeF& GetViewSize() const
375     {
376         return viewSize_;
377     }
378 
GetViewPortExtent()379     const SizeF& GetViewPortExtent() const
380     {
381         return viewPortExtent_;
382     }
383 
384     void ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const override;
385 
386     bool StartSnapAnimation(SnapAnimationOptions snapAnimationOptions) override;
387 
388     void StartScrollSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity, bool fromScrollBar);
389 
390     SizeF GetChildrenExpandedSize() override;
391 
392     void TriggerScrollBarDisplay();
393 
IsEnablePagingValid()394     bool IsEnablePagingValid() override
395     {
396         return enablePagingStatus_ == ScrollPagingStatus::VALID && GetScrollSnapAlign() == ScrollSnapAlign::NONE;
397     }
398 
399 protected:
400     void DoJump(float position, int32_t source = SCROLL_FROM_JUMP);
401 
402 private:
403     void OnModifyDone() override;
404     bool OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config) override;
405 
406     bool IsCrashTop() const;
407     bool IsCrashBottom() const;
408     bool ReachStart(bool firstLayout) const;
409     bool ReachEnd(bool firstLayout) const;
410     bool IsScrollOutOnEdge(float delta) const;
411     void HandleCrashTop();
412     void HandleCrashBottom();
413 
414     void RegisterScrollBarEventTask();
415     void HandleScrollEffect();
416     void ValidateOffset(int32_t source);
417     float ValidateOffset(int32_t source, float willScrollOffset);
418     void HandleScrollPosition(float scroll);
419     float FireTwoDimensionOnWillScroll(float scroll);
420     TwoDimensionScrollResult FireObserverTwoDimensionOnWillScroll(Dimension xOffset, Dimension yOffset,
421         ScrollState state, ScrollSource source);
422     void FireOnDidScroll(float scroll);
423     void FireOnReachStart(const OnReachEvent& onReachStart, const OnReachEvent& onJSFrameNodeReachStart) override;
424     void FireOnReachEnd(const OnReachEvent& onReachEnd, const OnReachEvent& onJSFrameNodeReachEnd) override;
425     void SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect) override;
426     void UpdateScrollBarOffset() override;
427     void SetAccessibilityAction() override;
428     bool ScrollSnapTrigger();
429     void CheckScrollable();
430     OffsetF GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const;
431     bool SetScrollProperties(const RefPtr<LayoutWrapper>& dirty, const RefPtr<FrameNode>& host);
432     std::string GetScrollSnapPagination() const;
433     void OnColorModeChange(uint32_t colorMode) override;
434 
435     float currentOffset_ = 0.0f;
436     float lastOffset_ = 0.0f;
437     // keep lastOffset_ for compatibility, use prevOffset_ for onReachStart/onReachEnd
438     float prevOffset_ = 0.0f;
439     float scrollableDistance_ = 0.0f;
440     float viewPortLength_ = 0.0f;
441     SizeF viewPort_;
442     SizeF viewSize_;
443     SizeF viewPortExtent_;
444     FlexDirection direction_ { FlexDirection::COLUMN };
445 
446     /* ============================= zoom Enhancements ============================= */
447 public:
448     void SetMaxZoomScale(float scale);
449     float GetMaxZoomScale() const;
450     void SetMinZoomScale(float scale);
451     float GetMinZoomScale() const;
452     void SetZoomScale(std::optional<float> scale);
453     float GetZoomScale() const;
454     void UpdateZoomScale(float scale);
455     void SetEnableBouncesZoom(bool enable);
456     bool GetEnableBouncesZoom() const;
457     void ProcessZoomScale();
458     void SetChildScale(std::optional<float> scale);
459 private:
460     void UpdatePinchGesture();
461     friend class ZoomController;
462     RefPtr<ZoomController> zoomCtrl_;
463     float maxZoomScale_ = 1.0f;
464     float minZoomScale_ = 1.0f;
465     std::optional<float> zoomScale_;
466     std::optional<float> childScale_;
467     bool enableBouncesZoom_ = true;
468     /* ============================================================================== */
469 
470     /* ============================= Free Scroll Enhancements ============================= */
471 public:
472     /**
473      * @return Pan gesture recognizer configured for Axis::FREE mode
474      */
475     RefPtr<NGGestureRecognizer> GetOverrideRecognizer();
Get2DScrollBar()476     RefPtr<ScrollBar2D> Get2DScrollBar() const
477     {
478         return scrollBar2d_;
479     }
480 
481     Offset GetFreeScrollOffset() const final;
482     bool FreeScrollBy(const OffsetF& delta) final;
483     bool FreeScrollPage(bool reverse, bool smooth) final;
484     bool FreeScrollToEdge(ScrollEdgeType type, bool smooth, std::optional<float> velocity) final;
485     void FreeScrollTo(const ScrollControllerBase::ScrollToParam& param) final;
486 
487 private:
488     RefPtr<ParallelRecognizer> gestureGroup_;
489     RefPtr<FreeScrollController> freeScroll_;
490     RefPtr<ScrollBar2D> scrollBar2d_;
491     /* ============================================================================== */
492 
493     // scrollSnap
494     std::vector<float> snapOffsets_;
495     std::vector<Dimension> snapPaginations_;
496     std::pair<bool, bool> enableSnapToSide_ = { true, true };
497     Dimension intervalSize_;
498     bool scrollSnapUpdate_ = false;
499 
500     bool isWidthModifiedBySelect_ = false;
501     bool isSelectScroll_ = false;
502     bool hasOptionWidth_ = false;
503 
504     // enablePaging
505     ScrollPagingStatus enablePagingStatus_ = ScrollPagingStatus::NONE;
506     float lastPageLength_ = 0.0f;
507     float GetPagingOffset(float delta, float dragDistance, float velocity) const;
508     float GetPagingDelta(float dragDistance, float velocity, float pageLength) const;
509 
510     RefPtr<ScrollContentModifier> scrollContentModifier_;
511 
512     // initialOffset
513     std::optional<OffsetT<CalcDimension>> initialOffset_;
514 
515     // scrollToEdge
516     ScrollEdgeType scrollEdgeType_ = ScrollEdgeType::SCROLL_NONE;
517 
518     // dump info
519     std::list<ScrollLayoutInfo> scrollLayoutInfos_;
520     std::list<ScrollMeasureInfo> scrollMeasureInfos_;
521 
522 #ifdef SUPPORT_DIGITAL_CROWN
523     int32_t crownEventNum_ = 0;
524     bool reachBoundary_ = false;
525     int64_t lastTime_ = 0;
526 #endif
527 };
528 
529 } // namespace OHOS::Ace::NG
530 
531 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H
532