1 /*
2 * Copyright (c) 2020-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 "components/ui_abstract_scroll.h"
17
18 #include "securec.h"
19
20 #include "animator/interpolation.h"
21 #include "common/screen.h"
22 #include "components/ui_abstract_scroll_bar.h"
23 #include "components/ui_arc_scroll_bar.h"
24 #include "components/ui_box_scroll_bar.h"
25 #if DEFAULT_ANIMATION
26 #include "graphic_timer.h"
27 #endif
28
29 namespace OHOS {
30 #if DEFAULT_ANIMATION
31 class BarEaseInOutAnimator final : public AnimatorCallback {
32 public:
33 BarEaseInOutAnimator() = delete;
34 BarEaseInOutAnimator(const BarEaseInOutAnimator&) = delete;
35 BarEaseInOutAnimator& operator=(const BarEaseInOutAnimator&) = delete;
36 BarEaseInOutAnimator(BarEaseInOutAnimator&&) = delete;
37 BarEaseInOutAnimator& operator=(BarEaseInOutAnimator&&) = delete;
38
BarEaseInOutAnimator(UIAbstractScroll & scrollView)39 explicit BarEaseInOutAnimator(UIAbstractScroll& scrollView)
40 : scrollView_(scrollView),
41 timer_(APPEAR_PERIOD, TimerCb, this),
42 animator_(this, nullptr, ANIMATOR_DURATION, false)
43 {
44 }
45
~BarEaseInOutAnimator()46 ~BarEaseInOutAnimator()
47 {
48 timer_.Stop();
49 animator_.Stop();
50 }
51
RefreshBar()52 void RefreshBar()
53 {
54 if (animator_.GetState() == Animator::START) {
55 if (!isEaseIn_) {
56 animator_.SetRunTime(ANIMATOR_DURATION - animator_.GetRunTime());
57 }
58 } else if (scrollView_.yScrollBar_->GetOpacity() == OPA_TRANSPARENT) {
59 animator_.Start();
60 } else {
61 timer_.Start(); // updates the start time of timer, ensuring that timer is triggered two seconds after the
62 // last operation
63 }
64 isEaseIn_ = true;
65 }
66
67 private:
Callback(UIView * view)68 void Callback(UIView* view) override
69 {
70 uint8_t opa = OPA_OPAQUE * animator_.GetRunTime() / ANIMATOR_DURATION;
71 if (!isEaseIn_) {
72 opa = OPA_OPAQUE - opa;
73 }
74 float bezielY = opa;
75 bezielY =
76 Interpolation::GetBezierY(bezielY / OPA_OPAQUE, BEZIER_CONTROL_POINT_X_1, 0, BEZIER_CONTROL_POINT_X_2, 1);
77 opa = static_cast<uint8_t>(bezielY * opa);
78 if (scrollView_.yScrollBarVisible_) {
79 scrollView_.yScrollBar_->SetOpacity(opa);
80 }
81 if (scrollView_.xScrollBarVisible_) {
82 scrollView_.xScrollBar_->SetOpacity(opa);
83 }
84 scrollView_.Invalidate();
85 }
86
OnStop(UIView & view)87 void OnStop(UIView& view) override
88 {
89 if (isEaseIn_) {
90 if (scrollView_.yScrollBarVisible_) {
91 scrollView_.yScrollBar_->SetOpacity(OPA_OPAQUE);
92 }
93 if (Screen::GetInstance().GetScreenShape() == ScreenShape::RECTANGLE && scrollView_.xScrollBarVisible_) {
94 scrollView_.xScrollBar_->SetOpacity(OPA_OPAQUE);
95 }
96 timer_.Start(); // The timer is triggered when animation stops.
97 } else {
98 if (scrollView_.yScrollBarVisible_) {
99 scrollView_.yScrollBar_->SetOpacity(OPA_TRANSPARENT);
100 }
101 if (scrollView_.xScrollBarVisible_) {
102 scrollView_.xScrollBar_->SetOpacity(OPA_TRANSPARENT);
103 }
104 }
105 scrollView_.Invalidate();
106 }
107
TimerCb(void * arg)108 static void TimerCb(void* arg)
109 {
110 BarEaseInOutAnimator* barAnimator = reinterpret_cast<BarEaseInOutAnimator*>(arg);
111 barAnimator->isEaseIn_ = false;
112 barAnimator->animator_.Start();
113 }
114 static constexpr uint16_t ANIMATOR_DURATION = 250;
115 static constexpr uint16_t APPEAR_PERIOD = 2000;
116 static constexpr float BEZIER_CONTROL_POINT_X_1 = 0.33f;
117 static constexpr float BEZIER_CONTROL_POINT_X_2 = 0.67f;
118 UIAbstractScroll& scrollView_;
119 GraphicTimer timer_;
120 Animator animator_;
121 bool isEaseIn_ = true;
122 };
123 #endif
124
UIAbstractScroll()125 UIAbstractScroll::UIAbstractScroll()
126 : direction_(VERTICAL),
127 deltaIndex_(0),
128 rotateIndex_(0),
129 reserve_(0),
130 easingFunc_(EasingEquation::CubicEaseOut),
131 scrollAnimator_(&animatorCallback_, this, 0, true),
132 scrollBarSide_(SCROLL_BAR_RIGHT_SIDE),
133 scrollBarCenter_({0, 0}),
134 scrollBarCenterSetFlag_(false)
135 {
136 #if defined(ENABLE_FOCUS_MANAGER) && ENABLE_FOCUS_MANAGER
137 focusable_ = true;
138 #endif
139 #if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
140 rotateFactor_ = DEFAULT_SCROLL_VIEW_ROTATE_FACTOR;
141 rotateThrowthreshold_ = ABSTRACT_ROTATE_THROW_THRESHOLD;
142 rotateAccCoefficient_ = ABSTRACT_ROTATE_DISTANCE_COEFF;
143 isRotating_ = false;
144 #endif
145 isViewGroup_ = true;
146 touchable_ = true;
147 draggable_ = true;
148 dragParentInstead_ = false;
149 }
150
~UIAbstractScroll()151 UIAbstractScroll::~UIAbstractScroll()
152 {
153 #if defined(DEFAULT_ANIMATION) && DEFAULT_ANIMATION
154 if (barEaseInOutAnimator_ != nullptr) {
155 delete barEaseInOutAnimator_;
156 barEaseInOutAnimator_ = nullptr;
157 }
158 #endif
159 if (xScrollBar_ != nullptr) {
160 delete xScrollBar_;
161 xScrollBar_ = nullptr;
162 }
163 if (yScrollBar_ != nullptr) {
164 delete yScrollBar_;
165 yScrollBar_ = nullptr;
166 }
167 }
168
MoveChildByOffset(int16_t offsetX,int16_t offsetY)169 void UIAbstractScroll::MoveChildByOffset(int16_t offsetX, int16_t offsetY)
170 {
171 if ((offsetX == 0) && (offsetY == 0)) {
172 return;
173 }
174 UIView* view = GetChildrenHead();
175 while (view != nullptr) {
176 int16_t x = view->GetX() + offsetX;
177 int16_t y = view->GetY() + offsetY;
178 view->SetPosition(x, y);
179 view = view->GetNextSibling();
180 }
181 Invalidate();
182 }
183
GetMaxDelta() const184 int16_t UIAbstractScroll::GetMaxDelta() const
185 {
186 int16_t result = 0;
187 for (int16_t i = 0; i < MAX_DELTA_SIZE; i++) {
188 if (result < MATH_ABS(lastDelta_[i])) {
189 result = MATH_ABS(lastDelta_[i]);
190 }
191 }
192 return result;
193 }
194
GetMaxRotate() const195 int16_t UIAbstractScroll::GetMaxRotate() const
196 {
197 int16_t result = 0;
198 for (int16_t i = 0; i < MAX_DELTA_SIZE; i++) {
199 if (MATH_ABS(result) < MATH_ABS(lastRotate_[i])) {
200 result = lastRotate_[i];
201 }
202 }
203 return result;
204 }
205
InitDelta()206 void UIAbstractScroll::InitDelta()
207 {
208 if (memset_s(lastDelta_, sizeof(lastDelta_), 0, sizeof(lastDelta_)) != EOK) {
209 GRAPHIC_LOGE("memset_s error");
210 }
211 }
212
InitRotate()213 void UIAbstractScroll::InitRotate()
214 {
215 if (memset_s(lastRotate_, sizeof(lastRotate_), 0, sizeof(lastRotate_)) != EOK) {
216 GRAPHIC_LOGE("memset_s error");
217 }
218 }
219
StopAnimator()220 void UIAbstractScroll::StopAnimator()
221 {
222 scrollAnimator_.Stop();
223 animatorCallback_.ResetCallback();
224 isDragging_ = false;
225 }
226
DragThrowAnimator(Point currentPos,Point lastPos,uint8_t dragDirection,bool dragBack)227 bool UIAbstractScroll::DragThrowAnimator(Point currentPos, Point lastPos, uint8_t dragDirection, bool dragBack)
228 {
229 if (!throwDrag_ && (reboundSize_ == 0)) {
230 return false;
231 }
232 int16_t dragDistanceX = 0;
233 int16_t dragDistanceY = 0;
234 if (throwDrag_) {
235 CalculateDragDistance(currentPos, lastPos, dragDirection, dragDistanceX, dragDistanceY);
236 }
237 if (reboundSize_ != 0) {
238 CalculateReboundDistance(dragDistanceX, dragDistanceY);
239 }
240
241 if (!dragBack) {
242 FixDistance(dragDistanceX, dragDistanceY);
243 }
244
245 StartAnimator(dragDistanceX, dragDistanceY);
246 return true;
247 }
248
StartAnimator(int16_t dragDistanceX,int16_t dragDistanceY)249 void UIAbstractScroll::StartAnimator(int16_t dragDistanceX, int16_t dragDistanceY)
250 {
251 int16_t dragTimes = MATH_MAX(MATH_ABS(dragDistanceX), MATH_ABS(dragDistanceY)) / DRAG_TIMES_COEFFICIENT;
252 if (dragTimes < MIN_DRAG_TIMES) {
253 dragTimes = MIN_DRAG_TIMES;
254 }
255 animatorCallback_.ResetCallback();
256 animatorCallback_.SetDragStartValue(0, 0);
257 animatorCallback_.SetDragEndValue(dragDistanceX, dragDistanceY);
258 animatorCallback_.SetDragTimes(dragTimes * DRAG_ACC_FACTOR / GetDragACCLevel());
259 scrollAnimator_.Start();
260 }
261
CalculateDragDistance(Point currentPos,Point lastPos,uint8_t dragDirection,int16_t & dragDistanceX,int16_t & dragDistanceY)262 void UIAbstractScroll::CalculateDragDistance(Point currentPos,
263 Point lastPos,
264 uint8_t dragDirection,
265 int16_t& dragDistanceX,
266 int16_t& dragDistanceY)
267 {
268 if ((direction_ == VERTICAL) || (direction_ == HORIZONTAL_AND_VERTICAL)) {
269 dragDistanceY = currentPos.y - lastPos.y;
270 if (isRotating_) {
271 dragDistanceY *= rotateAccCoefficient_;
272 } else {
273 dragDistanceY *= DRAG_DISTANCE_COEFFICIENT;
274 if (dragDistanceY > 0 || (dragDistanceY == 0 && dragDirection == DragEvent::DIRECTION_TOP_TO_BOTTOM)) {
275 dragDistanceY += GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
276 } else if (dragDistanceY < 0 ||
277 (dragDistanceY == 0 && dragDirection == DragEvent::DIRECTION_BOTTOM_TO_TOP)) {
278 dragDistanceY -= GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
279 }
280 }
281 }
282
283 if ((direction_ == HORIZONTAL) || (direction_ == HORIZONTAL_AND_VERTICAL)) {
284 dragDistanceX = currentPos.x - lastPos.x;
285 if (isRotating_) {
286 dragDistanceX *= rotateAccCoefficient_;
287 } else {
288 dragDistanceX *= DRAG_DISTANCE_COEFFICIENT;
289 if (dragDistanceX > 0 || (dragDistanceX == 0 && dragDirection == DragEvent::DIRECTION_LEFT_TO_RIGHT)) {
290 dragDistanceX += GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
291 } else if (dragDistanceX < 0 ||
292 (dragDistanceX == 0 && dragDirection == DragEvent::DIRECTION_RIGHT_TO_LEFT)) {
293 dragDistanceX -= GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
294 }
295 }
296 }
297
298 if (maxScrollDistance_ != 0) {
299 if (MATH_ABS(dragDistanceY) > maxScrollDistance_) {
300 int16_t calculatedValue = (dragDistanceY > 0) ? 1 : -1;
301 dragDistanceY = calculatedValue * maxScrollDistance_;
302 }
303 if (MATH_ABS(dragDistanceX) > maxScrollDistance_) {
304 int16_t calculatedValue = (dragDistanceX > 0) ? 1 : -1;
305 dragDistanceX = calculatedValue * maxScrollDistance_;
306 }
307 }
308 }
309
Callback(UIView * view)310 void UIAbstractScroll::ListAnimatorCallback::Callback(UIView* view)
311 {
312 if (view == nullptr) {
313 return;
314 }
315
316 UIAbstractScroll* scrollView = static_cast<UIAbstractScroll*>(view);
317 scrollView->isDragging_ = true;
318 curtTime_++;
319 if (curtTime_ <= dragTimes_) {
320 bool needStopX = false;
321 bool needStopY = false;
322 if (startValueY_ != endValueY_) {
323 int16_t actY = scrollView->easingFunc_(startValueY_, endValueY_, curtTime_, dragTimes_);
324 if (!scrollView->DragYInner(actY - previousValueY_)) {
325 needStopY = true;
326 }
327 previousValueY_ = actY;
328 } else {
329 needStopY = true;
330 }
331 if (startValueX_ != endValueX_) {
332 int16_t actX = scrollView->easingFunc_(startValueX_, endValueX_, curtTime_, dragTimes_);
333 if (!scrollView->DragXInner(actX - previousValueX_)) {
334 needStopX = true;
335 }
336 previousValueX_ = actX;
337 } else {
338 needStopX = true;
339 }
340 if (needStopX && needStopY) {
341 scrollView->StopAnimator();
342 }
343 } else {
344 scrollView->StopAnimator();
345 }
346 }
347
348 #if ENABLE_ROTATE_INPUT
OnRotateStartEvent(const RotateEvent & event)349 bool UIAbstractScroll::OnRotateStartEvent(const RotateEvent& event)
350 {
351 isRotating_ = true;
352 if (scrollAnimator_.GetState() != Animator::STOP) {
353 UIAbstractScroll::StopAnimator();
354 }
355 return UIView::OnRotateStartEvent(event);
356 }
357
OnRotateEvent(const RotateEvent & event)358 bool UIAbstractScroll::OnRotateEvent(const RotateEvent& event)
359 {
360 int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
361 RefreshRotate(rotateLen);
362 if (direction_ == HORIZONTAL) {
363 DragXInner(rotateLen);
364 } else {
365 DragYInner(rotateLen);
366 }
367 return UIView::OnRotateEvent(event);
368 }
369
OnRotateEndEvent(const RotateEvent & event)370 bool UIAbstractScroll::OnRotateEndEvent(const RotateEvent& event)
371 {
372 InitDelta();
373
374 uint8_t dir;
375 int16_t lastRotateLen = GetMaxRotate();
376 if (direction_ == HORIZONTAL) {
377 dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_LEFT_TO_RIGHT : DragEvent::DIRECTION_RIGHT_TO_LEFT;
378 } else {
379 dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_TOP_TO_BOTTOM : DragEvent::DIRECTION_BOTTOM_TO_TOP;
380 }
381 bool triggerAnimator = (MATH_ABS(lastRotateLen) >= rotateThrowthreshold_);
382 if (throwDrag_ && triggerAnimator) {
383 Point current;
384 if (direction_ == HORIZONTAL) {
385 current = {lastRotateLen, 0};
386 } else {
387 current = {0, lastRotateLen};
388 }
389 DragThrowAnimator(current, {0, 0}, dir, dragBack_);
390 } else {
391 DragThrowAnimator({0, 0}, {0, 0}, dir, dragBack_);
392 }
393 isRotating_ = false;
394 InitRotate();
395 return UIView::OnRotateEndEvent(event);
396 }
397 #endif
398
SetXScrollBarVisible(bool visible)399 void UIAbstractScroll::SetXScrollBarVisible(bool visible)
400 {
401 if (Screen::GetInstance().GetScreenShape() == ScreenShape::CIRCLE) {
402 return;
403 } else if (visible && xScrollBar_ == nullptr) {
404 xScrollBar_ = new UIBoxScrollBar();
405 }
406 xScrollBarVisible_ = visible;
407 #if DEFAULT_ANIMATION
408 if (xScrollBarVisible_ && barEaseInOutAnimator_ == nullptr) {
409 barEaseInOutAnimator_ = new BarEaseInOutAnimator(*this);
410 }
411 #endif
412 }
413
SetYScrollBarVisible(bool visible)414 void UIAbstractScroll::SetYScrollBarVisible(bool visible)
415 {
416 yScrollBarVisible_ = visible;
417 if (yScrollBarVisible_ && yScrollBar_ == nullptr) {
418 if (Screen::GetInstance().GetScreenShape() == ScreenShape::CIRCLE) {
419 yScrollBar_ = new UIArcScrollBar();
420 } else {
421 yScrollBar_ = new UIBoxScrollBar();
422 }
423 }
424 #if DEFAULT_ANIMATION
425 if (yScrollBarVisible_ && barEaseInOutAnimator_ == nullptr) {
426 barEaseInOutAnimator_ = new BarEaseInOutAnimator(*this);
427 }
428 #endif
429 }
430
OnPostDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)431 void UIAbstractScroll::OnPostDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
432 {
433 Rect scrollRect = GetRect();
434 uint8_t opa = GetMixOpaScale();
435 if (Screen::GetInstance().GetScreenShape() == ScreenShape::RECTANGLE) {
436 if (yScrollBarVisible_) {
437 if (scrollBarSide_ == SCROLL_BAR_RIGHT_SIDE) {
438 yScrollBar_->SetPosition(scrollRect.GetRight() - SCROLL_BAR_WIDTH + 1, scrollRect.GetTop(),
439 SCROLL_BAR_WIDTH, scrollRect.GetHeight());
440 } else {
441 yScrollBar_->SetPosition(scrollRect.GetLeft(), scrollRect.GetTop(), SCROLL_BAR_WIDTH,
442 scrollRect.GetHeight());
443 }
444 yScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
445 }
446 if (xScrollBarVisible_) {
447 if (scrollBarSide_ == SCROLL_BAR_RIGHT_SIDE) {
448 xScrollBar_->SetPosition(scrollRect.GetLeft(), scrollRect.GetBottom() - SCROLL_BAR_WIDTH + 1,
449 scrollRect.GetWidth() - SCROLL_BAR_WIDTH, SCROLL_BAR_WIDTH);
450 } else {
451 xScrollBar_->SetPosition(scrollRect.GetLeft() + SCROLL_BAR_WIDTH,
452 scrollRect.GetBottom() - SCROLL_BAR_WIDTH + 1,
453 scrollRect.GetWidth() - SCROLL_BAR_WIDTH, SCROLL_BAR_WIDTH);
454 }
455 xScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
456 }
457 } else {
458 if (yScrollBarVisible_) {
459 yScrollBar_->SetScrollBarSide(scrollBarSide_);
460 int16_t x;
461 int16_t y;
462 if (scrollBarCenterSetFlag_) {
463 x = scrollRect.GetX() + scrollBarCenter_.x;
464 y = scrollRect.GetY() + scrollBarCenter_.y;
465 } else {
466 x = scrollRect.GetX() + (GetWidth() / 2); // 2: half
467 y = scrollRect.GetY() + (GetHeight() / 2); // 2: half
468 }
469 yScrollBar_->SetPosition(x, y, SCROLL_BAR_WIDTH, GetWidth() / 2); // 2: half
470 yScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
471 }
472 }
473 UIView::OnPostDraw(gfxDstBuffer, invalidatedArea);
474 }
475
RefreshAnimator()476 void UIAbstractScroll::RefreshAnimator()
477 {
478 #if DEFAULT_ANIMATION
479 barEaseInOutAnimator_->RefreshBar();
480 #endif
481 }
482 } // namespace OHOS
483