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