qt/hotkey: Get rid of global hotkey map instance

Instead, we make a proper registry class and house it within the main
window, then pass it to whatever needs access to the loaded hotkeys.

This way, we avoid a global variable, and don't need to initialize a
std::map instance before the program can do anything.
This commit is contained in:
Lioncash 2018-08-07 00:43:07 -04:00
parent 826b1394e8
commit c8f6754417
8 changed files with 155 additions and 115 deletions

View file

@ -6,9 +6,12 @@
#include "ui_configure.h" #include "ui_configure.h"
#include "yuzu/configuration/config.h" #include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_dialog.h"
#include "yuzu/hotkeys.h"
ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
: QDialog(parent), ui(new Ui::ConfigureDialog) {
ui->setupUi(this); ui->setupUi(this);
ui->generalTab->PopulateHotkeyList(registry);
this->setConfiguration(); this->setConfiguration();
} }

View file

@ -7,6 +7,8 @@
#include <memory> #include <memory>
#include <QDialog> #include <QDialog>
class HotkeyRegistry;
namespace Ui { namespace Ui {
class ConfigureDialog; class ConfigureDialog;
} }
@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ConfigureDialog(QWidget* parent); explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
~ConfigureDialog(); ~ConfigureDialog();
void applyConfiguration(); void applyConfiguration();

View file

@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
} }
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
ui->widget->Populate(registry);
}
void ConfigureGeneral::applyConfiguration() { void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();

View file

@ -7,6 +7,8 @@
#include <memory> #include <memory>
#include <QWidget> #include <QWidget>
class HotkeyRegistry;
namespace Ui { namespace Ui {
class ConfigureGeneral; class ConfigureGeneral;
} }
@ -18,11 +20,11 @@ public:
explicit ConfigureGeneral(QWidget* parent = nullptr); explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral(); ~ConfigureGeneral();
void PopulateHotkeyList(const HotkeyRegistry& registry);
void applyConfiguration(); void applyConfiguration();
private: private:
void setConfiguration(); void setConfiguration();
private:
std::unique_ptr<Ui::ConfigureGeneral> ui; std::unique_ptr<Ui::ConfigureGeneral> ui;
}; };

View file

@ -10,58 +10,53 @@
#include "yuzu/hotkeys.h" #include "yuzu/hotkeys.h"
#include "yuzu/ui_settings.h" #include "yuzu/ui_settings.h"
struct Hotkey { HotkeyRegistry::HotkeyRegistry() = default;
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} HotkeyRegistry::~HotkeyRegistry() = default;
QKeySequence keyseq; void HotkeyRegistry::LoadHotkeys() {
QShortcut* shortcut;
Qt::ShortcutContext context;
};
typedef std::map<QString, Hotkey> HotkeyMap;
typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
HotkeyGroupMap hotkey_groups;
void SaveHotkeys() {
UISettings::values.shortcuts.clear();
for (auto group : hotkey_groups) {
for (auto hotkey : group.second) {
UISettings::values.shortcuts.emplace_back(
UISettings::Shortcut(group.first + "/" + hotkey.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
hotkey.second.context)));
}
}
}
void LoadHotkeys() {
// Make sure NOT to use a reference here because it would become invalid once we call // Make sure NOT to use a reference here because it would become invalid once we call
// beginGroup() // beginGroup()
for (auto shortcut : UISettings::values.shortcuts) { for (auto shortcut : UISettings::values.shortcuts) {
QStringList cat = shortcut.first.split("/"); const QStringList cat = shortcut.first.split('/');
Q_ASSERT(cat.size() >= 2); Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters // RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) { if (!shortcut.second.first.isEmpty()) {
hk.keyseq = QKeySequence::fromString(shortcut.second.first); hk.keyseq = QKeySequence::fromString(shortcut.second.first);
hk.context = (Qt::ShortcutContext)shortcut.second.second; hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
} }
if (hk.shortcut) if (hk.shortcut)
hk.shortcut->setKey(hk.keyseq); hk.shortcut->setKey(hk.keyseq);
} }
} }
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, void HotkeyRegistry::SaveHotkeys() {
Qt::ShortcutContext default_context) { UISettings::values.shortcuts.clear();
if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) { for (const auto& group : hotkey_groups) {
hotkey_groups[group][action].keyseq = default_keyseq; for (const auto& hotkey : group.second) {
hotkey_groups[group][action].context = default_context; UISettings::values.shortcuts.emplace_back(
UISettings::Shortcut(group.first + '/' + hotkey.first,
UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
hotkey.second.context)));
}
} }
} }
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) { 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;
}
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) {
Hotkey& hk = hotkey_groups[group][action]; Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut) if (!hk.shortcut)
@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
ui.setupUi(this); ui.setupUi(this);
}
for (auto group : hotkey_groups) { void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
for (const auto& group : registry.hotkey_groups) {
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
for (auto hotkey : group.second) { for (const auto& hotkey : group.second) {
QStringList columns; QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString(); columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns); QTreeWidgetItem* item = new QTreeWidgetItem(columns);

View file

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <map>
#include "ui_hotkeys.h" #include "ui_hotkeys.h"
class QDialog; class QDialog;
@ -11,47 +12,69 @@ class QKeySequence;
class QSettings; class QSettings;
class QShortcut; class QShortcut;
/** class HotkeyRegistry final {
* Register a hotkey. public:
* friend class GHotkeysDialog;
* @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
*/
void RegisterHotkey(const QString& group, const QString& action,
const QKeySequence& default_keyseq = QKeySequence(),
Qt::ShortcutContext default_context = Qt::WindowShortcut);
/** explicit HotkeyRegistry();
* Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. ~HotkeyRegistry();
*
* @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 widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
*/
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/** /**
* Saves all registered hotkeys to the settings file. * Loads hotkeys from the settings file.
* *
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a * @note Yet unregistered hotkeys which are present in the settings will automatically be
* settings group will be created to store the key sequence and the hotkey context. * registered.
*/ */
void SaveHotkeys(); void LoadHotkeys();
/** /**
* Loads hotkeys from the settings file. * Saves all registered hotkeys to the settings file.
* *
* @note Yet unregistered hotkeys which are present in the settings will automatically be * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* registered. * settings group will be created to store the key sequence and the hotkey context.
*/ */
void LoadHotkeys(); void SaveHotkeys();
/**
* Returns a QShortcut object whose activated() signal can be connected to other QObjects'
* slots.
*
* @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 widget Parent widget of the returned QShortcut.
* @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
* will be the same. Thus, you shouldn't rely on the caller really being the
* QShortcut's parent.
*/
QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
/**
* Register a hotkey.
*
* @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
*/
void RegisterHotkey(const QString& group, const QString& action,
const QKeySequence& default_keyseq = {},
Qt::ShortcutContext default_context = Qt::WindowShortcut);
private:
struct Hotkey {
QKeySequence keyseq;
QShortcut* shortcut = nullptr;
Qt::ShortcutContext context = Qt::WindowShortcut;
};
using HotkeyMap = std::map<QString, Hotkey>;
using HotkeyGroupMap = std::map<QString, HotkeyMap>;
HotkeyGroupMap hotkey_groups;
};
class GHotkeysDialog : public QWidget { class GHotkeysDialog : public QWidget {
Q_OBJECT Q_OBJECT
@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
public: public:
explicit GHotkeysDialog(QWidget* parent = nullptr); explicit GHotkeysDialog(QWidget* parent = nullptr);
void Populate(const HotkeyRegistry& registry);
private: private:
Ui::hotkeys ui; Ui::hotkeys ui;
}; };

View file

@ -208,43 +208,46 @@ void GMainWindow::InitializeRecentFileMenuActions() {
} }
void GMainWindow::InitializeHotkeys() { void GMainWindow::InitializeHotkeys() {
RegisterHotkey("Main Window", "Load File", QKeySequence::Open); hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
RegisterHotkey("Main Window", "Start Emulation"); hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
Qt::ApplicationShortcut); Qt::ApplicationShortcut);
RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
Qt::ApplicationShortcut); Qt::ApplicationShortcut);
LoadHotkeys(); hotkey_registry.LoadHotkeys();
connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
&GMainWindow::OnMenuLoadFile); this, &GMainWindow::OnMenuLoadFile);
connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this, connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
&GMainWindow::OnStartGame); &QShortcut::activated, this, &GMainWindow::OnStartGame);
connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] { connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
if (emulation_running) { this, [&] {
if (emu_thread->IsRunning()) { if (emulation_running) {
OnPauseGame(); if (emu_thread->IsRunning()) {
} else { OnPauseGame();
OnStartGame(); } else {
} OnStartGame();
} }
}); }
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated, });
ui.action_Fullscreen, &QAction::trigger); connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously, &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger);
ui.action_Fullscreen, &QAction::trigger); connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] { &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger);
if (emulation_running) { connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this),
ui.action_Fullscreen->setChecked(false); &QShortcut::activated, this, [&] {
ToggleFullscreen(); if (emulation_running) {
} ui.action_Fullscreen->setChecked(false);
}); ToggleFullscreen();
connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] { }
Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit; });
UpdateStatusBar(); connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
}); &QShortcut::activated, this, [&] {
Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
UpdateStatusBar();
});
} }
void GMainWindow::SetDefaultUIGeometry() { void GMainWindow::SetDefaultUIGeometry() {
@ -323,7 +326,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
// Fullscreen // Fullscreen
ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); ui.action_Fullscreen->setShortcut(
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help // Help
@ -757,7 +761,7 @@ void GMainWindow::ToggleWindowMode() {
} }
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this); ConfigureDialog configureDialog(this, hotkey_registry);
auto old_theme = UISettings::values.theme; auto old_theme = UISettings::values.theme;
auto result = configureDialog.exec(); auto result = configureDialog.exec();
if (result == QDialog::Accepted) { if (result == QDialog::Accepted) {
@ -896,7 +900,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.first_start = false; UISettings::values.first_start = false;
game_list->SaveInterfaceLayout(); game_list->SaveInterfaceLayout();
SaveHotkeys(); hotkey_registry.SaveHotkeys();
// Shutdown session if the emu thread is active... // Shutdown session if the emu thread is active...
if (emu_thread != nullptr) if (emu_thread != nullptr)

View file

@ -9,6 +9,7 @@
#include <QTimer> #include <QTimer>
#include "core/core.h" #include "core/core.h"
#include "ui_main.h" #include "ui_main.h"
#include "yuzu/hotkeys.h"
class Config; class Config;
class EmuThread; class EmuThread;
@ -172,6 +173,8 @@ private:
// stores default icon theme search paths for the platform // stores default icon theme search paths for the platform
QStringList default_theme_paths; QStringList default_theme_paths;
HotkeyRegistry hotkey_registry;
protected: protected:
void dropEvent(QDropEvent* event) override; void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;