diff options
Diffstat (limited to 'src/yuzu/configuration/configure_hotkeys.cpp')
-rw-r--r-- | src/yuzu/configuration/configure_hotkeys.cpp | 242 |
1 files changed, 210 insertions, 32 deletions
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index ed76fe18e7..be10e0a31e 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -5,15 +5,24 @@ #include <QMenu> #include <QMessageBox> #include <QStandardItemModel> -#include "common/settings.h" +#include <QTimer> + +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" + #include "ui_configure_hotkeys.h" #include "yuzu/configuration/config.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>()) { +constexpr int name_column = 0; +constexpr int hotkey_column = 1; +constexpr int controller_column = 2; + +ConfigureHotkeys::ConfigureHotkeys(Core::HID::HIDCore& hid_core, QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()), + timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { ui->setupUi(this); setFocusPolicy(Qt::ClickFocus); @@ -26,16 +35,24 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); 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); + ui->hotkey_list->setColumnWidth(name_column, 200); + ui->hotkey_list->resizeColumnToContents(hotkey_column); connect(ui->button_restore_defaults, &QPushButton::clicked, this, &ConfigureHotkeys::RestoreDefaults); connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll); + controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); + + connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); + + connect(poll_timer.get(), &QTimer::timeout, [this] { + const auto buttons = controller->GetNpadButtons(); + if (buttons.raw != Core::HID::NpadButton::None) { + SetPollingResult(buttons.raw, false); + return; + } + }); RetranslateUI(); } @@ -49,15 +66,18 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { auto* action = new QStandardItem(hotkey.first); auto* keyseq = new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); + auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); action->setEditable(false); keyseq->setEditable(false); - parent_item->appendRow({action, keyseq}); + controller_keyseq->setEditable(false); + parent_item->appendRow({action, keyseq, controller_keyseq}); } model->appendRow(parent_item); } ui->hotkey_list->expandAll(); - ui->hotkey_list->resizeColumnToContents(0); + ui->hotkey_list->resizeColumnToContents(name_column); + ui->hotkey_list->resizeColumnToContents(hotkey_column); } void ConfigureHotkeys::changeEvent(QEvent* event) { @@ -71,7 +91,7 @@ void ConfigureHotkeys::changeEvent(QEvent* event) { void ConfigureHotkeys::RetranslateUI() { ui->retranslateUi(this); - model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")}); + model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); } void ConfigureHotkeys::Configure(QModelIndex index) { @@ -79,7 +99,15 @@ void ConfigureHotkeys::Configure(QModelIndex index) { return; } - index = index.sibling(index.row(), 1); + // Controller configuration is selected + if (index.column() == controller_column) { + ConfigureController(index); + return; + } + + // Swap to the hotkey column + index = index.sibling(index.row(), hotkey_column); + const auto previous_key = model->data(index); SequenceDialog hotkey_dialog{this}; @@ -99,13 +127,113 @@ void ConfigureHotkeys::Configure(QModelIndex index) { model->setData(index, key_sequence.toString(QKeySequence::NativeText)); } } +void ConfigureHotkeys::ConfigureController(QModelIndex index) { + if (timeout_timer->isActive()) { + return; + } + + const auto previous_key = model->data(index); + + input_setter = [this, index, previous_key](const Core::HID::NpadButton button, + const bool cancel) { + if (cancel) { + model->setData(index, previous_key); + return; + } + + const QString button_string = tr("Home+%1").arg(GetButtonName(button)); + + const auto [key_sequence_used, used_action] = IsUsedControllerKey(button_string); + + if (key_sequence_used) { + QMessageBox::warning( + this, tr("Conflicting Key Sequence"), + tr("The entered key sequence is already assigned to: %1").arg(used_action)); + model->setData(index, previous_key); + } else { + model->setData(index, button_string); + } + }; + + model->setData(index, tr("[waiting]")); + timeout_timer->start(2500); // Cancel after 2.5 seconds + poll_timer->start(200); // Check for new inputs every 200ms + // We need to disable configuration to be able to read npad buttons + controller->DisableConfiguration(); + controller->DisableSystemButtons(); +} + +void ConfigureHotkeys::SetPollingResult(Core::HID::NpadButton button, const bool cancel) { + timeout_timer->stop(); + poll_timer->stop(); + // Re-Enable configuration + controller->EnableConfiguration(); + controller->EnableSystemButtons(); + + (*input_setter)(button, cancel); + + input_setter = std::nullopt; +} + +QString ConfigureHotkeys::GetButtonName(Core::HID::NpadButton button) const { + Core::HID::NpadButtonState state{button}; + if (state.a) { + return tr("A"); + } + if (state.b) { + return tr("B"); + } + if (state.x) { + return tr("X"); + } + if (state.y) { + return tr("Y"); + } + if (state.l || state.right_sl || state.left_sl) { + return tr("L"); + } + if (state.r || state.right_sr || state.left_sr) { + return tr("R"); + } + if (state.zl) { + return tr("ZL"); + } + if (state.zr) { + return tr("ZR"); + } + if (state.left) { + return tr("Dpad_Left"); + } + if (state.right) { + return tr("Dpad_Right"); + } + if (state.up) { + return tr("Dpad_Up"); + } + if (state.down) { + return tr("Dpad_Down"); + } + if (state.stick_l) { + return tr("Left_Stick"); + } + if (state.stick_r) { + return tr("Right_Stick"); + } + if (state.minus) { + return tr("Minus"); + } + if (state.plus) { + return tr("Plus"); + } + return tr("Invalid"); +} std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { for (int r = 0; r < model->rowCount(); ++r) { const QStandardItem* const parent = model->item(r, 0); for (int r2 = 0; r2 < parent->rowCount(); ++r2) { - const QStandardItem* const key_seq_item = parent->child(r2, 1); + const QStandardItem* const key_seq_item = parent->child(r2, hotkey_column); const auto key_seq_str = key_seq_item->text(); const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); @@ -118,12 +246,31 @@ std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) return std::make_pair(false, QString()); } +std::pair<bool, QString> ConfigureHotkeys::IsUsedControllerKey(const QString& key_sequence) const { + for (int r = 0; r < model->rowCount(); ++r) { + const QStandardItem* const parent = model->item(r, 0); + + for (int r2 = 0; r2 < parent->rowCount(); ++r2) { + const QStandardItem* const key_seq_item = parent->child(r2, controller_column); + const auto key_seq_str = key_seq_item->text(); + + if (key_sequence == key_seq_str) { + return std::make_pair(true, parent->child(r2, 0)->text()); + } + } + } + + return std::make_pair(false, QString()); +} + 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); + const QStandardItem* action = parent->child(key_column_id, name_column); + const QStandardItem* keyseq = parent->child(key_column_id, hotkey_column); + const QStandardItem* controller_keyseq = + parent->child(key_column_id, controller_column); for (auto& [group, sub_actions] : registry.hotkey_groups) { if (group != parent->text()) continue; @@ -131,6 +278,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { if (action_name != action->text()) continue; hotkey.keyseq = QKeySequence(keyseq->text()); + hotkey.controller_keyseq = controller_keyseq->text(); } } } @@ -144,7 +292,12 @@ void ConfigureHotkeys::RestoreDefaults() { const QStandardItem* parent = model->item(r, 0); for (int r2 = 0; r2 < parent->rowCount(); ++r2) { - model->item(r, 0)->child(r2, 1)->setText(Config::default_hotkeys[r2].shortcut.first); + model->item(r, 0) + ->child(r2, hotkey_column) + ->setText(Config::default_hotkeys[r2].shortcut.keyseq); + model->item(r, 0) + ->child(r2, controller_column) + ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); } } } @@ -154,7 +307,8 @@ void ConfigureHotkeys::ClearAll() { const QStandardItem* parent = model->item(r, 0); for (int r2 = 0; r2 < parent->rowCount(); ++r2) { - model->item(r, 0)->child(r2, 1)->setText(QString{}); + model->item(r, 0)->child(r2, hotkey_column)->setText(QString{}); + model->item(r, 0)->child(r2, controller_column)->setText(QString{}); } } } @@ -165,28 +319,52 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { return; } - const auto selected = index.sibling(index.row(), 1); + // Swap to the hotkey column if the controller hotkey column is not selected + if (index.column() != controller_column) { + index = index.sibling(index.row(), hotkey_column); + } + QMenu context_menu; QAction* restore_default = context_menu.addAction(tr("Restore Default")); QAction* clear = context_menu.addAction(tr("Clear")); - connect(restore_default, &QAction::triggered, [this, selected] { - const QKeySequence& default_key_sequence = QKeySequence::fromString( - Config::default_hotkeys[selected.row()].shortcut.first, QKeySequence::NativeText); - const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); - - if (key_sequence_used && - default_key_sequence != QKeySequence(model->data(selected).toString())) { - - QMessageBox::warning( - this, tr("Conflicting Key Sequence"), - tr("The default key sequence is already assigned to: %1").arg(used_action)); - } else { - model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText)); + connect(restore_default, &QAction::triggered, [this, index] { + if (index.column() == controller_column) { + RestoreControllerHotkey(index); + return; } + RestoreHotkey(index); }); - connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, QString{}); }); + connect(clear, &QAction::triggered, [this, index] { model->setData(index, QString{}); }); context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); } + +void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { + const QString& default_key_sequence = + Config::default_hotkeys[index.row()].shortcut.controller_keyseq; + const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); + + if (key_sequence_used && default_key_sequence != model->data(index).toString()) { + QMessageBox::warning( + this, tr("Conflicting Button Sequence"), + tr("The default button sequence is already assigned to: %1").arg(used_action)); + } else { + model->setData(index, default_key_sequence); + } +} + +void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { + const QKeySequence& default_key_sequence = QKeySequence::fromString( + Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); + const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); + + if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { + QMessageBox::warning( + this, tr("Conflicting Key Sequence"), + tr("The default key sequence is already assigned to: %1").arg(used_action)); + } else { + model->setData(index, default_key_sequence.toString(QKeySequence::NativeText)); + } +} |