diff options
Diffstat (limited to 'src/yuzu')
34 files changed, 637 insertions, 335 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 5c1b65a2c..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> @@ -58,10 +59,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) scroll_area = new QScrollArea; - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); @@ -163,6 +161,6 @@ void QtProfileSelector::SelectProfile( void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; callback(uuid); } 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/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 8a26fdff1..f3eb29b25 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -75,13 +75,13 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); }); - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(parameters.submit_text.empty() - ? tr("OK") - : QString::fromStdU16String(parameters.submit_text), - QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); + if (parameters.submit_text.empty()) { + buttons->addButton(QDialogButtonBox::Ok); + } else { + buttons->addButton(QString::fromStdU16String(parameters.submit_text), + QDialogButtonBox::AcceptRole); + } connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); layout->addWidget(header_label); @@ -141,12 +141,12 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; text_output(text); } void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_check(); } diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp index 979b9ec14..ac80b2fa2 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/web_browser.cpp @@ -104,12 +104,12 @@ void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_r void QtWebBrowser::MainWindowUnpackRomFS() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; unpack_romfs_callback(); } void QtWebBrowser::MainWindowFinishedBrowsing() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_callback(); } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 05ad19e1d..7438fbc0a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -67,7 +67,7 @@ void EmuThread::run() { was_active = false; } else { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); } } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 7226e690e..3183621bc 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -53,7 +53,7 @@ public: * @note This function is thread-safe */ void SetRunning(bool running) { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; this->running = running; lock.unlock(); running_cv.notify_all(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 4650f96a3..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]; @@ -413,7 +434,6 @@ void Config::ReadValues() { qt_config->beginGroup("System"); Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); - Settings::values.enable_nfc = ReadSetting("enable_nfc", true).toBool(); Settings::values.current_user = std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1); @@ -509,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(); @@ -675,7 +690,6 @@ void Config::SaveValues() { qt_config->beginGroup("System"); WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false); - WriteSetting("enable_nfc", Settings::values.enable_nfc, true); WriteSetting("current_user", Settings::values.current_user, 0); WriteSetting("language_index", Settings::values.language_index, 1); @@ -760,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 4116b6cd7..eeb038afb 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -33,11 +33,6 @@ void ConfigureGeneral::setConfiguration() { ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); - ui->enable_nfc->setChecked(Settings::values.enable_nfc); -} - -void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { - ui->widget->Populate(registry); } void ConfigureGeneral::applyConfiguration() { @@ -48,5 +43,4 @@ void ConfigureGeneral::applyConfiguration() { ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); - Settings::values.enable_nfc = ui->enable_nfc->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 dff0ad5d0..1a5721fe7 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -71,26 +71,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="EmulationGroupBox"> - <property name="title"> - <string>Emulation</string> - </property> - <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> - <item> - <layout class="QVBoxLayout" name="EmulationVerticalLayout"> - <item> - <widget class="QCheckBox" name="enable_nfc"> - <property name="text"> - <string>Enable NFC</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <widget class="QGroupBox" name="theme_group_box"> <property name="title"> <string>Theme</string> @@ -118,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> @@ -150,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/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 11023ed63..f2d14becf 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -7,6 +7,7 @@ #include <QDebug> #include <QFileDialog> #include <QLabel> +#include <QMessageBox> #include <QMouseEvent> #include <QPushButton> #include <QScrollArea> @@ -95,50 +96,91 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext surface_picker_y_control = new QSpinBox; surface_picker_y_control->setRange(0, max_dimension - 1); - surface_format_control = new QComboBox; - + // clang-format off // Color formats sorted by Maxwell texture format index - surface_format_control->addItem(tr("None")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("A8R8G8B8")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("DXT1")); - surface_format_control->addItem(tr("DXT23")); - surface_format_control->addItem(tr("DXT45")); - surface_format_control->addItem(tr("DXN1")); - surface_format_control->addItem(tr("DXN2")); + const QStringList surface_formats{ + tr("None"), + QStringLiteral("R32_G32_B32_A32"), + QStringLiteral("R32_G32_B32"), + QStringLiteral("R16_G16_B16_A16"), + QStringLiteral("R32_G32"), + QStringLiteral("R32_B24G8"), + QStringLiteral("ETC2_RGB"), + QStringLiteral("X8B8G8R8"), + QStringLiteral("A8R8G8B8"), + QStringLiteral("A2B10G10R10"), + QStringLiteral("ETC2_RGB_PTA"), + QStringLiteral("ETC2_RGBA"), + QStringLiteral("R16_G16"), + QStringLiteral("G8R24"), + QStringLiteral("G24R8"), + QStringLiteral("R32"), + QStringLiteral("BC6H_SF16"), + QStringLiteral("BC6H_UF16"), + QStringLiteral("A4B4G4R4"), + QStringLiteral("A5B5G5R1"), + QStringLiteral("A1B5G5R5"), + QStringLiteral("B5G6R5"), + QStringLiteral("B6G5R5"), + QStringLiteral("BC7U"), + QStringLiteral("G8R8"), + QStringLiteral("EAC"), + QStringLiteral("EACX2"), + QStringLiteral("R16"), + QStringLiteral("Y8_VIDEO"), + QStringLiteral("R8"), + QStringLiteral("G4R4"), + QStringLiteral("R1"), + QStringLiteral("E5B9G9R9_SHAREDEXP"), + QStringLiteral("BF10GF11RF11"), + QStringLiteral("G8B8G8R8"), + QStringLiteral("B8G8R8G8"), + QStringLiteral("DXT1"), + QStringLiteral("DXT23"), + QStringLiteral("DXT45"), + QStringLiteral("DXN1"), + QStringLiteral("DXN2"), + QStringLiteral("Z24S8"), + QStringLiteral("X8Z24"), + QStringLiteral("S8Z24"), + QStringLiteral("X4V4Z24__COV4R4V"), + QStringLiteral("X4V4Z24__COV8R8V"), + QStringLiteral("V8Z24__COV4R12V"), + QStringLiteral("ZF32"), + QStringLiteral("ZF32_X24S8"), + QStringLiteral("X8Z24_X20V4S8__COV4R4V"), + QStringLiteral("X8Z24_X20V4S8__COV8R8V"), + QStringLiteral("ZF32_X20V4X8__COV4R4V"), + QStringLiteral("ZF32_X20V4X8__COV8R8V"), + QStringLiteral("ZF32_X20V4S8__COV4R4V"), + QStringLiteral("ZF32_X20V4S8__COV8R8V"), + QStringLiteral("X8Z24_X16V8S8__COV4R12V"), + QStringLiteral("ZF32_X16V8X8__COV4R12V"), + QStringLiteral("ZF32_X16V8S8__COV4R12V"), + QStringLiteral("Z16"), + QStringLiteral("V8Z24__COV8R24V"), + QStringLiteral("X8Z24_X16V8S8__COV8R24V"), + QStringLiteral("ZF32_X16V8X8__COV8R24V"), + QStringLiteral("ZF32_X16V8S8__COV8R24V"), + QStringLiteral("ASTC_2D_4X4"), + QStringLiteral("ASTC_2D_5X5"), + QStringLiteral("ASTC_2D_6X6"), + QStringLiteral("ASTC_2D_8X8"), + QStringLiteral("ASTC_2D_10X10"), + QStringLiteral("ASTC_2D_12X12"), + QStringLiteral("ASTC_2D_5X4"), + QStringLiteral("ASTC_2D_6X5"), + QStringLiteral("ASTC_2D_8X6"), + QStringLiteral("ASTC_2D_10X8"), + QStringLiteral("ASTC_2D_12X10"), + QStringLiteral("ASTC_2D_8X5"), + QStringLiteral("ASTC_2D_10X5"), + QStringLiteral("ASTC_2D_10X6"), + }; + // clang-format on + + surface_format_control = new QComboBox; + surface_format_control->addItems(surface_formats); surface_info_label = new QLabel(); surface_info_label->setWordWrap(true); @@ -157,22 +199,20 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext // Connections connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); - connect(surface_source_list, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + connect(surface_source_list, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsSurfaceWidget::OnSurfaceSourceChanged); connect(surface_address_control, &CSpinBox::ValueChanged, this, &GraphicsSurfaceWidget::OnSurfaceAddressChanged); - connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); - connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); - connect(surface_format_control, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + connect(surface_width_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfaceWidthChanged); + connect(surface_height_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfaceHeightChanged); + connect(surface_format_control, qOverload<int>(&QComboBox::currentIndexChanged), this, &GraphicsSurfaceWidget::OnSurfaceFormatChanged); - connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); - connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); + connect(surface_picker_x_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfacePickerXChanged); + connect(surface_picker_y_control, qOverload<int>(&QSpinBox::valueChanged), this, + &GraphicsSurfaceWidget::OnSurfacePickerYChanged); connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); auto main_widget = new QWidget; @@ -420,40 +460,56 @@ void GraphicsSurfaceWidget::OnUpdate() { } void GraphicsSurfaceWidget::SaveSurface() { - QString png_filter = tr("Portable Network Graphic (*.png)"); - QString bin_filter = tr("Binary data (*.bin)"); + const QString png_filter = tr("Portable Network Graphic (*.png)"); + const QString bin_filter = tr("Binary data (*.bin)"); - QString selectedFilter; - QString filename = QFileDialog::getSaveFileName( + QString selected_filter; + const QString filename = QFileDialog::getSaveFileName( this, tr("Save Surface"), - QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), - QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); + QStringLiteral("texture-0x%1.png").arg(QString::number(surface_address, 16)), + QStringLiteral("%1;;%2").arg(png_filter, bin_filter), &selected_filter); if (filename.isEmpty()) { // If the user canceled the dialog, don't save anything. return; } - if (selectedFilter == png_filter) { - const QPixmap* pixmap = surface_picture_label->pixmap(); + if (selected_filter == png_filter) { + const QPixmap* const pixmap = surface_picture_label->pixmap(); ASSERT_MSG(pixmap != nullptr, "No pixmap set"); - QFile file(filename); - file.open(QIODevice::WriteOnly); - if (pixmap) - pixmap->save(&file, "PNG"); - } else if (selectedFilter == bin_filter) { + QFile file{filename}; + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); + return; + } + + if (!pixmap->save(&file, "PNG")) { + QMessageBox::warning(this, tr("Error"), + tr("Failed to save surface data to file '%1'").arg(filename)); + } + } else if (selected_filter == bin_filter) { auto& gpu = Core::System::GetInstance().GPU(); - std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); + const std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); - const u8* buffer = Memory::GetPointer(*address); + const u8* const buffer = Memory::GetPointer(*address); ASSERT_MSG(buffer != nullptr, "Memory not accessible"); - QFile file(filename); - file.open(QIODevice::WriteOnly); - int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); - QByteArray data(reinterpret_cast<const char*>(buffer), size); - file.write(data); + QFile file{filename}; + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, tr("Error"), tr("Failed to open file '%1'").arg(filename)); + return; + } + + const int size = + surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); + const QByteArray data(reinterpret_cast<const char*>(buffer), size); + if (file.write(data) != data.size()) { + QMessageBox::warning( + this, tr("Error"), + tr("Failed to completely write surface data to file. The saved data will " + "likely be corrupt.")); + } } else { UNREACHABLE_MSG("Unhandled filter selected"); } diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index 8b30e0a85..86e03e46d 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp @@ -7,6 +7,7 @@ #include <QMouseEvent> #include <QPainter> #include <QString> +#include <QTimer> #include "common/common_types.h" #include "common/microprofile.h" #include "yuzu/debugger/profiler.h" diff --git a/src/yuzu/debugger/profiler.h b/src/yuzu/debugger/profiler.h index eae1e9e3c..8e69fdb06 100644 --- a/src/yuzu/debugger/profiler.h +++ b/src/yuzu/debugger/profiler.h @@ -4,10 +4,11 @@ #pragma once -#include <QAbstractItemModel> -#include <QDockWidget> -#include <QTimer> -#include "common/microprofile.h" +#include <QWidget> + +class QAction; +class QHideEvent; +class QShowEvent; class MicroProfileDialog : public QWidget { Q_OBJECT diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 06ad74ffe..593bb681f 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -234,6 +234,9 @@ QString WaitTreeThread::GetText() const { case Kernel::ThreadStatus::WaitMutex: status = tr("waiting for mutex"); break; + case Kernel::ThreadStatus::WaitCondVar: + status = tr("waiting for condition variable"); + break; case Kernel::ThreadStatus::WaitArb: status = tr("waiting for address arbiter"); break; @@ -269,6 +272,7 @@ QColor WaitTreeThread::GetColor() const { case Kernel::ThreadStatus::WaitSynchAll: case Kernel::ThreadStatus::WaitSynchAny: case Kernel::ThreadStatus::WaitMutex: + case Kernel::ThreadStatus::WaitCondVar: case Kernel::ThreadStatus::WaitArb: return QColor(Qt::GlobalColor::red); case Kernel::ThreadStatus::Dormant: diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5e5737030..4b67656ac 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -331,6 +331,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { QMenu context_menu; QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); + QAction* open_transferable_shader_cache = + context_menu.addAction(tr("Open Transferable Shader Cache")); context_menu.addSeparator(); QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); @@ -346,6 +348,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); connect(open_lfs_location, &QAction::triggered, [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); + connect(open_transferable_shader_cache, &QAction::triggered, + [&]() { emit OpenTransferableShaderCacheRequested(program_id); }); connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index e645904cd..56007eef8 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -68,6 +68,7 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); + void OpenTransferableShaderCacheRequested(u64 program_id); void DumpRomFSRequested(u64 program_id, const std::string& game_path); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, 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/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 86f6d0165..4e2d988cd 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -192,7 +192,12 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size } // update labels and progress bar - ui->stage->setText(stage_translations[stage].arg(value).arg(total)); + if (stage == VideoCore::LoadCallbackStage::Decompile || + stage == VideoCore::LoadCallbackStage::Build) { + ui->stage->setText(stage_translations[stage].arg(value).arg(total)); + } else { + ui->stage->setText(stage_translations[stage]); + } ui->value->setText(estimate); ui->progress_bar->setValue(static_cast<int>(value)); previous_time = now; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 65bac76e7..d5a328d92 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -37,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include <glad/glad.h> #define QT_NO_OPENGL +#include <QClipboard> +#include <QDesktopServices> #include <QDesktopWidget> #include <QDialogButtonBox> #include <QFile> #include <QFileDialog> +#include <QInputDialog> #include <QMessageBox> +#include <QProgressBar> +#include <QProgressDialog> +#include <QShortcut> +#include <QStatusBar> #include <QtConcurrent/QtConcurrent> -#include <QtGui> -#include <QtWidgets> + #include <fmt/format.h> #include "common/common_paths.h" #include "common/detached_tasks.h" @@ -55,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" -#include "common/string_util.h" #include "common/telemetry.h" #include "core/core.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" @@ -71,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" @@ -516,33 +519,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(); @@ -551,8 +555,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)); @@ -653,6 +657,8 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); + connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, + &GMainWindow::OnTransferableShaderCacheOpenFile); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, @@ -697,7 +703,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); @@ -1087,6 +1092,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } +void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { + ASSERT(program_id != 0); + + const QString tranferable_shader_cache_folder_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + + DIR_SEP + "transferable"; + + const QString transferable_shader_cache_file_path = + tranferable_shader_cache_folder_path + DIR_SEP + + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); + + if (!QFile::exists(transferable_shader_cache_file_path)) { + QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), + tr("A shader cache for this title does not exist.")); + return; + } + + // Windows supports opening a folder with selecting a specified file in explorer. On every other + // OS we just open the transferable shader cache folder without preselecting the transferable + // shader cache file for the selected game. +#if defined(Q_OS_WIN) + const QString explorer = QStringLiteral("explorer"); + QStringList param; + if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { + param << QStringLiteral("/select,"); + } + param << QDir::toNativeSeparators(transferable_shader_cache_file_path); + QProcess::startDetached(explorer, param); +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); +#endif +} + static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { std::size_t out = 0; @@ -1629,6 +1667,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 36105dd39..c727e942c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -121,7 +121,6 @@ private: void InitializeWidgets(); void InitializeDebugWidgets(); void InitializeRecentFileMenuActions(); - void InitializeHotkeys(); void SetDefaultUIGeometry(); void RestoreUIState(); @@ -177,6 +176,7 @@ private slots: /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); + void OnTransferableShaderCacheOpenFile(u64 program_id); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, @@ -196,6 +196,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; +}; |
