diff --git a/include/xstudio/ui/keyboard.hpp b/include/xstudio/ui/keyboard.hpp index b39c0534a..9e5afdbb8 100644 --- a/include/xstudio/ui/keyboard.hpp +++ b/include/xstudio/ui/keyboard.hpp @@ -216,21 +216,61 @@ class Hotkey { //{0x7c {0x7d, "}"}, {0x7e, "~"}, - {93, "numpad 0"}, - {96, "numpad 1"}, - {97, "numpad 2"}, - {98, "numpad 3"}, - {99, "numpad 4"}, - {100, "numpad 5"}, - {101, "numpad 6"}, - {102, "numpad 7"}, - {103, "numpad 8"}, - {104, "numpad 9"}, - {105, "numpad multiply"}, - {106, "numpad add"}, - {107, "numpad subtract"}, - {109, "numpad decimal point"}, - {110, "numpad divide"}}; + + // Use Javascript keycodes for the numpad + // except for '0' as it conflicts with '`' above + { 97, "numpad 1"}, + { 98, "numpad 2"}, + { 99, "numpad 3"}, + {100, "numpad 4"}, + {101, "numpad 5"}, + {102, "numpad 6"}, + {103, "numpad 7"}, + {104, "numpad 8"}, + {105, "numpad 9"}, + {106, "numpad multiply"}, + {107, "numpad add"}, + {108, "numpad 0"}, + {109, "numpad subtract"}, + {110, "numpad decimal point"}, + {111, "numpad comma"}, + {111, "numpad divide"}}; + + inline static const std::map key_to_numpad_key = { + {0x2a, 106}, // * + {0x2b, 107}, // + + {0x2c, 112}, // , (replaces . on some keyboards) + {0x2d, 109}, // - + {0x2e, 110}, // . + {0x2f, 111}, // / + {0x30, 108}, // 0 + {0x31, 97}, // 1 + {0x32, 98}, // 2 + {0x33, 99}, // 3 + {0x34, 100}, // 4 + {0x35, 101}, // 5 + {0x36, 102}, // 6 + {0x37, 103}, // 7 + {0x38, 104}, // 8 + {0x39, 105}, // 9 + +#ifndef __apple__ + // remap numpad alternate keys to the above + // but not under macOS as it isn't needed + // and actually causes problems + {0x01000006, 108}, // Insert => 0 + {0x01000007, 110}, // Delete => . + {0x0100000b, 101}, // Clear => 5 + {0x01000010, 103}, // Home => 7 + {0x01000011, 97}, // End => 1 + {0x01000012, 100}, // Left => 4 + {0x01000013, 104}, // Up => 8 + {0x01000014, 102}, // Right => 6 + {0x01000015, 98}, // Down => 2 + {0x01000016, 105}, // PageUp => 9 + {0x01000017, 99}, // PageDown => 3 +#endif + }; inline static const std::map key_to_modifier = { {0x01000020, (int)ShiftModifier}, diff --git a/include/xstudio/ui/qml/helper_ui.hpp b/include/xstudio/ui/qml/helper_ui.hpp index 50768fd03..46954cd67 100644 --- a/include/xstudio/ui/qml/helper_ui.hpp +++ b/include/xstudio/ui/qml/helper_ui.hpp @@ -466,6 +466,8 @@ caf::actor actorFromQString(actor_system &sys, const QString &addr); std::string actorToString(actor_system &sys, const caf::actor &actor); caf::actor actorFromString(actor_system &sys, const std::string &addr); +std::pair decodeQKeyEvent(const QKeyEvent *key_event); + QString getThumbnailURL( actor_system &sys, const caf::actor &actor, diff --git a/src/ui/base/src/keyboard.cpp b/src/ui/base/src/keyboard.cpp index b6329f9b4..b440468bf 100644 --- a/src/ui/base/src/keyboard.cpp +++ b/src/ui/base/src/keyboard.cpp @@ -274,7 +274,9 @@ void Hotkey::sequence_to_key_and_modifier( modifier |= ControlModifier; } else { for (const auto &q : ui::Hotkey::key_names) { - if (q.second == D) { + const std::string qD = utility::replace_all(q.second, " ", ""); + const std::string qd = utility::to_lower(qD); + if (qd == d) { keycode = q.first; break; } @@ -300,7 +302,8 @@ void Hotkey::sequence_to_key_and_modifier( } for (const auto &p : seq) { - const std::string d = utility::to_lower(p); + const std::string D = utility::replace_all(p, " ", ""); + const std::string d = utility::to_lower(D); if (d == "shift") { modifier |= ShiftModifier; } else if (d == "meta") { @@ -311,7 +314,9 @@ void Hotkey::sequence_to_key_and_modifier( modifier |= ControlModifier; } else { for (const auto &q : ui::Hotkey::key_names) { - if (q.second == p) { + const std::string qD = utility::replace_all(q.second, " ", ""); + const std::string qd = utility::to_lower(qD); + if (qd == d) { keycode = q.first; break; } diff --git a/src/ui/qml/helper/src/helper_ui.cpp b/src/ui/qml/helper/src/helper_ui.cpp index 27d352b3c..d4f58fb4b 100644 --- a/src/ui/qml/helper/src/helper_ui.cpp +++ b/src/ui/qml/helper/src/helper_ui.cpp @@ -251,6 +251,20 @@ QVariant xstudio::ui::qml::json_to_qvariant(const nlohmann::json &json) { } +std::pair xstudio::ui::qml::decodeQKeyEvent(const QKeyEvent *key_event) { + auto key = key_event->key(); + auto text = StdFromQString(key_event->text()); + + // remap numpad keys + if ((key_event->modifiers() & Qt::KeypadModifier) == Qt::KeypadModifier and + xstudio::ui::Hotkey::key_to_numpad_key.find(key) != xstudio::ui::Hotkey::key_to_numpad_key.end()) { + key = xstudio::ui::Hotkey::key_to_numpad_key.at(key); + text = xstudio::ui::Hotkey::key_names.at(key); + } + + return {key, text}; +} + KeyEventsItem::KeyEventsItem(QQuickItem *parent) : QQuickItem(parent) { keypress_monitor_ = CafSystemObject::get_actor_system().registry().template get( @@ -272,10 +286,12 @@ bool KeyEventsItem::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_down_atom_v, - key_event->key(), - StdFromQString(key_event->text()), + key, + text, context_, window_name_, key_event->isAutoRepeat()) @@ -285,8 +301,10 @@ bool KeyEventsItem::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event && !key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( - ui::keypress_monitor::key_up_atom_v, key_event->key(), context_, window_name_) + ui::keypress_monitor::key_up_atom_v, key, context_, window_name_) .send(keypress_monitor_); } } else if ( @@ -326,25 +344,29 @@ bool KeyEventsItem::grabFocus() { return true; } -void KeyEventsItem::keyPressEvent(QKeyEvent *event) { +void KeyEventsItem::keyPressEvent(QKeyEvent *key_event) { if (window_name_.empty()) window_name_ = StdFromQString(item_window_name(parent())); + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::text_entry_atom_v, - StdFromQString(event->text()), + text, context_, window_name_) .send(keypress_monitor_); } -void KeyEventsItem::keyReleaseEvent(QKeyEvent *event) { +void KeyEventsItem::keyReleaseEvent(QKeyEvent *key_event) { if (window_name_.empty()) window_name_ = StdFromQString(item_window_name(parent())); - if (!event->isAutoRepeat()) { - anon_mail(ui::keypress_monitor::key_up_atom_v, event->key(), context_, window_name_) + if (!key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + + anon_mail(ui::keypress_monitor::key_up_atom_v, key, context_, window_name_) .send(keypress_monitor_); } } diff --git a/src/ui/qml/viewport/src/qml_viewport.cpp b/src/ui/qml/viewport/src/qml_viewport.cpp index 57d5bb6e1..de9520cb0 100644 --- a/src/ui/qml/viewport/src/qml_viewport.cpp +++ b/src/ui/qml/viewport/src/qml_viewport.cpp @@ -404,17 +404,18 @@ void QMLViewport::mouseDoubleClickEvent(QMouseEvent *event) { void QMLViewport::keyPressEvent(QKeyEvent *key_event) { + auto [key, text] = decodeQKeyEvent(key_event); + // On some platforms (MacOS) backspace and delete aren't // ASCII but widestring encoded. Hack here to get around // that until we do propoer wstring handling in the // backend - std::string text = StdFromQString(key_event->text()); - if (key_event->key() == Qt::Key_Backspace) { + if (key == Qt::Key_Backspace) { std::array v; v[0] = 8; v[1] = 0; text = v.data(); - } else if (key_event->key() == Qt::Key_Delete) { + } else if (key == Qt::Key_Delete) { std::array v; v[0] = 127; v[1] = 0; @@ -432,9 +433,11 @@ void QMLViewport::keyPressEvent(QKeyEvent *key_event) { void QMLViewport::keyReleaseEvent(QKeyEvent *key_event) { if (!key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_up_atom_v, - key_event->key(), + key, renderer_actor ? renderer_actor->std_name() : "", StdFromQString(m_window->objectName())) .send(keypress_monitor_); @@ -470,10 +473,12 @@ bool QMLViewport::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_down_atom_v, - key_event->key(), - StdFromQString(key_event->text()), + key, + text, renderer_actor ? renderer_actor->std_name() : "", StdFromQString(m_window->objectName()), key_event->isAutoRepeat()) @@ -483,9 +488,11 @@ bool QMLViewport::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event && !key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_up_atom_v, - key_event->key(), + key, renderer_actor ? renderer_actor->std_name() : "", StdFromQString(m_window->objectName())) .send(keypress_monitor_); diff --git a/src/ui/qt/viewport_widget/src/viewport_widget.cpp b/src/ui/qt/viewport_widget/src/viewport_widget.cpp index 634de069b..f84a44310 100644 --- a/src/ui/qt/viewport_widget/src/viewport_widget.cpp +++ b/src/ui/qt/viewport_widget/src/viewport_widget.cpp @@ -156,10 +156,11 @@ void ViewportGLWidget::mouseDoubleClickEvent(QMouseEvent *event) { } void ViewportGLWidget::keyPressEvent(QKeyEvent *key_event) { + const auto [key, text] = decodeQKeyEvent(key_event); anon_mail( ui::keypress_monitor::text_entry_atom_v, - StdFromQString(key_event->text()), + text, the_viewport_->name(), window_name_) .send(keypress_monitor_); @@ -168,9 +169,11 @@ void ViewportGLWidget::keyPressEvent(QKeyEvent *key_event) { void ViewportGLWidget::keyReleaseEvent(QKeyEvent *key_event) { if (!key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_up_atom_v, - key_event->key(), + key, the_viewport_->name(), window_name_) .send(keypress_monitor_); @@ -183,10 +186,12 @@ bool ViewportGLWidget::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_down_atom_v, - key_event->key(), - StdFromQString(key_event->text()), + key, + text, the_viewport_->name(), window_name_, key_event->isAutoRepeat()) @@ -196,9 +201,11 @@ bool ViewportGLWidget::event(QEvent *event) { auto key_event = dynamic_cast(event); if (key_event && !key_event->isAutoRepeat()) { + const auto [key, text] = decodeQKeyEvent(key_event); + anon_mail( ui::keypress_monitor::key_up_atom_v, - key_event->key(), + key, the_viewport_->name(), window_name_) .send(keypress_monitor_);