1 /*
2  * Copyright (c) 2021 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/render_scroll_bar.h"
17 
18 #include "core/components/display/render_display.h"
19 
20 namespace OHOS::Ace {
21 namespace {
22 
23 constexpr int32_t STOP_DURATION = 2000; // 2000ms
24 constexpr double KEYTIME_START = 0.0;
25 constexpr double KEYTIME_MIDDLE = 0.7;
26 constexpr double KEYTIME_END = 1.0;
27 
28 } // namespace
29 
~RenderScrollBar()30 RenderScrollBar::~RenderScrollBar()
31 {
32     if (proxy_) {
33         proxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
34     }
35 }
36 
Update(const RefPtr<Component> & component)37 void RenderScrollBar::Update(const RefPtr<Component>& component)
38 {
39     auto scrollBarComponent = AceType::DynamicCast<ScrollBarComponent>(component);
40     if (!scrollBarComponent) {
41         LOGE("Type of component is not ScrollBarComponent.");
42         return;
43     }
44     isAxisChanged_ = (axis_ == scrollBarComponent->GetAxis());
45     axis_ = scrollBarComponent->GetAxis();
46     displayMode_ = scrollBarComponent->GetDisplayMode();
47     proxy_ = scrollBarComponent->GetScrollBarProxy();
48     if (proxy_) {
49         proxy_->UnRegisterScrollBar(AceType::WeakClaim(this));
50         proxy_->RegisterScrollBar(AceType::WeakClaim(this));
51     }
52     InitOpacity();
53     InitRecognizer();
54     InitAnimator();
55     InitChildPosition();
56     MarkNeedLayout();
57 }
58 
InitRecognizer()59 void RenderScrollBar::InitRecognizer()
60 {
61     if (!isAxisChanged_ && dragRecognizer_) {
62         LOGW("Axis is not change and DragRecognizer is already exist.");
63         return;
64     }
65     dragRecognizer_ = AceType::MakeRefPtr<DragRecognizer>(axis_);
66     dragRecognizer_->SetOnDragStart([weak = WeakClaim(this)](const DragStartInfo& startInfo) {
67         auto scrollBar = weak.Upgrade();
68         if (scrollBar) {
69             scrollBar->HandleDragStart(startInfo);
70         }
71     });
72     dragRecognizer_->SetOnDragUpdate([weak = WeakClaim(this)](const DragUpdateInfo& updateInfo) {
73         auto scrollBar = weak.Upgrade();
74         if (scrollBar) {
75             scrollBar->HandleDragUpdate(updateInfo);
76         }
77     });
78     dragRecognizer_->SetOnDragEnd([weak = WeakClaim(this)](const DragEndInfo& endInfo) {
79         auto scrollBar = weak.Upgrade();
80         if (scrollBar) {
81             scrollBar->HandleDragFinish();
82         }
83     });
84     dragRecognizer_->SetOnDragCancel([weak = WeakClaim(this)]() {
85         auto scrollBar = weak.Upgrade();
86         if (scrollBar) {
87             scrollBar->HandleDragFinish();
88         }
89     });
90 }
91 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)92 void RenderScrollBar::OnTouchTestHit(
93     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
94 {
95     if (axis_ != Axis::NONE && displayMode_ != DisplayMode::OFF) {
96         dragRecognizer_->SetCoordinateOffset(coordinateOffset);
97         result.emplace_back(dragRecognizer_);
98     }
99 }
100 
HandleDragStart(const DragStartInfo & info)101 void RenderScrollBar::HandleDragStart(const DragStartInfo& info)
102 {
103     auto child = GetChildren().front();
104     if (!child) {
105         return;
106     }
107 
108     StopAnimator();
109 }
110 
HandleDragUpdate(const DragUpdateInfo & info)111 void RenderScrollBar::HandleDragUpdate(const DragUpdateInfo& info)
112 {
113     auto child = GetChildren().front();
114     if (!child) {
115         return;
116     }
117     StopAnimator();
118     if (axis_ == Axis::HORIZONTAL) {
119         auto localX = info.GetLocalLocation().GetX();
120         if (GreatOrEqual(localX, GetTouchRect().Left()) && LessOrEqual(localX, GetTouchRect().Right())) {
121             double positionX = std::clamp(child->GetPosition().GetX() + info.GetDelta().GetX(), 0.0,
122                 (GetLayoutSize() - child->GetLayoutSize()).Width());
123             child->SetPosition(Offset(positionX, child->GetPosition().GetY()));
124             MarkNeedRender();
125             if (proxy_) {
126                 proxy_->NotifyScrollableNode(-info.GetMainDelta(), AceType::WeakClaim(this));
127             }
128         }
129     } else {
130         auto localY = info.GetLocalLocation().GetY();
131         if (GreatOrEqual(localY, GetTouchRect().Top()) && LessOrEqual(localY, GetTouchRect().Bottom())) {
132             double positionY = std::clamp(child->GetPosition().GetY() + info.GetDelta().GetY(), 0.0,
133                 (GetLayoutSize() - child->GetLayoutSize()).Height());
134             child->SetPosition(Offset(child->GetPosition().GetX(), positionY));
135             MarkNeedRender();
136             if (proxy_) {
137                 proxy_->NotifyScrollableNode(-info.GetMainDelta(), AceType::WeakClaim(this));
138             }
139         }
140     }
141     childPosition_ = child->GetPosition();
142 }
143 
HandleDragFinish()144 void RenderScrollBar::HandleDragFinish()
145 {
146     if (displayMode_ == DisplayMode::AUTO && disappearAnimator_) {
147         if (!disappearAnimator_->IsStopped()) {
148             disappearAnimator_->Stop();
149         }
150         disappearAnimator_->Play();
151     }
152 }
153 
InitOpacity()154 void RenderScrollBar::InitOpacity()
155 {
156     switch (displayMode_) {
157         case DisplayMode::OFF:
158             opacity_ = 0;
159             break;
160         case DisplayMode::ON:
161             opacity_ = UINT8_MAX;
162             break;
163         case DisplayMode::AUTO:
164             opacity_ = 0;
165             break;
166         default:
167             break;
168     }
169     UpdateDisplayOpacity(opacity_);
170 }
171 
InitChildPosition()172 void RenderScrollBar::InitChildPosition()
173 {
174     auto child = GetLastChild();
175     if (!child) {
176         return;
177     }
178     auto childPosition = child->GetPosition();
179     if (axis_ == Axis::VERTICAL) {
180         childPosition.SetX(0.0);
181     } else if (axis_ == Axis::HORIZONTAL) {
182         childPosition.SetY(0.0);
183     } else {
184         return;
185     }
186     childPosition_ = childPosition;
187 }
188 
InitAnimator()189 void RenderScrollBar::InitAnimator()
190 {
191     if (disappearAnimator_ && !disappearAnimator_->IsStopped()) {
192         disappearAnimator_->Stop();
193     }
194     if (displayMode_ != DisplayMode::AUTO) {
195         LOGE("DisplayMode is not auto, don't need animator.");
196         return;
197     }
198     if (disappearAnimator_) {
199         disappearAnimator_->Play();
200         return;
201     }
202 
203     disappearAnimator_ = CREATE_ANIMATOR(context_);
204     auto hiddenStartKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEYTIME_START, UINT8_MAX);
205     auto hiddenMiddleKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEYTIME_MIDDLE, UINT8_MAX);
206     auto hiddenEndKeyframe = AceType::MakeRefPtr<Keyframe<int32_t>>(KEYTIME_END, 0);
207     hiddenMiddleKeyframe->SetCurve(Curves::LINEAR);
208     hiddenEndKeyframe->SetCurve(Curves::FRICTION);
209 
210     auto animation = AceType::MakeRefPtr<KeyframeAnimation<int32_t>>();
211     animation->AddKeyframe(hiddenStartKeyframe);
212     animation->AddKeyframe(hiddenMiddleKeyframe);
213     animation->AddKeyframe(hiddenEndKeyframe);
214     animation->AddListener([weakBar = AceType::WeakClaim(this)](int32_t value) {
215         auto scrollBar = weakBar.Upgrade();
216         if (scrollBar) {
217             scrollBar->opacity_ = value;
218             scrollBar->UpdateDisplayOpacity(value);
219         }
220     });
221     disappearAnimator_->AddInterpolator(animation);
222     disappearAnimator_->SetDuration(STOP_DURATION);
223     disappearAnimator_->Play();
224 }
225 
StopAnimator()226 void RenderScrollBar::StopAnimator()
227 {
228     if (disappearAnimator_ && !disappearAnimator_->IsStopped()) {
229         disappearAnimator_->Stop();
230     }
231     if (displayMode_ != DisplayMode::OFF) {
232         UpdateDisplayOpacity(UINT8_MAX);
233     }
234     MarkNeedRender();
235 }
236 
StartAnimator()237 void RenderScrollBar::StartAnimator()
238 {
239     if (!disappearAnimator_) {
240         LOGE("Animator is not exist.");
241         return;
242     }
243     if (!disappearAnimator_->IsStopped()) {
244         disappearAnimator_->Stop();
245     }
246     disappearAnimator_->Play();
247 }
248 
UpdateDisplayOpacity(int32_t opacity)249 void RenderScrollBar::UpdateDisplayOpacity(int32_t opacity)
250 {
251     auto parent = GetParent().Upgrade();
252     while (parent) {
253         auto display = AceType::DynamicCast<RenderDisplay>(parent);
254         if (display) {
255             display->UpdateOpacity(opacity);
256             break;
257         }
258         parent = parent->GetParent().Upgrade();
259     }
260 }
261 
PerformLayout()262 void RenderScrollBar::PerformLayout()
263 {
264     if (!GetChildren().empty()) {
265         const auto& child = GetChildren().front();
266         child->Layout(LayoutParam(GetLayoutParam().GetMaxSize(), Size()));
267         child->SetPosition(childPosition_);
268         childRect_ = Rect(child->GetPosition(), child->GetLayoutSize());
269     }
270     SetLayoutSize(GetLayoutParam().GetMaxSize());
271 }
272 
OnPaintFinish()273 void RenderScrollBar::OnPaintFinish()
274 {
275     auto child = GetLastChild();
276     if (child) {
277         childRect_ = Rect(child->GetPosition(), child->GetLayoutSize());
278     }
279 }
280 
281 } // namespace OHOS::Ace
282