From f354fd8c2787c3e4c44836eb2bafa3d2683a8550 Mon Sep 17 00:00:00 2001 From: David Griswold Date: Wed, 4 Feb 2026 19:57:25 +0300 Subject: [PATCH] controller hotkey config dialog added, still not editable --- src/citra_qt/CMakeLists.txt | 3 + .../configuration/configure_dialog.cpp | 39 +++-- src/citra_qt/configuration/configure_dialog.h | 2 + .../configuration/configure_hotkeys.cpp | 6 +- .../configure_hotkeys_controller.cpp | 137 ++++++++++++++++++ .../configure_hotkeys_controller.h | 43 ++++++ .../configure_hotkeys_controller.ui | 114 +++++++++++++++ src/citra_qt/hotkeys.h | 1 + 8 files changed, 325 insertions(+), 20 deletions(-) create mode 100644 src/citra_qt/configuration/configure_hotkeys_controller.cpp create mode 100644 src/citra_qt/configuration/configure_hotkeys_controller.h create mode 100644 src/citra_qt/configuration/configure_hotkeys_controller.ui diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 6432c765f..a46532bba 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -60,6 +60,9 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL configuration/configure_hotkeys.cpp configuration/configure_hotkeys.h configuration/configure_hotkeys.ui + configuration/configure_hotkeys_controller.cpp + configuration/configure_hotkeys_controller.h + configuration/configure_hotkeys_controller.ui configuration/configure_input.cpp configuration/configure_input.h configuration/configure_input.ui diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index 0f0fe2e71..c8de31b88 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -12,6 +12,7 @@ #include "citra_qt/configuration/configure_general.h" #include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_hotkeys.h" +#include "citra_qt/configuration/configure_hotkeys_controller.h" #include "citra_qt/configuration/configure_input.h" #include "citra_qt/configuration/configure_layout.h" #include "citra_qt/configuration/configure_storage.h" @@ -32,6 +33,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor system_tab{std::make_unique(system, this)}, input_tab{std::make_unique(system, this)}, hotkeys_tab{std::make_unique(this)}, + hotkeys_controller_tab{std::make_unique(this)}, graphics_tab{ std::make_unique(gl_renderer, physical_devices, is_powered_on, this)}, enhancements_tab{std::make_unique(this)}, @@ -48,7 +50,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor ui->tabWidget->addTab(general_tab.get(), tr("General")); ui->tabWidget->addTab(system_tab.get(), tr("System")); ui->tabWidget->addTab(input_tab.get(), tr("Input")); - ui->tabWidget->addTab(hotkeys_tab.get(), tr("Hotkeys")); + ui->tabWidget->addTab(hotkeys_controller_tab.get(), tr("Controller Hotkeys")); + ui->tabWidget->addTab(hotkeys_tab.get(), tr("Keyboard Hotkeys")); ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(enhancements_tab.get(), tr("Enhancements")); ui->tabWidget->addTab(layout_tab.get(), tr("Layout")); @@ -60,7 +63,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor ui->tabWidget->addTab(ui_tab.get(), tr("UI")); hotkeys_tab->Populate(registry); - + hotkeys_controller_tab->Populate(registry); PopulateSelectionList(); connect(ui_tab.get(), &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); @@ -104,6 +107,7 @@ void ConfigureDialog::ApplyConfiguration() { input_tab->ApplyConfiguration(); input_tab->ApplyProfile(); hotkeys_tab->ApplyConfiguration(registry); + hotkeys_controller_tab->ApplyConfiguration(registry); graphics_tab->ApplyConfiguration(); enhancements_tab->ApplyConfiguration(); layout_tab->ApplyConfiguration(); @@ -127,7 +131,7 @@ void ConfigureDialog::PopulateSelectionList() { {tr("System"), {system_tab.get(), camera_tab.get(), storage_tab.get()}}, {tr("Graphics"), {enhancements_tab.get(), layout_tab.get(), graphics_tab.get()}}, {tr("Audio"), {audio_tab.get()}}, - {tr("Controls"), {input_tab.get(), hotkeys_tab.get()}}}}; + {tr("Controls"), {input_tab.get(), hotkeys_controller_tab.get(), hotkeys_tab.get()}}}}; for (const auto& entry : items) { auto* const item = new QListWidgetItem(entry.first); @@ -158,6 +162,7 @@ void ConfigureDialog::RetranslateUI() { system_tab->RetranslateUI(); input_tab->RetranslateUI(); hotkeys_tab->RetranslateUI(); + hotkeys_controller_tab->RetranslateUI(); graphics_tab->RetranslateUI(); enhancements_tab->RetranslateUI(); layout_tab->RetranslateUI(); @@ -174,19 +179,21 @@ void ConfigureDialog::UpdateVisibleTabs() { if (items.isEmpty()) return; - const std::map widgets = {{general_tab.get(), tr("General")}, - {system_tab.get(), tr("System")}, - {input_tab.get(), tr("Input")}, - {hotkeys_tab.get(), tr("Hotkeys")}, - {enhancements_tab.get(), tr("Enhancements")}, - {layout_tab.get(), tr("Layout")}, - {graphics_tab.get(), tr("Advanced")}, - {audio_tab.get(), tr("Audio")}, - {camera_tab.get(), tr("Camera")}, - {debug_tab.get(), tr("Debug")}, - {storage_tab.get(), tr("Storage")}, - {web_tab.get(), tr("Web")}, - {ui_tab.get(), tr("UI")}}; + const std::map widgets = { + {general_tab.get(), tr("General")}, + {system_tab.get(), tr("System")}, + {input_tab.get(), tr("Input")}, + {hotkeys_tab.get(), tr("Keyboard Hotkeys")}, + {hotkeys_controller_tab.get(), tr("Controller Hotkeys")}, + {enhancements_tab.get(), tr("Enhancements")}, + {layout_tab.get(), tr("Layout")}, + {graphics_tab.get(), tr("Advanced")}, + {audio_tab.get(), tr("Audio")}, + {camera_tab.get(), tr("Camera")}, + {debug_tab.get(), tr("Debug")}, + {storage_tab.get(), tr("Storage")}, + {web_tab.get(), tr("Web")}, + {ui_tab.get(), tr("UI")}}; ui->tabWidget->clear(); diff --git a/src/citra_qt/configuration/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h index ce3c6170c..af6cb9f47 100644 --- a/src/citra_qt/configuration/configure_dialog.h +++ b/src/citra_qt/configuration/configure_dialog.h @@ -23,6 +23,7 @@ class ConfigureGeneral; class ConfigureSystem; class ConfigureInput; class ConfigureHotkeys; +class ConfigureControllerHotkeys; class ConfigureGraphics; class ConfigureLayout; class ConfigureEnhancements; @@ -65,6 +66,7 @@ private: std::unique_ptr system_tab; std::unique_ptr input_tab; std::unique_ptr hotkeys_tab; + std::unique_ptr hotkeys_controller_tab; std::unique_ptr graphics_tab; std::unique_ptr enhancements_tab; std::unique_ptr layout_tab; diff --git a/src/citra_qt/configuration/configure_hotkeys.cpp b/src/citra_qt/configuration/configure_hotkeys.cpp index ecaed462b..16e762438 100644 --- a/src/citra_qt/configuration/configure_hotkeys.cpp +++ b/src/citra_qt/configuration/configure_hotkeys.cpp @@ -29,7 +29,7 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); ui->hotkey_list->setModel(model); - ui->hotkey_list->setColumnWidth(0, 250); + ui->hotkey_list->setColumnWidth(0, 300); ui->hotkey_list->resizeColumnToContents(hotkey_column); connect(ui->button_restore_defaults, &QPushButton::clicked, this, @@ -63,11 +63,9 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { QStandardItem* action = new QStandardItem(hotkey.first); QStandardItem* keyseq = new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); - QStandardItem* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); action->setEditable(false); keyseq->setEditable(false); - controller_keyseq->setEditable(false); - parent_item->appendRow({action, keyseq, controller_keyseq}); + parent_item->appendRow({action, keyseq}); } model->appendRow(parent_item); } diff --git a/src/citra_qt/configuration/configure_hotkeys_controller.cpp b/src/citra_qt/configuration/configure_hotkeys_controller.cpp new file mode 100644 index 000000000..baf8f8c29 --- /dev/null +++ b/src/citra_qt/configuration/configure_hotkeys_controller.cpp @@ -0,0 +1,137 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "citra_qt/configuration/config.h" +#include "citra_qt/configuration/configure_hotkeys_controller.h" +#include "citra_qt/hotkeys.h" +#include "citra_qt/util/sequence_dialog/sequence_dialog.h" +#include "ui_configure_hotkeys_controller.h" + +constexpr int name_column = 0; +constexpr int readable_hotkey_column = 1; +constexpr int hotkey_column = 2; + +ConfigureControllerHotkeys::ConfigureControllerHotkeys(QWidget* parent) + : QWidget(parent), ui(std::make_unique()) { + ui->setupUi(this); + setFocusPolicy(Qt::ClickFocus); + + model = new QStandardItemModel(this); + model->setColumnCount(2); + model->setHorizontalHeaderLabels({tr("Action"), tr("Controller Hotkey")}); + // TODO: re-enable and get profiles workin + ui->horizontalLayout_5->setEnabled(false); + connect(ui->hotkey_list, &QTreeView::doubleClicked, this, + &ConfigureControllerHotkeys::Configure); + connect(ui->hotkey_list, &QTreeView::customContextMenuRequested, this, + &ConfigureControllerHotkeys::PopupContextMenu); + ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); + ui->hotkey_list->setModel(model); + + ui->hotkey_list->setColumnWidth(0, 300); + ui->hotkey_list->resizeColumnToContents(readable_hotkey_column); + + connect(ui->button_clear_all, &QPushButton::clicked, this, + &ConfigureControllerHotkeys::ClearAll); +} + +ConfigureControllerHotkeys::~ConfigureControllerHotkeys() = default; + +void ConfigureControllerHotkeys::Populate(const HotkeyRegistry& registry) { + for (const auto& group : registry.hotkey_groups) { + QStandardItem* parent_item = new QStandardItem(group.first); + parent_item->setEditable(false); + for (const auto& hotkey : group.second) { + QStandardItem* action = new QStandardItem(hotkey.first); + QStandardItem* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); + QStandardItem* readable_keyseq = + new QStandardItem(CleanSequence(hotkey.second.controller_keyseq)); + action->setEditable(false); + controller_keyseq->setEditable(false); + parent_item->appendRow({action, readable_keyseq, controller_keyseq}); + } + model->appendRow(parent_item); + } + + ui->hotkey_list->expandAll(); +} + +void ConfigureControllerHotkeys::Configure(QModelIndex index) {} + +void ConfigureControllerHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { + for (int key_id = 0; key_id < model->rowCount(); key_id++) { + 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, name_column); + const QStandardItem* controller_keyseq = parent->child(key_column_id, hotkey_column); + 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.controller_keyseq = controller_keyseq->text(); + } + } + } + } + + registry.SaveHotkeys(); +} + +void ConfigureControllerHotkeys::ClearAll() { + for (int r = 0; r < model->rowCount(); ++r) { + const QStandardItem* parent = model->item(r, 0); + + for (int r2 = 0; r2 < parent->rowCount(); ++r2) { + model->item(r, 0)->child(r2, readable_hotkey_column)->setText(QString{}); + model->item(r, 0)->child(r2, hotkey_column)->setText(QString{}); + } + } +} + +void ConfigureControllerHotkeys::PopupContextMenu(const QPoint& menu_location) { + const auto index = ui->hotkey_list->indexAt(menu_location); + if (!index.parent().isValid()) { + return; + } + + QMenu context_menu; + QAction* clear = context_menu.addAction(tr("Clear")); + + const auto readable_hotkey_index = index.sibling(index.row(), readable_hotkey_column); + const auto hotkey_index = index.sibling(index.row(), hotkey_column); + connect(clear, &QAction::triggered, this, [this, hotkey_index, readable_hotkey_index] { + model->setData(hotkey_index, QString{}); + model->setData(readable_hotkey_index, QString{}); + }); + + context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); +} + +void ConfigureControllerHotkeys::RetranslateUI() { + ui->retranslateUi(this); +} + +QString ConfigureControllerHotkeys::CleanSequence(QString controller_keyseq) { + if (controller_keyseq.isEmpty()) + return controller_keyseq; + QStringList keys = controller_keyseq.split(QStringLiteral("||")); + + QString output = QStringLiteral("Button ").append( + keys.value(0).split(QStringLiteral(",")).value(0).split(QStringLiteral(":")).value(1)); + + if (keys.length() > 1) { + output.append(QStringLiteral(" + Button ")) + .append(keys.value(1) + .split(QStringLiteral(",")) + .value(0) + .split(QStringLiteral(":")) + .value(1)); + } + return output; +} diff --git a/src/citra_qt/configuration/configure_hotkeys_controller.h b/src/citra_qt/configuration/configure_hotkeys_controller.h new file mode 100644 index 000000000..a61c289ca --- /dev/null +++ b/src/citra_qt/configuration/configure_hotkeys_controller.h @@ -0,0 +1,43 @@ +// Copyright 2026 Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Ui { +class ConfigureControllerHotkeys; +} + +class HotkeyRegistry; +class QStandardItemModel; + +class ConfigureControllerHotkeys : public QWidget { + Q_OBJECT + +public: + explicit ConfigureControllerHotkeys(QWidget* parent = nullptr); + ~ConfigureControllerHotkeys() override; + + void ApplyConfiguration(HotkeyRegistry& registry); + void RetranslateUI(); + + /** + * 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); + +private: + void Configure(QModelIndex index); + void ClearAll(); + void PopupContextMenu(const QPoint& menu_location); + QString CleanSequence(QString controller_keyseq); + + std::unique_ptr ui; + + QStandardItemModel* model; +}; diff --git a/src/citra_qt/configuration/configure_hotkeys_controller.ui b/src/citra_qt/configuration/configure_hotkeys_controller.ui new file mode 100644 index 000000000..f00acba78 --- /dev/null +++ b/src/citra_qt/configuration/configure_hotkeys_controller.ui @@ -0,0 +1,114 @@ + + + ConfigureControllerHotkeys + + + + 0 + 0 + 933 + 388 + + + + Controller Hotkey Settings + + + + + + + + Profile + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + New + + + + + + + Delete + + + + + + + Rename + + + + + + + + + + + Double-click on a binding to change it. You can use two-button chords as well. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear All + + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + + + + + + + + diff --git a/src/citra_qt/hotkeys.h b/src/citra_qt/hotkeys.h index 141ad0e5f..ba465a34f 100644 --- a/src/citra_qt/hotkeys.h +++ b/src/citra_qt/hotkeys.h @@ -29,6 +29,7 @@ struct Hotkey { class HotkeyRegistry final { public: friend class ConfigureHotkeys; + friend class ConfigureControllerHotkeys; explicit HotkeyRegistry(); ~HotkeyRegistry();