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 if (std::find(scrollableNodes_.begin(), scrollableNodes_.end(), scrollableNode) != scrollableNodes_.end()) {
58 return;
59 }
60 scrollableNodes_.emplace_back(scrollableNode);
61 }
62
RegisterScrollBar(const WeakPtr<ScrollBarPattern> & scrollBar)63 void ScrollBarProxy::RegisterScrollBar(const WeakPtr<ScrollBarPattern>& scrollBar)
64 {
65 if (std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar) != scrollBars_.end()) {
66 return;
67 }
68 scrollBars_.emplace_back(scrollBar);
69 }
70
UnRegisterScrollableNode(const WeakPtr<ScrollablePattern> & scrollableNode)71 void ScrollBarProxy::UnRegisterScrollableNode(const WeakPtr<ScrollablePattern>& scrollableNode)
72 {
73 auto iter = std::find_if(scrollableNodes_.begin(), scrollableNodes_.end(),
74 [&scrollableNode](const ScrollableNodeInfo& info) { return scrollableNode == info.scrollableNode; });
75 if (iter != scrollableNodes_.end()) {
76 scrollableNodes_.erase(iter);
77 }
78 }
79
UnRegisterScrollBar(const WeakPtr<ScrollBarPattern> & scrollBar)80 void ScrollBarProxy::UnRegisterScrollBar(const WeakPtr<ScrollBarPattern>& scrollBar)
81 {
82 auto iter = std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar);
83 if (iter != scrollBars_.end()) {
84 scrollBars_.erase(iter);
85 }
86 }
87
NotifyScrollableNode(float distance,int32_t source,const WeakPtr<ScrollBarPattern> & weakScrollBar) const88 void ScrollBarProxy::NotifyScrollableNode(
89 float distance, int32_t source, const WeakPtr<ScrollBarPattern>& weakScrollBar) const
90 {
91 auto scrollBar = weakScrollBar.Upgrade();
92 CHECK_NULL_VOID(scrollBar);
93 float barScrollableDistance = scrollBar->GetScrollableDistance();
94
95 for (const auto& node : scrollableNodes_) {
96 if (node.onPositionChanged == nullptr) {
97 continue;
98 }
99 auto scrollable = node.scrollableNode.Upgrade();
100 if (!scrollable || !CheckScrollable(scrollable)) {
101 continue;
102 }
103 float value = CalcPatternOffset(GetScrollableNodeDistance(scrollable), barScrollableDistance, distance);
104 node.onPositionChanged(value, source);
105 if (node.scrollbarFRcallback) {
106 node.scrollbarFRcallback(0, SceneStatus::RUNNING);
107 }
108 }
109 }
110
NotifyScrollBarNode(float distance,int32_t source) const111 void ScrollBarProxy::NotifyScrollBarNode(float distance, int32_t source) const
112 {
113 for (const auto& node : scrollableNodes_) {
114 if (node.onPositionChanged == nullptr) {
115 continue;
116 }
117 auto scrollable = node.scrollableNode.Upgrade();
118 if (!scrollable || !CheckScrollable(scrollable)) {
119 continue;
120 }
121 node.onPositionChanged(distance, source);
122 if (node.scrollbarFRcallback) {
123 node.scrollbarFRcallback(0, SceneStatus::RUNNING);
124 }
125 }
126 }
127
NotifyScrollStart() const128 void ScrollBarProxy::NotifyScrollStart() const
129 {
130 for (const auto& node : scrollableNodes_) {
131 if (node.scrollStartCallback == nullptr) {
132 continue;
133 }
134 node.scrollStartCallback(0, SCROLL_FROM_BAR);
135 if (node.scrollbarFRcallback) {
136 node.scrollbarFRcallback(0, SceneStatus::RUNNING);
137 }
138 }
139 }
140
NotifyScrollStop() const141 void ScrollBarProxy::NotifyScrollStop() const
142 {
143 for (const auto& node : scrollableNodes_) {
144 if (node.scrollEndCallback == nullptr) {
145 continue;
146 }
147 node.scrollEndCallback();
148 if (node.scrollbarFRcallback) {
149 node.scrollbarFRcallback(0, SceneStatus::RUNNING);
150 }
151 }
152 }
153
NotifyScrollBar(const WeakPtr<ScrollablePattern> & weakScrollableNode) const154 void ScrollBarProxy::NotifyScrollBar(const WeakPtr<ScrollablePattern>& weakScrollableNode) const
155 {
156 auto scrollable = weakScrollableNode.Upgrade();
157 if (!scrollable || !CheckScrollable(scrollable)) {
158 return;
159 }
160
161 float controlDistance = GetScrollableNodeDistance(scrollable);
162 float scrollableNodeOffset = -GetScrollableNodeOffset(scrollable); // scroll bar direction is reverse
163 double scrollBarOutBoundaryDistance = GetScrollBarOutBoundaryExtent(scrollable);
164 for (const auto& weakScrollBar : scrollBars_) {
165 auto scrollBar = weakScrollBar.Upgrade();
166 if (!scrollBar) {
167 continue;
168 }
169
170 scrollBar->SetControlDistance(controlDistance);
171 scrollBar->SetReverse(scrollable->IsReverse());
172 scrollBar->HandleScrollBarOutBoundary(scrollBarOutBoundaryDistance);
173 auto host = scrollBar->GetHost();
174 if (!host) {
175 continue;
176 }
177 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && !scrollBar->HasChild()) {
178 scrollBar->SetScrollableNodeOffset(scrollableNodeOffset);
179 scrollBar->UpdateScrollBarOffset();
180 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
181 } else {
182 scrollBar->SetScrollableNodeOffset(
183 !scrollable->IsReverse() ? scrollableNodeOffset : controlDistance - scrollableNodeOffset);
184 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
185 }
186 }
187 }
188
StartScrollBarAnimator() const189 void ScrollBarProxy::StartScrollBarAnimator() const
190 {
191 for (const auto& weakScrollBar : scrollBars_) {
192 auto scrollBar = weakScrollBar.Upgrade();
193 if (!scrollBar) {
194 continue;
195 }
196 if (scrollBar->GetDisplayMode() == DisplayMode::AUTO) {
197 scrollBar->StartDisappearAnimator();
198 }
199 // AccessibilityEventType::SCROLL_END
200 }
201 }
202
StopScrollBarAnimator() const203 void ScrollBarProxy::StopScrollBarAnimator() const
204 {
205 for (const auto& weakScrollBar : scrollBars_) {
206 auto scrollBar = weakScrollBar.Upgrade();
207 if (!scrollBar) {
208 continue;
209 }
210 scrollBar->StopDisappearAnimator();
211 scrollBar->StopMotion();
212 // AccessibilityEventType::SCROLL_START
213 }
214 }
215
NotifySnapScroll(float delta,float velocity,float barScrollableDistance,float dragDistance) const216 bool ScrollBarProxy::NotifySnapScroll(
217 float delta, float velocity, float barScrollableDistance, float dragDistance) const
218 {
219 for (const auto& node : scrollableNodes_) {
220 auto scrollable = node.scrollableNode.Upgrade();
221 if (!scrollable || !CheckScrollable(scrollable) || !node.calePredictSnapOffsetCallback ||
222 !node.startScrollSnapMotionCallback) {
223 continue;
224 }
225 auto controlDistance = GetScrollableNodeDistance(scrollable);
226 auto patternOffset = CalcPatternOffset(controlDistance, barScrollableDistance, delta);
227 dragDistance = CalcPatternOffset(controlDistance, barScrollableDistance, dragDistance);
228 auto predictSnapOffset = node.calePredictSnapOffsetCallback(patternOffset, dragDistance, -velocity);
229 // If snap scrolling, predictSnapOffset will has a value.
230 if (predictSnapOffset.has_value() && !NearZero(predictSnapOffset.value())) {
231 node.startScrollSnapMotionCallback(predictSnapOffset.value(), velocity);
232 // Outer scrollBar can only control one snap scrollable component.
233 return true;
234 }
235 }
236 return false;
237 }
238
CalcPatternOffset(float controlDistance,float barScrollableDistance,float delta) const239 float ScrollBarProxy::CalcPatternOffset(float controlDistance, float barScrollableDistance, float delta) const
240 {
241 if (!NearZero(barScrollableDistance)) {
242 return delta * controlDistance / barScrollableDistance;
243 } else {
244 return 0.0f;
245 }
246 }
247
SetScrollEnabled(bool scrollEnabled,const WeakPtr<ScrollablePattern> & weakScrollableNode) const248 void ScrollBarProxy::SetScrollEnabled(bool scrollEnabled, const WeakPtr<ScrollablePattern>& weakScrollableNode) const
249 {
250 auto scrollable = weakScrollableNode.Upgrade();
251 if (!scrollable || !CheckScrollable(scrollable)) {
252 return;
253 }
254
255 for (const auto& weakScrollBar : scrollBars_) {
256 auto scrollBar = weakScrollBar.Upgrade();
257 if (!scrollBar) {
258 continue;
259 }
260
261 scrollBar->SetScrollEnabled(scrollEnabled);
262 }
263 }
264 } // namespace OHOS::Ace::NG
265