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_swipe_view.h"
17 #include "dock/focus_manager.h"
18 #include "dock/vibrator_manager.h"
19 #include "gfx_utils/graphic_log.h"
20 
21 namespace OHOS {
UISwipeView(uint8_t direction)22 UISwipeView::UISwipeView(uint8_t direction)
23     : swipeListener_(nullptr), curIndex_(0), blankSize_(DEFAULT_BLANK_SIZE), curView_(nullptr), loop_(false)
24 {
25 #if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
26     rotateFactor_ = DEFAULT_SWIPE_VIEW_ROTATE_FACTOR;
27 #endif
28     direction_ = direction;
29     tickTime_ = ANIMATOR_TIME;
30     swipeAccCoefficient_ = DRAG_ACC_FACTOR;
31 }
32 
~UISwipeView()33 UISwipeView::~UISwipeView() {}
34 
Add(UIView * view)35 void UISwipeView::Add(UIView* view)
36 {
37     if (view == nullptr) {
38         return;
39     }
40     view->SetDragParentInstead(true);
41     UIViewGroup::Add(view);
42     SortChild();
43     Invalidate();
44 }
45 
Insert(UIView * prevView,UIView * insertView)46 void UISwipeView::Insert(UIView* prevView, UIView* insertView)
47 {
48     if (insertView == nullptr) {
49         return;
50     }
51     insertView->SetDragParentInstead(true);
52     UIViewGroup::Insert(prevView, insertView);
53     SortChild();
54     Invalidate();
55 }
56 
Remove(UIView * view)57 void UISwipeView::Remove(UIView* view)
58 {
59     if (view == nullptr) {
60         return;
61     }
62     UIViewGroup::Remove(view);
63     if (curView_ == view) {
64         curView_ = nullptr;
65     }
66     SortChild();
67     Invalidate();
68 }
69 
MoveHeadOrTailChild()70 void UISwipeView::MoveHeadOrTailChild()
71 {
72     if (loop_ && (childrenNum_ != 1)) {
73         if (direction_ == HORIZONTAL) {
74             while (childrenHead_->GetX() >= 0) {
75                 MoveLastChildToFirst();
76             }
77             while (childrenTail_->GetX() + childrenTail_->GetWidth() <= GetWidth()) {
78                 MoveFirstChildToLast();
79             }
80         } else {
81             while (childrenHead_->GetY() >= 0) {
82                 MoveLastChildToFirst();
83             }
84             while (childrenTail_->GetY() + childrenTail_->GetHeight() <= GetHeight()) {
85                 MoveFirstChildToLast();
86             }
87         }
88     }
89 }
90 
SetCurrentPage(uint16_t index,bool needAnimator)91 void UISwipeView::SetCurrentPage(uint16_t index, bool needAnimator)
92 {
93     if (needAnimator) {
94         MoveHeadOrTailChild();
95     }
96     SwitchToPage(index, needAnimator);
97     Invalidate();
98 }
99 
DragXInner(int16_t distance)100 bool UISwipeView::DragXInner(int16_t distance)
101 {
102     if (distance == 0) {
103         return true;
104     }
105     if (!loop_) {
106         if ((distance > 0) && (childrenHead_ != nullptr)) {
107             if (childrenHead_->GetX() >= blankSize_) {
108                 distance = 0;
109             } else if (childrenHead_ && (childrenHead_->GetX() + distance > blankSize_)) {
110                 distance = blankSize_ - childrenHead_->GetX();
111             }
112         } else if (childrenTail_ != nullptr) {
113             int16_t width = GetWidth();
114             if (childrenTail_->GetRelativeRect().GetRight() < width - blankSize_) {
115                 distance = 0;
116             } else if (width - (childrenTail_->GetX() + childrenTail_->GetWidth() + distance) > blankSize_) {
117                 distance = width - blankSize_ - childrenTail_->GetX() - childrenTail_->GetWidth();
118             }
119         }
120     }
121     CalculateInvalidate();
122     MoveChildByOffset(distance, 0);
123     CalculateInvalidate();
124     return true;
125 }
126 
DragYInner(int16_t distance)127 bool UISwipeView::DragYInner(int16_t distance)
128 {
129     if (distance == 0) {
130         return true;
131     }
132     if (!loop_) {
133         if ((distance > 0) && (childrenHead_ != nullptr)) {
134             if (childrenHead_->GetY() >= blankSize_) {
135                 distance = 0;
136             } else if ((childrenHead_ != nullptr) && (childrenHead_->GetY() + distance > blankSize_)) {
137                 distance = blankSize_ - childrenHead_->GetY();
138             }
139         } else if (childrenTail_ != nullptr) {
140             int16_t height = GetHeight();
141             if (childrenTail_->GetRelativeRect().GetBottom() < height - blankSize_) {
142                 distance = 0;
143             } else if (height - (childrenTail_->GetY() + childrenTail_->GetHeight() + distance) > blankSize_) {
144                 distance = height - blankSize_ - childrenTail_->GetY() - childrenTail_->GetHeight();
145             }
146         }
147     }
148     CalculateInvalidate();
149     MoveChildByOffset(0, distance);
150     CalculateInvalidate();
151     return true;
152 }
153 
OnDragEvent(const DragEvent & event)154 bool UISwipeView::OnDragEvent(const DragEvent& event)
155 {
156     UIView* currentView = GetViewByIndex(curIndex_);
157     if (currentView == nullptr) {
158         return UIView::OnDragEvent(event);
159     }
160     if (scrollAnimator_.GetState() != Animator::STOP) {
161         StopAnimator();
162     }
163 
164     if (direction_ == HORIZONTAL) {
165         DragXInner(event.GetDeltaX());
166         RefreshDelta(event.GetDeltaX());
167         RefreshCurrentViewByPosition(&UIView::GetX, &UIView::GetWidthWithMargin);
168     } else {
169         DragYInner(event.GetDeltaY());
170         RefreshDelta(event.GetDeltaY());
171         RefreshCurrentViewByPosition(&UIView::GetY, &UIView::GetHeightWithMargin);
172     }
173     return UIView::OnDragEvent(event);
174 }
175 
OnDragEndEvent(const DragEvent & event)176 bool UISwipeView::OnDragEndEvent(const DragEvent& event)
177 {
178     int16_t distance = 0;
179     if (direction_ == HORIZONTAL) {
180         distance = event.GetCurrentPos().x - event.GetPreLastPoint().x;
181         RefreshCurrentViewByThrow(distance, event.GetDragDirection(), &UIView::GetX, &UIView::GetWidthWithMargin);
182     } else {
183         distance = event.GetCurrentPos().y - event.GetPreLastPoint().y;
184         RefreshCurrentViewByThrow(distance, event.GetDragDirection(), &UIView::GetY, &UIView::GetHeightWithMargin);
185     }
186 
187     if (curView_ == nullptr) {
188         return UIView::OnDragEndEvent(event);
189     }
190 
191     SwitchToPage(curIndex_);
192 
193     Invalidate();
194     return UIView::OnDragEndEvent(event);
195 }
196 
197 #if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
198 
OnRotateEvent(const RotateEvent & event)199 bool UISwipeView::OnRotateEvent(const RotateEvent& event)
200 {
201     int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
202     RefreshRotate(rotateLen);
203     if (direction_ == HORIZONTAL) {
204         DragXInner(rotateLen);
205         RefreshCurrentViewByPosition(&UIView::GetX, &UIView::GetWidthWithMargin);
206     } else {
207         DragYInner(rotateLen);
208         RefreshCurrentViewByPosition(&UIView::GetY, &UIView::GetHeightWithMargin);
209     }
210 
211     return UIView::OnRotateEvent(event);
212 }
213 
OnRotateEndEvent(const RotateEvent & event)214 bool UISwipeView::OnRotateEndEvent(const RotateEvent& event)
215 {
216     uint8_t dir;
217     int16_t lastRotateLen = GetMaxRotate();
218     if (direction_ == HORIZONTAL) {
219         dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_LEFT_TO_RIGHT : DragEvent::DIRECTION_RIGHT_TO_LEFT;
220     } else {
221         dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_TOP_TO_BOTTOM : DragEvent::DIRECTION_BOTTOM_TO_TOP;
222     }
223     if (direction_ == HORIZONTAL) {
224         RefreshCurrentViewByThrow(lastRotateLen, dir, &UIView::GetX, &UIView::GetWidthWithMargin);
225     } else {
226         RefreshCurrentViewByThrow(lastRotateLen, dir, &UIView::GetY, &UIView::GetHeightWithMargin);
227     }
228     if (curView_ == nullptr) {
229         return UIView::OnRotateEndEvent(event);
230     }
231     SwitchToPage(curIndex_);
232     isRotating_ = false;
233     return UIView::OnRotateEndEvent(event);
234 }
235 #endif
236 
GetViewByIndex(uint16_t index) const237 UIView* UISwipeView::GetViewByIndex(uint16_t index) const
238 {
239     UIView* child = childrenHead_;
240     while (child != nullptr) {
241         if (child->GetViewIndex() == index) {
242             return child;
243         }
244         child = child->GetNextSibling();
245     }
246     return nullptr;
247 }
248 
SetAnimatorTime(uint16_t time)249 void UISwipeView::SetAnimatorTime(uint16_t time)
250 {
251     tickTime_ = time / DEFAULT_TASK_PERIOD;
252     if (tickTime_ == 0) {
253         tickTime_ = 1;
254     }
255     animatorCallback_.SetDragTimes(tickTime_);
256 }
257 
SwitchToPage(int16_t dst,bool needAnimator)258 void UISwipeView::SwitchToPage(int16_t dst, bool needAnimator)
259 {
260     if (IsNeedLoop()) {
261         dst = (dst + childrenNum_) % childrenNum_;
262     } else if (dst < 0) {
263         dst = 0;
264     } else if (dst >= childrenNum_) {
265         dst = childrenNum_ - 1;
266     }
267 
268     UIView* dstView = GetViewByIndex(dst);
269     if (dstView == nullptr) {
270         return;
271     }
272     curIndex_ = dst;
273     curView_ = dstView;
274     int16_t xOffset = 0;
275     int16_t yOffset = 0;
276 
277     if (direction_ == HORIZONTAL) {
278         if (alignMode_ == ALIGN_LEFT) {
279             xOffset = -dstView->GetX();
280         } else if (alignMode_ == ALIGN_RIGHT) {
281             xOffset = GetWidth() - (dstView->GetX() + dstView->GetWidthWithMargin());
282         } else {
283             xOffset = (GetWidth() >> 1) - (dstView->GetX() + (dstView->GetWidthWithMargin() >> 1));
284         }
285     } else {
286         yOffset = (GetHeight() >> 1) - (dstView->GetY() + (dstView->GetHeightWithMargin() >> 1));
287     }
288 
289     if ((xOffset != 0) || (yOffset != 0)) {
290         if (scrollAnimator_.GetState() != Animator::STOP) {
291             scrollAnimator_.Stop();
292         }
293         if (needAnimator) {
294             animatorCallback_.SetDragTimes(tickTime_);
295             animatorCallback_.SetDragStartValue(0, 0);
296             animatorCallback_.SetDragEndValue(xOffset, yOffset);
297             scrollAnimator_.Start();
298         } else {
299             MoveChildByOffset(xOffset, yOffset);
300         }
301     }
302 }
303 
StopAnimator()304 void UISwipeView::StopAnimator()
305 {
306     UIAbstractScroll::StopAnimator();
307     if (swipeListener_ != nullptr) {
308         swipeListener_->OnSwipe(*this);
309     }
310 }
311 
SortChild()312 void UISwipeView::SortChild()
313 {
314     if (childrenHead_ == nullptr) {
315         return;
316     }
317     int16_t index = 0;
318     UIView* pre = childrenHead_;
319     UIView* next = childrenHead_->GetNextSibling();
320     if (direction_ == HORIZONTAL) {
321         pre->SetX(0);
322     } else {
323         pre->SetY(0);
324     }
325     pre->SetViewIndex(index);
326     index++;
327 
328     while (next != nullptr) {
329         if (direction_ == HORIZONTAL) {
330             next->SetX(pre->GetRelativeRect().GetRight() + pre->GetStyle(STYLE_MARGIN_RIGHT) + 1);
331         } else {
332             next->SetY(pre->GetRelativeRect().GetBottom() + pre->GetStyle(STYLE_MARGIN_BOTTOM) + 1);
333         }
334         pre = next;
335         next->SetViewIndex(index);
336         next = next->GetNextSibling();
337         index++;
338     }
339     bool tmpLoop = loop_;
340     loop_ = false;
341     SwitchToPage(curIndex_, false);
342     loop_ = tmpLoop;
343 }
344 
RefreshCurrentViewByPosition(int16_t (UIView::* pfnGetXOrY)()const,int16_t (UIView::* pfnGetWidthOrHeight)())345 void UISwipeView::RefreshCurrentViewByPosition(int16_t (UIView::*pfnGetXOrY)() const,
346                                                int16_t (UIView::*pfnGetWidthOrHeight)())
347 {
348     if (childrenHead_ == nullptr) {
349         curIndex_ = 0;
350         curView_ = nullptr;
351         return;
352     }
353 
354 #if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
355     uint16_t lastIndex = curIndex_;
356 #endif
357     curIndex_ = 0;
358     curView_ = nullptr;
359 
360     /*
361      * It needs to be modified that swipemid should be calculated by the width and height of the current
362      * sub view itself, not the width and height of the parent, especially for ALIGN_LEFT and ALIGN_RIGHT.
363      */
364     uint16_t swipeMid;
365     if (alignMode_ == ALIGN_LEFT) {
366         swipeMid = 0;
367     } else if (alignMode_ == ALIGN_RIGHT) {
368         swipeMid = (this->*pfnGetWidthOrHeight)();
369     } else {
370         swipeMid = (this->*pfnGetWidthOrHeight)() >> 1;
371     }
372     UIView* view = childrenHead_;
373 
374     if ((childrenHead_->*pfnGetXOrY)() > swipeMid) {
375         curIndex_ = childrenHead_->GetViewIndex();
376         curView_ = childrenHead_;
377     } else if ((childrenTail_->*pfnGetXOrY)() + (childrenHead_->*pfnGetWidthOrHeight)() < swipeMid) {
378         curIndex_ = childrenTail_->GetViewIndex();
379         curView_ = childrenTail_;
380     } else {
381         while (view != nullptr) {
382             if ((swipeMid >= (view->*pfnGetXOrY)()) &&
383                 (swipeMid <= (view->*pfnGetXOrY)() + (view->*pfnGetWidthOrHeight)())) {
384                 curIndex_ = view->GetViewIndex();
385                 curView_ = view;
386                 break;
387             }
388             view = view->GetNextSibling();
389         }
390     }
391 #if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
392     if (lastIndex != curIndex_) {
393         Vibrator();
394     }
395 #endif
396 }
397 
RefreshCurrentViewByThrow(int16_t distance,uint8_t dragDirection,int16_t (UIView::* pfnGetXOrY)()const,int16_t (UIView::* pfnGetWidthOrHeight)())398 void UISwipeView::RefreshCurrentViewByThrow(int16_t distance,
399                                             uint8_t dragDirection,
400                                             int16_t (UIView::*pfnGetXOrY)() const,
401                                             int16_t (UIView::*pfnGetWidthOrHeight)())
402 {
403     if (curView_ == nullptr) {
404         return;
405     }
406 #if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
407     uint16_t lastIndex = curIndex_;
408 #endif
409 
410     /*
411      * It needs to be modified that swipemid should be calculated by the width and height of the current
412      * sub view itself, not the width and height of the parent, especially for ALIGN_LEFT and ALIGN_RIGHT.
413      */
414     uint16_t swipeMid;
415     if (alignMode_ == ALIGN_LEFT) {
416         swipeMid = 0;
417     } else if (alignMode_ == ALIGN_RIGHT) {
418         swipeMid = (this->*pfnGetWidthOrHeight)();
419     } else {
420         swipeMid = (this->*pfnGetWidthOrHeight)() >> 1;
421     }
422 
423     int16_t accelerationOffset = GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
424     if (distance < 0) {
425         /*
426          * 7, 10 : Check whether the current view is dragged by more than 1/5,
427          * that is, the x or y coordinate plus 7/10 width or height.
428          */
429         if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) < swipeMid) &&
430             ((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() * 7 / 10) - accelerationOffset <
431              swipeMid)) {
432             CurrentIndexInc();
433         }
434     } else if (distance > 0) {
435         /*
436          * 3, 10 : Check whether the current view is dragged by more than 1/5,
437          * that is, the x or y coordinate plus 3/10 width or height.
438          */
439         if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) > swipeMid) &&
440             ((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() * 3 / 10) + accelerationOffset >
441              swipeMid)) {
442             CurrentIndexDec();
443         }
444     } else {
445         if (alignMode_ == ALIGN_LEFT) {
446             if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) < swipeMid)) {
447                 CurrentIndexInc();
448             }
449         } else if (alignMode_ == ALIGN_RIGHT) {
450             if ((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) > swipeMid) {
451                 CurrentIndexDec();
452             }
453         } else {
454             /*
455              * If the absolute value of the offset is greater than the page turning threshold,
456              * page turning is considered.
457              */
458             int16_t offset = (curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) - swipeMid;
459             int16_t threshold = (this->*pfnGetWidthOrHeight)() >> 2; // 2: 1/4 width or height
460             if (offset > threshold && (dragDirection == DragEvent::DIRECTION_TOP_TO_BOTTOM ||
461                                        dragDirection == DragEvent::DIRECTION_LEFT_TO_RIGHT)) {
462                 CurrentIndexDec();
463             } else if ((offset < -threshold) && (dragDirection == DragEvent::DIRECTION_BOTTOM_TO_TOP ||
464                                                  dragDirection == DragEvent::DIRECTION_RIGHT_TO_LEFT)) {
465                 CurrentIndexInc();
466             }
467         }
468     }
469     curView_ = GetViewByIndex(curIndex_);
470 #if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
471     if (lastIndex != curIndex_) {
472         Vibrator();
473     }
474 #endif
475 }
476 
MoveChildByOffset(int16_t xOffset,int16_t yOffset)477 void UISwipeView::MoveChildByOffset(int16_t xOffset, int16_t yOffset)
478 {
479     UIViewGroup::MoveChildByOffset(xOffset, yOffset);
480     MoveHeadOrTailChild();
481 }
482 
IsNeedLoop()483 bool UISwipeView::IsNeedLoop()
484 {
485     if (!loop_ || (childrenHead_ == nullptr) || (childrenTail_ == nullptr)) {
486         return false;
487     }
488     Rect childRect = GetAllChildRelativeRect();
489     if (direction_ == HORIZONTAL) {
490         if ((childRect.GetWidth() - childrenHead_->GetWidth() >= GetWidth()) &&
491             (childRect.GetWidth() - childrenTail_->GetWidth() >= GetWidth())) {
492             return true;
493         }
494     } else {
495         if ((childRect.GetHeight() - childrenHead_->GetHeight() >= GetHeight()) &&
496             (childRect.GetHeight() - childrenTail_->GetHeight() >= GetHeight())) {
497             return true;
498         }
499     }
500     return false;
501 }
502 
MoveFirstChildToLast()503 void UISwipeView::MoveFirstChildToLast()
504 {
505     if ((childrenTail_ == nullptr) || (childrenHead_ == nullptr)) {
506         return;
507     }
508     if (direction_ == HORIZONTAL) {
509         childrenHead_->SetX(childrenTail_->GetX() + childrenTail_->GetWidth());
510     } else {
511         childrenHead_->SetY(childrenTail_->GetY() + childrenTail_->GetHeight());
512     }
513     UIView* head = childrenHead_;
514     UIViewGroup::Remove(childrenHead_);
515     UIViewGroup::Add(head);
516 }
517 
MoveLastChildToFirst()518 void UISwipeView::MoveLastChildToFirst()
519 {
520     if ((childrenTail_ == nullptr) || (childrenHead_ == nullptr)) {
521         return;
522     }
523     if (direction_ == HORIZONTAL) {
524         childrenTail_->SetX(childrenHead_->GetX() - childrenTail_->GetWidth());
525     } else {
526         childrenTail_->SetY(childrenHead_->GetY() - childrenTail_->GetHeight());
527     }
528     UIView* last = childrenTail_;
529     UIViewGroup::Remove(childrenTail_);
530     UIViewGroup::Insert(nullptr, last);
531 }
532 
CalculateInvalidate()533 void UISwipeView::CalculateInvalidate()
534 {
535     Rect swipeRect(0, 0, GetRelativeRect().GetWidth() - 1, GetRelativeRect().GetHeight() - 1);
536     UIView* view = childrenHead_;
537     bool isFound = false;
538     while (view != nullptr) {
539         Rect rect = view->GetRelativeRect();
540         if (rect.IsIntersect(swipeRect)) {
541             if (view->IsVisible() && (view->GetOpaScale() != OPA_TRANSPARENT)) {
542                 view->Invalidate();
543             }
544             isFound = true;
545         } else if (isFound) {
546             return;
547         }
548 
549         view = view->GetNextSibling();
550     }
551 }
552 
CurrentIndexInc()553 void UISwipeView::CurrentIndexInc()
554 {
555     curIndex_++;
556     if (curIndex_ > childrenNum_ - 1) {
557         if (IsNeedLoop()) {
558             curIndex_ = curIndex_ % childrenNum_;
559         } else {
560             curIndex_ = childrenNum_ - 1;
561         }
562     }
563 }
564 
CurrentIndexDec()565 void UISwipeView::CurrentIndexDec()
566 {
567     if (curIndex_ == 0) {
568         if (IsNeedLoop()) {
569             curIndex_ = childrenNum_ - 1;
570         }
571     } else {
572         curIndex_--;
573     }
574 }
575 
576 #if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
Vibrator()577 void UISwipeView::Vibrator()
578 {
579     VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
580     if (vibratorFunc != nullptr && isRotating_) {
581         if (!loop_ && (curIndex_ == 0 || curIndex_ == childrenNum_ - 1)) {
582             GRAPHIC_LOGI("UISwipeView::Vibrator calls TYPE_THREE vibrator");
583             vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
584         } else {
585             GRAPHIC_LOGI("UISwipeView::Vibrator calls TYPE_ONE vibrator");
586             vibratorFunc(VibratorType::VIBRATOR_TYPE_ONE);
587         }
588     }
589 }
590 #endif
591 } // namespace OHOS
592