Skip to content

Commit

Permalink
Qt: Separate controller settings to global and profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Jan 11, 2025
1 parent 2d63b34 commit e4c11aa
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 105 deletions.
178 changes: 101 additions & 77 deletions src/duckstation-qt/controllersettingswindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "controllerbindingwidgets.h"
#include "controllerglobalsettingswidget.h"
#include "hotkeysettingswidget.h"
#include "mainwindow.h"
#include "qthost.h"

#include "core/controller.h"
Expand All @@ -22,8 +23,8 @@
#include <array>

ControllerSettingsWindow::ControllerSettingsWindow(SettingsInterface* game_sif /* = nullptr */,
QWidget* parent /* = nullptr */)
: QWidget(parent), m_editing_settings_interface(game_sif)
bool edit_profiles /* = false */, QWidget* parent /* = nullptr */)
: QWidget(parent), m_editing_settings_interface(game_sif), m_editing_input_profiles(edit_profiles)
{
m_ui.setupUi(this);

Expand All @@ -35,23 +36,9 @@ ControllerSettingsWindow::ControllerSettingsWindow(SettingsInterface* game_sif /
&ControllerSettingsWindow::onCategoryCurrentRowChanged);
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsWindow::close);

if (!game_sif)
{
refreshProfileList();

m_ui.editProfileLayout->removeWidget(m_ui.copyGlobalSettings);
delete m_ui.copyGlobalSettings;
m_ui.copyGlobalSettings = nullptr;

connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this,
&ControllerSettingsWindow::onCurrentProfileChanged);
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked);
connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked);
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked);
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked);
}
else
if (!game_sif && !edit_profiles)
{
// editing global settings
m_ui.editProfileLayout->removeWidget(m_ui.editProfileLabel);
delete m_ui.editProfileLabel;
m_ui.editProfileLabel = nullptr;
Expand All @@ -67,11 +54,51 @@ ControllerSettingsWindow::ControllerSettingsWindow(SettingsInterface* game_sif /
m_ui.editProfileLayout->removeWidget(m_ui.deleteProfile);
delete m_ui.deleteProfile;
m_ui.deleteProfile = nullptr;
m_ui.editProfileLayout->removeWidget(m_ui.copyGlobalSettings);
delete m_ui.copyGlobalSettings;
m_ui.copyGlobalSettings = nullptr;

if (QPushButton* button = m_ui.buttonBox->button(QDialogButtonBox::RestoreDefaults))
connect(button, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked);
}
else
{
if (QPushButton* button = m_ui.buttonBox->button(QDialogButtonBox::RestoreDefaults))
m_ui.buttonBox->removeButton(button);

connect(m_ui.copyGlobalSettings, &QPushButton::clicked, this,
&ControllerSettingsWindow::onCopyGlobalSettingsClicked);
connect(m_ui.restoreDefaults, &QPushButton::clicked, this,
&ControllerSettingsWindow::onRestoreDefaultsForGameClicked);

if (edit_profiles)
{
setWindowTitle(tr("DuckStation Controller Profiles"));
refreshProfileList();

connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this,
&ControllerSettingsWindow::onCurrentProfileChanged);
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked);
connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked);
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked);
}
else
{
// editing game settings
m_ui.editProfileLayout->removeWidget(m_ui.editProfileLabel);
delete m_ui.editProfileLabel;
m_ui.editProfileLabel = nullptr;
m_ui.editProfileLayout->removeWidget(m_ui.currentProfile);
delete m_ui.currentProfile;
m_ui.currentProfile = nullptr;
m_ui.editProfileLayout->removeWidget(m_ui.newProfile);
delete m_ui.newProfile;
m_ui.newProfile = nullptr;
m_ui.editProfileLayout->removeWidget(m_ui.applyProfile);
delete m_ui.applyProfile;
m_ui.applyProfile = nullptr;
m_ui.editProfileLayout->removeWidget(m_ui.deleteProfile);
delete m_ui.deleteProfile;
m_ui.deleteProfile = nullptr;
}
}

createWidgets();
Expand All @@ -81,7 +108,7 @@ ControllerSettingsWindow::~ControllerSettingsWindow() = default;

void ControllerSettingsWindow::editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif)
{
ControllerSettingsWindow* dlg = new ControllerSettingsWindow(sif, parent);
ControllerSettingsWindow* dlg = new ControllerSettingsWindow(sif, false, parent);
dlg->setWindowFlag(Qt::Window);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setWindowModality(Qt::WindowModality::WindowModal);
Expand Down Expand Up @@ -207,9 +234,8 @@ void ControllerSettingsWindow::onNewProfileClicked()

if (!temp_si.Save())
{
QMessageBox::critical(
this, tr("Error"),
tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetPath())));
QMessageBox::critical(this, tr("Error"),
tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetPath())));
return;
}

Expand Down Expand Up @@ -238,14 +264,14 @@ void ControllerSettingsWindow::onApplyProfileClicked()
}
g_emu_thread->applySettings();

// make it visible
switchProfile({});
// Recreate global widget on profile apply
g_main_window->getControllerSettingsWindow()->createWidgets();
}

void ControllerSettingsWindow::onDeleteProfileClicked()
{
if (QMessageBox::question(this, tr("Delete Input Profile"),
tr("Are you sure you want to delete the input profile named '%1'?\n\n"
if (QMessageBox::question(this, tr("Delete Input Preset"),
tr("Are you sure you want to delete the input preset named '%1'?\n\n"
"You cannot undo this action.")
.arg(m_profile_name)) != QMessageBox::Yes)
{
Expand All @@ -266,11 +292,10 @@ void ControllerSettingsWindow::onDeleteProfileClicked()

void ControllerSettingsWindow::onRestoreDefaultsClicked()
{
if (QMessageBox::question(
this, tr("Restore Defaults"),
tr("Are you sure you want to restore the default controller configuration?\n\n"
"All shared bindings and configuration will be lost, but your input profiles will remain.\n\n"
"You cannot undo this action.")) != QMessageBox::Yes)
if (QMessageBox::question(this, tr("Restore Defaults"),
tr("Are you sure you want to restore the default controller configuration?\n\n"
"All bindings and configuration will be lost. You cannot undo this action.")) !=
QMessageBox::Yes)
{
return;
}
Expand All @@ -279,12 +304,12 @@ void ControllerSettingsWindow::onRestoreDefaultsClicked()
g_emu_thread->setDefaultSettings(false, true);

// reload all settings
switchProfile({});
createWidgets();
}

void ControllerSettingsWindow::onCopyGlobalSettingsClicked()
{
DebugAssert(isEditingGameSettings());
DebugAssert(!isEditingGlobalSettings());

{
const auto lock = Host::GetSettingsLock();
Expand All @@ -297,19 +322,8 @@ void ControllerSettingsWindow::onCopyGlobalSettingsClicked()
createWidgets();

QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"),
tr("Per-game controller configuration reset to global settings."));
}

void ControllerSettingsWindow::onRestoreDefaultsForGameClicked()
{
DebugAssert(isEditingGameSettings());
Settings::SetDefaultControllerConfig(*m_editing_settings_interface);
m_editing_settings_interface->Save();
g_emu_thread->reloadGameSettings();
createWidgets();

QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"),
tr("Per-game controller configuration reset to default settings."));
isEditingGameSettings() ? tr("Per-game controller configuration reset to global settings.") :
tr("Controller profile reset to global settings."));
}

bool ControllerSettingsWindow::getBoolValue(const char* section, const char* key, bool default_value) const
Expand Down Expand Up @@ -472,11 +486,12 @@ void ControllerSettingsWindow::createWidgets()
m_ui.settingsContainer->addWidget(m_hotkey_settings);
}

if (!isEditingGameSettings())
if (isEditingProfile())
{
m_ui.applyProfile->setEnabled(isEditingProfile());
m_ui.deleteProfile->setEnabled(isEditingProfile());
m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings());
const bool enable_buttons = static_cast<bool>(m_profile_settings_interface);
m_ui.applyProfile->setEnabled(enable_buttons);
m_ui.deleteProfile->setEnabled(enable_buttons);
m_ui.copyGlobalSettings->setEnabled(enable_buttons);
}
}

Expand Down Expand Up @@ -519,55 +534,64 @@ std::array<bool, 2> ControllerSettingsWindow::getEnabledMultitaps() const
return {{(mtap_mode == MultitapMode::Port1Only || mtap_mode == MultitapMode::BothPorts),
(mtap_mode == MultitapMode::Port2Only || mtap_mode == MultitapMode::BothPorts)}};
}

void ControllerSettingsWindow::refreshProfileList()
{
const std::vector<std::string> names(InputManager::GetInputProfileNames());

QSignalBlocker sb(m_ui.currentProfile);
m_ui.currentProfile->clear();
m_ui.currentProfile->addItem(tr("Shared"));
if (isEditingGlobalSettings())
m_ui.currentProfile->setCurrentIndex(0);

bool current_profile_found = false;
for (const std::string& name : names)
{
const QString qname(QString::fromStdString(name));
m_ui.currentProfile->addItem(qname);
if (qname == m_profile_name)
{
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->count() - 1);
current_profile_found = true;
}
}

if (!current_profile_found)
switchProfile(names.empty() ? std::string_view() : std::string_view(names.front()));
}

void ControllerSettingsWindow::switchProfile(const std::string_view name)
{
QSignalBlocker sb(m_ui.currentProfile);

if (!name.empty())
const QString name_qstr = QtUtils::StringViewToQString(name);
{
const QString name_qstr = QtUtils::StringViewToQString(name);

std::string path = System::GetInputProfilePath(name);
if (!FileSystem::FileExists(path.c_str()))
{
QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name_qstr));
return;
}
QSignalBlocker sb(m_ui.currentProfile);
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name_qstr));
}
m_profile_name = name_qstr;
m_profile_settings_interface.reset();
m_editing_settings_interface = nullptr;

std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(path));
sif->Load();
// disable UI if there is no selection
const bool disable_ui = name.empty();
m_ui.settingsCategory->setDisabled(disable_ui);
m_ui.settingsContainer->setDisabled(disable_ui);

m_profile_settings_interface = std::move(sif);
m_editing_settings_interface = m_profile_settings_interface.get();
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name_qstr));
m_profile_name = name_qstr;
if (name_qstr.isEmpty())
{
createWidgets();
return;
}
else

std::string path = System::GetInputProfilePath(name);
if (!FileSystem::FileExists(path.c_str()))
{
m_profile_settings_interface.reset();
m_editing_settings_interface = nullptr;
m_ui.currentProfile->setCurrentIndex(0);
m_profile_name = QString();
QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name_qstr));
return;
}

std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(path));
sif->Load();

m_profile_settings_interface = std::move(sif);
m_editing_settings_interface = m_profile_settings_interface.get();

createWidgets();
}
14 changes: 8 additions & 6 deletions src/duckstation-qt/controllersettingswindow.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0

#pragma once
Expand All @@ -14,6 +14,7 @@
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtWidgets/QDialog>
#include <QtCore/QAbstractListModel>

#include <array>
#include <string>
Expand Down Expand Up @@ -41,7 +42,8 @@ class ControllerSettingsWindow final : public QWidget
Count
};

ControllerSettingsWindow(SettingsInterface* game_sif = nullptr, QWidget* parent = nullptr);
ControllerSettingsWindow(SettingsInterface* game_sif = nullptr, bool edit_profiles = false,
QWidget* parent = nullptr);
~ControllerSettingsWindow();

static void editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif);
Expand All @@ -50,13 +52,13 @@ class ControllerSettingsWindow final : public QWidget

ALWAYS_INLINE bool isEditingGlobalSettings() const
{
return (m_profile_name.isEmpty() && !m_editing_settings_interface);
return (!m_editing_input_profiles && !m_editing_settings_interface);
}
ALWAYS_INLINE bool isEditingGameSettings() const
{
return (m_profile_name.isEmpty() && m_editing_settings_interface);
return (!m_editing_input_profiles && m_editing_settings_interface);
}
ALWAYS_INLINE bool isEditingProfile() const { return !m_profile_name.isEmpty(); }
ALWAYS_INLINE bool isEditingProfile() const { return m_editing_input_profiles; }
ALWAYS_INLINE SettingsInterface* getEditingSettingsInterface() { return m_editing_settings_interface; }

Category getCurrentCategory() const;
Expand Down Expand Up @@ -90,7 +92,6 @@ private Q_SLOTS:
void onDeleteProfileClicked();
void onRestoreDefaultsClicked();
void onCopyGlobalSettingsClicked();
void onRestoreDefaultsForGameClicked();

void createWidgets();

Expand All @@ -113,4 +114,5 @@ private Q_SLOTS:

QString m_profile_name;
std::unique_ptr<SettingsInterface> m_profile_settings_interface;
bool m_editing_input_profiles = false;
};
12 changes: 1 addition & 11 deletions src/duckstation-qt/controllersettingswindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,12 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreDefaults">
<property name="text">
<string>Restore Defaults</string>
</property>
<property name="icon">
<iconset theme="restart-line"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Close</set>
<set>QDialogButtonBox::StandardButton::Close|QDialogButtonBox::StandardButton::RestoreDefaults</set>
</property>
</widget>
</item>
Expand Down
Loading

0 comments on commit e4c11aa

Please sign in to comment.