From 363c9e48e568ef8512a34366f9a72a5693577f60 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Wed, 27 Nov 2024 05:15:34 -0500 Subject: [PATCH] Add Widget::KeyBaseEvent::isKeyCommand() for checking a key command correctly on all keyboard layouts. Make getKeyName() directly convert GLFW key to name, ignoring current keyboard layout. --- include/widget/Widget.hpp | 6 ++++++ include/widget/event.hpp | 6 +++--- src/widget/event.cpp | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/widget/Widget.hpp b/include/widget/Widget.hpp index c869a10f..c1ddbcc7 100644 --- a/include/widget/Widget.hpp +++ b/include/widget/Widget.hpp @@ -286,6 +286,12 @@ struct Widget : WeakBase { See GLFW_MOD_* for the list of possible values. */ int mods; + /** Checks whether this KeyBaseEvent is the given key command. `mods` is OR'd GLFW modifier keys. + On Latin keyboards such as QWERTY, AZERTY, QWERTZ, and Dvorak, the key name is checked regardless of position. For example, pressing Q with key=GLFW_KEY_Q returns true for all of these layouts. + On non-Latin keyboards such as JCUKEN (ЙЦУКЕН), the layout is assumed to match QWERTY. For example, pressing Й with key=GLFW_KEY_Q returns true. + Implemented in event.cpp. + */ + bool isKeyCommand(int key, int mods = 0) const; }; /** Occurs when a key is pressed, released, or repeated while the mouse is hovering a Widget. diff --git a/include/widget/event.hpp b/include/widget/event.hpp index b2e747c1..18815af4 100644 --- a/include/widget/event.hpp +++ b/include/widget/event.hpp @@ -40,11 +40,11 @@ namespace rack { namespace widget { -std::string getKeyName(int key); -std::string getKeyCommandName(int key, int mods); +struct Widget; -struct Widget; +std::string getKeyName(int key); +std::string getKeyCommandName(int key, int mods = 0); /** A per-event state shared and writable by all widgets that recursively handle an event. */ diff --git a/src/widget/event.cpp b/src/widget/event.cpp index 9028b3ed..08914cd1 100644 --- a/src/widget/event.cpp +++ b/src/widget/event.cpp @@ -15,12 +15,9 @@ std::string getKeyName(int key) { case GLFW_KEY_SPACE: return "Space"; } - const char* keyNameC = glfwGetKeyName(key, GLFW_KEY_UNKNOWN); - if (keyNameC) { - std::string keyName = keyNameC; - if (keyName.size() == 1) - keyName[0] = std::toupper((unsigned char) keyName[0]); - return keyName; + // Printable characters + if (key < 128) { + return std::string(1, (char) key); } // Unprintable keys with names @@ -64,6 +61,29 @@ std::string getKeyCommandName(int key, int mods) { } +bool Widget::KeyBaseEvent::isKeyCommand(int key, int mods) const { + // Reject if mods don't match + if ((this->mods & RACK_MOD_MASK) != mods) + return false; + // Reject invalid keys + if (this->key < 32) + return false; + // Are both keys printable? + if (this->key < 128 && key < 128) { + // Is the event a printable ASCII character? + if (this->keyName.size() == 1) { + // Uppercase event key name + char k = this->keyName[0]; + if (k >= 'a' && k <= 'z') + k += 'A' - 'a'; + return k == key; + } + } + // Check equal QWERTY keys, printable or not + return this->key == key; +} + + void EventState::setHoveredWidget(widget::Widget* w) { if (w == hoveredWidget) return; @@ -364,7 +384,7 @@ bool EventState::handleKey(math::Vec pos, int key, int scancode, int action, int eSelectKey.context = &cSelectKey; eSelectKey.key = key; eSelectKey.scancode = scancode; - const char* keyName = glfwGetKeyName(key, scancode); + const char* keyName = glfwGetKeyName(key, GLFW_KEY_UNKNOWN); if (keyName) eSelectKey.keyName = keyName; eSelectKey.action = action; @@ -381,7 +401,7 @@ bool EventState::handleKey(math::Vec pos, int key, int scancode, int action, int eHoverKey.pos = pos; eHoverKey.key = key; eHoverKey.scancode = scancode; - const char* keyName = glfwGetKeyName(key, scancode); + const char* keyName = glfwGetKeyName(key, GLFW_KEY_UNKNOWN); if (keyName) eHoverKey.keyName = keyName; eHoverKey.action = action;