1 /*
2  * Copyright (c) 2021-2022 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/scroll_bar/scroll_bar_proxy.h"
17 
18 #include "core/components/scroll/render_scroll.h"
19 #include "core/components/scroll_bar/render_scroll_bar.h"
20 #include "core/components_v2/grid/render_grid_scroll.h"
21 #include "core/components_v2/list/render_list.h"
22 #include "core/components_v2/water_flow/render_water_flow.h"
23 
24 namespace OHOS::Ace {
25 
RegisterScrollableNode(const ScrollableNodeInfo & scrollableNode)26 void ScrollBarProxy::RegisterScrollableNode(const ScrollableNodeInfo& scrollableNode)
27 {
28     if (std::find(scrollableNodes_.begin(), scrollableNodes_.end(), scrollableNode) != scrollableNodes_.end()) {
29         LOGE("scrollable node is already exist.");
30         return;
31     }
32     scrollableNodes_.emplace_back(scrollableNode);
33 }
34 
RegisterScrollBar(const WeakPtr<RenderScrollBar> & scrollBar)35 void ScrollBarProxy::RegisterScrollBar(const WeakPtr<RenderScrollBar>& scrollBar)
36 {
37     if (std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar) != scrollBars_.end()) {
38         LOGE("scroll bar is already exist.");
39         return;
40     }
41     scrollBars_.emplace_back(scrollBar);
42 }
43 
UnRegisterScrollableNode(const WeakPtr<RenderNode> & scrollableNode)44 void ScrollBarProxy::UnRegisterScrollableNode(const WeakPtr<RenderNode>& scrollableNode)
45 {
46     auto iter = std::find_if(scrollableNodes_.begin(), scrollableNodes_.end(),
47         [&scrollableNode](const ScrollableNodeInfo& info) { return scrollableNode == info.scrollableNode; });
48     if (iter != scrollableNodes_.end()) {
49         scrollableNodes_.erase(iter);
50     }
51 }
52 
UnRegisterScrollBar(const WeakPtr<RenderScrollBar> & scrollBar)53 void ScrollBarProxy::UnRegisterScrollBar(const WeakPtr<RenderScrollBar>& scrollBar)
54 {
55     auto iter = std::find(scrollBars_.begin(), scrollBars_.end(), scrollBar);
56     if (iter != scrollBars_.end()) {
57         scrollBars_.erase(iter);
58     }
59 }
60 
NotifyScrollableNode(double distance,const WeakPtr<RenderScrollBar> & weakScrollBar) const61 void ScrollBarProxy::NotifyScrollableNode(double distance, const WeakPtr<RenderScrollBar>& weakScrollBar) const
62 {
63     auto scrollBar = weakScrollBar.Upgrade();
64     if (!scrollBar) {
65         LOGE("ScrollBar has been released.");
66         return;
67     }
68 
69     auto scrollBarChild = scrollBar->GetLastChild();
70     if (!scrollBarChild) {
71         LOGE("ScrollBar has no child.");
72         return;
73     }
74 
75     for (const auto& [weakScrollableNode, onPositionChanged] : scrollableNodes_) {
76         if (!onPositionChanged) {
77             continue;
78         }
79         auto scrollable = weakScrollableNode.Upgrade();
80         if (!scrollable || !CheckScrollable(scrollable)) {
81             LOGE("Node is not scrollable node.");
82             continue;
83         }
84 
85         auto scrollableChild = scrollable->GetLastChild();
86         if (!scrollableChild) {
87             LOGE("Scrollable node has no child.");
88             continue;
89         }
90 
91         auto scrollBarAxis = scrollBar->GetAxis();
92         Size scrollableSize = scrollable->GetLayoutSize();
93         Size scrollableChildSize = GetScrollableChildSize(scrollable, scrollableChild->GetLayoutSize(), scrollBarAxis);
94         Size scrollBarSize = scrollBar->GetLayoutSize();
95         Size scrollBarChildSize = scrollBarChild->GetLayoutSize();
96         auto scrollableAxis = GetScrollableAxis(scrollable);
97         if (scrollableAxis != Axis::FREE && scrollBarAxis != Axis::FREE && scrollableAxis != scrollBarAxis) {
98             LOGE("Axis of ScrollBar and Scroll is not match.");
99             continue;
100         }
101 
102         double value = 0.0;
103         auto scrollableDeltaSize = scrollableChildSize - scrollableSize;
104         auto scrollBarDeltaSize = scrollBarSize - scrollBarChildSize;
105         if (scrollBarAxis == Axis::VERTICAL) {
106             if (!NearZero(scrollBarDeltaSize.Height())) {
107                 value = distance * scrollableDeltaSize.Height() / scrollBarDeltaSize.Height();
108             }
109         } else {
110             if (!NearZero(scrollBarDeltaSize.Width())) {
111                 value = distance * scrollableDeltaSize.Width() / scrollBarDeltaSize.Width();
112             }
113         }
114         constexpr int32_t SCROLL_FROM_BAR = 6; // Source type of scroll.
115         onPositionChanged(value, SCROLL_FROM_BAR);
116     }
117 }
118 
NotifyScrollBar(const WeakPtr<RenderNode> & weakScrollableNode) const119 void ScrollBarProxy::NotifyScrollBar(const WeakPtr<RenderNode>& weakScrollableNode) const
120 {
121     auto scrollable = weakScrollableNode.Upgrade();
122     if (!scrollable || !CheckScrollable(scrollable)) {
123         LOGE("Node is not scrollable node.");
124         return;
125     }
126 
127     auto scrollableChild = scrollable->GetLastChild();
128     if (!scrollableChild) {
129         LOGE("child of scrollable node is null");
130         return;
131     }
132 
133     for (const auto& weakScrollBar : scrollBars_) {
134         auto scrollBar = weakScrollBar.Upgrade();
135         if (!scrollBar) {
136             LOGE("ScrollBar is released.");
137             continue;
138         }
139         auto scrollBarChild = scrollBar->GetLastChild();
140         if (!scrollBarChild) {
141             LOGE("ScrollBar has no child.");
142             continue;
143         }
144         auto scrollBarAxis = scrollBar->GetAxis();
145         Axis scrollableAxis = Axis::NONE;
146         Offset scrollableChildPosition = scrollableChild->GetPosition();
147         Size scrollableSize = scrollable->GetLayoutSize();
148         Size scrollableChildSize = scrollableChild->GetLayoutSize();
149         Size scrollBarSize = scrollBar->GetLayoutSize();
150         Size scrollBarChildSize = scrollBarChild->GetLayoutSize();
151         AdjustParam(scrollable, scrollBarAxis, scrollableAxis, scrollableChildSize, scrollableChildPosition);
152 
153         if (scrollBarAxis != Axis::FREE && scrollableAxis != Axis::FREE && scrollBarAxis != scrollableAxis) {
154             LOGE("Axis of ScrollBar and Scroll is not match.");
155             continue;
156         }
157 
158         Offset position;
159         if (scrollBarAxis == Axis::VERTICAL) {
160             if (LessOrEqual((scrollableChildSize - scrollableSize).Height(), 0.0) ||
161                 LessOrEqual((scrollBarSize - scrollBarChildSize).Height(), 0.0)) {
162                 continue;
163             }
164             auto positionY = scrollableChildPosition.GetY() * (scrollBarSize - scrollBarChildSize).Height() /
165                              (scrollableChildSize - scrollableSize).Height();
166             positionY = std::clamp(positionY, 0.0, (scrollBarSize - scrollBarChildSize).Height());
167             position.SetY(positionY);
168         } else {
169             if (LessOrEqual((scrollableChildSize - scrollableSize).Width(), 0.0) ||
170                 LessOrEqual((scrollBarSize - scrollBarChildSize).Width(), 0.0)) {
171                 continue;
172             }
173             auto positionX = scrollableChildPosition.GetX() * (scrollBarSize - scrollBarChildSize).Width() /
174                              (scrollableChildSize - scrollableSize).Width();
175             positionX = std::clamp(positionX, 0.0, (scrollBarSize - scrollBarChildSize).Width());
176             position.SetX(positionX);
177         }
178         scrollBar->SetChildPosition(position);
179         scrollBarChild->SetPosition(position);
180         scrollBar->MarkNeedRender();
181     }
182 }
183 
StartScrollBarAnimator() const184 void ScrollBarProxy::StartScrollBarAnimator() const
185 {
186     for (const auto& weakScrollBar : scrollBars_) {
187         auto scrollBar = weakScrollBar.Upgrade();
188         if (!scrollBar) {
189             LOGE("ScrollBar is released.");
190             continue;
191         }
192         if (scrollBar->GetDisplayMode() == DisplayMode::AUTO) {
193             scrollBar->StartAnimator();
194         }
195     }
196 }
197 
StopScrollBarAnimator() const198 void ScrollBarProxy::StopScrollBarAnimator() const
199 {
200     for (const auto& weakScrollBar : scrollBars_) {
201         auto scrollBar = weakScrollBar.Upgrade();
202         if (!scrollBar) {
203             LOGE("ScrollBar is released.");
204             continue;
205         }
206         scrollBar->StopAnimator();
207     }
208 }
209 
CheckScrollable(const RefPtr<RenderNode> & node) const210 bool ScrollBarProxy::CheckScrollable(const RefPtr<RenderNode>& node) const
211 {
212     return AceType::InstanceOf<RenderScroll>(node) || AceType::InstanceOf<V2::RenderGridScroll>(node) ||
213            AceType::InstanceOf<V2::RenderList>(node) || AceType::InstanceOf<V2::RenderWaterFlow>(node);
214 }
215 
GetScrollableAxis(const RefPtr<RenderNode> & node) const216 Axis ScrollBarProxy::GetScrollableAxis(const RefPtr<RenderNode>& node) const
217 {
218     auto renderScroll = AceType::DynamicCast<RenderScroll>(node);
219     if (renderScroll) {
220         return renderScroll->GetAxis();
221     }
222     auto renderGridScroll = AceType::DynamicCast<V2::RenderGridScroll>(node);
223     if (renderGridScroll) {
224         return renderGridScroll->GetAxis();
225     }
226     auto renderList = AceType::DynamicCast<V2::RenderList>(node);
227     if (renderList) {
228         return renderList->GetAxis();
229     }
230     auto renderWaterFlow = AceType::DynamicCast<V2::RenderWaterFlow>(node);
231     if (renderWaterFlow) {
232         return renderWaterFlow->GetAxis();
233     }
234     return Axis::NONE;
235 }
236 
GetScrollableChildSize(const RefPtr<RenderNode> & scrollable,const Size & scrollableChildSize,Axis scrollBarAxis) const237 Size ScrollBarProxy::GetScrollableChildSize(
238     const RefPtr<RenderNode>& scrollable, const Size& scrollableChildSize, Axis scrollBarAxis) const
239 {
240     Size result = scrollableChildSize;
241     auto renderScroll = AceType::DynamicCast<RenderScroll>(scrollable);
242     if (renderScroll) {
243         scrollBarAxis == Axis::VERTICAL ? result.SetHeight(renderScroll->GetEstimatedHeight())
244                                         : result.SetWidth(renderScroll->GetEstimatedHeight());
245         return result;
246     }
247     auto renderGridScroll = AceType::DynamicCast<V2::RenderGridScroll>(scrollable);
248     if (renderGridScroll) {
249         scrollBarAxis == Axis::VERTICAL ? result.SetHeight(renderGridScroll->GetEstimatedHeight())
250                                         : result.SetWidth(renderGridScroll->GetEstimatedHeight());
251         return result;
252     }
253     auto renderList = AceType::DynamicCast<V2::RenderList>(scrollable);
254     if (renderList) {
255         scrollBarAxis == Axis::VERTICAL ? result.SetHeight(renderList->GetEstimatedHeight())
256                                         : result.SetWidth(renderList->GetEstimatedHeight());
257         return result;
258     }
259     auto renderWaterFlow = AceType::DynamicCast<V2::RenderWaterFlow>(scrollable);
260     if (renderWaterFlow) {
261         scrollBarAxis == Axis::VERTICAL ? result.SetHeight(renderWaterFlow->GetEstimatedHeight())
262                                         : result.SetWidth(renderWaterFlow->GetEstimatedHeight());
263         return result;
264     }
265     return result;
266 }
267 
AdjustParam(const RefPtr<RenderNode> & scrollable,Axis scrollBarAxis,Axis & scrollableAxis,Size & scrollableChildSize,Offset & scrollableChildPosition) const268 void ScrollBarProxy::AdjustParam(const RefPtr<RenderNode>& scrollable, Axis scrollBarAxis, Axis& scrollableAxis,
269     Size& scrollableChildSize, Offset& scrollableChildPosition) const
270 {
271     auto renderScroll = AceType::DynamicCast<RenderScroll>(scrollable);
272     if (renderScroll) {
273         scrollBarAxis == Axis::VERTICAL ? scrollableChildSize.SetHeight(renderScroll->GetEstimatedHeight())
274                                         : scrollableChildSize.SetWidth(renderScroll->GetEstimatedHeight());
275         scrollableAxis = renderScroll->GetAxis();
276         scrollableChildPosition = renderScroll->GetLastOffset();
277         return;
278     }
279 
280     auto renderGridScroll = AceType::DynamicCast<V2::RenderGridScroll>(scrollable);
281     if (renderGridScroll) {
282         scrollBarAxis == Axis::VERTICAL ? scrollableChildSize.SetHeight(renderGridScroll->GetEstimatedHeight())
283                                         : scrollableChildSize.SetWidth(renderGridScroll->GetEstimatedHeight());
284         scrollableAxis = renderGridScroll->GetAxis();
285         scrollableChildPosition = renderGridScroll->GetLastOffset();
286         return;
287     }
288 
289     auto renderList = AceType::DynamicCast<V2::RenderList>(scrollable);
290     if (renderList) {
291         scrollBarAxis == Axis::VERTICAL ? scrollableChildSize.SetHeight(renderList->GetEstimatedHeight())
292                                         : scrollableChildSize.SetWidth(renderList->GetEstimatedHeight());
293         scrollableAxis = renderList->GetAxis();
294         scrollableChildPosition = renderList->GetLastOffset();
295     }
296 
297     auto renderWaterFlow = AceType::DynamicCast<V2::RenderWaterFlow>(scrollable);
298     if (renderWaterFlow) {
299         scrollBarAxis == Axis::VERTICAL ? scrollableChildSize.SetHeight(renderWaterFlow->GetEstimatedHeight())
300                                         : scrollableChildSize.SetWidth(renderWaterFlow->GetEstimatedHeight());
301         scrollableAxis = renderWaterFlow->GetAxis();
302         scrollableChildPosition = renderWaterFlow->GetLastOffset();
303         return;
304     }
305 }
306 
307 } // namespace OHOS::Ace