Qt: Shortcut view now supports held events
@@ -8,6 +8,7 @@
#include "GamepadButtonEvent.h" #include <QAction> +#include <QKeyEvent> #include <QMenu> using namespace QGBA;@@ -28,10 +29,7 @@ switch (index.column()) {
case 0: return item->visibleName(); case 1: - if (item->action()) { - return item->action()->shortcut().toString(QKeySequence::NativeText); - } - break; + return item->shortcut().toString(QKeySequence::NativeText); case 2: if (item->button() >= 0) { return item->button();@@ -104,6 +102,22 @@ ShortcutItem* item = &smenu->items().last();
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) { + ShortcutItem* smenu = m_menuMap[menu]; + if (!smenu) { + return; + } + ShortcutItem* pmenu = smenu->parent(); + int row = pmenu->items().indexOf(*smenu); + QModelIndex parent = createIndex(row, 0, smenu); + beginInsertRows(parent, smenu->items().count(), smenu->items().count()); + smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name); + endInsertRows(); + ShortcutItem* item = &smenu->items().last(); + m_heldKeys[shortcut] = item; + emit dataChanged(createIndex(smenu->items().count() - 1, 0, item), createIndex(smenu->items().count() - 1, 2, item)); +} + void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) { ShortcutItem* smenu = m_menuMap[parentMenu]; if (!smenu) {@@ -154,7 +168,14 @@ if (!parent.isValid()) {
return; } ShortcutItem* item = itemAt(index); - item->action()->setShortcut(keySequence); + if (item->functions().first) { + QKeySequence oldShortcut = item->shortcut(); + if (!oldShortcut.isEmpty()) { + m_heldKeys.take(oldShortcut); + } + m_heldKeys[keySequence] = item; + } + item->setShortcut(keySequence); emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); }@@ -177,23 +198,82 @@ emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer()));
} bool ShortcutController::eventFilter(QObject*, QEvent* event) { + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { + 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(); + } + } else { + if (pair.second) { + pair.second(); + } + } + event->accept(); + return true; + } if (event->type() == GamepadButtonEvent::Down()) { auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value()); if (item == m_buttons.end()) { return false; } QAction* action = item.value()->action(); - if (action->isEnabled()) { + if (action && action->isEnabled()) { action->trigger(); } + ShortcutItem::Functions pair = item.value()->functions(); + if (pair.first) { + pair.first(); + } + event->accept(); + return true; + } + if (event->type() == GamepadButtonEvent::Up()) { + auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value()); + if (item == m_buttons.end()) { + return false; + } + ShortcutItem::Functions pair = item.value()->functions(); + if (pair.second) { + pair.second(); + } event->accept(); return true; } return false; } +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); +} + ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent) : m_action(action) + , m_shortcut(action->shortcut()) , m_menu(nullptr) , m_name(name) , m_button(-1)@@ -204,6 +284,18 @@ .remove(QRegExp("&(?!&)"))
.remove("..."); } +ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) + : m_action(nullptr) + , m_shortcut(shortcut) + , m_functions(functions) + , m_menu(nullptr) + , m_name(name) + , m_visibleName(visibleName) + , m_button(-1) + , m_parent(parent) +{ +} + ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent) : m_action(nullptr) , m_menu(menu)@@ -221,6 +313,17 @@ void ShortcutController::ShortcutItem::addAction(QAction* action, const QString& name) {
m_items.append(ShortcutItem(action, name, this)); } +void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions, const QKeySequence& shortcut, const QString& visibleName, const QString& name) { + m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this)); +} + void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) { m_items.append(ShortcutItem(menu, this)); } + +void ShortcutController::ShortcutItem::setShortcut(const QKeySequence& shortcut) { + m_shortcut = shortcut; + if (m_action) { + m_action->setShortcut(shortcut); + } +}
@@ -7,8 +7,12 @@ #ifndef QGBA_SHORTCUT_MODEL
#define QGBA_SHORTCUT_MODEL #include <QAbstractItemModel> +#include <QKeySequence> + +#include <functional> class QAction; +class QKeyEvent; class QMenu; class QString;@@ -20,11 +24,16 @@
private: class ShortcutItem { 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* 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; } + Functions functions() const { return m_functions; } QMenu* menu() { return m_menu; } const QMenu* menu() const { return m_menu; } const QString& visibleName() const { return m_visibleName; }@@ -34,15 +43,19 @@ 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, const QString& name); void addSubmenu(QMenu* menu); int button() const { return m_button; } + void setShortcut(const QKeySequence& sequence); void setButton(int button) { m_button = button; } bool operator==(const ShortcutItem& other) const { return m_menu == other.m_menu && m_action == other.m_action; } private: QAction* m_action; + QKeySequence m_shortcut; QMenu* m_menu; + Functions m_functions; QString m_name; QString m_visibleName; int m_button;@@ -63,6 +76,7 @@ virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
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, const QKeySequence& shortcut, const QString& visibleName, const QString& name); void addMenu(QMenu* menu, QMenu* parent = nullptr); ShortcutItem* itemAt(const QModelIndex& index);@@ -76,9 +90,11 @@ protected:
bool eventFilter(QObject*, QEvent*) override; private: + static QKeySequence keyEventToSequence(const QKeyEvent*); ShortcutItem m_rootMenu; QMap<QMenu*, ShortcutItem*> m_menuMap; QMap<int, ShortcutItem*> m_buttons; + QMap<QKeySequence, ShortcutItem*> m_heldKeys; }; }
@@ -15,7 +15,6 @@
class InputController; class ShortcutController; -// TODO: suspend shortcuts (keyboard and gamepad) while window is open class ShortcutView : public QWidget { Q_OBJECT
@@ -278,9 +278,6 @@ if (event->isAutoRepeat()) {
QWidget::keyPressEvent(event); return; } - if (event->key() == Qt::Key_Tab) { - m_controller->setTurbo(true, false); - } GBAKey key = m_inputController.mapKeyboard(event->key()); if (key == GBA_KEY_NONE) { QWidget::keyPressEvent(event);@@ -294,9 +291,6 @@ void Window::keyReleaseEvent(QKeyEvent* event) {
if (event->isAutoRepeat()) { QWidget::keyReleaseEvent(event); return; - } - if (event->key() == Qt::Key_Tab) { - m_controller->setTurbo(false, false); } GBAKey key = m_inputController.mapKeyboard(event->key()); if (key == GBA_KEY_NONE) {@@ -646,6 +640,14 @@ #endif
ConfigOption* skipBios = m_config->addOption("skipBios"); skipBios->connect([this](const QVariant& value) { m_controller->setSkipBIOS(value.toBool()); }); + + QMenu* other = new QMenu(tr("Other"), this); + m_shortcutController->addMenu(other); + m_shortcutController->addFunctions(other, [this]() { + m_controller->setTurbo(true, false); + }, [this]() { + m_controller->setTurbo(false, false); + }, QKeySequence(Qt::Key_Tab), tr("Fast Forward (held)"), "holdFastForward"); foreach (QAction* action, m_gameActions) { action->setDisabled(true);
@@ -11,6 +11,8 @@ #include <QList>
#include <QMainWindow> #include <QTimer> +#include <functional> + extern "C" { #include "gba.h" }