bug fixes and refactors

This commit is contained in:
David Griswold 2026-06-10 16:25:25 -05:00
parent b13911599b
commit 7a032e883a
3 changed files with 143 additions and 120 deletions

View File

@ -52,8 +52,8 @@ QMap<QKeySequence, ConfigureInput::InputBinding> ConfigureHotkeys::GetUsedKeyLis
auto seq = QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText);
if (seq.count() == 1 &&
seq[0].keyboardModifiers() == Qt::KeyboardModifier::NoModifier) {
auto binding =
ConfigureInput::InputBinding("Hotkey", parent->child(r2, 0)->text(), r2);
auto binding = ConfigureInput::InputBinding(
ConfigureInput::InputBindingType::Hotkey, parent->child(r2, 0)->text(), r2);
list[seq] = binding;
}
}
@ -147,7 +147,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
if (response == QMessageBox::No) {
return;
} else {
if (current_binding.binding_type == "Hotkey") {
if (current_binding.binding_type == ConfigureInput::InputBindingType::Hotkey) {
model->setData(index.sibling(current_binding.index, hotkey_column),
QStringLiteral(""));
} else {
@ -162,7 +162,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
void ConfigureHotkeys::OnClearBinding(ConfigureInput::InputBinding hotkey_to_clear) {
const auto index = ui->hotkey_list->currentIndex();
if (hotkey_to_clear.binding_type == "Hotkey") {
if (hotkey_to_clear.binding_type == ConfigureInput::InputBindingType::Hotkey) {
model->setData(index.sibling(hotkey_to_clear.index, hotkey_column), QStringLiteral(""));
}
EmitHotkeysChanged();
@ -171,7 +171,8 @@ void ConfigureHotkeys::OnClearBinding(ConfigureInput::InputBinding hotkey_to_cle
std::pair<bool, ConfigureInput::InputBinding> ConfigureHotkeys::IsUsedKey(
QKeySequence key_sequence) const {
if (key_sequence == QKeySequence::fromString(QStringLiteral(""), QKeySequence::NativeText)) {
return std::make_pair(false, ConfigureInput::InputBinding{"", QStringLiteral(""), -1});
return std::make_pair(false, ConfigureInput::InputBinding{
ConfigureInput::InputBindingType::Empty, QString(), -1});
}
if (input_keys_list.contains(key_sequence)) {
@ -188,12 +189,15 @@ std::pair<bool, ConfigureInput::InputBinding> ConfigureHotkeys::IsUsedKey(
if (key_sequence == key_seq) {
return std::make_pair(
true, ConfigureInput::InputBinding{"Hotkey", parent->child(r2, 0)->text(), r2});
true, ConfigureInput::InputBinding{ConfigureInput::InputBindingType::Hotkey,
parent->child(r2, 0)->text(), r2});
}
}
}
return std::make_pair(false, ConfigureInput::InputBinding{"", QStringLiteral(""), -1});
return std::make_pair(false,
ConfigureInput::InputBinding{ConfigureInput::InputBindingType::Empty,
QStringLiteral(""), -1});
}
void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {

View File

@ -46,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]");
@ -223,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);
});
@ -248,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));
});
@ -276,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);
});
@ -288,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));
@ -316,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);
}
@ -348,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));
});
@ -429,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();
@ -481,12 +497,12 @@ bool sameInput(const Common::ParamPackage& param1, const Common::ParamPackage& p
ConfigureInput::InputBinding ConfigureInput::GetMapping(const Common::ParamPackage& param) {
if (!param.Has("engine"))
return {"", QStringLiteral(""), 0};
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 (sameInput(param, button_param)) {
return {"NativeButton", button_names[button], button};
return {InputBindingType::NativeButton, button_names[button], button};
}
}
// check for an analog map
@ -501,13 +517,14 @@ ConfigureInput::InputBinding ConfigureInput::GetMapping(const Common::ParamPacka
analog_names[analog_id], analog_sub_button_names[sub_button_id]);
if (analog_sub_buttons[sub_button_id] == "modifier") {
return {"CircleModButton", input_ui_string, 0};
return {InputBindingType::CModButton, input_ui_string, 0};
}
return {"AnalogButton", input_ui_string, analog_id, sub_button_id};
return {InputBindingType::AnalogFromButton, input_ui_string, analog_id,
sub_button_id};
}
}
} else if (sameInput(param, analog_param)) {
return {"Analog", analog_names[analog_id], analog_id};
return {InputBindingType::NativeAnalog, analog_names[analog_id], analog_id};
}
}
@ -516,7 +533,7 @@ ConfigureInput::InputBinding ConfigureInput::GetMapping(const Common::ParamPacka
return hotkey_list[QKeySequence(param.Get("code", 0))];
}
return {"", QStringLiteral(""), 0};
return {InputBindingType::Empty, QStringLiteral(""), 0};
}
QMap<QKeySequence, ConfigureInput::InputBinding> ConfigureInput::GetUsedKeyboardKeys() {
@ -524,23 +541,31 @@ QMap<QKeySequence, ConfigureInput::InputBinding> ConfigureInput::GetUsedKeyboard
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{"NativeButton", button_names[button], button};
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") {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
// 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{
"AnalogButton",
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")};
}
}
}
@ -580,21 +605,21 @@ void ConfigureInput::RestoreDefaults() {
}
void ConfigureInput::ClearBinding(InputBinding binding) {
if (binding.binding_type == "NativeButton") {
if (binding.binding_type == InputBindingType::NativeButton) {
buttons_param[binding.index].Clear();
button_map[binding.index]->setText(tr("[not set]"));
} else if (binding.binding_type == "AnalogButton") {
} 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 == "Analog") {
} else if (binding.binding_type == InputBindingType::NativeAnalog) {
const auto analog_id = binding.index;
analogs_param[analog_id].Clear();
UpdateButtonLabels();
} else if (binding.binding_type == "Hotkey") {
} else if (binding.binding_type == InputBindingType::Hotkey) {
emit ClearHotkey(binding);
} else if (binding.binding_type == "CircleModButton") {
} 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");
}
@ -744,26 +769,10 @@ void ConfigureInput::StopPolling() {
}
void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool abort) {
auto const current_binding = GetMapping(params);
StopPolling();
if (!abort && current_binding.binding_type != "") {
auto response =
QMessageBox::information(this, tr("Key Already Bound"),
tr("This key 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) {
abort = true;
return;
} else {
ClearBinding(current_binding);
}
}
if (!abort && input_setter) {
(*input_setter)(params);
}
UpdateButtonLabels();
input_setter.reset();
}
@ -778,8 +787,8 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) {
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;
}
}

View File

@ -30,12 +30,21 @@ class ConfigureInput : public QWidget {
Q_OBJECT
public:
enum class InputBindingType {
NativeButton,
AnalogFromButton,
NativeAnalog,
Hotkey,
CModButton,
Empty
};
struct InputBinding {
std::string binding_type;
ConfigureInput::InputBindingType binding_type;
QString name;
int index;
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;
@ -111,7 +120,8 @@ private:
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();