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 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 ENABLE_FOCUS_MANAGER
137 focusable_ = true;
138 #endif
139 #if 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 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 int16_t x;
176 int16_t y;
177 while (view != nullptr) {
178 x = view->GetX() + offsetX;
179 y = view->GetY() + offsetY;
180 view->SetPosition(x, y);
181 view = view->GetNextSibling();
182 }
183 Invalidate();
184 }
185
GetMaxDelta() const186 int16_t UIAbstractScroll::GetMaxDelta() const
187 {
188 int16_t result = 0;
189 for (int16_t i = 0; i < MAX_DELTA_SIZE; i++) {
190 if (result < MATH_ABS(lastDelta_[i])) {
191 result = MATH_ABS(lastDelta_[i]);
192 }
193 }
194 return result;
195 }
196
GetMaxRotate() const197 int16_t UIAbstractScroll::GetMaxRotate() const
198 {
199 int16_t result = 0;
200 for (int16_t i = 0; i < MAX_DELTA_SIZE; i++) {
201 if (MATH_ABS(result) < MATH_ABS(lastRotate_[i])) {
202 result = lastRotate_[i];
203 }
204 }
205 return result;
206 }
207
InitDelta()208 void UIAbstractScroll::InitDelta()
209 {
210 if (memset_s(lastDelta_, sizeof(lastDelta_), 0, sizeof(lastDelta_)) != EOK) {
211 GRAPHIC_LOGE("memset_s error");
212 }
213 }
214
InitRotate()215 void UIAbstractScroll::InitRotate()
216 {
217 if (memset_s(lastRotate_, sizeof(lastRotate_), 0, sizeof(lastRotate_)) != EOK) {
218 GRAPHIC_LOGE("memset_s error");
219 }
220 }
221
StopAnimator()222 void UIAbstractScroll::StopAnimator()
223 {
224 scrollAnimator_.Stop();
225 animatorCallback_.ResetCallback();
226 isDragging_ = false;
227 }
228
DragThrowAnimator(Point currentPos,Point lastPos,uint8_t dragDirection,bool dragBack)229 bool UIAbstractScroll::DragThrowAnimator(Point currentPos, Point lastPos, uint8_t dragDirection, bool dragBack)
230 {
231 if (!throwDrag_ && (reboundSize_ == 0)) {
232 return false;
233 }
234 int16_t dragDistanceX = 0;
235 int16_t dragDistanceY = 0;
236 if (throwDrag_) {
237 CalculateDragDistance(currentPos, lastPos, dragDirection, dragDistanceX, dragDistanceY);
238 }
239 if (reboundSize_ != 0) {
240 CalculateReboundDistance(dragDistanceX, dragDistanceY);
241 }
242
243 if (!dragBack) {
244 FixDistance(dragDistanceX, dragDistanceY);
245 }
246
247 StartAnimator(dragDistanceX, dragDistanceY);
248 return true;
249 }
250
StartAnimator(int16_t dragDistanceX,int16_t dragDistanceY)251 void UIAbstractScroll::StartAnimator(int16_t dragDistanceX, int16_t dragDistanceY)
252 {
253 int16_t dragTimes = MATH_MAX(MATH_ABS(dragDistanceX), MATH_ABS(dragDistanceY)) / DRAG_TIMES_COEFFICIENT;
254 if (dragTimes < MIN_DRAG_TIMES) {
255 dragTimes = MIN_DRAG_TIMES;
256 }
257 animatorCallback_.ResetCallback();
258 animatorCallback_.SetDragStartValue(0, 0);
259 animatorCallback_.SetDragEndValue(dragDistanceX, dragDistanceY);
260 animatorCallback_.SetDragTimes(dragTimes * DRAG_ACC_FACTOR / GetDragACCLevel());
261 scrollAnimator_.Start();
262 }
263
CalculateDragDistance(Point currentPos,Point lastPos,uint8_t dragDirection,int16_t & dragDistanceX,int16_t & dragDistanceY)264 void UIAbstractScroll::CalculateDragDistance(Point currentPos,
265 Point lastPos,
266 uint8_t dragDirection,
267 int16_t& dragDistanceX,
268 int16_t& dragDistanceY)
269 {
270 if ((direction_ == VERTICAL) || (direction_ == HORIZONTAL_AND_VERTICAL)) {
271 dragDistanceY = currentPos.y - lastPos.y;
272 if (isRotating_) {
273 dragDistanceY *= rotateAccCoefficient_;
274 } else {
275 dragDistanceY *= DRAG_DISTANCE_COEFFICIENT;
276 if (dragDistanceY > 0 || (dragDistanceY == 0 && dragDirection == DragEvent::DIRECTION_TOP_TO_BOTTOM)) {
277 dragDistanceY += GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
278 } else if (dragDistanceY < 0 ||
279 (dragDistanceY == 0 && dragDirection == DragEvent::DIRECTION_BOTTOM_TO_TOP)) {
280 dragDistanceY -= GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
281 }
282 }
283 }
284
285 if ((direction_ == HORIZONTAL) || (direction_ == HORIZONTAL_AND_VERTICAL)) {
286 dragDistanceX = currentPos.x - lastPos.x;
287 if (isRotating_) {
288 dragDistanceX *= rotateAccCoefficient_;
289 } else {
290 dragDistanceX *= DRAG_DISTANCE_COEFFICIENT;
291 if (dragDistanceX > 0 || (dragDistanceX == 0 && dragDirection == DragEvent::DIRECTION_LEFT_TO_RIGHT)) {
292 dragDistanceX += GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
293 } else if (dragDistanceX < 0 ||
294 (dragDistanceX == 0 && dragDirection == DragEvent::DIRECTION_RIGHT_TO_LEFT)) {
295 dragDistanceX -= GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
296 }
297 }
298 }
299
300 if (maxScrollDistance_ != 0) {
301 if (MATH_ABS(dragDistanceY) > maxScrollDistance_) {
302 int16_t calculatedValue = (dragDistanceY > 0) ? 1 : -1;
303 dragDistanceY = calculatedValue * maxScrollDistance_;
304 }
305 if (MATH_ABS(dragDistanceX) > maxScrollDistance_) {
306 int16_t calculatedValue = (dragDistanceX > 0) ? 1 : -1;
307 dragDistanceX = calculatedValue * maxScrollDistance_;
308 }
309 }
310 }
311
Callback(UIView * view)312 void UIAbstractScroll::ListAnimatorCallback::Callback(UIView* view)
313 {
314 if (view == nullptr) {
315 return;
316 }
317
318 UIAbstractScroll* scrollView = static_cast<UIAbstractScroll*>(view);
319 scrollView->isDragging_ = true;
320 curtTime_++;
321 if (curtTime_ <= dragTimes_) {
322 bool needStopX = false;
323 bool needStopY = false;
324 if (startValueY_ != endValueY_) {
325 int16_t actY = scrollView->easingFunc_(startValueY_, endValueY_, curtTime_, dragTimes_);
326 if (!scrollView->DragYInner(actY - previousValueY_)) {
327 needStopY = true;
328 }
329 previousValueY_ = actY;
330 } else {
331 needStopY = true;
332 }
333 if (startValueX_ != endValueX_) {
334 int16_t actX = scrollView->easingFunc_(startValueX_, endValueX_, curtTime_, dragTimes_);
335 if (!scrollView->DragXInner(actX - previousValueX_)) {
336 needStopX = true;
337 }
338 previousValueX_ = actX;
339 } else {
340 needStopX = true;
341 }
342 if (needStopX && needStopY) {
343 scrollView->StopAnimator();
344 }
345 } else {
346 scrollView->StopAnimator();
347 }
348 }
349
350 #if ENABLE_ROTATE_INPUT
OnRotateStartEvent(const RotateEvent & event)351 bool UIAbstractScroll::OnRotateStartEvent(const RotateEvent& event)
352 {
353 isRotating_ = true;
354 if (scrollAnimator_.GetState() != Animator::STOP) {
355 UIAbstractScroll::StopAnimator();
356 }
357 return UIView::OnRotateStartEvent(event);
358 }
359
OnRotateEvent(const RotateEvent & event)360 bool UIAbstractScroll::OnRotateEvent(const RotateEvent& event)
361 {
362 int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
363 RefreshRotate(rotateLen);
364 if (direction_ == HORIZONTAL) {
365 DragXInner(rotateLen);
366 } else {
367 DragYInner(rotateLen);
368 }
369 return UIView::OnRotateEvent(event);
370 }
371
OnRotateEndEvent(const RotateEvent & event)372 bool UIAbstractScroll::OnRotateEndEvent(const RotateEvent& event)
373 {
374 InitDelta();
375
376 uint8_t dir;
377 int16_t lastRotateLen = GetMaxRotate();
378 if (direction_ == HORIZONTAL) {
379 dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_LEFT_TO_RIGHT : DragEvent::DIRECTION_RIGHT_TO_LEFT;
380 } else {
381 dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_TOP_TO_BOTTOM : DragEvent::DIRECTION_BOTTOM_TO_TOP;
382 }
383 bool triggerAnimator = (MATH_ABS(lastRotateLen) >= rotateThrowthreshold_);
384 if (throwDrag_ && triggerAnimator) {
385 Point current;
386 if (direction_ == HORIZONTAL) {
387 current = {lastRotateLen, 0};
388 } else {
389 current = {0, lastRotateLen};
390 }
391 DragThrowAnimator(current, {0, 0}, dir, dragBack_);
392 } else {
393 DragThrowAnimator({0, 0}, {0, 0}, dir, dragBack_);
394 }
395 isRotating_ = false;
396 InitRotate();
397 return UIView::OnRotateEndEvent(event);
398 }
399 #endif
400
SetXScrollBarVisible(bool visible)401 void UIAbstractScroll::SetXScrollBarVisible(bool visible)
402 {
403 if (Screen::GetInstance().GetScreenShape() == ScreenShape::CIRCLE) {
404 return;
405 } else if (visible && xScrollBar_ == nullptr) {
406 xScrollBar_ = new UIBoxScrollBar();
407 }
408 xScrollBarVisible_ = visible;
409 #if DEFAULT_ANIMATION
410 if (xScrollBarVisible_ && barEaseInOutAnimator_ == nullptr) {
411 barEaseInOutAnimator_ = new BarEaseInOutAnimator(*this);
412 }
413 #endif
414 }
415
SetYScrollBarVisible(bool visible)416 void UIAbstractScroll::SetYScrollBarVisible(bool visible)
417 {
418 yScrollBarVisible_ = visible;
419 if (yScrollBarVisible_ && yScrollBar_ == nullptr) {
420 if (Screen::GetInstance().GetScreenShape() == ScreenShape::CIRCLE) {
421 yScrollBar_ = new UIArcScrollBar();
422 } else {
423 yScrollBar_ = new UIBoxScrollBar();
424 }
425 }
426 #if DEFAULT_ANIMATION
427 if (yScrollBarVisible_ && barEaseInOutAnimator_ == nullptr) {
428 barEaseInOutAnimator_ = new BarEaseInOutAnimator(*this);
429 }
430 #endif
431 }
432
OnPostDraw(BufferInfo & gfxDstBuffer,const Rect & invalidatedArea)433 void UIAbstractScroll::OnPostDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
434 {
435 Rect scrollRect = GetRect();
436 uint8_t opa = GetMixOpaScale();
437 if (Screen::GetInstance().GetScreenShape() == ScreenShape::RECTANGLE) {
438 if (yScrollBarVisible_) {
439 if (scrollBarSide_ == SCROLL_BAR_RIGHT_SIDE) {
440 yScrollBar_->SetPosition(scrollRect.GetRight() - SCROLL_BAR_WIDTH + 1, scrollRect.GetTop(),
441 SCROLL_BAR_WIDTH, scrollRect.GetHeight());
442 } else {
443 yScrollBar_->SetPosition(scrollRect.GetLeft(), scrollRect.GetTop(), SCROLL_BAR_WIDTH,
444 scrollRect.GetHeight());
445 }
446 yScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
447 }
448 if (xScrollBarVisible_) {
449 if (scrollBarSide_ == SCROLL_BAR_RIGHT_SIDE) {
450 xScrollBar_->SetPosition(scrollRect.GetLeft(), scrollRect.GetBottom() - SCROLL_BAR_WIDTH + 1,
451 scrollRect.GetWidth() - SCROLL_BAR_WIDTH, SCROLL_BAR_WIDTH);
452 } else {
453 xScrollBar_->SetPosition(scrollRect.GetLeft() + SCROLL_BAR_WIDTH,
454 scrollRect.GetBottom() - SCROLL_BAR_WIDTH + 1,
455 scrollRect.GetWidth() - SCROLL_BAR_WIDTH, SCROLL_BAR_WIDTH);
456 }
457 xScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
458 }
459 } else {
460 if (yScrollBarVisible_) {
461 yScrollBar_->SetScrollBarSide(scrollBarSide_);
462 int16_t x;
463 int16_t y;
464 if (scrollBarCenterSetFlag_) {
465 x = scrollRect.GetX() + scrollBarCenter_.x;
466 y = scrollRect.GetY() + scrollBarCenter_.y;
467 } else {
468 x = scrollRect.GetX() + (GetWidth() / 2); // 2: half
469 y = scrollRect.GetY() + (GetHeight() / 2); // 2: half
470 }
471 yScrollBar_->SetPosition(x, y, SCROLL_BAR_WIDTH, GetWidth() / 2); // 2: half
472 yScrollBar_->OnDraw(gfxDstBuffer, invalidatedArea, opa);
473 }
474 }
475 UIView::OnPostDraw(gfxDstBuffer, invalidatedArea);
476 }
477
RefreshAnimator()478 void UIAbstractScroll::RefreshAnimator()
479 {
480 #if DEFAULT_ANIMATION
481 barEaseInOutAnimator_->RefreshBar();
482 #endif
483 }
484 } // namespace OHOS
485