mirror of
https://github.com/stenzek/duckstation.git
synced 2026-07-11 01:24:11 +02:00
e21bf74f29
Avoids runtime heap allocation. NOTE: appending, comparing, etc with latin1 views are still allocation-free.
283 lines
12 KiB
C++
283 lines
12 KiB
C++
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
|
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
|
|
#include "controllerglobalsettingswidget.h"
|
|
#include "controllerbindingwidgets.h"
|
|
#include "controllersettingswindow.h"
|
|
#include "controllersettingwidgetbinder.h"
|
|
#include "qtutils.h"
|
|
#include "settingwidgetbinder.h"
|
|
|
|
#include "fmt/format.h"
|
|
|
|
#include "util/ini_settings_interface.h"
|
|
#include "util/sdl_input_source.h"
|
|
|
|
#include <QtWidgets/QDialogButtonBox>
|
|
#include <QtWidgets/QGridLayout>
|
|
#include <QtWidgets/QGroupBox>
|
|
#include <QtWidgets/QScrollArea>
|
|
#include <QtWidgets/QVBoxLayout>
|
|
|
|
#include "moc_controllerglobalsettingswidget.cpp"
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsWindow* dialog)
|
|
: QWidget(parent), m_dialog(dialog)
|
|
{
|
|
m_ui.setupUi(this);
|
|
|
|
SettingsInterface* sif = dialog->getEditingSettingsInterface();
|
|
|
|
bool remove_sources = false;
|
|
if (sif)
|
|
{
|
|
// Editing game profile or input profile.
|
|
m_ui.useProfileHotkeyBindings->setChecked(sif->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false));
|
|
connect(m_ui.useProfileHotkeyBindings, &QCheckBox::checkStateChanged, this, [this](int new_state) {
|
|
m_dialog->setBoolValue("ControllerPorts", "UseProfileHotkeyBindings", (new_state == Qt::Checked));
|
|
emit bindingSetupChanged();
|
|
});
|
|
m_ui.useProfileInputSources->setChecked(sif->GetBoolValue("ControllerPorts", "UseProfileInputSources", false));
|
|
connect(m_ui.useProfileInputSources, &QCheckBox::checkStateChanged, this, [this](int new_state) {
|
|
m_dialog->setBoolValue("ControllerPorts", "UseProfileInputSources", (new_state == Qt::Checked));
|
|
emit bindingSetupChanged();
|
|
});
|
|
remove_sources = !m_ui.useProfileInputSources->isChecked();
|
|
}
|
|
else
|
|
{
|
|
// Editing base settings, remove profile options from the UI.
|
|
m_ui.mainLayout->removeWidget(m_ui.profileSettings);
|
|
QtUtils::SafeDeleteWidget(m_ui.profileSettings);
|
|
m_ui.profileSettingsLayout = nullptr;
|
|
m_ui.profileSettingsDescription = nullptr;
|
|
m_ui.useProfileHotkeyBindings = nullptr;
|
|
m_ui.useProfileInputSources = nullptr;
|
|
}
|
|
|
|
if (!remove_sources)
|
|
{
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLSource, "InputSources", "SDL", true);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLEnhancedMode, "InputSources",
|
|
"SDLControllerEnhancedMode", false);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableTouchPadAsPointer, "InputSources",
|
|
"SDLTouchpadAsPointer", false);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources",
|
|
"SDLPS5PlayerLED", false);
|
|
connect(m_ui.enableSDLSource, &QCheckBox::checkStateChanged, this,
|
|
&ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
|
|
connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked);
|
|
connect(m_ui.sdlHelpText, &QLabel::linkActivated, this, &ControllerGlobalSettingsWidget::sdlHelpTextLinkClicked);
|
|
|
|
#ifdef _WIN32
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableDInputSource, "InputSources", "DInput",
|
|
false);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableXInputSource, "InputSources", "XInput",
|
|
false);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableRawInput, "InputSources", "RawInput",
|
|
false);
|
|
#else
|
|
m_ui.groupsLayout->removeWidget(m_ui.xinputGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.xinputGroup);
|
|
m_ui.xinputLayout = nullptr;
|
|
m_ui.enableXInputSource = nullptr;
|
|
m_ui.xinputDescription = nullptr;
|
|
m_ui.groupsLayout->removeWidget(m_ui.dinputGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.dinputGroup);
|
|
m_ui.dinputLayout = nullptr;
|
|
m_ui.enableDInputSource = nullptr;
|
|
m_ui.dinputDescription = nullptr;
|
|
m_ui.pointerLayout->removeWidget(m_ui.enableRawInput);
|
|
m_ui.pointerLayout->removeWidget(m_ui.rawInputDescription);
|
|
QtUtils::SafeDeleteWidget(m_ui.enableRawInput);
|
|
QtUtils::SafeDeleteWidget(m_ui.rawInputDescription);
|
|
#endif
|
|
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "ControllerPorts",
|
|
"PointerXScale", 8.0f);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "ControllerPorts",
|
|
"PointerYScale", 8.0f);
|
|
|
|
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
|
|
[this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
|
|
connect(m_ui.pointerYScale, &QSlider::valueChanged, this,
|
|
[this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
|
|
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
|
|
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
|
|
|
|
updateSDLOptionsEnabled();
|
|
}
|
|
else
|
|
{
|
|
m_ui.groupsLayout->removeWidget(m_ui.sdlGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.sdlGroup);
|
|
m_ui.sdlGridLayout = nullptr;
|
|
m_ui.sdlLEDLayout = nullptr;
|
|
m_ui.enableSDLPS5PlayerLED = nullptr;
|
|
m_ui.ledSettings = nullptr;
|
|
m_ui.enableSDLSource = nullptr;
|
|
m_ui.enableSDLEnhancedMode = nullptr;
|
|
m_ui.sdlHelpText = nullptr;
|
|
m_ui.enableTouchPadAsPointer = nullptr;
|
|
m_ui.groupsLayout->removeWidget(m_ui.xinputGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.xinputGroup);
|
|
m_ui.xinputLayout = nullptr;
|
|
m_ui.enableXInputSource = nullptr;
|
|
m_ui.xinputDescription = nullptr;
|
|
m_ui.groupsLayout->removeWidget(m_ui.dinputGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.dinputGroup);
|
|
m_ui.dinputLayout = nullptr;
|
|
m_ui.enableDInputSource = nullptr;
|
|
m_ui.dinputDescription = nullptr;
|
|
m_ui.groupsLayout->removeWidget(m_ui.pointerGroup);
|
|
QtUtils::SafeDeleteWidget(m_ui.pointerGroup);
|
|
m_ui.pointerLayout = nullptr;
|
|
m_ui.pointerXScaleDescription = nullptr;
|
|
m_ui.pointerXScaleLayout = nullptr;
|
|
m_ui.pointerXScale = nullptr;
|
|
m_ui.pointerXScaleLabel = nullptr;
|
|
m_ui.pointerYScaleDescription = nullptr;
|
|
m_ui.pointerYScaleLayout = nullptr;
|
|
m_ui.pointerYScale = nullptr;
|
|
m_ui.pointerYScaleLabel = nullptr;
|
|
m_ui.enableRawInput = nullptr;
|
|
m_ui.rawInputDescription = nullptr;
|
|
}
|
|
|
|
// Mapping options are only shown in global settings.
|
|
if (m_dialog->isEditingGlobalSettings())
|
|
{
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableMouseMapping, "UI",
|
|
"EnableMouseMapping", false);
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSensorMapping, "UI",
|
|
"EnableSensorMapping", false);
|
|
}
|
|
else
|
|
{
|
|
QtUtils::SafeDeleteWidget(m_ui.mappingGroup);
|
|
m_ui.mappingLayout = nullptr;
|
|
m_ui.mappingDescription = nullptr;
|
|
m_ui.enableMouseMapping = nullptr;
|
|
m_ui.enableSensorMapping = nullptr;
|
|
}
|
|
|
|
m_ui.deviceList->setModel(g_core_thread->getInputDeviceListModel());
|
|
|
|
ControllerSettingWidgetBinder::BindWidgetToInputProfileEnumSetting(
|
|
sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode", &Settings::ParseMultitapModeName,
|
|
&Settings::GetMultitapModeName, &Settings::GetMultitapModeDisplayName, Settings::DEFAULT_MULTITAP_MODE,
|
|
MultitapMode::Count);
|
|
connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); });
|
|
}
|
|
|
|
ControllerGlobalSettingsWidget::~ControllerGlobalSettingsWidget() = default;
|
|
|
|
void ControllerGlobalSettingsWidget::sdlHelpTextLinkClicked(const QString& link)
|
|
{
|
|
if (link == "ADVANCED_SDL_OPTIONS"_L1)
|
|
{
|
|
QDialog* const dlg = new ControllerCustomSettingsDialog(m_dialog, m_dialog->getEditingSettingsInterface(),
|
|
"InputSources", SDLInputSource::GetAdvancedSettingsInfo(),
|
|
"SDLInputSource", tr("Advanced SDL Options"));
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
dlg->open();
|
|
}
|
|
}
|
|
|
|
void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled()
|
|
{
|
|
const bool enabled = m_ui.enableSDLSource->isChecked();
|
|
if (m_ui.enableSDLEnhancedMode)
|
|
m_ui.enableSDLEnhancedMode->setEnabled(enabled);
|
|
if (m_ui.enableTouchPadAsPointer)
|
|
m_ui.enableTouchPadAsPointer->setEnabled(enabled);
|
|
if (m_ui.enableSDLPS5PlayerLED)
|
|
m_ui.enableSDLPS5PlayerLED->setEnabled(enabled);
|
|
if (m_ui.ledSettings)
|
|
m_ui.ledSettings->setEnabled(enabled);
|
|
}
|
|
|
|
void ControllerGlobalSettingsWidget::ledSettingsClicked()
|
|
{
|
|
static constexpr auto config_key = [](u32 player_id, bool active) {
|
|
return TinyString::from_format("Player{}{}LED", player_id, active ? "Active" : "");
|
|
};
|
|
|
|
if (std::ranges::none_of(
|
|
g_core_thread->getInputDeviceListModel()->getDeviceList(),
|
|
[](const InputDeviceListModel::Device& dev) { return (dev.key.source_type == InputSourceType::SDL); }))
|
|
{
|
|
QtUtils::AsyncMessageBox(this, QMessageBox::Critical, tr("Error"), tr("No SDL devices are currently connected."));
|
|
return;
|
|
}
|
|
|
|
QDialog* const dlg = new QDialog(this);
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
dlg->setWindowTitle(tr("Controller LED Settings"));
|
|
dlg->setFixedWidth(450);
|
|
dlg->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
|
|
QVBoxLayout* const main_layout = new QVBoxLayout(dlg);
|
|
|
|
QHBoxLayout* const heading_layout = new QHBoxLayout;
|
|
QLabel* const icon = new QLabel;
|
|
icon->setPixmap(QIcon(u":/icons/monochrome/svg/lightbulb-line.svg"_s).pixmap(32));
|
|
QLabel* const heading = new QLabel(
|
|
tr("<strong>Controller LED Settings</strong><br>\nThe \"alternate\" color is used when analog mode is active."));
|
|
heading->setWordWrap(true);
|
|
heading_layout->addWidget(icon, 0, Qt::AlignTop | Qt::AlignLeft);
|
|
heading_layout->addWidget(heading, 1);
|
|
main_layout->addLayout(heading_layout);
|
|
|
|
QScrollArea* const scroll_area = new QScrollArea(dlg);
|
|
scroll_area->setWidgetResizable(true);
|
|
scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
main_layout->addWidget(scroll_area, 1);
|
|
|
|
QWidget* const scroll_area_widget = new QWidget(scroll_area);
|
|
scroll_area->setWidget(scroll_area_widget);
|
|
|
|
QVBoxLayout* const scroll_area_layout = new QVBoxLayout(scroll_area_widget);
|
|
scroll_area_layout->setContentsMargins(10, 10, 10, 10);
|
|
|
|
for (const InputDeviceListModel::Device& dev : g_core_thread->getInputDeviceListModel()->getDeviceList())
|
|
{
|
|
if (dev.key.source_type != InputSourceType::SDL)
|
|
continue;
|
|
|
|
QGroupBox* const gbox = new QGroupBox(QStringLiteral("%1: %2").arg(dev.identifier).arg(dev.display_name));
|
|
QGridLayout* const gbox_layout = new QGridLayout(gbox);
|
|
|
|
for (u32 active = 0; active < 2; active++)
|
|
{
|
|
gbox_layout->addWidget(new QLabel(active ? tr("Alternate Mode:") : tr("Normal Mode:"), dlg),
|
|
static_cast<int>(active), 0);
|
|
|
|
ColorPickerButton* const button = new ColorPickerButton(gbox);
|
|
button->setColor(SDLInputSource::ParseRGBForPlayerId(
|
|
m_dialog->getStringValue("SDLExtra", config_key(dev.key.source_index, active != 0), ""), dev.key.source_index,
|
|
active != 0));
|
|
gbox_layout->addWidget(button, static_cast<int>(active), 1);
|
|
connect(button, &ColorPickerButton::colorChanged, this,
|
|
[this, player_id = dev.key.source_index, active](u32 new_rgb) {
|
|
m_dialog->setStringValue("SDLExtra", config_key(player_id, active),
|
|
TinyString::from_format("{:06X}", new_rgb));
|
|
});
|
|
}
|
|
|
|
scroll_area_layout->addWidget(gbox);
|
|
}
|
|
|
|
scroll_area_layout->addStretch(1);
|
|
|
|
QDialogButtonBox* const bbox = new QDialogButtonBox(QDialogButtonBox::Close, dlg);
|
|
bbox->button(QDialogButtonBox::Close)->setDefault(true);
|
|
connect(bbox, &QDialogButtonBox::rejected, dlg, &QDialog::accept);
|
|
main_layout->addWidget(bbox);
|
|
|
|
dlg->open();
|
|
}
|