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
|
||||
{
|
||||
return _curSelectedIndex;
|
||||
|
|
|
@ -220,7 +220,54 @@ public:
|
|||
virtual void removeAllChildrenWithCleanup(bool cleanup) 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.
|
||||
*
|
||||
|
|
|
@ -67,10 +67,7 @@ bool UIListViewTest_Vertical::init()
|
|||
listView->setBackGroundImage("cocosui/green_edit.png");
|
||||
listView->setBackGroundImageScale9Enabled(true);
|
||||
listView->setContentSize(Size(240, 130));
|
||||
listView->setPosition(Vec2((widgetSize.width - backgroundSize.width) / 2.0f +
|
||||
(backgroundSize.width - listView->getContentSize().width) / 2.0f,
|
||||
(widgetSize.height - backgroundSize.height) / 2.0f +
|
||||
(backgroundSize.height - listView->getContentSize().height) / 2.0f));
|
||||
listView->setPosition(Vec2((widgetSize - listView->getContentSize()) / 2.0f));
|
||||
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->setScrollBarPositionFromCorner(Vec2(7, 7));
|
||||
|
@ -84,8 +81,7 @@ bool UIListViewTest_Vertical::init()
|
|||
Layout* default_item = Layout::create();
|
||||
default_item->setTouchEnabled(true);
|
||||
default_item->setContentSize(default_button->getContentSize());
|
||||
default_button->setPosition(Vec2(default_item->getContentSize().width / 2.0f,
|
||||
default_item->getContentSize().height / 2.0f));
|
||||
default_button->setPosition(Vec2(default_item->getContentSize() / 2.0f));
|
||||
default_item->addChild(default_button);
|
||||
|
||||
// set model
|
||||
|
@ -166,6 +162,41 @@ bool UIListViewTest_Vertical::init()
|
|||
// set items margin
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue