1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

Initial version of radial wheel for army management

This commit is contained in:
Ivan Savenko 2023-07-06 01:20:44 +03:00
parent cfdc7e6e65
commit dca3785f84
9 changed files with 185 additions and 29 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

View File

@ -15,6 +15,7 @@
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h" #include "../gui/WindowHandler.h"
#include "../render/IImage.h"
#include "../windows/CCreatureWindow.h" #include "../windows/CCreatureWindow.h"
#include "../windows/GUIClasses.h" #include "../windows/GUIClasses.h"
#include "../CGameInfo.h" #include "../CGameInfo.h"
@ -29,6 +30,90 @@
#include "../../lib/TextOperations.h" #include "../../lib/TextOperations.h"
#include "../../lib/gameState/CGameState.h" #include "../../lib/gameState/CGameState.h"
RadialMenuItem::RadialMenuItem(std::string imageName, std::function<void()> callback)
: callback(callback)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
image = IImage::createFromFile("radialMenu/" + imageName);
picture = std::make_shared<CPicture>(image, Point(0,0));
pos = picture->pos;
}
bool RadialMenuItem::isInside(const Point & position)
{
Point localPosition = position - pos.topLeft();
return !image->isTransparent(localPosition);
}
void RadialMenuItem::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
{
}
void RadialMenuItem::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
{
}
RadialMenu::RadialMenu(CGarrisonInt * army, CGarrisonSlot * slot)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
bool isExchange = army->armedObjs[0] && army->armedObjs[1]; // two armies exist
addItem(ITEM_NW, "stackMerge", [=](){army->bulkMergeStacks(slot);});
addItem(ITEM_NE, "stackInfo", [=](){slot->viewInfo();});
addItem(ITEM_WW, "stackSplitOne", [=](){slot->splitIntoParts(slot->upg, 1); });
addItem(ITEM_EE, "stackSplitEqual", [=](){army->bulkSmartSplitStack(slot);});
if (isExchange)
{
addItem(ITEM_SW, "stackMove", [=](){army->moveStackToAnotherArmy(slot);});
//FIXME: addItem(ITEM_SE, "stackSplitDialog", [=](){slot->split();});
}
for(const auto & item : items)
pos = pos.include(item->pos);
}
void RadialMenu::addItem(const Point & offset, const std::string & path, std::function<void()> callback )
{
auto item = std::make_shared<RadialMenuItem>(path, callback);
item->moveBy(offset);
items.push_back(item);
}
void RadialMenu::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
{
}
void RadialMenu::show(Canvas & to)
{
showAll(to);
}
void RadialMenu::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
{
if (!on)
{
for(const auto & item : items)
{
if (item->isInside(finalPosition))
{
item->callback();
return;
}
}
}
}
void CGarrisonSlot::setHighlight(bool on) void CGarrisonSlot::setHighlight(bool on)
{ {
if (on) if (on)
@ -227,8 +312,6 @@ bool CGarrisonSlot::highlightOrDropArtifact()
bool CGarrisonSlot::split() bool CGarrisonSlot::split()
{ {
const CGarrisonSlot * selection = owner->getSelection(); const CGarrisonSlot * selection = owner->getSelection();
owner->p2 = ID; // store the second stack pos
owner->pb = upg; // store the second stack owner (up or down army)
owner->setSplittingMode(false); owner->setSplittingMode(false);
int minLeft=0, minRight=0; int minLeft=0, minRight=0;
@ -252,8 +335,12 @@ bool CGarrisonSlot::split()
int countLeft = selection->myStack ? selection->myStack->count : 0; int countLeft = selection->myStack ? selection->myStack->count : 0;
int countRight = myStack ? myStack->count : 0; int countRight = myStack ? myStack->count : 0;
GH.windows().createAndPushWindow<CSplitWindow>(selection->creature, std::bind(&CGarrisonInt::splitStacks, owner, _1, _2), auto splitFunctor = [this, selection](int amountLeft, int amountRight)
minLeft, minRight, countLeft, countRight); {
owner->splitStacks(selection, owner->armedObjs[upg], ID, amountRight);
};
GH.windows().createAndPushWindow<CSplitWindow>(selection->creature, splitFunctor, minLeft, minRight, countLeft, countRight);
return true; return true;
} }
@ -349,17 +436,50 @@ void CGarrisonSlot::clickPressed(const Point & cursorPosition)
} }
} }
void CGarrisonSlot::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
{
assert(radialMenu);
if (radialMenu)
radialMenu->gesturePanning(initialPosition, currentPosition, lastUpdateDistance);
}
void CGarrisonSlot::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
{
if (on)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
radialMenu = std::make_shared<RadialMenu>(owner, this);
radialMenu->center(pos.center());
auto topParent = parent;
while (topParent->parent)
topParent = topParent->parent;
// Add radial menu to current window / topmost parent for proper rendering order
topParent->addChild(radialMenu.get(), false);
}
else
{
radialMenu->gesture(on, initialPosition, finalPosition);
radialMenu.reset();
}
GH.windows().totalRedraw();
}
void CGarrisonSlot::update() void CGarrisonSlot::update()
{ {
if(getObj() != nullptr) if(getObj() != nullptr)
{ {
addUsedEvents(LCLICK | SHOW_POPUP | HOVER); addUsedEvents(LCLICK | SHOW_POPUP | GESTURE | HOVER);
myStack = getObj()->getStackPtr(ID); myStack = getObj()->getStackPtr(ID);
creature = myStack ? myStack->type : nullptr; creature = myStack ? myStack->type : nullptr;
} }
else else
{ {
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER); removeUsedEvents(LCLICK | SHOW_POPUP | GESTURE | HOVER);
myStack = nullptr; myStack = nullptr;
creature = nullptr; creature = nullptr;
} }
@ -434,9 +554,7 @@ void CGarrisonSlot::splitIntoParts(CGarrisonSlot::EGarrisonType type, int amount
if(empty == SlotID()) if(empty == SlotID())
return; return;
owner->pb = type; owner->splitStacks(this, owner->armedObjs[type], empty, amount);
owner->p2 = empty;
owner->splitStacks(1, amount);
} }
bool CGarrisonSlot::handleSplittingShortcuts() bool CGarrisonSlot::handleSplittingShortcuts()
@ -555,9 +673,9 @@ void CGarrisonInt::splitClick()
setSplittingMode(!getSplittingMode()); setSplittingMode(!getSplittingMode());
redraw(); redraw();
} }
void CGarrisonInt::splitStacks(int, int amountRight) void CGarrisonInt::splitStacks(const CGarrisonSlot * from, const CArmedInstance * armyDest, SlotID slotDest, int amount )
{ {
LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight); LOCPLINT->cb->splitStack(armedObjs[from->upg], armyDest, from->ID, slotDest, amount);
} }
bool CGarrisonInt::checkSelected(const CGarrisonSlot * selected, TQuantity min) const bool CGarrisonInt::checkSelected(const CGarrisonSlot * selected, TQuantity min) const
@ -669,17 +787,14 @@ void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected)
LOCPLINT->cb->bulkSmartSplitStack(armedObjs[type]->id, selected->ID); LOCPLINT->cb->bulkSmartSplitStack(armedObjs[type]->id, selected->ID);
} }
CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset, CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset, const CArmedInstance * s1, const CArmedInstance * s2, bool _removableUnits, bool smallImgs, ESlotsLayout _layout)
const CArmedInstance * s1, const CArmedInstance * s2, : highlighted(nullptr)
bool _removableUnits, bool smallImgs, ESlotsLayout _layout) , inSplittingMode(false)
: highlighted(nullptr), , interx(inx)
inSplittingMode(false), , garOffset(garsOffset)
interx(inx), , smallIcons(smallImgs)
garOffset(garsOffset), , removableUnits(_removableUnits)
pb(false), , layout(_layout)
smallIcons(smallImgs),
removableUnits(_removableUnits),
layout(_layout)
{ {
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);

View File

@ -24,10 +24,47 @@ class CButton;
class CAnimImage; class CAnimImage;
class CGarrisonSlot; class CGarrisonSlot;
class CLabel; class CLabel;
class IImage;
class RadialMenuItem : public CIntObject
{
std::shared_ptr<IImage> image;
std::shared_ptr<CPicture> picture;
public:
std::function<void()> callback;
RadialMenuItem(std::string imageName, std::function<void()> callback);
bool isInside(const Point & position);
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
};
class RadialMenu : public CIntObject
{
static constexpr Point ITEM_NW = Point( -35, -85);
static constexpr Point ITEM_NE = Point( +35, -85);
static constexpr Point ITEM_WW = Point( -85, 0);
static constexpr Point ITEM_EE = Point( +85, 0);
static constexpr Point ITEM_SW = Point( -35, +85);
static constexpr Point ITEM_SE = Point( +35, +85);
std::vector<std::shared_ptr<RadialMenuItem>> items;
void addItem(const Point & offset, const std::string & path, std::function<void()> callback );
public:
RadialMenu(CGarrisonInt * army, CGarrisonSlot * slot);
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
void show(Canvas & to) override;
};
/// A single garrison slot which holds one creature of a specific amount /// A single garrison slot which holds one creature of a specific amount
class CGarrisonSlot : public CIntObject class CGarrisonSlot : public CIntObject
{ {
public:
SlotID ID; //for identification SlotID ID; //for identification
CGarrisonInt *owner; CGarrisonInt *owner;
const CStackInstance * myStack; //nullptr if slot is empty const CStackInstance * myStack; //nullptr if slot is empty
@ -43,7 +80,9 @@ class CGarrisonSlot : public CIntObject
std::shared_ptr<CAnimImage> creatureImage; std::shared_ptr<CAnimImage> creatureImage;
std::shared_ptr<CAnimImage> selectionImage; // image for selection, not always visible std::shared_ptr<CAnimImage> selectionImage; // image for selection, not always visible
std::shared_ptr<CLabel> stackCount; std::shared_ptr<CLabel> stackCount;
std::shared_ptr<RadialMenu> radialMenu;
public:
bool viewInfo(); bool viewInfo();
bool highlightOrDropArtifact(); bool highlightOrDropArtifact();
bool split(); bool split();
@ -52,7 +91,6 @@ class CGarrisonSlot : public CIntObject
void setHighlight(bool on); void setHighlight(bool on);
std::function<void()> getDismiss() const; std::function<void()> getDismiss() const;
public:
virtual void hover (bool on) override; //call-in virtual void hover (bool on) override; //call-in
const CArmedInstance * getObj() const; const CArmedInstance * getObj() const;
bool our() const; bool our() const;
@ -60,6 +98,10 @@ public:
bool ally() const; bool ally() const;
void showPopupWindow(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override;
void clickPressed(const Point & cursorPosition) override; void clickPressed(const Point & cursorPosition) override;
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
void update(); void update();
CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * creature_ = nullptr); CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * creature_ = nullptr);
@ -92,11 +134,10 @@ public:
Point garOffset; ///< Offset between garrisons (not used if only one hero) Point garOffset; ///< Offset between garrisons (not used if only one hero)
std::vector<std::shared_ptr<CButton>> splitButtons; ///< May be empty if no buttons std::vector<std::shared_ptr<CButton>> splitButtons; ///< May be empty if no buttons
SlotID p2; ///< TODO: comment me bool smallIcons; ///< true - 32x32 imgs, false - 58x64
bool pb, bool removableUnits; ///< player Can remove units from up
smallIcons, ///< true - 32x32 imgs, false - 58x64 bool twoRows; ///< slots Will be placed in 2 rows
removableUnits, ///< player Can remove units from up bool owned[2]; ///< player Owns up or down army ([0] upper, [1] lower)
owned[2]; ///< player Owns up or down army ([0] upper, [1] lower)
ESlotsLayout layout; ESlotsLayout layout;
@ -117,7 +158,7 @@ public:
void recreateSlots(); void recreateSlots();
void splitClick(); ///< handles click on split button void splitClick(); ///< handles click on split button
void splitStacks(int amountLeft, int amountRight); ///< TODO: comment me void splitStacks(const CGarrisonSlot * from, const CArmedInstance * armyDest, SlotID slotDest, int amount); ///< TODO: comment me
void moveStackToAnotherArmy(const CGarrisonSlot * selected); void moveStackToAnotherArmy(const CGarrisonSlot * selected);
void bulkMoveArmy(const CGarrisonSlot * selected); void bulkMoveArmy(const CGarrisonSlot * selected);
void bulkMergeStacks(const CGarrisonSlot * selected); // Gather all creatures of selected type to the selected slot from other hero/garrison slots void bulkMergeStacks(const CGarrisonSlot * selected); // Gather all creatures of selected type to the selected slot from other hero/garrison slots