diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index a371fb16933..ba2e5522a42 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -68,6 +68,10 @@ add_executable(dolphin-emu Config/ConfigControls/ConfigFloatSlider.h Config/ConfigControls/ConfigSlider.cpp Config/ConfigControls/ConfigSlider.h + Config/ConfigControls/ConfigText.cpp + Config/ConfigControls/ConfigText.h + Config/ConfigControls/ConfigUserPath.cpp + Config/ConfigControls/ConfigUserPath.h Config/ControllerInterface/ControllerInterfaceWindow.cpp Config/ControllerInterface/ControllerInterfaceWindow.h Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.cpp new file mode 100644 index 00000000000..41e4d2166ed --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.cpp @@ -0,0 +1,39 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "DolphinQt/Config/ConfigControls/ConfigText.h" + +#include + +ConfigText::ConfigText(const Config::Info& setting) : ConfigText(setting, nullptr) +{ +} + +ConfigText::ConfigText(const Config::Info& setting, Config::Layer* layer) + : ConfigControl(setting.GetLocation(), layer), m_setting(setting) +{ + setText(QString::fromStdString(ReadValue(setting))); + + connect(this, &QLineEdit::editingFinished, this, &ConfigText::Update); +} + +void ConfigText::SetTextAndUpdate(const QString& text) +{ + if (text == this->text()) + return; + + setText(text); + Update(); +} + +void ConfigText::Update() +{ + const std::string value = text().toStdString(); + + SaveValue(m_setting, value); +} + +void ConfigText::OnConfigChanged() +{ + setText(QString::fromStdString(ReadValue(m_setting))); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.h new file mode 100644 index 00000000000..51cd8b2179c --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigText.h @@ -0,0 +1,28 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" + +#include "Common/Config/ConfigInfo.h" + +class ConfigText : public ConfigControl +{ + Q_OBJECT +public: + ConfigText(const Config::Info& setting); + ConfigText(const Config::Info& setting, Config::Layer* layer); + + void SetTextAndUpdate(const QString& text); + +protected: + void OnConfigChanged() override; + + const Config::Info m_setting; + +private: + void Update(); +}; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.cpp new file mode 100644 index 00000000000..1b6c03f915d --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.cpp @@ -0,0 +1,59 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "DolphinQt/Config/ConfigControls/ConfigUserPath.h" + +#include +#include + +#include "Common/FileUtil.h" + +#include "DolphinQt/Config/ConfigControls/ConfigText.h" +#include "DolphinQt/QtUtils/ModalMessageBox.h" + +ConfigUserPath::ConfigUserPath(const unsigned int dir_index, + const Config::Info& setting) + : ConfigUserPath(dir_index, setting, nullptr) +{ +} + +ConfigUserPath::ConfigUserPath(const unsigned int dir_index, + const Config::Info& setting, Config::Layer* layer) + : ConfigText(setting, layer), m_dir_index(dir_index) +{ + OnConfigChanged(); + + connect(this, &QLineEdit::editingFinished, this, &ConfigUserPath::Update); +} + +// Display the effective path: if Config has a value, use it; otherwise fall back to UserPath. +// Config values only serve to initialize UserPath at startup, an empty config meaning "use the +// current UserPath". +void ConfigUserPath::RefreshText() +{ + const std::string config_value = ReadValue(m_setting); + const std::string userpath_value = File::GetUserPath(m_dir_index); + const QString text = QString::fromStdString(config_value.empty() ? userpath_value : config_value); + setText(text); +} + +void ConfigUserPath::Update() +{ + const QString new_text = text().trimmed(); + if (new_text.isEmpty()) + { + ModalMessageBox::warning(this, tr("Empty Value"), + tr("This field cannot be left empty. Please enter a value.")); + RefreshText(); + return; + } + + const std::string value = new_text.toStdString(); + File::SetUserPath(m_dir_index, value); + SaveValue(m_setting, value); +} + +void ConfigUserPath::OnConfigChanged() +{ + RefreshText(); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.h new file mode 100644 index 00000000000..40fea637092 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigUserPath.h @@ -0,0 +1,28 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "Common/Config/ConfigInfo.h" + +#include "DolphinQt/Config/ConfigControls/ConfigText.h" + +class ConfigUserPath final : public ConfigText +{ + Q_OBJECT +public: + ConfigUserPath(const unsigned int dir_index, const Config::Info& setting); + ConfigUserPath(const unsigned int dir_index, const Config::Info& setting, + Config::Layer* layer); + +protected: + void OnConfigChanged() override; + +private: + void RefreshText(); + void Update(); + + const unsigned int m_dir_index; +}; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 2084db0e32f..fe5a2dda4a7 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -64,6 +64,8 @@ + + @@ -295,6 +297,8 @@ + + diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index 1cd5dc5a2fc..c84475600e4 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -34,6 +34,10 @@ #include "Core/NetPlayServer.h" #include "Core/System.h" +#include "DolphinQt/Config/ConfigControls/ConfigBool.h" +#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" +#include "DolphinQt/Config/ConfigControls/ConfigText.h" +#include "DolphinQt/Config/ConfigControls/ConfigUserPath.h" #include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/GCMemcardManager.h" #include "DolphinQt/QtUtils/DolphinFileDialog.h" @@ -64,7 +68,7 @@ void GameCubePane::CreateWidgets() QVBoxLayout* ipl_box_layout = new QVBoxLayout(ipl_box); ipl_box->setLayout(ipl_box_layout); - m_skip_main_menu = new QCheckBox(tr("Skip Main Menu"), ipl_box); + m_skip_main_menu = new ConfigBool(tr("Skip Main Menu"), Config::MAIN_SKIP_IPL); ipl_box_layout->addWidget(m_skip_main_menu); QFormLayout* ipl_language_layout = new QFormLayout; @@ -72,17 +76,11 @@ void GameCubePane::CreateWidgets() ipl_language_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); ipl_box_layout->addLayout(ipl_language_layout); - m_language_combo = new QComboBox(ipl_box); - m_language_combo->setCurrentIndex(-1); - ipl_language_layout->addRow(tr("System Language:"), m_language_combo); + const QStringList language_list{tr("English"), tr("German"), tr("French"), + tr("Spanish"), tr("Italian"), tr("Dutch")}; - // Add languages - for (const auto& entry : {std::make_pair(tr("English"), 0), std::make_pair(tr("German"), 1), - std::make_pair(tr("French"), 2), std::make_pair(tr("Spanish"), 3), - std::make_pair(tr("Italian"), 4), std::make_pair(tr("Dutch"), 5)}) - { - m_language_combo->addItem(entry.first, entry.second); - } + m_language_combo = new ConfigChoice(language_list, Config::MAIN_GC_LANGUAGE); + ipl_language_layout->addRow(tr("System Language:"), m_language_combo); // Device Settings QGroupBox* device_box = new QGroupBox(tr("Device Settings"), this); @@ -194,11 +192,12 @@ void GameCubePane::CreateWidgets() gba_box->setLayout(gba_layout); int gba_row = 0; - m_gba_threads = new QCheckBox(tr("Run GBA Cores in Dedicated Threads")); + m_gba_threads = + new ConfigBool(tr("Run GBA Cores in Dedicated Threads"), Config::MAIN_GBA_THREADS); gba_layout->addWidget(m_gba_threads, gba_row, 0, 1, -1); gba_row++; - m_gba_bios_edit = new QLineEdit(); + m_gba_bios_edit = new ConfigUserPath(F_GBABIOS_IDX, Config::MAIN_GBA_BIOS_PATH); m_gba_browse_bios = new NonDefaultQPushButton(QStringLiteral("...")); gba_layout->addWidget(new QLabel(tr("BIOS:")), gba_row, 0); gba_layout->addWidget(m_gba_bios_edit, gba_row, 1); @@ -207,7 +206,7 @@ void GameCubePane::CreateWidgets() for (size_t i = 0; i < m_gba_rom_edits.size(); ++i) { - m_gba_rom_edits[i] = new QLineEdit(); + m_gba_rom_edits[i] = new ConfigText(Config::MAIN_GBA_ROM_PATHS[i]); m_gba_browse_roms[i] = new NonDefaultQPushButton(QStringLiteral("...")); gba_layout->addWidget(new QLabel(tr("Port %1 ROM:").arg(i + 1)), gba_row, 0); gba_layout->addWidget(m_gba_rom_edits[i], gba_row, 1); @@ -215,11 +214,12 @@ void GameCubePane::CreateWidgets() gba_row++; } - m_gba_save_rom_path = new QCheckBox(tr("Save in Same Directory as the ROM")); + m_gba_save_rom_path = + new ConfigBool(tr("Save in Same Directory as the ROM"), Config::MAIN_GBA_SAVES_IN_ROM_PATH); gba_layout->addWidget(m_gba_save_rom_path, gba_row, 0, 1, -1); gba_row++; - m_gba_saves_edit = new QLineEdit(); + m_gba_saves_edit = new ConfigUserPath(D_GBASAVES_IDX, Config::MAIN_GBA_SAVES_PATH); m_gba_browse_saves = new NonDefaultQPushButton(QStringLiteral("...")); gba_layout->addWidget(new QLabel(tr("Saves:")), gba_row, 0); gba_layout->addWidget(m_gba_saves_edit, gba_row, 1); @@ -240,14 +240,6 @@ void GameCubePane::CreateWidgets() void GameCubePane::ConnectWidgets() { - // IPL Settings -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - connect(m_skip_main_menu, &QCheckBox::checkStateChanged, this, &GameCubePane::SaveSettings); -#else - connect(m_skip_main_menu, &QCheckBox::stateChanged, this, &GameCubePane::SaveSettings); -#endif - connect(m_language_combo, &QComboBox::currentIndexChanged, this, &GameCubePane::SaveSettings); - // Device Settings for (ExpansionInterface::Slot slot : GUI_SLOTS) { @@ -276,13 +268,6 @@ void GameCubePane::ConnectWidgets() #ifdef HAS_LIBMGBA // GBA Settings - -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - connect(m_gba_threads, &QCheckBox::checkStateChanged, this, &GameCubePane::SaveSettings); -#else - connect(m_gba_threads, &QCheckBox::stateChanged, this, &GameCubePane::SaveSettings); -#endif - connect(m_gba_bios_edit, &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings); connect(m_gba_browse_bios, &QPushButton::clicked, this, &GameCubePane::BrowseGBABios); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) connect(m_gba_save_rom_path, &QCheckBox::checkStateChanged, this, @@ -290,11 +275,9 @@ void GameCubePane::ConnectWidgets() #else connect(m_gba_save_rom_path, &QCheckBox::stateChanged, this, &GameCubePane::SaveRomPathChanged); #endif - connect(m_gba_saves_edit, &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings); connect(m_gba_browse_saves, &QPushButton::clicked, this, &GameCubePane::BrowseGBASaves); for (size_t i = 0; i < m_gba_browse_roms.size(); ++i) { - connect(m_gba_rom_edits[i], &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings); connect(m_gba_browse_roms[i], &QPushButton::clicked, this, [this, i] { BrowseGBARom(i); }); } #endif @@ -682,37 +665,30 @@ void GameCubePane::SetAGPRom(ExpansionInterface::Slot slot, const QString& filen void GameCubePane::BrowseGBABios() { QString file = QDir::toNativeSeparators(DolphinFileDialog::getOpenFileName( - this, tr("Select GBA BIOS"), QString::fromStdString(File::GetUserPath(F_GBABIOS_IDX)), + this, tr("Select GBA BIOS"), QString::fromStdString(Config::Get(Config::MAIN_GBA_BIOS_PATH)), tr("All Files (*)"))); if (!file.isEmpty()) - { - m_gba_bios_edit->setText(file); - SaveSettings(); - } + m_gba_bios_edit->SetTextAndUpdate(file); } void GameCubePane::BrowseGBARom(size_t index) { QString file = QString::fromStdString(GetOpenGBARom({})); if (!file.isEmpty()) - { - m_gba_rom_edits[index]->setText(file); - SaveSettings(); - } + m_gba_rom_edits[index]->SetTextAndUpdate(file); } void GameCubePane::SaveRomPathChanged() { m_gba_saves_edit->setEnabled(!m_gba_save_rom_path->isChecked()); m_gba_browse_saves->setEnabled(!m_gba_save_rom_path->isChecked()); - SaveSettings(); } void GameCubePane::BrowseGBASaves() { QString dir = QDir::toNativeSeparators(DolphinFileDialog::getExistingDirectory( this, tr("Select GBA Saves Path"), - QString::fromStdString(File::GetUserPath(D_GBASAVES_IDX)))); + QString::fromStdString(Config::Get(Config::MAIN_GBA_SAVES_PATH)))); if (!dir.isEmpty()) { m_gba_saves_edit->setText(dir); @@ -722,11 +698,6 @@ void GameCubePane::BrowseGBASaves() void GameCubePane::LoadSettings() { - // IPL Settings - SignalBlocking(m_skip_main_menu)->setChecked(Config::Get(Config::MAIN_SKIP_IPL)); - SignalBlocking(m_language_combo) - ->setCurrentIndex(m_language_combo->findData(Config::Get(Config::MAIN_GC_LANGUAGE))); - bool have_menu = false; for (const std::string dir : {USA_DIR, JAP_DIR, EUR_DIR}) @@ -741,7 +712,7 @@ void GameCubePane::LoadSettings() } m_skip_main_menu->setEnabled(have_menu || !m_skip_main_menu->isChecked()); - m_skip_main_menu->setToolTip(have_menu ? QString{} : tr("Put IPL ROMs in User/GC/.")); + m_skip_main_menu->SetDescription(have_menu ? QString{} : tr("Put IPL ROMs in User/GC/.")); // Device Settings for (ExpansionInterface::Slot slot : GUI_SLOTS) @@ -762,31 +733,12 @@ void GameCubePane::LoadSettings() SignalBlocking(m_gci_paths[slot]) ->setText(QString::fromStdString(Config::GetGCIFolderPath(slot, std::nullopt))); } - -#ifdef HAS_LIBMGBA - // GBA Settings - SignalBlocking(m_gba_threads)->setChecked(Config::Get(Config::MAIN_GBA_THREADS)); - SignalBlocking(m_gba_bios_edit) - ->setText(QString::fromStdString(File::GetUserPath(F_GBABIOS_IDX))); - SignalBlocking(m_gba_save_rom_path)->setChecked(Config::Get(Config::MAIN_GBA_SAVES_IN_ROM_PATH)); - SignalBlocking(m_gba_saves_edit) - ->setText(QString::fromStdString(File::GetUserPath(D_GBASAVES_IDX))); - for (size_t i = 0; i < m_gba_rom_edits.size(); ++i) - { - SignalBlocking(m_gba_rom_edits[i]) - ->setText(QString::fromStdString(Config::Get(Config::MAIN_GBA_ROM_PATHS[i]))); - } -#endif } void GameCubePane::SaveSettings() { Config::ConfigChangeCallbackGuard config_guard; - // IPL Settings - Config::SetBaseOrCurrent(Config::MAIN_SKIP_IPL, m_skip_main_menu->isChecked()); - Config::SetBaseOrCurrent(Config::MAIN_GC_LANGUAGE, m_language_combo->currentData().toInt()); - auto& system = Core::System::GetInstance(); // Device Settings for (ExpansionInterface::Slot slot : GUI_SLOTS) @@ -808,18 +760,6 @@ void GameCubePane::SaveSettings() // GBA Settings if (!NetPlay::IsNetPlayRunning()) { - Config::SetBaseOrCurrent(Config::MAIN_GBA_THREADS, m_gba_threads->isChecked()); - Config::SetBaseOrCurrent(Config::MAIN_GBA_BIOS_PATH, m_gba_bios_edit->text().toStdString()); - Config::SetBaseOrCurrent(Config::MAIN_GBA_SAVES_IN_ROM_PATH, m_gba_save_rom_path->isChecked()); - Config::SetBaseOrCurrent(Config::MAIN_GBA_SAVES_PATH, m_gba_saves_edit->text().toStdString()); - File::SetUserPath(F_GBABIOS_IDX, Config::Get(Config::MAIN_GBA_BIOS_PATH)); - File::SetUserPath(D_GBASAVES_IDX, Config::Get(Config::MAIN_GBA_SAVES_PATH)); - for (size_t i = 0; i < m_gba_rom_edits.size(); ++i) - { - Config::SetBaseOrCurrent(Config::MAIN_GBA_ROM_PATHS[i], - m_gba_rom_edits[i]->text().toStdString()); - } - auto server = Settings::Instance().GetNetPlayServer(); if (server) server->SetGBAConfig(server->GetGBAConfig(), true); diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.h b/Source/Core/DolphinQt/Settings/GameCubePane.h index 05186f81aaf..010da7325d9 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.h +++ b/Source/Core/DolphinQt/Settings/GameCubePane.h @@ -12,7 +12,10 @@ #include "Common/EnumMap.h" #include "Core/HW/EXI/EXI.h" -class QCheckBox; +class ConfigBool; +class ConfigChoice; +class ConfigText; +class ConfigUserPath; class QComboBox; class QHBoxLayout; class QLabel; @@ -52,8 +55,8 @@ private: void SaveRomPathChanged(); void BrowseGBASaves(); - QCheckBox* m_skip_main_menu; - QComboBox* m_language_combo; + ConfigBool* m_skip_main_menu; + ConfigChoice* m_language_combo; Common::EnumMap m_slot_buttons; Common::EnumMap m_slot_combos; @@ -71,12 +74,12 @@ private: Common::EnumMap m_gci_override_labels; Common::EnumMap m_gci_paths; - QCheckBox* m_gba_threads; - QCheckBox* m_gba_save_rom_path; + ConfigBool* m_gba_threads; + ConfigBool* m_gba_save_rom_path; QPushButton* m_gba_browse_bios; - QLineEdit* m_gba_bios_edit; + ConfigUserPath* m_gba_bios_edit; std::array m_gba_browse_roms; - std::array m_gba_rom_edits; + std::array m_gba_rom_edits; QPushButton* m_gba_browse_saves; - QLineEdit* m_gba_saves_edit; + ConfigUserPath* m_gba_saves_edit; }; diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 40f31a231da..19de3dc612b 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -114,8 +114,6 @@ static void InitCustomPaths() Config::Get(Config::MAIN_WII_SD_CARD_SYNC_FOLDER_PATH)); File::CreateFullPath(File::GetUserPath(D_WIISDCARDSYNCFOLDER_IDX)); #ifdef HAS_LIBMGBA - File::SetUserPath(F_GBABIOS_IDX, Config::Get(Config::MAIN_GBA_BIOS_PATH)); - File::SetUserPath(D_GBASAVES_IDX, Config::Get(Config::MAIN_GBA_SAVES_PATH)); File::CreateFullPath(File::GetUserPath(D_GBASAVES_IDX)); #endif }