dolphin/Source/Core/DolphinQt/Settings/InterfacePane.cpp
JosJuice 22aa88109f Use a stub AchivementManager when USE_RETRO_ACHIEVEMENTS isn't defined
This lets us reduce the number of USE_RETRO_ACHIEVEMENTS ifdefs in the
code base, reducing visual clutter. In particular, needing an ifdef for
each call to IsHardcodeModeActive was annoying to me. This also reduces
the risk that someone writes code that accidentally fails to compile
with USE_RETRO_ACHIEVEMENTS disabled.

We could cut down on ifdefs even further by making HardcodeWarningWidget
always exist, but that would result in non-trivial code ending up in the
binary even with USE_RETRO_ACHIEVEMENTS disabled, so I'm leaving it out
of this PR. It's not a lot of code though, so I might end up revisiting
it at some point.
2024-06-06 08:26:20 +02:00

419 lines
20 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Settings/InterfacePane.h"
#include <QCheckBox>
#include <QComboBox>
#include <QFileInfo>
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QRadioButton>
#include <QVBoxLayout>
#include <QWidget>
#include "Common/CommonPaths.h"
#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/UISettings.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
#include "UICommon/GameFile.h"
static ConfigStringChoice* MakeLanguageComboBox()
{
using QPair = std::pair<QString, QString>;
std::vector<QPair> languages = {
QPair{QObject::tr("<System Language>"), QString{}},
QPair{QStringLiteral(u"Bahasa Melayu"), QStringLiteral("ms")}, // Malay
QPair{QStringLiteral(u"Catal\u00E0"), QStringLiteral("ca")}, // Catalan
QPair{QStringLiteral(u"\u010Ce\u0161tina"), QStringLiteral("cs")}, // Czech
QPair{QStringLiteral(u"Dansk"), QStringLiteral("da")}, // Danish
QPair{QStringLiteral(u"Deutsch"), QStringLiteral("de")}, // German
QPair{QStringLiteral(u"English"), QStringLiteral("en")}, // English
QPair{QStringLiteral(u"Espa\u00F1ol"), QStringLiteral("es")}, // Spanish
QPair{QStringLiteral(u"Fran\u00E7ais"), QStringLiteral("fr")}, // French
QPair{QStringLiteral(u"Hrvatski"), QStringLiteral("hr")}, // Croatian
QPair{QStringLiteral(u"Italiano"), QStringLiteral("it")}, // Italian
QPair{QStringLiteral(u"Magyar"), QStringLiteral("hu")}, // Hungarian
QPair{QStringLiteral(u"Nederlands"), QStringLiteral("nl")}, // Dutch
QPair{QStringLiteral(u"Norsk bokm\u00E5l"), QStringLiteral("nb")}, // Norwegian
QPair{QStringLiteral(u"Polski"), QStringLiteral("pl")}, // Polish
QPair{QStringLiteral(u"Portugu\u00EAs"), QStringLiteral("pt")}, // Portuguese
QPair{QStringLiteral(u"Portugu\u00EAs (Brasil)"),
QStringLiteral("pt_BR")}, // Portuguese (Brazil)
QPair{QStringLiteral(u"Rom\u00E2n\u0103"), QStringLiteral("ro")}, // Romanian
QPair{QStringLiteral(u"Srpski"), QStringLiteral("sr")}, // Serbian
QPair{QStringLiteral(u"Suomi"), QStringLiteral("fi")}, // Finnish
QPair{QStringLiteral(u"Svenska"), QStringLiteral("sv")}, // Swedish
QPair{QStringLiteral(u"T\u00FCrk\u00E7e"), QStringLiteral("tr")}, // Turkish
QPair{QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"),
QStringLiteral("el")}, // Greek
QPair{QStringLiteral(u"\u0420\u0443\u0441\u0441\u043A\u0438\u0439"),
QStringLiteral("ru")}, // Russian
QPair{QStringLiteral(u"\u0627\u0644\u0639\u0631\u0628\u064A\u0629"),
QStringLiteral("ar")}, // Arabic
QPair{QStringLiteral(u"\u0641\u0627\u0631\u0633\u06CC"), QStringLiteral("fa")}, // Farsi
QPair{QStringLiteral(u"\uD55C\uAD6D\uC5B4"), QStringLiteral("ko")}, // Korean
QPair{QStringLiteral(u"\u65E5\u672C\u8A9E"), QStringLiteral("ja")}, // Japanese
QPair{QStringLiteral(u"\u7B80\u4F53\u4E2D\u6587"),
QStringLiteral("zh_CN")}, // Simplified Chinese
QPair{QStringLiteral(u"\u7E41\u9AD4\u4E2D\u6587"),
QStringLiteral("zh_TW")}, // Traditional Chinese
};
auto* const combobox = new ConfigStringChoice(languages, Config::MAIN_INTERFACE_LANGUAGE);
// The default, QComboBox::AdjustToContentsOnFirstShow, causes a noticeable pause when opening the
// SettingWindow for the first time. The culprit seems to be non-Latin graphemes in the above
// list. QComboBox::AdjustToContents still has some lag but it's much less noticeable.
combobox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
return combobox;
}
InterfacePane::InterfacePane(QWidget* parent) : QWidget(parent)
{
CreateLayout();
UpdateShowDebuggingCheckbox();
LoadUserStyle();
ConnectLayout();
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&InterfacePane::UpdateShowDebuggingCheckbox);
}
void InterfacePane::CreateLayout()
{
m_main_layout = new QVBoxLayout;
// Create layout here
CreateUI();
CreateInGame();
AddDescriptions();
m_main_layout->addStretch(1);
setLayout(m_main_layout);
}
void InterfacePane::CreateUI()
{
auto* groupbox = new QGroupBox(tr("User Interface"));
auto* groupbox_layout = new QVBoxLayout;
groupbox->setLayout(groupbox_layout);
m_main_layout->addWidget(groupbox);
auto* combobox_layout = new QFormLayout;
combobox_layout->setFormAlignment(Qt::AlignLeft | Qt::AlignTop);
combobox_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
groupbox_layout->addLayout(combobox_layout);
m_combobox_language = MakeLanguageComboBox();
combobox_layout->addRow(tr("&Language:"), m_combobox_language);
// List avalable themes
auto theme_paths =
Common::DoFileSearch({File::GetUserPath(D_THEMES_IDX), File::GetSysDirectory() + THEMES_DIR});
std::vector<std::string> theme_names;
theme_names.reserve(theme_paths.size());
std::transform(theme_paths.cbegin(), theme_paths.cend(), std::back_inserter(theme_names),
PathToFileName);
// Theme Combobox
m_combobox_theme = new ConfigStringChoice(theme_names, Config::MAIN_THEME_NAME);
combobox_layout->addRow(tr("&Theme:"), m_combobox_theme);
// User Style Combobox
m_combobox_userstyle = new ToolTipComboBox;
m_label_userstyle = new QLabel(tr("Style:"));
combobox_layout->addRow(m_label_userstyle, m_combobox_userstyle);
auto userstyle_search_results = Common::DoFileSearch({File::GetUserPath(D_STYLES_IDX)});
m_combobox_userstyle->addItem(tr("(System)"), static_cast<int>(Settings::StyleType::System));
// TODO: Support forcing light/dark on other OSes too.
#ifdef _WIN32
m_combobox_userstyle->addItem(tr("(Light)"), static_cast<int>(Settings::StyleType::Light));
m_combobox_userstyle->addItem(tr("(Dark)"), static_cast<int>(Settings::StyleType::Dark));
#endif
for (const std::string& path : userstyle_search_results)
{
const QFileInfo file_info(QString::fromStdString(path));
m_combobox_userstyle->addItem(file_info.completeBaseName(), file_info.fileName());
}
// Checkboxes
m_checkbox_use_builtin_title_database = new ConfigBool(tr("Use Built-In Database of Game Names"),
Config::MAIN_USE_BUILT_IN_TITLE_DATABASE);
m_checkbox_use_covers =
new ConfigBool(tr("Download Game Covers from GameTDB.com for Use in Grid Mode"),
Config::MAIN_USE_GAME_COVERS);
m_checkbox_show_debugging_ui = new ToolTipCheckBox(tr("Enable Debugging UI"));
m_checkbox_focused_hotkeys =
new ConfigBool(tr("Hotkeys Require Window Focus"), Config::MAIN_FOCUSED_HOTKEYS);
m_checkbox_disable_screensaver =
new ConfigBool(tr("Inhibit Screensaver During Emulation"), Config::MAIN_DISABLE_SCREENSAVER);
groupbox_layout->addWidget(m_checkbox_use_builtin_title_database);
groupbox_layout->addWidget(m_checkbox_use_covers);
groupbox_layout->addWidget(m_checkbox_show_debugging_ui);
groupbox_layout->addWidget(m_checkbox_focused_hotkeys);
groupbox_layout->addWidget(m_checkbox_disable_screensaver);
}
void InterfacePane::CreateInGame()
{
auto* groupbox = new QGroupBox(tr("Render Window"));
auto* groupbox_layout = new QVBoxLayout;
groupbox->setLayout(groupbox_layout);
m_main_layout->addWidget(groupbox);
m_checkbox_top_window = new ConfigBool(tr("Keep Window on Top"), Config::MAIN_KEEP_WINDOW_ON_TOP);
m_checkbox_confirm_on_stop = new ConfigBool(tr("Confirm on Stop"), Config::MAIN_CONFIRM_ON_STOP);
m_checkbox_use_panic_handlers =
new ConfigBool(tr("Use Panic Handlers"), Config::MAIN_USE_PANIC_HANDLERS);
m_checkbox_enable_osd =
new ConfigBool(tr("Show On-Screen Display Messages"), Config::MAIN_OSD_MESSAGES);
m_checkbox_show_active_title =
new ConfigBool(tr("Show Active Title in Window Title"), Config::MAIN_SHOW_ACTIVE_TITLE);
m_checkbox_pause_on_focus_lost =
new ConfigBool(tr("Pause on Focus Loss"), Config::MAIN_PAUSE_ON_FOCUS_LOST);
auto* mouse_groupbox = new QGroupBox(tr("Mouse Cursor Visibility"));
auto* m_vboxlayout_hide_mouse = new QVBoxLayout;
mouse_groupbox->setLayout(m_vboxlayout_hide_mouse);
m_radio_cursor_visible_movement =
new ConfigRadioInt(tr("On Movement"), Config::MAIN_SHOW_CURSOR,
static_cast<int>(Config::ShowCursor::OnMovement));
m_radio_cursor_visible_never = new ConfigRadioInt(tr("Never"), Config::MAIN_SHOW_CURSOR,
static_cast<int>(Config::ShowCursor::Never));
m_radio_cursor_visible_always = new ConfigRadioInt(
tr("Always"), Config::MAIN_SHOW_CURSOR, static_cast<int>(Config::ShowCursor::Constantly));
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_movement);
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_never);
m_vboxlayout_hide_mouse->addWidget(m_radio_cursor_visible_always);
m_checkbox_lock_mouse = new ConfigBool(tr("Lock Mouse Cursor"), Config::MAIN_LOCK_CURSOR);
// this ends up not being managed unless _WIN32, so lets not leak
m_checkbox_lock_mouse->setParent(this);
mouse_groupbox->setLayout(m_vboxlayout_hide_mouse);
groupbox_layout->addWidget(m_checkbox_top_window);
groupbox_layout->addWidget(m_checkbox_confirm_on_stop);
groupbox_layout->addWidget(m_checkbox_use_panic_handlers);
groupbox_layout->addWidget(m_checkbox_enable_osd);
groupbox_layout->addWidget(m_checkbox_show_active_title);
groupbox_layout->addWidget(m_checkbox_pause_on_focus_lost);
groupbox_layout->addWidget(mouse_groupbox);
#ifdef _WIN32
groupbox_layout->addWidget(m_checkbox_lock_mouse);
#else
m_checkbox_lock_mouse->hide();
#endif
}
void InterfacePane::ConnectLayout()
{
connect(m_checkbox_use_builtin_title_database, &QCheckBox::toggled, &Settings::Instance(),
&Settings::GameListRefreshRequested);
connect(m_checkbox_use_covers, &QCheckBox::toggled, &Settings::Instance(),
&Settings::RefreshMetadata);
connect(m_checkbox_show_debugging_ui, &QCheckBox::toggled, &Settings::Instance(),
&Settings::SetDebugModeEnabled);
connect(m_combobox_theme, &QComboBox::currentIndexChanged, this,
[this](int index) { Settings::Instance().TriggerThemeChanged(); });
connect(m_combobox_userstyle, &QComboBox::currentIndexChanged, this,
&InterfacePane::OnUserStyleChanged);
connect(m_combobox_language, &QComboBox::currentIndexChanged, this,
[this]() { OnLanguageChanged(); });
connect(m_checkbox_top_window, &QCheckBox::toggled, &Settings::Instance(),
&Settings::KeepWindowOnTopChanged);
connect(m_radio_cursor_visible_movement, &ConfigRadioInt::OnSelected, &Settings::Instance(),
&Settings::CursorVisibilityChanged);
connect(m_radio_cursor_visible_never, &ConfigRadioInt::OnSelected, &Settings::Instance(),
&Settings::CursorVisibilityChanged);
connect(m_radio_cursor_visible_always, &ConfigRadioInt::OnSelected, &Settings::Instance(),
&Settings::CursorVisibilityChanged);
connect(m_checkbox_lock_mouse, &QCheckBox::toggled, &Settings::Instance(),
[this]() { Settings::Instance().LockCursorChanged(); });
}
void InterfacePane::UpdateShowDebuggingCheckbox()
{
SignalBlocking(m_checkbox_show_debugging_ui)
->setChecked(Settings::Instance().IsDebugModeEnabled());
static constexpr char TR_SHOW_DEBUGGING_UI_DESCRIPTION[] = QT_TR_NOOP(
"Shows Dolphin's debugging User Interface. This lets you view and modify a game's code and "
"memory contents, set debugging breakpoints, examine network requests, and more."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static constexpr char TR_DISABLED_IN_HARDCORE_DESCRIPTION[] =
QT_TR_NOOP("<dolphin_emphasis>Disabled in Hardcore Mode.</dolphin_emphasis>");
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
SignalBlocking(m_checkbox_show_debugging_ui)->setEnabled(!hardcore);
if (hardcore)
{
m_checkbox_show_debugging_ui->SetDescription(tr("%1<br><br>%2")
.arg(tr(TR_SHOW_DEBUGGING_UI_DESCRIPTION))
.arg(tr(TR_DISABLED_IN_HARDCORE_DESCRIPTION)));
}
else
{
m_checkbox_show_debugging_ui->SetDescription(tr(TR_SHOW_DEBUGGING_UI_DESCRIPTION));
}
}
void InterfacePane::LoadUserStyle()
{
const Settings::StyleType style_type = Settings::Instance().GetStyleType();
const QString userstyle = Settings::Instance().GetUserStyleName();
const int index = style_type == Settings::StyleType::User ?
m_combobox_userstyle->findData(userstyle) :
m_combobox_userstyle->findData(static_cast<int>(style_type));
if (index > 0)
SignalBlocking(m_combobox_userstyle)->setCurrentIndex(index);
}
void InterfacePane::OnUserStyleChanged()
{
const auto selected_style = m_combobox_userstyle->currentData();
bool is_builtin_type = false;
const int style_type_int = selected_style.toInt(&is_builtin_type);
Settings::Instance().SetStyleType(is_builtin_type ?
static_cast<Settings::StyleType>(style_type_int) :
Settings::StyleType::User);
if (!is_builtin_type)
Settings::Instance().SetUserStyleName(selected_style.toString());
Settings::Instance().ApplyStyle();
}
void InterfacePane::OnLanguageChanged()
{
ModalMessageBox::information(
this, tr("Restart Required"),
tr("You must restart Dolphin in order for the change to take effect."));
}
void InterfacePane::AddDescriptions()
{
static constexpr char TR_TITLE_DATABASE_DESCRIPTION[] = QT_TR_NOOP(
"Uses Dolphin's database of properly formatted names in the Game List Title column."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_THEME_DESCRIPTION[] =
QT_TR_NOOP("Changes the appearance and color of Dolphin's buttons."
"<br><br><dolphin_emphasis>If unsure, select Clean.</dolphin_emphasis>");
static constexpr char TR_TOP_WINDOW_DESCRIPTION[] =
QT_TR_NOOP("Forces the render window to stay on top of other windows and applications."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static constexpr char TR_LANGUAGE_DESCRIPTION[] = QT_TR_NOOP(
"Sets the language displayed by Dolphin's User Interface."
"<br><br>Changes to this setting only take effect once Dolphin is restarted."
"<br><br><dolphin_emphasis>If unsure, select &lt;System Language&gt;.</dolphin_emphasis>");
static constexpr char TR_FOCUSED_HOTKEYS_DESCRIPTION[] =
QT_TR_NOOP("Requires the render window to be focused for hotkeys to take effect."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_USE_COVERS_DESCRIPTION[] =
QT_TR_NOOP("Downloads full game covers from GameTDB.com to display in the Game List's Grid "
"View. If this setting is unchecked the Game List displays a banner generated "
"from the game's save files, and if the game has no save file displays a generic "
"banner instead."
"<br><br>List View will always use the save file banners."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_DISABLE_SCREENSAVER_DESCRIPTION[] =
QT_TR_NOOP("Disables your screensaver while running a game."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_CONFIRM_ON_STOP_DESCRIPTION[] =
QT_TR_NOOP("Prompts you to confirm that you want to end emulation when you press Stop."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_USE_PANIC_HANDLERS_DESCRIPTION[] =
QT_TR_NOOP("In the event of an error, Dolphin will halt to inform you of the error and "
"present choices on how to proceed. With this option disabled, Dolphin will "
"\"ignore\" all errors. Emulation will not be halted and you will not be notified."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_ENABLE_OSD_DESCRIPTION[] =
QT_TR_NOOP("Shows on-screen display messages over the render window. These messages "
"disappear after several seconds."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_SHOW_ACTIVE_TITLE_DESCRIPTION[] =
QT_TR_NOOP("Shows the active game title in the render window's title bar."
"<br><br><dolphin_emphasis>If unsure, leave this checked.</dolphin_emphasis>");
static constexpr char TR_PAUSE_ON_FOCUS_LOST_DESCRIPTION[] =
QT_TR_NOOP("Pauses the game whenever the render window isn't focused."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static constexpr char TR_LOCK_MOUSE_DESCRIPTION[] =
QT_TR_NOOP("Locks the Mouse Cursor to the Render Widget as long as it has focus. You can "
"set a hotkey to unlock it."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static constexpr char TR_CURSOR_VISIBLE_MOVEMENT_DESCRIPTION[] =
QT_TR_NOOP("Shows the Mouse Cursor briefly whenever it has recently moved, then hides it."
"<br><br><dolphin_emphasis>If unsure, select this mode.</dolphin_emphasis>");
static constexpr char TR_CURSOR_VISIBLE_NEVER_DESCRIPTION[] = QT_TR_NOOP(
"Hides the Mouse Cursor whenever it is inside the render window and the render window is "
"focused."
"<br><br><dolphin_emphasis>If unsure, select &quot;On Movement&quot;.</dolphin_emphasis>");
static constexpr char TR_CURSOR_VISIBLE_ALWAYS_DESCRIPTION[] = QT_TR_NOOP(
"Shows the Mouse Cursor at all times."
"<br><br><dolphin_emphasis>If unsure, select &quot;On Movement&quot;.</dolphin_emphasis>");
static constexpr char TR_USER_STYLE_DESCRIPTION[] =
QT_TR_NOOP("Sets the style of Dolphin's User Interface. Any Custom User Styles that you have "
"loaded will be presented here, allowing you to switch to them."
"<br><br><dolphin_emphasis>If unsure, select (System).</dolphin_emphasis>");
m_checkbox_use_builtin_title_database->SetDescription(tr(TR_TITLE_DATABASE_DESCRIPTION));
m_combobox_theme->SetTitle(tr("Theme"));
m_combobox_theme->SetDescription(tr(TR_THEME_DESCRIPTION));
m_checkbox_top_window->SetDescription(tr(TR_TOP_WINDOW_DESCRIPTION));
m_combobox_language->SetTitle(tr("Language"));
m_combobox_language->SetDescription(tr(TR_LANGUAGE_DESCRIPTION));
m_checkbox_focused_hotkeys->SetDescription(tr(TR_FOCUSED_HOTKEYS_DESCRIPTION));
m_checkbox_use_covers->SetDescription(tr(TR_USE_COVERS_DESCRIPTION));
m_checkbox_disable_screensaver->SetDescription(tr(TR_DISABLE_SCREENSAVER_DESCRIPTION));
m_checkbox_confirm_on_stop->SetDescription(tr(TR_CONFIRM_ON_STOP_DESCRIPTION));
m_checkbox_use_panic_handlers->SetDescription(tr(TR_USE_PANIC_HANDLERS_DESCRIPTION));
m_checkbox_enable_osd->SetDescription(tr(TR_ENABLE_OSD_DESCRIPTION));
m_checkbox_show_active_title->SetDescription(tr(TR_SHOW_ACTIVE_TITLE_DESCRIPTION));
m_checkbox_pause_on_focus_lost->SetDescription(tr(TR_PAUSE_ON_FOCUS_LOST_DESCRIPTION));
m_checkbox_lock_mouse->SetDescription(tr(TR_LOCK_MOUSE_DESCRIPTION));
m_radio_cursor_visible_movement->SetDescription(tr(TR_CURSOR_VISIBLE_MOVEMENT_DESCRIPTION));
m_radio_cursor_visible_never->SetDescription(tr(TR_CURSOR_VISIBLE_NEVER_DESCRIPTION));
m_radio_cursor_visible_always->SetDescription(tr(TR_CURSOR_VISIBLE_ALWAYS_DESCRIPTION));
m_combobox_userstyle->SetTitle(tr("Style"));
m_combobox_userstyle->SetDescription(tr(TR_USER_STYLE_DESCRIPTION));
}