1 /*
2 * Copyright (c) 2025 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 "interfaces/inner_api/ace_kit/src/view/scroller_impl.h"
17
18 #include <functional>
19 #include <limits>
20 #include <type_traits>
21
22 #include "view/frame_node_impl.h"
23
24 #include "core/components_ng/pattern/grid/grid_pattern.h"
25 #include "core/components_ng/pattern/list/list_pattern.h"
26 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
27 #include "core/components_ng/pattern/scrollable/scrollable_controller.h"
28 #include "core/components_ng/pattern/waterflow/water_flow_pattern.h"
29
30 namespace OHOS::Ace::Kit {
31
ScrollerImpl(const RefPtr<Framework::JSScroller> & jsScroller)32 ScrollerImpl::ScrollerImpl(const RefPtr<Framework::JSScroller>& jsScroller) : jsScroller_(jsScroller) {}
33
AddObserver(const Observer & observer,int32_t id)34 void ScrollerImpl::AddObserver(const Observer& observer, int32_t id)
35 {
36 ScrollerObserver scrollerObserver;
37 scrollerObserver.onReachStartEvent = observer.onReachStartEvent;
38 scrollerObserver.onReachEndEvent = observer.onReachEndEvent;
39 scrollerObserver.onScrollStartEvent = observer.onScrollStartEvent;
40 scrollerObserver.onScrollStopEvent = observer.onScrollStopEvent;
41 scrollerObserver.onDidScrollEvent = observer.onDidScrollEvent;
42 scrollerObserver.onScrollerAreaChangeEvent = observer.onScrollerAreaChangeEvent;
43 scrollerObserver.onWillScrollEventEx = observer.onWillScrollEventEx;
44 scrollerObserver.twoDimensionOnWillScrollEvent = observer.twoDimensionOnWillScrollEvent;
45 jsScroller_->AddObserver(scrollerObserver, id);
46 }
47
RemoveObserver(int32_t id)48 void ScrollerImpl::RemoveObserver(int32_t id)
49 {
50 jsScroller_->RemoveObserver(id);
51 }
52
GetCurrentOffsetX()53 double ScrollerImpl::GetCurrentOffsetX()
54 {
55 RefPtr<ScrollControllerBase> scrollController = jsScroller_->GetController().Upgrade();
56 if (!scrollController) {
57 return 0.0;
58 }
59 return scrollController->GetCurrentOffset().GetX();
60 }
61
GetCurrentOffsetY()62 double ScrollerImpl::GetCurrentOffsetY()
63 {
64 RefPtr<ScrollControllerBase> scrollController = jsScroller_->GetController().Upgrade();
65 if (!scrollController) {
66 return 0.0;
67 }
68 return scrollController->GetCurrentOffset().GetY();
69 }
70
GetScrollablePattern(const RefPtr<Framework::JSScroller> & jsScroller)71 RefPtr<NG::ScrollablePattern> GetScrollablePattern(const RefPtr<Framework::JSScroller>& jsScroller)
72 {
73 auto controller = jsScroller->GetController().Upgrade();
74 CHECK_NULL_RETURN(controller, nullptr);
75 auto scrollableController = AceType::DynamicCast<NG::ScrollableController>(controller);
76 CHECK_NULL_RETURN(scrollableController, nullptr);
77 auto scrollablePattern = scrollableController->GetScrollPattern().Upgrade();
78 return scrollablePattern;
79 }
80
IsAtEnd()81 bool ScrollerImpl::IsAtEnd()
82 {
83 RefPtr<ScrollControllerBase> scrollController = jsScroller_->GetController().Upgrade();
84 if (!scrollController) {
85 return false;
86 }
87 return scrollController->IsAtEnd();
88 }
89
IsAtStart()90 bool ScrollerImpl::IsAtStart()
91 {
92 auto scrollablePattern = GetScrollablePattern(jsScroller_);
93 if (!scrollablePattern) {
94 return false;
95 }
96 return scrollablePattern->IsAtTop();
97 }
98
GetScrollableOffsetY(const RefPtr<NG::ScrollablePattern> & pattern,const RefPtr<FrameNode> & node)99 double GetScrollableOffsetY(const RefPtr<NG::ScrollablePattern>& pattern, const RefPtr<FrameNode>& node)
100 {
101 auto scrollableNode = pattern->GetHost();
102 CHECK_NULL_RETURN(scrollableNode, 0.0f);
103 auto context = scrollableNode->GetRenderContext();
104 CHECK_NULL_RETURN(context, 0.0f);
105 auto offset = context->GetPaintRectWithTransform().GetOffset();
106 auto parent = scrollableNode->GetAncestorNodeOfFrame(true);
107 auto nodeImpl = AceType::DynamicCast<FrameNodeImpl>(node);
108 while (parent && parent != nodeImpl->GetAceNode()) {
109 auto renderContext = parent->GetRenderContext();
110 CHECK_NULL_RETURN(renderContext, 0.0f);
111 offset += renderContext->GetPaintRectWithTransform().GetOffset();
112 parent = parent->GetAncestorNodeOfFrame(true);
113 }
114 return offset.GetY();
115 }
116
startEndIndices(const RefPtr<NG::GridPattern> & pattern)117 std::pair<int32_t, int32_t> startEndIndices(const RefPtr<NG::GridPattern>& pattern)
118 {
119 const auto& gridLayoutInfo = pattern->GetGridLayoutInfo();
120 return { gridLayoutInfo.startIndex_, gridLayoutInfo.endIndex_ };
121 }
122
startEndIndices(const RefPtr<NG::ListPattern> & pattern)123 std::pair<int32_t, int32_t> startEndIndices(const RefPtr<NG::ListPattern>& pattern)
124 {
125 return { pattern->GetStartIndex(), pattern->GetEndIndex() };
126 }
127
startEndIndices(const RefPtr<NG::ScrollPattern> & pattern)128 std::pair<int32_t, int32_t> startEndIndices(const RefPtr<NG::ScrollPattern>& pattern)
129 {
130 return { 0, 0 };
131 }
132
startEndIndices(const RefPtr<NG::WaterFlowPattern> & pattern)133 std::pair<int32_t, int32_t> startEndIndices(const RefPtr<NG::WaterFlowPattern>& pattern)
134 {
135 return { pattern->GetBeginIndex(), pattern->GetEndIndex() };
136 }
137
138 enum class Edge {
139 TOP,
140 BOTTOM
141 };
142
143 template<typename T, Edge edge>
GetContentEdge(const RefPtr<T> & pattern,const RefPtr<FrameNode> & node)144 double GetContentEdge(const RefPtr<T>& pattern, const RefPtr<FrameNode>& node)
145 {
146 const auto [startIndex, endIndex] = startEndIndices(pattern);
147 APP_LOGD("[HDS_TABS] ScrollerImpl::GetContentEdge(): startIndex = %{public}d, endIndex = %{public}d",
148 startIndex, endIndex);
149 auto edgePosition = 0.0;
150 const auto& rect = pattern->GetItemRect(startIndex);
151 if constexpr (edge == Edge::TOP) {
152 edgePosition = rect.Top();
153 } else {
154 edgePosition = rect.Bottom();
155 }
156 for (auto i = startIndex + 1; i <= endIndex; ++i) {
157 const auto& rect = pattern->GetItemRect(i);
158 if constexpr (edge == Edge::TOP) {
159 if (const auto e = rect.Top(); e < edgePosition) {
160 edgePosition = e;
161 }
162 } else {
163 if (const auto e = rect.Bottom(); e > edgePosition) {
164 edgePosition = e;
165 }
166 }
167 }
168 APP_LOGD("[HDS_TABS] ScrollerImpl::GetContentEdge(): edge = %{public}f", edgePosition);
169 edgePosition += GetScrollableOffsetY(pattern, node);
170 return edgePosition;
171 }
172
173 template<Edge edge>
GetContentEdge(const RefPtr<Framework::JSScroller> & jsScroller,const RefPtr<FrameNode> & node)174 double GetContentEdge(const RefPtr<Framework::JSScroller>& jsScroller, const RefPtr<FrameNode>& node)
175 {
176 APP_LOGD("[HDS_TABS] -> ScrollerImpl::GetEdge()");
177 auto scrollablePattern = GetScrollablePattern(jsScroller);
178 CHECK_NULL_RETURN(scrollablePattern, 0.0);
179
180 auto gridPattern = AceType::DynamicCast<NG::GridPattern>(scrollablePattern);
181 if (gridPattern) {
182 APP_LOGD("[HDS_TABS] ScrollerImpl::GetEdge(): GridPattern");
183 return GetContentEdge<NG::GridPattern, edge>(gridPattern, node);
184 }
185
186 auto listPattern = AceType::DynamicCast<NG::ListPattern>(scrollablePattern);
187 if (listPattern) {
188 APP_LOGD("[HDS_TABS] ScrollerImpl::GetEdge(): ListPattern");
189 return GetContentEdge<NG::ListPattern, edge>(listPattern, node);
190 }
191
192 auto scrollPattern = AceType::DynamicCast<NG::ScrollPattern>(scrollablePattern);
193 if (scrollPattern) {
194 APP_LOGD("[HDS_TABS] ScrollerImpl::GetEdge(): ScrollPattern");
195 return GetContentEdge<NG::ScrollPattern, edge>(scrollPattern, node);
196 }
197
198 auto waterFlowPattern = AceType::DynamicCast<NG::WaterFlowPattern>(scrollablePattern);
199 if (waterFlowPattern) {
200 APP_LOGD("[HDS_TABS] ScrollerImpl::GetEdge(): WaterFlowPattern");
201 return GetContentEdge<NG::WaterFlowPattern, edge>(waterFlowPattern, node);
202 }
203
204 return 0.0;
205 }
206
207 template<Edge edge>
GetScrollableEdge(const RefPtr<NG::ScrollablePattern> & pattern,const RefPtr<FrameNode> & node)208 double GetScrollableEdge(const RefPtr<NG::ScrollablePattern>& pattern, const RefPtr<FrameNode>& node)
209 {
210 auto edgePosition = 0.0;
211 auto scrollableNode = pattern->GetHost();
212 CHECK_NULL_RETURN(scrollableNode, edgePosition);
213 auto geometryNode = scrollableNode->GetGeometryNode();
214 CHECK_NULL_RETURN(geometryNode, edgePosition);
215 const auto& rect = geometryNode->GetFrameRect();
216 if constexpr (edge != Edge::TOP) {
217 edgePosition = rect.Bottom() - rect.Top();
218 }
219 APP_LOGD("[HDS_TABS] ScrollerImpl::GetScrollableEdge(): edge = %{public}f", edgePosition);
220 edgePosition += GetScrollableOffsetY(pattern, node);
221 return edgePosition;
222 }
223
224 template<Edge edge>
GetScrollableEdge(const RefPtr<Framework::JSScroller> & jsScroller,const RefPtr<FrameNode> & node)225 double GetScrollableEdge(const RefPtr<Framework::JSScroller>& jsScroller, const RefPtr<FrameNode>& node)
226 {
227 auto scrollablePattern = GetScrollablePattern(jsScroller);
228 CHECK_NULL_RETURN(scrollablePattern, 0.0);
229 return GetScrollableEdge<edge>(scrollablePattern, node);
230 }
231
GetContentTop(const RefPtr<FrameNode> & node)232 double ScrollerImpl::GetContentTop(const RefPtr<FrameNode>& node)
233 {
234 auto scrollablePattern = GetScrollablePattern(jsScroller_);
235 CHECK_NULL_RETURN(scrollablePattern, 0.0);
236 auto scrollPattern = AceType::DynamicCast<NG::ScrollPattern>(scrollablePattern);
237 auto waterFlowPattern = AceType::DynamicCast<NG::WaterFlowPattern>(scrollablePattern);
238
239 bool isNeedGetScrollableEdge = (waterFlowPattern && !waterFlowPattern->GetItemStart()) ||
240 (!waterFlowPattern && !scrollPattern && !IsAtStart());
241 if (isNeedGetScrollableEdge) {
242 return GetScrollableEdge<Edge::TOP>(jsScroller_, node);
243 }
244 return GetContentEdge<Edge::TOP>(jsScroller_, node);
245 }
246
GetContentBottom(const RefPtr<FrameNode> & node)247 double ScrollerImpl::GetContentBottom(const RefPtr<FrameNode>& node)
248 {
249 auto scrollablePattern = GetScrollablePattern(jsScroller_);
250 CHECK_NULL_RETURN(scrollablePattern, 0.0);
251 auto scrollPattern = AceType::DynamicCast<NG::ScrollPattern>(scrollablePattern);
252 auto waterFlowPattern = AceType::DynamicCast<NG::WaterFlowPattern>(scrollablePattern);
253
254 bool isNeedGetScrollableEdge = (waterFlowPattern && !waterFlowPattern->GetItemEnd()) ||
255 (!waterFlowPattern && !scrollPattern && !IsAtEnd());
256 if (isNeedGetScrollableEdge) {
257 return GetScrollableEdge<Edge::BOTTOM>(jsScroller_, node);
258 }
259 return GetContentEdge<Edge::BOTTOM>(jsScroller_, node);
260 }
261
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve,bool smooth,bool canOverScroll)262 bool ScrollerImpl::AnimateTo(const Dimension& position, float duration,
263 const RefPtr<Curve>& curve, bool smooth, bool canOverScroll)
264 {
265 RefPtr<ScrollControllerBase> scrollController = jsScroller_->GetController().Upgrade();
266 if (!scrollController) {
267 return false;
268 }
269 return scrollController->AnimateTo(position, duration, curve, smooth, canOverScroll);
270 }
271
operator ==(const Ace::RefPtr<Scroller> & other) const272 bool ScrollerImpl::operator==(const Ace::RefPtr<Scroller>& other) const
273 {
274 auto impl = AceType::DynamicCast<Ace::Kit::ScrollerImpl>(other);
275 CHECK_NULL_RETURN(impl, false);
276 return jsScroller_ == impl->jsScroller_;
277 }
278
GetBindingFrameNode()279 RefPtr<FrameNode> ScrollerImpl::GetBindingFrameNode()
280 {
281 auto pattern = GetScrollablePattern(jsScroller_);
282 CHECK_NULL_RETURN(pattern, nullptr);
283 auto host = pattern->GetHost();
284 CHECK_NULL_RETURN(host, nullptr);
285 auto kitNode = host->GetKitNode();
286 if (kitNode) {
287 return kitNode;
288 }
289
290 kitNode = AceType::MakeRefPtr<FrameNodeImpl>(AceType::RawPtr(host));
291 host->SetKitNode(kitNode);
292 return kitNode;
293 }
294 } // namespace OHOS::Ace::Kit
295