Qt: Redo key shortcut editing
@@ -7,6 +7,7 @@ #include "KeyEditor.h"
#include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "ShortcutController.h" #include <QKeyEvent>@@ -20,6 +21,7 @@ , m_axis(-1)
, m_button(false) { setAlignment(Qt::AlignCenter); + setFocusPolicy(Qt::ClickFocus); } void KeyEditor::setValue(int key) {@@ -27,7 +29,11 @@ m_key = key;
if (m_button) { updateButtonText(); } else { - setText(QKeySequence(key).toString(QKeySequence::NativeText)); + if (key < 0) { + setText(tr("---")); + } else { + setText(QKeySequence(key).toString(QKeySequence::NativeText)); + } } emit valueChanged(key); }@@ -71,27 +77,60 @@ }
void KeyEditor::keyPressEvent(QKeyEvent* event) { if (!m_button) { - setValue(event->key()); + if (m_key < 0) { + m_key = 0; + } + if (ShortcutController::isModifierKey(event->key())) { + switch (event->key()) { + case Qt::Key_Shift: + setValue(m_key | Qt::ShiftModifier); + break; + case Qt::Key_Control: + setValue(m_key | Qt::ControlModifier); + break; + case Qt::Key_Alt: + setValue(m_key | Qt::AltModifier); + break; + case Qt::Key_Meta: + setValue(m_key | Qt::MetaModifier); + break; + default: + setValue(m_key); + } + } else { + setValue(event->key() | (m_key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))); + } } event->accept(); } bool KeyEditor::event(QEvent* event) { if (!m_button) { - return QWidget::event(event); - } - if (event->type() == GamepadButtonEvent::Down()) { - setValueButton(static_cast<GamepadButtonEvent*>(event)->value()); - event->accept(); - return true; - } - if (event->type() == GamepadAxisEvent::Type()) { - GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); - if (gae->isNew()) { - setValueAxis(gae->axis(), gae->direction()); + if (event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); + if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { + return QWidget::event(event); + } + if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { + keyPressEvent(keyEvent); + keyEvent->accept(); + return true; + } } - event->accept(); - return true; + } else { + if (event->type() == GamepadButtonEvent::Down()) { + setValueButton(static_cast<GamepadButtonEvent*>(event)->value()); + event->accept(); + return true; + } + if (event->type() == GamepadAxisEvent::Type()) { + GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event); + if (gae->isNew()) { + setValueAxis(gae->axis(), gae->direction()); + } + event->accept(); + return true; + } } return QWidget::event(event); }
@@ -37,7 +37,7 @@ switch (index.column()) {
case 0: return item->visibleName(); case 1: - return item->shortcut().toString(QKeySequence::NativeText); + return QKeySequence(item->shortcut()).toString(QKeySequence::NativeText); case 2: if (item->button() >= 0) { return item->button();@@ -125,7 +125,7 @@ createIndex(smenu->items().count() - 1, 2, item));
} void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, - const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + int shortcut, const QString& visibleName, const QString& name) { ShortcutItem* smenu = m_menuMap[menu]; if (!smenu) { return;@@ -143,6 +143,11 @@ }
m_heldKeys[shortcut] = item; emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); +} + +void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, + const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + addFunctions(menu, press, release, shortcut[0], visibleName, name); } void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) {@@ -179,10 +184,10 @@ }
return static_cast<const ShortcutItem*>(index.internalPointer()); } -QKeySequence ShortcutController::shortcutAt(const QModelIndex& index) const { +int ShortcutController::shortcutAt(const QModelIndex& index) const { const ShortcutItem* item = itemAt(index); if (!item) { - return QKeySequence(); + return 0; } return item->shortcut(); }@@ -195,7 +200,7 @@ }
return item->menu(); } -void ShortcutController::updateKey(const QModelIndex& index, const QKeySequence& keySequence) { +void ShortcutController::updateKey(const QModelIndex& index, int keySequence) { if (!index.isValid()) { return; }@@ -204,21 +209,26 @@ if (!parent.isValid()) {
return; } ShortcutItem* item = itemAt(index); + updateKey(item, keySequence); + if (m_config) { + m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION); + } + emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), + createIndex(index.row(), 2, index.internalPointer())); +} + +void ShortcutController::updateKey(ShortcutItem* item, int keySequence) { + int oldShortcut = item->shortcut(); if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { + if (oldShortcut > 0) { m_heldKeys.take(oldShortcut); } - if (!keySequence.isEmpty()) { + if (keySequence > 0) { m_heldKeys[keySequence] = item; } } + item->setShortcut(keySequence); - if (m_config) { - m_config->setQtOption(item->name(), keySequence.toString(), KEY_SECTION); - } - emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), - createIndex(index.row(), 2, index.internalPointer())); } void ShortcutController::updateButton(const QModelIndex& index, int button) {@@ -286,7 +296,7 @@ createIndex(index.row(), 2, index.internalPointer()));
} void ShortcutController::clearKey(const QModelIndex& index) { - updateKey(index, QKeySequence()); + updateKey(index, 0); } void ShortcutController::clearButton(const QModelIndex& index) {@@ -299,22 +309,27 @@ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->isAutoRepeat()) { return false; } - auto item = m_heldKeys.find(keyEventToSequence(keyEvent)); - if (item == m_heldKeys.end()) { - return false; - } - ShortcutItem::Functions pair = item.value()->functions(); - if (event->type() == QEvent::KeyPress) { - if (pair.first) { - pair.first(); - } + int key = keyEvent->key(); + if (!isModifierKey(key)) { + key |= keyEvent->modifiers(); } else { - if (pair.second) { - pair.second(); + key = toModifierKey(key | keyEvent->modifiers()); + } + auto item = m_heldKeys.find(key); + if (item != m_heldKeys.end()) { + ShortcutItem::Functions pair = item.value()->functions(); + if (event->type() == QEvent::KeyPress) { + if (pair.first) { + pair.first(); + } + } else { + if (pair.second) { + pair.second(); + } } + event->accept(); + return true; } - event->accept(); - return true; } if (event->type() == GamepadButtonEvent::Down()) { auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());@@ -378,15 +393,11 @@ return;
} QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); if (!shortcut.isNull()) { - QKeySequence keySequence(shortcut.toString()); - if (item->functions().first) { - QKeySequence oldShortcut = item->shortcut(); - if (!oldShortcut.isEmpty()) { - m_heldKeys.take(oldShortcut); - } - m_heldKeys[keySequence] = item; + if (shortcut.toString().endsWith("+")) { + updateKey(item, toModifierShortcut(shortcut.toString())); + } else { + updateKey(item, QKeySequence(shortcut.toString())[0]); } - item->setShortcut(keySequence); } loadGamepadShortcuts(item); }@@ -446,26 +457,6 @@ }
} } -QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) { - QString modifier = QString::null; - - if (event->modifiers() & Qt::ShiftModifier) { - modifier += "Shift+"; - } - if (event->modifiers() & Qt::ControlModifier) { - modifier += "Ctrl+"; - } - if (event->modifiers() & Qt::AltModifier) { - modifier += "Alt+"; - } - if (event->modifiers() & Qt::MetaModifier) { - modifier += "Meta+"; - } - - QString key = QKeySequence(event->key()).toString(); - return QKeySequence(modifier + key); -} - void ShortcutController::loadProfile(const QString& profile) { m_profileName = profile; m_profile = InputProfile::findProfile(profile);@@ -481,9 +472,69 @@ onSubitems(&subitem, func);
} } +int ShortcutController::toModifierShortcut(const QString& shortcut) { + // Qt doesn't seem to work with raw modifier shortcuts! + QStringList modifiers = shortcut.split('+'); + int value = 0; + for (const auto& mod : modifiers) { + if (mod == QLatin1String("Shift")) { + value |= Qt::ShiftModifier; + continue; + } + if (mod == QLatin1String("Ctrl")) { + value |= Qt::ControlModifier; + continue; + } + if (mod == QLatin1String("Alt")) { + value |= Qt::AltModifier; + continue; + } + if (mod == QLatin1String("Meta")) { + value |= Qt::MetaModifier; + continue; + } + } + return value; +} + +bool ShortcutController::isModifierKey(int key) { + switch (key) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + return true; + default: + return false; + } +} + +int ShortcutController::toModifierKey(int key) { + int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier); + key ^= modifiers; + switch (key) { + case Qt::Key_Shift: + modifiers |= Qt::ShiftModifier; + break; + case Qt::Key_Control: + modifiers |= Qt::ControlModifier; + break; + case Qt::Key_Alt: + modifiers |= Qt::AltModifier; + break; + case Qt::Key_Meta: + modifiers |= Qt::MetaModifier; + break; + default: + break; + } + return modifiers; + +} + ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent) : m_action(action) - , m_shortcut(action->shortcut()) + , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) , m_menu(nullptr) , m_name(name) , m_button(-1)@@ -496,7 +547,7 @@ .remove(QRegExp("&(?!&)"))
.remove("..."); } -ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) +ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) : m_action(nullptr) , m_shortcut(shortcut) , m_functions(functions)@@ -512,6 +563,7 @@ }
ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent) : m_action(nullptr) + , m_shortcut(0) , m_menu(menu) , m_button(-1) , m_axis(-1)@@ -530,7 +582,7 @@ m_items.append(ShortcutItem(action, name, this));
} void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, - const QKeySequence& shortcut, const QString& visibleName, + int shortcut, const QString& visibleName, const QString& name) { m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this)); }@@ -539,10 +591,10 @@ void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) {
m_items.append(ShortcutItem(menu, this)); } -void ShortcutController::ShortcutItem::setShortcut(const QKeySequence& shortcut) { +void ShortcutController::ShortcutItem::setShortcut(int shortcut) { m_shortcut = shortcut; if (m_action) { - m_action->setShortcut(shortcut); + m_action->setShortcut(QKeySequence(shortcut)); } }
@@ -9,7 +9,6 @@
#include "GamepadAxisEvent.h" #include <QAbstractItemModel> -#include <QKeySequence> #include <functional>@@ -38,13 +37,13 @@ public:
typedef QPair<std::function<void ()>, std::function<void ()>> Functions; ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr); - ShortcutItem(Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, + ShortcutItem(Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent = nullptr); ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr); QAction* action() { return m_action; } const QAction* action() const { return m_action; } - const QKeySequence& shortcut() const { return m_shortcut; } + const int shortcut() const { return m_shortcut; } Functions functions() const { return m_functions; } QMenu* menu() { return m_menu; } const QMenu* menu() const { return m_menu; }@@ -55,11 +54,11 @@ const QList<ShortcutItem>& items() const { return m_items; }
ShortcutItem* parent() { return m_parent; } const ShortcutItem* parent() const { return m_parent; } void addAction(QAction* action, const QString& name); - void addFunctions(Functions functions, const QKeySequence& shortcut, const QString& visibleName, + void addFunctions(Functions functions, int shortcut, const QString& visibleName, const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } - void setShortcut(const QKeySequence& sequence); + void setShortcut(int sequence); void setButton(int button) { m_button = button; } int axis() const { return m_axis; } GamepadAxisEvent::Direction direction() const { return m_direction; }@@ -71,7 +70,7 @@ }
private: QAction* m_action; - QKeySequence m_shortcut; + int m_shortcut; QMenu* m_menu; Functions m_functions; QString m_name;@@ -100,20 +99,24 @@ virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
void addAction(QMenu* menu, QAction* action, const QString& name); void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, + int shortcut, const QString& visibleName, const QString& name); + void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release, const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); - QKeySequence shortcutAt(const QModelIndex& index) const; + int shortcutAt(const QModelIndex& index) const; bool isMenuAt(const QModelIndex& index) const; - void updateKey(const QModelIndex& index, const QKeySequence& keySequence); + void updateKey(const QModelIndex& index, int keySequence); void updateButton(const QModelIndex& index, int button); void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction); void clearKey(const QModelIndex& index); void clearButton(const QModelIndex& index); - static QKeySequence keyEventToSequence(const QKeyEvent*); + static int toModifierShortcut(const QString& shortcut); + static bool isModifierKey(int key); + static int toModifierKey(int key); public slots: void loadProfile(const QString& profile);@@ -127,12 +130,13 @@ const ShortcutItem* itemAt(const QModelIndex& index) const;
void loadShortcuts(ShortcutItem*); void loadGamepadShortcuts(ShortcutItem*); void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func); + void updateKey(ShortcutItem* item, int keySequence); ShortcutItem m_rootMenu; QMap<QMenu*, ShortcutItem*> m_menuMap; QMap<int, ShortcutItem*> m_buttons; QMap<QPair<int, GamepadAxisEvent::Direction>, ShortcutItem*> m_axes; - QMap<QKeySequence, ShortcutItem*> m_heldKeys; + QMap<int, ShortcutItem*> m_heldKeys; ConfigController* m_config; QString m_profileName; const InputProfile* m_profile;
@@ -19,10 +19,14 @@ , m_controller(nullptr)
, m_input(nullptr) { m_ui.setupUi(this); - m_ui.keyEdit->setValueButton(-1); - m_ui.keySequenceEdit->installEventFilter(this); + m_ui.keyEdit->setValueKey(0); - connect(m_ui.keySequenceEdit, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(updateKey(const QKeySequence&))); + connect(m_ui.gamepadButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueButton(-1); + }); + connect(m_ui.keyboardButton, &QAbstractButton::pressed, [this]() { + m_ui.keyEdit->setValueKey(0); + }); connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); connect(m_ui.keyEdit, SIGNAL(axisChanged(int, int)), this, SLOT(updateAxis(int, int))); connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(load(const QModelIndex&)));@@ -42,21 +46,6 @@ m_input = controller;
m_input->stealFocus(this); } -bool ShortcutView::eventFilter(QObject*, QEvent* event) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->key() != Qt::Key_Tab && keyEvent->key() != Qt::Key_Backtab) { - return false; - } - if (!(keyEvent->modifiers() & ~Qt::ShiftModifier)) { - m_ui.keySequenceEdit->setKeySequence(ShortcutController::keyEventToSequence(keyEvent)); - keyEvent->accept(); - return true; - } - } - return false; -} - void ShortcutView::load(const QModelIndex& index) { if (!m_controller) { return;@@ -64,23 +53,20 @@ }
if (m_controller->isMenuAt(index)) { return; } - QKeySequence sequence = m_controller->shortcutAt(index); + int shortcut = m_controller->shortcutAt(index); if (index.column() == 1) { m_ui.keyboardButton->click(); } else if (index.column() == 2) { m_ui.gamepadButton->click(); } + bool blockSignals = m_ui.keyEdit->blockSignals(true); + m_ui.keyEdit->setFocus(Qt::MouseFocusReason); if (m_ui.gamepadButton->isChecked()) { - bool blockSignals = m_ui.keyEdit->blockSignals(true); - m_ui.keyEdit->setFocus(); m_ui.keyEdit->setValueButton(-1); // There are no default bindings - m_ui.keyEdit->blockSignals(blockSignals); } else { - bool blockSignals = m_ui.keySequenceEdit->blockSignals(true); - m_ui.keySequenceEdit->setFocus(); - m_ui.keySequenceEdit->setKeySequence(sequence); - m_ui.keySequenceEdit->blockSignals(blockSignals); + m_ui.keyEdit->setValueKey(shortcut); } + m_ui.keyEdit->blockSignals(blockSignals); } void ShortcutView::clear() {@@ -96,22 +82,19 @@ m_controller->clearButton(index);
m_ui.keyEdit->setValueButton(-1); } else { m_controller->clearKey(index); - m_ui.keySequenceEdit->setKeySequence(QKeySequence()); - } -} - -void ShortcutView::updateKey(const QKeySequence& shortcut) { - if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { - return; + m_ui.keyEdit->setValueKey(-1); } - m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), shortcut); } void ShortcutView::updateButton(int button) { if (!m_controller || m_controller->isMenuAt(m_ui.shortcutTable->selectionModel()->currentIndex())) { return; } - m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + if (m_ui.gamepadButton->isChecked()) { + m_controller->updateButton(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } else { + m_controller->updateKey(m_ui.shortcutTable->selectionModel()->currentIndex(), button); + } } void ShortcutView::updateAxis(int axis, int direction) {
@@ -27,14 +27,12 @@ void setController(ShortcutController* controller);
void setInputController(InputController* input); protected: - virtual bool eventFilter(QObject* obj, QEvent* event) override; virtual bool event(QEvent*) override; virtual void closeEvent(QCloseEvent*) override; private slots: void load(const QModelIndex&); void clear(); - void updateKey(const QKeySequence&); void updateButton(int button); void updateAxis(int axis, int direction);
@@ -6,7 +6,7 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>425</width> + <width>432</width> <height>443</height> </rect> </property>@@ -61,16 +61,6 @@ </property>
</spacer> </item> <item> - <widget class="QKeySequenceEdit" name="keySequenceEdit"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item> <widget class="QGBA::KeyEditor" name="keyEdit"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">@@ -78,9 +68,6 @@ <horstretch>0</horstretch>
<verstretch>0</verstretch> </sizepolicy> </property> - <property name="visible"> - <bool>false</bool> - </property> </widget> </item> </layout>@@ -95,38 +82,5 @@ <header>KeyEditor.h</header>
</customwidget> </customwidgets> <resources/> - <connections> - <connection> - <sender>keyboardButton</sender> - <signal>toggled(bool)</signal> - <receiver>keySequenceEdit</receiver> - <slot>setVisible(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>86</x> - <y>374</y> - </hint> - <hint type="destinationlabel"> - <x>66</x> - <y>20</y> - </hint> - </hints> - </connection> - <connection> - <sender>gamepadButton</sender> - <signal>toggled(bool)</signal> - <receiver>keyEdit</receiver> - <slot>setVisible(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>213</x> - <y>374</y> - </hint> - <hint type="destinationlabel"> - <x>206</x> - <y>340</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui>