Qt: Move language/theme setting to window

This commit is contained in:
Stenzek 2024-08-20 23:27:42 +10:00
parent 5c14ac2fd1
commit ccd7ba9acf
No known key found for this signature in database
14 changed files with 184 additions and 127 deletions

View File

@ -58,6 +58,23 @@ ControllerSettingsWindow::ControllerSettingsWindow() : QWidget()
ControllerSettingsWindow::~ControllerSettingsWindow() = default;
int ControllerSettingsWindow::getHotkeyCategoryIndex() const
{
const std::array<bool, 2> mtap_enabled = getEnabledMultitaps();
return 1 + (mtap_enabled[0] ? 4 : 1) + (mtap_enabled[1] ? 4 : 1);
}
ControllerSettingsWindow::Category ControllerSettingsWindow::getCurrentCategory() const
{
const int index = m_ui.settingsCategory->currentRow();
if (index == 0)
return Category::GlobalSettings;
else if (index >= getHotkeyCategoryIndex())
return Category::HotkeySettings;
else
return Category::FirstControllerSettings;
}
void ControllerSettingsWindow::setCategory(Category category)
{
switch (category)
@ -66,13 +83,12 @@ void ControllerSettingsWindow::setCategory(Category category)
m_ui.settingsCategory->setCurrentRow(0);
break;
// TODO: These will need to take multitap into consideration in the future.
case Category::FirstControllerSettings:
m_ui.settingsCategory->setCurrentRow(1);
break;
case Category::HotkeySettings:
m_ui.settingsCategory->setCurrentRow(3);
m_ui.settingsCategory->setCurrentRow(getHotkeyCategoryIndex());
break;
default:

View File

@ -56,6 +56,8 @@ public:
ALWAYS_INLINE bool isEditingProfile() const { return !m_profile_name.isEmpty(); }
ALWAYS_INLINE SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); }
Category getCurrentCategory() const;
void updateListDescription(u32 global_slot, ControllerBindingWidget* widget);
// Helper functions for updating setting values globally or in the profile.
@ -90,6 +92,7 @@ private Q_SLOTS:
void createWidgets();
private:
int getHotkeyCategoryIndex() const;
void refreshProfileList();
void switchProfile(const QString& name);

View File

@ -76,6 +76,14 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* dialog, QWidget
connect(m_ui.renderToSeparateWindow, &QCheckBox::checkStateChanged, this,
&InterfaceSettingsWidget::onRenderToSeparateWindowChanged);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.theme, "UI", "Theme", THEME_NAMES, THEME_VALUES,
QtHost::GetDefaultThemeName(), "MainWindow");
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); });
populateLanguageDropdown(m_ui.language);
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.language, "Main", "Language", QtHost::GetDefaultLanguage());
connect(m_ui.language, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InterfaceSettingsWidget::onLanguageChanged);
onRenderToSeparateWindowChanged();
dialog->registerWidgetHelp(
@ -126,14 +134,37 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* dialog, QWidget
}
else
{
m_ui.verticalLayout->removeWidget(m_ui.automaticUpdaterGroup);
m_ui.automaticUpdaterGroup->hide();
m_ui.verticalLayout->removeWidget(m_ui.updatesGroup);
m_ui.updatesGroup->hide();
}
}
InterfaceSettingsWidget::~InterfaceSettingsWidget() = default;
void InterfaceSettingsWidget::populateLanguageDropdown(QComboBox* cb)
{
for (const auto& [language, code] : Host::GetAvailableLanguageList())
{
QString icon_filename(QStringLiteral(":/icons/flags/%1.png").arg(QLatin1StringView(code)));
if (!QFile::exists(icon_filename))
{
// try without the suffix (e.g. es-es -> es)
const char* pos = std::strrchr(code, '-');
if (pos)
icon_filename = QStringLiteral(":/icons/flags/%1.png").arg(QLatin1StringView(pos));
}
cb->addItem(QIcon(icon_filename), QString::fromUtf8(language), QString::fromLatin1(code));
}
}
void InterfaceSettingsWidget::onRenderToSeparateWindowChanged()
{
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
}
void InterfaceSettingsWidget::onLanguageChanged()
{
QtHost::UpdateApplicationLanguage(QtUtils::GetRootWidget(this));
g_main_window->recreate();
}

View File

@ -17,8 +17,14 @@ public:
explicit InterfaceSettingsWidget(SettingsWindow* dialog, QWidget* parent);
~InterfaceSettingsWidget();
static void populateLanguageDropdown(QComboBox* cb);
Q_SIGNALS:
void themeChanged();
private Q_SLOTS:
void onRenderToSeparateWindowChanged();
void onLanguageChanged();
private:
Ui::InterfaceSettingsWidget m_ui;

View File

@ -140,9 +140,38 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="automaticUpdaterGroup">
<widget class="QGroupBox" name="appearanceGroup">
<property name="title">
<string>Automatic Updater</string>
<string>Appearance</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="0,1,0,1">
<item row="0" column="3">
<widget class="QComboBox" name="theme"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Language:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Theme:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="language"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="updatesGroup">
<property name="title">
<string>Updates</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">

View File

@ -725,6 +725,22 @@ void MainWindow::quit()
void MainWindow::recreate()
{
std::optional<QPoint> settings_window_pos;
int settings_window_row = 0;
std::optional<QPoint> controller_settings_window_pos;
ControllerSettingsWindow::Category controller_settings_window_row =
ControllerSettingsWindow::Category::GlobalSettings;
if (m_settings_window && m_settings_window->isVisible())
{
settings_window_pos = m_settings_window->pos();
settings_window_row = m_settings_window->getCategoryRow();
}
if (m_controller_settings_window && m_controller_settings_window->isVisible())
{
controller_settings_window_pos = m_controller_settings_window->pos();
controller_settings_window_row = m_controller_settings_window->getCurrentCategory();
}
// Remove subwindows before switching to surfaceless, because otherwise e.g. the debugger can cause funkyness.
destroySubWindows();
@ -763,6 +779,21 @@ void MainWindow::recreate()
g_main_window->updateEmulationActions(false, System::IsValid(), Achievements::IsHardcoreModeActive());
g_main_window->onFullscreenUIStateChange(g_emu_thread->isRunningFullscreenUI());
}
if (settings_window_pos.has_value())
{
SettingsWindow* dlg = g_main_window->getSettingsWindow();
dlg->move(settings_window_pos.value());
dlg->setCategoryRow(settings_window_row);
QtUtils::ShowOrRaiseWindow(dlg);
}
if (controller_settings_window_pos.has_value())
{
ControllerSettingsWindow* dlg = g_main_window->getControllerSettingsWindow();
dlg->move(controller_settings_window_pos.value());
dlg->setCategory(controller_settings_window_row);
QtUtils::ShowOrRaiseWindow(dlg);
}
}
void MainWindow::destroySubWindows()
@ -1524,7 +1555,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
menu.addSeparator();
connect(menu.addAction(tr("Exclude From List")), &QAction::triggered,
[this, entry]() { getSettingsDialog()->getGameListSettingsWidget()->addExcludedPath(entry->path); });
[this, entry]() { getSettingsWindow()->getGameListSettingsWidget()->addExcludedPath(entry->path); });
connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered,
[this, entry]() { clearGameListEntryPlayTime(entry); });
@ -1558,7 +1589,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
menu.addSeparator();
connect(menu.addAction(tr("Add Search Directory...")), &QAction::triggered,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
menu.exec(point);
}
@ -1732,32 +1763,6 @@ void MainWindow::setupAdditionalUi()
}
updateDebugMenuCropMode();
const std::string current_language = Host::GetBaseStringSettingValue("Main", "Language", "");
QActionGroup* language_group = new QActionGroup(m_ui.menuSettingsLanguage);
for (const auto& [language, code] : Host::GetAvailableLanguageList())
{
QAction* action = language_group->addAction(QString::fromUtf8(language));
action->setCheckable(true);
action->setChecked(current_language == code);
QString icon_filename(QStringLiteral(":/icons/flags/%1.png").arg(QLatin1StringView(code)));
if (!QFile::exists(icon_filename))
{
// try without the suffix (e.g. es-es -> es)
const char* pos = std::strrchr(code, '-');
if (pos)
icon_filename = QStringLiteral(":/icons/flags/%1.png").arg(QLatin1StringView(pos));
}
action->setIcon(QIcon(icon_filename));
m_ui.menuSettingsLanguage->addAction(action);
action->setData(QString::fromLatin1(code));
connect(action, &QAction::triggered, [action]() {
const QString new_language = action->data().toString();
Host::ChangeLanguage(new_language.toUtf8().constData());
});
}
for (u32 scale = 1; scale <= 10; scale++)
{
QAction* action = m_ui.menuWindowSize->addAction(tr("%1x Scale").arg(scale));
@ -2050,7 +2055,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionStartFullscreenUI2, &QAction::triggered, this, &MainWindow::onStartFullscreenUITriggered);
connect(m_ui.actionRemoveDisc, &QAction::triggered, this, &MainWindow::onRemoveDiscActionTriggered);
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
connect(m_ui.actionPowerOff, &QAction::triggered, this,
[this]() { requestShutdown(true, true, g_settings.save_state_on_exit); });
connect(m_ui.actionPowerOffWithoutSaving, &QAction::triggered, this,
@ -2154,7 +2159,7 @@ void MainWindow::connectSignals()
connect(m_game_list_widget, &GameListWidget::entryContextMenuRequested, this,
&MainWindow::onGameListEntryContextMenuRequested, Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::addGameDirectoryRequested, this,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDisableAllEnhancements, "Main",
"DisableAllEnhancements", false);
@ -2195,26 +2200,25 @@ void MainWindow::connectSignals()
false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowMDECState, "Debug", "ShowMDECState", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowDMAState, "Debug", "ShowDMAState", false);
for (u32 i = 0; InterfaceSettingsWidget::THEME_NAMES[i]; i++)
{
const QString key = QString::fromUtf8(InterfaceSettingsWidget::THEME_VALUES[i]);
QAction* action =
m_ui.menuSettingsTheme->addAction(qApp->translate("MainWindow", InterfaceSettingsWidget::THEME_NAMES[i]));
action->setCheckable(true);
action->setData(key);
connect(action, &QAction::toggled, [this, key](bool) { setTheme(key); });
}
updateMenuSelectedTheme();
}
void MainWindow::setTheme(const QString& theme)
void MainWindow::updateTheme()
{
[[maybe_unused]] const QString old_style_name = qApp->style()->name();
QtHost::UpdateApplicationTheme();
reloadThemeSpecificImages();
}
void MainWindow::reloadThemeSpecificImages()
{
m_game_list_widget->reloadThemeSpecificImages();
}
void MainWindow::onSettingsThemeChanged()
{
#ifdef _WIN32
const QString old_style_name = qApp->style()->name();
#endif
Host::SetBaseStringSettingValue("UI", "Theme", theme.toUtf8().constData());
Host::CommitBaseSettingChanges();
updateTheme();
#ifdef _WIN32
@ -2225,18 +2229,6 @@ void MainWindow::setTheme(const QString& theme)
#endif
}
void MainWindow::updateTheme()
{
QtHost::UpdateApplicationTheme();
updateMenuSelectedTheme();
reloadThemeSpecificImages();
}
void MainWindow::reloadThemeSpecificImages()
{
m_game_list_widget->reloadThemeSpecificImages();
}
void MainWindow::onSettingsResetToDefault(bool system, bool controller)
{
if (system && m_settings_window)
@ -2265,7 +2257,6 @@ void MainWindow::onSettingsResetToDefault(bool system, bool controller)
updateDebugMenuGPURenderer();
updateDebugMenuCropMode();
updateDebugMenuVisibility();
updateMenuSelectedTheme();
}
void MainWindow::saveStateToConfig()
@ -2366,31 +2357,41 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
}
}
SettingsWindow* MainWindow::getSettingsDialog()
SettingsWindow* MainWindow::getSettingsWindow()
{
if (!m_settings_window)
{
m_settings_window = new SettingsWindow();
connect(m_settings_window->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this,
&MainWindow::onSettingsThemeChanged);
}
return m_settings_window;
}
void MainWindow::doSettings(const char* category /* = nullptr */)
{
SettingsWindow* dlg = getSettingsDialog();
SettingsWindow* dlg = getSettingsWindow();
QtUtils::ShowOrRaiseWindow(dlg);
if (category)
dlg->setCategory(category);
}
void MainWindow::doControllerSettings(
ControllerSettingsWindow::Category category /*= ControllerSettingsDialog::Category::Count*/)
ControllerSettingsWindow* MainWindow::getControllerSettingsWindow()
{
if (!m_controller_settings_window)
m_controller_settings_window = new ControllerSettingsWindow();
QtUtils::ShowOrRaiseWindow(m_controller_settings_window);
return m_controller_settings_window;
}
void MainWindow::doControllerSettings(
ControllerSettingsWindow::Category category /*= ControllerSettingsDialog::Category::Count*/)
{
ControllerSettingsWindow* dlg = getControllerSettingsWindow();
QtUtils::ShowOrRaiseWindow(dlg);
if (category != ControllerSettingsWindow::Category::Count)
m_controller_settings_window->setCategory(category);
dlg->setCategory(category);
}
void MainWindow::updateDebugMenuCPUExecutionMode()
@ -2445,26 +2446,6 @@ void MainWindow::updateDebugMenuCropMode()
}
}
void MainWindow::updateMenuSelectedTheme()
{
QString theme =
QString::fromStdString(Host::GetBaseStringSettingValue("UI", "Theme", InterfaceSettingsWidget::DEFAULT_THEME_NAME));
for (QObject* obj : m_ui.menuSettingsTheme->children())
{
QAction* action = qobject_cast<QAction*>(obj);
if (action)
{
QVariant action_data(action->data());
if (action_data.isValid())
{
QSignalBlocker blocker(action);
action->setChecked(action_data == theme);
}
}
}
}
void MainWindow::showEvent(QShowEvent* event)
{
QMainWindow::showEvent(event);
@ -2815,9 +2796,7 @@ void MainWindow::onToolsMediaCaptureToggled(bool checked)
return;
}
Host::RunOnCPUThread([path = path.toStdString()]() {
System::StartMediaCapture(path);
});
Host::RunOnCPUThread([path = path.toStdString()]() { System::StartMediaCapture(path); });
}
void MainWindow::onToolsMemoryScannerTriggered()

View File

@ -236,21 +236,21 @@ private:
void updateDisplayWidgetCursor();
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
SettingsWindow* getSettingsDialog();
SettingsWindow* getSettingsWindow();
void doSettings(const char* category = nullptr);
ControllerSettingsWindow* getControllerSettingsWindow();
void doControllerSettings(ControllerSettingsWindow::Category category = ControllerSettingsWindow::Category::Count);
void updateDebugMenuCPUExecutionMode();
void updateDebugMenuGPURenderer();
void updateDebugMenuCropMode();
void updateMenuSelectedTheme();
std::string getDeviceDiscPath(const QString& title);
void setGameListEntryCoverImage(const GameList::Entry* entry);
void clearGameListEntryPlayTime(const GameList::Entry* entry);
void setTheme(const QString& theme);
void updateTheme();
void reloadThemeSpecificImages();
void onSettingsThemeChanged();
void destroySubWindows();
void registerForDeviceNotifications();

View File

@ -100,22 +100,6 @@
<property name="title">
<string>S&amp;ettings</string>
</property>
<widget class="QMenu" name="menuSettingsTheme">
<property name="title">
<string>Theme</string>
</property>
<property name="icon">
<iconset theme="paint-brush-line"/>
</property>
</widget>
<widget class="QMenu" name="menuSettingsLanguage">
<property name="title">
<string>Language</string>
</property>
<property name="icon">
<iconset theme="global-line"/>
</property>
</widget>
<addaction name="actionViewGameProperties"/>
<addaction name="separator"/>
<addaction name="actionInterfaceSettings"/>
@ -137,9 +121,6 @@
<addaction name="actionAddGameDirectory"/>
<addaction name="actionScanForNewGames"/>
<addaction name="actionRescanAllGames"/>
<addaction name="separator"/>
<addaction name="menuSettingsLanguage"/>
<addaction name="menuSettingsTheme"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">

View File

@ -478,7 +478,7 @@ bool QtHost::InitializeConfig(std::string settings_filename)
Log::SetConsoleOutputParams(true, s_base_settings_interface->GetBoolValue("Logging", "LogTimestamps", true));
}
InstallTranslator(nullptr);
UpdateApplicationLanguage(nullptr);
return true;
}

View File

@ -278,7 +278,7 @@ void RunOnUIThread(const std::function<void()>& func, bool block = false);
const char* GetDefaultLanguage();
/// Call when the language changes.
void InstallTranslator(QWidget* dialog_parent);
void UpdateApplicationLanguage(QWidget* dialog_parent);
/// Returns the application name and version, optionally including debug/devel config indicator.
QString GetAppNameAndVersion();

View File

@ -66,7 +66,7 @@ static constexpr const char* DEFAULT_IMGUI_FONT_NAME = "Roboto-Regular.ttf";
static std::vector<QTranslator*> s_translators;
} // namespace QtHost
void QtHost::InstallTranslator(QWidget* dialog_parent)
void QtHost::UpdateApplicationLanguage(QWidget* dialog_parent)
{
for (QTranslator* translator : s_translators)
{
@ -206,7 +206,7 @@ bool Host::ChangeLanguage(const char* new_language)
QtHost::RunOnUIThread([new_language = std::string(new_language)]() {
Host::SetBaseStringSettingValue("Main", "Language", new_language.c_str());
Host::CommitBaseSettingChanges();
QtHost::InstallTranslator(g_main_window);
QtHost::UpdateApplicationLanguage(g_main_window);
g_main_window->recreate();
});
return true;

View File

@ -79,7 +79,7 @@ void SettingsWindow::closeEvent(QCloseEvent* event)
void SettingsWindow::addPages()
{
addWidget(
m_general_settings = new InterfaceSettingsWidget(this, m_ui.settingsContainer), tr("Interface"),
m_interface_settings = new InterfaceSettingsWidget(this, m_ui.settingsContainer), tr("Interface"),
QStringLiteral("settings-3-line"),
tr("<strong>Interface Settings</strong><hr>These options control how the emulator looks and "
"behaves.<br><br>Mouse over an option for additional information, and Shift+Wheel to scroll this panel."));
@ -250,6 +250,16 @@ void SettingsWindow::setCategory(const char* category)
}
}
int SettingsWindow::getCategoryRow() const
{
return m_ui.settingsCategory->currentRow();
}
void SettingsWindow::setCategoryRow(int index)
{
m_ui.settingsCategory->setCurrentRow(index);
}
void SettingsWindow::onCategoryCurrentRowChanged(int row)
{
DebugAssert(row < static_cast<int>(MAX_SETTINGS_WIDGETS));

View File

@ -20,7 +20,7 @@ enum class DiscRegion : u8;
namespace GameDatabase {
enum class Trait : u32;
struct Entry;
}
} // namespace GameDatabase
class InterfaceSettingsWidget;
class BIOSSettingsWidget;
@ -52,10 +52,12 @@ public:
// Helper for externally setting fields in game settings ini.
static bool setGameSettingsBoolForSerial(const std::string& serial, const char* section, const char* key, bool value);
int getCategoryRow() const;
ALWAYS_INLINE bool isPerGameSettings() const { return static_cast<bool>(m_sif); }
ALWAYS_INLINE INISettingsInterface* getSettingsInterface() const { return m_sif.get(); }
ALWAYS_INLINE InterfaceSettingsWidget* getGeneralSettingsWidget() const { return m_general_settings; }
ALWAYS_INLINE InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; }
ALWAYS_INLINE BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; }
ALWAYS_INLINE ConsoleSettingsWidget* getConsoleSettingsWidget() const { return m_console_settings; }
ALWAYS_INLINE EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; }
@ -99,6 +101,7 @@ Q_SIGNALS:
public Q_SLOTS:
void setCategory(const char* category);
void setCategoryRow(int index);
private Q_SLOTS:
void onCategoryCurrentRowChanged(int row);
@ -128,7 +131,7 @@ private:
std::unique_ptr<INISettingsInterface> m_sif;
const GameDatabase::Entry* m_database_entry = nullptr;
InterfaceSettingsWidget* m_general_settings = nullptr;
InterfaceSettingsWidget* m_interface_settings = nullptr;
BIOSSettingsWidget* m_bios_settings = nullptr;
ConsoleSettingsWidget* m_console_settings = nullptr;
EmulationSettingsWidget* m_emulation_settings = nullptr;

View File

@ -185,8 +185,7 @@ void SetupWizardDialog::setupLanguagePage()
InterfaceSettingsWidget::DEFAULT_THEME_NAME, "InterfaceSettingsWidget");
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SetupWizardDialog::themeChanged);
for (const auto& [language, code] : Host::GetAvailableLanguageList())
m_ui.language->addItem(QString::fromUtf8(language), QString::fromLatin1(code));
InterfaceSettingsWidget::populateLanguageDropdown(m_ui.language);
SettingWidgetBinder::BindWidgetToStringSetting(nullptr, m_ui.language, "Main", "Language",
QtHost::GetDefaultLanguage());
connect(m_ui.language, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@ -204,7 +203,7 @@ void SetupWizardDialog::themeChanged()
void SetupWizardDialog::languageChanged()
{
// Skip the recreation, since we don't have many dynamic UI elements.
QtHost::InstallTranslator(this);
QtHost::UpdateApplicationLanguage(this);
m_ui.retranslateUi(this);
setupControllerPage(false);
}