mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-07-11 02:14:43 -06:00
Merge 7a032e883a into c2dd5f3bea
This commit is contained in:
commit
904bb2c3f2
@ -75,7 +75,10 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, Cor
|
||||
&ConfigureHotkeys::OnInputKeysChanged);
|
||||
connect(hotkeys_tab.get(), &ConfigureHotkeys::HotkeysChanged, input_tab.get(),
|
||||
&ConfigureInput::OnHotkeysChanged);
|
||||
|
||||
connect(input_tab.get(), &ConfigureInput::ClearHotkey, hotkeys_tab.get(),
|
||||
&ConfigureHotkeys::OnClearBinding);
|
||||
connect(hotkeys_tab.get(), &ConfigureHotkeys::ClearInputBinding, input_tab.get(),
|
||||
&ConfigureInput::OnClearBinding);
|
||||
// Synchronise lists upon initialisation
|
||||
input_tab->EmitInputKeysChanged();
|
||||
hotkeys_tab->EmitHotkeysChanged();
|
||||
|
||||
@ -43,13 +43,19 @@ void ConfigureHotkeys::EmitHotkeysChanged() {
|
||||
emit HotkeysChanged(GetUsedKeyList());
|
||||
}
|
||||
|
||||
QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const {
|
||||
QList<QKeySequence> list;
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> ConfigureHotkeys::GetUsedKeyList() const {
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> list;
|
||||
for (int r = 0; r < model->rowCount(); r++) {
|
||||
QStandardItem* parent = model->item(r, 0);
|
||||
for (int r2 = 0; r2 < parent->rowCount(); r2++) {
|
||||
QStandardItem* keyseq = parent->child(r2, 1);
|
||||
list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
|
||||
auto seq = QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
|
||||
if (seq.count() == 1 &&
|
||||
seq[0].keyboardModifiers() == Qt::KeyboardModifier::NoModifier) {
|
||||
auto binding = ConfigureInput::InputBinding(
|
||||
ConfigureInput::InputBindingType::Hotkey, parent->child(r2, 0)->text(), r2);
|
||||
list[seq] = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
@ -75,7 +81,8 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
|
||||
ui->hotkey_list->expandAll();
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::OnInputKeysChanged(QList<QKeySequence> new_key_list) {
|
||||
void ConfigureHotkeys::OnInputKeysChanged(
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list) {
|
||||
input_keys_list = new_key_list;
|
||||
}
|
||||
|
||||
@ -96,7 +103,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
|
||||
if (return_code == QDialog::Rejected || key_sequence.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto [key_sequence_used, used_action] = IsUsedKey(key_sequence);
|
||||
const auto [key_sequence_used, current_binding] = IsUsedKey(key_sequence);
|
||||
|
||||
// Check for turbo/per-game speed conflict. Needed to prevent the user from binding both hotkeys
|
||||
// to the same action. Which cuases problems resetting the frame limit.to the inititla value.
|
||||
@ -123,7 +130,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
|
||||
|
||||
// Show warning if either hotkey is already set
|
||||
if (!key_sequence.isEmpty() && !other_sequence.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Conflicting Key Sequence"),
|
||||
QMessageBox::warning(this, tr("Conflicting Hotkeys"),
|
||||
tr("The per-application speed and turbo speed hotkeys cannot be "
|
||||
"bound at the same time."));
|
||||
return;
|
||||
@ -131,22 +138,45 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
|
||||
}
|
||||
|
||||
if (key_sequence_used && key_sequence != QKeySequence(previous_key.toString())) {
|
||||
QMessageBox::warning(
|
||||
this, tr("Conflicting Key Sequence"),
|
||||
tr("The entered key sequence is already assigned to: %1").arg(used_action));
|
||||
} else {
|
||||
model->setData(index, key_sequence.toString(QKeySequence::NativeText));
|
||||
EmitHotkeysChanged();
|
||||
auto response = QMessageBox::information(
|
||||
this, tr("Combination Already Bound"),
|
||||
tr("This key combination is already bound to the '%1' input.\n\n"
|
||||
"Continuing will remove the previous binding. Proceed?")
|
||||
.arg(current_binding.name),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (response == QMessageBox::No) {
|
||||
return;
|
||||
} else {
|
||||
if (current_binding.binding_type == ConfigureInput::InputBindingType::Hotkey) {
|
||||
model->setData(index.sibling(current_binding.index, hotkey_column),
|
||||
QStringLiteral(""));
|
||||
} else {
|
||||
emit ClearInputBinding(current_binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model->setData(index, key_sequence.toString(QKeySequence::NativeText));
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const {
|
||||
void ConfigureHotkeys::OnClearBinding(ConfigureInput::InputBinding hotkey_to_clear) {
|
||||
const auto index = ui->hotkey_list->currentIndex();
|
||||
if (hotkey_to_clear.binding_type == ConfigureInput::InputBindingType::Hotkey) {
|
||||
model->setData(index.sibling(hotkey_to_clear.index, hotkey_column), QStringLiteral(""));
|
||||
}
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
std::pair<bool, ConfigureInput::InputBinding> ConfigureHotkeys::IsUsedKey(
|
||||
QKeySequence key_sequence) const {
|
||||
if (key_sequence == QKeySequence::fromString(QStringLiteral(""), QKeySequence::NativeText)) {
|
||||
return std::make_pair(false, QString());
|
||||
return std::make_pair(false, ConfigureInput::InputBinding{
|
||||
ConfigureInput::InputBindingType::Empty, QString(), -1});
|
||||
}
|
||||
|
||||
if (input_keys_list.contains(key_sequence)) {
|
||||
return std::make_pair(true, tr("A 3ds button"));
|
||||
return std::make_pair(true, input_keys_list[key_sequence]);
|
||||
}
|
||||
|
||||
for (int r = 0; r < model->rowCount(); ++r) {
|
||||
@ -158,12 +188,16 @@ std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence)
|
||||
const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText);
|
||||
|
||||
if (key_sequence == key_seq) {
|
||||
return std::make_pair(true, parent->child(r2, 0)->text());
|
||||
return std::make_pair(
|
||||
true, ConfigureInput::InputBinding{ConfigureInput::InputBindingType::Hotkey,
|
||||
parent->child(r2, 0)->text(), r2});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(false, QString());
|
||||
return std::make_pair(false,
|
||||
ConfigureInput::InputBinding{ConfigureInput::InputBindingType::Empty,
|
||||
QStringLiteral(""), -1});
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
|
||||
@ -197,6 +231,7 @@ void ConfigureHotkeys::RestoreDefaults() {
|
||||
->setText(QtConfig::default_hotkeys[r2].shortcut.keyseq);
|
||||
}
|
||||
}
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::ClearAll() {
|
||||
@ -207,6 +242,7 @@ void ConfigureHotkeys::ClearAll() {
|
||||
model->item(r, 0)->child(r2, hotkey_column)->setText(QString{});
|
||||
}
|
||||
}
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
|
||||
@ -235,11 +271,14 @@ void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
|
||||
|
||||
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));
|
||||
this, tr("Failed to Restore"),
|
||||
tr("Unable to restore the default binding for this hotkey, as it's already "
|
||||
"bound to the '%1' input.")
|
||||
.arg(used_action.name));
|
||||
} else {
|
||||
model->setData(index, default_key_sequence.toString(QKeySequence::NativeText));
|
||||
}
|
||||
EmitHotkeysChanged();
|
||||
}
|
||||
|
||||
void ConfigureHotkeys::RetranslateUI() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
#include "citra_qt/configuration/configure_input.h"
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureHotkeys;
|
||||
@ -34,15 +35,16 @@ public:
|
||||
void Populate(const HotkeyRegistry& registry);
|
||||
|
||||
public slots:
|
||||
void OnInputKeysChanged(QList<QKeySequence> new_key_list);
|
||||
|
||||
void OnInputKeysChanged(QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list);
|
||||
void OnClearBinding(ConfigureInput::InputBinding hotkey_to_clear);
|
||||
signals:
|
||||
void HotkeysChanged(QList<QKeySequence> new_key_list);
|
||||
void HotkeysChanged(QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list);
|
||||
void ClearInputBinding(ConfigureInput::InputBinding binding);
|
||||
|
||||
private:
|
||||
void Configure(QModelIndex index);
|
||||
std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const;
|
||||
QList<QKeySequence> GetUsedKeyList() const;
|
||||
std::pair<bool, ConfigureInput::InputBinding> IsUsedKey(QKeySequence key_sequence) const;
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> GetUsedKeyList() const;
|
||||
|
||||
void RestoreDefaults();
|
||||
void ClearAll();
|
||||
@ -54,7 +56,7 @@ private:
|
||||
* These can't be bound to any hotkey.
|
||||
* Synchronised with ConfigureInput via signal-slot.
|
||||
*/
|
||||
QList<QKeySequence> input_keys_list;
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> input_keys_list;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureHotkeys> ui;
|
||||
|
||||
|
||||
@ -19,19 +19,6 @@
|
||||
#include "core/core.h"
|
||||
#include "ui_configure_input.h"
|
||||
|
||||
const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
|
||||
ConfigureInput::analog_sub_buttons{{
|
||||
"up",
|
||||
"down",
|
||||
"left",
|
||||
"right",
|
||||
"up_left",
|
||||
"up_right",
|
||||
"down_left",
|
||||
"down_right",
|
||||
"modifier",
|
||||
}};
|
||||
|
||||
enum class AnalogSubButtons {
|
||||
up,
|
||||
down,
|
||||
@ -59,16 +46,6 @@ static QString GetKeyName(int key_code) {
|
||||
}
|
||||
}
|
||||
|
||||
static void SetAnalogButton(const Common::ParamPackage& input_param,
|
||||
Common::ParamPackage& analog_param, const std::string& button_name) {
|
||||
if (analog_param.Get("engine", "") != "analog_from_button") {
|
||||
analog_param = {
|
||||
{"engine", "analog_from_button"},
|
||||
};
|
||||
}
|
||||
analog_param.Set(button_name, input_param.Serialize());
|
||||
}
|
||||
|
||||
static QString ButtonToText(const Common::ParamPackage& param) {
|
||||
if (!param.Has("engine")) {
|
||||
return QObject::tr("[not set]");
|
||||
@ -166,6 +143,17 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
|
||||
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
|
||||
|
||||
// clang-format off
|
||||
analog_sub_buttons = {
|
||||
"up", "down", "left", "right", "up_left",
|
||||
"up_right", "down_left", "down_right", "modifier",
|
||||
};
|
||||
|
||||
analog_sub_button_names = {
|
||||
tr("Up"), tr("Down"), tr("Left"), tr("Right"), tr("Up-Left"),
|
||||
tr("Up-Right"), tr("Down-Left"), tr("Down-Right"), tr("Modifier"),
|
||||
};
|
||||
|
||||
button_map = {
|
||||
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
|
||||
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
|
||||
@ -174,6 +162,17 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
ui->buttonHome, ui->buttonPower,
|
||||
};
|
||||
|
||||
button_names = {
|
||||
tr("A Button"), tr("B Button"), tr("X Button"), tr("Y Button"),
|
||||
tr("D-Pad Up"), tr("D-Pad Down"), tr("D-Pad Left"), tr("D-Pad Right"),
|
||||
tr("L Button"), tr("R Button"), tr("Start Button"), tr("Select Button"),
|
||||
tr("Debug"), tr("GPIO4"), tr("ZL Button"), tr("ZR Button"),
|
||||
tr("Home Button"), tr("Power Button")
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/// A group of five QPushButtons represent one analog input. The buttons each represent up,
|
||||
/// down, left, right, and modifier, respectively.
|
||||
analog_map_buttons = {{
|
||||
{
|
||||
ui->buttonCircleUp,
|
||||
@ -199,6 +198,7 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
},
|
||||
}};
|
||||
|
||||
analog_names = {tr("Circle Pad"), tr("C-Stick")};
|
||||
analog_map_stick = {ui->buttonCircleAnalog, ui->buttonCStickAnalog};
|
||||
analog_map_deadzone_and_modifier_slider = {ui->sliderCirclePadDeadzoneAndModifier,
|
||||
ui->sliderCStickDeadzoneAndModifier};
|
||||
@ -213,24 +213,7 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
HandleClick(
|
||||
button_map[button_id],
|
||||
[this, button_id](Common::ParamPackage params) {
|
||||
// Workaround for ZL & ZR for analog triggers like on XBOX controllors.
|
||||
// Analog triggers (from controllers like the XBOX controller) would not
|
||||
// work due to a different range of their signals (from 0 to 255 on
|
||||
// analog triggers instead of -32768 to 32768 on analog joysticks). The
|
||||
// SDL driver misinterprets analog triggers as analog joysticks.
|
||||
// TODO: reinterpret the signal range for analog triggers to map the
|
||||
// values correctly. This is required for the correct emulation of the
|
||||
// analog triggers of the GameCube controller.
|
||||
if (button_id == Settings::NativeButton::ZL ||
|
||||
button_id == Settings::NativeButton::ZR) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.5");
|
||||
}
|
||||
buttons_param[button_id] = std::move(params);
|
||||
// If the user closes the dialog, the changes are reverted in
|
||||
// `GMainWindow::OnConfigure()`
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::NativeButton, QString(), button_id}, params);
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
@ -238,18 +221,13 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
[this, button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
ClearBinding({InputBindingType::NativeButton, QString(), button_id});
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
buttons_param[button_id] =
|
||||
Common::ParamPackage def =
|
||||
Common::ParamPackage{InputCommon::GenerateKeyboardParam(
|
||||
QtConfig::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::NativeButton, QString(), button_id}, def);
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
@ -266,10 +244,9 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
HandleClick(
|
||||
analog_map_buttons[analog_id][sub_button_id],
|
||||
[this, analog_id, sub_button_id](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::AnalogFromButton, QString(),
|
||||
analog_id, sub_button_id},
|
||||
params);
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
@ -278,20 +255,15 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
[this, analog_id, sub_button_id](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
ClearBinding({InputBindingType::AnalogFromButton, QString(), analog_id,
|
||||
sub_button_id});
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
QtConfig::default_analogs[analog_id][sub_button_id])};
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
|
||||
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::AnalogFromButton, QString(), analog_id,
|
||||
sub_button_id},
|
||||
params);
|
||||
});
|
||||
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
|
||||
menu_location));
|
||||
@ -306,9 +278,7 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
HandleClick(
|
||||
analog_map_stick[analog_id],
|
||||
[this, analog_id](const Common::ParamPackage& params) {
|
||||
analogs_param[analog_id] = params;
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::NativeAnalog, QString(), analog_id}, params);
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Analog);
|
||||
}
|
||||
@ -338,40 +308,22 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
HandleClick(
|
||||
ui->buttonCircleMod,
|
||||
[this](const Common::ParamPackage& params) {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
||||
}
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
SetBinding({InputBindingType::CModButton, QString()}, params);
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
ui->buttonCircleMod->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->buttonCircleMod, &QPushButton::customContextMenuRequested, this,
|
||||
[&](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
analogs_param[analog_id].Erase("modifier");
|
||||
}
|
||||
ui->buttonCircleMod->setText(tr("[not set]"));
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
ClearBinding({InputBindingType::CModButton, QString()});
|
||||
});
|
||||
|
||||
context_menu.addAction(tr("Restore Default"), this, [&] {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs;
|
||||
analog_id++) {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
QtConfig::default_analogs[analog_id][static_cast<u32>(
|
||||
AnalogSubButtons::modifier)])};
|
||||
SetAnalogButton(params, analogs_param[analog_id], "modifier");
|
||||
ui->buttonCircleMod->setText(
|
||||
AnalogToText(analogs_param[analog_id], "modifier"));
|
||||
}
|
||||
ApplyConfiguration();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
Common::ParamPackage params{
|
||||
InputCommon::GenerateKeyboardParam(QtConfig::default_analogs[0][4])};
|
||||
SetBinding({InputBindingType::CModButton, QString()}, params);
|
||||
});
|
||||
context_menu.exec(ui->buttonCircleMod->mapToGlobal(menu_location));
|
||||
});
|
||||
@ -419,6 +371,80 @@ ConfigureInput::ConfigureInput(Core::System& _system, QWidget* parent)
|
||||
|
||||
ConfigureInput::~ConfigureInput() = default;
|
||||
|
||||
/**
|
||||
* Returns true if we find a conflict and should stop, otherwise it clears the map and returns true.
|
||||
*/
|
||||
bool ConfigureInput::CheckForDuplicateMap(const Common::ParamPackage& params,
|
||||
InputBinding this_binding) {
|
||||
auto const current_binding = GetMapping(params);
|
||||
bool abort = false;
|
||||
if (current_binding.binding_type != InputBindingType::Empty &&
|
||||
!(current_binding.binding_type == this_binding.binding_type &&
|
||||
current_binding.index == this_binding.index &&
|
||||
current_binding.sub_index == this_binding.sub_index)) {
|
||||
auto response =
|
||||
QMessageBox::information(this, tr("Key Already Bound"),
|
||||
tr("This key is already bound to the '%1' input.\n\n"
|
||||
"Continuing will unbind the previous input. Proceed?")
|
||||
.arg(current_binding.name),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
if (response == QMessageBox::No) {
|
||||
abort = true;
|
||||
} else {
|
||||
ClearBinding(current_binding);
|
||||
}
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
void ConfigureInput::SetBinding(InputBinding binding, const Common::ParamPackage& params) {
|
||||
bool abort = CheckForDuplicateMap(params, binding);
|
||||
if (!abort) {
|
||||
if (binding.binding_type == InputBindingType::NativeButton) {
|
||||
const auto button_id = binding.index;
|
||||
// Workaround for ZL & ZR for analog triggers like on XBOX controllors.
|
||||
// Analog triggers (from controllers like the XBOX controller) would not
|
||||
// work due to a different range of their signals (from 0 to 255 on
|
||||
// analog triggers instead of -32768 to 32768 on analog joysticks). The
|
||||
// SDL driver misinterprets analog triggers as analog joysticks.
|
||||
// TODO: reinterpret the signal range for analog triggers to map the
|
||||
// values correctly. This is required for the correct emulation of the
|
||||
// analog triggers of the GameCube controller.
|
||||
Common::ParamPackage new_params = params;
|
||||
if (button_id == Settings::NativeButton::ZL ||
|
||||
button_id == Settings::NativeButton::ZR) {
|
||||
new_params.Set("direction", "+");
|
||||
new_params.Set("threshold", "0.5");
|
||||
}
|
||||
buttons_param[button_id] = std::move(new_params);
|
||||
} else if (binding.binding_type == InputBindingType::AnalogFromButton) {
|
||||
const auto button_name = analog_sub_buttons[binding.sub_index];
|
||||
if (analogs_param[binding.index].Get("engine", "") != "analog_from_button") {
|
||||
analogs_param[binding.index] = {
|
||||
{"engine", "analog_from_button"},
|
||||
};
|
||||
}
|
||||
analogs_param[binding.index].Set(button_name, params.Serialize());
|
||||
} else if (binding.binding_type == InputBindingType::NativeAnalog) {
|
||||
const auto analog_id = binding.index;
|
||||
analogs_param[analog_id] = params;
|
||||
} else if (binding.binding_type == InputBindingType::CModButton) {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") {
|
||||
analogs_param[analog_id] = {
|
||||
{"engine", "analog_from_button"},
|
||||
};
|
||||
}
|
||||
analogs_param[analog_id].Set("modifier", params.Serialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyConfiguration();
|
||||
UpdateButtonLabels();
|
||||
Settings::SaveProfile(ui->profile->currentIndex());
|
||||
}
|
||||
|
||||
void ConfigureInput::ApplyConfiguration() {
|
||||
|
||||
Settings::values.use_artic_base_controller = ui->use_artic_controller->isChecked();
|
||||
@ -439,26 +465,106 @@ void ConfigureInput::EmitInputKeysChanged() {
|
||||
emit InputKeysChanged(GetUsedKeyboardKeys());
|
||||
}
|
||||
|
||||
void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) {
|
||||
void ConfigureInput::OnHotkeysChanged(
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list) {
|
||||
hotkey_list = new_key_list;
|
||||
}
|
||||
|
||||
QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
|
||||
QList<QKeySequence> list;
|
||||
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
|
||||
void ConfigureInput::OnClearBinding(ConfigureInput::InputBinding binding) {
|
||||
ClearBinding(binding);
|
||||
}
|
||||
|
||||
bool sameInput(const Common::ParamPackage& param1, const Common::ParamPackage& param2) {
|
||||
// if the engines or guid's don't match, return false as default
|
||||
if (!param1.Has("engine") || !param2.Has("engine"))
|
||||
return false;
|
||||
if (param1.Get("engine", "") != param2.Get("engine", ""))
|
||||
return false;
|
||||
if (param1.Get("guid", "") != param2.Get("guid", ""))
|
||||
return false;
|
||||
|
||||
const bool sameCode = param1.Get("code", -1) == param2.Get("code", -2);
|
||||
const bool sameButton = param1.Get("button", -1) == param2.Get("button", -2);
|
||||
const bool sameAxisAndDirection = param1.Get("axis", -1) == param2.Get("axis", -2) &&
|
||||
param1.Get("direction", "a") == param2.Get("direction", "b");
|
||||
const bool sameAxisXAndAxisY = param1.Get("axis_x", -1) == param2.Get("axis_x", -2) &&
|
||||
param1.Get("axis_y", -1) == param2.Get("axis_y", -2);
|
||||
const bool sameHatAndDirection = param1.Get("hat", -1) == param2.Get("hat", -2) &&
|
||||
param1.Get("direction", "a") == param2.Get("direction", "b");
|
||||
return sameCode || sameButton || sameAxisAndDirection || sameAxisXAndAxisY ||
|
||||
sameHatAndDirection;
|
||||
}
|
||||
|
||||
ConfigureInput::InputBinding ConfigureInput::GetMapping(const Common::ParamPackage& param) {
|
||||
if (!param.Has("engine"))
|
||||
return {InputBindingType::Empty, QString(), 0};
|
||||
// check for a button map
|
||||
for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
|
||||
const auto& button_param = buttons_param[button];
|
||||
if (button_param.Get("engine", "") == "keyboard") {
|
||||
list << QKeySequence(button_param.Get("code", 0));
|
||||
if (sameInput(param, button_param)) {
|
||||
return {InputBindingType::NativeButton, button_names[button], button};
|
||||
}
|
||||
}
|
||||
|
||||
// check for an analog map
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
||||
const auto& analog_param = analogs_param[analog_id];
|
||||
if (analog_param.Get("engine", "") == "analog_from_button") {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
|
||||
const Common::ParamPackage sub_button{
|
||||
analog_param.Get(analog_sub_buttons[sub_button_id], "")};
|
||||
list << QKeySequence(sub_button.Get("code", 0));
|
||||
if (sameInput(param, sub_button)) {
|
||||
const auto input_ui_string = QStringLiteral("%1 (%2)").arg(
|
||||
analog_names[analog_id], analog_sub_button_names[sub_button_id]);
|
||||
|
||||
if (analog_sub_buttons[sub_button_id] == "modifier") {
|
||||
return {InputBindingType::CModButton, input_ui_string, 0};
|
||||
}
|
||||
return {InputBindingType::AnalogFromButton, input_ui_string, analog_id,
|
||||
sub_button_id};
|
||||
}
|
||||
}
|
||||
} else if (sameInput(param, analog_param)) {
|
||||
return {InputBindingType::NativeAnalog, analog_names[analog_id], analog_id};
|
||||
}
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "keyboard" &&
|
||||
hotkey_list.contains(QKeySequence(param.Get("code", 0)))) {
|
||||
return hotkey_list[QKeySequence(param.Get("code", 0))];
|
||||
}
|
||||
|
||||
return {InputBindingType::Empty, QStringLiteral(""), 0};
|
||||
}
|
||||
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> ConfigureInput::GetUsedKeyboardKeys() {
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> list;
|
||||
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
|
||||
const auto& button_param = buttons_param[button];
|
||||
if (button_param.Get("engine", "") == "keyboard") {
|
||||
list[QKeySequence(button_param.Get("code", 0))] = ConfigureInput::InputBinding{
|
||||
InputBindingType::NativeButton, button_names[button], button};
|
||||
}
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
|
||||
const auto& analog_param = analogs_param[analog_id];
|
||||
if (analog_param.Get("engine", "") == "analog_from_button") {
|
||||
// stop one early so as not to include the "modifier" option
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM - 1;
|
||||
++sub_button_id) {
|
||||
const Common::ParamPackage sub_button{
|
||||
analog_param.Get(analog_sub_buttons[sub_button_id], "")};
|
||||
list[QKeySequence(sub_button.Get("code", 0))] = ConfigureInput::InputBinding{
|
||||
InputBindingType::AnalogFromButton,
|
||||
QStringLiteral("%1 (%2)").arg(analog_names[analog_id],
|
||||
analog_sub_button_names[sub_button_id]),
|
||||
analog_id, sub_button_id};
|
||||
}
|
||||
// add the circle mod button if it exist
|
||||
const Common::ParamPackage modButton{analog_param.Get("modifier", "")};
|
||||
if (modButton.Get("code", 0) != 0) {
|
||||
list[QKeySequence(modButton.Get("code", 0))] =
|
||||
InputBinding{InputBindingType::CModButton, QStringLiteral("Circle Mod")};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -498,6 +604,30 @@ void ConfigureInput::RestoreDefaults() {
|
||||
Settings::SaveProfile(Settings::values.current_input_profile_index);
|
||||
}
|
||||
|
||||
void ConfigureInput::ClearBinding(InputBinding binding) {
|
||||
if (binding.binding_type == InputBindingType::NativeButton) {
|
||||
buttons_param[binding.index].Clear();
|
||||
button_map[binding.index]->setText(tr("[not set]"));
|
||||
} else if (binding.binding_type == InputBindingType::AnalogFromButton) {
|
||||
const auto analog_id = binding.index;
|
||||
const auto sub_button_id = binding.sub_index;
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
} else if (binding.binding_type == InputBindingType::NativeAnalog) {
|
||||
const auto analog_id = binding.index;
|
||||
analogs_param[analog_id].Clear();
|
||||
UpdateButtonLabels();
|
||||
} else if (binding.binding_type == InputBindingType::Hotkey) {
|
||||
emit ClearHotkey(binding);
|
||||
} else if (binding.binding_type == InputBindingType::CModButton) {
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
analogs_param[analog_id].Erase("modifier");
|
||||
}
|
||||
ui->buttonCircleMod->setText(tr("[not set]"));
|
||||
}
|
||||
ApplyConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureInput::ClearAll() {
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
if (button_map[button_id] && button_map[button_id]->isEnabled())
|
||||
@ -628,7 +758,7 @@ void ConfigureInput::HandleClick(QPushButton* button,
|
||||
poll_timer->start(200); // Check for new inputs every 200ms
|
||||
}
|
||||
|
||||
void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
||||
void ConfigureInput::StopPolling() {
|
||||
releaseKeyboard();
|
||||
releaseMouse();
|
||||
timeout_timer->stop();
|
||||
@ -636,11 +766,13 @@ void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool a
|
||||
for (auto& poller : device_pollers) {
|
||||
poller->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool abort) {
|
||||
StopPolling();
|
||||
if (!abort && input_setter) {
|
||||
(*input_setter)(params);
|
||||
}
|
||||
|
||||
UpdateButtonLabels();
|
||||
input_setter.reset();
|
||||
}
|
||||
@ -651,19 +783,12 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) {
|
||||
|
||||
if (event->key() != Qt::Key_Escape && event->key() != previous_key_code) {
|
||||
if (want_keyboard_keys) {
|
||||
// Check if key is already bound
|
||||
if (hotkey_list.contains(QKeySequence(event->key())) ||
|
||||
GetUsedKeyboardKeys().contains(QKeySequence(event->key()))) {
|
||||
SetPollingResult({}, true);
|
||||
QMessageBox::critical(this, tr("Error!"),
|
||||
tr("You're using a key that's already bound."));
|
||||
return;
|
||||
}
|
||||
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
|
||||
false);
|
||||
auto param = Common::ParamPackage(InputCommon::GenerateKeyboardParam(event->key()));
|
||||
previous_key_code = 0;
|
||||
SetPollingResult(param, false);
|
||||
} else {
|
||||
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop
|
||||
// polling
|
||||
// Escape key wasn't pressed and we don't want any keyboard keys, so don't
|
||||
// stop polling
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright Citra Emulator Project / Lime3DS Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@ -30,6 +30,21 @@ class ConfigureInput : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class InputBindingType {
|
||||
NativeButton,
|
||||
AnalogFromButton,
|
||||
NativeAnalog,
|
||||
Hotkey,
|
||||
CModButton,
|
||||
Empty
|
||||
};
|
||||
struct InputBinding {
|
||||
ConfigureInput::InputBindingType binding_type;
|
||||
QString name;
|
||||
int index = -1; // used for anything there are multiple of
|
||||
int sub_index = -1; // used for sub-buttons
|
||||
};
|
||||
|
||||
explicit ConfigureInput(Core::System& system, QWidget* parent = nullptr);
|
||||
~ConfigureInput() override;
|
||||
|
||||
@ -44,10 +59,12 @@ public:
|
||||
/// Save the current input profile index
|
||||
void ApplyProfile();
|
||||
public slots:
|
||||
void OnHotkeysChanged(QList<QKeySequence> new_key_list);
|
||||
void OnHotkeysChanged(QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list);
|
||||
void OnClearBinding(ConfigureInput::InputBinding binding);
|
||||
|
||||
signals:
|
||||
void InputKeysChanged(QList<QKeySequence> new_key_list);
|
||||
void InputKeysChanged(QMap<QKeySequence, ConfigureInput::InputBinding> new_key_list);
|
||||
void ClearHotkey(ConfigureInput::InputBinding hotkey_to_clear);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
@ -66,6 +83,8 @@ private:
|
||||
|
||||
/// Each button input is represented by a QPushButton.
|
||||
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
|
||||
std::array<QString, Settings::NativeButton::NumButtons> button_names;
|
||||
std::array<QString, Settings::NativeAnalog::NumAnalogs> analog_names;
|
||||
|
||||
/// A group of five QPushButtons represent one analog input. The buttons each represent up,
|
||||
/// down, left, right, and modifier, respectively.
|
||||
@ -80,7 +99,8 @@ private:
|
||||
std::array<QLabel*, Settings::NativeAnalog::NumAnalogs>
|
||||
analog_map_deadzone_and_modifier_slider_label;
|
||||
|
||||
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
|
||||
std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
|
||||
std::array<QString, ANALOG_SUB_BUTTONS_NUM> analog_sub_button_names;
|
||||
|
||||
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
|
||||
|
||||
@ -89,15 +109,19 @@ private:
|
||||
* These can't be bound to any input key.
|
||||
* Synchronised with ConfigureHotkeys via signal-slot.
|
||||
*/
|
||||
QList<QKeySequence> hotkey_list;
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> hotkey_list;
|
||||
|
||||
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
|
||||
/// keyboard events are ignored.
|
||||
bool want_keyboard_keys = false;
|
||||
|
||||
/// Generates list of all used keys
|
||||
QList<QKeySequence> GetUsedKeyboardKeys();
|
||||
/// Generates list of all used keyboard keys for sharing with hotkey code
|
||||
QMap<QKeySequence, ConfigureInput::InputBinding> GetUsedKeyboardKeys();
|
||||
InputBinding GetMapping(const Common::ParamPackage& param);
|
||||
|
||||
void ClearBinding(InputBinding binding);
|
||||
void SetBinding(InputBinding binding, const Common::ParamPackage& params);
|
||||
bool CheckForDuplicateMap(const Common::ParamPackage& params, InputBinding this_binding);
|
||||
void MapFromButton(const Common::ParamPackage& params);
|
||||
void AutoMap();
|
||||
|
||||
@ -117,7 +141,9 @@ private:
|
||||
/// The key code of the previous state of the key being currently bound.
|
||||
int previous_key_code;
|
||||
|
||||
/// Finish polling and configure input using the input_setter
|
||||
/// Stop polling
|
||||
void StopPolling();
|
||||
/// configure input using the input_setter
|
||||
void SetPollingResult(const Common::ParamPackage& params, bool abort);
|
||||
|
||||
/// Handle key press events.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user