aseprite/src/ui/shortcut.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

379 lines
8.8 KiB
C++
Raw Normal View History

// Aseprite UI Library
// Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
2007-09-19 07:57:02 +08:00
#ifdef HAVE_CONFIG_H
2009-07-13 04:29:16 +08:00
#include "config.h"
#endif
2009-07-13 04:29:16 +08:00
#include "ui/shortcut.h"
#include "base/debug.h"
2015-06-11 03:44:46 +08:00
#include "base/replace_string.h"
#include "base/split_string.h"
#include "base/string.h"
2018-08-09 23:58:43 +08:00
#include "os/system.h"
#include <cctype>
#include <cstdlib>
#include <string>
#include <vector>
2007-09-19 07:57:02 +08:00
namespace ui {
#ifdef _WIN32
const char* kWinKeyName = "Win";
#else
const char* kWinKeyName = "Super";
#endif
namespace {
2018-08-09 23:58:43 +08:00
const char* scancode_to_string[] = {
// Same order that os::KeyScancode
nullptr,
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0 Pad",
"1 Pad",
"2 Pad",
"3 Pad",
"4 Pad",
"5 Pad",
"6 Pad",
"7 Pad",
"8 Pad",
"9 Pad",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"Esc",
"~",
"-",
"=",
"Backspace",
"Tab",
"[",
"]",
"Enter",
";",
"\'",
"\\",
"KEY_BACKSLASH2",
",",
".",
"/",
"Space",
"Ins",
"Del",
"Home",
"End",
"PgUp",
"PgDn",
"Left",
"Right",
"Up",
"Down",
"/ Pad",
"* Pad",
"- Pad",
"+ Pad",
"Del Pad",
"Enter Pad",
"PrtScr",
"Pause",
"KEY_ABNT_C1",
"Yen",
"Kana",
"KEY_CONVERT",
"KEY_NOCONVERT",
"KEY_AT",
"KEY_CIRCUMFLEX",
"KEY_COLON2",
"Kanji",
};
int scancode_to_string_size = sizeof(scancode_to_string) / sizeof(scancode_to_string[0]);
} // anonymous namespace
Shortcut::Shortcut() : m_modifiers(kKeyNoneModifier), m_scancode(kKeyNil), m_unicodeChar(0)
2007-09-19 07:57:02 +08:00
{
}
Shortcut::Shortcut(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
: m_modifiers(modifiers)
, m_scancode(scancode)
, m_unicodeChar(unicodeChar)
2007-09-19 07:57:02 +08:00
{
}
Shortcut::Shortcut(const std::string& str)
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{
// Special case: plus sign
if (str == "+") {
m_unicodeChar = '+';
return;
}
2007-09-19 07:57:02 +08:00
2015-06-11 03:44:46 +08:00
std::size_t i, j;
for (i = 0; i < str.size(); i = j + 1) {
// i+1 because the first character can be '+' sign
for (j = i + 1; j < str.size() && str[j] != '+'; ++j)
;
std::string tok = base::string_to_lower(str.substr(i, j - i));
if (m_scancode == kKeySpace) {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeySpaceModifier);
m_scancode = kKeyNil;
}
2007-09-19 07:57:02 +08:00
// Modifiers
if (tok == "shift") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyShiftModifier);
}
else if (tok == "alt") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyAltModifier);
}
else if (tok == "ctrl") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCtrlModifier);
}
else if (tok == "cmd") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCmdModifier);
}
else if (tok == base::string_to_lower(kWinKeyName)) {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyWinModifier);
}
// Word with one character
else if (base::utf8_length(tok) == 1) {
std::wstring wstr = base::from_utf8(tok);
if (wstr.size() != 1) {
ASSERT(false && "Something wrong converting utf-8 to wchar string");
continue;
2007-09-19 07:57:02 +08:00
}
m_unicodeChar = std::tolower(wstr[0]);
m_scancode = kKeyNil;
2007-09-19 07:57:02 +08:00
}
2017-01-07 01:19:06 +08:00
// F1, F2, ..., F11, F12
else if (tok[0] == 'f' && (tok.size() <= 3)) {
2020-04-09 04:48:06 +08:00
int num = std::strtol(tok.c_str() + 1, nullptr, 10);
2017-01-07 01:19:06 +08:00
if ((num >= 1) && (num <= 12))
m_scancode = (KeyScancode)((int)kKeyF1 + num - 1);
2007-09-19 07:57:02 +08:00
}
2017-01-07 01:19:06 +08:00
else if ((tok == "escape") || (tok == "esc"))
m_scancode = kKeyEsc;
else if (tok == "backspace")
m_scancode = kKeyBackspace;
else if (tok == "tab")
m_scancode = kKeyTab;
else if (tok == "enter")
m_scancode = kKeyEnter;
else if (tok == "space")
m_scancode = kKeySpace;
else if ((tok == "insert") || (tok == "ins"))
m_scancode = kKeyInsert;
else if ((tok == "delete") || (tok == "del"))
m_scancode = kKeyDel;
else if (tok == "home")
m_scancode = kKeyHome;
else if (tok == "end")
m_scancode = kKeyEnd;
else if ((tok == "page up") || (tok == "pgup"))
m_scancode = kKeyPageUp;
else if ((tok == "page down") || (tok == "pgdn"))
m_scancode = kKeyPageDown;
else if (tok == "left")
m_scancode = kKeyLeft;
else if (tok == "right")
m_scancode = kKeyRight;
else if (tok == "up")
m_scancode = kKeyUp;
else if (tok == "down")
m_scancode = kKeyDown;
else if (tok == "0 pad")
m_scancode = kKey0Pad;
else if (tok == "1 pad")
m_scancode = kKey1Pad;
else if (tok == "2 pad")
m_scancode = kKey2Pad;
else if (tok == "3 pad")
m_scancode = kKey3Pad;
else if (tok == "4 pad")
m_scancode = kKey4Pad;
else if (tok == "5 pad")
m_scancode = kKey5Pad;
else if (tok == "6 pad")
m_scancode = kKey6Pad;
else if (tok == "7 pad")
m_scancode = kKey7Pad;
else if (tok == "8 pad")
m_scancode = kKey8Pad;
else if (tok == "9 pad")
m_scancode = kKey9Pad;
else if (tok == "/ pad" || tok == "slash pad")
m_scancode = kKeySlashPad;
else if (tok == "* pad" || tok == "asterisk pad" || tok == "asterisk")
m_scancode = kKeyAsterisk;
else if (tok == "- pad" || tok == "minus pad")
m_scancode = kKeyMinusPad;
else if (tok == "+ pad" || tok == "plus pad")
m_scancode = kKeyPlusPad;
else if (tok == "del pad" || tok == "delete pad")
m_scancode = kKeyDelPad;
else if (tok == "enter pad")
m_scancode = kKeyEnterPad;
2007-09-19 07:57:02 +08:00
}
}
2007-09-19 07:57:02 +08:00
bool Shortcut::operator==(const Shortcut& other) const
{
// TODO improve this, avoid conversion to std::string
return toString() == other.toString();
}
2007-09-19 07:57:02 +08:00
bool Shortcut::isEmpty() const
{
return (m_modifiers == kKeyNoneModifier && m_scancode == kKeyNil && m_unicodeChar == 0);
2007-09-19 07:57:02 +08:00
}
std::string Shortcut::toString() const
2007-09-19 07:57:02 +08:00
{
std::string buf;
2007-09-19 07:57:02 +08:00
// Shifts
if (m_modifiers & kKeyCtrlModifier)
buf += "Ctrl+";
if (m_modifiers & kKeyCmdModifier)
buf += "Cmd+";
if (m_modifiers & kKeyAltModifier)
buf += "Alt+";
if (m_modifiers & kKeyShiftModifier)
buf += "Shift+";
if ((m_modifiers & kKeySpaceModifier) && (m_scancode != kKeySpace) && (m_unicodeChar != ' ')) {
buf += "Space+";
}
if (m_modifiers & kKeyWinModifier) {
buf += kWinKeyName;
buf += "+";
}
2007-09-19 07:57:02 +08:00
// Key
if (m_unicodeChar) {
std::wstring wideUnicodeChar;
wideUnicodeChar.push_back((wchar_t)std::toupper(m_unicodeChar));
buf += base::to_utf8(wideUnicodeChar);
}
2017-01-07 01:19:06 +08:00
else if (m_scancode > 0 && m_scancode < scancode_to_string_size && scancode_to_string[m_scancode])
buf += scancode_to_string[m_scancode];
else if (!buf.empty() && buf[buf.size() - 1] == '+')
buf.erase(buf.size() - 1);
return buf;
2007-09-19 07:57:02 +08:00
}
bool Shortcut::isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const
2007-09-19 07:57:02 +08:00
{
return ((scancode && *this == Shortcut(modifiers, scancode, 0)) ||
(unicodeChar && *this == Shortcut(modifiers, kKeyNil, unicodeChar)));
2007-09-19 07:57:02 +08:00
}
bool Shortcut::isPressed() const
{
os::SystemRef sys = os::System::instance();
if (!sys)
return false;
2016-11-18 22:15:02 +08:00
KeyModifiers pressedModifiers = sys->keyModifiers();
// Check if this shortcut is only
if (m_scancode == kKeyNil && m_unicodeChar == 0)
return (m_modifiers == pressedModifiers);
// Compare with all pressed scancodes
for (int s = int(kKeyNil); s < int(kKeyFirstModifierScancode); ++s) {
2016-11-18 22:15:02 +08:00
if (sys->isKeyPressed(KeyScancode(s)) &&
isPressed(pressedModifiers, KeyScancode(s), sys->getUnicodeFromScancode(KeyScancode(s))))
return true;
}
return false;
}
bool Shortcut::isLooselyPressed() const
{
os::SystemRef sys = os::System::instance();
if (!sys)
return false;
2016-11-18 22:15:02 +08:00
KeyModifiers pressedModifiers = sys->keyModifiers();
if ((m_modifiers & pressedModifiers) != m_modifiers)
return false;
// Check if this shortcut is only
if (m_scancode == kKeyNil && m_unicodeChar == 0)
return true;
// Compare with all pressed scancodes
for (int s = int(kKeyNil); s < int(kKeyFirstModifierScancode); ++s) {
if (sys->isKeyPressed(KeyScancode(s)) && isPressed(m_modifiers, // Use same modifiers (we've
// already compared the
// modifiers)
KeyScancode(s),
2016-11-18 22:15:02 +08:00
sys->getUnicodeFromScancode(KeyScancode(s))))
return true;
}
return false;
}
} // namespace ui