Rename ui::Accelerator to ui::Shortcut

This was a pending refactor, where "user.aseprite-keys" files were
already using the "shortcut" attribute (instead of "accelerator").
This will include a refactor in the Weblate projects/all translations
to change [select_accelerator] section to [select_shortcut]. But that
must be coordinated after this commit is merged.
This commit is contained in:
David Capello 2025-01-06 20:21:51 -03:00
parent 698d79b049
commit 2b522e222b
28 changed files with 400 additions and 415 deletions

View File

@ -1707,7 +1707,7 @@ allow_load_lib_access = &Allow Load External Library
give_full_access = Give Script Full &Access give_full_access = Give Script Full &Access
stop_script = &Stop Script stop_script = &Stop Script
[select_accelerator] [select_shortcut]
title = Keyboard Shortcut title = Keyboard Shortcut
key = Key: key = Key:
clear = Clear clear = Clear

View File

@ -1,7 +1,8 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2025 by Igara Studio S.A. -->
<!-- Copyright (C) 2001-2016 by David Capello --> <!-- Copyright (C) 2001-2016 by David Capello -->
<gui> <gui>
<window id="select_accelerator" text="@.title"> <window id="select_shortcut" text="@.title">
<vbox expansive="true"> <vbox expansive="true">
<grid columns="3"> <grid columns="3">
<label text="@.key" /> <label text="@.key" />

View File

@ -669,7 +669,7 @@ target_sources(app-lib PRIVATE
ui/rgbmap_algorithm_selector.cpp ui/rgbmap_algorithm_selector.cpp
ui/sampling_selector.cpp ui/sampling_selector.cpp
ui/search_entry.cpp ui/search_entry.cpp
ui/select_accelerator.cpp ui/select_shortcut.cpp
ui/selection_mode_field.cpp ui/selection_mode_field.cpp
ui/skin/skin_part.cpp ui/skin/skin_part.cpp
ui/skin/skin_property.cpp ui/skin/skin_property.cpp

View File

@ -113,11 +113,11 @@ bool can_call_global_shortcut(const AppMenuItem::Native* native)
(focus == nullptr || focus->type() != ui::kEntryWidget || (focus == nullptr || focus->type() != ui::kEntryWidget ||
!is_text_entry_shortcut(native->shortcut)) && !is_text_entry_shortcut(native->shortcut)) &&
(native->keyContext == KeyContext::Any || (native->keyContext == KeyContext::Any ||
native->keyContext == KeyboardShortcuts::instance()->getCurrentKeyContext()); native->keyContext == KeyboardShortcuts::getCurrentKeyContext());
} }
// TODO this should be on "she" library (or we should use // TODO this should be on laf-os library (or we should use
// os::Shortcut instead of ui::Accelerators) // os::Shortcut instead of ui::Shortcuts)
int from_scancode_to_unicode(KeyScancode scancode) int from_scancode_to_unicode(KeyScancode scancode)
{ {
static int map[] = { static int map[] = {
@ -284,22 +284,21 @@ void destroy_menu_item(ui::Widget* item)
os::Shortcut get_os_shortcut_from_key(const Key* key) os::Shortcut get_os_shortcut_from_key(const Key* key)
{ {
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
const ui::Accelerator& accel = key->accels().front(); const ui::Shortcut& shortcut = key->shortcuts().front();
#if LAF_MACOS #if LAF_MACOS
// Shortcuts with spacebar as modifier do not work well in macOS // Shortcuts with spacebar as modifier do not work well in macOS
// (they will be called when the space bar is unpressed too). // (they will be called when the space bar is unpressed too).
if ((accel.modifiers() & ui::kKeySpaceModifier) == ui::kKeySpaceModifier) if ((shortcut.modifiers() & ui::kKeySpaceModifier) == ui::kKeySpaceModifier)
return os::Shortcut(); return os::Shortcut();
#endif #endif
return os::Shortcut( return os::Shortcut((shortcut.unicodeChar() ? shortcut.unicodeChar() :
(accel.unicodeChar() ? accel.unicodeChar() : from_scancode_to_unicode(accel.scancode())), from_scancode_to_unicode(shortcut.scancode())),
accel.modifiers()); shortcut.modifiers());
} }
else return {};
return os::Shortcut();
} }
AppMenus* AppMenus::s_instance = nullptr; AppMenus* AppMenus::s_instance = nullptr;

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -54,11 +55,11 @@ void AdvancedModeCommand::onExecute(Context* context)
if (oldMode == MainWindow::NormalMode && pref.advancedMode.showAlert()) { if (oldMode == MainWindow::NormalMode && pref.advancedMode.showAlert()) {
KeyPtr key = KeyboardShortcuts::instance()->command(this->id().c_str()); KeyPtr key = KeyboardShortcuts::instance()->command(this->id().c_str());
if (!key->accels().empty()) { if (!key->shortcuts().empty()) {
app::gen::AdvancedMode window; app::gen::AdvancedMode window;
window.warningLabel()->setTextf("You can go back pressing \"%s\" key.", window.warningLabel()->setTextf("You can go back pressing \"%s\" key.",
key->accels().front().toString().c_str()); key->shortcuts().front().toString().c_str());
window.openWindowInForeground(); window.openWindowInForeground();

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -23,7 +23,7 @@
#include "app/ui/app_menuitem.h" #include "app/ui/app_menuitem.h"
#include "app/ui/keyboard_shortcuts.h" #include "app/ui/keyboard_shortcuts.h"
#include "app/ui/search_entry.h" #include "app/ui/search_entry.h"
#include "app/ui/select_accelerator.h" #include "app/ui/select_shortcut.h"
#include "app/ui/separator_in_view.h" #include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "base/fs.h" #include "base/fs.h"
@ -151,7 +151,7 @@ public:
, m_keyOrig(key ? new Key(*key) : nullptr) , m_keyOrig(key ? new Key(*key) : nullptr)
, m_menuitem(menuitem) , m_menuitem(menuitem)
, m_level(level) , m_level(level)
, m_hotAccel(-1) , m_hotShortcut(-1)
, m_lockButtons(false) , m_lockButtons(false)
, m_headerItem(headerItem) , m_headerItem(headerItem)
{ {
@ -204,45 +204,45 @@ public:
} }
private: private:
void onChangeAccel(int index) void onChangeShortcut(int index)
{ {
LockButtons lock(this); LockButtons lock(this);
Accelerator origAccel = m_key->accels()[index]; Shortcut origShortcut = m_key->shortcuts()[index];
SelectAccelerator window(origAccel, m_key->keycontext(), m_keys); SelectShortcut window(origShortcut, m_key->keycontext(), m_keys);
window.openWindowInForeground(); window.openWindowInForeground();
if (window.isModified()) { if (window.isModified()) {
m_key->disableAccel(origAccel, KeySource::UserDefined); m_key->disableShortcut(origShortcut, KeySource::UserDefined);
if (!window.accel().isEmpty()) if (!window.shortcut().isEmpty())
m_key->add(window.accel(), KeySource::UserDefined, m_keys); m_key->add(window.shortcut(), KeySource::UserDefined, m_keys);
} }
this->window()->layout(); this->window()->layout();
} }
void onDeleteAccel(int index) void onDeleteShortcut(int index)
{ {
LockButtons lock(this); LockButtons lock(this);
// We need to create a copy of the accelerator because // We need to create a copy of the shortcut because
// Key::disableAccel() will modify the accels() collection itself. // Key::disableShortcut() will modify the shortcuts() collection itself.
ui::Accelerator accel = m_key->accels()[index]; ui::Shortcut shortcut = m_key->shortcuts()[index];
if (ui::Alert::show(Strings::alerts_delete_shortcut(accel.toString())) != 1) if (ui::Alert::show(Strings::alerts_delete_shortcut(shortcut.toString())) != 1)
return; return;
m_key->disableAccel(accel, KeySource::UserDefined); m_key->disableShortcut(shortcut, KeySource::UserDefined);
window()->layout(); window()->layout();
} }
void onAddAccel() void onAddShortcut()
{ {
LockButtons lock(this); LockButtons lock(this);
ui::Accelerator accel; ui::Shortcut shortcut;
SelectAccelerator window(accel, m_key ? m_key->keycontext() : KeyContext::Any, m_keys); SelectShortcut window(shortcut, m_key ? m_key->keycontext() : KeyContext::Any, m_keys);
window.openWindowInForeground(); window.openWindowInForeground();
if ((window.isModified()) || if ((window.isModified()) ||
// We can assign a "None" accelerator to mouse wheel actions // We can assign a "None" shortcut to mouse wheel actions
(m_key && m_key->type() == KeyType::WheelAction && window.isOK())) { (m_key && m_key->type() == KeyType::WheelAction && window.isOK())) {
if (!m_key) { if (!m_key) {
ASSERT(m_menuitem); ASSERT(m_menuitem);
@ -256,7 +256,7 @@ private:
m_menuKeys[m_menuitem] = m_key; m_menuKeys[m_menuitem] = m_key;
} }
m_key->add(window.accel(), KeySource::UserDefined, m_keys); m_key->add(window.shortcut(), KeySource::UserDefined, m_keys);
} }
this->window()->layout(); this->window()->layout();
@ -273,8 +273,8 @@ private:
size.w = std::max(size.w, w); size.w = std::max(size.w, w);
} }
if (m_key && !m_key->accels().empty()) { if (m_key && !m_key->shortcuts().empty()) {
size_t combos = m_key->accels().size(); size_t combos = m_key->shortcuts().size();
if (combos > 1) if (combos > 1)
size.h *= combos; size.h *= combos;
} }
@ -315,7 +315,7 @@ private:
} }
} }
if (m_key && !m_key->accels().empty()) { if (m_key && !m_key->shortcuts().empty()) {
if (m_key->keycontext() != KeyContext::Any) { if (m_key->keycontext() != KeyContext::Any) {
g->drawText(convertKeyContextToUserFriendlyString(m_key->keycontext()), g->drawText(convertKeyContextToUserFriendlyString(m_key->keycontext()),
fg, fg,
@ -324,13 +324,14 @@ private:
} }
const int dh = th + 4 * guiscale(); const int dh = th + 4 * guiscale();
IntersectClip clip(g, IntersectClip clip(
gfx::Rect(keyXPos, y, contextXPos - keyXPos, dh * m_key->accels().size())); g,
gfx::Rect(keyXPos, y, contextXPos - keyXPos, dh * m_key->shortcuts().size()));
if (clip) { if (clip) {
int i = 0; int i = 0;
for (const Accelerator& accel : m_key->accels()) { for (const Shortcut& shortcut : m_key->shortcuts()) {
if (i != m_hotAccel || !m_changeButton) { if (i != m_hotShortcut || !m_changeButton) {
g->drawText(getAccelText(accel), fg, bg, gfx::Point(keyXPos, y)); g->drawText(getShortcutText(shortcut), fg, bg, gfx::Point(keyXPos, y));
} }
y += dh; y += dh;
++i; ++i;
@ -361,40 +362,41 @@ private:
gfx::Rect bounds = this->bounds(); gfx::Rect bounds = this->bounds();
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
const Accelerators* accels = (m_key ? &m_key->accels() : NULL); const Shortcuts* shortcuts = (m_key ? &m_key->shortcuts() : NULL);
int y = bounds.y; int y = bounds.y;
int dh = textSize().h + 4 * guiscale(); int dh = textSize().h + 4 * guiscale();
int maxi = (accels && accels->size() > 1 ? accels->size() : 1); int maxi = (shortcuts && shortcuts->size() > 1 ? shortcuts->size() : 1);
auto theme = SkinTheme::get(this); auto* theme = SkinTheme::get(this);
for (int i = 0; i < maxi; ++i, y += dh) { for (int i = 0; i < maxi; ++i, y += dh) {
int w = font()->textLength( int w = font()->textLength((shortcuts && i < (int)shortcuts->size() ?
(accels && i < (int)accels->size() ? getAccelText((*accels)[i]) : std::string())); getShortcutText((*shortcuts)[i]) :
std::string()));
gfx::Rect itemBounds(bounds.x + m_headerItem->keyXPos(), y, w, dh); gfx::Rect itemBounds(bounds.x + m_headerItem->keyXPos(), y, w, dh);
itemBounds = itemBounds.enlarge( itemBounds = itemBounds.enlarge(
gfx::Border(4 * guiscale(), 0, 6 * guiscale(), 1 * guiscale())); gfx::Border(4 * guiscale(), 0, 6 * guiscale(), 1 * guiscale()));
if (accels && i < (int)accels->size() && mouseMsg->position().y >= itemBounds.y && if (shortcuts && i < (int)shortcuts->size() && mouseMsg->position().y >= itemBounds.y &&
mouseMsg->position().y < itemBounds.y + itemBounds.h) { mouseMsg->position().y < itemBounds.y + itemBounds.h) {
if (m_hotAccel != i) { if (m_hotShortcut != i) {
m_hotAccel = i; m_hotShortcut = i;
m_changeConn = obs::connection(); m_changeConn = obs::connection();
m_changeButton.reset(new Button("")); m_changeButton.reset(new Button(""));
m_changeConn = m_changeButton->Click.connect([this, i] { onChangeAccel(i); }); m_changeConn = m_changeButton->Click.connect([this, i] { onChangeShortcut(i); });
m_changeButton->setStyle(theme->styles.miniButton()); m_changeButton->setStyle(theme->styles.miniButton());
addChild(m_changeButton.get()); addChild(m_changeButton.get());
m_deleteConn = obs::connection(); m_deleteConn = obs::connection();
m_deleteButton.reset(new Button("")); m_deleteButton.reset(new Button(""));
m_deleteConn = m_deleteButton->Click.connect([this, i] { onDeleteAccel(i); }); m_deleteConn = m_deleteButton->Click.connect([this, i] { onDeleteShortcut(i); });
m_deleteButton->setStyle(theme->styles.miniButton()); m_deleteButton->setStyle(theme->styles.miniButton());
addChild(m_deleteButton.get()); addChild(m_deleteButton.get());
m_changeButton->setBgColor(gfx::ColorNone); m_changeButton->setBgColor(gfx::ColorNone);
m_changeButton->setBounds(itemBounds); m_changeButton->setBounds(itemBounds);
m_changeButton->setText(getAccelText((*accels)[i])); m_changeButton->setText(getShortcutText((*shortcuts)[i]));
const char* label = "x"; const char* label = "x";
m_deleteButton->setBgColor(gfx::ColorNone); m_deleteButton->setBgColor(gfx::ColorNone);
@ -411,7 +413,7 @@ private:
if (i == 0 && !m_addButton && (!m_menuitem || m_menuitem->getCommand())) { if (i == 0 && !m_addButton && (!m_menuitem || m_menuitem->getCommand())) {
m_addConn = obs::connection(); m_addConn = obs::connection();
m_addButton.reset(new Button("")); m_addButton.reset(new Button(""));
m_addConn = m_addButton->Click.connect([this] { onAddAccel(); }); m_addConn = m_addButton->Click.connect([this] { onAddShortcut(); });
m_addButton->setStyle(theme->styles.miniButton()); m_addButton->setStyle(theme->styles.miniButton());
addChild(m_addButton.get()); addChild(m_addButton.get());
@ -452,17 +454,15 @@ private:
m_addButton->setVisible(false); m_addButton->setVisible(false);
} }
m_hotAccel = -1; m_hotShortcut = -1;
} }
std::string getAccelText(const Accelerator& accel) const std::string getShortcutText(const Shortcut& shortcut) const
{ {
if (m_key && m_key->type() == KeyType::WheelAction && accel.isEmpty()) { if (m_key && m_key->type() == KeyType::WheelAction && shortcut.isEmpty()) {
return Strings::keyboard_shortcuts_default_action(); return Strings::keyboard_shortcuts_default_action();
} }
else { return shortcut.toString();
return accel.toString();
}
} }
KeyboardShortcuts& m_keys; KeyboardShortcuts& m_keys;
@ -471,14 +471,14 @@ private:
KeyPtr m_keyOrig; KeyPtr m_keyOrig;
AppMenuItem* m_menuitem; AppMenuItem* m_menuitem;
int m_level; int m_level;
ui::Accelerators m_newAccels; ui::Shortcuts m_newShortcuts;
std::shared_ptr<ui::Button> m_changeButton; std::shared_ptr<ui::Button> m_changeButton;
std::shared_ptr<ui::Button> m_deleteButton; std::shared_ptr<ui::Button> m_deleteButton;
std::shared_ptr<ui::Button> m_addButton; std::shared_ptr<ui::Button> m_addButton;
obs::scoped_connection m_changeConn; obs::scoped_connection m_changeConn;
obs::scoped_connection m_deleteConn; obs::scoped_connection m_deleteConn;
obs::scoped_connection m_addConn; obs::scoped_connection m_addConn;
int m_hotAccel; int m_hotShortcut;
bool m_lockButtons; bool m_lockButtons;
HeaderItem* m_headerItem; HeaderItem* m_headerItem;
}; };

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -164,10 +164,10 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
params.set("change", "custom"); params.set("change", "custom");
params.set("slot", base::convert_to<std::string>(slot).c_str()); params.set("slot", base::convert_to<std::string>(slot).c_str());
KeyPtr key = KeyboardShortcuts::instance()->command(CommandId::ChangeBrush(), params); KeyPtr key = KeyboardShortcuts::instance()->command(CommandId::ChangeBrush(), params);
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
std::string tooltip; std::string tooltip;
tooltip += Strings::new_brush_shortcut() + " "; tooltip += Strings::new_brush_shortcut() + " ";
tooltip += key->accels().front().toString(); tooltip += key->shortcuts().front().toString();
StatusBar::instance()->showTip(2000, tooltip); StatusBar::instance()->showTip(2000, tooltip);
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -128,7 +128,7 @@ private:
case ui::kKeyDownMessage: { case ui::kKeyDownMessage: {
KeyboardShortcuts* keys = KeyboardShortcuts::instance(); KeyboardShortcuts* keys = KeyboardShortcuts::instance();
const KeyPtr key = keys->command(CommandId::SwitchColors()); const KeyPtr key = keys->command(CommandId::SwitchColors());
if (key && key->isPressed(msg, *keys)) { if (key && key->isPressed(msg)) {
// Switch colors // Switch colors
app::Color from = m_fromButton->getColor(); app::Color from = m_fromButton->getColor();
app::Color to = m_toButton->getColor(); app::Color to = m_toButton->getColor();

View File

@ -682,11 +682,11 @@ bool CustomizedGuiManager::processKey(Message* msg)
{ {
App* app = App::instance(); App* app = App::instance();
const KeyboardShortcuts* keys = KeyboardShortcuts::instance(); const KeyboardShortcuts* keys = KeyboardShortcuts::instance();
const KeyContext contexts[] = { keys->getCurrentKeyContext(), KeyContext::Normal }; const KeyContext contexts[] = { KeyboardShortcuts::getCurrentKeyContext(), KeyContext::Normal };
int n = (contexts[0] != contexts[1] ? 2 : 1); int n = (contexts[0] != contexts[1] ? 2 : 1);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
for (const KeyPtr& key : *keys) { for (const KeyPtr& key : *keys) {
if (key->isPressed(msg, *keys, contexts[i])) { if (key->isPressed(msg, contexts[i])) {
// Cancel menu-bar loops (to close any popup menu) // Cancel menu-bar loops (to close any popup menu)
app->mainWindow()->getMenuBar()->cancelMenuLoop(); app->mainWindow()->getMenuBar()->cancelMenuLoop();
@ -700,7 +700,7 @@ bool CustomizedGuiManager::processKey(Message* msg)
// Collect all tools with the pressed keyboard-shortcut // Collect all tools with the pressed keyboard-shortcut
for (tools::Tool* tool : *toolbox) { for (tools::Tool* tool : *toolbox) {
const KeyPtr key = keys->tool(tool); const KeyPtr key = keys->tool(tool);
if (key && key->isPressed(msg, *keys)) if (key && key->isPressed(msg))
possibles.push_back(tool); possibles.push_back(tool);
} }

View File

@ -19,10 +19,10 @@
#include "app/ui/keyboard_shortcuts.h" #include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "os/menus.h" #include "os/menus.h"
#include "ui/accelerator.h"
#include "ui/menu.h" #include "ui/menu.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/scale.h" #include "ui/scale.h"
#include "ui/shortcut.h"
#include "ui/size_hint_event.h" #include "ui/size_hint_event.h"
#include "ui/widget.h" #include "ui/widget.h"
@ -139,8 +139,8 @@ void AppMenuItem::onSizeHint(SizeHintEvent& ev)
border().width(); border().width();
size.h = textHeight() + border().height(); size.h = textHeight() + border().height();
if (m_key && !m_key->accels().empty()) { if (m_key && !m_key->shortcuts().empty()) {
size.w += font()->textLength(m_key->accels().front().toString()); size.w += font()->textLength(m_key->shortcuts().front().toString());
} }
} }

View File

@ -431,8 +431,8 @@ void BrushPopup::regenerate(ui::Display* display, const gfx::Point& pos)
params.set("change", "custom"); params.set("change", "custom");
params.set("slot", base::convert_to<std::string>(slot).c_str()); params.set("slot", base::convert_to<std::string>(slot).c_str());
KeyPtr key = KeyboardShortcuts::instance()->command(CommandId::ChangeBrush(), params); KeyPtr key = KeyboardShortcuts::instance()->command(CommandId::ChangeBrush(), params);
if (key && !key->accels().empty()) if (key && !key->shortcuts().empty())
shortcut = key->accels().front().toString(); shortcut = key->shortcuts().front().toString();
} }
m_customBrushes->addItem(new SelectBrushItem(brush, slot)); m_customBrushes->addItem(new SelectBrushItem(brush, slot));
m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot)); m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot));

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -41,10 +41,10 @@
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "ui/accelerator.h"
#include "ui/alert.h" #include "ui/alert.h"
#include "ui/menu.h" #include "ui/menu.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/shortcut.h"
#include "ui/system.h" #include "ui/system.h"
#include "ui/view.h" #include "ui/view.h"
@ -147,11 +147,11 @@ protected:
KeyPtr rmb = keys->action(KeyAction::RightMouseButton, KeyContext::Any); KeyPtr rmb = keys->action(KeyAction::RightMouseButton, KeyContext::Any);
// Convert action keys into mouse messages. // Convert action keys into mouse messages.
if (lmb->isPressed(msg, *keys) || rmb->isPressed(msg, *keys)) { if (lmb->isPressed(msg) || rmb->isPressed(msg)) {
MouseMessage mouseMsg( MouseMessage mouseMsg(
(msg->type() == kKeyDownMessage ? kMouseDownMessage : kMouseUpMessage), (msg->type() == kKeyDownMessage ? kMouseDownMessage : kMouseUpMessage),
PointerType::Unknown, PointerType::Unknown,
(lmb->isPressed(msg, *keys) ? kButtonLeft : kButtonRight), (lmb->isPressed(msg) ? kButtonLeft : kButtonRight),
msg->modifiers(), msg->modifiers(),
mousePosInDisplay()); mousePosInDisplay());

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -464,8 +464,7 @@ bool StandbyState::onKeyDown(Editor* editor, KeyMessage* msg)
checkStartDrawingStraightLine(editor, nullptr, nullptr)) checkStartDrawingStraightLine(editor, nullptr, nullptr))
return false; return false;
Keys keys = KeyboardShortcuts::instance()->getDragActionsFromKeyMessage(KeyContext::MouseWheel, Keys keys = KeyboardShortcuts::instance()->getDragActionsFromKeyMessage(msg);
msg);
if (editor->hasMouse() && !keys.empty()) { if (editor->hasMouse() && !keys.empty()) {
// Don't enter DraggingValueState to change brush size if we are // Don't enter DraggingValueState to change brush size if we are
// in a selection-like tool // in a selection-like tool

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -13,7 +13,7 @@
#include "app/ui/key_context.h" #include "app/ui/key_context.h"
#include "base/convert_to.h" #include "base/convert_to.h"
#include "base/vector2d.h" #include "base/vector2d.h"
#include "ui/accelerator.h" #include "ui/shortcut.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
@ -106,7 +106,7 @@ inline KeyAction operator&(KeyAction a, KeyAction b)
class Key; class Key;
using KeyPtr = std::shared_ptr<Key>; using KeyPtr = std::shared_ptr<Key>;
using Keys = std::vector<KeyPtr>; using Keys = std::vector<KeyPtr>;
using KeySourceAccelList = std::vector<std::pair<KeySource, ui::Accelerator>>; using KeySourceShortcutList = std::vector<std::pair<KeySource, ui::Shortcut>>;
using DragVector = base::Vector2d<double>; using DragVector = base::Vector2d<double>;
class Key { class Key {
@ -119,29 +119,26 @@ public:
static KeyPtr MakeDragAction(WheelAction dragAction); static KeyPtr MakeDragAction(WheelAction dragAction);
KeyType type() const { return m_type; } KeyType type() const { return m_type; }
const ui::Accelerators& accels() const; const ui::Shortcuts& shortcuts() const;
const KeySourceAccelList addsKeys() const { return m_adds; } const KeySourceShortcutList& addsKeys() const { return m_adds; }
const KeySourceAccelList delsKeys() const { return m_dels; } const KeySourceShortcutList& delsKeys() const { return m_dels; }
void add(const ui::Accelerator& accel, const KeySource source, KeyboardShortcuts& globalKeys); void add(const ui::Shortcut& shortcut, KeySource source, KeyboardShortcuts& globalKeys);
const ui::Accelerator* isPressed(const ui::Message* msg, const ui::Shortcut* isPressed(const ui::Message* msg, KeyContext keyContext) const;
const KeyboardShortcuts& globalKeys, const ui::Shortcut* isPressed(const ui::Message* msg) const;
const KeyContext keyContext) const;
const ui::Accelerator* isPressed(const ui::Message* msg,
const KeyboardShortcuts& globalKeys) const;
bool isPressed() const; bool isPressed() const;
bool isLooselyPressed() const; bool isLooselyPressed() const;
bool isCommandListed() const; bool isCommandListed() const;
bool hasAccel(const ui::Accelerator& accel) const; bool hasShortcut(const ui::Shortcut& shortcut) const;
bool hasUserDefinedAccels() const; bool hasUserDefinedShortcuts() const;
// The KeySource indicates from where the key was disabled // The KeySource indicates from where the key was disabled
// (e.g. if it was removed from an extension-defined file, or from // (e.g. if it was removed from an extension-defined file, or from
// user-defined). // user-defined).
void disableAccel(const ui::Accelerator& accel, const KeySource source); void disableShortcut(const ui::Shortcut& shortcut, KeySource source);
// Resets user accelerators to the original & extension-defined ones. // Resets user shortcuts to the original & extension-defined ones.
void reset(); void reset();
void copyOriginalToUser(); void copyOriginalToUser();
@ -164,11 +161,11 @@ public:
private: private:
KeyType m_type; KeyType m_type;
KeySourceAccelList m_adds; KeySourceShortcutList m_adds;
KeySourceAccelList m_dels; KeySourceShortcutList m_dels;
// Final list of accelerators after processing the // Final list of shortcuts after processing the
// addition/deletion of extension-defined & user-defined keys. // addition/deletion of extension-defined & user-defined keys.
mutable std::unique_ptr<ui::Accelerators> m_accels; mutable std::unique_ptr<ui::Shortcuts> m_shortcuts;
KeyContext m_keycontext; KeyContext m_keycontext;
// for KeyType::Command // for KeyType::Command

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -28,8 +28,8 @@
#include "app/xml_document.h" #include "app/xml_document.h"
#include "app/xml_exception.h" #include "app/xml_exception.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "ui/accelerator.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/shortcut.h"
#include "tinyxml2.h" #include "tinyxml2.h"
@ -244,13 +244,13 @@ std::string get_user_friendly_string_for_wheelaction(app::WheelAction wheelActio
return std::string(); return std::string();
} }
void erase_accel(app::KeySourceAccelList& kvs, void erase_shortcut(app::KeySourceShortcutList& kvs,
const app::KeySource source, const app::KeySource source,
const ui::Accelerator& accel) const ui::Shortcut& shortcut)
{ {
for (auto it = kvs.begin(); it != kvs.end();) { for (auto it = kvs.begin(); it != kvs.end();) {
auto& kv = *it; auto& kv = *it;
if (kv.first == source && kv.second == accel) { if (kv.first == source && kv.second == shortcut) {
it = kvs.erase(it); it = kvs.erase(it);
} }
else else
@ -258,7 +258,7 @@ void erase_accel(app::KeySourceAccelList& kvs,
} }
} }
void erase_accels(app::KeySourceAccelList& kvs, const app::KeySource source) void erase_shortcuts(app::KeySourceShortcutList& kvs, const app::KeySource source)
{ {
for (auto it = kvs.begin(); it != kvs.end();) { for (auto it = kvs.begin(); it != kvs.end();) {
auto& kv = *it; auto& kv = *it;
@ -436,104 +436,100 @@ KeyPtr Key::MakeDragAction(WheelAction dragAction)
return k; return k;
} }
const ui::Accelerators& Key::accels() const const ui::Shortcuts& Key::shortcuts() const
{ {
if (!m_accels) { if (!m_shortcuts) {
m_accels = std::make_unique<ui::Accelerators>(); m_shortcuts = std::make_unique<ui::Shortcuts>();
// Add default keys // Add default keys
for (const auto& kv : m_adds) { for (const auto& kv : m_adds) {
if (kv.first == KeySource::Original) if (kv.first == KeySource::Original)
m_accels->add(kv.second); m_shortcuts->add(kv.second);
} }
// Delete/add extension-defined keys // Delete/add extension-defined keys
for (const auto& kv : m_dels) { for (const auto& kv : m_dels) {
if (kv.first == KeySource::ExtensionDefined) if (kv.first == KeySource::ExtensionDefined)
m_accels->remove(kv.second); m_shortcuts->remove(kv.second);
else { else {
ASSERT(kv.first != KeySource::Original); ASSERT(kv.first != KeySource::Original);
} }
} }
for (const auto& kv : m_adds) { for (const auto& kv : m_adds) {
if (kv.first == KeySource::ExtensionDefined) if (kv.first == KeySource::ExtensionDefined)
m_accels->add(kv.second); m_shortcuts->add(kv.second);
} }
// Delete/add user-defined keys // Delete/add user-defined keys
for (const auto& kv : m_dels) { for (const auto& kv : m_dels) {
if (kv.first == KeySource::UserDefined) if (kv.first == KeySource::UserDefined)
m_accels->remove(kv.second); m_shortcuts->remove(kv.second);
} }
for (const auto& kv : m_adds) { for (const auto& kv : m_adds) {
if (kv.first == KeySource::UserDefined) if (kv.first == KeySource::UserDefined)
m_accels->add(kv.second); m_shortcuts->add(kv.second);
} }
} }
return *m_accels; return *m_shortcuts;
} }
void Key::add(const ui::Accelerator& accel, const KeySource source, KeyboardShortcuts& globalKeys) void Key::add(const ui::Shortcut& shortcut, const KeySource source, KeyboardShortcuts& globalKeys)
{ {
m_adds.emplace_back(source, accel); m_adds.emplace_back(source, shortcut);
m_accels.reset(); m_shortcuts.reset();
// Remove the accelerator from other commands // Remove the shortcut from other commands
if (source == KeySource::ExtensionDefined || source == KeySource::UserDefined) { if (source == KeySource::ExtensionDefined || source == KeySource::UserDefined) {
erase_accel(m_dels, source, accel); erase_shortcut(m_dels, source, shortcut);
globalKeys.disableAccel(accel, source, m_keycontext, this); globalKeys.disableShortcut(shortcut, source, m_keycontext, this);
} }
} }
const ui::Accelerator* Key::isPressed(const Message* msg, const ui::Shortcut* Key::isPressed(const Message* msg, const KeyContext keyContext) const
const KeyboardShortcuts& globalKeys,
const KeyContext keyContext) const
{ {
if (auto keyMsg = dynamic_cast<const KeyMessage*>(msg)) { if (const auto* keyMsg = dynamic_cast<const KeyMessage*>(msg)) {
for (const Accelerator& accel : accels()) { for (const Shortcut& shortcut : shortcuts()) {
if (accel.isPressed(keyMsg->modifiers(), keyMsg->scancode(), keyMsg->unicodeChar()) && if (shortcut.isPressed(keyMsg->modifiers(), keyMsg->scancode(), keyMsg->unicodeChar()) &&
(m_keycontext == KeyContext::Any || m_keycontext == keyContext)) { (m_keycontext == KeyContext::Any || m_keycontext == keyContext)) {
return &accel; return &shortcut;
} }
} }
} }
else if (auto mouseMsg = dynamic_cast<const MouseMessage*>(msg)) { else if (const auto* mouseMsg = dynamic_cast<const MouseMessage*>(msg)) {
for (const Accelerator& accel : accels()) { for (const Shortcut& shortcut : shortcuts()) {
if ((accel.modifiers() == mouseMsg->modifiers()) && if ((shortcut.modifiers() == mouseMsg->modifiers()) &&
(m_keycontext == KeyContext::Any || (m_keycontext == KeyContext::Any ||
// TODO we could have multiple mouse wheel key-context, // TODO we could have multiple mouse wheel key-context,
// like "sprite editor" context, or "timeline" context, // like "sprite editor" context, or "timeline" context,
// etc. // etc.
m_keycontext == KeyContext::MouseWheel)) { m_keycontext == KeyContext::MouseWheel)) {
return &accel; return &shortcut;
} }
} }
} }
return nullptr; return nullptr;
} }
const ui::Accelerator* Key::isPressed(const Message* msg, const KeyboardShortcuts& globalKeys) const const ui::Shortcut* Key::isPressed(const Message* msg) const
{ {
return isPressed(msg, globalKeys, globalKeys.getCurrentKeyContext()); return isPressed(msg, KeyboardShortcuts::getCurrentKeyContext());
} }
bool Key::isPressed() const bool Key::isPressed() const
{ {
for (const Accelerator& accel : this->accels()) { const auto& ss = this->shortcuts();
if (accel.isPressed()) return std::any_of(ss.begin(), ss.end(), [](const Shortcut& shortcut) {
return true; return shortcut.isPressed();
} });
return false;
} }
bool Key::isLooselyPressed() const bool Key::isLooselyPressed() const
{ {
for (const Accelerator& accel : this->accels()) { const auto& ss = this->shortcuts();
if (accel.isLooselyPressed()) return std::any_of(ss.begin(), ss.end(), [](const Shortcut& shortcut) {
return true; return shortcut.isLooselyPressed();
} });
return false;
} }
bool Key::isCommandListed() const bool Key::isCommandListed() const
@ -541,51 +537,49 @@ bool Key::isCommandListed() const
return type() == KeyType::Command && command()->isListed(params()); return type() == KeyType::Command && command()->isListed(params());
} }
bool Key::hasAccel(const ui::Accelerator& accel) const bool Key::hasShortcut(const ui::Shortcut& shortcut) const
{ {
return accels().has(accel); return shortcuts().has(shortcut);
} }
bool Key::hasUserDefinedAccels() const bool Key::hasUserDefinedShortcuts() const
{ {
for (const auto& kv : m_adds) { return std::any_of(m_adds.begin(), m_adds.end(), [](const auto& kv) {
if (kv.first == KeySource::UserDefined) return (kv.first == KeySource::UserDefined);
return true; });
}
return false;
} }
void Key::disableAccel(const ui::Accelerator& accel, const KeySource source) void Key::disableShortcut(const ui::Shortcut& shortcut, const KeySource source)
{ {
// It doesn't make sense that the default keyboard shortcuts file // It doesn't make sense that the default keyboard shortcuts file
// (gui.xml) removes some accelerator. // (gui.xml) removes some shortcut.
ASSERT(source != KeySource::Original); ASSERT(source != KeySource::Original);
erase_accel(m_adds, source, accel); erase_shortcut(m_adds, source, shortcut);
erase_accel(m_dels, source, accel); erase_shortcut(m_dels, source, shortcut);
m_dels.emplace_back(source, accel); m_dels.emplace_back(source, shortcut);
m_accels.reset(); m_shortcuts.reset();
} }
void Key::reset() void Key::reset()
{ {
erase_accels(m_adds, KeySource::UserDefined); erase_shortcuts(m_adds, KeySource::UserDefined);
erase_accels(m_dels, KeySource::UserDefined); erase_shortcuts(m_dels, KeySource::UserDefined);
m_accels.reset(); m_shortcuts.reset();
} }
void Key::copyOriginalToUser() void Key::copyOriginalToUser()
{ {
// Erase all user-defined keys // Erase all user-defined keys
erase_accels(m_adds, KeySource::UserDefined); erase_shortcuts(m_adds, KeySource::UserDefined);
erase_accels(m_dels, KeySource::UserDefined); erase_shortcuts(m_dels, KeySource::UserDefined);
// Then copy all original & extension-defined keys as user-defined // Then copy all original & extension-defined keys as user-defined
auto copy = m_adds; auto copy = m_adds;
for (const auto& kv : copy) for (const auto& kv : copy)
m_adds.emplace_back(KeySource::UserDefined, kv.second); m_adds.emplace_back(KeySource::UserDefined, kv.second);
m_accels.reset(); m_shortcuts.reset();
} }
std::string Key::triggerString() const std::string Key::triggerString() const
@ -693,21 +687,21 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
// add the keyboard shortcut to the command // add the keyboard shortcut to the command
KeyPtr key = this->command(command_name, params, keycontext); KeyPtr key = this->command(command_name, params, keycontext);
if (key && command_key) { if (key && command_key) {
Accelerator accel(command_key); Shortcut shortcut(command_key);
if (!removed) { if (!removed) {
key->add(accel, source, *this); key->add(shortcut, source, *this);
// Add the shortcut to the menuitems with this command // Add the shortcut to the menuitems with this command
// (this is only visual, the // (this is only visual, the
// "CustomizedGuiManager::onProcessMessage" is the only // "CustomizedGuiManager::onProcessMessage" is the only
// one that process keyboard shortcuts) // one that process keyboard shortcuts)
if (key->accels().size() == 1) { if (key->shortcuts().size() == 1) {
AppMenus::instance()->applyShortcutToMenuitemsWithCommand(command, params, key); AppMenus::instance()->applyShortcutToMenuitemsWithCommand(command, params, key);
} }
} }
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -729,12 +723,12 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
KeyPtr key = this->tool(tool); KeyPtr key = this->tool(tool);
if (key && tool_key) { if (key && tool_key) {
LOG(VERBOSE, "KEYS: Shortcut for tool %s: %s\n", tool_id, tool_key); LOG(VERBOSE, "KEYS: Shortcut for tool %s: %s\n", tool_id, tool_key);
Accelerator accel(tool_key); Shortcut shortcut(tool_key);
if (!removed) if (!removed)
key->add(accel, source, *this); key->add(shortcut, source, *this);
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -755,12 +749,12 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
KeyPtr key = this->quicktool(tool); KeyPtr key = this->quicktool(tool);
if (key && tool_key) { if (key && tool_key) {
LOG(VERBOSE, "KEYS: Shortcut for quicktool %s: %s\n", tool_id, tool_key); LOG(VERBOSE, "KEYS: Shortcut for quicktool %s: %s\n", tool_id, tool_key);
Accelerator accel(tool_key); Shortcut shortcut(tool_key);
if (!removed) if (!removed)
key->add(accel, source, *this); key->add(shortcut, source, *this);
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -791,12 +785,12 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
action_id, action_id,
(keycontextstr ? keycontextstr : "Any"), (keycontextstr ? keycontextstr : "Any"),
action_key); action_key);
Accelerator accel(action_key); Shortcut shortcut(action_key);
if (!removed) if (!removed)
key->add(accel, source, *this); key->add(shortcut, source, *this);
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -817,12 +811,12 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
KeyPtr key = this->wheelAction(action); KeyPtr key = this->wheelAction(action);
if (key && action_key) { if (key && action_key) {
LOG(VERBOSE, "KEYS: Shortcut for wheel action %s: %s\n", action_id, action_key); LOG(VERBOSE, "KEYS: Shortcut for wheel action %s: %s\n", action_id, action_key);
Accelerator accel(action_key); Shortcut shortcut(action_key);
if (!removed) if (!removed)
key->add(accel, source, *this); key->add(shortcut, source, *this);
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -854,12 +848,12 @@ void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
} }
LOG(VERBOSE, "KEYS: Shortcut for drag action %s: %s\n", action_id, action_key); LOG(VERBOSE, "KEYS: Shortcut for drag action %s: %s\n", action_id, action_key);
Accelerator accel(action_key); Shortcut shortcut(action_key);
if (!removed) if (!removed)
key->add(accel, source, *this); key->add(shortcut, source, *this);
else else
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
@ -904,24 +898,25 @@ void KeyboardShortcuts::exportFile(const std::string& filename)
void KeyboardShortcuts::exportKeys(XMLElement* parent, KeyType type) void KeyboardShortcuts::exportKeys(XMLElement* parent, KeyType type)
{ {
for (KeyPtr& key : m_keys) { for (KeyPtr& key : m_keys) {
// Save only user defined accelerators. // Save only user defined shortcuts.
if (key->type() != type) if (key->type() != type)
continue; continue;
for (const auto& kv : key->delsKeys()) for (const auto& kv : key->delsKeys())
if (kv.first == KeySource::UserDefined) if (kv.first == KeySource::UserDefined)
exportAccel(parent, key.get(), kv.second, true); exportShortcut(parent, key.get(), kv.second, true);
for (const auto& kv : key->addsKeys()) for (const auto& kv : key->addsKeys())
if (kv.first == KeySource::UserDefined) if (kv.first == KeySource::UserDefined)
exportAccel(parent, key.get(), kv.second, false); exportShortcut(parent, key.get(), kv.second, false);
} }
} }
void KeyboardShortcuts::exportAccel(XMLElement* parent, // static
const Key* key, void KeyboardShortcuts::exportShortcut(XMLElement* parent,
const ui::Accelerator& accel, const Key* key,
bool removed) const ui::Shortcut& shortcut,
bool removed)
{ {
XMLElement* elem = parent->InsertNewChildElement("key"); XMLElement* elem = parent->InsertNewChildElement("key");
@ -964,7 +959,7 @@ void KeyboardShortcuts::exportAccel(XMLElement* parent,
break; break;
} }
elem->SetAttribute("shortcut", accel.toString().c_str()); elem->SetAttribute("shortcut", shortcut.toString().c_str());
if (removed) if (removed)
elem->SetAttribute("removed", "true"); elem->SetAttribute("removed", "true");
@ -1062,27 +1057,28 @@ KeyPtr KeyboardShortcuts::dragAction(const WheelAction dragAction) const
return key; return key;
} }
void KeyboardShortcuts::disableAccel(const ui::Accelerator& accel, void KeyboardShortcuts::disableShortcut(const ui::Shortcut& shortcut,
const KeySource source, const KeySource source,
const KeyContext keyContext, const KeyContext keyContext,
const Key* newKey) const Key* newKey)
{ {
for (KeyPtr& key : m_keys) { for (KeyPtr& key : m_keys) {
if (key.get() != newKey && key->keycontext() == keyContext && key->hasAccel(accel) && if (key.get() != newKey && key->keycontext() == keyContext && key->hasShortcut(shortcut) &&
// Tools can contain the same keyboard shortcut // Tools can contain the same keyboard shortcut
(key->type() != KeyType::Tool || newKey == nullptr || newKey->type() != KeyType::Tool) && (key->type() != KeyType::Tool || newKey == nullptr || newKey->type() != KeyType::Tool) &&
// DragActions can share the same keyboard shortcut (e.g. to // DragActions can share the same keyboard shortcut (e.g. to
// change different values using different DragVectors) // change different values using different DragVectors)
(key->type() != KeyType::DragAction || newKey == nullptr || (key->type() != KeyType::DragAction || newKey == nullptr ||
newKey->type() != KeyType::DragAction)) { newKey->type() != KeyType::DragAction)) {
key->disableAccel(accel, source); key->disableShortcut(shortcut, source);
} }
} }
} }
KeyContext KeyboardShortcuts::getCurrentKeyContext() const // static
KeyContext KeyboardShortcuts::getCurrentKeyContext()
{ {
auto ctx = UIContext::instance(); auto* ctx = UIContext::instance();
Doc* doc = ctx->activeDocument(); Doc* doc = ctx->activeDocument();
if (doc && doc->isMaskVisible() && if (doc && doc->isMaskVisible() &&
// The active key context will be the selectedTool() (in the // The active key context will be the selectedTool() (in the
@ -1116,7 +1112,7 @@ bool KeyboardShortcuts::getCommandFromKeyMessage(const Message* msg,
int n = (contexts[0] != contexts[1] ? 2 : 1); int n = (contexts[0] != contexts[1] ? 2 : 1);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
for (KeyPtr& key : m_keys) { for (KeyPtr& key : m_keys) {
if (key->type() == KeyType::Command && key->isPressed(msg, *this, contexts[i])) { if (key->type() == KeyType::Command && key->isPressed(msg, contexts[i])) {
if (command) if (command)
*command = key->command(); *command = key->command();
if (params) if (params)
@ -1168,12 +1164,12 @@ WheelAction KeyboardShortcuts::getWheelActionFromMouseMessage(const KeyContext c
const ui::Message* msg) const ui::Message* msg)
{ {
WheelAction wheelAction = WheelAction::None; WheelAction wheelAction = WheelAction::None;
const ui::Accelerator* bestAccel = nullptr; const ui::Shortcut* bestShortcut = nullptr;
for (const KeyPtr& key : m_keys) { for (const KeyPtr& key : m_keys) {
if (key->type() == KeyType::WheelAction && key->keycontext() == context) { if (key->type() == KeyType::WheelAction && key->keycontext() == context) {
const ui::Accelerator* accel = key->isPressed(msg, *this); const ui::Shortcut* shortcut = key->isPressed(msg);
if ((accel) && (!bestAccel || bestAccel->modifiers() < accel->modifiers())) { if ((shortcut) && (!bestShortcut || bestShortcut->modifiers() < shortcut->modifiers())) {
bestAccel = accel; bestShortcut = shortcut;
wheelAction = key->wheelAction(); wheelAction = key->wheelAction();
} }
} }
@ -1181,15 +1177,14 @@ WheelAction KeyboardShortcuts::getWheelActionFromMouseMessage(const KeyContext c
return wheelAction; return wheelAction;
} }
Keys KeyboardShortcuts::getDragActionsFromKeyMessage(const KeyContext context, Keys KeyboardShortcuts::getDragActionsFromKeyMessage(const ui::Message* msg)
const ui::Message* msg)
{ {
KeyPtr bestKey = nullptr; KeyPtr bestKey = nullptr;
Keys keys; Keys keys;
for (const KeyPtr& key : m_keys) { for (const KeyPtr& key : m_keys) {
if (key->type() == KeyType::DragAction) { if (key->type() == KeyType::DragAction) {
const ui::Accelerator* accel = key->isPressed(msg, *this); const ui::Shortcut* shortcut = key->isPressed(msg);
if (accel) { if (shortcut) {
keys.push_back(key); keys.push_back(key);
} }
} }
@ -1199,11 +1194,9 @@ Keys KeyboardShortcuts::getDragActionsFromKeyMessage(const KeyContext context,
bool KeyboardShortcuts::hasMouseWheelCustomization() const bool KeyboardShortcuts::hasMouseWheelCustomization() const
{ {
for (const KeyPtr& key : m_keys) { return std::any_of(m_keys.begin(), m_keys.end(), [](const KeyPtr& key) {
if (key->type() == KeyType::WheelAction && key->hasUserDefinedAccels()) return (key->type() == KeyType::WheelAction && key->hasUserDefinedShortcuts());
return true; });
}
return false;
} }
void KeyboardShortcuts::clearMouseWheelKeys() void KeyboardShortcuts::clearMouseWheelKeys()
@ -1245,38 +1238,38 @@ void KeyboardShortcuts::setDefaultMouseWheelKeys(const bool zoomWithWheel)
KeyPtr key; KeyPtr key;
key = std::make_shared<Key>(WheelAction::Zoom); key = std::make_shared<Key>(WheelAction::Zoom);
key->add(Accelerator(zoomWithWheel ? kKeyNoneModifier : kKeyCtrlModifier, kKeyNil, 0), key->add(Shortcut(zoomWithWheel ? kKeyNoneModifier : kKeyCtrlModifier, kKeyNil, 0),
KeySource::Original, KeySource::Original,
*this); *this);
m_keys.push_back(key); m_keys.push_back(key);
if (!zoomWithWheel) { if (!zoomWithWheel) {
key = std::make_shared<Key>(WheelAction::VScroll); key = std::make_shared<Key>(WheelAction::VScroll);
key->add(Accelerator(kKeyNoneModifier, kKeyNil, 0), KeySource::Original, *this); key->add(Shortcut(kKeyNoneModifier, kKeyNil, 0), KeySource::Original, *this);
m_keys.push_back(key); m_keys.push_back(key);
} }
key = std::make_shared<Key>(WheelAction::HScroll); key = std::make_shared<Key>(WheelAction::HScroll);
key->add(Accelerator(kKeyShiftModifier, kKeyNil, 0), KeySource::Original, *this); key->add(Shortcut(kKeyShiftModifier, kKeyNil, 0), KeySource::Original, *this);
m_keys.push_back(key); m_keys.push_back(key);
key = std::make_shared<Key>(WheelAction::FgColor); key = std::make_shared<Key>(WheelAction::FgColor);
key->add(Accelerator(kKeyAltModifier, kKeyNil, 0), KeySource::Original, *this); key->add(Shortcut(kKeyAltModifier, kKeyNil, 0), KeySource::Original, *this);
m_keys.push_back(key); m_keys.push_back(key);
key = std::make_shared<Key>(WheelAction::BgColor); key = std::make_shared<Key>(WheelAction::BgColor);
key->add(Accelerator((KeyModifiers)(kKeyAltModifier | kKeyShiftModifier), kKeyNil, 0), key->add(Shortcut((KeyModifiers)(kKeyAltModifier | kKeyShiftModifier), kKeyNil, 0),
KeySource::Original, KeySource::Original,
*this); *this);
m_keys.push_back(key); m_keys.push_back(key);
if (zoomWithWheel) { if (zoomWithWheel) {
key = std::make_shared<Key>(WheelAction::BrushSize); key = std::make_shared<Key>(WheelAction::BrushSize);
key->add(Accelerator(kKeyCtrlModifier, kKeyNil, 0), KeySource::Original, *this); key->add(Shortcut(kKeyCtrlModifier, kKeyNil, 0), KeySource::Original, *this);
m_keys.push_back(key); m_keys.push_back(key);
key = std::make_shared<Key>(WheelAction::Frame); key = std::make_shared<Key>(WheelAction::Frame);
key->add(Accelerator((KeyModifiers)(kKeyCtrlModifier | kKeyShiftModifier), kKeyNil, 0), key->add(Shortcut((KeyModifiers)(kKeyCtrlModifier | kKeyShiftModifier), kKeyNil, 0),
KeySource::Original, KeySource::Original,
*this); *this);
m_keys.push_back(key); m_keys.push_back(key);
@ -1321,9 +1314,9 @@ std::string key_tooltip(const char* str, const app::Key* key)
std::string res; std::string res;
if (str) if (str)
res += str; res += str;
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
res += " ("; res += " (";
res += key->accels().front().toString(); res += key->shortcuts().front().toString();
res += ")"; res += ")";
} }
return res; return res;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020-2024 Igara Studio S.A. // Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -50,20 +50,20 @@ public:
KeyPtr tool(tools::Tool* tool) const; KeyPtr tool(tools::Tool* tool) const;
KeyPtr quicktool(tools::Tool* tool) const; KeyPtr quicktool(tools::Tool* tool) const;
KeyPtr action(const KeyAction action, const KeyContext keyContext = KeyContext::Any) const; KeyPtr action(const KeyAction action, const KeyContext keyContext = KeyContext::Any) const;
KeyPtr wheelAction(const WheelAction action) const; KeyPtr wheelAction(WheelAction action) const;
KeyPtr dragAction(const WheelAction action) const; KeyPtr dragAction(WheelAction action) const;
void disableAccel(const ui::Accelerator& accel, void disableShortcut(const ui::Shortcut& shortcut,
const KeySource source, KeySource source,
const KeyContext keyContext, KeyContext keyContext,
const Key* newKey); const Key* newKey);
KeyContext getCurrentKeyContext() const; static KeyContext getCurrentKeyContext();
bool getCommandFromKeyMessage(const ui::Message* msg, Command** command, Params* params); bool getCommandFromKeyMessage(const ui::Message* msg, Command** command, Params* params);
tools::Tool* getCurrentQuicktool(tools::Tool* currentTool); tools::Tool* getCurrentQuicktool(tools::Tool* currentTool);
KeyAction getCurrentActionModifiers(KeyContext context); KeyAction getCurrentActionModifiers(KeyContext context);
WheelAction getWheelActionFromMouseMessage(const KeyContext context, const ui::Message* msg); WheelAction getWheelActionFromMouseMessage(KeyContext context, const ui::Message* msg);
Keys getDragActionsFromKeyMessage(const KeyContext context, const ui::Message* msg); Keys getDragActionsFromKeyMessage(const ui::Message* msg);
bool hasMouseWheelCustomization() const; bool hasMouseWheelCustomization() const;
void clearMouseWheelKeys(); void clearMouseWheelKeys();
void addMissingMouseWheelKeys(); void addMissingMouseWheelKeys();
@ -77,10 +77,10 @@ public:
private: private:
void exportKeys(tinyxml2::XMLElement* parent, KeyType type); void exportKeys(tinyxml2::XMLElement* parent, KeyType type);
void exportAccel(tinyxml2::XMLElement* parent, static void exportShortcut(tinyxml2::XMLElement* parent,
const Key* key, const Key* key,
const ui::Accelerator& accel, const ui::Shortcut& shortcut,
bool removed); bool removed);
mutable Keys m_keys; mutable Keys m_keys;
}; };

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,7 +9,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/ui/select_accelerator.h" #include "app/ui/select_shortcut.h"
#include "app/ui/key.h" #include "app/ui/key.h"
#include "app/ui/keyboard_shortcuts.h" #include "app/ui/keyboard_shortcuts.h"
@ -23,23 +23,23 @@ namespace app {
using namespace ui; using namespace ui;
class SelectAccelerator::KeyField : public ui::Entry { class SelectShortcut::KeyField : public ui::Entry {
public: public:
KeyField(const Accelerator& accel) : ui::Entry(256, "") KeyField(const Shortcut& shortcut) : ui::Entry(256, "")
{ {
setTranslateDeadKeys(false); setTranslateDeadKeys(false);
setExpansive(true); setExpansive(true);
setFocusMagnet(true); setFocusMagnet(true);
setAccel(accel); setShortcut(shortcut);
} }
void setAccel(const Accelerator& accel) void setShortcut(const Shortcut& shortcut)
{ {
m_accel = accel; m_shortcut = shortcut;
updateText(); updateText();
} }
obs::signal<void(const ui::Accelerator*)> AccelChange; obs::signal<void(const ui::Shortcut*)> ShortcutChange;
protected: protected:
bool onProcessMessage(Message* msg) override bool onProcessMessage(Message* msg) override
@ -56,19 +56,19 @@ protected:
if (keymsg->scancode() == kKeySpace) if (keymsg->scancode() == kKeySpace)
modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier); modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier);
m_accel = Accelerator( m_shortcut = Shortcut(
modifiers, modifiers,
keymsg->scancode(), keymsg->scancode(),
keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0); keymsg->unicodeChar() > 32 ? std::tolower(keymsg->unicodeChar()) : 0);
// Convert the accelerator to a string, and parse it // Convert the shortcut to a string, and parse it
// again. Just to obtain the exact accelerator we'll read // again. Just to obtain the exact shortcut we'll read
// when we import the gui.xml file or an .aseprite-keys file. // when we import the gui.xml file or an .aseprite-keys file.
m_accel = Accelerator(m_accel.toString()); m_shortcut = Shortcut(m_shortcut.toString());
updateText(); updateText();
AccelChange(&m_accel); ShortcutChange(&m_shortcut);
return true; return true;
} }
break; break;
@ -78,20 +78,20 @@ protected:
void updateText() void updateText()
{ {
setText( setText(Shortcut(kKeyNoneModifier, m_shortcut.scancode(), m_shortcut.unicodeChar()).toString());
Accelerator(kKeyNoneModifier, m_accel.scancode(), m_accel.unicodeChar()).toString().c_str());
} }
Accelerator m_accel; private:
Shortcut m_shortcut;
}; };
SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel, SelectShortcut::SelectShortcut(const ui::Shortcut& shortcut,
const KeyContext keyContext, const KeyContext keyContext,
const KeyboardShortcuts& currentKeys) const KeyboardShortcuts& currentKeys)
: m_keyField(new KeyField(accel)) : m_keyField(new KeyField(shortcut))
, m_keyContext(keyContext) , m_keyContext(keyContext)
, m_currentKeys(currentKeys) , m_currentKeys(currentKeys)
, m_accel(accel) , m_shortcut(shortcut)
, m_ok(false) , m_ok(false)
, m_modified(false) , m_modified(false)
{ {
@ -107,7 +107,7 @@ SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel,
space()->Click.connect([this] { onModifierChange(kKeySpaceModifier, space()); }); space()->Click.connect([this] { onModifierChange(kKeySpaceModifier, space()); });
win()->Click.connect([this] { onModifierChange(kKeyWinModifier, win()); }); win()->Click.connect([this] { onModifierChange(kKeyWinModifier, win()); });
m_keyField->AccelChange.connect(&SelectAccelerator::onAccelChange, this); m_keyField->ShortcutChange.connect(&SelectShortcut::onShortcutChange, this);
clearButton()->Click.connect([this] { onClear(); }); clearButton()->Click.connect([this] { onClear(); });
okButton()->Click.connect([this] { onOK(); }); okButton()->Click.connect([this] { onOK(); });
cancelButton()->Click.connect([this] { onCancel(); }); cancelButton()->Click.connect([this] { onCancel(); });
@ -115,63 +115,63 @@ SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel,
addChild(&m_tooltipManager); addChild(&m_tooltipManager);
} }
void SelectAccelerator::onModifierChange(KeyModifiers modifier, CheckBox* checkbox) void SelectShortcut::onModifierChange(KeyModifiers modifier, CheckBox* checkbox)
{ {
bool state = (checkbox->isSelected()); bool state = (checkbox->isSelected());
KeyModifiers modifiers = m_accel.modifiers(); KeyModifiers modifiers = m_shortcut.modifiers();
KeyScancode scancode = m_accel.scancode(); KeyScancode scancode = m_shortcut.scancode();
int unicodeChar = m_accel.unicodeChar(); int unicodeChar = m_shortcut.unicodeChar();
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_accel = Accelerator(modifiers, scancode, unicodeChar); m_shortcut = Shortcut(modifiers, scancode, unicodeChar);
m_keyField->setAccel(m_accel); m_keyField->setShortcut(m_shortcut);
m_keyField->requestFocus(); m_keyField->requestFocus();
updateAssignedTo(); updateAssignedTo();
} }
void SelectAccelerator::onAccelChange(const ui::Accelerator* accel) void SelectShortcut::onShortcutChange(const ui::Shortcut* shortcut)
{ {
m_accel = *accel; m_shortcut = *shortcut;
updateModifiers(); updateModifiers();
updateAssignedTo(); updateAssignedTo();
} }
void SelectAccelerator::onClear() void SelectShortcut::onClear()
{ {
m_accel = Accelerator(kKeyNoneModifier, kKeyNil, 0); m_shortcut = Shortcut(kKeyNoneModifier, kKeyNil, 0);
m_keyField->setAccel(m_accel); m_keyField->setShortcut(m_shortcut);
updateModifiers(); updateModifiers();
updateAssignedTo(); updateAssignedTo();
m_keyField->requestFocus(); m_keyField->requestFocus();
} }
void SelectAccelerator::onOK() void SelectShortcut::onOK()
{ {
m_ok = true; m_ok = true;
m_modified = (m_origAccel != m_accel); m_modified = (m_origShortcut != m_shortcut);
closeWindow(NULL); closeWindow(NULL);
} }
void SelectAccelerator::onCancel() void SelectShortcut::onCancel()
{ {
closeWindow(NULL); closeWindow(NULL);
} }
void SelectAccelerator::updateModifiers() void SelectShortcut::updateModifiers()
{ {
alt()->setSelected(m_accel.modifiers() & kKeyAltModifier ? true : false); alt()->setSelected((m_shortcut.modifiers() & kKeyAltModifier) == kKeyAltModifier);
ctrl()->setSelected(m_accel.modifiers() & kKeyCtrlModifier ? true : false); ctrl()->setSelected((m_shortcut.modifiers() & kKeyCtrlModifier) == kKeyCtrlModifier);
shift()->setSelected(m_accel.modifiers() & kKeyShiftModifier ? true : false); shift()->setSelected((m_shortcut.modifiers() & kKeyShiftModifier) == kKeyShiftModifier);
space()->setSelected(m_accel.modifiers() & kKeySpaceModifier ? true : false); space()->setSelected((m_shortcut.modifiers() & kKeySpaceModifier) == kKeySpaceModifier);
#if __APPLE__ #if __APPLE__
win()->setVisible(false); win()->setVisible(false);
cmd()->setSelected(m_accel.modifiers() & kKeyCmdModifier ? true : false); cmd()->setSelected((m_shortcut.modifiers() & kKeyCmdModifier) == kKeyCmdModifier);
#else #else
#if __linux__ #if __linux__
win()->setText(kWinKeyName); win()->setText(kWinKeyName);
@ -180,17 +180,17 @@ void SelectAccelerator::updateModifiers()
"Also known as Windows key, logo key,\ncommand key, or system key.", "Also known as Windows key, logo key,\ncommand key, or system key.",
TOP); TOP);
#endif #endif
win()->setSelected(m_accel.modifiers() & kKeyWinModifier ? true : false); win()->setSelected((m_shortcut.modifiers() & kKeyWinModifier) == kKeyWinModifier);
cmd()->setVisible(false); cmd()->setVisible(false);
#endif #endif
} }
void SelectAccelerator::updateAssignedTo() void SelectShortcut::updateAssignedTo()
{ {
std::string res = "None"; std::string res = "None";
for (const KeyPtr& key : m_currentKeys) { for (const KeyPtr& key : m_currentKeys) {
if (key->keycontext() == m_keyContext && key->hasAccel(m_accel)) { if (key->keycontext() == m_keyContext && key->hasShortcut(m_shortcut)) {
res = key->triggerString(); res = key->triggerString();
break; break;
} }

View File

@ -1,35 +1,36 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#ifndef APP_UI_SELECT_ACCELERATOR_H_INCLUDED #ifndef APP_UI_SELECT_SHORTCUT_H_INCLUDED
#define APP_UI_SELECT_ACCELERATOR_H_INCLUDED #define APP_UI_SELECT_SHORTCUT_H_INCLUDED
#pragma once #pragma once
#include "app/ui/key_context.h" #include "app/ui/key_context.h"
#include "ui/accelerator.h" #include "ui/shortcut.h"
#include "ui/tooltips.h" #include "ui/tooltips.h"
#include "select_accelerator.xml.h" #include "select_shortcut.xml.h"
namespace app { namespace app {
class KeyboardShortcuts; class KeyboardShortcuts;
class SelectAccelerator : public app::gen::SelectAccelerator { class SelectShortcut : public app::gen::SelectShortcut {
public: public:
SelectAccelerator(const ui::Accelerator& accelerator, SelectShortcut(const ui::Shortcut& shortcut,
const KeyContext keyContext, KeyContext keyContext,
const KeyboardShortcuts& currentKeys); const KeyboardShortcuts& currentKeys);
bool isOK() const { return m_ok; } bool isOK() const { return m_ok; }
bool isModified() const { return m_modified; } bool isModified() const { return m_modified; }
const ui::Accelerator& accel() const { return m_accel; } const ui::Shortcut& shortcut() const { return m_shortcut; }
private: private:
void onModifierChange(ui::KeyModifiers modifier, ui::CheckBox* checkbox); void onModifierChange(ui::KeyModifiers modifier, ui::CheckBox* checkbox);
void onAccelChange(const ui::Accelerator* accel); void onShortcutChange(const ui::Shortcut* shortcut);
void onClear(); void onClear();
void onOK(); void onOK();
void onCancel(); void onCancel();
@ -42,8 +43,8 @@ private:
KeyField* m_keyField; KeyField* m_keyField;
KeyContext m_keyContext; KeyContext m_keyContext;
const KeyboardShortcuts& m_currentKeys; const KeyboardShortcuts& m_currentKeys;
ui::Accelerator m_origAccel; ui::Shortcut m_origShortcut;
ui::Accelerator m_accel; ui::Shortcut m_shortcut;
bool m_ok; bool m_ok;
bool m_modified; bool m_modified;
}; };

View File

@ -1577,13 +1577,13 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev)
} }
// Draw the keyboard shortcut // Draw the keyboard shortcut
else if (AppMenuItem* appMenuItem = dynamic_cast<AppMenuItem*>(widget)) { else if (AppMenuItem* appMenuItem = dynamic_cast<AppMenuItem*>(widget)) {
if (appMenuItem->key() && !appMenuItem->key()->accels().empty()) { if (appMenuItem->key() && !appMenuItem->key()->shortcuts().empty()) {
int old_align = appMenuItem->align(); int old_align = appMenuItem->align();
pos = bounds; pos = bounds;
pos.w -= widget->childSpacing() / 4; pos.w -= widget->childSpacing() / 4;
std::string buf = appMenuItem->key()->accels().front().toString(); std::string buf = appMenuItem->key()->shortcuts().front().toString();
widget->setAlign(RIGHT | MIDDLE); widget->setAlign(RIGHT | MIDDLE);
drawText(g, buf.c_str(), fg, ColorNone, widget, pos, widget->align(), 0); drawText(g, buf.c_str(), fg, ColorNone, widget, pos, widget->align(), 0);

View File

@ -536,9 +536,9 @@ public:
// Tool shortcut // Tool shortcut
KeyPtr key = KeyboardShortcuts::instance()->tool(tool); KeyPtr key = KeyboardShortcuts::instance()->tool(tool);
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
add(theme->parts.iconKey(), true); add(theme->parts.iconKey(), true);
m_indicators->addTextIndicator(key->accels().front().toString().c_str()); m_indicators->addTextIndicator(key->shortcuts().front().toString().c_str());
} }
return *this; return *this;
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -110,11 +110,11 @@ std::string AniControls::getTooltipFor(int index) const
tooltip = cmd->friendlyName(); tooltip = cmd->friendlyName();
KeyPtr key = KeyboardShortcuts::instance()->command(cmd->id().c_str()); KeyPtr key = KeyboardShortcuts::instance()->command(cmd->id().c_str());
if (!key || key->accels().empty()) if (!key || key->shortcuts().empty())
key = KeyboardShortcuts::instance()->command(cmd->id().c_str(), Params(), KeyContext::Normal); key = KeyboardShortcuts::instance()->command(cmd->id().c_str(), Params(), KeyContext::Normal);
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
tooltip += "\n\n" + Strings::ani_controls_shortcut() + " "; tooltip += "\n\n" + Strings::ani_controls_shortcut() + " ";
tooltip += key->accels().front().toString(); tooltip += key->shortcuts().front().toString();
} }
if (index == ACTION_PLAY) { if (index == ACTION_PLAY) {

View File

@ -571,9 +571,9 @@ void ToolBar::openTipWindow(int group_index, Tool* tool)
// Tool shortcut // Tool shortcut
KeyPtr key = KeyboardShortcuts::instance()->tool(tool); KeyPtr key = KeyboardShortcuts::instance()->tool(tool);
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
tooltip += "\n\n"; tooltip += "\n\n";
tooltip += Strings::tools_shortcut(key->accels().front().toString()); tooltip += Strings::tools_shortcut(key->shortcuts().front().toString());
} }
} }
else if (group_index == PreviewVisibilityIndex) { else if (group_index == PreviewVisibilityIndex) {

View File

@ -1,5 +1,5 @@
# Aseprite UI Library # Aseprite UI Library
# Copyright (C) 2019-2024 Igara Studio S.A. # Copyright (C) 2019-2025 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello # Copyright (C) 2001-2018 David Capello
if(WIN32) if(WIN32)
@ -7,7 +7,6 @@ if(WIN32)
endif() endif()
add_library(ui-lib add_library(ui-lib
accelerator.cpp
alert.cpp alert.cpp
app_state.cpp app_state.cpp
box.cpp box.cpp
@ -44,6 +43,7 @@ add_library(ui-lib
scroll_bar.cpp scroll_bar.cpp
scroll_helper.cpp scroll_helper.cpp
separator.cpp separator.cpp
shortcut.cpp
size_hint_event.cpp size_hint_event.cpp
slider.cpp slider.cpp
splitter.cpp splitter.cpp

View File

@ -1,64 +0,0 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#define TEST_GUI
#include "tests/app_test.h"
using namespace ui;
TEST(Accelerator, Parser)
{
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyF1, '\0'), Accelerator("F1"));
EXPECT_EQ(Accelerator(kKeyAltModifier, kKeyQ, 'q'), Accelerator("Alt+Q"));
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyQ, 'q'), Accelerator("Ctrl+Q"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinus, '-'), Accelerator("-"));
EXPECT_EQ(Accelerator(kKeyShiftModifier, kKeyMinus, '-'), Accelerator("Shift+-"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyEquals, '='), Accelerator("="));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyNil, '+'), Accelerator("+"));
EXPECT_EQ(Accelerator(kKeyShiftModifier, kKeyNil, '+'), Accelerator("Shift++"));
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyNil, '+'), Accelerator("Ctrl++"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinusPad, 0), Accelerator("Minus Pad"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinusPad, 0), Accelerator("- Pad"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyPlusPad, 0), Accelerator("Plus Pad"));
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyPlusPad, 0), Accelerator("+ Pad"));
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyPlusPad, 0), Accelerator("Ctrl++ Pad"));
}
TEST(Accelerator, ToString)
{
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyF1, '\0').toString(), Accelerator("F1").toString());
EXPECT_EQ(Accelerator(kKeyAltModifier, kKeyQ, 'q').toString(), Accelerator("Alt+Q").toString());
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyQ, 'q').toString(), Accelerator("Ctrl+Q").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinus, '-').toString(), Accelerator("-").toString());
EXPECT_EQ(Accelerator(kKeyShiftModifier, kKeyMinus, '-').toString(),
Accelerator("Shift+-").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyEquals, '=').toString(), Accelerator("=").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyNil, '+').toString(), Accelerator("+").toString());
EXPECT_EQ(Accelerator(kKeyShiftModifier, kKeyNil, '+').toString(),
Accelerator("Shift++").toString());
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyNil, '+').toString(),
Accelerator("Ctrl++").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinusPad, 0).toString(),
Accelerator("Minus Pad").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyMinusPad, 0).toString(),
Accelerator("- Pad").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyPlusPad, 0).toString(),
Accelerator("Plus Pad").toString());
EXPECT_EQ(Accelerator(kKeyNoneModifier, kKeyPlusPad, 0).toString(),
Accelerator("+ Pad").toString());
EXPECT_EQ(Accelerator(kKeyCtrlModifier, kKeyPlusPad, 0).toString(),
Accelerator("Ctrl++ Pad").toString());
EXPECT_EQ("- Pad", Accelerator(kKeyNoneModifier, kKeyMinusPad, 0).toString());
EXPECT_EQ("- Pad", Accelerator("Minus Pad").toString());
EXPECT_EQ("- Pad", Accelerator("- Pad").toString());
EXPECT_EQ("+ Pad", Accelerator(kKeyNoneModifier, kKeyPlusPad, 0).toString());
EXPECT_EQ("+ Pad", Accelerator("Plus Pad").toString());
EXPECT_EQ("+ Pad", Accelerator("+ Pad").toString());
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2020-2024 Igara Studio S.A. // Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -9,7 +9,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "ui/accelerator.h" #include "ui/shortcut.h"
#include "base/debug.h" #include "base/debug.h"
#include "base/replace_string.h" #include "base/replace_string.h"
@ -144,18 +144,18 @@ int scancode_to_string_size = sizeof(scancode_to_string) / sizeof(scancode_to_st
} // anonymous namespace } // anonymous namespace
Accelerator::Accelerator() : m_modifiers(kKeyNoneModifier), m_scancode(kKeyNil), m_unicodeChar(0) Shortcut::Shortcut() : m_modifiers(kKeyNoneModifier), m_scancode(kKeyNil), m_unicodeChar(0)
{ {
} }
Accelerator::Accelerator(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) Shortcut::Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
: m_modifiers(modifiers) : m_modifiers(modifiers)
, m_scancode(scancode) , m_scancode(scancode)
, m_unicodeChar(unicodeChar) , m_unicodeChar(unicodeChar)
{ {
} }
Accelerator::Accelerator(const std::string& str) Shortcut::Shortcut(const std::string& str)
: m_modifiers(kKeyNoneModifier) : m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil) , m_scancode(kKeyNil)
, m_unicodeChar(0) , m_unicodeChar(0)
@ -275,18 +275,18 @@ Accelerator::Accelerator(const std::string& str)
} }
} }
bool Accelerator::operator==(const Accelerator& other) const bool Shortcut::operator==(const Shortcut& other) const
{ {
// TODO improve this, avoid conversion to std::string // TODO improve this, avoid conversion to std::string
return toString() == other.toString(); return toString() == other.toString();
} }
bool Accelerator::isEmpty() const bool Shortcut::isEmpty() const
{ {
return (m_modifiers == kKeyNoneModifier && m_scancode == kKeyNil && m_unicodeChar == 0); return (m_modifiers == kKeyNoneModifier && m_scancode == kKeyNil && m_unicodeChar == 0);
} }
std::string Accelerator::toString() const std::string Shortcut::toString() const
{ {
std::string buf; std::string buf;
@ -321,13 +321,13 @@ std::string Accelerator::toString() const
return buf; return buf;
} }
bool Accelerator::isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const bool Shortcut::isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const
{ {
return ((scancode && *this == Accelerator(modifiers, scancode, 0)) || return ((scancode && *this == Shortcut(modifiers, scancode, 0)) ||
(unicodeChar && *this == Accelerator(modifiers, kKeyNil, unicodeChar))); (unicodeChar && *this == Shortcut(modifiers, kKeyNil, unicodeChar)));
} }
bool Accelerator::isPressed() const bool Shortcut::isPressed() const
{ {
os::SystemRef sys = os::System::instance(); os::SystemRef sys = os::System::instance();
if (!sys) if (!sys)
@ -349,7 +349,7 @@ bool Accelerator::isPressed() const
return false; return false;
} }
bool Accelerator::isLooselyPressed() const bool Shortcut::isLooselyPressed() const
{ {
os::SystemRef sys = os::System::instance(); os::SystemRef sys = os::System::instance();
if (!sys) if (!sys)
@ -378,22 +378,22 @@ bool Accelerator::isLooselyPressed() const
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Accelerators // Shortcuts
bool Accelerators::has(const Accelerator& accel) const bool Shortcuts::has(const Shortcut& shortcut) const
{ {
return (std::find(begin(), end(), accel) != end()); return (std::find(begin(), end(), shortcut) != end());
} }
void Accelerators::add(const Accelerator& accel) void Shortcuts::add(const Shortcut& shortcut)
{ {
if (!has(accel)) if (!has(shortcut))
m_list.push_back(accel); m_list.push_back(shortcut);
} }
void Accelerators::remove(const Accelerator& accel) void Shortcuts::remove(const Shortcut& shortcut)
{ {
auto it = std::find(begin(), end(), accel); auto it = std::find(begin(), end(), shortcut);
if (it != end()) if (it != end())
m_list.erase(it); m_list.erase(it);
} }

View File

@ -1,11 +1,12 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
#ifndef UI_ACCELERATOR_H_INCLUDED #ifndef UI_SHORTCUT_H_INCLUDED
#define UI_ACCELERATOR_H_INCLUDED #define UI_SHORTCUT_H_INCLUDED
#pragma once #pragma once
#include <string> #include <string>
@ -17,13 +18,12 @@ namespace ui {
extern const char* kWinKeyName; extern const char* kWinKeyName;
// TODO rename this class to Shortcut class Shortcut {
class Accelerator {
public: public:
Accelerator(); Shortcut();
Accelerator(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar); Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar);
// Convert string like "Ctrl+Q" or "Alt+X" into an accelerator. // Convert string like "Ctrl+Q" or "Alt+X" into an shortcut.
explicit Accelerator(const std::string& str); explicit Shortcut(const std::string& str);
bool isEmpty() const; bool isEmpty() const;
std::string toString() const; std::string toString() const;
@ -38,8 +38,8 @@ public:
// modifiers are allowed too). // modifiers are allowed too).
bool isLooselyPressed() const; bool isLooselyPressed() const;
bool operator==(const Accelerator& other) const; bool operator==(const Shortcut& other) const;
bool operator!=(const Accelerator& other) const { return !operator==(other); } bool operator!=(const Shortcut& other) const { return !operator==(other); }
KeyModifiers modifiers() const { return m_modifiers; } KeyModifiers modifiers() const { return m_modifiers; }
KeyScancode scancode() const { return m_scancode; } KeyScancode scancode() const { return m_scancode; }
@ -51,10 +51,9 @@ private:
int m_unicodeChar; int m_unicodeChar;
}; };
// TODO rename this class to Shortcuts class Shortcuts {
class Accelerators {
public: public:
typedef std::vector<Accelerator> List; typedef std::vector<Shortcut> List;
typedef List::iterator iterator; typedef List::iterator iterator;
typedef List::const_iterator const_iterator; typedef List::const_iterator const_iterator;
@ -66,16 +65,16 @@ public:
bool empty() const { return m_list.empty(); } bool empty() const { return m_list.empty(); }
std::size_t size() const { return m_list.size(); } std::size_t size() const { return m_list.size(); }
const ui::Accelerator& front() const { return m_list.front(); } const ui::Shortcut& front() const { return m_list.front(); }
const ui::Accelerator& operator[](int index) const { return m_list[index]; } const ui::Shortcut& operator[](int index) const { return m_list[index]; }
ui::Accelerator& operator[](int index) { return m_list[index]; } ui::Shortcut& operator[](int index) { return m_list[index]; }
void clear() { m_list.clear(); } void clear() { m_list.clear(); }
bool has(const Accelerator& accel) const; bool has(const Shortcut& shortcut) const;
void add(const Accelerator& accel); void add(const Shortcut& shortcut);
void remove(const Accelerator& accel); void remove(const Shortcut& shortcut);
private: private:
List m_list; List m_list;

View File

@ -0,0 +1,59 @@
// Aseprite UI Library
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#define TEST_GUI
#include "tests/app_test.h"
using namespace ui;
TEST(Shortcut, Parser)
{
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyF1, '\0'), Shortcut("F1"));
EXPECT_EQ(Shortcut(kKeyAltModifier, kKeyQ, 'q'), Shortcut("Alt+Q"));
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyQ, 'q'), Shortcut("Ctrl+Q"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinus, '-'), Shortcut("-"));
EXPECT_EQ(Shortcut(kKeyShiftModifier, kKeyMinus, '-'), Shortcut("Shift+-"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyEquals, '='), Shortcut("="));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyNil, '+'), Shortcut("+"));
EXPECT_EQ(Shortcut(kKeyShiftModifier, kKeyNil, '+'), Shortcut("Shift++"));
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyNil, '+'), Shortcut("Ctrl++"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinusPad, 0), Shortcut("Minus Pad"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinusPad, 0), Shortcut("- Pad"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyPlusPad, 0), Shortcut("Plus Pad"));
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyPlusPad, 0), Shortcut("+ Pad"));
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyPlusPad, 0), Shortcut("Ctrl++ Pad"));
}
TEST(Shortcut, ToString)
{
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyF1, '\0').toString(), Shortcut("F1").toString());
EXPECT_EQ(Shortcut(kKeyAltModifier, kKeyQ, 'q').toString(), Shortcut("Alt+Q").toString());
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyQ, 'q').toString(), Shortcut("Ctrl+Q").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinus, '-').toString(), Shortcut("-").toString());
EXPECT_EQ(Shortcut(kKeyShiftModifier, kKeyMinus, '-').toString(), Shortcut("Shift+-").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyEquals, '=').toString(), Shortcut("=").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyNil, '+').toString(), Shortcut("+").toString());
EXPECT_EQ(Shortcut(kKeyShiftModifier, kKeyNil, '+').toString(), Shortcut("Shift++").toString());
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyNil, '+').toString(), Shortcut("Ctrl++").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinusPad, 0).toString(),
Shortcut("Minus Pad").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyMinusPad, 0).toString(), Shortcut("- Pad").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyPlusPad, 0).toString(), Shortcut("Plus Pad").toString());
EXPECT_EQ(Shortcut(kKeyNoneModifier, kKeyPlusPad, 0).toString(), Shortcut("+ Pad").toString());
EXPECT_EQ(Shortcut(kKeyCtrlModifier, kKeyPlusPad, 0).toString(),
Shortcut("Ctrl++ Pad").toString());
EXPECT_EQ("- Pad", Shortcut(kKeyNoneModifier, kKeyMinusPad, 0).toString());
EXPECT_EQ("- Pad", Shortcut("Minus Pad").toString());
EXPECT_EQ("- Pad", Shortcut("- Pad").toString());
EXPECT_EQ("+ Pad", Shortcut(kKeyNoneModifier, kKeyPlusPad, 0).toString());
EXPECT_EQ("+ Pad", Shortcut("Plus Pad").toString());
EXPECT_EQ("+ Pad", Shortcut("+ Pad").toString());
}

View File

@ -9,7 +9,6 @@
#define UI_UI_H_INCLUDED #define UI_UI_H_INCLUDED
#pragma once #pragma once
#include "ui/accelerator.h"
#include "ui/alert.h" #include "ui/alert.h"
#include "ui/app_state.h" #include "ui/app_state.h"
#include "ui/base.h" #include "ui/base.h"
@ -57,6 +56,7 @@
#include "ui/scroll_bar.h" #include "ui/scroll_bar.h"
#include "ui/scroll_helper.h" #include "ui/scroll_helper.h"
#include "ui/separator.h" #include "ui/separator.h"
#include "ui/shortcut.h"
#include "ui/size_hint_event.h" #include "ui/size_hint_event.h"
#include "ui/slider.h" #include "ui/slider.h"
#include "ui/splitter.h" #include "ui/splitter.h"