Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/duckstation-qt/controllerglobalsettingswidget.cpp
7451 views
1
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "controllerglobalsettingswidget.h"
5
#include "controllerbindingwidgets.h"
6
#include "controllersettingswindow.h"
7
#include "controllersettingwidgetbinder.h"
8
#include "qtutils.h"
9
#include "settingwidgetbinder.h"
10
11
#include "fmt/format.h"
12
13
#include "util/ini_settings_interface.h"
14
#include "util/sdl_input_source.h"
15
16
#include <QtWidgets/QDialogButtonBox>
17
#include <QtWidgets/QGridLayout>
18
#include <QtWidgets/QGroupBox>
19
#include <QtWidgets/QScrollArea>
20
#include <QtWidgets/QVBoxLayout>
21
22
#include "moc_controllerglobalsettingswidget.cpp"
23
24
using namespace Qt::StringLiterals;
25
26
ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsWindow* dialog)
27
: QWidget(parent), m_dialog(dialog)
28
{
29
m_ui.setupUi(this);
30
31
SettingsInterface* sif = dialog->getEditingSettingsInterface();
32
33
bool remove_sources = false;
34
if (sif)
35
{
36
// Editing game profile or input profile.
37
m_ui.useProfileHotkeyBindings->setChecked(sif->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false));
38
connect(m_ui.useProfileHotkeyBindings, &QCheckBox::checkStateChanged, this, [this](int new_state) {
39
m_dialog->setBoolValue("ControllerPorts", "UseProfileHotkeyBindings", (new_state == Qt::Checked));
40
emit bindingSetupChanged();
41
});
42
m_ui.useProfileInputSources->setChecked(sif->GetBoolValue("ControllerPorts", "UseProfileInputSources", false));
43
connect(m_ui.useProfileInputSources, &QCheckBox::checkStateChanged, this, [this](int new_state) {
44
m_dialog->setBoolValue("ControllerPorts", "UseProfileInputSources", (new_state == Qt::Checked));
45
emit bindingSetupChanged();
46
});
47
remove_sources = !m_ui.useProfileInputSources->isChecked();
48
}
49
else
50
{
51
// Editing base settings, remove profile options from the UI.
52
m_ui.mainLayout->removeWidget(m_ui.profileSettings);
53
QtUtils::SafeDeleteWidget(m_ui.profileSettings);
54
m_ui.profileSettingsLayout = nullptr;
55
m_ui.profileSettingsDescription = nullptr;
56
m_ui.useProfileHotkeyBindings = nullptr;
57
m_ui.useProfileInputSources = nullptr;
58
}
59
60
if (!remove_sources)
61
{
62
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLSource, "InputSources", "SDL", true);
63
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLEnhancedMode, "InputSources",
64
"SDLControllerEnhancedMode", false);
65
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableTouchPadAsPointer, "InputSources",
66
"SDLTouchpadAsPointer", false);
67
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources",
68
"SDLPS5PlayerLED", false);
69
connect(m_ui.enableSDLSource, &QCheckBox::checkStateChanged, this,
70
&ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
71
connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked);
72
connect(m_ui.sdlHelpText, &QLabel::linkActivated, this, &ControllerGlobalSettingsWidget::sdlHelpTextLinkClicked);
73
74
#ifdef _WIN32
75
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableDInputSource, "InputSources", "DInput",
76
false);
77
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableXInputSource, "InputSources", "XInput",
78
false);
79
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableRawInput, "InputSources", "RawInput",
80
false);
81
#else
82
m_ui.groupsLayout->removeWidget(m_ui.xinputGroup);
83
QtUtils::SafeDeleteWidget(m_ui.xinputGroup);
84
m_ui.xinputLayout = nullptr;
85
m_ui.enableXInputSource = nullptr;
86
m_ui.xinputDescription = nullptr;
87
m_ui.groupsLayout->removeWidget(m_ui.dinputGroup);
88
QtUtils::SafeDeleteWidget(m_ui.dinputGroup);
89
m_ui.dinputLayout = nullptr;
90
m_ui.enableDInputSource = nullptr;
91
m_ui.dinputDescription = nullptr;
92
m_ui.pointerLayout->removeWidget(m_ui.enableRawInput);
93
m_ui.pointerLayout->removeWidget(m_ui.rawInputDescription);
94
QtUtils::SafeDeleteWidget(m_ui.enableRawInput);
95
QtUtils::SafeDeleteWidget(m_ui.rawInputDescription);
96
#endif
97
98
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "ControllerPorts",
99
"PointerXScale", 8.0f);
100
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "ControllerPorts",
101
"PointerYScale", 8.0f);
102
103
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
104
[this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
105
connect(m_ui.pointerYScale, &QSlider::valueChanged, this,
106
[this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
107
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
108
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
109
110
updateSDLOptionsEnabled();
111
}
112
else
113
{
114
m_ui.groupsLayout->removeWidget(m_ui.sdlGroup);
115
QtUtils::SafeDeleteWidget(m_ui.sdlGroup);
116
m_ui.sdlGridLayout = nullptr;
117
m_ui.sdlLEDLayout = nullptr;
118
m_ui.enableSDLPS5PlayerLED = nullptr;
119
m_ui.ledSettings = nullptr;
120
m_ui.enableSDLSource = nullptr;
121
m_ui.enableSDLEnhancedMode = nullptr;
122
m_ui.sdlHelpText = nullptr;
123
m_ui.enableTouchPadAsPointer = nullptr;
124
m_ui.groupsLayout->removeWidget(m_ui.xinputGroup);
125
QtUtils::SafeDeleteWidget(m_ui.xinputGroup);
126
m_ui.xinputLayout = nullptr;
127
m_ui.enableXInputSource = nullptr;
128
m_ui.xinputDescription = nullptr;
129
m_ui.groupsLayout->removeWidget(m_ui.dinputGroup);
130
QtUtils::SafeDeleteWidget(m_ui.dinputGroup);
131
m_ui.dinputLayout = nullptr;
132
m_ui.enableDInputSource = nullptr;
133
m_ui.dinputDescription = nullptr;
134
m_ui.groupsLayout->removeWidget(m_ui.pointerGroup);
135
QtUtils::SafeDeleteWidget(m_ui.pointerGroup);
136
m_ui.pointerLayout = nullptr;
137
m_ui.pointerXScaleDescription = nullptr;
138
m_ui.pointerXScaleLayout = nullptr;
139
m_ui.pointerXScale = nullptr;
140
m_ui.pointerXScaleLabel = nullptr;
141
m_ui.pointerYScaleDescription = nullptr;
142
m_ui.pointerYScaleLayout = nullptr;
143
m_ui.pointerYScale = nullptr;
144
m_ui.pointerYScaleLabel = nullptr;
145
m_ui.enableRawInput = nullptr;
146
m_ui.rawInputDescription = nullptr;
147
}
148
149
// Mapping options are only shown in global settings.
150
if (m_dialog->isEditingGlobalSettings())
151
{
152
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableMouseMapping, "UI",
153
"EnableMouseMapping", false);
154
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSensorMapping, "UI",
155
"EnableSensorMapping", false);
156
}
157
else
158
{
159
QtUtils::SafeDeleteWidget(m_ui.mappingGroup);
160
m_ui.mappingLayout = nullptr;
161
m_ui.mappingDescription = nullptr;
162
m_ui.enableMouseMapping = nullptr;
163
m_ui.enableSensorMapping = nullptr;
164
}
165
166
m_ui.deviceList->setModel(g_core_thread->getInputDeviceListModel());
167
168
ControllerSettingWidgetBinder::BindWidgetToInputProfileEnumSetting(
169
sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode", &Settings::ParseMultitapModeName,
170
&Settings::GetMultitapModeName, &Settings::GetMultitapModeDisplayName, Settings::DEFAULT_MULTITAP_MODE,
171
MultitapMode::Count);
172
connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); });
173
}
174
175
ControllerGlobalSettingsWidget::~ControllerGlobalSettingsWidget() = default;
176
177
void ControllerGlobalSettingsWidget::sdlHelpTextLinkClicked(const QString& link)
178
{
179
if (link == "ADVANCED_SDL_OPTIONS"_L1)
180
{
181
QDialog* const dlg = new ControllerCustomSettingsDialog(m_dialog, m_dialog->getEditingSettingsInterface(),
182
"InputSources", SDLInputSource::GetAdvancedSettingsInfo(),
183
"SDLInputSource", tr("Advanced SDL Options"));
184
dlg->setAttribute(Qt::WA_DeleteOnClose);
185
dlg->open();
186
}
187
}
188
189
void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled()
190
{
191
const bool enabled = m_ui.enableSDLSource->isChecked();
192
if (m_ui.enableSDLEnhancedMode)
193
m_ui.enableSDLEnhancedMode->setEnabled(enabled);
194
if (m_ui.enableTouchPadAsPointer)
195
m_ui.enableTouchPadAsPointer->setEnabled(enabled);
196
if (m_ui.enableSDLPS5PlayerLED)
197
m_ui.enableSDLPS5PlayerLED->setEnabled(enabled);
198
if (m_ui.ledSettings)
199
m_ui.ledSettings->setEnabled(enabled);
200
}
201
202
void ControllerGlobalSettingsWidget::ledSettingsClicked()
203
{
204
static constexpr auto config_key = [](u32 player_id, bool active) {
205
return TinyString::from_format("Player{}{}LED", player_id, active ? "Active" : "");
206
};
207
208
if (std::ranges::none_of(
209
g_core_thread->getInputDeviceListModel()->getDeviceList(),
210
[](const InputDeviceListModel::Device& dev) { return (dev.key.source_type == InputSourceType::SDL); }))
211
{
212
QtUtils::AsyncMessageBox(this, QMessageBox::Critical, tr("Error"), tr("No SDL devices are currently connected."));
213
return;
214
}
215
216
QDialog* const dlg = new QDialog(this);
217
dlg->setAttribute(Qt::WA_DeleteOnClose);
218
dlg->setWindowTitle(tr("Controller LED Settings"));
219
dlg->setFixedWidth(450);
220
dlg->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
221
222
QVBoxLayout* const main_layout = new QVBoxLayout(dlg);
223
224
QHBoxLayout* const heading_layout = new QHBoxLayout;
225
QLabel* const icon = new QLabel;
226
icon->setPixmap(QIcon::fromTheme("lightbulb-line"_L1).pixmap(32));
227
QLabel* const heading = new QLabel(
228
tr("<strong>Controller LED Settings</strong><br>\nThe \"alternate\" color is used when analog mode is active."));
229
heading->setWordWrap(true);
230
heading_layout->addWidget(icon, 0, Qt::AlignTop | Qt::AlignLeft);
231
heading_layout->addWidget(heading, 1);
232
main_layout->addLayout(heading_layout);
233
234
QScrollArea* const scroll_area = new QScrollArea(dlg);
235
scroll_area->setWidgetResizable(true);
236
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
237
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
238
main_layout->addWidget(scroll_area, 1);
239
240
QWidget* const scroll_area_widget = new QWidget(scroll_area);
241
scroll_area->setWidget(scroll_area_widget);
242
243
QVBoxLayout* const scroll_area_layout = new QVBoxLayout(scroll_area_widget);
244
scroll_area_layout->setContentsMargins(10, 10, 10, 10);
245
246
for (const InputDeviceListModel::Device& dev : g_core_thread->getInputDeviceListModel()->getDeviceList())
247
{
248
if (dev.key.source_type != InputSourceType::SDL)
249
continue;
250
251
QGroupBox* const gbox = new QGroupBox(QStringLiteral("%1: %2").arg(dev.identifier).arg(dev.display_name));
252
QGridLayout* const gbox_layout = new QGridLayout(gbox);
253
254
for (u32 active = 0; active < 2; active++)
255
{
256
gbox_layout->addWidget(new QLabel(active ? tr("Alternate Mode:") : tr("Normal Mode:"), dlg),
257
static_cast<int>(active), 0);
258
259
ColorPickerButton* const button = new ColorPickerButton(gbox);
260
button->setColor(SDLInputSource::ParseRGBForPlayerId(
261
m_dialog->getStringValue("SDLExtra", config_key(dev.key.source_index, active != 0), ""), dev.key.source_index,
262
active != 0));
263
gbox_layout->addWidget(button, static_cast<int>(active), 1);
264
connect(button, &ColorPickerButton::colorChanged, this,
265
[this, player_id = dev.key.source_index, active](u32 new_rgb) {
266
m_dialog->setStringValue("SDLExtra", config_key(player_id, active),
267
TinyString::from_format("{:06X}", new_rgb));
268
});
269
}
270
271
scroll_area_layout->addWidget(gbox);
272
}
273
274
scroll_area_layout->addStretch(1);
275
276
QDialogButtonBox* const bbox = new QDialogButtonBox(QDialogButtonBox::Close, dlg);
277
bbox->button(QDialogButtonBox::Close)->setDefault(true);
278
connect(bbox, &QDialogButtonBox::rejected, dlg, &QDialog::accept);
279
main_layout->addWidget(bbox);
280
281
dlg->open();
282
}
283
284