controller hotkey config dialog added, still not editable

This commit is contained in:
David Griswold 2026-02-04 19:57:25 +03:00
parent 76722813b1
commit f354fd8c27
8 changed files with 325 additions and 20 deletions

View File

@ -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

View File

@ -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<ConfigureSystem>(system, this)},
input_tab{std::make_unique<ConfigureInput>(system, this)},
hotkeys_tab{std::make_unique<ConfigureHotkeys>(this)},
hotkeys_controller_tab{std::make_unique<ConfigureControllerHotkeys>(this)},
graphics_tab{
std::make_unique<ConfigureGraphics>(gl_renderer, physical_devices, is_powered_on, this)},
enhancements_tab{std::make_unique<ConfigureEnhancements>(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<QWidget*, QString> 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<QWidget*, QString> 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();

View File

@ -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<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureInput> input_tab;
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
std::unique_ptr<ConfigureControllerHotkeys> hotkeys_controller_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureEnhancements> enhancements_tab;
std::unique_ptr<ConfigureLayout> layout_tab;

View File

@ -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);
}

View File

@ -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 <QMenu>
#include <QMessageBox>
#include <QStandardItemModel>
#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::ConfigureControllerHotkeys>()) {
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;
}

View File

@ -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 <memory>
#include <QWidget>
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::ConfigureControllerHotkeys> ui;
QStandardItemModel* model;
};

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureControllerHotkeys</class>
<widget class="QWidget" name="ConfigureControllerHotkeys">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>933</width>
<height>388</height>
</rect>
</property>
<property name="windowTitle">
<string>Controller Hotkey Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Profile</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="profile"/>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonNew">
<property name="text">
<string>New</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDelete">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRename">
<property name="text">
<string>Rename</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Double-click on a binding to change it. You can use two-button chords as well.</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="button_clear_all">
<property name="text">
<string>Clear All</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<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>

View File

@ -29,6 +29,7 @@ struct Hotkey {
class HotkeyRegistry final {
public:
friend class ConfigureHotkeys;
friend class ConfigureControllerHotkeys;
explicit HotkeyRegistry();
~HotkeyRegistry();