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 "yuzu/configuration/config.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->generalTab->PopulateHotkeyList(registry);
this->setConfiguration();
}

View File

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

View File

@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
}
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();

View File

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

View File

@ -10,58 +10,53 @@
#include "yuzu/hotkeys.h"
#include "yuzu/ui_settings.h"
struct Hotkey {
Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
HotkeyRegistry::HotkeyRegistry() = default;
HotkeyRegistry::~HotkeyRegistry() = default;
QKeySequence keyseq;
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() {
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) {
QStringList cat = shortcut.first.split("/");
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 = (Qt::ShortcutContext)shortcut.second.second;
hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
}
if (hk.shortcut)
hk.shortcut->setKey(hk.keyseq);
}
}
void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
Qt::ShortcutContext default_context) {
if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
hotkey_groups[group][action].keyseq = default_keyseq;
hotkey_groups[group][action].context = default_context;
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)));
}
}
}
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];
if (!hk.shortcut)
@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
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));
for (auto hotkey : group.second) {
for (const auto& hotkey : group.second) {
QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns);

View File

@ -4,6 +4,7 @@
#pragma once
#include <map>
#include "ui_hotkeys.h"
class QDialog;
@ -11,47 +12,69 @@ class QKeySequence;
class QSettings;
class QShortcut;
/**
* 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 = QKeySequence(),
Qt::ShortcutContext default_context = Qt::WindowShortcut);
class HotkeyRegistry final {
public:
friend class GHotkeysDialog;
/**
* 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);
explicit HotkeyRegistry();
~HotkeyRegistry();
/**
* Saves all registered hotkeys to the settings file.
*
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* settings group will be created to store the key sequence and the hotkey context.
*/
void SaveHotkeys();
/**
* Loads hotkeys from the settings file.
*
* @note Yet unregistered hotkeys which are present in the settings will automatically be
* registered.
*/
void LoadHotkeys();
/**
* Loads hotkeys from the settings file.
*
* @note Yet unregistered hotkeys which are present in the settings will automatically be
* registered.
*/
void LoadHotkeys();
/**
* Saves all registered hotkeys to the settings file.
*
* @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
* settings group will be created to store the key sequence and the hotkey context.
*/
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 {
Q_OBJECT
@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
public:
explicit GHotkeysDialog(QWidget* parent = nullptr);
void Populate(const HotkeyRegistry& registry);
private:
Ui::hotkeys ui;
};

View File

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

View File

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