1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Integrated hotkeys with InterfaceObjectConfigurable

This commit is contained in:
Ivan Savenko 2023-04-29 13:48:21 +03:00
parent dea10e6091
commit 6c637dd8e6
6 changed files with 109 additions and 79 deletions

View File

@ -51,19 +51,23 @@ BattleWindow::BattleWindow(BattleInterface & owner):
const JsonNode config(ResourceID("config/widgets/BattleWindow.json"));
addCallback("options", std::bind(&BattleWindow::bOptionsf, this));
addCallback("surrender", std::bind(&BattleWindow::bSurrenderf, this));
addCallback("flee", std::bind(&BattleWindow::bFleef, this));
addCallback("autofight", std::bind(&BattleWindow::bAutofightf, this));
addCallback("spellbook", std::bind(&BattleWindow::bSpellf, this));
addCallback("wait", std::bind(&BattleWindow::bWaitf, this));
addCallback("defence", std::bind(&BattleWindow::bDefencef, this));
addCallback("consoleUp", std::bind(&BattleWindow::bConsoleUpf, this));
addCallback("consoleDown", std::bind(&BattleWindow::bConsoleDownf, this));
addCallback("tacticNext", std::bind(&BattleWindow::bTacticNextStack, this));
addCallback("tacticEnd", std::bind(&BattleWindow::bTacticPhaseEnd, this));
addCallback("alternativeAction", std::bind(&BattleWindow::bSwitchActionf, this));
addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this));
addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this));
addShortcut(EShortcut::BATTLE_RETREAT, std::bind(&BattleWindow::bFleef, this));
addShortcut(EShortcut::BATTLE_AUTOCOMBAT, std::bind(&BattleWindow::bAutofightf, this));
addShortcut(EShortcut::BATTLE_CAST_SPELL, std::bind(&BattleWindow::bSpellf, this));
addShortcut(EShortcut::BATTLE_WAIT, std::bind(&BattleWindow::bWaitf, this));
addShortcut(EShortcut::BATTLE_DEFEND, std::bind(&BattleWindow::bDefencef, this));
addShortcut(EShortcut::BATTLE_CONSOLE_UP, std::bind(&BattleWindow::bConsoleUpf, this));
addShortcut(EShortcut::BATTLE_CONSOLE_DOWN, std::bind(&BattleWindow::bConsoleDownf, this));
addShortcut(EShortcut::BATTLE_TACTICS_NEXT, std::bind(&BattleWindow::bTacticNextStack, this));
addShortcut(EShortcut::BATTLE_TACTICS_END, std::bind(&BattleWindow::bTacticPhaseEnd, this));
addShortcut(EShortcut::BATTLE_SELECT_ACTION, std::bind(&BattleWindow::bSwitchActionf, this));
addShortcut(EShortcut::BATTLE_TOGGLE_QUEUE, [this](){ this->toggleQueueVisibility();});
addShortcut(EShortcut::BATTLE_USE_CREATURE_SPELL, [this](){ this->owner.actionsController->enterCreatureCastingMode(); });
addShortcut(EShortcut::GLOBAL_CANCEL, [this](){ this->owner.actionsController->endCastingSpell(); });
build(config);
console = widget<BattleConsole>("console");
@ -190,19 +194,7 @@ void BattleWindow::keyPressed(EShortcut key)
owner.openingEnd();
return;
}
if(key == EShortcut::BATTLE_TOGGLE_QUEUE)
{
toggleQueueVisibility();
}
else if(key == EShortcut::BATTLE_USE_CREATURE_SPELL)
{
owner.actionsController->enterCreatureCastingMode();
}
else if(key == EShortcut::GLOBAL_CANCEL)
{
owner.actionsController->endCastingSpell();
}
InterfaceObjectConfigurable::keyPressed(key);
}
void BattleWindow::clickRight(tribool down, bool previousState)
@ -538,40 +530,18 @@ void BattleWindow::blockUI(bool on)
bool canWait = owner.stacksController->getActiveStack() ? !owner.stacksController->getActiveStack()->waitedThisTurn : false;
if(auto w = widget<CButton>("options"))
w->block(on);
if(auto w = widget<CButton>("flee"))
w->block(on || !owner.curInt->cb->battleCanFlee());
if(auto w = widget<CButton>("surrender"))
w->block(on || owner.curInt->cb->battleGetSurrenderCost() < 0);
if(auto w = widget<CButton>("cast"))
w->block(on || owner.tacticsMode || !canCastSpells);
if(auto w = widget<CButton>("wait"))
w->block(on || owner.tacticsMode || !canWait);
if(auto w = widget<CButton>("defence"))
w->block(on || owner.tacticsMode);
if(auto w = widget<CButton>("alternativeAction"))
w->block(on || owner.tacticsMode);
if(auto w = widget<CButton>("autofight"))
w->block(owner.actionsController->spellcastingModeActive());
auto btactEnd = widget<CButton>("tacticEnd");
auto btactNext = widget<CButton>("tacticNext");
if(owner.tacticsMode && btactEnd && btactNext)
{
btactNext->block(on);
btactEnd->block(on);
}
else
{
auto bConsoleUp = widget<CButton>("consoleUp");
auto bConsoleDown = widget<CButton>("consoleDown");
if(bConsoleUp && bConsoleDown)
{
bConsoleUp->block(on);
bConsoleDown->block(on);
}
}
setShortcutBlocked(EShortcut::GLOBAL_OPTIONS, on);
setShortcutBlocked(EShortcut::BATTLE_RETREAT, on || !owner.curInt->cb->battleCanFlee());
setShortcutBlocked(EShortcut::BATTLE_SURRENDER, on || owner.curInt->cb->battleGetSurrenderCost() < 0);
setShortcutBlocked(EShortcut::BATTLE_CAST_SPELL, on || owner.tacticsMode || !canCastSpells);
setShortcutBlocked(EShortcut::BATTLE_WAIT, on || owner.tacticsMode || !canWait);
setShortcutBlocked(EShortcut::BATTLE_DEFEND, on || owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_SELECT_ACTION, on || owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_AUTOCOMBAT, owner.actionsController->spellcastingModeActive());
setShortcutBlocked(EShortcut::BATTLE_TACTICS_END, on && owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on && owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode);
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode);
}
std::optional<uint32_t> BattleWindow::getQueueHoveredUnitId()

View File

@ -201,19 +201,19 @@ std::pair<std::string, std::string> InterfaceObjectConfigurable::readHintText(co
return result;
}
EShortcut InterfaceObjectConfigurable::readKeycode(const JsonNode & config) const
EShortcut InterfaceObjectConfigurable::readHotkey(const JsonNode & config) const
{
logGlobal->debug("Reading keycode");
logGlobal->debug("Reading hotkey");
if(config.getType() != JsonNode::JsonType::DATA_STRING)
{
logGlobal->error("Invalid keycode format in interface configuration! Expected string!", config.String());
logGlobal->error("Invalid hotket format in interface configuration! Expected string!", config.String());
return EShortcut::NONE;
}
EShortcut result = GH.shortcutsHandler().findShortcut(config.String());
if (result == EShortcut::NONE)
logGlobal->error("Invalid keycode '%s' in interface configuration!", config.String());
logGlobal->error("Invalid hotkey '%s' in interface configuration!", config.String());
return result;;
}
@ -320,11 +320,27 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
}
if(!config["callback"].isNull())
button->addCallback(std::bind(callbacks.at(config["callback"].String()), 0));
{
std::string callbackName = config["callback"].String();
if (callbacks.count(callbackName) > 0)
button->addCallback(std::bind(callbacks.at(callbackName), 0));
else
logGlobal->error("Invalid callback '%s' in widget", callbackName );
}
if(!config["hotkey"].isNull())
{
if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING)
button->assignedKey = readKeycode(config["hotkey"]);
{
button->assignedKey = readHotkey(config["hotkey"]);
auto target = shortcuts.find(button->assignedKey);
if (target != shortcuts.end())
{
button->addCallback(target->second.callback);
target->second.assignedToButton = true;
}
}
}
return button;
}
@ -430,3 +446,41 @@ std::shared_ptr<CIntObject> InterfaceObjectConfigurable::buildWidget(JsonNode co
logGlobal->error("Builder with type %s is not registered", type);
return nullptr;
}
void InterfaceObjectConfigurable::setShortcutBlocked(EShortcut shortcut, bool isBlocked)
{
auto target = shortcuts.find(key);
if (target == shortcuts.end())
return;
target->second.blocked = isBlocked;
for (auto & entry : widgets)
{
auto button = std::dynamic_pointer_cast<CButton>(entry.second);
if (button && button->assignedKey == shortcut)
button->block(isBlocked);
}
}
void InterfaceObjectConfigurable::addShortcut(EShortcut shortcut, std::function<void()> callback)
{
assert(shortcuts.count(shortcut) == 0);
shortcuts[shortcut].callback = callback;
}
void InterfaceObjectConfigurable::keyPressed(EShortcut key)
{
auto target = shortcuts.find(key);
if (target == shortcuts.end())
return;
if (target->second.assignedToButton)
return; // will be handled by button instance
if (target->second.blocked)
return;
target->second.callback();
}

View File

@ -35,7 +35,14 @@ public:
InterfaceObjectConfigurable(const JsonNode & config, int used=0, Point offset=Point());
protected:
/// Set blocked status for all buttons assotiated with provided shortcut
void setShortcutBlocked(EShortcut shortcut, bool isBlocked);
/// Registers provided callback to be called whenever specified shortcut is triggered
void addShortcut(EShortcut shortcut, std::function<void()> callback);
void keyPressed(EShortcut key) override;
using BuilderFunction = std::function<std::shared_ptr<CIntObject>(const JsonNode &)>;
void registerBuilder(const std::string &, BuilderFunction);
@ -64,7 +71,7 @@ protected:
EFonts readFont(const JsonNode &) const;
std::string readText(const JsonNode &) const;
std::pair<std::string, std::string> readHintText(const JsonNode &) const;
EShortcut readKeycode(const JsonNode &) const;
EShortcut readHotkey(const JsonNode &) const;
//basic widgets
std::shared_ptr<CPicture> buildPicture(const JsonNode &) const;
@ -82,9 +89,16 @@ protected:
std::shared_ptr<CIntObject> buildWidget(JsonNode config) const;
private:
struct ShortcutState
{
std::function<void()> callback;
mutable bool assignedToButton = false;
bool blocked = false;
};
int unnamedObjectId = 0;
std::map<std::string, BuilderFunction> builders;
std::map<std::string, std::shared_ptr<CIntObject>> widgets;
std::map<std::string, std::function<void(int)>> callbacks;
std::map<EShortcut, ShortcutState> shortcuts;
};

View File

@ -124,6 +124,7 @@ enum class EShortcut
BATTLE_CONSOLE_DOWN,
BATTLE_TACTICS_NEXT,
BATTLE_TACTICS_END,
BATTLE_SELECT_ACTION, // Alternative actions toggle
// Town screen
TOWN_OPEN_TAVERN,

View File

@ -126,6 +126,7 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
{SDLK_SPACE, EShortcut::BATTLE_TACTICS_NEXT },
{SDLK_RETURN, EShortcut::BATTLE_TACTICS_END },
{SDLK_KP_ENTER, EShortcut::BATTLE_TACTICS_END },
{SDLK_s, EShortcut::BATTLE_SELECT_ACTION },
{SDLK_t, EShortcut::TOWN_OPEN_TAVERN },
{SDLK_SPACE, EShortcut::TOWN_SWAP_ARMIES },
{SDLK_END, EShortcut::RECRUITMENT_MAX },
@ -255,6 +256,7 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
{"battleConsoleDown", EShortcut::BATTLE_CONSOLE_DOWN },
{"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT },
{"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END },
{"battleSelectAction", EShortcut::BATTLE_SELECT_ACTION },
{"townOpenTavern", EShortcut::TOWN_OPEN_TAVERN },
{"townSwapArmies", EShortcut::TOWN_SWAP_ARMIES },
{"recruitmentMax", EShortcut::RECRUITMENT_MAX },

View File

@ -21,7 +21,6 @@
"position": {"x": 4, "y": 560},
"image": "icm003",
"help": "core.help.381",
"callback": "options",
"hotkey": "globalOptions"
},
@ -31,7 +30,6 @@
"position": {"x": 55, "y": 560},
"image": "icm001",
"help": "core.help.379",
"callback": "surrender",
"hotkey": "battleSurrender"
},
@ -41,7 +39,6 @@
"position": {"x": 106, "y": 560},
"image": "icm002",
"help": "core.help.380",
"callback": "flee",
"hotkey": "battleRetreat"
},
@ -51,7 +48,6 @@
"position": {"x": 157, "y": 560},
"image": "icm004",
"help": "core.help.382",
"callback": "autofight",
"hotkey": "battleAutocombat"
},
@ -61,7 +57,6 @@
"position": {"x": 646, "y": 560},
"image": "icm005",
"help": "core.help.385",
"callback": "spellbook",
"hotkey": "battleCastSpell"
},
@ -71,7 +66,6 @@
"position": {"x": 697, "y": 560},
"image": "icm006",
"help": "core.help.386",
"callback": "wait",
"hotkey": "battleWait"
},
@ -81,7 +75,6 @@
"position": {"x": 748, "y": 560},
"image": "icm007",
"help": "core.help.387",
"callback": "defence",
"hotkey": "battleDefend"
},
@ -90,7 +83,6 @@
"name": "consoleUp",
"position": {"x": 625, "y": 560},
"image": "ComSlide",
"callback": "consoleUp",
"imageOrder": [0, 1, 0, 0],
"hotkey": "battleConsoleUp"
},
@ -100,7 +92,6 @@
"name": "consoleDown",
"position": {"x": 625, "y": 579},
"image": "ComSlide",
"callback": "consoleDown",
"imageOrder": [2, 3, 2, 2],
"hotkey": "battleConsoleDown"
},
@ -117,7 +108,6 @@
"name": "tacticNext",
"position": {"x": 213, "y": 560},
"image": "icm011",
"callback": "tacticNext",
"hotkey": "battleTacticsNext"
},
@ -126,7 +116,6 @@
"name": "tacticEnd",
"position": {"x": 419, "y": 560},
"image": "icm012",
"callback": "tacticEnd",
"hotkey": "battleTacticsEnd"
}
]