mirror of https://github.com/axmolengine/axmol.git
ListView - Add APIs which return the closest item in specific position in current view.
This commit is contained in:
parent
a4e5f7c0a8
commit
2599271410
|
@ -529,6 +529,118 @@ void ListView::interceptTouchEvent(TouchEventType event, Widget *sender, Touch*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Vec2 calculateItemPositionWithAnchor(Widget* item, const Vec2& itemAnchorPoint)
|
||||||
|
{
|
||||||
|
Vec2 origin(item->getLeftBoundary(), item->getBottomBoundary());
|
||||||
|
Size size = item->getContentSize();
|
||||||
|
return origin + Vec2(size.width * itemAnchorPoint.x, size.height * itemAnchorPoint.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget* findClosestItem(const Vec2& targetPosition, const Vector<Widget*>& items, const Vec2& itemAnchorPoint, ssize_t firstIndex, float distanceFromFirst, ssize_t lastIndex, float distanceFromLast)
|
||||||
|
{
|
||||||
|
CCASSERT(firstIndex >= 0 && lastIndex < items.size() && firstIndex <= lastIndex, "");
|
||||||
|
if (firstIndex == lastIndex)
|
||||||
|
{
|
||||||
|
return items.at(firstIndex);
|
||||||
|
}
|
||||||
|
if (lastIndex - firstIndex == 1)
|
||||||
|
{
|
||||||
|
if (distanceFromFirst <= distanceFromLast)
|
||||||
|
{
|
||||||
|
return items.at(firstIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return items.at(lastIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search
|
||||||
|
ssize_t midIndex = (firstIndex + lastIndex) / 2;
|
||||||
|
Vec2 itemPosition = calculateItemPositionWithAnchor(items.at(midIndex), itemAnchorPoint);
|
||||||
|
float distanceFromMid = (targetPosition - itemPosition).length();
|
||||||
|
if (distanceFromFirst <= distanceFromLast)
|
||||||
|
{
|
||||||
|
// Left half
|
||||||
|
return findClosestItem(targetPosition, items, itemAnchorPoint, firstIndex, distanceFromFirst, midIndex, distanceFromMid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Right half
|
||||||
|
return findClosestItem(targetPosition, items, itemAnchorPoint, midIndex, distanceFromMid, lastIndex, distanceFromLast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getClosestItemToPosition(const Vec2& targetPosition, const Vec2& itemAnchorPoint) const
|
||||||
|
{
|
||||||
|
if (_items.empty())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the closest item through binary search
|
||||||
|
ssize_t firstIndex = 0;
|
||||||
|
Vec2 firstPosition = calculateItemPositionWithAnchor(_items.at(firstIndex), itemAnchorPoint);
|
||||||
|
float distanceFromFirst = (targetPosition - firstPosition).length();
|
||||||
|
|
||||||
|
ssize_t lastIndex = _items.size() - 1;
|
||||||
|
Vec2 lastPosition = calculateItemPositionWithAnchor(_items.at(lastIndex), itemAnchorPoint);
|
||||||
|
float distanceFromLast = (targetPosition - lastPosition).length();
|
||||||
|
|
||||||
|
return findClosestItem(targetPosition, _items, itemAnchorPoint, firstIndex, distanceFromFirst, lastIndex, distanceFromLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getClosestItemToPositionInCurrentView(const Vec2& positionRatioInView, const Vec2& itemAnchorPoint) const
|
||||||
|
{
|
||||||
|
// Calculate the target position
|
||||||
|
Size contentSize = getContentSize();
|
||||||
|
Vec2 targetPosition = -_innerContainer->getPosition();
|
||||||
|
targetPosition.x += contentSize.width * positionRatioInView.x;
|
||||||
|
targetPosition.y += contentSize.height * positionRatioInView.y;
|
||||||
|
return getClosestItemToPosition(targetPosition, itemAnchorPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getCenterItemInCurrentView() const
|
||||||
|
{
|
||||||
|
return getClosestItemToPositionInCurrentView(Vec2::ANCHOR_MIDDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getLeftmostItemInCurrentView() const
|
||||||
|
{
|
||||||
|
if (_direction == Direction::HORIZONTAL)
|
||||||
|
{
|
||||||
|
return getClosestItemToPositionInCurrentView(Vec2::ANCHOR_MIDDLE_LEFT);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getRightmostItemInCurrentView() const
|
||||||
|
{
|
||||||
|
if (_direction == Direction::HORIZONTAL)
|
||||||
|
{
|
||||||
|
return getClosestItemToPositionInCurrentView(Vec2::ANCHOR_MIDDLE_RIGHT);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getTopmostItemInCurrentView() const
|
||||||
|
{
|
||||||
|
if (_direction == Direction::VERTICAL)
|
||||||
|
{
|
||||||
|
return getClosestItemToPositionInCurrentView(Vec2::ANCHOR_MIDDLE_TOP);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget* ListView::getBottommostItemInCurrentView() const
|
||||||
|
{
|
||||||
|
if (_direction == Direction::VERTICAL)
|
||||||
|
{
|
||||||
|
return getClosestItemToPositionInCurrentView(Vec2::ANCHOR_MIDDLE_BOTTOM);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t ListView::getCurSelectedIndex() const
|
ssize_t ListView::getCurSelectedIndex() const
|
||||||
{
|
{
|
||||||
return _curSelectedIndex;
|
return _curSelectedIndex;
|
||||||
|
|
|
@ -220,7 +220,54 @@ public:
|
||||||
virtual void removeAllChildrenWithCleanup(bool cleanup) override;
|
virtual void removeAllChildrenWithCleanup(bool cleanup) override;
|
||||||
virtual void removeChild(Node* child, bool cleaup = true) override;
|
virtual void removeChild(Node* child, bool cleaup = true) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the closest item to a specific position in inner container.
|
||||||
|
*
|
||||||
|
* @param targetPosition Specifies the target position in inner container's coordinates.
|
||||||
|
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||||
|
* @return A item instance if list view is not empty. Otherwise, returns null.
|
||||||
|
*/
|
||||||
|
Widget* getClosestItemToPosition(const Vec2& targetPosition, const Vec2& itemAnchorPoint = Vec2::ANCHOR_MIDDLE) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the closest item to a specific position in current view.
|
||||||
|
*
|
||||||
|
* @param positionRatioInView Specifies the target position with ratio in list view's content size.
|
||||||
|
* @param itemAnchorPoint Specifies an anchor point of each item for position to calculate distance.
|
||||||
|
* @return A item instance if list view is not empty. Otherwise, returns null.
|
||||||
|
*/
|
||||||
|
Widget* getClosestItemToPositionInCurrentView(const Vec2& positionRatioInView, const Vec2& itemAnchorPoint = Vec2::ANCHOR_MIDDLE) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the center item
|
||||||
|
* @return A item instance.
|
||||||
|
*/
|
||||||
|
Widget* getCenterItemInCurrentView() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the leftmost item in horizontal list
|
||||||
|
* @return A item instance.
|
||||||
|
*/
|
||||||
|
Widget* getLeftmostItemInCurrentView() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the rightmost item in horizontal list
|
||||||
|
* @return A item instance.
|
||||||
|
*/
|
||||||
|
Widget* getRightmostItemInCurrentView() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the topmost item in horizontal list
|
||||||
|
* @return A item instance.
|
||||||
|
*/
|
||||||
|
Widget* getTopmostItemInCurrentView() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the bottommost item in horizontal list
|
||||||
|
* @return A item instance.
|
||||||
|
*/
|
||||||
|
Widget* getBottommostItemInCurrentView() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Query current selected widget's idnex.
|
* @brief Query current selected widget's idnex.
|
||||||
*
|
*
|
||||||
|
|
|
@ -67,10 +67,7 @@ bool UIListViewTest_Vertical::init()
|
||||||
listView->setBackGroundImage("cocosui/green_edit.png");
|
listView->setBackGroundImage("cocosui/green_edit.png");
|
||||||
listView->setBackGroundImageScale9Enabled(true);
|
listView->setBackGroundImageScale9Enabled(true);
|
||||||
listView->setContentSize(Size(240, 130));
|
listView->setContentSize(Size(240, 130));
|
||||||
listView->setPosition(Vec2((widgetSize.width - backgroundSize.width) / 2.0f +
|
listView->setPosition(Vec2((widgetSize - listView->getContentSize()) / 2.0f));
|
||||||
(backgroundSize.width - listView->getContentSize().width) / 2.0f,
|
|
||||||
(widgetSize.height - backgroundSize.height) / 2.0f +
|
|
||||||
(backgroundSize.height - listView->getContentSize().height) / 2.0f));
|
|
||||||
listView->addEventListener((ui::ListView::ccListViewCallback)CC_CALLBACK_2(UIListViewTest_Vertical::selectedItemEvent, this));
|
listView->addEventListener((ui::ListView::ccListViewCallback)CC_CALLBACK_2(UIListViewTest_Vertical::selectedItemEvent, this));
|
||||||
listView->addEventListener((ui::ListView::ccScrollViewCallback)CC_CALLBACK_2(UIListViewTest_Vertical::selectedItemEventScrollView,this));
|
listView->addEventListener((ui::ListView::ccScrollViewCallback)CC_CALLBACK_2(UIListViewTest_Vertical::selectedItemEventScrollView,this));
|
||||||
listView->setScrollBarPositionFromCorner(Vec2(7, 7));
|
listView->setScrollBarPositionFromCorner(Vec2(7, 7));
|
||||||
|
@ -84,8 +81,7 @@ bool UIListViewTest_Vertical::init()
|
||||||
Layout* default_item = Layout::create();
|
Layout* default_item = Layout::create();
|
||||||
default_item->setTouchEnabled(true);
|
default_item->setTouchEnabled(true);
|
||||||
default_item->setContentSize(default_button->getContentSize());
|
default_item->setContentSize(default_button->getContentSize());
|
||||||
default_button->setPosition(Vec2(default_item->getContentSize().width / 2.0f,
|
default_button->setPosition(Vec2(default_item->getContentSize() / 2.0f));
|
||||||
default_item->getContentSize().height / 2.0f));
|
|
||||||
default_item->addChild(default_button);
|
default_item->addChild(default_button);
|
||||||
|
|
||||||
// set model
|
// set model
|
||||||
|
@ -166,6 +162,41 @@ bool UIListViewTest_Vertical::init()
|
||||||
// set items margin
|
// set items margin
|
||||||
listView->setItemsMargin(2.0f);
|
listView->setItemsMargin(2.0f);
|
||||||
|
|
||||||
|
// Show the indexes of items on each boundary.
|
||||||
|
{
|
||||||
|
float position = 75;
|
||||||
|
// Labels
|
||||||
|
Text* labels[3];
|
||||||
|
labels[0] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
|
||||||
|
labels[0]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
|
||||||
|
labels[0]->setPosition(_uiLayer->getContentSize() / 2 + Size(0, position));
|
||||||
|
_uiLayer->addChild(labels[0]);
|
||||||
|
labels[1] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
|
||||||
|
labels[1]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
|
||||||
|
labels[1]->setPosition(_uiLayer->getContentSize() / 2 + Size(140, 0));
|
||||||
|
_uiLayer->addChild(labels[1]);
|
||||||
|
labels[2] = Text::create(" ", "fonts/Marker Felt.ttf", 12);
|
||||||
|
labels[2]->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
|
||||||
|
labels[2]->setPosition(_uiLayer->getContentSize() / 2 + Size(0, -position));
|
||||||
|
_uiLayer->addChild(labels[2]);
|
||||||
|
|
||||||
|
// Callback
|
||||||
|
listView->ScrollView::addEventListener([labels](Ref* ref, ScrollView::EventType eventType) {
|
||||||
|
ListView* listView = dynamic_cast<ListView*>(ref);
|
||||||
|
if(listView == nullptr || eventType != ScrollView::EventType::CONTAINER_MOVED)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto bottom = listView->getBottommostItemInCurrentView();
|
||||||
|
auto center = listView->getCenterItemInCurrentView();
|
||||||
|
auto top = listView->getTopmostItemInCurrentView();
|
||||||
|
|
||||||
|
labels[0]->setString(StringUtils::format("Top index=%zd", listView->getIndex(top)));
|
||||||
|
labels[1]->setString(StringUtils::format("Center\nindex=%zd", listView->getIndex(center)));
|
||||||
|
labels[2]->setString(StringUtils::format("Bottom index=%zd", listView->getIndex(bottom)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue