Files
graphic_ui/frameworks/components/ui_list.cpp
T
2021-03-11 18:38:06 +08:00

764 lines
23 KiB
C++
Executable File

/*
* 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_list.h"
namespace OHOS {
UIList::Recycle::~Recycle()
{
ListNode<UIView*>* node = scrapView_.Begin();
while (node != scrapView_.End()) {
if (node->data_) {
UIView* deleteView = node->data_;
if (deleteView != nullptr) {
delete deleteView;
deleteView = nullptr;
node->data_ = nullptr;
}
}
node = node->next_;
}
scrapView_.Clear();
}
void UIList::Recycle::InitRecycle()
{
if ((adapter_ == nullptr) || (listView_ == nullptr)) {
return;
}
FillActiveView();
listView_->Invalidate();
}
UIView* UIList::Recycle::GetView(int16_t index)
{
if (adapter_ == nullptr) {
return nullptr;
}
UIView* inView = nullptr;
UIView* retView = nullptr;
if (scrapView_.Size() != 0) {
inView = scrapView_.Back();
}
retView = adapter_->GetView(inView, index);
if (retView != nullptr) {
retView->SetViewIndex(index);
scrapView_.PopBack();
}
return retView;
}
void UIList::Recycle::FillActiveView()
{
if ((adapter_ == nullptr) || (listView_ == nullptr)) {
return;
}
uint16_t index = listView_->GetStartIndex();
if (listView_->GetDirection() == UIList::VERTICAL) {
int16_t childBottom = 0;
while ((index < adapter_->GetCount()) && (childBottom < listView_->GetHeight())) {
UIView* view = GetView(index);
if (view == nullptr) {
break;
}
listView_->PushBack(view);
if (listView_->childrenTail_) {
childBottom =
listView_->childrenTail_->GetY() + listView_->childrenTail_->GetRelativeRect().GetHeight();
} else {
break;
}
index++;
}
} else {
int16_t childRight = 0;
while ((index < adapter_->GetCount()) && (childRight < listView_->GetWidth())) {
UIView* view = GetView(index);
listView_->PushBack(view);
if (listView_->childrenTail_) {
childRight = listView_->childrenTail_->GetX() + listView_->childrenTail_->GetRelativeRect().GetWidth();
} else {
break;
}
index++;
}
}
}
UIList::UIList()
: onSelectedView_(nullptr),
isLoopList_(false),
isReCalculateDragEnd_(true),
autoAlign_(false),
startIndex_(0),
topIndex_(0),
bottomIndex_(0),
selectPosition_(0),
onSelectedIndex_(0),
recycle_(this),
scrollListener_(nullptr)
{
#if ENABLE_ROTATE_INPUT
rotateFactor_ = 1;
rotateThreshold_ = 4; // 4: which means 25% of half view size
#endif
#if ENABLE_MOTOR
motorType_ = MotorType::MOTOR_TYPE_ONE;
#endif
direction_ = VERTICAL;
touchable_ = true;
draggable_ = true;
dragParentInstead_ = false;
}
UIList::UIList(uint8_t direction)
: onSelectedView_(nullptr),
isLoopList_(false),
isReCalculateDragEnd_(true),
autoAlign_(false),
startIndex_(0),
topIndex_(0),
bottomIndex_(0),
selectPosition_(0),
onSelectedIndex_(0),
recycle_(this),
scrollListener_(nullptr)
{
#if ENABLE_ROTATE_INPUT
rotateFactor_ = 1;
rotateThreshold_ = 4; // 4: which means 25% of half view size
#endif
#if ENABLE_MOTOR
motorType_ = MotorType::MOTOR_TYPE_ONE;
#endif
direction_ = direction;
touchable_ = true;
draggable_ = true;
dragParentInstead_ = false;
}
UIList::~UIList()
{
UIView* view = GetChildrenHead();
while (view != nullptr) {
UIView* tmp = view->GetNextSibling();
delete view;
view = tmp;
}
}
bool UIList::OnDragEvent(const DragEvent& event)
{
if (scrollAnimator_.GetState() != Animator::STOP) {
UIAbstractScroll::StopAnimator();
}
int16_t xDistance = event.GetDeltaX();
int16_t yDistance = event.GetDeltaY();
isReCalculateDragEnd_ = true;
if (direction_ == VERTICAL) {
RefreshDeltaY(yDistance);
DragYInner(yDistance);
} else {
DragXInner(xDistance);
}
return UIView::OnDragEvent(event);
}
bool UIList::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();
}
isReCalculateDragEnd_ = false;
if (!DragThrowAnimator(current, last)) {
if (scrollListener_ && (scrollListener_->GetScrollState() == ListScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->SetScrollState(ListScrollListener::SCROLL_STATE_STOP);
scrollListener_->OnScrollEnd(onSelectedIndex_, onSelectedView_);
}
}
return UIView::OnDragEndEvent(event);
}
bool UIList::OnPressEvent(const PressEvent& event)
{
StopAnimator();
return UIView::OnPressEvent(event);
}
#if ENABLE_ROTATE_INPUT
bool UIList::OnRotateEvent(const RotateEvent& event)
{
int16_t tmpRotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
int16_t midPointX = static_cast<int16_t>(GetWidth() / 2); // 2 : Get the middle point X coord of the view
int16_t midPointY = static_cast<int16_t>(GetHeight() / 2); // 2 : Get the middle point Y coord of the view
Point last, current;
#if ENABLE_MOTOR
MotorFunc motorFunc = FocusManager::GetInstance()->GetMotorFunc();
#endif
if (!throwDrag_ || ((MATH_ABS(tmpRotateLen) < (midPointX / rotateThreshold_)) &&
(MATH_ABS(tmpRotateLen) < (midPointY / rotateThreshold_)))) {
ScrollBy(tmpRotateLen);
if (event.GetRotate() == 0) {
isReCalculateDragEnd_ = false;
DragThrowAnimator(Point{0, 0}, Point{0, 0});
}
} else {
tmpRotateLen += tmpRotateLen;
last = Point{midPointX, midPointY};
if (direction_ == VERTICAL) {
current = Point{midPointX, static_cast<int16_t>(midPointY + tmpRotateLen)};
} else {
current = Point{static_cast<int16_t>(midPointX + tmpRotateLen), midPointY};
}
isReCalculateDragEnd_ = false;
DragThrowAnimator(current, last);
#if ENABLE_MOTOR
if (motorFunc != nullptr && motorType_ == MotorType::MOTOR_TYPE_TWO) {
motorFunc(MotorType::MOTOR_TYPE_TWO);
}
#endif
}
#if ENABLE_MOTOR
if (motorFunc != nullptr && motorType_ == MotorType::MOTOR_TYPE_ONE) {
motorFunc(MotorType::MOTOR_TYPE_ONE);
}
#endif
return UIView::OnRotateEvent(event);
}
#endif
void UIList::ScrollBy(int16_t distance)
{
if (direction_ == VERTICAL) {
DragYInner(distance);
} else {
DragXInner(distance);
}
if (scrollListener_ && (scrollListener_->GetScrollState() == ListScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->SetScrollState(ListScrollListener::SCROLL_STATE_STOP);
scrollListener_->OnScrollEnd(onSelectedIndex_, onSelectedView_);
}
}
bool UIList::DragXInner(int16_t distance)
{
if (IsNeedReCalculateDragEnd()) {
return false;
}
int16_t listWidth = GetWidth();
if (distance == 0) {
return true;
}
int16_t reboundSize = reboundSize_;
if (isLoopList_ || (scrollAnimator_.GetState() != Animator::STOP)) {
reboundSize = 0;
}
bool ret = 0;
do {
ret = MoveChildStep(distance);
} while (ret);
if (isLoopList_) {
return MoveOffset(distance);
}
if (distance > 0) {
if (childrenHead_ && (childrenHead_->GetX() + distance > scrollBlankSize_ + reboundSize)) {
distance = scrollBlankSize_ + reboundSize - childrenHead_->GetX();
}
} else {
if (childrenTail_) {
if (childrenTail_->GetRelativeRect().GetRight() <= listWidth - scrollBlankSize_ - reboundSize) {
distance = 0;
} else if (listWidth - (childrenTail_->GetX() + childrenTail_->GetRelativeRect().GetWidth() + distance) >
scrollBlankSize_ + reboundSize) {
distance = listWidth - scrollBlankSize_ - reboundSize - childrenTail_->GetX() -
childrenTail_->GetRelativeRect().GetWidth();
}
}
}
return MoveOffset(distance);
}
bool UIList::DragYInner(int16_t distance)
{
if (IsNeedReCalculateDragEnd()) {
return false;
}
int16_t listHeigh = GetHeight();
if (distance == 0) {
return true;
}
int16_t reboundSize = reboundSize_;
if (isLoopList_ || (scrollAnimator_.GetState() != Animator::STOP)) {
reboundSize = 0;
}
bool ret = 0;
do {
ret = MoveChildStep(distance);
} while (ret);
if (isLoopList_) {
return MoveOffset(distance);
}
if (distance > 0) {
if (childrenHead_ && (childrenHead_->GetY() + distance > scrollBlankSize_ + reboundSize)) {
distance = scrollBlankSize_ + reboundSize - childrenHead_->GetY();
}
} else {
if (childrenTail_) {
if (childrenTail_->GetRelativeRect().GetBottom() <= listHeigh - scrollBlankSize_ - reboundSize) {
distance = 0;
} else if (listHeigh - (childrenTail_->GetY() + childrenTail_->GetRelativeRect().GetHeight() + distance) >
scrollBlankSize_ + reboundSize) {
distance = listHeigh - scrollBlankSize_ - reboundSize - childrenTail_->GetY() -
childrenTail_->GetRelativeRect().GetHeight();
}
}
}
return MoveOffset(distance);
}
bool UIList::MoveOffset(int16_t offset)
{
if (offset == 0) {
return false;
}
if (direction_ == VERTICAL) {
MoveChildByOffset(0, offset);
} else {
MoveChildByOffset(offset, 0);
}
Invalidate();
if (scrollListener_ && (scrollListener_->GetScrollState() == ListScrollListener::SCROLL_STATE_STOP)) {
scrollListener_->SetScrollState(ListScrollListener::SCROLL_STATE_MOVE);
scrollListener_->OnScrollStart(onSelectedIndex_, onSelectedView_);
}
return true;
}
bool UIList::IsNeedReCalculateDragEnd()
{
if (!autoAlign_ || isReCalculateDragEnd_ || (onSelectedView_ == nullptr)) {
return false;
}
int16_t animationLess = 0;
if (direction_ == VERTICAL) {
animationLess = animatorCallback_.endValueY_ - animatorCallback_.previousValueY_;
} else {
animationLess = animatorCallback_.endValueX_ - animatorCallback_.previousValueX_;
}
if (!isDragging_ || (MATH_ABS(animationLess) > RECALCULATE_DRAG_DISTANCE)) {
return false;
}
return true;
}
bool UIList::ReCalculateDragEnd()
{
if ((onSelectedView_ == nullptr) || isReCalculateDragEnd_ || !autoAlign_) {
return false;
}
int16_t offsetX = 0;
int16_t offsetY = 0;
if (direction_ == VERTICAL) {
// 2: half
offsetY = selectPosition_ - (onSelectedView_->GetY() + (onSelectedView_->GetRelativeRect().GetHeight() / 2));
} else {
// 2: half
offsetX = selectPosition_ - (onSelectedView_->GetX() + (onSelectedView_->GetRelativeRect().GetWidth() / 2));
}
animatorCallback_.SetDragStartValue(0, 0);
animatorCallback_.SetDragEndValue(offsetX, offsetY);
animatorCallback_.SetDragTimes(RECALCULATE_DRAG_TIMES * DRAG_ACC_FACTOR / GetDragACCLevel());
scrollAnimator_.Start();
isReCalculateDragEnd_ = true;
return true;
}
bool UIList::MoveChildStepInner(int16_t distance,
int16_t (UIView::*pfnGetXOrY)() const,
int16_t (Rect::*pfnGetWidthOrHeight)() const)
{
bool popRet = false;
bool pushRet = false;
if (distance > 0) {
if ((childrenHead_ == nullptr) || ((childrenHead_->*pfnGetXOrY)() + distance > 0)) {
uint16_t index = GetIndexDec(topIndex_);
if (index == topIndex_) {
return false;
}
UIView* newView = recycle_.GetView(index);
if (newView == nullptr) {
return false;
}
PushFront(newView);
pushRet = true;
}
if (childrenTail_ != nullptr &&
((childrenTail_->*pfnGetXOrY)() + distance > (this->GetRelativeRect().*pfnGetWidthOrHeight)())) {
PopItem(childrenTail_);
popRet = true;
}
} else {
if ((childrenTail_ == nullptr) ||
((childrenTail_->*pfnGetXOrY)() + (childrenTail_->GetRelativeRect().*pfnGetWidthOrHeight)() + distance <
(this->GetRelativeRect().*pfnGetWidthOrHeight)())) {
UIView* newView = recycle_.GetView(GetIndexInc(bottomIndex_));
if (newView == nullptr) {
return false;
}
PushBack(newView);
pushRet = true;
}
if (childrenHead_ &&
(childrenHead_->*pfnGetXOrY)() + distance + (childrenHead_->GetRelativeRect().*pfnGetWidthOrHeight)() < 0) {
PopItem(childrenHead_);
popRet = true;
}
}
return (popRet || pushRet);
}
bool UIList::MoveChildStep(int16_t distance)
{
if (direction_ == VERTICAL) {
return MoveChildStepInner(distance, &UIView::GetY, &Rect::GetHeight);
} else {
return MoveChildStepInner(distance, &UIView::GetX, &Rect::GetWidth);
}
}
void UIList::SetAdapter(AbstractAdapter* adapter)
{
recycle_.SetAdapter(adapter);
recycle_.InitRecycle();
}
UIView* UIList::GetSelectView()
{
if (onSelectedView_ != nullptr) {
return onSelectedView_;
}
if ((childrenHead_ == nullptr) || (selectPosition_ == 0)) {
return nullptr;
}
UIView* child = childrenHead_;
while (child != nullptr) {
if (direction_ == VERTICAL) {
if ((child->GetY() <= selectPosition_) &&
(child->GetY() + child->GetRelativeRect().GetHeight() >= selectPosition_)) {
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(child->GetViewIndex(), child);
}
return child;
}
} else {
if ((child->GetX() <= selectPosition_) &&
(child->GetX() + child->GetRelativeRect().GetWidth() >= selectPosition_)) {
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(child->GetViewIndex(), child);
}
return child;
}
}
child = child->GetNextSibling();
}
return nullptr;
}
void UIList::PushBack(UIView* view)
{
if (view == nullptr) {
return;
}
if (childrenTail_ == nullptr) {
SetHead(view);
} else {
if (direction_ == VERTICAL) {
view->SetPosition(0, childrenTail_->GetY() + childrenTail_->GetRelativeRect().GetHeight());
} else {
view->SetPosition(childrenTail_->GetX() + childrenTail_->GetRelativeRect().GetWidth(), 0);
}
bottomIndex_ = GetIndexInc(bottomIndex_);
}
view->SetDragParentInstead(true);
UIViewGroup::Add(view);
}
void UIList::PushFront(UIView* view)
{
if (view == nullptr) {
return;
}
if (GetChildrenHead() == nullptr) {
SetHead(view);
} else {
if (direction_ == VERTICAL) {
view->SetPosition(0, GetChildrenHead()->GetY() - view->GetRelativeRect().GetHeight());
} else {
view->SetPosition(GetChildrenHead()->GetX() - view->GetRelativeRect().GetWidth(), 0);
}
topIndex_ = GetIndexDec(topIndex_);
}
view->SetDragParentInstead(true);
UIViewGroup::Insert(nullptr, view);
}
void UIList::PopItem(UIView* view)
{
if (view == nullptr) {
return;
}
recycle_.AddScrapView(view);
if (view == GetChildrenHead()) {
topIndex_ = GetIndexInc(topIndex_);
}
if (view == childrenTail_) {
bottomIndex_ = GetIndexDec(bottomIndex_);
}
UIViewGroup::Remove(view);
}
void UIList::SetHead(UIView* view)
{
if (view != nullptr) {
view->SetPosition(0, 0);
topIndex_ = startIndex_;
bottomIndex_ = startIndex_;
}
}
void UIList::MoveChildByOffset(int16_t xOffset, int16_t yOffset)
{
UIView* view = GetChildrenHead();
if (view == nullptr) {
return;
}
int16_t x;
int16_t y;
int16_t height;
int16_t width;
if ((onSelectedIndex_ != NULL_SELECT_INDEX) && (selectPosition_ != 0)) {
if (direction_ == VERTICAL) {
height = view->GetRelativeRect().GetHeight();
if ((GetChildrenHead()->GetY() + yOffset > selectPosition_) ||
(childrenTail_->GetY() + height + yOffset < selectPosition_)) {
onSelectedIndex_ = NULL_SELECT_INDEX;
onSelectedView_ = nullptr;
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(onSelectedIndex_, onSelectedView_);
}
}
} else {
width = view->GetRelativeRect().GetWidth();
if ((GetChildrenHead()->GetX() + xOffset > selectPosition_) ||
(childrenTail_->GetX() + width < selectPosition_)) {
onSelectedIndex_ = NULL_SELECT_INDEX;
onSelectedView_ = nullptr;
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(onSelectedIndex_, onSelectedView_);
}
}
}
}
bool isSelectViewFind = false;
while (view != nullptr) {
x = view->GetX() + xOffset;
y = view->GetY() + yOffset;
view->SetPosition(x, y);
if ((selectPosition_ != 0) && !isSelectViewFind) {
if (direction_ == VERTICAL) {
height = view->GetRelativeRect().GetHeight();
/* Views may be the same but have different indexes because of view recycling. */
if ((y <= selectPosition_) && (y + height >= selectPosition_) &&
((onSelectedView_ != view) || (onSelectedIndex_ != view->GetViewIndex()))) {
onSelectedIndex_ = view->GetViewIndex();
onSelectedView_ = view;
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(onSelectedIndex_, onSelectedView_);
}
isSelectViewFind = true;
}
} else {
width = view->GetRelativeRect().GetWidth();
if ((x <= selectPosition_) && (x + width >= selectPosition_) &&
((onSelectedView_ != view) || (onSelectedIndex_ != view->GetViewIndex()))) {
onSelectedIndex_ = view->GetViewIndex();
onSelectedView_ = view;
if (scrollListener_ != nullptr) {
scrollListener_->OnItemSelected(onSelectedIndex_, onSelectedView_);
}
isSelectViewFind = true;
}
}
}
view = view->GetNextSibling();
}
}
void UIList::StopAnimator()
{
UIAbstractScroll::StopAnimator();
if (!ReCalculateDragEnd()) {
if ((scrollListener_ != nullptr) &&
(scrollListener_->GetScrollState() == ListScrollListener::SCROLL_STATE_MOVE)) {
scrollListener_->SetScrollState(ListScrollListener::SCROLL_STATE_STOP);
scrollListener_->OnScrollEnd(onSelectedIndex_, onSelectedView_);
}
}
}
uint16_t UIList::GetIndexInc(uint16_t index)
{
uint16_t ret = index + 1;
if (isLoopList_ && (recycle_.GetAdapterItemCount() != 0)) {
ret = ret % recycle_.GetAdapterItemCount();
}
return ret;
}
uint16_t UIList::GetIndexDec(uint16_t index)
{
if (index == 0) {
if (isLoopList_) {
return recycle_.GetAdapterItemCount() - 1;
} else {
return 0;
}
} else {
return index - 1;
}
}
void UIList::ScrollTo(uint16_t index)
{
UIView* child = GetChildrenHead();
UIView* tmp = nullptr;
while (child != nullptr) {
tmp = child;
child = child->GetNextSibling();
PopItem(tmp);
}
onSelectedView_ = nullptr;
SetStartIndex(index);
recycle_.InitRecycle();
}
void UIList::RefreshList()
{
int16_t topIndex = topIndex_;
UIView* child = GetChildrenHead();
UIView* tmp = nullptr;
int16_t offset = 0;
if (child != nullptr) {
if (direction_ == VERTICAL) {
offset = child->GetY();
} else {
offset = child->GetX();
}
}
while (child != nullptr) {
tmp = child;
child = child->GetNextSibling();
PopItem(tmp);
}
onSelectedView_ = nullptr;
uint16_t tmpStartIndex = startIndex_;
if (topIndex > recycle_.GetAdapterItemCount() - 1) {
startIndex_ = 0;
offset = 0;
} else {
startIndex_ = topIndex;
}
recycle_.InitRecycle();
startIndex_ = tmpStartIndex;
if (direction_ == VERTICAL) {
DragYInner(offset);
} else {
DragXInner(offset);
}
Invalidate();
}
void UIList::RemoveAll()
{
UIViewGroup::RemoveAll();
recycle_.ClearScrapView();
}
void UIList::CalculateReboundDistance(int16_t& dragDistanceX, int16_t& dragDistanceY)
{
if (isLoopList_) {
return;
}
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 ((direction_ == VERTICAL) || (direction_ == HORIZONTAL_AND_VERTICAL)) {
if (top > scrollBlankSize_) {
if ((dragDistanceY + top) > (scrollBlankSize_ + reboundSize_)) {
dragDistanceY = 0;
}
dragDistanceY += scrollBlankSize_ - (top + dragDistanceY);
}
if (bottom < (scrollHeight - scrollBlankSize_ - 1)) {
if ((dragDistanceY + bottom) < (scrollHeight - scrollBlankSize_ - reboundSize_ - 1)) {
dragDistanceY = 0;
}
dragDistanceY += scrollHeight - scrollBlankSize_ - 1 - (bottom + dragDistanceY);
}
} else {
if (left > scrollBlankSize_) {
if ((dragDistanceX + left) > (scrollBlankSize_ + reboundSize_)) {
dragDistanceX = 0;
}
dragDistanceX += scrollBlankSize_ - (left + dragDistanceX);
}
if (right < (scrollWidth - scrollBlankSize_ - 1)) {
if ((dragDistanceX + right) < (scrollWidth - scrollBlankSize_ - reboundSize_ - 1)) {
dragDistanceX = 0;
}
dragDistanceX += scrollWidth - scrollBlankSize_ - 1 - (right + dragDistanceX);
}
}
}
#if ENABLE_MOTOR
void UIList::SetMotorType(MotorType motorType)
{
motorType_ = motorType;
}
#endif
} // namespace OHOS