1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #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/pattern/scroll/scroll_accessibility_property.h"
22 #include "core/components_ng/pattern/scroll/scroll_content_modifier.h"
23 #include "core/components_ng/pattern/scroll/scroll_edge_effect.h"
24 #include "core/components_ng/pattern/scroll/scroll_event_hub.h"
25 #include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h"
26 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
27 #include "core/components_ng/pattern/scroll/scroll_paint_method.h"
28 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h"
29 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
30 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
31 
32 namespace OHOS::Ace::NG {
33 class InspectorFilter;
34 
35 class ScrollPattern : public ScrollablePattern {
36     DECLARE_ACE_TYPE(ScrollPattern, ScrollablePattern);
37 
38 public:
ScrollPattern()39     ScrollPattern() : ScrollablePattern(EdgeEffect::NONE, true) {}
40 
41     ~ScrollPattern() override = default;
42 
UsResRegion()43     bool UsResRegion() override
44     {
45         return false;
46     }
47 
CreateLayoutProperty()48     RefPtr<LayoutProperty> CreateLayoutProperty() override
49     {
50         return MakeRefPtr<ScrollLayoutProperty>();
51     }
52 
CreateAccessibilityProperty()53     RefPtr<AccessibilityProperty> CreateAccessibilityProperty() override
54     {
55         return MakeRefPtr<ScrollAccessibilityProperty>();
56     }
57 
CreateLayoutAlgorithm()58     RefPtr<LayoutAlgorithm> CreateLayoutAlgorithm() override
59     {
60         auto layoutAlgorithm = MakeRefPtr<ScrollLayoutAlgorithm>(currentOffset_);
61         return layoutAlgorithm;
62     }
63 
CreateNodePaintMethod()64     RefPtr<NodePaintMethod> CreateNodePaintMethod() override
65     {
66         auto host = GetHost();
67         CHECK_NULL_RETURN(host, nullptr);
68         auto layoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
69         CHECK_NULL_RETURN(layoutProperty, nullptr);
70         auto layoutDirection = layoutProperty->GetNonAutoLayoutDirection();
71         auto drawDirection = (layoutDirection == TextDirection::RTL);
72         auto paint = MakeRefPtr<ScrollPaintMethod>(GetAxis() == Axis::HORIZONTAL, drawDirection);
73         paint->SetScrollBar(GetScrollBar());
74         CreateScrollBarOverlayModifier();
75         paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
76         auto scrollEffect = GetScrollEdgeEffect();
77         if (scrollEffect && scrollEffect->IsFadeEffect()) {
78             paint->SetEdgeEffect(scrollEffect);
79         }
80         if (!scrollContentModifier_) {
81             scrollContentModifier_ = AceType::MakeRefPtr<ScrollContentModifier>();
82         }
83         paint->SetContentModifier(scrollContentModifier_);
84         UpdateFadingEdge(paint);
85         return paint;
86     }
87     RefPtr<PaintProperty> CreatePaintProperty() override;
88 
OpIncType()89     OPINC_TYPE_E OpIncType() override
90     {
91         return OPINC_PARENT_POSSIBLE;
92     }
93 
CreateEventHub()94     RefPtr<EventHub> CreateEventHub() override
95     {
96         return MakeRefPtr<ScrollEventHub>();
97     }
98 
IsScrollable()99     bool IsScrollable() const override
100     {
101         return GetAxis() != Axis::NONE;
102     }
103 
IsPositiveScrollableDistance()104     bool IsPositiveScrollableDistance()
105     {
106         return Positive(scrollableDistance_);
107     }
108 
109     bool OnScrollCallback(float offset, int32_t source) override;
110 
111     void OnScrollEndCallback() override;
112 
GetCurrentPosition()113     double GetCurrentPosition() const
114     {
115         return currentOffset_;
116     }
117 
GetTotalOffset()118     float GetTotalOffset() const override
119     {
120         return -currentOffset_;
121     }
122 
123     void ResetPosition();
124 
GetCurrentOffset()125     Offset GetCurrentOffset() const
126     {
127         if (GetAxis() == Axis::HORIZONTAL) {
128             return Offset{currentOffset_, 0};
129         }
130         return Offset{0, currentOffset_};
131     }
132 
GetScrollableDistance()133     float GetScrollableDistance() const
134     {
135         return scrollableDistance_;
136     }
137 
IsRowReverse()138     bool IsRowReverse() const
139     {
140         return direction_ == FlexDirection::ROW_REVERSE;
141     }
142 
IsColReverse()143     bool IsColReverse() const
144     {
145         return  direction_ == FlexDirection::COLUMN_REVERSE;
146     }
147 
GetScrollPositionController()148     RefPtr<ScrollableController> GetScrollPositionController() const
149     {
150         return positionController_;
151     }
152 
SetDirection(FlexDirection direction)153     void SetDirection(FlexDirection direction)
154     {
155         direction_ = direction;
156     }
157 
GetFocusPattern()158     FocusPattern GetFocusPattern() const override
159     {
160         return { FocusType::SCOPE, true };
161     }
162 
163     bool ScrollToNode(const RefPtr<FrameNode>& focusFrameNode) override;
164     ScrollOffsetAbility GetScrollOffsetAbility() override;
165 
166     bool IsAtTop() const override;
167     bool IsAtBottom() const override;
168     bool IsOutOfBoundary(bool useCurrentDelta = true) override;
169     OverScrollOffset GetOverScrollOffset(double delta) const override;
170 
171     void OnAnimateStop() override;
172     bool UpdateCurrentOffset(float offset, int32_t source) override;
173     void ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth) override;
174 
175     void CheckScrollToEdge();
176 
GetScrollEdgeType()177     ScrollEdgeType GetScrollEdgeType() const override
178     {
179         return scrollEdgeType_;
180     }
181 
SetScrollEdgeType(ScrollEdgeType scrollEdgeType)182     void SetScrollEdgeType(ScrollEdgeType scrollEdgeType) override
183     {
184         scrollEdgeType_ = scrollEdgeType;
185     }
186 
187     void ScrollBy(float pixelX, float pixelY, bool smooth, const std::function<void()>& onFinish = nullptr);
188     void ScrollPage(bool reverse, bool smooth = false,
189         AccessibilityScrollType scrollType = AccessibilityScrollType::SCROLL_FULL) override;
190     void ScrollTo(float position) override;
191     void JumpToPosition(float position, int32_t source = SCROLL_FROM_JUMP);
GetMainContentSize()192     float GetMainContentSize() const override
193     {
194         return viewPortLength_;
195     }
SupportScrollToIndex()196     bool SupportScrollToIndex() const override
197     {
198         return false;
199     }
200     bool ScrollPageCheck(float delta, int32_t source);
201     void AdjustOffset(float& delta, int32_t source);
202     Rect GetItemRect(int32_t index) const override;
203 
204     // scrollSnap
205     std::optional<float> CalePredictSnapOffset(float delta, float dragDistance = 0.f, float velocity = 0.f) override;
206     bool NeedScrollSnapToSide(float delta) override;
207     void CaleSnapOffsets();
208     void CaleSnapOffsetsByInterval(ScrollSnapAlign scrollSnapAlign);
209     void CaleSnapOffsetsByPaginations(ScrollSnapAlign scrollSnapAlign);
210 
211     float GetSelectScrollWidth();
212 
IsSnapToInterval()213     bool IsSnapToInterval() const
214     {
215         return snapPaginations_.empty();
216     }
217 
GetSnapOffsets()218     std::vector<float> GetSnapOffsets() const
219     {
220         return snapOffsets_;
221     }
222 
SetSnapOffsets(const std::vector<float> & snapOffset)223     void SetSnapOffsets(const std::vector<float>& snapOffset)
224     {
225         snapOffsets_ = snapOffset;
226     }
227 
SetIntervalSize(const Dimension & intervalSize)228     void SetIntervalSize(const Dimension& intervalSize)
229     {
230         if (intervalSize_ != intervalSize) {
231             intervalSize_ = intervalSize;
232             TAG_LOGI(AceLogTag::ACE_SCROLL, "scroll setIntervalSize:%{public}f", intervalSize.Value());
233             scrollSnapUpdate_ = true;
234         }
235     }
236 
GetIntervalSize()237     Dimension GetIntervalSize() const
238     {
239         return intervalSize_;
240     }
241 
SetSnapPaginations(const std::vector<Dimension> & snapPaginations)242     void SetSnapPaginations(const std::vector<Dimension>& snapPaginations)
243     {
244         if (snapPaginations_ != snapPaginations) {
245             snapPaginations_ = snapPaginations;
246             scrollSnapUpdate_ = true;
247         }
248     }
249 
GetSnapPaginations()250     std::vector<Dimension> GetSnapPaginations() const
251     {
252         return snapPaginations_;
253     }
254 
SetEnableSnapToSide(const std::pair<bool,bool> & enableSnapToSide)255     void SetEnableSnapToSide(const std::pair<bool, bool>& enableSnapToSide)
256     {
257         enableSnapToSide_ = enableSnapToSide;
258     }
259 
GetEnableSnapToSide()260     std::pair<bool, bool> GetEnableSnapToSide() const
261     {
262         return enableSnapToSide_;
263     }
264 
SetScrollSnapUpdate(bool scrollSnapUpdate)265     void SetScrollSnapUpdate(bool scrollSnapUpdate)
266     {
267         scrollSnapUpdate_ = scrollSnapUpdate;
268     }
269 
GetScrollSnapUpdate()270     bool GetScrollSnapUpdate() const
271     {
272         return scrollSnapUpdate_;
273     }
274 
GetScrollSnapAlign()275     ScrollSnapAlign GetScrollSnapAlign() const
276     {
277         auto host = GetHost();
278         CHECK_NULL_RETURN(host, ScrollSnapAlign::NONE);
279         auto scrollLayoutProperty = host->GetLayoutProperty<ScrollLayoutProperty>();
280         CHECK_NULL_RETURN(scrollLayoutProperty, ScrollSnapAlign::NONE);
281         return scrollLayoutProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
282     }
283 
284     std::string ProvideRestoreInfo() override;
285     void OnRestoreInfo(const std::string& restoreInfo) override;
286 
SetIsWidthModifiedBySelect(bool isModified)287     void SetIsWidthModifiedBySelect(bool isModified)
288     {
289         isWidthModifiedBySelect_ = isModified;
290     }
291 
IsWidthModifiedBySelect()292     bool IsWidthModifiedBySelect() const
293     {
294         return isWidthModifiedBySelect_;
295     }
296 
SetIsSelectScroll(bool isSelect)297     void SetIsSelectScroll(bool isSelect)
298     {
299         isSelectScroll_ = isSelect;
300     }
301 
IsSelectScroll()302     bool IsSelectScroll() const
303     {
304         return isSelectScroll_;
305     }
306 
SetHasOptionWidth(bool hasOptionWidth)307     void SetHasOptionWidth(bool hasOptionWidth)
308     {
309         hasOptionWidth_ = hasOptionWidth;
310     }
311 
GetHasOptionWidth()312     bool GetHasOptionWidth()
313     {
314         return hasOptionWidth_;
315     }
316 
SetEnablePaging(ScrollPagingStatus status)317     void SetEnablePaging(ScrollPagingStatus status)
318     {
319         enablePagingStatus_ =  status;
320     }
321 
GetEnablePaging()322     ScrollPagingStatus GetEnablePaging()
323     {
324         return enablePagingStatus_;
325     }
326 
IsScrollSnap()327     bool IsScrollSnap() override
328     {
329         return !snapOffsets_.empty() &&
330                (GetScrollSnapAlign() != ScrollSnapAlign::NONE || enablePagingStatus_ == ScrollPagingStatus::VALID);
331     }
332 
333     void TriggerModifyDone();
334 
SetInitialOffset(const OffsetT<CalcDimension> & offset)335     void SetInitialOffset(const OffsetT<CalcDimension>& offset)
336     {
337         initialOffset_ = offset;
338     }
339 
GetInitialOffset()340     OffsetT<CalcDimension> GetInitialOffset() const
341     {
342         return initialOffset_.has_value() ? initialOffset_.value() : OffsetT(CalcDimension(), CalcDimension());
343     }
344 
NeedSetInitialOffset()345     bool NeedSetInitialOffset()
346     {
347         return !isInitialized_ && initialOffset_.has_value();
348     }
349 
350     void AddScrollMeasureInfo(const std::optional<LayoutConstraintF>& parentConstraint,
351         const std::optional<LayoutConstraintF>& childConstraint, const SizeF& selfSize, const SizeF& childSize);
352 
353     void AddScrollLayoutInfo();
354 
355     void GetScrollSnapAlignDumpInfo();
356 
357     void GetScrollPagingStatusDumpInfo();
358 
359     void DumpAdvanceInfo() override;
360 
GetViewSize()361     SizeF GetViewSize() const
362     {
363         return viewSize_;
364     }
365 
GetViewPortExtent()366     SizeF GetViewPortExtent() const
367     {
368         return viewPortExtent_;
369     }
370 
371     void ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const override;
372 
373     bool OnScrollSnapCallback(double targetOffset, double velocity) override;
374 
375     SizeF GetChildrenExpandedSize() override;
376 
377 protected:
378     void DoJump(float position, int32_t source = SCROLL_FROM_JUMP);
379 
380 private:
381     void OnModifyDone() override;
382     bool OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config) override;
383 
384     bool IsCrashTop() const;
385     bool IsCrashBottom() const;
386     bool ReachStart(bool firstLayout) const;
387     bool ReachEnd(bool firstLayout) const;
388     bool IsScrollOutOnEdge(float delta) const;
389     void HandleCrashTop();
390     void HandleCrashBottom();
IsEnablePagingValid()391     bool IsEnablePagingValid() const
392     {
393         return enablePagingStatus_ == ScrollPagingStatus::VALID && GetScrollSnapAlign() == ScrollSnapAlign::NONE;
394     }
395 
396     void RegisterScrollBarEventTask();
397     void HandleScrollEffect();
398     void ValidateOffset(int32_t source);
399     float ValidateOffset(int32_t source, float willScrollOffset);
400     void HandleScrollPosition(float scroll);
401     float FireTwoDimensionOnWillScroll(float scroll);
402     void FireOnDidScroll(float scroll);
403     void FireOnReachStart(const OnReachEvent& onReachStart) override;
404     void FireOnReachEnd(const OnReachEvent& onReachEnd) override;
405     void SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect) override;
406     void UpdateScrollBarOffset() override;
407     void SetAccessibilityAction() override;
408     bool ScrollSnapTrigger();
409     void CheckScrollable();
410     OffsetF GetOffsetToScroll(const RefPtr<FrameNode>& childFrame) const;
411     bool SetScrollProperties(const RefPtr<LayoutWrapper>& dirty);
412     std::string GetScrollSnapPagination() const;
413 
414     float currentOffset_ = 0.0f;
415     float lastOffset_ = 0.0f;
416     // keep lastOffset_ for compatibility, use prevOffset_ for onReachStart/onReachEnd
417     float prevOffset_ = 0.0f;
418     float scrollableDistance_ = 0.0f;
419     float viewPortLength_ = 0.0f;
420     SizeF viewPort_;
421     SizeF viewSize_;
422     SizeF viewPortExtent_;
423     FlexDirection direction_ { FlexDirection::COLUMN };
424 
425     // scrollSnap
426     std::vector<float> snapOffsets_;
427     std::vector<Dimension> snapPaginations_;
428     std::pair<bool, bool> enableSnapToSide_ = { true, true };
429     Dimension intervalSize_;
430     bool scrollSnapUpdate_ = false;
431 
432     bool isWidthModifiedBySelect_ = false;
433     bool isSelectScroll_ = false;
434     bool hasOptionWidth_ = false;
435 
436     // enablePaging
437     ScrollPagingStatus enablePagingStatus_ = ScrollPagingStatus::NONE;
438     float lastPageLength_ = 0.0f;
439     float GetPagingOffset(float delta, float dragDistance, float velocity)  const;
440     float GetPagingDelta(float dragDistance, float velocity, float pageLength) const;
441 
442     RefPtr<ScrollContentModifier> scrollContentModifier_;
443 
444     //initialOffset
445     std::optional<OffsetT<CalcDimension>> initialOffset_;
446 
447     //scrollToEdge
448     ScrollEdgeType scrollEdgeType_ = ScrollEdgeType::SCROLL_NONE;
449 
450     // dump info
451     std::list<ScrollLayoutInfo> scrollLayoutInfos_;
452     std::list<ScrollMeasureInfo> scrollMeasureInfos_;
453 };
454 
455 } // namespace OHOS::Ace::NG
456 
457 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H
458