1 /*
2  * Copyright (c) 2022-2023 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/scroll_bar/proxy/scroll_bar_proxy.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/pattern/grid/grid_pattern.h"
20 #include "core/components_ng/pattern/list/list_pattern.h"
21 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
22 #include "core/components_ng/pattern/scroll_bar/scroll_bar_pattern.h"
23 
24 namespace OHOS::Ace::NG {
25 namespace {
26 constexpr int32_t SCROLL_FROM_BAR = 6; // Source type of scroll.
27 
CheckScrollable(const RefPtr<Pattern> & pattern)28 bool CheckScrollable(const RefPtr<Pattern>& pattern)
29 {
30     return AceType::InstanceOf<ScrollablePattern>(pattern);
31 }
32 
GetScrollableNodeDistance(RefPtr<Pattern> pattern)33 float GetScrollableNodeDistance(RefPtr<Pattern> pattern)
34 {
35     auto scrollablePattern = AceType::DynamicCast<ScrollablePattern>(pattern);
36     CHECK_NULL_RETURN(scrollablePattern, 0.0f);
37     return scrollablePattern->GetScrollableDistance();
38 }
39 
GetScrollableNodeOffset(RefPtr<Pattern> pattern)40 float GetScrollableNodeOffset(RefPtr<Pattern> pattern)
41 {
42     auto scrollablePattern = AceType::DynamicCast<ScrollablePattern>(pattern);
43     CHECK_NULL_RETURN(scrollablePattern, 0.0f);
44     return scrollablePattern->GetBarOffset();
45 }
46 
GetScrollBarOutBoundaryExtent(RefPtr<Pattern> pattern)47 double GetScrollBarOutBoundaryExtent(RefPtr<Pattern> pattern)
48 {
49     auto scrollPattern = AceType::DynamicCast<ScrollablePattern>(pattern);
50     CHECK_NULL_RETURN(scrollPattern, 0.0f);
51     return scrollPattern->GetScrollBarOutBoundaryExtent();
52 }
53 } // namespace
54 
RegisterScrollableNode(const ScrollableNodeInfo & scrollableNode)55 void ScrollBarProxy::RegisterScrollableNode(const ScrollableNodeInfo& scrollableNode)
56 {
57     scorllableNode_ = scrollableNode;
58 }
59 
RegisterNestScrollableNode(const ScrollableNodeInfo & scrollableNode)60 void ScrollBarProxy::RegisterNestScrollableNode(const ScrollableNodeInfo& scrollableNode)
61 {
62     if (std::find(nestScrollableNodes_.begin(), nestScrollableNodes_.end(), scrollableNode) !=
63         nestScrollableNodes_.end()) {
64         return;
65     }
66     nestScrollableNodes_.emplace_back(scrollableNode);
67 }
68 
RegisterScrollBar(const WeakPtr<ScrollBarPattern> & scrollBar)69 void ScrollBarProxy::RegisterScrollBar(const WeakPtr<ScrollBarPattern>& scrollBar)
70 {
71     if (std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar) != scrollBars_.end()) {
72         return;
73     }
74     scrollBars_.emplace_back(scrollBar);
75 }
76 
UnRegisterNestScrollableNode(const WeakPtr<ScrollablePattern> & scrollableNode)77 void ScrollBarProxy::UnRegisterNestScrollableNode(const WeakPtr<ScrollablePattern>& scrollableNode)
78 {
79     auto iter = std::find_if(nestScrollableNodes_.begin(), nestScrollableNodes_.end(),
80         [&scrollableNode](const ScrollableNodeInfo& info) { return scrollableNode == info.scrollableNode; });
81     if (iter != nestScrollableNodes_.end()) {
82         nestScrollableNodes_.erase(iter);
83     }
84 }
85 
UnRegisterScrollBar(const WeakPtr<ScrollBarPattern> & scrollBar)86 void ScrollBarProxy::UnRegisterScrollBar(const WeakPtr<ScrollBarPattern>& scrollBar)
87 {
88     auto iter = std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar);
89     if (iter != scrollBars_.end()) {
90         scrollBars_.erase(iter);
91     }
92 }
93 
NotifyScrollableNode(float distance,int32_t source,const WeakPtr<ScrollBarPattern> & weakScrollBar) const94 void ScrollBarProxy::NotifyScrollableNode(
95     float distance, int32_t source, const WeakPtr<ScrollBarPattern>& weakScrollBar) const
96 {
97     auto scrollBar = weakScrollBar.Upgrade();
98     CHECK_NULL_VOID(scrollBar);
99     float barScrollableDistance = scrollBar->GetScrollableDistance();
100     auto node = scorllableNode_;
101     CHECK_NULL_VOID(node.onPositionChanged);
102     auto scrollable = node.scrollableNode.Upgrade();
103     if (!scrollable || !CheckScrollable(scrollable)) {
104         return;
105     }
106     float controlDistance = scrollBar->GetControlDistance();
107     float value = CalcPatternOffset(controlDistance, barScrollableDistance, distance);
108     node.onPositionChanged(value, source, IsNestScroller());
109     if (node.scrollbarFRcallback) {
110         node.scrollbarFRcallback(0, SceneStatus::RUNNING);
111     }
112 }
113 
NotifyScrollBarNode(float distance,int32_t source) const114 void ScrollBarProxy::NotifyScrollBarNode(float distance, int32_t source) const
115 {
116     auto node = scorllableNode_;
117     CHECK_NULL_VOID(node.onPositionChanged);
118     auto scrollable = node.scrollableNode.Upgrade();
119     if (!scrollable || !CheckScrollable(scrollable)) {
120         return;
121     }
122     node.onPositionChanged(distance, source, IsNestScroller());
123     if (node.scrollbarFRcallback) {
124         node.scrollbarFRcallback(0, SceneStatus::RUNNING);
125     }
126 }
127 
NotifyScrollStart() const128 void ScrollBarProxy::NotifyScrollStart() const
129 {
130     auto node = scorllableNode_;
131     CHECK_NULL_VOID(node.scrollStartCallback);
132     node.scrollStartCallback(0, SCROLL_FROM_BAR, IsNestScroller());
133     if (node.scrollbarFRcallback) {
134         node.scrollbarFRcallback(0, SceneStatus::RUNNING);
135     }
136 }
137 
NotifyScrollStop() const138 void ScrollBarProxy::NotifyScrollStop() const
139 {
140     auto node = scorllableNode_;
141     CHECK_NULL_VOID(node.scrollEndCallback);
142     node.scrollEndCallback(IsNestScroller());
143     if (node.scrollbarFRcallback) {
144         node.scrollbarFRcallback(0, SceneStatus::RUNNING);
145     }
146 }
147 
NotifyScrollBar() const148 void ScrollBarProxy::NotifyScrollBar() const
149 {
150     auto scrollable = scorllableNode_.scrollableNode.Upgrade();
151     if (!scrollable || !CheckScrollable(scrollable)) {
152         return;
153     }
154 
155     float controlDistance = GetScrollableNodeDistance(scrollable);
156     float scrollableNodeOffset = -GetScrollableNodeOffset(scrollable);
157     for (auto info : nestScrollableNodes_) {
158         auto pattern = info.scrollableNode.Upgrade();
159         if (!pattern || !CheckScrollable(pattern)) {
160             continue;
161         }
162         controlDistance += GetScrollableNodeDistance(pattern);
163         scrollableNodeOffset += -GetScrollableNodeOffset(pattern);
164     }
165 
166     double scrollBarOutBoundaryDistance = GetScrollBarOutBoundaryExtent(scrollable);
167     for (const auto& weakScrollBar : scrollBars_) {
168         auto scrollBar = weakScrollBar.Upgrade();
169         if (!scrollBar) {
170             continue;
171         }
172 
173         scrollBar->SetControlDistance(controlDistance);
174         scrollBar->SetReverse(scrollable->IsReverse());
175         scrollBar->HandleScrollBarOutBoundary(scrollBarOutBoundaryDistance);
176         auto host = scrollBar->GetHost();
177         if (!host) {
178             continue;
179         }
180         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && !scrollBar->HasChild()) {
181             scrollBar->SetScrollableNodeOffset(scrollableNodeOffset);
182             scrollBar->UpdateScrollBarOffset();
183             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
184         } else {
185             scrollBar->SetScrollableNodeOffset(
186                 !scrollable->IsReverse() ? scrollableNodeOffset : controlDistance - scrollableNodeOffset);
187             host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
188         }
189     }
190 }
191 
StartScrollBarAnimator() const192 void ScrollBarProxy::StartScrollBarAnimator() const
193 {
194     for (const auto& weakScrollBar : scrollBars_) {
195         auto scrollBar = weakScrollBar.Upgrade();
196         if (!scrollBar) {
197             continue;
198         }
199         if (scrollBar->GetDisplayMode() == DisplayMode::AUTO) {
200             scrollBar->StartDisappearAnimator();
201         }
202         // AccessibilityEventType::SCROLL_END
203     }
204 }
205 
StopScrollBarAnimator() const206 void ScrollBarProxy::StopScrollBarAnimator() const
207 {
208     for (const auto& weakScrollBar : scrollBars_) {
209         auto scrollBar = weakScrollBar.Upgrade();
210         if (!scrollBar) {
211             continue;
212         }
213         scrollBar->StopDisappearAnimator();
214         scrollBar->StopMotion();
215         // AccessibilityEventType::SCROLL_START
216     }
217 }
218 
NotifySnapScroll(float delta,float velocity,float barScrollableDistance,float dragDistance) const219 bool ScrollBarProxy::NotifySnapScroll(
220     float delta, float velocity, float barScrollableDistance, float dragDistance) const
221 {
222     auto scrollable = scorllableNode_.scrollableNode.Upgrade();
223     if (!scrollable || !CheckScrollable(scrollable) || !scorllableNode_.calePredictSnapOffsetCallback ||
224         !scorllableNode_.startScrollSnapMotionCallback) {
225         return false;
226     }
227     auto controlDistance = GetScrollableNodeDistance(scrollable);
228     auto patternOffset = CalcPatternOffset(controlDistance, barScrollableDistance, delta);
229     dragDistance = CalcPatternOffset(controlDistance, barScrollableDistance, dragDistance);
230     auto predictSnapOffset = scorllableNode_.calePredictSnapOffsetCallback(patternOffset, dragDistance, -velocity);
231     // If snap scrolling, predictSnapOffset will has a value.
232     if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
233         scorllableNode_.startScrollSnapMotionCallback(predictSnapOffset.value(), velocity);
234         // Outer scrollBar can only control one snap scrollable component.
235         return true;
236     }
237     return false;
238 }
239 
CalcPatternOffset(float controlDistance,float barScrollableDistance,float delta) const240 float ScrollBarProxy::CalcPatternOffset(float controlDistance, float barScrollableDistance, float delta) const
241 {
242     if (!NearZero(barScrollableDistance)) {
243         return delta * controlDistance / barScrollableDistance;
244     } else {
245         return 0.0f;
246     }
247 }
248 
SetScrollEnabled(bool scrollEnabled,const WeakPtr<ScrollablePattern> & weakScrollableNode) const249 void ScrollBarProxy::SetScrollEnabled(bool scrollEnabled, const WeakPtr<ScrollablePattern>& weakScrollableNode) const
250 {
251     auto scrollable = weakScrollableNode.Upgrade();
252     if (!scrollable || !CheckScrollable(scrollable)) {
253         return;
254     }
255 
256     for (const auto& weakScrollBar : scrollBars_) {
257         auto scrollBar = weakScrollBar.Upgrade();
258         if (!scrollBar) {
259             continue;
260         }
261 
262         scrollBar->SetScrollEnabled(scrollEnabled);
263     }
264 }
265 
IsNestScroller() const266 bool ScrollBarProxy::IsNestScroller() const
267 {
268     for (auto bar : scrollBars_) {
269         auto scrollBarPattern = bar.Upgrade();
270         if (!scrollBarPattern) {
271             continue;
272         }
273         auto enableNestedSorll = scrollBarPattern->GetEnableNestedSorll();
274         if (enableNestedSorll) {
275             return true;
276         }
277     }
278     return false;
279 }
280 
ScrollPage(bool reverse,bool smooth)281 void ScrollBarProxy::ScrollPage(bool reverse, bool smooth)
282 {
283     auto node = scorllableNode_;
284     CHECK_NULL_VOID(node.scrollPageCallback);
285     node.scrollPageCallback(reverse, smooth);
286 }
287 } // namespace OHOS::Ace::NG
288