From 1e348218975099e9e4988705b18dd02ff2c7287a Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 9 Jan 2025 11:41:19 -0300 Subject: [PATCH] Add support to use mouse buttons as shortcuts (fix #598) --- src/app/modules/gui.cpp | 18 ++++++++++ src/app/ui/key.cpp | 7 ++-- src/app/ui/select_shortcut.cpp | 44 ++++++++++++++++------- src/ui/manager.cpp | 65 +++++++++++++++++++--------------- src/ui/manager.h | 21 +++++------ src/ui/shortcut.cpp | 42 +++++++++++++++++----- src/ui/shortcut.h | 16 ++++++--- 7 files changed, 148 insertions(+), 65 deletions(-) diff --git a/src/app/modules/gui.cpp b/src/app/modules/gui.cpp index b089eab4a..e1b1ad5b5 100644 --- a/src/app/modules/gui.cpp +++ b/src/app/modules/gui.cpp @@ -94,6 +94,7 @@ protected: void onInitTheme(InitThemeEvent& ev) override; LayoutIO* onGetLayoutIO() override { return this; } void onNewDisplayConfiguration(Display* display) override; + bool onEnqueueMouseDown(MouseMessage* mouseMsg) override; // LayoutIO implementation std::string loadLayout(Widget* widget) override; @@ -678,6 +679,23 @@ void CustomizedGuiManager::onNewDisplayConfiguration(Display* display) } } +bool CustomizedGuiManager::onEnqueueMouseDown(MouseMessage* mouseMsg) +{ + ASSERT(mouseMsg->type() == kMouseDownMessage); + + // If there is no modal window running... + App* app = App::instance(); + if (app && getForegroundWindow() == app->mainWindow()) { + // Process a mouse button as a shortcut. + if (processKey(mouseMsg)) { + // Don't enqueue this message + return false; + } + } + + return true; +} + bool CustomizedGuiManager::processKey(Message* msg) { const KeyboardShortcuts* keys = KeyboardShortcuts::instance(); diff --git a/src/app/ui/key.cpp b/src/app/ui/key.cpp index e6df4cd2d..a6883720b 100644 --- a/src/app/ui/key.cpp +++ b/src/app/ui/key.cpp @@ -507,8 +507,11 @@ const AppShortcut* Key::isPressed(const Message* msg, const KeyContext keyContex // etc. m_keycontext == KeyContext::MouseWheel) { for (const AppShortcut& shortcut : shortcuts()) { - if (shortcut.modifiers() == mouseMsg->modifiers()) - return &shortcut; + if ((shortcut.modifiers() == mouseMsg->modifiers()) && + (shortcut.mouseButton() == mouseMsg->button()) && + (!best || shortcut.fitsBetterThan(keyContext, keycontext(), keycontext(), *best))) { + best = &shortcut; + } } } } diff --git a/src/app/ui/select_shortcut.cpp b/src/app/ui/select_shortcut.cpp index 42436771f..1b7474ffb 100644 --- a/src/app/ui/select_shortcut.cpp +++ b/src/app/ui/select_shortcut.cpp @@ -57,19 +57,21 @@ protected: if (keymsg->scancode() == kKeySpace) modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier); - m_shortcut = Shortcut( - modifiers, - keymsg->scancode(), - keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0); + setAndParseShortcut( + Shortcut(modifiers, + keymsg->scancode(), + keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0)); - // Convert the shortcut to a string, and parse it - // again. Just to obtain the exact shortcut we'll read - // when we import the gui.xml file or an .aseprite-keys file. - m_shortcut = Shortcut(m_shortcut.toString()); + return true; + } + break; - updateText(); + case kMouseDownMessage: + if (!isReadOnly()) { + auto* mouseMsg = static_cast(msg); + const KeyModifiers modifiers = mouseMsg->modifiers(); - ShortcutChange(&m_shortcut); + setAndParseShortcut(Shortcut(modifiers, mouseMsg->button())); return true; } break; @@ -77,9 +79,23 @@ protected: return Entry::onProcessMessage(msg); } + void setAndParseShortcut(const Shortcut& shortcut) + { + // Convert the shortcut to a string, and parse it + // again. Just to obtain the exact shortcut we'll read + // when we import the gui.xml file or an .aseprite-keys file. + m_shortcut = Shortcut(shortcut.toString()); + + updateText(); + + ShortcutChange(&m_shortcut); + } + void updateText() { - setText(Shortcut(kKeyNoneModifier, m_shortcut.scancode(), m_shortcut.unicodeChar()).toString()); + Shortcut tmp = m_shortcut; + tmp.removeModifiers(); + setText(tmp.toString()); } private: @@ -122,12 +138,16 @@ void SelectShortcut::onModifierChange(KeyModifiers modifier, CheckBox* checkbox) KeyModifiers modifiers = m_shortcut.modifiers(); KeyScancode scancode = m_shortcut.scancode(); int unicodeChar = m_shortcut.unicodeChar(); + MouseButton mouseButton = m_shortcut.mouseButton(); modifiers = (KeyModifiers)((modifiers & ~modifier) | (state ? modifier : 0)); if (modifiers == kKeySpaceModifier && scancode == kKeySpace) modifiers = kKeyNoneModifier; - m_shortcut = Shortcut(modifiers, scancode, unicodeChar); + if (mouseButton != kButtonNone) + m_shortcut = Shortcut(modifiers, mouseButton); + else + m_shortcut = Shortcut(modifiers, scancode, unicodeChar); m_keyField->setShortcut(m_shortcut); m_keyField->requestFocus(); diff --git a/src/ui/manager.cpp b/src/ui/manager.cpp index c4e980001..211422293 100644 --- a/src/ui/manager.cpp +++ b/src/ui/manager.cpp @@ -652,16 +652,20 @@ void Manager::handleMouseDown(Display* display, if (!handleWindowZOrder()) return; - enqueueMessage(newMouseMessage(kMouseDownMessage, - display, - (capture_widget ? capture_widget : mouse_widget), - mousePos, - pointerType, - mouseButton, - modifiers, - gfx::Point(0, 0), - false, - pressure)); + std::unique_ptr mouseMsg( + newMouseMessage(kMouseDownMessage, + display, + (capture_widget ? capture_widget : mouse_widget), + mousePos, + pointerType, + mouseButton, + modifiers, + gfx::Point(0, 0), + false, + pressure)); + + if (onEnqueueMouseDown(mouseMsg.get())) + enqueueMessage(mouseMsg.release()); } void Manager::handleMouseUp(Display* display, @@ -1847,6 +1851,11 @@ void Manager::onNewDisplayConfiguration(Display* display) container->flushRedraw(); } +bool Manager::onEnqueueMouseDown(MouseMessage* mouseMsg) +{ + return true; +} + void Manager::onSizeHint(SizeHintEvent& ev) { int w = 0, h = 0; @@ -2270,16 +2279,16 @@ Widget* Manager::findMagneticWidget(Widget* widget) } // static -Message* Manager::newMouseMessage(MessageType type, - Display* display, - Widget* widget, - const gfx::Point& mousePos, - PointerType pointerType, - MouseButton button, - KeyModifiers modifiers, - const gfx::Point& wheelDelta, - bool preciseWheel, - float pressure) +MouseMessage* Manager::newMouseMessage(MessageType type, + Display* display, + Widget* widget, + const gfx::Point& mousePos, + PointerType pointerType, + MouseButton button, + KeyModifiers modifiers, + const gfx::Point& wheelDelta, + bool preciseWheel, + float pressure) { #ifdef __APPLE__ // Convert Ctrl+left click -> right-click @@ -2290,14 +2299,14 @@ Message* Manager::newMouseMessage(MessageType type, } #endif - Message* msg = new MouseMessage(type, - pointerType, - button, - modifiers, - mousePos, - wheelDelta, - preciseWheel, - pressure); + auto* msg = new MouseMessage(type, + pointerType, + button, + modifiers, + mousePos, + wheelDelta, + preciseWheel, + pressure); if (display) msg->setDisplay(display); diff --git a/src/ui/manager.h b/src/ui/manager.h index 0fdbc8979..68f5f1305 100644 --- a/src/ui/manager.h +++ b/src/ui/manager.h @@ -151,6 +151,7 @@ protected: void onInitTheme(InitThemeEvent& ev) override; virtual LayoutIO* onGetLayoutIO(); virtual void onNewDisplayConfiguration(Display* display); + virtual bool onEnqueueMouseDown(MouseMessage* mouseMsg); private: void generateSetCursorMessage(Display* display, @@ -206,16 +207,16 @@ private: static Widget* findLowestCommonAncestor(Widget* a, Widget* b); static bool someParentIsFocusStop(Widget* widget); static Widget* findMagneticWidget(Widget* widget); - static Message* newMouseMessage(MessageType type, - Display* display, - Widget* widget, - const gfx::Point& mousePos, - PointerType pointerType, - MouseButton button, - KeyModifiers modifiers, - const gfx::Point& wheelDelta = gfx::Point(0, 0), - bool preciseWheel = false, - float pressure = 0.0f); + static MouseMessage* newMouseMessage(MessageType type, + Display* display, + Widget* widget, + const gfx::Point& mousePos, + PointerType pointerType, + MouseButton button, + KeyModifiers modifiers, + const gfx::Point& wheelDelta = gfx::Point(0, 0), + bool preciseWheel = false, + float pressure = 0.0f); void broadcastKeyMsg(Message* msg); static Manager* m_defaultManager; diff --git a/src/ui/shortcut.cpp b/src/ui/shortcut.cpp index d48a49698..fe76ee0f8 100644 --- a/src/ui/shortcut.cpp +++ b/src/ui/shortcut.cpp @@ -142,7 +142,7 @@ int scancode_to_string_size = sizeof(scancode_to_string) / sizeof(scancode_to_st } // anonymous namespace -Shortcut::Shortcut() : m_modifiers(kKeyNoneModifier), m_scancode(kKeyNil), m_unicodeChar(0) +Shortcut::Shortcut() { } @@ -153,10 +153,13 @@ Shortcut::Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar { } +Shortcut::Shortcut(KeyModifiers modifiers, MouseButton mouseButton) + : m_modifiers(modifiers) + , m_mouseButton(mouseButton) +{ +} + Shortcut::Shortcut(const std::string& str) - : m_modifiers(kKeyNoneModifier) - , m_scancode(kKeyNil) - , m_unicodeChar(0) { // Special case: plus sign if (str == "+") { @@ -270,6 +273,16 @@ Shortcut::Shortcut(const std::string& str) m_scancode = kKeyDelPad; else if (tok == "enter pad") m_scancode = kKeyEnterPad; + else if (tok == "left mouse button") + m_mouseButton = kButtonLeft; + else if (tok == "right mouse button") + m_mouseButton = kButtonRight; + else if (tok == "middle mouse button") + m_mouseButton = kButtonMiddle; + else if (tok == "x1 mouse button") + m_mouseButton = kButtonX1; + else if (tok == "x2 mouse button") + m_mouseButton = kButtonX2; } } @@ -311,10 +324,23 @@ std::string Shortcut::toString() const wideUnicodeChar.push_back((wchar_t)std::toupper(m_unicodeChar)); buf += base::to_utf8(wideUnicodeChar); } - else if (m_scancode > 0 && m_scancode < scancode_to_string_size && scancode_to_string[m_scancode]) + else if (m_scancode > 0 && m_scancode < scancode_to_string_size && + scancode_to_string[m_scancode]) { buf += scancode_to_string[m_scancode]; - else if (!buf.empty() && buf[buf.size() - 1] == '+') + } + // Mouse button + else if (m_mouseButton != kButtonNone) { + switch (m_mouseButton) { + case kButtonLeft: buf += "Left Mouse Button"; break; + case kButtonRight: buf += "Right Mouse Button"; break; + case kButtonMiddle: buf += "Middle Mouse Button"; break; + case kButtonX1: buf += "X1 Mouse Button"; break; + case kButtonX2: buf += "X2 Mouse Button"; break; + } + } + else if (!buf.empty() && buf[buf.size() - 1] == '+') { buf.erase(buf.size() - 1); + } return buf; } @@ -334,7 +360,7 @@ bool Shortcut::isPressed() const KeyModifiers pressedModifiers = sys->keyModifiers(); // Check if this shortcut is only - if (m_scancode == kKeyNil && m_unicodeChar == 0) + if (m_scancode == kKeyNil && m_unicodeChar == 0 && m_mouseButton == kButtonNone) return (m_modifiers == pressedModifiers); // Compare with all pressed scancodes @@ -359,7 +385,7 @@ bool Shortcut::isLooselyPressed() const return false; // Check if this shortcut is only - if (m_scancode == kKeyNil && m_unicodeChar == 0) + if (m_scancode == kKeyNil && m_unicodeChar == 0 && m_mouseButton == kButtonNone) return true; // Compare with all pressed scancodes diff --git a/src/ui/shortcut.h b/src/ui/shortcut.h index 58c7c98a1..74a9c38b5 100644 --- a/src/ui/shortcut.h +++ b/src/ui/shortcut.h @@ -9,12 +9,13 @@ #define UI_SHORTCUT_H_INCLUDED #pragma once +#include "ui/keys.h" +#include "ui/mouse_button.h" + #include #include #include -#include "ui/keys.h" - namespace ui { extern const char* kWinKeyName; @@ -23,6 +24,7 @@ class Shortcut { public: Shortcut(); Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar); + Shortcut(KeyModifiers modifiers, MouseButton mouseButton); // Convert string like "Ctrl+Q" or "Alt+X" into an shortcut. explicit Shortcut(const std::string& str); @@ -45,11 +47,15 @@ public: KeyModifiers modifiers() const { return m_modifiers; } KeyScancode scancode() const { return m_scancode; } int unicodeChar() const { return m_unicodeChar; } + MouseButton mouseButton() const { return m_mouseButton; } + + void removeModifiers() { m_modifiers = kKeyNoneModifier; } private: - KeyModifiers m_modifiers; - KeyScancode m_scancode; - int m_unicodeChar; + KeyModifiers m_modifiers = kKeyNoneModifier; + KeyScancode m_scancode = kKeyNil; + int m_unicodeChar = 0; + MouseButton m_mouseButton = kButtonNone; }; template