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