• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/scrollable/scrollable_pattern.h"
17 #include "base/geometry/axis.h"
18 #include "base/geometry/point.h"
19 #include "base/memory/ace_type.h"
20 #include "base/utils/utils.h"
21 #include "core/components/scroll/scrollable.h"
22 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
23 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
24 
25 namespace OHOS::Ace::NG {
SetAxis(Axis axis)26 void ScrollablePattern::SetAxis(Axis axis)
27 {
28     if (axis_ == axis) {
29         return;
30     }
31     axis_ = axis;
32     if (scrollBar_) {
33         scrollBar_->SetPositionMode(axis_ == Axis::HORIZONTAL ? PositionMode::BOTTOM : PositionMode::RIGHT);
34     }
35     auto gestureHub = GetGestureHub();
36     CHECK_NULL_VOID(gestureHub);
37     if (scrollableEvent_) {
38         gestureHub->RemoveScrollableEvent(scrollableEvent_);
39         scrollableEvent_->SetAxis(axis);
40         gestureHub->AddScrollableEvent(scrollableEvent_);
41     }
42     if (scrollEffect_) {
43         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
44         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
45     }
46 }
47 
GetGestureHub()48 const RefPtr<GestureEventHub>& ScrollablePattern::GetGestureHub()
49 {
50     auto host = GetHost();
51     CHECK_NULL_RETURN(host, nullptr);
52     auto hub = host->GetEventHub<EventHub>();
53     CHECK_NULL_RETURN(hub, nullptr);
54     return hub->GetOrCreateGestureEventHub();
55 }
56 
GetInputHub()57 const RefPtr<InputEventHub>& ScrollablePattern::GetInputHub()
58 {
59     auto host = GetHost();
60     CHECK_NULL_RETURN(host, nullptr);
61     auto hub = host->GetEventHub<EventHub>();
62     CHECK_NULL_RETURN(host, nullptr);
63     return hub->GetOrCreateInputEventHub();
64 }
65 
OnScrollCallback(float offset,int32_t source)66 bool ScrollablePattern::OnScrollCallback(float offset, int32_t source)
67 {
68     if (source == SCROLL_FROM_START) {
69         return true;
70     }
71     if (scrollBar_ && scrollBar_->IsDriving()) {
72         offset = scrollBar_->CalcPatternOffset(offset);
73         source = SCROLL_FROM_BAR;
74     }
75     return UpdateCurrentOffset(offset, source);
76 }
77 
OnScrollPosition(double offset,int32_t source)78 bool ScrollablePattern::OnScrollPosition(double offset, int32_t source)
79 {
80     if (coordinationEvent_ && isReactInParentMovement_) {
81         auto onScroll = coordinationEvent_->GetOnScroll();
82         if (onScroll) {
83             onScroll(offset);
84             return false;
85         }
86     }
87     auto isAtTop = (IsAtTop() && Positive(offset));
88     if (isAtTop && source == SCROLL_FROM_UPDATE && !isReactInParentMovement_ && (axis_ == Axis::VERTICAL)) {
89         isReactInParentMovement_ = true;
90         if (coordinationEvent_) {
91             auto onScrollStart = coordinationEvent_->GetOnScrollStartEvent();
92             if (onScrollStart) {
93                 onScrollStart();
94             }
95         }
96     }
97     if (source == SCROLL_FROM_START) {
98         if (scrollBarProxy_) {
99             scrollBarProxy_->StopScrollBarAnimator();
100         }
101     }
102     return true;
103 }
104 
OnScrollEnd()105 void ScrollablePattern::OnScrollEnd()
106 {
107     if (coordinationEvent_ && isReactInParentMovement_) {
108         isReactInParentMovement_ = false;
109         auto onScrollEnd = coordinationEvent_->GetOnScrollEndEvent();
110         if (onScrollEnd) {
111             onScrollEnd();
112             return;
113         }
114     }
115     if (scrollBar_) {
116         scrollBar_->SetDriving(false);
117         scrollBar_->OnScrollEnd();
118     }
119     if (scrollBarProxy_) {
120         scrollBarProxy_->StartScrollBarAnimator();
121     }
122 }
123 
AddScrollEvent()124 void ScrollablePattern::AddScrollEvent()
125 {
126     auto gestureHub = GetGestureHub();
127     CHECK_NULL_VOID(gestureHub);
128     if (scrollableEvent_) {
129         gestureHub->RemoveScrollableEvent(scrollableEvent_);
130     }
131     scrollableEvent_ = MakeRefPtr<ScrollableEvent>(GetAxis());
132     auto scrollCallback = [weak = WeakClaim(this)](double offset, int32_t source) {
133         auto pattern = weak.Upgrade();
134         CHECK_NULL_RETURN(pattern, false);
135         if (!pattern->OnScrollPosition(offset, source)) {
136             return false;
137         }
138         return pattern->OnScrollCallback(static_cast<float>(offset), source);
139     };
140     scrollableEvent_->SetScrollPositionCallback(std::move(scrollCallback));
141     auto scrollEnd = [weak = WeakClaim(this)]() {
142         auto pattern = weak.Upgrade();
143         CHECK_NULL_VOID(pattern);
144         pattern->OnScrollEnd();
145         pattern->OnScrollEndCallback();
146     };
147     scrollableEvent_->SetScrollEndCallback(std::move(scrollEnd));
148     gestureHub->AddScrollableEvent(scrollableEvent_);
149 }
150 
SetEdgeEffect(EdgeEffect edgeEffect)151 void ScrollablePattern::SetEdgeEffect(EdgeEffect edgeEffect)
152 {
153     auto gestureHub = GetGestureHub();
154     CHECK_NULL_VOID(gestureHub);
155     if (scrollEffect_ && (edgeEffect != scrollEffect_->GetEdgeEffect())) {
156         gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
157         scrollEffect_.Reset();
158     }
159     if (edgeEffect == EdgeEffect::SPRING && !scrollEffect_) {
160         auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
161         CHECK_NULL_VOID(springEffect);
162         springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
163             auto pattern = weak.Upgrade();
164             CHECK_NULL_RETURN_NOLOG(pattern, false);
165             return pattern->IsAtTop() || pattern->IsAtBottom();
166         });
167         // add callback to springEdgeEffect
168         SetEdgeEffectCallback(springEffect);
169         scrollEffect_ = springEffect;
170         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
171     }
172     if (edgeEffect == EdgeEffect::FADE && !scrollEffect_) {
173         auto fadeEdgeEffect = AceType::MakeRefPtr<ScrollFadeEffect>(Color::GRAY);
174         CHECK_NULL_VOID(fadeEdgeEffect);
175         fadeEdgeEffect->SetHandleOverScrollCallback([weakScroll = AceType::WeakClaim(this)]() -> void {
176             auto list = weakScroll.Upgrade();
177             CHECK_NULL_VOID_NOLOG(list);
178             auto host = list->GetHost();
179             CHECK_NULL_VOID_NOLOG(host);
180             host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
181         });
182         SetEdgeEffectCallback(fadeEdgeEffect);
183         fadeEdgeEffect->InitialEdgeEffect();
184         scrollEffect_ = fadeEdgeEffect;
185         gestureHub->AddScrollEdgeEffect(GetAxis(), scrollEffect_);
186     }
187 }
188 
HandleEdgeEffect(float offset,int32_t source,const SizeF & size)189 bool ScrollablePattern::HandleEdgeEffect(float offset, int32_t source, const SizeF& size)
190 {
191     // check edgeEffect is not springEffect
192     if (scrollEffect_ && scrollEffect_->IsFadeEffect() && source != SCROLL_FROM_BAR) {    // handle edge effect
193         if ((IsAtTop() && Positive(offset)) || (IsAtBottom() && Negative(offset))) {
194             scrollEffect_->HandleOverScroll(GetAxis(), -offset, size);
195         }
196     }
197     if (!(scrollEffect_ && scrollEffect_->IsSpringEffect()) ||
198         source == SCROLL_FROM_BAR || source == SCROLL_FROM_JUMP) {
199         if (IsAtTop() && Positive(offset)) {
200             return false;
201         }
202         if (IsAtBottom() && Negative(offset)) {
203             return false;
204         }
205     }
206     return true;
207 }
208 
RegisterScrollBarEventTask()209 void ScrollablePattern::RegisterScrollBarEventTask()
210 {
211     CHECK_NULL_VOID(scrollBar_);
212     auto host = GetHost();
213     CHECK_NULL_VOID(host);
214     auto gestureHub = GetGestureHub();
215     auto inputHub = GetInputHub();
216     CHECK_NULL_VOID(gestureHub);
217     CHECK_NULL_VOID(inputHub);
218     scrollBar_->SetGestureEvent();
219     scrollBar_->SetMouseEvent();
220     scrollBar_->SetMarkNeedRenderFunc([weak = AceType::WeakClaim(AceType::RawPtr(host))]() {
221         auto host = weak.Upgrade();
222         CHECK_NULL_VOID(host);
223         host->MarkNeedRenderOnly();
224     });
225     gestureHub->AddTouchEvent(scrollBar_->GetTouchEvent());
226     inputHub->AddOnMouseEvent(scrollBar_->GetMouseEvent());
227 }
228 
SetScrollBar(DisplayMode displayMode)229 void ScrollablePattern::SetScrollBar(DisplayMode displayMode)
230 {
231     if (displayMode == DisplayMode::OFF) {
232         if (scrollBar_) {
233             auto gestureHub = GetGestureHub();
234             if (gestureHub) {
235                 gestureHub->RemoveTouchEvent(scrollBar_->GetTouchEvent());
236             }
237             scrollBar_->MarkNeedRender();
238             scrollBar_.Reset();
239         }
240     } else if (!scrollBar_) {
241         scrollBar_ = AceType::MakeRefPtr<ScrollBar>(displayMode);
242         // set the scroll bar style
243         if (GetAxis() == Axis::HORIZONTAL) {
244             scrollBar_->SetPositionMode(PositionMode::BOTTOM);
245         }
246         RegisterScrollBarEventTask();
247         if (displayMode == DisplayMode::AUTO) {
248             scrollBar_->OnScrollEnd();
249         }
250     } else if (scrollBar_->GetDisplayMode() != displayMode) {
251         scrollBar_->SetDisplayMode(displayMode);
252     } else {
253         return;
254     }
255     if (scrollBar_) {
256         UpdateScrollBarOffset();
257     }
258 }
259 
SetScrollBar(const std::unique_ptr<ScrollBarProperty> & property)260 void ScrollablePattern::SetScrollBar(const std::unique_ptr<ScrollBarProperty>& property)
261 {
262     if (!property) {
263         SetScrollBar(DisplayMode::AUTO);
264         return;
265     }
266     auto displayMode = property->GetScrollBarMode().value_or(DisplayMode::AUTO);
267     SetScrollBar(displayMode);
268     if (scrollBar_) {
269         auto barColor = property->GetScrollBarColor();
270         if (barColor) {
271             scrollBar_->SetForegroundColor(barColor.value());
272         }
273         auto barWidth = property->GetScrollBarWidth();
274         if (barWidth) {
275             scrollBar_->SetInactiveWidth(barWidth.value());
276             scrollBar_->SetNormalWidth(barWidth.value());
277             scrollBar_->SetActiveWidth(barWidth.value());
278             scrollBar_->SetTouchWidth(barWidth.value());
279         }
280     }
281 }
282 
UpdateScrollBarRegion(float offset,float estimatedHeight,Size viewPort)283 void ScrollablePattern::UpdateScrollBarRegion(float offset, float estimatedHeight, Size viewPort)
284 {
285     // inner scrollbar
286     if (scrollBar_) {
287         auto host = GetHost();
288         CHECK_NULL_VOID(host);
289         auto layoutPriority = host->GetLayoutProperty();
290         CHECK_NULL_VOID(layoutPriority);
291         auto paddingOffset = layoutPriority->CreatePaddingAndBorder().Offset();
292         auto mainSize = axis_ == Axis::VERTICAL ? viewPort.Height() : viewPort.Width();
293         bool scrollable = GreatNotEqual(estimatedHeight, mainSize);
294         scrollBar_->SetScrollable(IsScrollable() && scrollable);
295         Offset scrollOffset = { offset, offset }; // fit for w/h switched.
296         Offset viewOffset = { paddingOffset.GetX(), paddingOffset.GetY() };
297         scrollBar_->UpdateScrollBarRegion(viewOffset, viewPort, scrollOffset, estimatedHeight);
298         scrollBar_->MarkNeedRender();
299     }
300 
301     // outer scrollbar
302     if (scrollBarProxy_) {
303         estimatedHeight_ = estimatedHeight - (GetAxis() == Axis::VERTICAL ? viewPort.Height() : viewPort.Width());
304         barOffset_ = -offset;
305         scrollBarProxy_->NotifyScrollBar(AceType::WeakClaim(this));
306     }
307 }
308 
SetScrollBarProxy(const RefPtr<ScrollBarProxy> & scrollBarProxy)309 void ScrollablePattern::SetScrollBarProxy(const RefPtr<ScrollBarProxy>& scrollBarProxy)
310 {
311     CHECK_NULL_VOID(scrollBarProxy);
312     auto scrollFunction = [weak = WeakClaim(this)](double offset, int32_t source) {
313         if (source != SCROLL_FROM_START) {
314             auto pattern = weak.Upgrade();
315             if (!pattern || pattern->GetAxis() == Axis::NONE) {
316                 return false;
317             }
318             return pattern->UpdateCurrentOffset(offset, SCROLL_FROM_BAR);
319         }
320         return true;
321     };
322     ScrollableNodeInfo nodeInfo = { AceType::WeakClaim(this), std::move(scrollFunction) };
323     scrollBarProxy->RegisterScrollableNode(nodeInfo);
324     scrollBarProxy_ = scrollBarProxy;
325 }
326 } // namespace OHOS::Ace::NG