Initial version of radial wheel for army management
BIN
Mods/vcmi/Data/radialMenu/stackInfo.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
Mods/vcmi/Data/radialMenu/stackMerge.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
Mods/vcmi/Data/radialMenu/stackMove.png
Normal file
After Width: | Height: | Size: 824 B |
BIN
Mods/vcmi/Data/radialMenu/stackSplitDialog.png
Normal file
After Width: | Height: | Size: 831 B |
BIN
Mods/vcmi/Data/radialMenu/stackSplitEqual.png
Normal file
After Width: | Height: | Size: 962 B |
BIN
Mods/vcmi/Data/radialMenu/stackSplitOne.png
Normal file
After Width: | Height: | Size: 942 B |
BIN
Mods/vcmi/Data/radialMenu/stackSwap.png
Normal file
After Width: | Height: | Size: 832 B |
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|