Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ControllerEmu: Allow changing the target gate radius of octagon sticks. #10682

Merged
merged 3 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
119 changes: 96 additions & 23 deletions Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "DolphinQt/Config/Mapping/MappingWidget.h"

#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
Expand Down Expand Up @@ -121,8 +122,54 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
for (auto& control : group->controls)
CreateControl(control.get(), form_layout, !indicator);

AddSettingWidgets(form_layout, group, ControllerEmu::SettingVisibility::Normal);

if (group->default_value != ControllerEmu::ControlGroup::DefaultValue::AlwaysEnabled)
{
QLabel* group_enable_label = new QLabel(tr("Enable"));
QCheckBox* group_enable_checkbox = new QCheckBox();
group_enable_checkbox->setChecked(group->enabled);
form_layout->insertRow(0, group_enable_label, group_enable_checkbox);
auto enable_group_by_checkbox = [group, form_layout, group_enable_label,
group_enable_checkbox] {
group->enabled = group_enable_checkbox->isChecked();
for (int i = 0; i < form_layout->count(); ++i)
{
QWidget* widget = form_layout->itemAt(i)->widget();
if (widget != nullptr && widget != group_enable_label && widget != group_enable_checkbox)
widget->setEnabled(group->enabled);
}
};
enable_group_by_checkbox();
connect(group_enable_checkbox, &QCheckBox::toggled, this, enable_group_by_checkbox);
connect(this, &MappingWidget::ConfigChanged, this,
[group_enable_checkbox, group] { group_enable_checkbox->setChecked(group->enabled); });
}

const auto advanced_setting_count = std::count_if(
group->numeric_settings.begin(), group->numeric_settings.end(), [](auto& setting) {
return setting->GetVisibility() == ControllerEmu::SettingVisibility::Advanced;
});

if (advanced_setting_count != 0)
{
const auto advanced_button = new QPushButton(tr("Advanced"));
form_layout->addRow(advanced_button);
connect(advanced_button, &QPushButton::clicked,
[this, group] { ShowAdvancedControlGroupDialog(group); });
}

return group_box;
}

void MappingWidget::AddSettingWidgets(QFormLayout* layout, ControllerEmu::ControlGroup* group,
ControllerEmu::SettingVisibility visibility)
{
for (auto& setting : group->numeric_settings)
{
if (setting->GetVisibility() != visibility)
continue;

QWidget* setting_widget = nullptr;

switch (setting->GetType())
Expand All @@ -149,33 +196,59 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
hbox->addWidget(setting_widget);
hbox->addWidget(CreateSettingAdvancedMappingButton(*setting));

form_layout->addRow(tr(setting->GetUIName()), hbox);
layout->addRow(tr(setting->GetUIName()), hbox);
}
}
}

if (group->default_value != ControllerEmu::ControlGroup::DefaultValue::AlwaysEnabled)
{
QLabel* group_enable_label = new QLabel(tr("Enable"));
QCheckBox* group_enable_checkbox = new QCheckBox();
group_enable_checkbox->setChecked(group->enabled);
form_layout->insertRow(0, group_enable_label, group_enable_checkbox);
auto enable_group_by_checkbox = [group, form_layout, group_enable_label,
group_enable_checkbox] {
group->enabled = group_enable_checkbox->isChecked();
for (int i = 0; i < form_layout->count(); ++i)
{
QWidget* widget = form_layout->itemAt(i)->widget();
if (widget != nullptr && widget != group_enable_label && widget != group_enable_checkbox)
widget->setEnabled(group->enabled);
}
};
enable_group_by_checkbox();
connect(group_enable_checkbox, &QCheckBox::toggled, this, enable_group_by_checkbox);
connect(this, &MappingWidget::ConfigChanged, this,
[group_enable_checkbox, group] { group_enable_checkbox->setChecked(group->enabled); });
}
void MappingWidget::ShowAdvancedControlGroupDialog(ControllerEmu::ControlGroup* group)
{
QDialog dialog{this};
dialog.setWindowTitle(tr(group->ui_name.c_str()));

return group_box;
const auto group_box = new QGroupBox(tr("Advanced Settings"));

QFormLayout* form_layout = new QFormLayout();

AddSettingWidgets(form_layout, group, ControllerEmu::SettingVisibility::Advanced);

const auto reset_button = new QPushButton(tr("Reset All"));
form_layout->addRow(reset_button);

connect(reset_button, &QPushButton::clicked, [this, group] {
for (auto& setting : group->numeric_settings)
{
if (setting->GetVisibility() != ControllerEmu::SettingVisibility::Advanced)
continue;

setting->SetToDefault();
}

emit ConfigChanged();
});

const auto main_layout = new QVBoxLayout();
const auto button_box = new QDialogButtonBox(QDialogButtonBox::Close);

group_box->setLayout(form_layout);

main_layout->addWidget(group_box);
main_layout->addWidget(button_box);

dialog.setLayout(main_layout);

// Focusing something else by default instead of the first spin box.
// Dynamically changing expression-backed settings pause when taking input.
// This just avoids that weird edge case behavior when the dialog is first open.
button_box->setFocus();

// Signal the newly created numeric setting widgets to display the current values.
emit ConfigChanged();

// Enable "Close" button functionality.
connect(button_box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);

dialog.exec();
}

QGroupBox* MappingWidget::CreateControlsBox(const QString& name, ControllerEmu::ControlGroup* group,
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/DolphinQt/Config/Mapping/MappingWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Control;
class ControlGroup;
class EmulatedController;
class NumericSettingBase;
enum class SettingVisibility;
} // namespace ControllerEmu

constexpr int INDICATOR_UPDATE_FREQ = 30;
Expand Down Expand Up @@ -57,6 +58,9 @@ class MappingWidget : public QWidget
int columns);
void CreateControl(const ControllerEmu::Control* control, QFormLayout* layout, bool indicator);
QPushButton* CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingBase& setting);
void AddSettingWidgets(QFormLayout* layout, ControllerEmu::ControlGroup* group,
ControllerEmu::SettingVisibility visibility);
void ShowAdvancedControlGroupDialog(ControllerEmu::ControlGroup* group);

private:
MappingWindow* m_parent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,29 @@ OctagonAnalogStick::OctagonAnalogStick(const char* name_, ControlState gate_radi

OctagonAnalogStick::OctagonAnalogStick(const char* name_, const char* ui_name_,
ControlState gate_radius)
: AnalogStick(name_, ui_name_, std::make_unique<ControllerEmu::OctagonStickGate>(gate_radius))
: AnalogStick(name_, ui_name_, std::make_unique<ControllerEmu::OctagonStickGate>(1.0))
{
AddVirtualNotchSetting(&m_virtual_notch_setting, 45);

AddSetting(
&m_gate_size_setting,
{_trans("Gate Size"),
// i18n: The percent symbol.
_trans("%"),
// i18n: Refers to plastic shell of game controller (stick gate) that limits stick movements.
_trans("Adjusts target radius of simulated stick gate."), nullptr,
SettingVisibility::Advanced},
gate_radius * 100, 0.01, 100);
}

ControlState OctagonAnalogStick::GetVirtualNotchSize() const
{
return m_virtual_notch_setting.GetValue() * MathUtil::TAU / 360;
}

ControlState OctagonAnalogStick::GetGateRadiusAtAngle(double ang) const
{
return AnalogStick::GetGateRadiusAtAngle(ang) * m_gate_size_setting.GetValue() / 100;
}

} // namespace ControllerEmu
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ class OctagonAnalogStick : public AnalogStick
OctagonAnalogStick(const char* name, const char* ui_name, ControlState gate_radius);

ControlState GetVirtualNotchSize() const override;
ControlState GetGateRadiusAtAngle(double ang) const override;

private:
SettingValue<double> m_virtual_notch_setting;
SettingValue<double> m_gate_size_setting;
};

} // namespace ControllerEmu
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ void ControlGroup::AddVirtualNotchSetting(SettingValue<double>* value, double ma
AddSetting(value,
{_trans("Virtual Notches"),
// i18n: The degrees symbol.
_trans("°"), _trans("Snap the thumbstick position to the nearest octagonal axis.")},
_trans("°"), _trans("Snap the thumbstick position to the nearest octagonal axis."),
nullptr, SettingVisibility::Advanced},
0, 0, max_virtual_notch_deg);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const char* NumericSettingBase::GetUIDescription() const
return m_details.ui_description;
}

SettingVisibility NumericSettingBase::GetVisibility() const
{
return m_details.visibility;
}

template <>
void NumericSetting<int>::SetExpressionFromValue()
{
Expand Down
21 changes: 18 additions & 3 deletions Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ enum class SettingType
Bool,
};

enum class SettingVisibility
{
Normal,
Advanced,
};

struct NumericSettingDetails
{
NumericSettingDetails(const char* const _ini_name, const char* const _ui_suffix = nullptr,
const char* const _ui_description = nullptr,
const char* const _ui_name = nullptr)
const char* const _ui_name = nullptr,
SettingVisibility _visibility = SettingVisibility::Normal)
: ini_name(_ini_name), ui_suffix(_ui_suffix), ui_description(_ui_description),
ui_name(_ui_name ? _ui_name : _ini_name)
ui_name(_ui_name ? _ui_name : _ini_name), visibility(_visibility)
{
}

Expand All @@ -41,6 +48,9 @@ struct NumericSettingDetails

// The name used in the UI (if different from ini file).
const char* const ui_name;

// Advanced settings should be harder to change in the UI. They might confuse users.
const SettingVisibility visibility;
};

class NumericSettingBase
Expand All @@ -66,9 +76,12 @@ class NumericSettingBase

virtual SettingType GetType() const = 0;

virtual void SetToDefault() = 0;

const char* GetUIName() const;
const char* GetUISuffix() const;
const char* GetUIDescription() const;
SettingVisibility GetVisibility() const;

protected:
NumericSettingDetails m_details;
Expand All @@ -92,9 +105,11 @@ class NumericSetting final : public NumericSettingBase
: NumericSettingBase(details), m_value(*value), m_default_value(default_value),
m_min_value(min_value), m_max_value(max_value)
{
m_value.SetValue(m_default_value);
SetToDefault();
}

void SetToDefault() override { m_value.SetValue(m_default_value); }

void LoadFromIni(const IniFile::Section& section, const std::string& group_name) override
{
std::string str_value;
Expand Down