summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2019-04-10 00:08:30 +0200
committerGitHub <noreply@github.com>2019-04-10 00:08:30 +0200
commit1a3098f11a21bd102c63f2bc4ebb01789d3fd93e (patch)
tree8f977d1f87ff108edaf285ee1569cdff64d747d2
parentMerge pull request #2370 from lioncash/qt-warn (diff)
parentyuzu: Make hotkeys configurable via the GUI (diff)
downloadyuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar.gz
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar.bz2
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar.lz
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar.xz
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.tar.zst
yuzu-1a3098f11a21bd102c63f2bc4ebb01789d3fd93e.zip
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/applets/profile_select.cpp1
-rw-r--r--src/yuzu/applets/profile_select.h2
-rw-r--r--src/yuzu/configuration/config.cpp59
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure.ui19
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp16
-rw-r--r--src/yuzu/configuration/configure_dialog.h3
-rw-r--r--src/yuzu/configuration/configure_general.cpp4
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui24
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp121
-rw-r--r--src/yuzu/configuration/configure_hotkeys.h48
-rw-r--r--src/yuzu/configuration/configure_hotkeys.ui42
-rw-r--r--src/yuzu/hotkeys.cpp73
-rw-r--r--src/yuzu/hotkeys.h42
-rw-r--r--src/yuzu/hotkeys.ui46
-rw-r--r--src/yuzu/main.cpp53
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/ui_settings.cpp1
-rw-r--r--src/yuzu/ui_settings.h7
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp37
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.h24
23 files changed, 426 insertions, 208 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4cab599b4..732a1bf89 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -31,6 +31,8 @@ add_executable(yuzu
configuration/configure_general.h
configuration/configure_graphics.cpp
configuration/configure_graphics.h
+ configuration/configure_hotkeys.cpp
+ configuration/configure_hotkeys.h
configuration/configure_input.cpp
configuration/configure_input.h
configuration/configure_input_player.cpp
@@ -78,6 +80,8 @@ add_executable(yuzu
ui_settings.h
util/limitable_input_dialog.cpp
util/limitable_input_dialog.h
+ util/sequence_dialog/sequence_dialog.cpp
+ util/sequence_dialog/sequence_dialog.h
util/spinbox.cpp
util/spinbox.h
util/util.cpp
@@ -95,6 +99,7 @@ set(UIS
configuration/configure_gamelist.ui
configuration/configure_general.ui
configuration/configure_graphics.ui
+ configuration/configure_hotkeys.ui
configuration/configure_input.ui
configuration/configure_input_player.ui
configuration/configure_input_simple.ui
@@ -105,7 +110,6 @@ set(UIS
configuration/configure_touchscreen_advanced.ui
configuration/configure_web.ui
compatdb.ui
- hotkeys.ui
loading_screen.ui
main.ui
)
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index f95f7fe3c..743b24d76 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -4,6 +4,7 @@
#include <mutex>
#include <QDialogButtonBox>
+#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QScrollArea>
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
index 868573324..1c2922e54 100644
--- a/src/yuzu/applets/profile_select.h
+++ b/src/yuzu/applets/profile_select.h
@@ -7,6 +7,7 @@
#include <vector>
#include <QDialog>
#include <QList>
+#include <QTreeView>
#include "core/frontend/applets/profile_select.h"
class GMainWindow;
@@ -16,7 +17,6 @@ class QLabel;
class QScrollArea;
class QStandardItem;
class QStandardItemModel;
-class QTreeView;
class QVBoxLayout;
class QtProfileSelectionDialog final : public QDialog {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index dead9f807..802db3945 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <QKeySequence>
#include <QSettings>
#include "common/file_util.h"
#include "configure_input_simple.h"
@@ -9,7 +11,6 @@
#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
#include "yuzu/configuration/config.h"
-#include "yuzu/ui_settings.h"
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -17,7 +18,6 @@ Config::Config() {
FileUtil::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
-
Reload();
}
@@ -205,6 +205,27 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
};
+// This shouldn't have anything except static initializers (no functions). So
+// QKeySequnce(...).toString() is NOT ALLOWED HERE.
+// This must be in alphabetical order according to action name as it must have the same order as
+// UISetting::values.shortcuts, which is alphabetically ordered.
+const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{
+ {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}},
+ {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}},
+ {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}},
+ {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}},
+ {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}},
+ {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}},
+ {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}},
+ {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}},
+ {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}},
+ {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}},
+ {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}},
+ {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}},
+ {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}},
+ {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}},
+ {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}};
+
void Config::ReadPlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
auto& player = Settings::values.players[p];
@@ -508,20 +529,15 @@ void Config::ReadValues() {
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");
- QStringList groups = qt_config->childGroups();
- for (auto group : groups) {
+ for (auto [name, group, shortcut] : default_hotkeys) {
+ auto [keyseq, context] = shortcut;
qt_config->beginGroup(group);
-
- QStringList hotkeys = qt_config->childGroups();
- for (auto hotkey : hotkeys) {
- qt_config->beginGroup(hotkey);
- UISettings::values.shortcuts.emplace_back(UISettings::Shortcut(
- group + "/" + hotkey,
- UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(),
- ReadSetting("Context").toInt())));
- qt_config->endGroup();
- }
-
+ qt_config->beginGroup(name);
+ UISettings::values.shortcuts.push_back(
+ {name,
+ group,
+ {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}});
+ qt_config->endGroup();
qt_config->endGroup();
}
qt_config->endGroup();
@@ -758,9 +774,16 @@ void Config::SaveValues() {
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");
- for (auto shortcut : UISettings::values.shortcuts) {
- WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first);
- WriteSetting(shortcut.first + "/Context", shortcut.second.second);
+ // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
+ // However, their ordering must also be the same.
+ for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
+ auto [name, group, shortcut] = UISettings::values.shortcuts[i];
+ qt_config->beginGroup(group);
+ qt_config->beginGroup(name);
+ WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first);
+ WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second);
+ qt_config->endGroup();
+ qt_config->endGroup();
}
qt_config->endGroup();
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index f4185db18..221d2364c 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
#include <string>
#include <QVariant>
#include "core/settings.h"
+#include "yuzu/ui_settings.h"
class QSettings;
@@ -47,6 +48,8 @@ private:
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
+ static const std::array<UISettings::Shortcut, 15> default_hotkeys;
+
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
};
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 3f03f0b77..267717bc9 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -7,9 +7,15 @@
<x>0</x>
<y>0</y>
<width>382</width>
- <height>241</height>
+ <height>650</height>
</rect>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>650</height>
+ </size>
+ </property>
<property name="windowTitle">
<string>yuzu Configuration</string>
</property>
@@ -62,6 +68,11 @@
<string>Input</string>
</attribute>
</widget>
+ <widget class="ConfigureHotkeys" name="hotkeysTab">
+ <attribute name="title">
+ <string>Hotkeys</string>
+ </attribute>
+ </widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
@@ -150,6 +161,12 @@
<header>configuration/configure_input_simple.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>ConfigureHotkeys</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_hotkeys.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 777050405..51bd1f121 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -8,20 +8,22 @@
#include "ui_configure.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h"
+#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/hotkeys.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
- : QDialog(parent), ui(new Ui::ConfigureDialog) {
+ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
+ : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) {
ui->setupUi(this);
- ui->generalTab->PopulateHotkeyList(registry);
+ ui->hotkeysTab->Populate(registry);
this->setConfiguration();
this->PopulateSelectionList();
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs);
-
adjustSize();
-
ui->selectorList->setCurrentRow(0);
+
+ // Synchronise lists upon initialisation
+ ui->hotkeysTab->EmitHotkeysChanged();
}
ConfigureDialog::~ConfigureDialog() = default;
@@ -34,6 +36,7 @@ void ConfigureDialog::applyConfiguration() {
ui->systemTab->applyConfiguration();
ui->profileManagerTab->applyConfiguration();
ui->inputTab->applyConfiguration();
+ ui->hotkeysTab->applyConfiguration(registry);
ui->graphicsTab->applyConfiguration();
ui->audioTab->applyConfiguration();
ui->debugTab->applyConfiguration();
@@ -47,7 +50,7 @@ void ConfigureDialog::PopulateSelectionList() {
{{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}},
{tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}},
{tr("Graphics"), {tr("Graphics")}},
- {tr("Controls"), {tr("Input")}}}};
+ {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}};
for (const auto& entry : items) {
auto* const item = new QListWidgetItem(entry.first);
@@ -66,6 +69,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{tr("System"), ui->systemTab},
{tr("Profiles"), ui->profileManagerTab},
{tr("Input"), ui->inputTab},
+ {tr("Hotkeys"), ui->hotkeysTab},
{tr("Graphics"), ui->graphicsTab},
{tr("Audio"), ui->audioTab},
{tr("Debug"), ui->debugTab},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 243d9fa09..2363ba584 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -17,7 +17,7 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
+ explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
~ConfigureDialog() override;
void applyConfiguration();
@@ -28,4 +28,5 @@ private:
void PopulateSelectionList();
std::unique_ptr<Ui::ConfigureDialog> ui;
+ HotkeyRegistry& registry;
};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 389fcf667..eeb038afb 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -35,10 +35,6 @@ void ConfigureGeneral::setConfiguration() {
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
}
-void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
- ui->widget->Populate(registry);
-}
-
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 59738af40..df41d995b 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -20,7 +20,6 @@ public:
explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral() override;
- void PopulateHotkeyList(const HotkeyRegistry& registry);
void applyConfiguration();
private:
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 01d1c0b8e..1a5721fe7 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -98,22 +98,6 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="HotKeysGroupBox">
- <property name="title">
- <string>Hotkeys</string>
- </property>
- <layout class="QHBoxLayout" name="HotKeysHorizontalLayout">
- <item>
- <layout class="QVBoxLayout" name="HotKeysVerticalLayout">
- <item>
- <widget class="GHotkeysDialog" name="widget" native="true"/>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -130,14 +114,6 @@
</item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>GHotkeysDialog</class>
- <extends>QWidget</extends>
- <header>hotkeys.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
new file mode 100644
index 000000000..bfb562535
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -0,0 +1,121 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QMessageBox>
+#include <QStandardItemModel>
+#include "core/settings.h"
+#include "ui_configure_hotkeys.h"
+#include "yuzu/configuration/configure_hotkeys.h"
+#include "yuzu/hotkeys.h"
+#include "yuzu/util/sequence_dialog/sequence_dialog.h"
+
+ConfigureHotkeys::ConfigureHotkeys(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) {
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+
+ model = new QStandardItemModel(this);
+ model->setColumnCount(3);
+ model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")});
+
+ connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure);
+ ui->hotkey_list->setModel(model);
+
+ // TODO(Kloen): Make context configurable as well (hiding the column for now)
+ ui->hotkey_list->hideColumn(2);
+
+ ui->hotkey_list->setColumnWidth(0, 200);
+ ui->hotkey_list->resizeColumnToContents(1);
+}
+
+ConfigureHotkeys::~ConfigureHotkeys() = default;
+
+void ConfigureHotkeys::EmitHotkeysChanged() {
+ emit HotkeysChanged(GetUsedKeyList());
+}
+
+QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const {
+ QList<QKeySequence> list;
+ for (int r = 0; r < model->rowCount(); r++) {
+ const QStandardItem* parent = model->item(r, 0);
+ for (int r2 = 0; r2 < parent->rowCount(); r2++) {
+ const QStandardItem* keyseq = parent->child(r2, 1);
+ list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
+ }
+ }
+ return list;
+}
+
+void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
+ for (const auto& group : registry.hotkey_groups) {
+ auto* parent_item = new QStandardItem(group.first);
+ parent_item->setEditable(false);
+ for (const auto& hotkey : group.second) {
+ auto* action = new QStandardItem(hotkey.first);
+ auto* keyseq =
+ new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
+ action->setEditable(false);
+ keyseq->setEditable(false);
+ parent_item->appendRow({action, keyseq});
+ }
+ model->appendRow(parent_item);
+ }
+
+ ui->hotkey_list->expandAll();
+}
+
+void ConfigureHotkeys::Configure(QModelIndex index) {
+ if (index.parent() == QModelIndex())
+ return;
+
+ index = index.sibling(index.row(), 1);
+ auto* model = ui->hotkey_list->model();
+ auto previous_key = model->data(index);
+
+ auto* hotkey_dialog = new SequenceDialog;
+ int return_code = hotkey_dialog->exec();
+
+ auto key_sequence = hotkey_dialog->GetSequence();
+
+ if (return_code == QDialog::Rejected || key_sequence.isEmpty())
+ return;
+
+ if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) {
+ QMessageBox::critical(this, tr("Error in inputted key"),
+ tr("You're using a key that's already bound."));
+ } else {
+ model->setData(index, key_sequence.toString(QKeySequence::NativeText));
+ EmitHotkeysChanged();
+ }
+}
+
+bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) {
+ return GetUsedKeyList().contains(key_sequence);
+}
+
+void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) {
+ for (int key_id = 0; key_id < model->rowCount(); key_id++) {
+ const QStandardItem* parent = model->item(key_id, 0);
+ for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
+ const QStandardItem* action = parent->child(key_column_id, 0);
+ const QStandardItem* keyseq = parent->child(key_column_id, 1);
+ for (auto& [group, sub_actions] : registry.hotkey_groups) {
+ if (group != parent->text())
+ continue;
+ for (auto& [action_name, hotkey] : sub_actions) {
+ if (action_name != action->text())
+ continue;
+ hotkey.keyseq = QKeySequence(keyseq->text());
+ }
+ }
+ }
+ }
+
+ registry.SaveHotkeys();
+ Settings::Apply();
+}
+
+void ConfigureHotkeys::retranslateUi() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h
new file mode 100644
index 000000000..cd203aad6
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+#include "core/settings.h"
+
+namespace Ui {
+class ConfigureHotkeys;
+}
+
+class HotkeyRegistry;
+class QStandardItemModel;
+
+class ConfigureHotkeys : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureHotkeys(QWidget* parent = nullptr);
+ ~ConfigureHotkeys() override;
+
+ void applyConfiguration(HotkeyRegistry& registry);
+ void retranslateUi();
+
+ void EmitHotkeysChanged();
+
+ /**
+ * Populates the hotkey list widget using data from the provided registry.
+ * Called everytime the Configure dialog is opened.
+ * @param registry The HotkeyRegistry whose data is used to populate the list.
+ */
+ void Populate(const HotkeyRegistry& registry);
+
+signals:
+ void HotkeysChanged(QList<QKeySequence> new_key_list);
+
+private:
+ void Configure(QModelIndex index);
+ bool IsUsedKey(QKeySequence key_sequence);
+ QList<QKeySequence> GetUsedKeyList() const;
+
+ std::unique_ptr<Ui::ConfigureHotkeys> ui;
+
+ QStandardItemModel* model;
+};
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui
new file mode 100644
index 000000000..0d0b70f38
--- /dev/null
+++ b/src/yuzu/configuration/configure_hotkeys.ui
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureHotkeys</class>
+ <widget class="QWidget" name="ConfigureHotkeys">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>363</width>
+ <height>388</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Hotkey Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Double-click on a binding to change it.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTreeView" name="hotkey_list">
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui> \ No newline at end of file
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index dce399774..4582e7f21 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <map>
#include <QKeySequence>
#include <QShortcut>
#include <QTreeWidgetItem>
@@ -13,47 +12,32 @@
HotkeyRegistry::HotkeyRegistry() = default;
HotkeyRegistry::~HotkeyRegistry() = default;
-void HotkeyRegistry::LoadHotkeys() {
- // Make sure NOT to use a reference here because it would become invalid once we call
- // beginGroup()
- for (auto shortcut : UISettings::values.shortcuts) {
- const QStringList cat = shortcut.first.split('/');
- Q_ASSERT(cat.size() >= 2);
-
- // RegisterHotkey assigns default keybindings, so use old values as default parameters
- Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
- if (!shortcut.second.first.isEmpty()) {
- hk.keyseq = QKeySequence::fromString(shortcut.second.first);
- hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
- }
- if (hk.shortcut)
- hk.shortcut->setKey(hk.keyseq);
- }
-}
-
void HotkeyRegistry::SaveHotkeys() {
UISettings::values.shortcuts.clear();
for (const auto& group : hotkey_groups) {
for (const auto& hotkey : group.second) {
- UISettings::values.shortcuts.emplace_back(
- UISettings::Shortcut(group.first + '/' + hotkey.first,
- UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
- hotkey.second.context)));
+ UISettings::values.shortcuts.push_back(
+ {hotkey.first, group.first,
+ UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
+ hotkey.second.context)});
}
}
}
-void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action,
- const QKeySequence& default_keyseq,
- Qt::ShortcutContext default_context) {
- auto& hotkey_group = hotkey_groups[group];
- if (hotkey_group.find(action) != hotkey_group.end()) {
- return;
+void HotkeyRegistry::LoadHotkeys() {
+ // Make sure NOT to use a reference here because it would become invalid once we call
+ // beginGroup()
+ for (auto shortcut : UISettings::values.shortcuts) {
+ Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
+ if (!shortcut.shortcut.first.isEmpty()) {
+ hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText);
+ hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second);
+ }
+ if (hk.shortcut) {
+ hk.shortcut->disconnect();
+ hk.shortcut->setKey(hk.keyseq);
+ }
}
-
- auto& hotkey_action = hotkey_groups[group][action];
- hotkey_action.keyseq = default_keyseq;
- hotkey_action.context = default_context;
}
QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
@@ -65,24 +49,11 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
return hk.shortcut;
}
-GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
- ui.setupUi(this);
+QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) {
+ return hotkey_groups[group][action].keyseq;
}
-void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
- for (const auto& group : registry.hotkey_groups) {
- QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
- for (const auto& hotkey : group.second) {
- QStringList columns;
- columns << hotkey.first << hotkey.second.keyseq.toString();
- QTreeWidgetItem* item = new QTreeWidgetItem(columns);
- toplevel_item->addChild(item);
- }
- ui.treeWidget->addTopLevelItem(toplevel_item);
- }
- // TODO: Make context configurable as well (hiding the column for now)
- ui.treeWidget->setColumnCount(2);
-
- ui.treeWidget->resizeColumnToContents(0);
- ui.treeWidget->resizeColumnToContents(1);
+Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group,
+ const QString& action) {
+ return hotkey_groups[group][action].context;
}
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index f38e6c002..4f526dc7e 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -5,7 +5,6 @@
#pragma once
#include <map>
-#include "ui_hotkeys.h"
class QDialog;
class QKeySequence;
@@ -14,7 +13,7 @@ class QShortcut;
class HotkeyRegistry final {
public:
- friend class GHotkeysDialog;
+ friend class ConfigureHotkeys;
explicit HotkeyRegistry();
~HotkeyRegistry();
@@ -49,22 +48,27 @@ public:
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/**
- * Register a hotkey.
+ * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut.
*
- * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
- * @param action Name of the action (e.g. "Start Emulation", "Load Image")
- * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the
- * settings file before
- * @param default_context Default context to assign if the hotkey wasn't present in the settings
- * file before
- * @warning Both the group and action strings will be displayed in the hotkey settings dialog
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image").
+ */
+ QKeySequence GetKeySequence(const QString& group, const QString& action);
+
+ /**
+ * Returns a Qt::ShortcutContext object who can be connected to other
+ * QAction::setShortcutContext.
+ *
+ * @param group General group this shortcut context belongs to (e.g. "Main Window",
+ * "Debugger").
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image").
*/
- void RegisterHotkey(const QString& group, const QString& action,
- const QKeySequence& default_keyseq = {},
- Qt::ShortcutContext default_context = Qt::WindowShortcut);
+ Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action);
private:
struct Hotkey {
+ Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
+
QKeySequence keyseq;
QShortcut* shortcut = nullptr;
Qt::ShortcutContext context = Qt::WindowShortcut;
@@ -75,15 +79,3 @@ private:
HotkeyGroupMap hotkey_groups;
};
-
-class GHotkeysDialog : public QWidget {
- Q_OBJECT
-
-public:
- explicit GHotkeysDialog(QWidget* parent = nullptr);
-
- void Populate(const HotkeyRegistry& registry);
-
-private:
- Ui::hotkeys ui;
-};
diff --git a/src/yuzu/hotkeys.ui b/src/yuzu/hotkeys.ui
deleted file mode 100644
index 050fe064e..000000000
--- a/src/yuzu/hotkeys.ui
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>hotkeys</class>
- <widget class="QWidget" name="hotkeys">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>363</width>
- <height>388</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Hotkey Settings</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QTreeWidget" name="treeWidget">
- <property name="selectionBehavior">
- <enum>QAbstractItemView::SelectItems</enum>
- </property>
- <property name="headerHidden">
- <bool>false</bool>
- </property>
- <column>
- <property name="text">
- <string>Action</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Hotkey</string>
- </property>
- </column>
- <column>
- <property name="text">
- <string>Context</string>
- </property>
- </column>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2b9db69a3..77b6f7cc8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -514,33 +514,34 @@ void GMainWindow::InitializeRecentFileMenuActions() {
}
void GMainWindow::InitializeHotkeys() {
- hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
- hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
- hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
- hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5));
- hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
- hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
- Qt::ApplicationShortcut);
- hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
- Qt::ApplicationShortcut);
- hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"),
- Qt::ApplicationShortcut);
- hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
- Qt::ApplicationShortcut);
- hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
- Qt::ApplicationShortcut);
- hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
- QKeySequence(QKeySequence::Print));
- hotkey_registry.RegisterHotkey("Main Window", "Change Docked Mode", QKeySequence(Qt::Key_F10));
-
hotkey_registry.LoadHotkeys();
+ ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File"));
+ ui.action_Load_File->setShortcutContext(
+ hotkey_registry.GetShortcutContext("Main Window", "Load File"));
+
+ ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu"));
+ ui.action_Exit->setShortcutContext(
+ hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu"));
+
+ ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation"));
+ ui.action_Stop->setShortcutContext(
+ hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation"));
+
+ ui.action_Show_Filter_Bar->setShortcut(
+ hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar"));
+ ui.action_Show_Filter_Bar->setShortcutContext(
+ hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar"));
+
+ ui.action_Show_Status_Bar->setShortcut(
+ hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar"));
+ ui.action_Show_Status_Bar->setShortcutContext(
+ hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar"));
+
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
this, &GMainWindow::OnMenuLoadFile);
- connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
- &QShortcut::activated, this, &GMainWindow::OnStartGame);
- connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
- this, [&] {
+ connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this),
+ &QShortcut::activated, this, [&] {
if (emulation_running) {
if (emu_thread->IsRunning()) {
OnPauseGame();
@@ -549,8 +550,8 @@ void GMainWindow::InitializeHotkeys() {
}
}
});
- connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this,
- [this] {
+ connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this),
+ &QShortcut::activated, this, [this] {
if (!Core::System::GetInstance().IsPoweredOn())
return;
BootGame(QString(game_path));
@@ -697,7 +698,6 @@ void GMainWindow::ConnectMenuEvents() {
&GMainWindow::ToggleWindowMode);
connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this,
&GMainWindow::OnDisplayTitleBars);
- ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F"));
connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar);
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
@@ -1662,6 +1662,7 @@ void GMainWindow::OnConfigure() {
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
+ InitializeHotkeys();
if (UISettings::values.theme != old_theme)
UpdateUITheme();
if (UISettings::values.enable_discord_presence != old_discord_presence)
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7f3aa998e..ba406ae64 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -120,7 +120,6 @@ private:
void InitializeWidgets();
void InitializeDebugWidgets();
void InitializeRecentFileMenuActions();
- void InitializeHotkeys();
void SetDefaultUIGeometry();
void RestoreUIState();
@@ -196,6 +195,7 @@ private slots:
void OnAbout();
void OnToggleFilterBar();
void OnDisplayTitleBars(bool);
+ void InitializeHotkeys();
void ToggleFullscreen();
void ShowFullscreen();
void HideFullscreen();
diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp
index a314493fc..4bdc302e0 100644
--- a/src/yuzu/ui_settings.cpp
+++ b/src/yuzu/ui_settings.cpp
@@ -12,5 +12,4 @@ const Themes themes{{
}};
Values values = {};
-
} // namespace UISettings
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 82aaeedb0..45e705b61 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -15,7 +15,12 @@
namespace UISettings {
using ContextualShortcut = std::pair<QString, int>;
-using Shortcut = std::pair<QString, ContextualShortcut>;
+
+struct Shortcut {
+ QString name;
+ QString group;
+ ContextualShortcut shortcut;
+};
using Themes = std::array<std::pair<const char*, const char*>, 2>;
extern const Themes themes;
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
new file mode 100644
index 000000000..d3edf6ec3
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -0,0 +1,37 @@
+// Copyright 2018 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QDialogButtonBox>
+#include <QKeySequenceEdit>
+#include <QVBoxLayout>
+#include "yuzu/util/sequence_dialog/sequence_dialog.h"
+
+SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
+ setWindowTitle(tr("Enter a hotkey"));
+ auto* layout = new QVBoxLayout(this);
+ key_sequence = new QKeySequenceEdit;
+ layout->addWidget(key_sequence);
+ auto* buttons =
+ new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
+ buttons->setCenterButtons(true);
+ layout->addWidget(buttons);
+ connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+}
+
+SequenceDialog::~SequenceDialog() = default;
+
+QKeySequence SequenceDialog::GetSequence() const {
+ // Only the first key is returned. The other 3, if present, are ignored.
+ return QKeySequence(key_sequence->keySequence()[0]);
+}
+
+bool SequenceDialog::focusNextPrevChild(bool next) {
+ return false;
+}
+
+void SequenceDialog::closeEvent(QCloseEvent*) {
+ reject();
+}
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.h b/src/yuzu/util/sequence_dialog/sequence_dialog.h
new file mode 100644
index 000000000..969c77740
--- /dev/null
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.h
@@ -0,0 +1,24 @@
+// Copyright 2018 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QDialog>
+
+class QKeySequenceEdit;
+
+class SequenceDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit SequenceDialog(QWidget* parent = nullptr);
+ ~SequenceDialog() override;
+
+ QKeySequence GetSequence() const;
+ void closeEvent(QCloseEvent*) override;
+
+private:
+ QKeySequenceEdit* key_sequence;
+ bool focusNextPrevChild(bool next) override;
+};