From 06286349101fd999d83f9e238fe7e622db163780 Mon Sep 17 00:00:00 2001 From: spycrab Date: Fri, 13 Jul 2018 12:56:58 +0200 Subject: [PATCH] Qt/GameConfigWidget: Complete overhaul --- Source/Core/DolphinQt/CMakeLists.txt | 2 + .../Core/DolphinQt/Config/GameConfigEdit.cpp | 295 ++++++++++++++ Source/Core/DolphinQt/Config/GameConfigEdit.h | 50 +++ .../Config/GameConfigHighlighter.cpp | 63 +++ .../DolphinQt/Config/GameConfigHighlighter.h | 28 ++ .../DolphinQt/Config/GameConfigWidget.cpp | 371 ++---------------- .../Core/DolphinQt/Config/GameConfigWidget.h | 40 +- Source/Core/DolphinQt/DolphinQt.vcxproj | 6 + 8 files changed, 491 insertions(+), 364 deletions(-) create mode 100644 Source/Core/DolphinQt/Config/GameConfigEdit.cpp create mode 100644 Source/Core/DolphinQt/Config/GameConfigEdit.h create mode 100644 Source/Core/DolphinQt/Config/GameConfigHighlighter.cpp create mode 100644 Source/Core/DolphinQt/Config/GameConfigHighlighter.h diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 46f0880092a6..06212aee5ce9 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -28,6 +28,8 @@ add_executable(dolphin-emu Config/CheatWarningWidget.cpp Config/ControllersWindow.cpp Config/FilesystemWidget.cpp + Config/GameConfigEdit.cpp + Config/GameConfigHighlighter.cpp Config/GameConfigWidget.cpp Config/GeckoCodeWidget.cpp Config/Graphics/AdvancedWidget.cpp diff --git a/Source/Core/DolphinQt/Config/GameConfigEdit.cpp b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp new file mode 100644 index 000000000000..17647d5d9f97 --- /dev/null +++ b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp @@ -0,0 +1,295 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/GameConfigEdit.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DolphinQt/Config/GameConfigHighlighter.h" + +GameConfigEdit::GameConfigEdit(QWidget* parent, const QString& path) : m_path(path) +{ + LoadFile(); + + setAcceptRichText(false); + setContextMenuPolicy(Qt::CustomContextMenu); + + new GameConfigHighlighter(document()); + + AddDescription(QStringLiteral("Core"), + tr("Section that contains most CPU and Hardware related settings.")); + + AddDescription(QStringLiteral("CPUThread"), tr("Controls whether or not Dual Core should be " + "enabled. Can improve performance but can also " + "cause issues. Defaults to True")); + + AddDescription(QStringLiteral("FastDiscSpeed"), + tr("Shortens loading times but may break some games. Can have negative effects on " + "performance. Defaults to False")); + + AddDescription(QStringLiteral("MMU"), tr("Controls whether or not the Memory Management Unit " + "should be emulated fully. Few games require it.")); + + AddDescription( + QStringLiteral("DSPHLE"), + tr("Controls whether to use high or low-level DSP emulation. Defaults to True")); + + AddDescription( + QStringLiteral("JITFollowBranch"), + tr("Tries to translate branches ahead of time, improving performance in most cases. Defaults " + "to True")); + + AddDescription(QStringLiteral("Gecko"), tr("Section that contains all Gecko cheat codes.")); + + AddDescription(QStringLiteral("ActionReplay"), + tr("Section that contains all Action Replay cheat codes.")); + + AddDescription(QStringLiteral("Video_Settings"), + tr("Section that contains all graphics related settings.")); + + m_completer = new QCompleter(this); + + auto* completion_model = new QStringListModel; + completion_model->setStringList(m_completions); + + m_completer->setModel(completion_model); + m_completer->setModelSorting(QCompleter::UnsortedModel); + m_completer->setCompletionMode(QCompleter::PopupCompletion); + m_completer->setWidget(this); + + Connect(); +} + +void GameConfigEdit::AddDescription(const QString& keyword, const QString& description) +{ + m_keyword_map[keyword] = description; + m_completions << keyword; +} + +void GameConfigEdit::LoadFile() +{ + QFile file(m_path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + setPlainText(QString::fromStdString(file.readAll().toStdString())); +} + +void GameConfigEdit::SaveFile() +{ + if (!isVisible() || isReadOnly()) + return; + + QFile file(m_path); + + const QByteArray contents = toPlainText().toUtf8(); + + if (contents.isEmpty()) + { + if (file.exists()) + file.remove(); + return; + } + + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return; + + if (!file.write(contents)) + QMessageBox::warning(this, tr("Warning"), tr("Failed to write config file!")); +} + +void GameConfigEdit::Connect() +{ + connect(this, &QTextEdit::textChanged, this, &GameConfigEdit::SaveFile); + connect(this, &QTextEdit::selectionChanged, this, &GameConfigEdit::OnSelectionChanged); + connect(this, &QTextEdit::customContextMenuRequested, this, &GameConfigEdit::OnContextMenu); + connect(m_completer, static_cast(&QCompleter::activated), + this, &GameConfigEdit::OnAutoComplete); +} + +void GameConfigEdit::OnSelectionChanged() +{ + const QString& keyword = textCursor().selectedText(); + + if (m_keyword_map.count(keyword)) + QWhatsThis::showText(QCursor::pos(), m_keyword_map[keyword], this); +} + +void GameConfigEdit::AddBoolOption(QMenu* menu, const QString& name, const QString& section, + const QString& key) +{ + auto* option = menu->addMenu(name); + + option->addAction(tr("On"), this, + [this, section, key] { SetOption(section, key, QStringLiteral("True")); }); + option->addAction(tr("Off"), this, + [this, section, key] { SetOption(section, key, QStringLiteral("False")); }); +} + +void GameConfigEdit::SetOption(const QString& section, const QString& key, const QString& value) +{ + auto section_cursor = document()->find(QRegExp(QStringLiteral("^\\[%1\\]").arg(section)), 0); + + // Check if the section this belongs in can be found + if (section_cursor.isNull()) + { + append(QStringLiteral("[%1]\n\n%2 = %3\n").arg(section).arg(key).arg(value)); + } + else + { + auto value_cursor = + document()->find(QRegExp(QStringLiteral("^%1 = .*").arg(key)), section_cursor); + + const QString new_line = QStringLiteral("%1 = %2").arg(key).arg(value); + + // Check if the value that has to be set already exists + if (value_cursor.isNull()) + { + section_cursor.clearSelection(); + section_cursor.insertText(QStringLiteral("\n") + new_line); + } + else + { + value_cursor.insertText(new_line); + } + } +} + +QString GameConfigEdit::GetTextUnderCursor() +{ + QTextCursor tc = textCursor(); + tc.select(QTextCursor::WordUnderCursor); + return tc.selectedText(); +} + +void GameConfigEdit::OnContextMenu(const QPoint& pos) +{ + auto* menu = createStandardContextMenu(pos); + + menu->addSeparator(); + + menu->addAction(tr("Refresh"), this, &GameConfigEdit::LoadFile); + menu->addAction(tr("Open in External Editor"), this, &GameConfigEdit::OpenExternalEditor); + + if (!isReadOnly()) + { + menu->addSeparator(); + auto* core_menu = menu->addMenu(tr("Core")); + + AddBoolOption(core_menu, tr("Dual Core"), QStringLiteral("Core"), QStringLiteral("CPUThread")); + AddBoolOption(core_menu, tr("MMU"), QStringLiteral("Core"), QStringLiteral("MMU")); + + auto* video_menu = menu->addMenu(tr("Video")); + + AddBoolOption(video_menu, tr("Store EFB Copies to Texture Only"), + QStringLiteral("Video_Settings"), QStringLiteral("EFBToTextureEnable")); + + AddBoolOption(video_menu, tr("Store XFB Copies to Texture Only"), + QStringLiteral("Video_Settings"), QStringLiteral("XFBToTextureEnable")); + + { + auto* texture_cache = video_menu->addMenu(tr("Texture Cache")); + texture_cache->addAction(tr("Safe"), this, [this] { + SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), + QStringLiteral("0")); + }); + texture_cache->addAction(tr("Medium"), this, [this] { + SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), + QStringLiteral("512")); + }); + texture_cache->addAction(tr("Fast"), this, [this] { + SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), + QStringLiteral("128")); + }); + } + } + + menu->exec(QCursor::pos()); +} + +void GameConfigEdit::OnAutoComplete(const QString& completion) +{ + QTextCursor cursor = textCursor(); + int extra = completion.length() - m_completer->completionPrefix().length(); + cursor.movePosition(QTextCursor::Left); + cursor.movePosition(QTextCursor::EndOfWord); + cursor.insertText(completion.right(extra)); + setTextCursor(cursor); +} + +void GameConfigEdit::OpenExternalEditor() +{ + QFile file(m_path); + + if (!file.exists()) + { + if (isReadOnly()) + return; + + file.open(QIODevice::WriteOnly); + file.close(); + } + + QDesktopServices::openUrl(QUrl::fromLocalFile(m_path)); +} + +void GameConfigEdit::keyPressEvent(QKeyEvent* e) +{ + if (m_completer->popup()->isVisible()) + { + // The following keys are forwarded by the completer to the widget + switch (e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Escape: + case Qt::Key_Tab: + case Qt::Key_Backtab: + e->ignore(); + return; // let the completer do default behavior + default: + break; + } + } + + QTextEdit::keyPressEvent(e); + + const static QString end_of_word = QStringLiteral("~!@#$%^&*()_+{}|:\"<>?,./;'\\-="); + + QString completion_prefix = GetTextUnderCursor(); + + if (e->text().isEmpty() || completion_prefix.length() < 2 || + end_of_word.contains(e->text().right(1))) + { + m_completer->popup()->hide(); + return; + } + + if (completion_prefix != m_completer->completionPrefix()) + { + m_completer->setCompletionPrefix(completion_prefix); + m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0)); + } + QRect cr = cursorRect(); + cr.setWidth(m_completer->popup()->sizeHintForColumn(0) + + m_completer->popup()->verticalScrollBar()->sizeHint().width()); + m_completer->complete(cr); // popup it up! +} + +void GameConfigEdit::focusInEvent(QFocusEvent* e) +{ + m_completer->setWidget(this); + QTextEdit::focusInEvent(e); +} diff --git a/Source/Core/DolphinQt/Config/GameConfigEdit.h b/Source/Core/DolphinQt/Config/GameConfigEdit.h new file mode 100644 index 000000000000..7701edb7ab69 --- /dev/null +++ b/Source/Core/DolphinQt/Config/GameConfigEdit.h @@ -0,0 +1,50 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +class QCompleter; + +class GameConfigEdit : public QTextEdit +{ +public: + explicit GameConfigEdit(QWidget* parent, const QString& path); + +protected: + void keyPressEvent(QKeyEvent* e) override; + void focusInEvent(QFocusEvent* e) override; + +private: + void Connect(); + + void LoadFile(); + void SaveFile(); + + void OnSelectionChanged(); + void OnContextMenu(const QPoint& pos); + void OnAutoComplete(const QString& completion); + void OpenExternalEditor(); + + void SetReadOnly(bool read_only); + + QString GetTextUnderCursor(); + + void AddBoolOption(QMenu* menu, const QString& name, const QString& section, const QString& key); + + void SetOption(const QString& section, const QString& key, const QString& value); + + void AddDescription(const QString& keyword, const QString& description); + + QCompleter* m_completer; + QStringList m_completions; + + const QString m_path; + + QMap m_keyword_map; +}; diff --git a/Source/Core/DolphinQt/Config/GameConfigHighlighter.cpp b/Source/Core/DolphinQt/Config/GameConfigHighlighter.cpp new file mode 100644 index 000000000000..d9d2bb55bdf0 --- /dev/null +++ b/Source/Core/DolphinQt/Config/GameConfigHighlighter.cpp @@ -0,0 +1,63 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/GameConfigHighlighter.h" + +struct HighlightingRule +{ + QRegularExpression pattern; + QTextCharFormat format; +}; + +GameConfigHighlighter::~GameConfigHighlighter() = default; + +GameConfigHighlighter::GameConfigHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) +{ + QTextCharFormat equal_format; + equal_format.setForeground(Qt::red); + + QTextCharFormat section_format; + section_format.setFontWeight(QFont::Bold); + + QTextCharFormat comment_format; + comment_format.setForeground(Qt::darkGreen); + comment_format.setFontItalic(true); + + QTextCharFormat const_format; + const_format.setFontWeight(QFont::Bold); + const_format.setForeground(Qt::blue); + + QTextCharFormat num_format; + num_format.setForeground(Qt::darkBlue); + + m_rules.emplace_back(HighlightingRule{QRegularExpression(QStringLiteral("=")), equal_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("^\\[.*?\\]")), section_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("\\bTrue\\b")), const_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("\\bFalse\\b")), const_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("\\b[0-9a-fA-F]+\\b")), num_format}); + + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("^#.*")), comment_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("^\\$.*")), comment_format}); + m_rules.emplace_back( + HighlightingRule{QRegularExpression(QStringLiteral("^\\*.*")), comment_format}); +} + +void GameConfigHighlighter::highlightBlock(const QString& text) +{ + for (const auto& rule : m_rules) + { + auto it = rule.pattern.globalMatch(text); + while (it.hasNext()) + { + auto match = it.next(); + setFormat(match.capturedStart(), match.capturedLength(), rule.format); + } + } +} diff --git a/Source/Core/DolphinQt/Config/GameConfigHighlighter.h b/Source/Core/DolphinQt/Config/GameConfigHighlighter.h new file mode 100644 index 000000000000..4b20291e932c --- /dev/null +++ b/Source/Core/DolphinQt/Config/GameConfigHighlighter.h @@ -0,0 +1,28 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include +#include +#include + +struct HighlightingRule; + +class GameConfigHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + explicit GameConfigHighlighter(QTextDocument* parent = nullptr); + ~GameConfigHighlighter(); + +protected: + void highlightBlock(const QString& text) override; + +private: + std::vector m_rules; +}; diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp index c626515ce0b4..928e51c4ea18 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp @@ -4,18 +4,8 @@ #include "DolphinQt/Config/GameConfigWidget.h" -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include #include #include "Common/CommonPaths.h" @@ -24,352 +14,77 @@ #include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigManager.h" +#include "DolphinQt/Config/GameConfigEdit.h" #include "DolphinQt/Config/Graphics/GraphicsSlider.h" #include "UICommon/GameFile.h" -constexpr int DETERMINISM_NOT_SET_INDEX = 0; -constexpr int DETERMINISM_AUTO_INDEX = 1; -constexpr int DETERMINISM_NONE_INDEX = 2; -constexpr int DETERMINISM_FAKE_COMPLETION_INDEX = 3; - -constexpr const char* DETERMINISM_NOT_SET_STRING = ""; -constexpr const char* DETERMINISM_AUTO_STRING = "auto"; -constexpr const char* DETERMINISM_NONE_STRING = "none"; -constexpr const char* DETERMINISM_FAKE_COMPLETION_STRING = "fake-completion"; - -GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game) +static void PopulateTab(QTabWidget* tab, const std::string& path, const std::string& game_id, + u16 revision, bool read_only) { - m_game_id = m_game.GetGameID(); - m_gameini_local_path = - QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"); - - CreateWidgets(); - LoadSettings(); - ConnectWidgets(); -} - -void GameConfigWidget::CreateWidgets() -{ - m_refresh_config = new QPushButton(tr("Refresh")); - m_edit_user_config = new QPushButton(tr("Edit User Config")); - m_view_default_config = new QPushButton(tr("View Default Config")); - - // Core - auto* core_box = new QGroupBox(tr("Core")); - auto* core_layout = new QGridLayout; - core_box->setLayout(core_layout); - - m_enable_dual_core = new QCheckBox(tr("Enable Dual Core")); - m_enable_mmu = new QCheckBox(tr("Enable MMU")); - m_enable_fprf = new QCheckBox(tr("Enable FPRF")); - m_sync_gpu = new QCheckBox(tr("Synchronize GPU thread")); - m_enable_fast_disc = new QCheckBox(tr("Speed up Disc Transfer Rate")); - m_use_dsp_hle = new QCheckBox(tr("DSP HLE Emulation (fast)")); - m_deterministic_dual_core = new QComboBox; - - for (const auto& item : {tr("Not Set"), tr("auto"), tr("none"), tr("fake-completion")}) - m_deterministic_dual_core->addItem(item); - - m_enable_mmu->setToolTip(tr( - "Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)")); - - m_enable_fprf->setToolTip(tr("Enables Floating Point Result Flag calculation, needed for a few " - "games. (ON = Compatible, OFF = Fast)")); - m_sync_gpu->setToolTip(tr("Synchronizes the GPU and CPU threads to help prevent random freezes " - "in Dual core mode. (ON = Compatible, OFF = Fast)")); - m_enable_fast_disc->setToolTip(tr("Enable fast disc access. This can cause crashes and other " - "problems in some games. (ON = Fast, OFF = Compatible)")); - - core_layout->addWidget(m_enable_dual_core, 0, 0); - core_layout->addWidget(m_enable_mmu, 1, 0); - core_layout->addWidget(m_enable_fprf, 2, 0); - core_layout->addWidget(m_sync_gpu, 3, 0); - core_layout->addWidget(m_enable_fast_disc, 4, 0); - core_layout->addWidget(m_use_dsp_hle, 5, 0); - core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 6, 0); - core_layout->addWidget(m_deterministic_dual_core, 6, 1); - - // Stereoscopy - auto* stereoscopy_box = new QGroupBox(tr("Stereoscopy")); - auto* stereoscopy_layout = new QGridLayout; - stereoscopy_box->setLayout(stereoscopy_layout); - - m_depth_slider = new QSlider(Qt::Horizontal); - - m_depth_slider->setMinimum(100); - m_depth_slider->setMaximum(200); - - m_convergence_spin = new QSpinBox; - m_convergence_spin->setMinimum(0); - m_convergence_spin->setMaximum(INT32_MAX); - m_use_monoscopic_shadows = new QCheckBox(tr("Monoscopic Shadows")); - - m_depth_slider->setToolTip( - tr("This value is multiplied with the depth set in the graphics configuration.")); - m_convergence_spin->setToolTip( - tr("This value is added to the convergence value set in the graphics configuration.")); - m_use_monoscopic_shadows->setToolTip( - tr("Use a single depth buffer for both eyes. Needed for a few games.")); - - stereoscopy_layout->addWidget(new QLabel(tr("Depth Percentage:")), 0, 0); - stereoscopy_layout->addWidget(m_depth_slider, 0, 1); - stereoscopy_layout->addWidget(new QLabel(tr("Convergence:")), 1, 0); - stereoscopy_layout->addWidget(m_convergence_spin, 1, 1); - stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0); - - auto* settings_box = new QGroupBox(tr("Game-Specific Settings")); - auto* settings_layout = new QVBoxLayout; - settings_box->setLayout(settings_layout); - - settings_layout->addWidget( - new QLabel(tr("These settings override core Dolphin settings.\nUndetermined means the game " - "uses Dolphin's setting."))); - settings_layout->addWidget(core_box); - settings_layout->addWidget(stereoscopy_box); - - auto* layout = new QGridLayout; - - layout->addWidget(settings_box, 0, 0, 1, -1); - - auto* button_layout = new QHBoxLayout; - button_layout->setMargin(0); - - layout->addLayout(button_layout, 1, 0, 1, -1); - - button_layout->addWidget(m_refresh_config); - button_layout->addWidget(m_edit_user_config); - button_layout->addWidget(m_view_default_config); - - for (QCheckBox* item : {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, - m_enable_fast_disc, m_use_dsp_hle, m_use_monoscopic_shadows}) - item->setTristate(true); - - setLayout(layout); -} - -void GameConfigWidget::ConnectWidgets() -{ - // Buttons - connect(m_refresh_config, &QPushButton::pressed, this, &GameConfigWidget::LoadSettings); - connect(m_edit_user_config, &QPushButton::pressed, this, &GameConfigWidget::EditUserConfig); - connect(m_view_default_config, &QPushButton::pressed, this, &GameConfigWidget::ViewDefaultConfig); - - for (QCheckBox* box : {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, - m_enable_fast_disc, m_use_dsp_hle, m_use_monoscopic_shadows}) - connect(box, &QCheckBox::stateChanged, this, &GameConfigWidget::SaveSettings); - - connect(m_deterministic_dual_core, - static_cast(&QComboBox::currentIndexChanged), this, - &GameConfigWidget::SaveSettings); - connect(m_depth_slider, static_cast(&QSlider::valueChanged), this, - &GameConfigWidget::SaveSettings); - connect(m_convergence_spin, static_cast(&QSpinBox::valueChanged), this, - &GameConfigWidget::SaveSettings); -} - -void GameConfigWidget::LoadCheckBox(QCheckBox* checkbox, const std::string& section, - const std::string& key) -{ - bool checked; - - if (m_gameini_local.GetOrCreateSection(section)->Get(key, &checked)) - return checkbox->setCheckState(checked ? Qt::Checked : Qt::Unchecked); - - if (m_gameini_default.GetOrCreateSection(section)->Get(key, &checked)) - return checkbox->setCheckState(checked ? Qt::Checked : Qt::Unchecked); - - checkbox->setCheckState(Qt::PartiallyChecked); -} - -void GameConfigWidget::SaveCheckBox(QCheckBox* checkbox, const std::string& section, - const std::string& key) -{ - // Delete any existing entries from the local gameini if checkbox is undetermined. - // Otherwise, write the current value to the local gameini if the value differs from the default - // gameini values. - // Delete any existing entry from the local gameini if the value does not differ from the default - // gameini value. - - if (checkbox->checkState() == Qt::PartiallyChecked) + for (const auto& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision)) { - m_gameini_local.DeleteKey(section, key); - return; - } - - bool checked = checkbox->checkState() == Qt::Checked; + const std::string ini_path = path + filename; - if (m_gameini_default.Exists(section, key)) - { - bool default_value; - m_gameini_default.GetOrCreateSection(section)->Get(key, &default_value); + if (!File::Exists(ini_path)) + continue; - if (default_value != checked) - m_gameini_local.GetOrCreateSection(section)->Set(key, checked); - else - m_gameini_local.DeleteKey(section, key); + auto* edit = new GameConfigEdit(nullptr, QString::fromStdString(ini_path)); + edit->setReadOnly(read_only); + tab->addTab(edit, QString::fromStdString(filename)); - return; + // Three letter INIs are the most likely to contain useful information + if (filename.length() == 7) + tab->setCurrentIndex(tab->count() - 1); } - - m_gameini_local.GetOrCreateSection(section)->Set(key, checked); } -void GameConfigWidget::LoadSettings() +GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game) { - // Reload config - m_gameini_local = SConfig::LoadLocalGameIni(m_game_id, m_game.GetRevision()); - m_gameini_default = SConfig::LoadDefaultGameIni(m_game_id, m_game.GetRevision()); - - // Load game-specific settings - - // Core - LoadCheckBox(m_enable_dual_core, "Core", "CPUThread"); - LoadCheckBox(m_enable_mmu, "Core", "MMU"); - LoadCheckBox(m_enable_fprf, "Core", "FPRF"); - LoadCheckBox(m_sync_gpu, "Core", "SyncGPU"); - LoadCheckBox(m_enable_fast_disc, "Core", "FastDiscSpeed"); - LoadCheckBox(m_use_dsp_hle, "Core", "DSPHLE"); + m_game_id = m_game.GetGameID(); - std::string determinism_mode; + CreateWidgets(); + ConnectWidgets(); - int determinism_index = DETERMINISM_NOT_SET_INDEX; + const auto revision = game.GetRevision(); - m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); - m_gameini_local.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); + PopulateTab(m_default_tab, File::GetSysDirectory() + "GameSettings/", m_game_id, revision, true); + PopulateTab(m_local_tab, File::GetUserPath(D_GAMESETTINGS_IDX), m_game_id, revision, false); - if (determinism_mode == DETERMINISM_AUTO_STRING) + // Always give the user the opportunity to create a new INI + if (m_local_tab->count() == 0) { - determinism_index = DETERMINISM_AUTO_INDEX; + auto* edit = + new GameConfigEdit(nullptr, QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + + m_game_id + ".ini")); + m_local_tab->addTab(edit, QString::fromStdString(m_game_id)); } - else if (determinism_mode == DETERMINISM_NONE_STRING) - { - determinism_index = DETERMINISM_NONE_INDEX; - } - else if (determinism_mode == DETERMINISM_FAKE_COMPLETION_STRING) - { - determinism_index = DETERMINISM_FAKE_COMPLETION_INDEX; - } - - m_deterministic_dual_core->setCurrentIndex(determinism_index); - - // Stereoscopy - int depth_percentage = 100; - - m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage); - m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage); - - m_depth_slider->setValue(depth_percentage); - - int convergence = 0; - - m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence); - m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence); - - m_convergence_spin->setValue(convergence); - - LoadCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth"); } -void GameConfigWidget::SaveSettings() +void GameConfigWidget::CreateWidgets() { - // Core - SaveCheckBox(m_enable_dual_core, "Core", "CPUThread"); - SaveCheckBox(m_enable_mmu, "Core", "MMU"); - SaveCheckBox(m_enable_fprf, "Core", "FPRF"); - SaveCheckBox(m_sync_gpu, "Core", "SyncGPU"); - SaveCheckBox(m_enable_fast_disc, "Core", "FastDiscSpeed"); - SaveCheckBox(m_use_dsp_hle, "Core", "DSPHLE"); + auto* layout = new QVBoxLayout; - int determinism_num = m_deterministic_dual_core->currentIndex(); + auto* default_group = new QGroupBox(tr("Default Config (Read Only)")); + auto* default_layout = new QVBoxLayout; + m_default_tab = new QTabWidget; - std::string determinism_mode = DETERMINISM_NOT_SET_STRING; + default_group->setLayout(default_layout); + default_layout->addWidget(m_default_tab); - switch (determinism_num) - { - case DETERMINISM_AUTO_INDEX: - determinism_mode = DETERMINISM_AUTO_STRING; - break; - case DETERMINISM_NONE_INDEX: - determinism_mode = DETERMINISM_NONE_STRING; - break; - case DETERMINISM_FAKE_COMPLETION_INDEX: - determinism_mode = DETERMINISM_FAKE_COMPLETION_STRING; - break; - } + auto* local_group = new QGroupBox(tr("User Config")); + auto* local_layout = new QVBoxLayout; + m_local_tab = new QTabWidget; - if (determinism_mode != DETERMINISM_NOT_SET_STRING) - { - std::string default_mode = DETERMINISM_NOT_SET_STRING; - if (!(m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &default_mode) && - default_mode == determinism_mode)) - { - m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode); - } - } - else - { - m_gameini_local.DeleteKey("Core", "GPUDeterminismMode"); - } - - // Stereoscopy - int depth_percentage = m_depth_slider->value(); - - if (depth_percentage != 100) - { - int default_value = 0; - if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", - &default_value) && - default_value == depth_percentage)) - { - m_gameini_local.GetOrCreateSection("Video_Stereoscopy") - ->Set("StereoDepthPercentage", depth_percentage); - } - } - - int convergence = m_convergence_spin->value(); - if (convergence != 0) - { - int default_value = 0; - if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &default_value) && - default_value == convergence)) - { - m_gameini_local.GetOrCreateSection("Video_Stereoscopy") - ->Set("StereoConvergence", convergence); - } - } - - SaveCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth"); + local_group->setLayout(local_layout); + local_layout->addWidget(m_local_tab); - bool success = m_gameini_local.Save(m_gameini_local_path.toStdString()); + layout->addWidget(default_group); + layout->addWidget(local_group); - // If the resulting file is empty, delete it. Kind of a hack, but meh. - if (success && File::GetSize(m_gameini_local_path.toStdString()) == 0) - File::Delete(m_gameini_local_path.toStdString()); -} - -void GameConfigWidget::EditUserConfig() -{ - QFile file(m_gameini_local_path); - - if (!file.exists()) - { - file.open(QIODevice::WriteOnly); - file.close(); - } - - QDesktopServices::openUrl(QUrl::fromLocalFile(m_gameini_local_path)); + setLayout(layout); } -void GameConfigWidget::ViewDefaultConfig() +void GameConfigWidget::ConnectWidgets() { - for (const std::string& filename : - ConfigLoaders::GetGameIniFilenames(m_game_id, m_game.GetRevision())) - { - QString path = - QString::fromStdString(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename); - - if (QFile(path).exists()) - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); - } } diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.h b/Source/Core/DolphinQt/Config/GameConfigWidget.h index bb1c58d8030f..062f32046a2b 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.h +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.h @@ -16,14 +16,7 @@ namespace UICommon class GameFile; } -class QCheckBox; -class QComboBox; -class QGroupBox; -class QLineEdit; -class QPushButton; -class QSlider; -class QSpinBox; -class QVBoxLayout; +class QTabWidget; class GameConfigWidget : public QWidget { @@ -34,37 +27,12 @@ class GameConfigWidget : public QWidget private: void CreateWidgets(); void ConnectWidgets(); - void LoadSettings(); - void SaveSettings(); - - void EditUserConfig(); - void ViewDefaultConfig(); - - void LoadCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key); - void SaveCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key); - - QPushButton* m_refresh_config; - QPushButton* m_edit_user_config; - QPushButton* m_view_default_config; - - // Core - QCheckBox* m_enable_dual_core; - QCheckBox* m_enable_mmu; - QCheckBox* m_enable_fprf; - QCheckBox* m_sync_gpu; - QCheckBox* m_enable_fast_disc; - QCheckBox* m_use_dsp_hle; - QComboBox* m_deterministic_dual_core; - - // Stereoscopy - QSlider* m_depth_slider; - QSpinBox* m_convergence_spin; - QCheckBox* m_use_monoscopic_shadows; + QString m_gameini_sys_path; QString m_gameini_local_path; - IniFile m_gameini_local; - IniFile m_gameini_default; + QTabWidget* m_default_tab; + QTabWidget* m_local_tab; const UICommon::GameFile& m_game; std::string m_game_id; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index a1ddc418e935..5a6143809005 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -63,6 +63,8 @@ + + @@ -185,6 +187,8 @@ + + @@ -265,6 +269,8 @@ + +