Add support to use mouse buttons as shortcuts (fix #598)

This commit is contained in:
David Capello 2025-01-09 11:41:19 -03:00
parent 6c7544a132
commit 1e34821897
7 changed files with 148 additions and 65 deletions

View File

@ -94,6 +94,7 @@ protected:
void onInitTheme(InitThemeEvent& ev) override; void onInitTheme(InitThemeEvent& ev) override;
LayoutIO* onGetLayoutIO() override { return this; } LayoutIO* onGetLayoutIO() override { return this; }
void onNewDisplayConfiguration(Display* display) override; void onNewDisplayConfiguration(Display* display) override;
bool onEnqueueMouseDown(MouseMessage* mouseMsg) override;
// LayoutIO implementation // LayoutIO implementation
std::string loadLayout(Widget* widget) override; 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) bool CustomizedGuiManager::processKey(Message* msg)
{ {
const KeyboardShortcuts* keys = KeyboardShortcuts::instance(); const KeyboardShortcuts* keys = KeyboardShortcuts::instance();

View File

@ -507,8 +507,11 @@ const AppShortcut* Key::isPressed(const Message* msg, const KeyContext keyContex
// etc. // etc.
m_keycontext == KeyContext::MouseWheel) { m_keycontext == KeyContext::MouseWheel) {
for (const AppShortcut& shortcut : shortcuts()) { for (const AppShortcut& shortcut : shortcuts()) {
if (shortcut.modifiers() == mouseMsg->modifiers()) if ((shortcut.modifiers() == mouseMsg->modifiers()) &&
return &shortcut; (shortcut.mouseButton() == mouseMsg->button()) &&
(!best || shortcut.fitsBetterThan(keyContext, keycontext(), keycontext(), *best))) {
best = &shortcut;
}
} }
} }
} }

View File

@ -57,19 +57,21 @@ protected:
if (keymsg->scancode() == kKeySpace) if (keymsg->scancode() == kKeySpace)
modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier); modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier);
m_shortcut = Shortcut( setAndParseShortcut(
modifiers, Shortcut(modifiers,
keymsg->scancode(), keymsg->scancode(),
keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0); keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0));
// Convert the shortcut to a string, and parse it return true;
// again. Just to obtain the exact shortcut we'll read }
// when we import the gui.xml file or an .aseprite-keys file. break;
m_shortcut = Shortcut(m_shortcut.toString());
updateText(); case kMouseDownMessage:
if (!isReadOnly()) {
auto* mouseMsg = static_cast<MouseMessage*>(msg);
const KeyModifiers modifiers = mouseMsg->modifiers();
ShortcutChange(&m_shortcut); setAndParseShortcut(Shortcut(modifiers, mouseMsg->button()));
return true; return true;
} }
break; break;
@ -77,9 +79,23 @@ protected:
return Entry::onProcessMessage(msg); 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() void updateText()
{ {
setText(Shortcut(kKeyNoneModifier, m_shortcut.scancode(), m_shortcut.unicodeChar()).toString()); Shortcut tmp = m_shortcut;
tmp.removeModifiers();
setText(tmp.toString());
} }
private: private:
@ -122,12 +138,16 @@ void SelectShortcut::onModifierChange(KeyModifiers modifier, CheckBox* checkbox)
KeyModifiers modifiers = m_shortcut.modifiers(); KeyModifiers modifiers = m_shortcut.modifiers();
KeyScancode scancode = m_shortcut.scancode(); KeyScancode scancode = m_shortcut.scancode();
int unicodeChar = m_shortcut.unicodeChar(); int unicodeChar = m_shortcut.unicodeChar();
MouseButton mouseButton = m_shortcut.mouseButton();
modifiers = (KeyModifiers)((modifiers & ~modifier) | (state ? modifier : 0)); modifiers = (KeyModifiers)((modifiers & ~modifier) | (state ? modifier : 0));
if (modifiers == kKeySpaceModifier && scancode == kKeySpace) if (modifiers == kKeySpaceModifier && scancode == kKeySpace)
modifiers = kKeyNoneModifier; 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->setShortcut(m_shortcut);
m_keyField->requestFocus(); m_keyField->requestFocus();

View File

@ -652,16 +652,20 @@ void Manager::handleMouseDown(Display* display,
if (!handleWindowZOrder()) if (!handleWindowZOrder())
return; return;
enqueueMessage(newMouseMessage(kMouseDownMessage, std::unique_ptr<MouseMessage> mouseMsg(
display, newMouseMessage(kMouseDownMessage,
(capture_widget ? capture_widget : mouse_widget), display,
mousePos, (capture_widget ? capture_widget : mouse_widget),
pointerType, mousePos,
mouseButton, pointerType,
modifiers, mouseButton,
gfx::Point(0, 0), modifiers,
false, gfx::Point(0, 0),
pressure)); false,
pressure));
if (onEnqueueMouseDown(mouseMsg.get()))
enqueueMessage(mouseMsg.release());
} }
void Manager::handleMouseUp(Display* display, void Manager::handleMouseUp(Display* display,
@ -1847,6 +1851,11 @@ void Manager::onNewDisplayConfiguration(Display* display)
container->flushRedraw(); container->flushRedraw();
} }
bool Manager::onEnqueueMouseDown(MouseMessage* mouseMsg)
{
return true;
}
void Manager::onSizeHint(SizeHintEvent& ev) void Manager::onSizeHint(SizeHintEvent& ev)
{ {
int w = 0, h = 0; int w = 0, h = 0;
@ -2270,16 +2279,16 @@ Widget* Manager::findMagneticWidget(Widget* widget)
} }
// static // static
Message* Manager::newMouseMessage(MessageType type, MouseMessage* Manager::newMouseMessage(MessageType type,
Display* display, Display* display,
Widget* widget, Widget* widget,
const gfx::Point& mousePos, const gfx::Point& mousePos,
PointerType pointerType, PointerType pointerType,
MouseButton button, MouseButton button,
KeyModifiers modifiers, KeyModifiers modifiers,
const gfx::Point& wheelDelta, const gfx::Point& wheelDelta,
bool preciseWheel, bool preciseWheel,
float pressure) float pressure)
{ {
#ifdef __APPLE__ #ifdef __APPLE__
// Convert Ctrl+left click -> right-click // Convert Ctrl+left click -> right-click
@ -2290,14 +2299,14 @@ Message* Manager::newMouseMessage(MessageType type,
} }
#endif #endif
Message* msg = new MouseMessage(type, auto* msg = new MouseMessage(type,
pointerType, pointerType,
button, button,
modifiers, modifiers,
mousePos, mousePos,
wheelDelta, wheelDelta,
preciseWheel, preciseWheel,
pressure); pressure);
if (display) if (display)
msg->setDisplay(display); msg->setDisplay(display);

View File

@ -151,6 +151,7 @@ protected:
void onInitTheme(InitThemeEvent& ev) override; void onInitTheme(InitThemeEvent& ev) override;
virtual LayoutIO* onGetLayoutIO(); virtual LayoutIO* onGetLayoutIO();
virtual void onNewDisplayConfiguration(Display* display); virtual void onNewDisplayConfiguration(Display* display);
virtual bool onEnqueueMouseDown(MouseMessage* mouseMsg);
private: private:
void generateSetCursorMessage(Display* display, void generateSetCursorMessage(Display* display,
@ -206,16 +207,16 @@ private:
static Widget* findLowestCommonAncestor(Widget* a, Widget* b); static Widget* findLowestCommonAncestor(Widget* a, Widget* b);
static bool someParentIsFocusStop(Widget* widget); static bool someParentIsFocusStop(Widget* widget);
static Widget* findMagneticWidget(Widget* widget); static Widget* findMagneticWidget(Widget* widget);
static Message* newMouseMessage(MessageType type, static MouseMessage* newMouseMessage(MessageType type,
Display* display, Display* display,
Widget* widget, Widget* widget,
const gfx::Point& mousePos, const gfx::Point& mousePos,
PointerType pointerType, PointerType pointerType,
MouseButton button, MouseButton button,
KeyModifiers modifiers, KeyModifiers modifiers,
const gfx::Point& wheelDelta = gfx::Point(0, 0), const gfx::Point& wheelDelta = gfx::Point(0, 0),
bool preciseWheel = false, bool preciseWheel = false,
float pressure = 0.0f); float pressure = 0.0f);
void broadcastKeyMsg(Message* msg); void broadcastKeyMsg(Message* msg);
static Manager* m_defaultManager; static Manager* m_defaultManager;

View File

@ -142,7 +142,7 @@ int scancode_to_string_size = sizeof(scancode_to_string) / sizeof(scancode_to_st
} // anonymous namespace } // 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) Shortcut::Shortcut(const std::string& str)
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{ {
// Special case: plus sign // Special case: plus sign
if (str == "+") { if (str == "+") {
@ -270,6 +273,16 @@ Shortcut::Shortcut(const std::string& str)
m_scancode = kKeyDelPad; m_scancode = kKeyDelPad;
else if (tok == "enter pad") else if (tok == "enter pad")
m_scancode = kKeyEnterPad; 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)); wideUnicodeChar.push_back((wchar_t)std::toupper(m_unicodeChar));
buf += base::to_utf8(wideUnicodeChar); 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]; 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); buf.erase(buf.size() - 1);
}
return buf; return buf;
} }
@ -334,7 +360,7 @@ bool Shortcut::isPressed() const
KeyModifiers pressedModifiers = sys->keyModifiers(); KeyModifiers pressedModifiers = sys->keyModifiers();
// Check if this shortcut is only // 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); return (m_modifiers == pressedModifiers);
// Compare with all pressed scancodes // Compare with all pressed scancodes
@ -359,7 +385,7 @@ bool Shortcut::isLooselyPressed() const
return false; return false;
// Check if this shortcut is only // 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; return true;
// Compare with all pressed scancodes // Compare with all pressed scancodes

View File

@ -9,12 +9,13 @@
#define UI_SHORTCUT_H_INCLUDED #define UI_SHORTCUT_H_INCLUDED
#pragma once #pragma once
#include "ui/keys.h"
#include "ui/mouse_button.h"
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <vector> #include <vector>
#include "ui/keys.h"
namespace ui { namespace ui {
extern const char* kWinKeyName; extern const char* kWinKeyName;
@ -23,6 +24,7 @@ class Shortcut {
public: public:
Shortcut(); Shortcut();
Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar); Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar);
Shortcut(KeyModifiers modifiers, MouseButton mouseButton);
// Convert string like "Ctrl+Q" or "Alt+X" into an shortcut. // Convert string like "Ctrl+Q" or "Alt+X" into an shortcut.
explicit Shortcut(const std::string& str); explicit Shortcut(const std::string& str);
@ -45,11 +47,15 @@ public:
KeyModifiers modifiers() const { return m_modifiers; } KeyModifiers modifiers() const { return m_modifiers; }
KeyScancode scancode() const { return m_scancode; } KeyScancode scancode() const { return m_scancode; }
int unicodeChar() const { return m_unicodeChar; } int unicodeChar() const { return m_unicodeChar; }
MouseButton mouseButton() const { return m_mouseButton; }
void removeModifiers() { m_modifiers = kKeyNoneModifier; }
private: private:
KeyModifiers m_modifiers; KeyModifiers m_modifiers = kKeyNoneModifier;
KeyScancode m_scancode; KeyScancode m_scancode = kKeyNil;
int m_unicodeChar; int m_unicodeChar = 0;
MouseButton m_mouseButton = kButtonNone;
}; };
template<typename T> template<typename T>