1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-16 10:19:47 +02:00
vcmi/client/widgets/RadialMenu.cpp

146 lines
3.6 KiB
C++
Raw Normal View History

2023-07-07 00:08:29 +02:00
/*
* RadialMenu.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "RadialMenu.h"
#include "Images.h"
#include "TextControls.h"
2023-07-07 00:08:29 +02:00
2023-07-23 15:17:30 +02:00
#include "../eventsSDL/InputHandler.h"
2023-07-07 00:08:29 +02:00
#include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h"
2023-07-07 00:08:29 +02:00
#include "../render/IImage.h"
2023-07-21 14:46:49 +02:00
#include "../CGameInfo.h"
#include "../../lib/CGeneralTextHandler.h"
2023-07-07 00:08:29 +02:00
RadialMenuItem::RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback)
2023-07-07 00:08:29 +02:00
: callback(callback)
, hoverText(hoverText)
2023-07-07 00:08:29 +02:00
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
inactiveImage = std::make_shared<CPicture>("radialMenu/itemInactive", Point(0, 0));
selectedImage = std::make_shared<CPicture>("radialMenu/itemEmpty", Point(0, 0));
iconImage = std::make_shared<CPicture>("radialMenu/" + imageName, Point(0, 0));
pos = selectedImage->pos;
selectedImage->setEnabled(false);
}
void RadialMenuItem::setSelected(bool selected)
{
selectedImage->setEnabled(selected);
inactiveImage->setEnabled(!selected);
2023-07-07 00:08:29 +02:00
}
RadialMenu::RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig):
centerPosition(positionToCenter)
2023-07-07 00:08:29 +02:00
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos += positionToCenter;
2023-07-07 00:08:29 +02:00
Point itemSize = Point(70, 80);
moveBy(-itemSize / 2);
pos.w = itemSize.x;
pos.h = itemSize.y;
for (auto const & item : menuConfig)
addItem(item.itemPosition, item.enabled, item.imageName, item.hoverText, item.callback);
2023-07-07 00:08:29 +02:00
statusBar = CGStatusBar::create(-80, -100, "radialMenu/statusBar");
2023-07-07 00:08:29 +02:00
for(const auto & item : items)
pos = pos.include(item->pos);
fitToScreen(10);
addUsedEvents(GESTURE);
2023-07-07 00:08:29 +02:00
}
void RadialMenu::addItem(const Point & offset, bool enabled, const std::string & path, const std::string & hoverText, const std::function<void()>& callback )
2023-07-07 00:08:29 +02:00
{
if (!enabled)
return;
2023-07-21 14:46:49 +02:00
auto item = std::make_shared<RadialMenuItem>(path, CGI->generaltexth->translate(hoverText), callback);
2023-07-07 00:08:29 +02:00
item->moveBy(offset);
items.push_back(item);
}
std::shared_ptr<RadialMenuItem> RadialMenu::findNearestItem(const Point & cursorPosition) const
2023-07-07 00:08:29 +02:00
{
static const int requiredDistanceFromCenter = 45;
// cursor is inside centeral area -> no selection
if ((centerPosition - cursorPosition).length() < requiredDistanceFromCenter)
return nullptr;
int bestDistance = std::numeric_limits<int>::max();
std::shared_ptr<RadialMenuItem> bestItem;
2023-07-07 00:08:29 +02:00
2023-07-19 16:08:15 +02:00
for(const auto & item : items)
{
Point vector = item->pos.center() - cursorPosition;
if (vector.length() < bestDistance)
2023-07-19 16:08:15 +02:00
{
bestDistance = vector.length();
bestItem = item;
2023-07-19 16:08:15 +02:00
}
}
assert(bestItem);
return bestItem;
}
void RadialMenu::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
{
auto newSelection = findNearestItem(currentPosition);
if (newSelection != selectedItem)
{
if (selectedItem)
selectedItem->setSelected(false);
if (newSelection)
{
GH.statusbar()->write(newSelection->hoverText);
newSelection->setSelected(true);
}
else
GH.statusbar()->clear();
selectedItem = newSelection;
GH.windows().totalRedraw();
}
2023-07-07 00:08:29 +02:00
}
void RadialMenu::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
{
if (on)
return;
auto item = findNearestItem(finalPosition);
// we need to close this window first so if action spawns a new window it won't be closed instead
GH.windows().popWindows(1);
if (item)
2023-07-23 15:17:30 +02:00
{
GH.input().hapticFeedback();
item->callback();
2023-07-23 15:17:30 +02:00
}
2023-07-07 00:08:29 +02:00
}