* 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_scroll_view.h"
#include "components/ui_abstract_scroll_bar.h"
#include "dock/focus_manager.h"
#include "dock/vibrator_manager.h"
#include "draw/draw_rect.h"
#include "gfx_utils/graphic_log.h"
namespace OHOS {
UIScrollView::UIScrollView() : scrollListener_(nullptr)
{
#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
rotateFactor_ = DEFAULT_SCROLL_VIEW_ROTATE_FACTOR;
rotateThrowthreshold_ = SCROLLVIEW_ROTATE_THROW_THRESHOLD;
rotateAccCoefficient_ = SCROLLVIEW_ROTATE_DISTANCE_COEFF;
#endif
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
totalRotateLen_ = 0;
lastVibratorRotateLen_ = 0;
#endif
#if defined(ENABLE_FOCUS_MANAGER) && ENABLE_FOCUS_MANAGER
focusable_ = true;
#endif
direction_ = HORIZONTAL_AND_VERTICAL;
}
bool UIScrollView::OnDragEvent(const DragEvent& event)
{
if (scrollAnimator_.GetState() != Animator::STOP) {
UIAbstractScroll::StopAnimator();
}
Drag(event);
return UIView::OnDragEvent(event);
}
bool UIScrollView::OnDragEndEvent(const DragEvent& event)
{
Point last = event.GetPreLastPoint();
Point current = event.GetLastPoint();
if ((last.x == current.x) && (last.y == current.y)) {
last = current;
current = event.GetCurrentPos();
}
if (!DragThrowAnimator(current, last, event.GetDragDirection())) {
if (scrollListener_ && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->OnScrollEnd();
scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
}
}
return UIView::OnDragEndEvent(event);
}
void UIScrollView::Drag(const DragEvent& event)
{
int16_t xDistance = event.GetDeltaX();
int16_t yDistance = event.GetDeltaY();
if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
DragXInner(xDistance);
}
if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
RefreshDelta(yDistance);
DragYInner(yDistance);
}
}
bool UIScrollView::OnPressEvent(const PressEvent& event)
{
StopAnimator();
return UIView::OnPressEvent(event);
}
#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
bool UIScrollView::OnRotateEvent(const RotateEvent& event)
{
if (direction_ == HORIZONTAL_NOR_VERTICAL) {
return UIView::OnRotateEvent(event);
}
int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
bool lastIsEdge = false;
Rect childRect = GetAllChildRelativeRect();
SetIsEdge(lastIsEdge, childRect);
#endif
RefreshRotate(rotateLen);
if (direction_ == HORIZONTAL) {
DragXInner(rotateLen);
} else {
DragYInner(rotateLen);
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
totalRotateLen_ += rotateLen;
childRect = GetAllChildRelativeRect();
bool isEdge = false;
if (direction_ == HORIZONTAL) {
if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
isEdge = true;
}
} else {
if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
isEdge = true;
}
}
VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
if (vibratorFunc != nullptr && !isEdge) {
rotateLen = MATH_ABS(totalRotateLen_ - lastVibratorRotateLen_);
if (rotateLen > DEFAULT_SCROLL_VIEW_VIBRATION_LEN) {
uint16_t vibrationCnt = rotateLen / DEFAULT_SCROLL_VIEW_VIBRATION_LEN;
for (uint16_t i = 0; i < vibrationCnt; i++) {
GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_ONE vibrator");
vibratorFunc(VibratorType::VIBRATOR_TYPE_ONE);
}
lastVibratorRotateLen_ = totalRotateLen_;
}
}
if (vibratorFunc != nullptr && (!lastIsEdge && isEdge)) {
GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_THREE vibrator");
vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
}
#endif
return UIView::OnRotateEvent(event);
}
bool UIScrollView::OnRotateEndEvent(const RotateEvent& event)
{
if (direction_ == HORIZONTAL_NOR_VERTICAL) {
return UIView::OnRotateEndEvent(event);
}
return UIAbstractScroll::OnRotateEndEvent(event);
}
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
void UIScrollView::SetIsEdge(bool& lastIsEdge, Rect childRect)
{
if (direction_ == HORIZONTAL) {
if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
lastIsEdge = true;
}
} else {
if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
lastIsEdge = true;
}
}
}
#endif
#endif
void UIScrollView::ScrollBy(int16_t xDistance, int16_t yDistance)
{
if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
DragXInner(xDistance);
}
if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
DragYInner(yDistance);
}
if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->OnScrollEnd();
scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
}
}
bool UIScrollView::DragXInner(int16_t distance)
{
Rect childRect = GetAllChildRelativeRect();
int16_t reboundSize = reboundSize_;
if (scrollAnimator_.GetState() != Animator::STOP) {
reboundSize = 0;
}
if (childRect.GetWidth() <= (GetWidth() - (scrollBlankSize_ << 1)) ||
!(direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
return false;
}
if (distance > 0) {
if (childRect.GetLeft() > scrollBlankSize_ + reboundSize) {
distance = 0;
} else if ((childRect.GetLeft() + distance) > scrollBlankSize_ + reboundSize) {
distance = scrollBlankSize_ - childRect.GetLeft() + reboundSize;
}
} else {
int16_t childRight = childRect.GetRight();
int16_t scrollWidth = GetWidth();
if (childRight < scrollWidth - (scrollBlankSize_ + reboundSize)) {
distance = 0;
} else if (childRight + distance < scrollWidth - (scrollBlankSize_ + reboundSize)) {
distance = scrollWidth - (scrollBlankSize_ + reboundSize) - childRight - 1;
}
}
return MoveOffset(distance, 0);
}
bool UIScrollView::DragYInner(int16_t distance)
{
Rect childRect = GetAllChildRelativeRect();
int16_t reboundSize = reboundSize_;
if (scrollAnimator_.GetState() != Animator::STOP) {
reboundSize = 0;
}
if (childRect.GetHeight() <= (GetHeight() - (scrollBlankSize_ << 1)) ||
!(direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
return false;
}
if (distance > 0) {
if (childRect.GetTop() > scrollBlankSize_ + reboundSize) {
distance = 0;
} else if ((childRect.GetTop() + distance) > scrollBlankSize_ + reboundSize) {
distance = scrollBlankSize_ - childRect.GetTop() + reboundSize;
}
} else {
int16_t childBottom = childRect.GetBottom();
int16_t scrollHeight = GetHeight();
if (childBottom < scrollHeight - (scrollBlankSize_ + reboundSize)) {
distance = 0;
} else if (childBottom + distance < scrollHeight - (scrollBlankSize_ + reboundSize)) {
distance = scrollHeight - (scrollBlankSize_ + reboundSize) - childBottom - 1;
}
}
return MoveOffset(0, distance);
}
bool UIScrollView::MoveOffset(int16_t offsetX, int16_t offsetY)
{
if ((offsetX != 0) || (offsetY != 0)) {
if ((scrollListener_ != nullptr) &&
(scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_STOP)) {
scrollListener_->OnScrollStart();
scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_MOVE);
}
UIAbstractScroll::MoveChildByOffset(offsetX, offsetY);
if (xScrollBarVisible_ || yScrollBarVisible_) {
RefreshScrollBar();
}
Invalidate();
return true;
}
return false;
}
void UIScrollView::RefreshScrollBar()
{
Rect childrenRect = GetAllChildRelativeRect();
int16_t totalLen = childrenRect.GetHeight() + 2 * scrollBlankSize_;
int16_t len = GetHeight();
if (yScrollBarVisible_) {
yScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
yScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetTop()) / (totalLen - len));
}
if (xScrollBarVisible_) {
totalLen = childrenRect.GetWidth() + 2 * scrollBlankSize_;
len = GetWidth();
xScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
xScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetLeft()) /
(totalLen - len));
}
RefreshAnimator();
}
void UIScrollView::CalculateReboundDistance(int16_t& dragDistanceX, int16_t& dragDistanceY)
{
Rect rect = GetAllChildRelativeRect();
int16_t top = rect.GetTop();
int16_t bottom = rect.GetBottom();
int16_t scrollHeight = GetHeight();
int16_t left = rect.GetLeft();
int16_t right = rect.GetRight();
int16_t scrollWidth = GetWidth();
if (scrollBlankSize_ < top) {
dragDistanceY = scrollBlankSize_ - top;
} else if (bottom < scrollHeight - 1) {
dragDistanceY = scrollHeight - scrollBlankSize_ - bottom - 1;
}
if (scrollBlankSize_ < left) {
dragDistanceX = scrollBlankSize_ - left;
} else if (right < scrollWidth - 1) {
dragDistanceX = scrollWidth - scrollBlankSize_ - right - 1;
}
}
void UIScrollView::StopAnimator()
{
if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->OnScrollEnd();
scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
}
UIAbstractScroll::StopAnimator();
}
}