ui: modifier chord support in the keybind registry
This commit is contained in:
parent
8642f6fe12
commit
66c42a571c
2 changed files with 66 additions and 9 deletions
|
|
@ -57,7 +57,9 @@ std::string VirtualKeyToString(VirtualKey vk);
|
||||||
* binding is visible in the settings overlay and persisted to config.
|
* binding is visible in the settings overlay and persisted to config.
|
||||||
*
|
*
|
||||||
* @param name CVAR name for this bind (e.g. "bind_console").
|
* @param name CVAR name for this bind (e.g. "bind_console").
|
||||||
* @param default_key Default key name (e.g. "Backtick", "F3").
|
* @param default_key Default key spec: a key name (e.g. "Backtick", "F3")
|
||||||
|
* or a modifier chord "Mod+...+Key" (e.g. "Ctrl+Shift+B";
|
||||||
|
* modifiers are Ctrl/Control, Shift, Alt, Super/Win).
|
||||||
* @param description Human-readable description for the settings UI.
|
* @param description Human-readable description for the settings UI.
|
||||||
* @param callback Function to invoke when the bound key is pressed.
|
* @param callback Function to invoke when the bound key is pressed.
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,9 +75,11 @@ void UnregisterBind(std::string_view name);
|
||||||
/**
|
/**
|
||||||
* Process a key-down event against all registered binds.
|
* Process a key-down event against all registered binds.
|
||||||
*
|
*
|
||||||
* Looks up each bind's current key from its CVAR, parses it, and compares
|
* Looks up each bind's current key spec from its CVAR, parses it, and
|
||||||
* against the event's virtual key. If a match is found, the bind's callback
|
* compares against the event's virtual key. Chord specs ("Ctrl+Shift+B")
|
||||||
* is invoked and the event is marked as handled.
|
* additionally require the event's modifier state to match exactly; plain
|
||||||
|
* single-key specs fire regardless of held modifiers. If a match is found,
|
||||||
|
* the bind's callback is invoked and the event is marked as handled.
|
||||||
*
|
*
|
||||||
* @param e The key event to process.
|
* @param e The key event to process.
|
||||||
* @return True if a bind matched and the event was consumed.
|
* @return True if a bind matched and the event was consumed.
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,49 @@ std::string VirtualKeyToString(VirtualKey vk) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Chord parsing ---- */
|
||||||
|
|
||||||
|
/* A key spec is either a single key name ("Home", "F3") or a chord of
|
||||||
|
modifier names joined to a final key with '+' ("Ctrl+Shift+B",
|
||||||
|
"Alt+NumpadPlus"). The last token is always the main key; every earlier
|
||||||
|
token must be a modifier name (Ctrl/Control, Shift, Alt, Super/Win). */
|
||||||
|
struct KeyChord {
|
||||||
|
VirtualKey key = VirtualKey::kNone;
|
||||||
|
bool shift = false;
|
||||||
|
bool ctrl = false;
|
||||||
|
bool alt = false;
|
||||||
|
bool super_key = false;
|
||||||
|
bool has_modifiers() const { return shift || ctrl || alt || super_key; }
|
||||||
|
};
|
||||||
|
|
||||||
|
static KeyChord ParseKeyChord(std::string_view spec) {
|
||||||
|
KeyChord chord;
|
||||||
|
size_t pos = 0;
|
||||||
|
while (true) {
|
||||||
|
const size_t plus = spec.find('+', pos);
|
||||||
|
if (plus == std::string_view::npos) {
|
||||||
|
chord.key = ParseVirtualKey(spec.substr(pos));
|
||||||
|
return chord;
|
||||||
|
}
|
||||||
|
const std::string_view token = spec.substr(pos, plus - pos);
|
||||||
|
if (token == "Ctrl" || token == "Control") {
|
||||||
|
chord.ctrl = true;
|
||||||
|
} else if (token == "Shift") {
|
||||||
|
chord.shift = true;
|
||||||
|
} else if (token == "Alt") {
|
||||||
|
chord.alt = true;
|
||||||
|
} else if (token == "Super" || token == "Win") {
|
||||||
|
chord.super_key = true;
|
||||||
|
} else {
|
||||||
|
/* Unknown modifier name: treat the whole spec as invalid rather than
|
||||||
|
silently binding the trailing key. */
|
||||||
|
chord.key = VirtualKey::kNone;
|
||||||
|
return chord;
|
||||||
|
}
|
||||||
|
pos = plus + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Bind registry ---- */
|
/* ---- Bind registry ---- */
|
||||||
|
|
||||||
struct BindEntry {
|
struct BindEntry {
|
||||||
|
|
@ -219,13 +262,23 @@ bool ProcessKeyEvent(KeyEvent& e) {
|
||||||
for (auto& entry : g_binds) {
|
for (auto& entry : g_binds) {
|
||||||
if (!entry.callback)
|
if (!entry.callback)
|
||||||
continue;
|
continue;
|
||||||
VirtualKey vk = ParseVirtualKey(entry.current_key);
|
const KeyChord chord = ParseKeyChord(entry.current_key);
|
||||||
if (vk != VirtualKey::kNone && e.virtual_key() == vk) {
|
if (chord.key == VirtualKey::kNone || e.virtual_key() != chord.key)
|
||||||
|
continue;
|
||||||
|
/* Chord binds require the exact modifier set. Plain binds keep firing
|
||||||
|
regardless of held modifiers, so a bind like "End" still works while
|
||||||
|
Shift/Ctrl are held for another purpose. */
|
||||||
|
if (chord.has_modifiers() &&
|
||||||
|
(e.is_shift_pressed() != chord.shift ||
|
||||||
|
e.is_ctrl_pressed() != chord.ctrl ||
|
||||||
|
e.is_alt_pressed() != chord.alt ||
|
||||||
|
e.is_super_pressed() != chord.super_key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
entry.callback();
|
entry.callback();
|
||||||
e.set_handled(true);
|
e.set_handled(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue