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