* Copyright (c) 2020-2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "components/ui_swipe_view.h"
#include "dock/focus_manager.h"
#include "dock/vibrator_manager.h"
#include "gfx_utils/graphic_log.h"
namespace OHOS {
UISwipeView::UISwipeView(uint8_t direction)
: swipeListener_(nullptr), curIndex_(0), blankSize_(DEFAULT_BLANK_SIZE), curView_(nullptr), loop_(false)
{
#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
rotateFactor_ = DEFAULT_SWIPE_VIEW_ROTATE_FACTOR;
#endif
direction_ = direction;
tickTime_ = ANIMATOR_TIME;
swipeAccCoefficient_ = DRAG_ACC_FACTOR;
}
UISwipeView::~UISwipeView() {}
void UISwipeView::Add(UIView* view)
{
if (view == nullptr) {
return;
}
view->SetDragParentInstead(true);
UIViewGroup::Add(view);
SortChild();
Invalidate();
}
void UISwipeView::Insert(UIView* prevView, UIView* insertView)
{
if (insertView == nullptr) {
return;
}
insertView->SetDragParentInstead(true);
UIViewGroup::Insert(prevView, insertView);
SortChild();
Invalidate();
}
void UISwipeView::Remove(UIView* view)
{
if (view == nullptr) {
return;
}
UIViewGroup::Remove(view);
if (curView_ == view) {
curView_ = nullptr;
}
SortChild();
Invalidate();
}
void UISwipeView::MoveHeadOrTailChild()
{
if (loop_ && (childrenNum_ != 1)) {
if (direction_ == HORIZONTAL) {
while (childrenHead_ != nullptr && childrenHead_->GetX() >= 0) {
MoveLastChildToFirst();
}
while (childrenTail_ != nullptr && childrenTail_->GetX() + childrenTail_->GetWidth() <= GetWidth()) {
MoveFirstChildToLast();
}
} else {
while (childrenHead_ != nullptr && childrenHead_->GetY() >= 0) {
MoveLastChildToFirst();
}
while (childrenTail_ != nullptr && childrenTail_->GetY() + childrenTail_->GetHeight() <= GetHeight()) {
MoveFirstChildToLast();
}
}
}
}
void UISwipeView::SetCurrentPage(uint16_t index, bool needAnimator)
{
if (needAnimator) {
MoveHeadOrTailChild();
}
SwitchToPage(index, needAnimator);
Invalidate();
}
bool UISwipeView::DragXInner(int16_t distance)
{
if (distance == 0) {
return true;
}
if (!loop_) {
if ((distance > 0) && (childrenHead_ != nullptr)) {
if (childrenHead_->GetX() >= blankSize_) {
distance = 0;
} else if (childrenHead_ && (childrenHead_->GetX() + distance > blankSize_)) {
distance = blankSize_ - childrenHead_->GetX();
}
} else if (childrenTail_ != nullptr) {
int16_t width = GetWidth();
if (childrenTail_->GetRelativeRect().GetRight() < width - blankSize_) {
distance = 0;
} else if (width - (childrenTail_->GetX() + childrenTail_->GetWidth() + distance) > blankSize_) {
distance = width - blankSize_ - childrenTail_->GetX() - childrenTail_->GetWidth();
}
}
}
CalculateInvalidate();
MoveChildByOffset(distance, 0);
CalculateInvalidate();
return true;
}
bool UISwipeView::DragYInner(int16_t distance)
{
if (distance == 0) {
return true;
}
if (!loop_) {
if ((distance > 0) && (childrenHead_ != nullptr)) {
if (childrenHead_->GetY() >= blankSize_) {
distance = 0;
} else if ((childrenHead_ != nullptr) && (childrenHead_->GetY() + distance > blankSize_)) {
distance = blankSize_ - childrenHead_->GetY();
}
} else if (childrenTail_ != nullptr) {
int16_t height = GetHeight();
if (childrenTail_->GetRelativeRect().GetBottom() < height - blankSize_) {
distance = 0;
} else if (height - (childrenTail_->GetY() + childrenTail_->GetHeight() + distance) > blankSize_) {
distance = height - blankSize_ - childrenTail_->GetY() - childrenTail_->GetHeight();
}
}
}
CalculateInvalidate();
MoveChildByOffset(0, distance);
CalculateInvalidate();
return true;
}
bool UISwipeView::OnDragEvent(const DragEvent& event)
{
UIView* currentView = GetViewByIndex(curIndex_);
if (currentView == nullptr) {
return UIView::OnDragEvent(event);
}
if (scrollAnimator_.GetState() != Animator::STOP) {
StopAnimator();
}
if (direction_ == HORIZONTAL) {
DragXInner(event.GetDeltaX());
RefreshDelta(event.GetDeltaX());
RefreshCurrentViewByPosition(&UIView::GetX, &UIView::GetWidthWithMargin);
} else {
DragYInner(event.GetDeltaY());
RefreshDelta(event.GetDeltaY());
RefreshCurrentViewByPosition(&UIView::GetY, &UIView::GetHeightWithMargin);
}
return UIView::OnDragEvent(event);
}
bool UISwipeView::OnDragEndEvent(const DragEvent& event)
{
int16_t distance = 0;
if (direction_ == HORIZONTAL) {
distance = event.GetCurrentPos().x - event.GetPreLastPoint().x;
RefreshCurrentViewByThrow(distance, event.GetDragDirection(), &UIView::GetX, &UIView::GetWidthWithMargin);
} else {
distance = event.GetCurrentPos().y - event.GetPreLastPoint().y;
RefreshCurrentViewByThrow(distance, event.GetDragDirection(), &UIView::GetY, &UIView::GetHeightWithMargin);
}
if (curView_ == nullptr) {
return UIView::OnDragEndEvent(event);
}
SwitchToPage(curIndex_);
Invalidate();
return UIView::OnDragEndEvent(event);
}
#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
bool UISwipeView::OnRotateEvent(const RotateEvent& event)
{
int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
RefreshRotate(rotateLen);
if (direction_ == HORIZONTAL) {
DragXInner(rotateLen);
RefreshCurrentViewByPosition(&UIView::GetX, &UIView::GetWidthWithMargin);
} else {
DragYInner(rotateLen);
RefreshCurrentViewByPosition(&UIView::GetY, &UIView::GetHeightWithMargin);
}
return UIView::OnRotateEvent(event);
}
bool UISwipeView::OnRotateEndEvent(const RotateEvent& event)
{
uint8_t dir;
int16_t lastRotateLen = GetMaxRotate();
if (direction_ == HORIZONTAL) {
dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_LEFT_TO_RIGHT : DragEvent::DIRECTION_RIGHT_TO_LEFT;
} else {
dir = (lastRotateLen >= 0) ? DragEvent::DIRECTION_TOP_TO_BOTTOM : DragEvent::DIRECTION_BOTTOM_TO_TOP;
}
if (direction_ == HORIZONTAL) {
RefreshCurrentViewByThrow(lastRotateLen, dir, &UIView::GetX, &UIView::GetWidthWithMargin);
} else {
RefreshCurrentViewByThrow(lastRotateLen, dir, &UIView::GetY, &UIView::GetHeightWithMargin);
}
if (curView_ == nullptr) {
return UIView::OnRotateEndEvent(event);
}
SwitchToPage(curIndex_);
isRotating_ = false;
return UIView::OnRotateEndEvent(event);
}
#endif
UIView* UISwipeView::GetViewByIndex(uint16_t index) const
{
UIView* child = childrenHead_;
while (child != nullptr) {
if (child->GetViewIndex() == index) {
return child;
}
child = child->GetNextSibling();
}
return nullptr;
}
void UISwipeView::SetAnimatorTime(uint16_t time)
{
tickTime_ = time / DEFAULT_TASK_PERIOD;
if (tickTime_ == 0) {
tickTime_ = 1;
}
animatorCallback_.SetDragTimes(tickTime_);
}
void UISwipeView::SwitchToPage(int16_t dst, bool needAnimator)
{
if (IsNeedLoop()) {
dst = (dst + childrenNum_) % childrenNum_;
} else if (dst < 0) {
dst = 0;
} else if (dst >= childrenNum_) {
dst = childrenNum_ - 1;
}
UIView* dstView = GetViewByIndex(dst);
if (dstView == nullptr) {
return;
}
curIndex_ = dst;
curView_ = dstView;
int16_t xOffset = 0;
int16_t yOffset = 0;
if (direction_ == HORIZONTAL) {
if (alignMode_ == ALIGN_LEFT) {
xOffset = -dstView->GetX();
} else if (alignMode_ == ALIGN_RIGHT) {
xOffset = GetWidth() - (dstView->GetX() + dstView->GetWidthWithMargin());
} else {
xOffset = (GetWidth() >> 1) - (dstView->GetX() + (dstView->GetWidthWithMargin() >> 1));
}
} else {
yOffset = (GetHeight() >> 1) - (dstView->GetY() + (dstView->GetHeightWithMargin() >> 1));
}
if ((xOffset != 0) || (yOffset != 0)) {
if (scrollAnimator_.GetState() != Animator::STOP) {
scrollAnimator_.Stop();
}
if (needAnimator) {
animatorCallback_.SetDragTimes(tickTime_);
animatorCallback_.SetDragStartValue(0, 0);
animatorCallback_.SetDragEndValue(xOffset, yOffset);
scrollAnimator_.Start();
} else {
MoveChildByOffset(xOffset, yOffset);
}
}
}
void UISwipeView::StopAnimator()
{
UIAbstractScroll::StopAnimator();
if (swipeListener_ != nullptr) {
swipeListener_->OnSwipe(*this);
}
}
void UISwipeView::SortChild()
{
if (childrenHead_ == nullptr) {
return;
}
int16_t index = 0;
UIView* pre = childrenHead_;
UIView* next = childrenHead_->GetNextSibling();
if (direction_ == HORIZONTAL) {
pre->SetX(0);
} else {
pre->SetY(0);
}
pre->SetViewIndex(index);
index++;
while (next != nullptr) {
if (direction_ == HORIZONTAL) {
next->SetX(pre->GetRelativeRect().GetRight() + pre->GetStyle(STYLE_MARGIN_RIGHT) + 1);
} else {
next->SetY(pre->GetRelativeRect().GetBottom() + pre->GetStyle(STYLE_MARGIN_BOTTOM) + 1);
}
pre = next;
next->SetViewIndex(index);
next = next->GetNextSibling();
index++;
}
bool tmpLoop = loop_;
loop_ = false;
SwitchToPage(curIndex_, false);
loop_ = tmpLoop;
}
void UISwipeView::RefreshCurrentViewByPosition(int16_t (UIView::*pfnGetXOrY)() const,
int16_t (UIView::*pfnGetWidthOrHeight)())
{
if (childrenHead_ == nullptr) {
curIndex_ = 0;
curView_ = nullptr;
return;
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
uint16_t lastIndex = curIndex_;
#endif
curIndex_ = 0;
curView_ = nullptr;
* It needs to be modified that swipemid should be calculated by the width and height of the current
* sub view itself, not the width and height of the parent, especially for ALIGN_LEFT and ALIGN_RIGHT.
*/
uint16_t swipeMid;
if (alignMode_ == ALIGN_LEFT) {
swipeMid = 0;
} else if (alignMode_ == ALIGN_RIGHT) {
swipeMid = (this->*pfnGetWidthOrHeight)();
} else {
swipeMid = (this->*pfnGetWidthOrHeight)() >> 1;
}
UIView* view = childrenHead_;
if ((childrenHead_->*pfnGetXOrY)() > swipeMid) {
curIndex_ = childrenHead_->GetViewIndex();
curView_ = childrenHead_;
} else if ((childrenTail_->*pfnGetXOrY)() + (childrenHead_->*pfnGetWidthOrHeight)() < swipeMid) {
curIndex_ = childrenTail_->GetViewIndex();
curView_ = childrenTail_;
} else {
while (view != nullptr) {
if ((swipeMid >= (view->*pfnGetXOrY)()) &&
(swipeMid <= (view->*pfnGetXOrY)() + (view->*pfnGetWidthOrHeight)())) {
curIndex_ = view->GetViewIndex();
curView_ = view;
break;
}
view = view->GetNextSibling();
}
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
if (lastIndex != curIndex_) {
Vibrator();
}
#endif
}
void UISwipeView::RefreshCurrentViewByThrow(int16_t distance,
uint8_t dragDirection,
int16_t (UIView::*pfnGetXOrY)() const,
int16_t (UIView::*pfnGetWidthOrHeight)())
{
if (curView_ == nullptr) {
return;
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
uint16_t lastIndex = curIndex_;
#endif
* It needs to be modified that swipemid should be calculated by the width and height of the current
* sub view itself, not the width and height of the parent, especially for ALIGN_LEFT and ALIGN_RIGHT.
*/
uint16_t swipeMid;
if (alignMode_ == ALIGN_LEFT) {
swipeMid = 0;
} else if (alignMode_ == ALIGN_RIGHT) {
swipeMid = (this->*pfnGetWidthOrHeight)();
} else {
swipeMid = (this->*pfnGetWidthOrHeight)() >> 1;
}
int16_t accelerationOffset = GetMaxDelta() * GetSwipeACCLevel() / DRAG_ACC_FACTOR;
if (distance < 0) {
* 7, 10 : Check whether the current view is dragged by more than 1/5,
* that is, the x or y coordinate plus 7/10 width or height.
*/
if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) < swipeMid) &&
((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() * 7 / 10) - accelerationOffset <
swipeMid)) {
CurrentIndexInc();
}
} else if (distance > 0) {
* 3, 10 : Check whether the current view is dragged by more than 1/5,
* that is, the x or y coordinate plus 3/10 width or height.
*/
if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) > swipeMid) &&
((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() * 3 / 10) + accelerationOffset >
swipeMid)) {
CurrentIndexDec();
}
} else {
if (alignMode_ == ALIGN_LEFT) {
if (((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) < swipeMid)) {
CurrentIndexInc();
}
} else if (alignMode_ == ALIGN_RIGHT) {
if ((curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) > swipeMid) {
CurrentIndexDec();
}
} else {
* If the absolute value of the offset is greater than the page turning threshold,
* page turning is considered.
*/
int16_t offset = (curView_->*pfnGetXOrY)() + ((curView_->*pfnGetWidthOrHeight)() >> 1) - swipeMid;
int16_t threshold = (this->*pfnGetWidthOrHeight)() >> 2;
if (offset > threshold && (dragDirection == DragEvent::DIRECTION_TOP_TO_BOTTOM ||
dragDirection == DragEvent::DIRECTION_LEFT_TO_RIGHT)) {
CurrentIndexDec();
} else if ((offset < -threshold) && (dragDirection == DragEvent::DIRECTION_BOTTOM_TO_TOP ||
dragDirection == DragEvent::DIRECTION_RIGHT_TO_LEFT)) {
CurrentIndexInc();
}
}
}
curView_ = GetViewByIndex(curIndex_);
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
if (lastIndex != curIndex_) {
Vibrator();
}
#endif
}
void UISwipeView::MoveChildByOffset(int16_t xOffset, int16_t yOffset)
{
UIViewGroup::MoveChildByOffset(xOffset, yOffset);
MoveHeadOrTailChild();
}
bool UISwipeView::IsNeedLoop()
{
if (!loop_ || (childrenHead_ == nullptr) || (childrenTail_ == nullptr)) {
return false;
}
Rect childRect = GetAllChildRelativeRect();
if (direction_ == HORIZONTAL) {
if ((childRect.GetWidth() - childrenHead_->GetWidth() >= GetWidth()) &&
(childRect.GetWidth() - childrenTail_->GetWidth() >= GetWidth())) {
return true;
}
} else {
if ((childRect.GetHeight() - childrenHead_->GetHeight() >= GetHeight()) &&
(childRect.GetHeight() - childrenTail_->GetHeight() >= GetHeight())) {
return true;
}
}
return false;
}
void UISwipeView::MoveFirstChildToLast()
{
if ((childrenTail_ == nullptr) || (childrenHead_ == nullptr)) {
return;
}
if (direction_ == HORIZONTAL) {
childrenHead_->SetX(childrenTail_->GetX() + childrenTail_->GetWidth());
} else {
childrenHead_->SetY(childrenTail_->GetY() + childrenTail_->GetHeight());
}
UIView* head = childrenHead_;
UIViewGroup::Remove(childrenHead_);
UIViewGroup::Add(head);
}
void UISwipeView::MoveLastChildToFirst()
{
if ((childrenTail_ == nullptr) || (childrenHead_ == nullptr)) {
return;
}
if (direction_ == HORIZONTAL) {
childrenTail_->SetX(childrenHead_->GetX() - childrenTail_->GetWidth());
} else {
childrenTail_->SetY(childrenHead_->GetY() - childrenTail_->GetHeight());
}
UIView* last = childrenTail_;
UIViewGroup::Remove(childrenTail_);
UIViewGroup::Insert(nullptr, last);
}
void UISwipeView::CalculateInvalidate()
{
Rect swipeRect(0, 0, GetRelativeRect().GetWidth() - 1, GetRelativeRect().GetHeight() - 1);
UIView* view = childrenHead_;
bool isFound = false;
while (view != nullptr) {
Rect rect = view->GetRelativeRect();
if (rect.IsIntersect(swipeRect)) {
if (view->IsVisible() && (view->GetOpaScale() != OPA_TRANSPARENT)) {
view->Invalidate();
}
isFound = true;
} else if (isFound) {
return;
}
view = view->GetNextSibling();
}
}
void UISwipeView::CurrentIndexInc()
{
curIndex_++;
if (curIndex_ > childrenNum_ - 1) {
if (IsNeedLoop()) {
curIndex_ = curIndex_ % childrenNum_;
} else {
curIndex_ = childrenNum_ - 1;
}
}
}
void UISwipeView::CurrentIndexDec()
{
if (curIndex_ == 0) {
if (IsNeedLoop()) {
curIndex_ = childrenNum_ - 1;
}
} else {
curIndex_--;
}
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
void UISwipeView::Vibrator()
{
VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
if (vibratorFunc != nullptr && isRotating_) {
if (!loop_ && (curIndex_ == 0 || curIndex_ == childrenNum_ - 1)) {
GRAPHIC_LOGI("UISwipeView::Vibrator calls TYPE_THREE vibrator");
vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
} else {
GRAPHIC_LOGI("UISwipeView::Vibrator calls TYPE_ONE vibrator");
vibratorFunc(VibratorType::VIBRATOR_TYPE_ONE);
}
}
}
#endif
}