diff options
Diffstat (limited to 'src/yuzu')
37 files changed, 603 insertions, 915 deletions
| diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 4cab599b4..2eb86d6e5 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 @@ -54,8 +56,6 @@ add_executable(yuzu      debugger/graphics/graphics_breakpoints.cpp      debugger/graphics/graphics_breakpoints.h      debugger/graphics/graphics_breakpoints_p.h -    debugger/graphics/graphics_surface.cpp -    debugger/graphics/graphics_surface.h      debugger/console.cpp      debugger/console.h      debugger/profiler.cpp @@ -78,6 +78,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 +97,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 +108,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 deleted file mode 100644 index 29f01dfb2..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <QBoxLayout> -#include <QComboBox> -#include <QDebug> -#include <QFileDialog> -#include <QLabel> -#include <QMouseEvent> -#include <QPushButton> -#include <QScrollArea> -#include <QSpinBox> -#include "common/vector_math.h" -#include "core/core.h" -#include "core/memory.h" -#include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" -#include "video_core/textures/decoders.h" -#include "video_core/textures/texture.h" -#include "yuzu/debugger/graphics/graphics_surface.h" -#include "yuzu/util/spinbox.h" - -static Tegra::Texture::TextureFormat ConvertToTextureFormat( -    Tegra::RenderTargetFormat render_target_format) { -    switch (render_target_format) { -    case Tegra::RenderTargetFormat::RGBA8_UNORM: -        return Tegra::Texture::TextureFormat::A8R8G8B8; -    case Tegra::RenderTargetFormat::RGB10_A2_UNORM: -        return Tegra::Texture::TextureFormat::A2B10G10R10; -    default: -        UNIMPLEMENTED_MSG("Unimplemented RT format"); -        return Tegra::Texture::TextureFormat::A8R8G8B8; -    } -} - -SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) -    : QLabel(parent), surface_widget(surface_widget_) {} - -SurfacePicture::~SurfacePicture() = default; - -void SurfacePicture::mousePressEvent(QMouseEvent* event) { -    // Only do something while the left mouse button is held down -    if (!(event->buttons() & Qt::LeftButton)) -        return; - -    if (pixmap() == nullptr) -        return; - -    if (surface_widget) -        surface_widget->Pick(event->x() * pixmap()->width() / width(), -                             event->y() * pixmap()->height() / height()); -} - -void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { -    // We also want to handle the event if the user moves the mouse while holding down the LMB -    mousePressEvent(event); -} - -GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, -                                             QWidget* parent) -    : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), -      surface_source(Source::RenderTarget0) { -    setObjectName("MaxwellSurface"); - -    surface_source_list = new QComboBox; -    surface_source_list->addItem(tr("Render Target 0")); -    surface_source_list->addItem(tr("Render Target 1")); -    surface_source_list->addItem(tr("Render Target 2")); -    surface_source_list->addItem(tr("Render Target 3")); -    surface_source_list->addItem(tr("Render Target 4")); -    surface_source_list->addItem(tr("Render Target 5")); -    surface_source_list->addItem(tr("Render Target 6")); -    surface_source_list->addItem(tr("Render Target 7")); -    surface_source_list->addItem(tr("Z Buffer")); -    surface_source_list->addItem(tr("Custom")); -    surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); - -    surface_address_control = new CSpinBox; -    surface_address_control->SetBase(16); -    surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); -    surface_address_control->SetPrefix("0x"); - -    unsigned max_dimension = 16384; // TODO: Find actual maximum - -    surface_width_control = new QSpinBox; -    surface_width_control->setRange(0, max_dimension); - -    surface_height_control = new QSpinBox; -    surface_height_control->setRange(0, max_dimension); - -    surface_picker_x_control = new QSpinBox; -    surface_picker_x_control->setRange(0, max_dimension - 1); - -    surface_picker_y_control = new QSpinBox; -    surface_picker_y_control->setRange(0, max_dimension - 1); - -    surface_format_control = new QComboBox; - -    // 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")); - -    surface_info_label = new QLabel(); -    surface_info_label->setWordWrap(true); - -    surface_picture_label = new SurfacePicture(0, this); -    surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); -    surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); -    surface_picture_label->setScaledContents(false); - -    auto scroll_area = new QScrollArea(); -    scroll_area->setBackgroundRole(QPalette::Dark); -    scroll_area->setWidgetResizable(false); -    scroll_area->setWidget(surface_picture_label); - -    save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); - -    // Connections -    connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); -    connect(surface_source_list, -            static_cast<void (QComboBox::*)(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, -            &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(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); - -    auto main_widget = new QWidget; -    auto main_layout = new QVBoxLayout; -    { -        auto sub_layout = new QHBoxLayout; -        sub_layout->addWidget(new QLabel(tr("Source:"))); -        sub_layout->addWidget(surface_source_list); -        main_layout->addLayout(sub_layout); -    } -    { -        auto sub_layout = new QHBoxLayout; -        sub_layout->addWidget(new QLabel(tr("GPU Address:"))); -        sub_layout->addWidget(surface_address_control); -        main_layout->addLayout(sub_layout); -    } -    { -        auto sub_layout = new QHBoxLayout; -        sub_layout->addWidget(new QLabel(tr("Width:"))); -        sub_layout->addWidget(surface_width_control); -        main_layout->addLayout(sub_layout); -    } -    { -        auto sub_layout = new QHBoxLayout; -        sub_layout->addWidget(new QLabel(tr("Height:"))); -        sub_layout->addWidget(surface_height_control); -        main_layout->addLayout(sub_layout); -    } -    { -        auto sub_layout = new QHBoxLayout; -        sub_layout->addWidget(new QLabel(tr("Format:"))); -        sub_layout->addWidget(surface_format_control); -        main_layout->addLayout(sub_layout); -    } -    main_layout->addWidget(scroll_area); - -    auto info_layout = new QHBoxLayout; -    { -        auto xy_layout = new QVBoxLayout; -        { -            { -                auto sub_layout = new QHBoxLayout; -                sub_layout->addWidget(new QLabel(tr("X:"))); -                sub_layout->addWidget(surface_picker_x_control); -                xy_layout->addLayout(sub_layout); -            } -            { -                auto sub_layout = new QHBoxLayout; -                sub_layout->addWidget(new QLabel(tr("Y:"))); -                sub_layout->addWidget(surface_picker_y_control); -                xy_layout->addLayout(sub_layout); -            } -        } -        info_layout->addLayout(xy_layout); -        surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); -        info_layout->addWidget(surface_info_label); -    } -    main_layout->addLayout(info_layout); - -    main_layout->addWidget(save_surface); -    main_widget->setLayout(main_layout); -    setWidget(main_widget); - -    // Load current data - TODO: Make sure this works when emulation is not running -    if (debug_context && debug_context->at_breakpoint) { -        emit Update(); -        widget()->setEnabled(debug_context->at_breakpoint); -    } else { -        widget()->setEnabled(false); -    } -} - -void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { -    emit Update(); -    widget()->setEnabled(true); -} - -void GraphicsSurfaceWidget::OnResumed() { -    widget()->setEnabled(false); -} - -void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { -    surface_source = static_cast<Source>(new_value); -    emit Update(); -} - -void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { -    if (surface_address != new_value) { -        surface_address = static_cast<Tegra::GPUVAddr>(new_value); - -        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); -        emit Update(); -    } -} - -void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { -    if (surface_width != static_cast<unsigned>(new_value)) { -        surface_width = static_cast<unsigned>(new_value); - -        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); -        emit Update(); -    } -} - -void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { -    if (surface_height != static_cast<unsigned>(new_value)) { -        surface_height = static_cast<unsigned>(new_value); - -        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); -        emit Update(); -    } -} - -void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { -    if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { -        surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); - -        surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); -        emit Update(); -    } -} - -void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { -    if (surface_picker_x != new_value) { -        surface_picker_x = new_value; -        Pick(surface_picker_x, surface_picker_y); -    } -} - -void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { -    if (surface_picker_y != new_value) { -        surface_picker_y = new_value; -        Pick(surface_picker_x, surface_picker_y); -    } -} - -void GraphicsSurfaceWidget::Pick(int x, int y) { -    surface_picker_x_control->setValue(x); -    surface_picker_y_control->setValue(y); - -    if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || -        y >= static_cast<int>(surface_height)) { -        surface_info_label->setText(tr("Pixel out of bounds")); -        surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); -        return; -    } - -    surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); -    surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); -} - -void GraphicsSurfaceWidget::OnUpdate() { -    auto& gpu = Core::System::GetInstance().GPU(); - -    QPixmap pixmap; - -    switch (surface_source) { -    case Source::RenderTarget0: -    case Source::RenderTarget1: -    case Source::RenderTarget2: -    case Source::RenderTarget3: -    case Source::RenderTarget4: -    case Source::RenderTarget5: -    case Source::RenderTarget6: -    case Source::RenderTarget7: { -        // TODO: Store a reference to the registers in the debug context instead of accessing them -        // directly... - -        const auto& registers = gpu.Maxwell3D().regs; -        const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - -                                      static_cast<std::size_t>(Source::RenderTarget0)]; - -        surface_address = rt.Address(); -        surface_width = rt.width; -        surface_height = rt.height; -        if (rt.format != Tegra::RenderTargetFormat::NONE) { -            surface_format = ConvertToTextureFormat(rt.format); -        } - -        break; -    } - -    case Source::Custom: { -        // Keep user-specified values -        break; -    } - -    default: -        qDebug() << "Unknown surface source " << static_cast<int>(surface_source); -        break; -    } - -    surface_address_control->SetValue(surface_address); -    surface_width_control->setValue(surface_width); -    surface_height_control->setValue(surface_height); -    surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); - -    if (surface_address == 0) { -        surface_picture_label->hide(); -        surface_info_label->setText(tr("(invalid surface address)")); -        surface_info_label->setAlignment(Qt::AlignCenter); -        surface_picker_x_control->setEnabled(false); -        surface_picker_y_control->setEnabled(false); -        save_surface->setEnabled(false); -        return; -    } - -    // TODO: Implement a good way to visualize alpha components! - -    QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); - -    // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. -    // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. -    auto unswizzled_data = Tegra::Texture::UnswizzleTexture( -        gpu.MemoryManager().GetPointer(surface_address), 1, 1, -        Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U); - -    auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, -                                                      surface_width, surface_height); - -    surface_picture_label->show(); - -    for (unsigned int y = 0; y < surface_height; ++y) { -        for (unsigned int x = 0; x < surface_width; ++x) { -            Common::Vec4<u8> color; -            color[0] = texture_data[x + y * surface_width + 0]; -            color[1] = texture_data[x + y * surface_width + 1]; -            color[2] = texture_data[x + y * surface_width + 2]; -            color[3] = texture_data[x + y * surface_width + 3]; -            decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); -        } -    } - -    pixmap = QPixmap::fromImage(decoded_image); -    surface_picture_label->setPixmap(pixmap); -    surface_picture_label->resize(pixmap.size()); - -    // Update the info with pixel data -    surface_picker_x_control->setEnabled(true); -    surface_picker_y_control->setEnabled(true); -    Pick(surface_picker_x, surface_picker_y); - -    // Enable saving the converted pixmap to file -    save_surface->setEnabled(true); -} - -void GraphicsSurfaceWidget::SaveSurface() { -    QString png_filter = tr("Portable Network Graphic (*.png)"); -    QString bin_filter = tr("Binary data (*.bin)"); - -    QString selectedFilter; -    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); - -    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(); -        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) { -        auto& gpu = Core::System::GetInstance().GPU(); -        std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); - -        const u8* 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); -    } else { -        UNREACHABLE_MSG("Unhandled filter selected"); -    } -} diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h deleted file mode 100644 index 323e39d94..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <QLabel> -#include <QPushButton> -#include "video_core/memory_manager.h" -#include "video_core/textures/texture.h" -#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" - -class QComboBox; -class QSpinBox; -class CSpinBox; - -class GraphicsSurfaceWidget; - -class SurfacePicture : public QLabel { -    Q_OBJECT - -public: -    explicit SurfacePicture(QWidget* parent = nullptr, -                            GraphicsSurfaceWidget* surface_widget = nullptr); -    ~SurfacePicture() override; - -protected slots: -    void mouseMoveEvent(QMouseEvent* event) override; -    void mousePressEvent(QMouseEvent* event) override; - -private: -    GraphicsSurfaceWidget* surface_widget; -}; - -class GraphicsSurfaceWidget : public BreakPointObserverDock { -    Q_OBJECT - -    using Event = Tegra::DebugContext::Event; - -    enum class Source { -        RenderTarget0 = 0, -        RenderTarget1 = 1, -        RenderTarget2 = 2, -        RenderTarget3 = 3, -        RenderTarget4 = 4, -        RenderTarget5 = 5, -        RenderTarget6 = 6, -        RenderTarget7 = 7, -        ZBuffer = 8, -        Custom = 9, -    }; - -public: -    explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, -                                   QWidget* parent = nullptr); -    void Pick(int x, int y); - -public slots: -    void OnSurfaceSourceChanged(int new_value); -    void OnSurfaceAddressChanged(qint64 new_value); -    void OnSurfaceWidthChanged(int new_value); -    void OnSurfaceHeightChanged(int new_value); -    void OnSurfaceFormatChanged(int new_value); -    void OnSurfacePickerXChanged(int new_value); -    void OnSurfacePickerYChanged(int new_value); -    void OnUpdate(); - -signals: -    void Update(); - -private: -    void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; -    void OnResumed() override; - -    void SaveSurface(); - -    QComboBox* surface_source_list; -    CSpinBox* surface_address_control; -    QSpinBox* surface_width_control; -    QSpinBox* surface_height_control; -    QComboBox* surface_format_control; - -    SurfacePicture* surface_picture_label; -    QSpinBox* surface_picker_x_control; -    QSpinBox* surface_picker_y_control; -    QLabel* surface_info_label; -    QPushButton* save_surface; - -    Source surface_source; -    Tegra::GPUVAddr surface_address; -    unsigned surface_width; -    unsigned surface_height; -    Tegra::Texture::TextureFormat surface_format; -    int surface_picker_x = 0; -    int surface_picker_y = 0; -}; 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 c0e3c5fa9..4b67656ac 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -18,6 +18,7 @@  #include "common/common_types.h"  #include "common/logging/log.h"  #include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h"  #include "yuzu/compatibility_list.h"  #include "yuzu/game_list.h"  #include "yuzu/game_list_p.h" @@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() {      main_window->filterBarSetChecked(false);  } -GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) -    : QWidget{parent}, vfs(std::move(vfs)) { +GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider, +                   GMainWindow* parent) +    : QWidget{parent}, vfs(std::move(vfs)), provider(provider) {      watcher = new QFileSystemWatcher(this);      connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); @@ -329,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")); @@ -344,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, @@ -428,7 +434,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {      emit ShouldCancelWorker(); -    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); +    GameListWorker* worker = +        new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list);      connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);      connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index b317eb2fc..56007eef8 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -26,8 +26,9 @@ class GameListSearchField;  class GMainWindow;  namespace FileSys { +class ManualContentProvider;  class VfsFilesystem; -} +} // namespace FileSys  enum class GameListOpenTarget {      SaveData, @@ -47,7 +48,8 @@ public:          COLUMN_COUNT, // Number of columns      }; -    explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); +    explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, +                      FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);      ~GameList() override;      void clearFilter(); @@ -66,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, @@ -85,6 +88,7 @@ private:      void RefreshGameDirectory();      std::shared_ptr<FileSys::VfsFilesystem> vfs; +    FileSys::ManualContentProvider* provider;      GameListSearchField* search_field;      GMainWindow* main_window = nullptr;      QVBoxLayout* layout = nullptr; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index b37710f59..8687e7c5a 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -12,12 +12,15 @@  #include "common/common_paths.h"  #include "common/file_util.h" +#include "core/core.h" +#include "core/file_sys/card_image.h"  #include "core/file_sys/content_archive.h"  #include "core/file_sys/control_metadata.h"  #include "core/file_sys/mode.h"  #include "core/file_sys/nca_metadata.h"  #include "core/file_sys/patch_manager.h"  #include "core/file_sys/registered_cache.h" +#include "core/file_sys/submission_package.h"  #include "core/hle/service/filesystem/filesystem.h"  #include "core/loader/loader.h"  #include "yuzu/compatibility_list.h" @@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri  }  } // Anonymous namespace -GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, -                               const CompatibilityList& compatibility_list) -    : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), +GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, +                               FileSys::ManualContentProvider* provider, QString dir_path, +                               bool deep_scan, const CompatibilityList& compatibility_list) +    : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan),        compatibility_list(compatibility_list) {}  GameListWorker::~GameListWorker() = default; -void GameListWorker::AddInstalledTitlesToGameList() { -    const auto cache = Service::FileSystem::GetUnionContents(); -    const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, -                                                         FileSys::ContentRecordType::Program); +void GameListWorker::AddTitlesToGameList() { +    const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>( +        Core::System::GetInstance().GetContentProvider()); +    const auto installed_games = cache.ListEntriesFilterOrigin( +        std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program); -    for (const auto& game : installed_games) { -        const auto file = cache.GetEntryUnparsed(game); +    for (const auto& [slot, game] : installed_games) { +        if (slot == FileSys::ContentProviderUnionSlot::FrontendManual) +            continue; + +        const auto file = cache.GetEntryUnparsed(game.title_id, game.type);          std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);          if (!loader)              continue; @@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() {          emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,                                            compatibility_list, patch));      } - -    const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application, -                                                      FileSys::ContentRecordType::Control); - -    for (const auto& entry : control_data) { -        auto nca = cache.GetEntry(entry); -        if (nca != nullptr) { -            nca_control_map.insert_or_assign(entry.title_id, std::move(nca)); -        } -    }  } -void GameListWorker::FillControlMap(const std::string& dir_path) { -    const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, -                                             const std::string& virtual_name) -> bool { -        if (stop_processing) { -            // Breaks the callback loop -            return false; -        } - -        const std::string physical_name = directory + DIR_SEP + virtual_name; -        const QFileInfo file_info(QString::fromStdString(physical_name)); -        if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) { -            auto nca = -                std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); -            if (nca->GetType() == FileSys::NCAContentType::Control) { -                const u64 title_id = nca->GetTitleId(); -                nca_control_map.insert_or_assign(title_id, std::move(nca)); -            } -        } -        return true; -    }; - -    FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); -} - -void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { -    const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, -                                            const std::string& virtual_name) -> bool { +void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, +                                    unsigned int recursion) { +    const auto callback = [this, target, recursion](u64* num_entries_out, +                                                    const std::string& directory, +                                                    const std::string& virtual_name) -> bool {          if (stop_processing) {              // Breaks the callback loop.              return false; @@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign          const bool is_dir = FileUtil::IsDirectory(physical_name);          if (!is_dir &&              (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { -            auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); +            const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); +            auto loader = Loader::GetLoader(file);              if (!loader) {                  return true;              } @@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign                  return true;              } -            std::vector<u8> icon; -            const auto res1 = loader->ReadIcon(icon); -              u64 program_id = 0;              const auto res2 = loader->ReadProgramId(program_id); -            std::string name = " "; -            const auto res3 = loader->ReadTitle(name); +            if (target == ScanTarget::FillManualContentProvider) { +                if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { +                    provider->AddEntry(FileSys::TitleType::Application, +                                       FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), +                                       program_id, file); +                } else if (res2 == Loader::ResultStatus::Success && +                           (file_type == Loader::FileType::XCI || +                            file_type == Loader::FileType::NSP)) { +                    const auto nsp = file_type == Loader::FileType::NSP +                                         ? std::make_shared<FileSys::NSP>(file) +                                         : FileSys::XCI{file}.GetSecurePartitionNSP(); +                    for (const auto& title : nsp->GetNCAs()) { +                        for (const auto& entry : title.second) { +                            provider->AddEntry(entry.first.first, entry.first.second, title.first, +                                               entry.second->GetBaseFile()); +                        } +                    } +                } +            } else { +                std::vector<u8> icon; +                const auto res1 = loader->ReadIcon(icon); -            const FileSys::PatchManager patch{program_id}; +                std::string name = " "; +                const auto res3 = loader->ReadTitle(name); -            if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && -                res2 == Loader::ResultStatus::Success) { -                // Use from metadata pool. -                if (nca_control_map.find(program_id) != nca_control_map.end()) { -                    const auto& nca = nca_control_map[program_id]; -                    GetMetadataFromControlNCA(patch, *nca, icon, name); -                } -            } +                const FileSys::PatchManager patch{program_id}; -            emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, -                                              compatibility_list, patch)); +                emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, +                                                  compatibility_list, patch)); +            }          } else if (is_dir && recursion > 0) {              watch_list.append(QString::fromStdString(physical_name)); -            AddFstEntriesToGameList(physical_name, recursion - 1); +            ScanFileSystem(target, physical_name, recursion - 1);          }          return true; @@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign  void GameListWorker::run() {      stop_processing = false;      watch_list.append(dir_path); -    FillControlMap(dir_path.toStdString()); -    AddInstalledTitlesToGameList(); -    AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); -    nca_control_map.clear(); +    provider->ClearAllEntries(); +    ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(), +                   deep_scan ? 256 : 0); +    AddTitlesToGameList(); +    ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0);      emit Finished(watch_list);  } diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 0e42d0bde..7c3074af9 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable {      Q_OBJECT  public: -    GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, +    GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, +                   FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan,                     const CompatibilityList& compatibility_list);      ~GameListWorker() override; @@ -58,12 +59,17 @@ signals:      void Finished(QStringList watch_list);  private: -    void AddInstalledTitlesToGameList(); -    void FillControlMap(const std::string& dir_path); -    void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); +    void AddTitlesToGameList(); + +    enum class ScanTarget { +        FillManualContentProvider, +        PopulateGameList, +    }; + +    void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0);      std::shared_ptr<FileSys::VfsFilesystem> vfs; -    std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map; +    FileSys::ManualContentProvider* provider;      QStringList watch_list;      QString dir_path;      bool deep_scan; 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 41ba3c4c6..ca231d710 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" @@ -87,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual  #include "yuzu/configuration/configure_dialog.h"  #include "yuzu/debugger/console.h"  #include "yuzu/debugger/graphics/graphics_breakpoints.h" -#include "yuzu/debugger/graphics/graphics_surface.h"  #include "yuzu/debugger/profiler.h"  #include "yuzu/debugger/wait_tree.h"  #include "yuzu/discord.h" @@ -168,7 +170,8 @@ static void InitializeLogging() {  GMainWindow::GMainWindow()      : config(new Config()), emu_thread(nullptr), -      vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { +      vfs(std::make_shared<FileSys::RealVfsFilesystem>()), +      provider(std::make_unique<FileSys::ManualContentProvider>()) {      InitializeLogging();      debug_context = Tegra::DebugContext::Construct(); @@ -200,11 +203,15 @@ GMainWindow::GMainWindow()                         .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));      show(); +    Core::System::GetInstance().SetContentProvider( +        std::make_unique<FileSys::ContentProviderUnion>()); +    Core::System::GetInstance().RegisterContentProvider( +        FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); +    Service::FileSystem::CreateFactories(*vfs); +      // Gen keys if necessary      OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); -    // Necessary to load titles from nand in gamelist. -    Service::FileSystem::CreateFactories(*vfs);      game_list->LoadCompatibilityList();      game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); @@ -416,7 +423,7 @@ void GMainWindow::InitializeWidgets() {      render_window = new GRenderWindow(this, emu_thread.get());      render_window->hide(); -    game_list = new GameList(vfs, this); +    game_list = new GameList(vfs, provider.get(), this);      ui.horizontalLayout->addWidget(game_list);      loading_screen = new LoadingScreen(this); @@ -475,11 +482,6 @@ void GMainWindow::InitializeDebugWidgets() {      graphicsBreakpointsWidget->hide();      debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); -    graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); -    addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); -    graphicsSurfaceWidget->hide(); -    debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); -      waitTreeWidget = new WaitTreeWidget(this);      addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);      waitTreeWidget->hide(); @@ -511,33 +513,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(); @@ -546,8 +549,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)); @@ -648,6 +651,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, @@ -692,7 +697,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); @@ -1082,6 +1086,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; @@ -1141,7 +1178,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa          return;      } -    const auto installed = Service::FileSystem::GetUnionContents(); +    const auto& installed = Core::System::GetInstance().GetContentProvider();      const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);      if (!romfs_title_id) { @@ -1624,6 +1661,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) @@ -1886,14 +1924,14 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {      }  } -std::optional<u64> GMainWindow::SelectRomFSDumpTarget( -    const FileSys::RegisteredCacheUnion& installed, u64 program_id) { +std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, +                                                      u64 program_id) {      const auto dlc_entries =          installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); -    std::vector<FileSys::RegisteredCacheEntry> dlc_match; +    std::vector<FileSys::ContentProviderEntry> dlc_match;      dlc_match.reserve(dlc_entries.size());      std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), -                 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { +                 [&program_id, &installed](const FileSys::ContentProviderEntry& entry) {                       return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&                              installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;                   }); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e07c892cf..85e3810f2 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -23,7 +23,6 @@ class EmuThread;  class GameList;  class GImageInfo;  class GraphicsBreakPointsWidget; -class GraphicsSurfaceWidget;  class GRenderWindow;  class LoadingScreen;  class MicroProfileDialog; @@ -37,7 +36,8 @@ struct SoftwareKeyboardParameters;  } // namespace Core::Frontend  namespace FileSys { -class RegisteredCacheUnion; +class ContentProvider; +class ManualContentProvider;  class VfsFilesystem;  } // namespace FileSys @@ -120,7 +120,6 @@ private:      void InitializeWidgets();      void InitializeDebugWidgets();      void InitializeRecentFileMenuActions(); -    void InitializeHotkeys();      void SetDefaultUIGeometry();      void RestoreUIState(); @@ -176,6 +175,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, @@ -195,6 +195,7 @@ private slots:      void OnAbout();      void OnToggleFilterBar();      void OnDisplayTitleBars(bool); +    void InitializeHotkeys();      void ToggleFullscreen();      void ShowFullscreen();      void HideFullscreen(); @@ -204,7 +205,7 @@ private slots:      void OnReinitializeKeys(ReinitializeKeyBehavior behavior);  private: -    std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); +    std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);      void UpdateStatusBar();      Ui::MainWindow ui; @@ -232,12 +233,12 @@ private:      // FS      std::shared_ptr<FileSys::VfsFilesystem> vfs; +    std::unique_ptr<FileSys::ManualContentProvider> provider;      // Debugger panes      ProfilerWidget* profilerWidget;      MicroProfileDialog* microProfileDialog;      GraphicsBreakPointsWidget* graphicsBreakpointsWidget; -    GraphicsSurfaceWidget* graphicsSurfaceWidget;      WaitTreeWidget* waitTreeWidget;      QAction* actions_recent_files[max_recent_files_item]; 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; +}; | 
