• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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