This commit is contained in:
F David
2024-03-01 00:32:06 -08:00
committed by GitHub
97 changed files with 1087 additions and 1416 deletions
+2 -1
View File
@@ -15,6 +15,7 @@
#define CONFIG_DIR "config"
#define CRASH_DUMPS_DIR "crash_dumps"
#define DUMP_DIR "dump"
#define ICONS_DIR "icons"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define LOG_DIR "log"
@@ -24,7 +25,7 @@
#define SDMC_DIR "sdmc"
#define SHADER_DIR "shader"
#define TAS_DIR "tas"
#define ICONS_DIR "icons"
#define THEMES_DIR "qt_themes"
// yuzu-specific files
+2 -1
View File
@@ -121,6 +121,7 @@ public:
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
@@ -130,7 +131,7 @@ public:
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
GenerateYuzuPath(YuzuPath::ThemesDir, yuzu_path / THEMES_DIR);
}
private:
+2 -1
View File
@@ -17,6 +17,7 @@ enum class YuzuPath {
ConfigDir, // Where config files are stored.
CrashDumpsDir, // Where crash dumps are stored.
DumpDir, // Where dumped data is stored.
IconsDir, // Where Icons for Windows shortcuts are stored.
KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored.
LogDir, // Where log files are stored.
@@ -26,7 +27,7 @@ enum class YuzuPath {
SDMCDir, // Where the emulated SDMC is stored.
ShaderDir, // Where shaders are stored.
TASDir, // Where TAS scripts are stored.
IconsDir, // Where Icons for Windows shortcuts are stored.
ThemesDir, // Where users should put their custom themes
};
/**
+1
View File
@@ -67,6 +67,7 @@ SWITCHABLE(u8, true);
// Used in UISettings
// TODO see if we can move this to uisettings.cpp
SWITCHABLE(ConfirmStop, true);
SWITCHABLE(DarkModeState, true);
#undef SETTING
#undef SWITCHABLE
+1
View File
@@ -88,6 +88,7 @@ SWITCHABLE(u8, true);
// Used in UISettings
// TODO see if we can move this to uisettings.h
SWITCHABLE(ConfirmStop, true);
SWITCHABLE(DarkModeState, true);
#undef SETTING
#undef SWITCHABLE
+2
View File
@@ -155,6 +155,8 @@ ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
ENUM(DarkModeState, Off, On, Auto);
template <typename Type>
inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations();
-1
View File
@@ -164,7 +164,6 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<resources>
<include location="../../dist/qt_themes_default/default/default.qrc"/>
<include location="../../dist/qt_themes/default/default.qrc"/>
</resources>
<connections>
+10 -6
View File
@@ -384,10 +384,12 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
void QtControllerSelectorDialog::SetSupportedControllers() {
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
return QStringLiteral("_midnight");
} else if (GMainWindow::CheckDarkMode() ||
QIcon::themeName().contains(QStringLiteral("dark"))) {
// Use dark icons if current OS mode is dark, or the theme contains "dark" in its name
return QStringLiteral("_dark");
} else {
return QString{};
}
@@ -572,10 +574,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
}
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark"))) {
return QStringLiteral("_dark");
} else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
return QStringLiteral("_midnight");
} else if (GMainWindow::CheckDarkMode() ||
QIcon::themeName().contains(QStringLiteral("dark"))) {
// Use dark icons if current OS mode is dark, or the theme contains "dark" in its name
return QStringLiteral("_dark");
} else {
return QString{};
}
+3 -1
View File
@@ -823,7 +823,9 @@ void QtSoftwareKeyboardDialog::SetControllerImage() {
handheld->IsConnected() ? handheld->GetNpadStyleIndex() : player_1->GetNpadStyleIndex();
const QString theme = [] {
if (QIcon::themeName().contains(QStringLiteral("dark")) ||
// Use dark icons if current OS mode is dark, or the theme contains "dark", or "midnight" in
// its name
if (GMainWindow::CheckDarkMode() || QIcon::themeName().contains(QStringLiteral("dark")) ||
QIcon::themeName().contains(QStringLiteral("midnight"))) {
return QStringLiteral("_dark");
} else {
@@ -1430,12 +1430,12 @@
</item>
<item>
<widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>ZL</string>
</property>
@@ -1481,20 +1481,20 @@
</widget>
</item>
<item>
<widget class="QSlider" name="sliderZLThreshold">
<property name="maximumSize">
<size>
<width>70</width>
<height>15</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QSlider" name="sliderZLThreshold">
<property name="maximumSize">
<size>
<width>70</width>
<height>40</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
@@ -1927,22 +1927,22 @@
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sliderZRThreshold">
<property name="maximumSize">
<size>
<width>70</width>
<height>15</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sliderZRThreshold">
<property name="maximumSize">
<size>
<width>70</width>
<height>40</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+55 -4
View File
@@ -18,6 +18,7 @@
#include <QString>
#include <QToolButton>
#include <QVariant>
#include <QtGlobal>
#include "common/common_types.h"
#include "common/fs/path_util.h"
@@ -29,6 +30,8 @@
#include "ui_configure_ui.h"
#include "yuzu/uisettings.h"
using Settings::DarkModeState;
namespace {
constexpr std::array default_game_icon_sizes{
std::make_pair(0, QT_TRANSLATE_NOOP("ConfigureUI", "None")),
@@ -106,11 +109,56 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
InitializeLanguageComboBox();
for (const auto& theme : UISettings::themes) {
for (const auto& theme : UISettings::included_themes) {
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
QString::fromUtf8(theme.second));
}
// Add custom styles stored in yuzu directory
const QDir local_dir(
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ThemesDir)));
for (const QString& theme_dir :
local_dir.entryList(QDir::NoDot | QDir::NoDotDot | QDir::Dirs)) {
// folders ending with "_dark" are reserved for dark variant icons of other styles
if (theme_dir.endsWith(QStringLiteral("_dark"))) {
continue;
}
// Split at _ and capitalize words in name
QStringList cased_name;
for (QString word : theme_dir.split(QChar::fromLatin1('_'))) {
cased_name.append(word.at(0).toUpper() + word.mid(1));
}
QString theme_name = cased_name.join(QChar::fromLatin1(' '));
theme_name += QStringLiteral(" (%1)").arg(tr("Custom"));
ui->theme_combobox->addItem(theme_name, theme_dir);
}
QByteArray current_qt_qpa = qgetenv("QT_QPA_PLATFORM");
#ifdef _WIN32
// Indicate which option needs a restart to be applied, depending on current environment
// variable
if (current_qt_qpa.contains("darkmode=2")) {
ui->dark_mode_combobox->addItem(tr("Auto"), QVariant::fromValue(DarkModeState::Auto));
ui->dark_mode_combobox->addItem(tr("Always On") + QStringLiteral(" (") +
tr("Needs restart") + QStringLiteral(")"),
QVariant::fromValue(DarkModeState::On));
ui->dark_mode_combobox->addItem(tr("Always Off") + QStringLiteral(" (") +
tr("Needs restart") + QStringLiteral(")"),
QVariant::fromValue(DarkModeState::Off));
} else {
ui->dark_mode_combobox->addItem(tr("Auto") + QStringLiteral(" (") + tr("Needs restart") +
QStringLiteral(")"),
QVariant::fromValue(DarkModeState::Auto));
ui->dark_mode_combobox->addItem(tr("Always On"), QVariant::fromValue(DarkModeState::On));
ui->dark_mode_combobox->addItem(tr("Always Off"), QVariant::fromValue(DarkModeState::Off));
}
#else
ui->dark_mode_combobox->addItem(tr("Auto"), QVariant::fromValue(DarkModeState::Auto));
ui->dark_mode_combobox->addItem(tr("Always On"), QVariant::fromValue(DarkModeState::On));
ui->dark_mode_combobox->addItem(tr("Always Off"), QVariant::fromValue(DarkModeState::Off));
#endif
InitializeIconSizeComboBox();
InitializeRowComboBoxes();
@@ -164,7 +212,9 @@ ConfigureUi::~ConfigureUi() = default;
void ConfigureUi::ApplyConfiguration() {
UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString();
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
UISettings::values.dark_mode_state =
static_cast<DarkModeState>(ui->dark_mode_combobox->currentData().toUInt());
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
UISettings::values.show_compat = ui->show_compat->isChecked();
UISettings::values.show_size = ui->show_size->isChecked();
@@ -191,8 +241,9 @@ void ConfigureUi::RequestGameListUpdate() {
}
void ConfigureUi::SetConfiguration() {
ui->theme_combobox->setCurrentIndex(
ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme)));
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->dark_mode_combobox->setCurrentIndex(
ui->dark_mode_combobox->findData(QVariant::fromValue(UISettings::values.dark_mode_state)));
ui->language_combobox->setCurrentIndex(ui->language_combobox->findData(
QString::fromStdString(UISettings::values.language.GetValue())));
ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue());
+14
View File
@@ -63,6 +63,20 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="dark_mode_label">
<property name="text">
<string>Dark Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="dark_mode_combobox"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
+8 -7
View File
@@ -260,9 +260,10 @@ void QtConfig::ReadShortcutValues() {
void QtConfig::ReadUIValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Ui));
UISettings::values.theme = ReadStringSetting(
std::string("theme"),
std::string(UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second));
UISettings::values.theme =
QString::fromStdString(ReadStringSetting("theme", std::string(UISettings::default_theme)));
UISettings::values.dark_mode_state = static_cast<DarkModeState>(
ReadIntegerSetting("dark_mode_state", static_cast<int>(DarkModeState::Auto)));
ReadUIGamelistValues();
ReadUILayoutValues();
@@ -468,10 +469,10 @@ void QtConfig::SaveUIValues() {
WriteCategory(Settings::Category::Ui);
WriteCategory(Settings::Category::UiGeneral);
WriteStringSetting(
std::string("theme"), UISettings::values.theme,
std::make_optional(std::string(
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
WriteStringSetting("theme", UISettings::values.theme.toStdString(),
std::make_optional(std::string(UISettings::default_theme)));
WriteIntegerSetting("dark_mode_state", static_cast<int>(UISettings::values.dark_mode_state),
std::make_optional(static_cast<int>(DarkModeState::Auto)));
SaveUIGamelistValues();
SaveUILayoutValues();
+2 -3
View File
@@ -35,9 +35,8 @@ constexpr std::array<std::array<Qt::GlobalColor, 2>, 10> WaitTreeColors{{
}};
bool IsDarkTheme() {
const auto& theme = UISettings::values.theme;
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
return UISettings::values.theme.contains(QStringLiteral("dark")) ||
UISettings::values.theme.contains(QStringLiteral("midnight"));
}
} // namespace
+366 -85
View File
@@ -8,6 +8,7 @@
#include <iostream>
#include <memory>
#include <thread>
#include "core/hle/service/am/applet_manager.h"
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
@@ -17,6 +18,9 @@
#endif
#ifdef __unix__
#include <csignal>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QtDBus>
#include <sys/socket.h>
#include "common/linux/gamemode.h"
#endif
@@ -301,18 +305,6 @@ static void OverrideWindowsFont() {
}
#endif
bool GMainWindow::CheckDarkMode() {
#ifdef __unix__
const QPalette test_palette(qApp->palette());
const QColor text_color = test_palette.color(QPalette::Active, QPalette::Text);
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
return (text_color.value() > window_color.value());
#else
// TODO: Windows
return false;
#endif // __unix__
}
GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
@@ -333,15 +325,12 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
ui->setupUi(this);
statusBar()->hide();
// Check dark mode before a theme is loaded
os_dark_mode = CheckDarkMode();
startup_icon_theme = QIcon::themeName();
// fallback can only be set once, colorful theme icons are okay on both light/dark
QIcon::setFallbackThemeName(QStringLiteral("colorful"));
// fallback can only be set once, default theme icons are okay on both light/dark
QIcon::setFallbackThemeName(QStringLiteral("default"));
QIcon::setFallbackSearchPaths(QStringList(QStringLiteral(":/icons")));
default_theme_paths = QIcon::themeSearchPaths();
UpdateUITheme();
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
discord_rpc->Update();
@@ -359,6 +348,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
SetDefaultUIGeometry();
RestoreUIState();
UpdateUITheme();
ConnectMenuEvents();
ConnectWidgetEvents();
@@ -482,7 +472,10 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
SDL_EnableScreenSaver();
#endif
#ifdef __unix__
SetupPrepareForSleep();
ListenColorSchemeChange();
#endif
QStringList args = QApplication::arguments();
@@ -1671,8 +1664,8 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
}
}
void GMainWindow::SetupPrepareForSleep() {
#ifdef __unix__
void GMainWindow::SetupPrepareForSleep() {
auto bus = QDBusConnection::systemBus();
if (bus.isConnected()) {
const bool success = bus.connect(
@@ -1686,8 +1679,8 @@ void GMainWindow::SetupPrepareForSleep() {
} else {
LOG_WARNING(Frontend, "QDBusConnection system bus is not connected");
}
#endif // __unix__
}
#endif // __unix__
void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
if (emu_thread == nullptr) {
@@ -3731,7 +3724,8 @@ void GMainWindow::ResetWindowSize1080() {
}
void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const QString old_theme = UISettings::values.theme;
DarkModeState old_dark_mode_state = UISettings::values.dark_mode_state;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
const auto old_language_index = Settings::values.language_index.GetValue();
#ifdef __unix__
@@ -3792,7 +3786,8 @@ void GMainWindow::OnConfigure() {
}
InitializeHotkeys();
if (UISettings::values.theme != old_theme) {
if (UISettings::values.theme != old_theme ||
UISettings::values.dark_mode_state != old_dark_mode_state) {
UpdateUITheme();
}
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
@@ -3808,8 +3803,6 @@ void GMainWindow::OnConfigure() {
multiplayer_state->UpdateCredentials();
}
emit UpdateThemedIcons();
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload || Settings::values.language_index.GetValue() != old_language_index) {
game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -5022,9 +5015,113 @@ void GMainWindow::filterBarSetChecked(bool state) {
emit(OnToggleFilterBar());
}
void GMainWindow::UpdateUITheme() {
const QString default_theme = QString::fromStdString(UISettings::default_theme.data());
QString current_theme = UISettings::values.theme;
if (current_theme.isEmpty()) {
current_theme = default_theme;
}
UpdateIcons(current_theme);
// Find the stylesheet to load
QString theme_uri{QStringLiteral(":/%1/").arg(current_theme)};
if (TryLoadStylesheet(theme_uri)) {
return;
}
// New style not found in app, reading local directory
LOG_DEBUG(Frontend, "Style \"{}\" not found in app package, reading local directory",
current_theme.toStdString());
std::filesystem::path theme_path =
Common::FS::GetYuzuPath(Common::FS::YuzuPath::ThemesDir) / current_theme.toStdString();
// Try to load theme locally
if (TryLoadStylesheet(theme_path)) {
return;
}
// Reading new theme failed, loading default stylesheet
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
current_theme.toStdString());
theme_uri = QStringLiteral(":/%1/").arg(default_theme);
if (TryLoadStylesheet(theme_uri)) {
return;
}
// Reading default failed, loading empty stylesheet
LOG_ERROR(Frontend, "Unable to set default style, stylesheet file not found");
qApp->setStyleSheet({});
setStyleSheet({});
}
void GMainWindow::UpdateIcons(const QString& theme_used) {
// Append _dark to the theme name to use dark variant icons
if (CheckDarkMode()) {
LOG_DEBUG(Frontend, "Using icons from: {}", theme_used.toStdString() + "_dark");
QIcon::setThemeName(theme_used + QStringLiteral("_dark"));
} else {
LOG_DEBUG(Frontend, "Using icons from: {}", theme_used.toStdString());
QIcon::setThemeName(theme_used);
}
const QString theme_directory{
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ThemesDir))};
// Set path for default icons
// Use icon resources from application binary and current theme local subdirectory, if it exists
QStringList theme_paths;
theme_paths << QString::fromStdString(":/icons") << QStringLiteral("%1").arg(theme_directory);
QIcon::setThemeSearchPaths(theme_paths);
// Change current directory, to allow user themes to use their own icons
QDir::setCurrent(QStringLiteral("%1/%2").arg(theme_directory, UISettings::values.theme));
emit UpdateThemedIcons();
}
bool GMainWindow::TryLoadStylesheet(const QString& theme_uri) {
QString style_path;
// Use themed stylesheet if it exists
if (CheckDarkMode()) {
style_path = theme_uri + QStringLiteral("dark.qss");
} else {
style_path = theme_uri + QStringLiteral("light.qss");
}
if (!QFile::exists(style_path)) {
LOG_DEBUG(Frontend, "No themed (light/dark) stylesheet, using default one");
// Use common stylesheet if themed one does not exist
style_path = theme_uri + QStringLiteral("style.qss");
}
// Loading stylesheet
QFile style_file(style_path);
if (style_file.open(QFile::ReadOnly | QFile::Text)) {
// Update the color palette before applying the stylesheet
UpdateThemePalette();
LOG_DEBUG(Frontend, "Loading stylesheet in: {}", theme_uri.toStdString());
QTextStream ts_theme(&style_file);
qApp->setStyleSheet(ts_theme.readAll());
setStyleSheet(ts_theme.readAll());
SetCustomStylesheet();
return true;
}
// Opening the file failed
return false;
}
bool GMainWindow::TryLoadStylesheet(const std::filesystem::path& theme_path) {
return TryLoadStylesheet(QString::fromStdString(theme_path.string() + "/"));
}
static void AdjustLinkColor() {
QPalette new_pal(qApp->palette());
if (UISettings::IsDarkTheme()) {
if (GMainWindow::CheckDarkMode()) {
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
} else {
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
@@ -5034,54 +5131,233 @@ static void AdjustLinkColor() {
}
}
void GMainWindow::UpdateUITheme() {
const QString default_theme = QString::fromUtf8(
UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
QString current_theme = QString::fromStdString(UISettings::values.theme);
if (current_theme.isEmpty()) {
current_theme = default_theme;
}
void GMainWindow::UpdateThemePalette() {
QPalette themePalette(qApp->palette());
#ifdef _WIN32
QIcon::setThemeName(current_theme);
AdjustLinkColor();
#else
if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
: startup_icon_theme);
QIcon::setThemeSearchPaths(QStringList(default_theme_paths));
if (CheckDarkMode()) {
current_theme = QStringLiteral("default_dark");
QColor dark(25, 25, 25);
QString style_name;
if (CheckDarkMode()) {
// We check that the dark mode state is "On" and force a dark palette
if (UISettings::values.dark_mode_state == DarkModeState::On) {
// Set Default Windows Dark palette on Windows platforms to force Dark mode
themePalette.setColor(QPalette::Window, Qt::black);
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
themePalette.setColor(QPalette::Base, Qt::black);
themePalette.setColor(QPalette::AlternateBase, dark);
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
themePalette.setColor(QPalette::ToolTipText, Qt::black);
themePalette.setColor(QPalette::Text, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
themePalette.setColor(QPalette::Dark, QColor(128, 128, 128));
themePalette.setColor(QPalette::Shadow, Qt::white);
themePalette.setColor(QPalette::Button, Qt::black);
themePalette.setColor(QPalette::ButtonText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
themePalette.setColor(QPalette::BrightText, QColor(192, 192, 192));
themePalette.setColor(QPalette::Link, QColor(0, 140, 200));
themePalette.setColor(QPalette::Highlight, QColor(0, 85, 255));
themePalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(0, 85, 255));
themePalette.setColor(QPalette::HighlightedText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::white);
}
// AlternateBase is kept at rgb(233, 231, 227) or rgb(245, 245, 245) on Windows dark
// palette, fix this. Sometimes, it even is rgb(0, 0, 0), but uses a very light gray for
// alternate rows, do not know why
if (themePalette.alternateBase().color() == QColor(233, 231, 227) ||
themePalette.alternateBase().color() == QColor(245, 245, 245) ||
themePalette.alternateBase().color() == QColor(0, 0, 0)) {
themePalette.setColor(QPalette::AlternateBase, dark);
alternate_base_modified = true;
}
// Use fusion theme, since its close to windowsvista, but works well with a dark palette
style_name = QStringLiteral("fusion");
} else {
QIcon::setThemeName(current_theme);
QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons")));
AdjustLinkColor();
// Reset AlternateBase if it has been modified
if (alternate_base_modified) {
themePalette.setColor(QPalette::AlternateBase, QColor(245, 245, 245));
alternate_base_modified = false;
}
// Reset light palette
themePalette = this->style()->standardPalette();
// Reset Windows theme to the default
style_name = QStringLiteral("windowsvista");
}
LOG_DEBUG(Frontend, "Using style: {}", style_name.toStdString());
qApp->setStyle(style_name);
#else
if (CheckDarkMode()) {
// Set Dark palette on non Windows platforms (that may not have a dark palette)
themePalette.setColor(QPalette::Window, QColor(53, 53, 53));
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
themePalette.setColor(QPalette::Base, QColor(42, 42, 42));
themePalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66));
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
themePalette.setColor(QPalette::ToolTipText, QColor(53, 53, 53));
themePalette.setColor(QPalette::Text, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
themePalette.setColor(QPalette::Dark, QColor(35, 35, 35));
themePalette.setColor(QPalette::Shadow, QColor(20, 20, 20));
themePalette.setColor(QPalette::Button, QColor(53, 53, 53));
themePalette.setColor(QPalette::ButtonText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
themePalette.setColor(QPalette::BrightText, Qt::red);
themePalette.setColor(QPalette::Link, QColor(42, 130, 218));
themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
themePalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
themePalette.setColor(QPalette::HighlightedText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
} else {
// Reset light palette
themePalette = this->style()->standardPalette();
}
#endif
if (current_theme != default_theme) {
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
QFile f(theme_uri);
if (!f.open(QFile::ReadOnly | QFile::Text)) {
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
UISettings::values.theme);
current_theme = default_theme;
qApp->setPalette(themePalette);
AdjustLinkColor();
}
void GMainWindow::SetCustomStylesheet() {
setStyleSheet(QStringLiteral("QStatusBar::item { border: none; }"));
// Set "dark" qss property value, that may be used in stylesheets
bool is_dark_mode = CheckDarkMode();
if (renderer_status_button) {
renderer_status_button->setProperty("dark", is_dark_mode);
}
if (gpu_accuracy_button) {
gpu_accuracy_button->setProperty("dark", is_dark_mode);
}
#ifdef _WIN32
// Windows dark mode uses "fusion" style. Make it look like more "windowsvista" light style
if (is_dark_mode) {
/* the groove expands to the size of the slider by default. by giving it a height, it has a
fixed size */
/* handle is placed by default on the contents rect of the groove. Negative margin expands
it outside the groove */
setStyleSheet(QStringLiteral("QSlider:horizontal{ height:30px; }\
QSlider::sub-page:horizontal { background-color: palette(highlight); }\
QSlider::add-page:horizontal { background-color: palette(midlight);}\
QSlider::groove:horizontal { border-width: 1px; margin: 1px 0; height: 2px;}\
QSlider::handle:horizontal { border-width: 1px; border-style: solid; border-color: palette(dark);\
width: 10px; margin: -10px 0px; }\
QSlider::handle { background-color: palette(button); }\
QSlider::handle:hover { background-color: palette(highlight); }"));
}
#endif
}
#ifdef __unix__
bool GMainWindow::ListenColorSchemeChange() {
auto bus = QDBusConnection::sessionBus();
if (bus.isConnected()) {
const QString dbus_service = QStringLiteral("org.freedesktop.portal.Desktop");
const QString dbus_path = QStringLiteral("/org/freedesktop/portal/desktop");
const QString dbus_interface = QStringLiteral("org.freedesktop.portal.Settings");
const QString dbus_method = QStringLiteral("SettingChanged");
QStringList dbus_arguments;
dbus_arguments << QStringLiteral("org.freedesktop.appearance")
<< QStringLiteral("color-scheme");
const QString dbus_signature = QStringLiteral("ssv");
LOG_INFO(Frontend, "Connected to DBus, listening for OS theme changes");
return bus.connect(dbus_service, dbus_path, dbus_interface, dbus_method, dbus_arguments,
dbus_signature, this, SLOT(UpdateUITheme()));
}
LOG_WARNING(Frontend, "Unable to connect to DBus to listen for OS theme changes");
return false;
}
#endif
bool GMainWindow::CheckDarkMode() {
bool is_dark_mode_auto;
#ifdef _WIN32
// Dark mode cannot be changed after the app started on Windows
is_dark_mode_auto = qgetenv("QT_QPA_PLATFORM").contains("darkmode=2");
#else
is_dark_mode_auto = UISettings::values.dark_mode_state == DarkModeState::Auto;
#endif
if (!is_dark_mode_auto) {
return UISettings::values.dark_mode_state == DarkModeState::On;
} else {
const QPalette current_palette(qApp->palette());
#ifdef __unix__
QProcess process;
// Using the freedesktop specifications for checking dark mode
LOG_DEBUG(Frontend, "Retrieving theme from freedesktop color-scheme...");
QStringList gdbus_arguments;
gdbus_arguments << QStringLiteral("--dest=org.freedesktop.portal.Desktop")
<< QStringLiteral("--object-path /org/freedesktop/portal/desktop")
<< QStringLiteral("--method org.freedesktop.portal.Settings.Read")
<< QStringLiteral("org.freedesktop.appearance color-scheme");
process.start(QStringLiteral("gdbus call --session"), gdbus_arguments);
process.waitForFinished(1000);
QByteArray dbus_output = process.readAllStandardOutput();
if (!dbus_output.isEmpty()) {
const int systemColorSchema = QString::fromUtf8(dbus_output).trimmed().right(1).toInt();
return systemColorSchema == 1;
}
// Try alternative for Gnome if the previous one failed
QStringList gsettings_arguments;
gsettings_arguments << QStringLiteral("get")
<< QStringLiteral("org.gnome.desktop.interface")
<< QStringLiteral("color-scheme");
LOG_DEBUG(Frontend, "failed, retrieving theme from gsettings color-scheme...");
process.start(QStringLiteral("gsettings"), gsettings_arguments);
process.waitForFinished(1000);
QByteArray gsettings_output = process.readAllStandardOutput();
// Try older gtk-theme method if the previous one failed
if (gsettings_output.isEmpty()) {
LOG_DEBUG(Frontend, "failed, retrieving theme from gtk-theme...");
gsettings_arguments.takeLast();
gsettings_arguments << QStringLiteral("gtk-theme");
process.start(QStringLiteral("gsettings"), gsettings_arguments);
process.waitForFinished(1000);
gsettings_output = process.readAllStandardOutput();
}
// Interpret gsettings value if it succeeded
if (!gsettings_output.isEmpty()) {
QString systeme_theme = QString::fromUtf8(gsettings_output);
LOG_DEBUG(Frontend, "Gsettings output: {}", systeme_theme.toStdString());
return systeme_theme.contains(QStringLiteral("dark"), Qt::CaseInsensitive);
}
LOG_DEBUG(Frontend, "failed, retrieving theme from palette");
#endif
// Use default method based on palette swap by OS. It is the only method on Windows with
// Qt 5. Windows needs QT_QPA_PLATFORM env variable set to windows:darkmode=2 to force
// palette change
return (current_palette.color(QPalette::WindowText).lightness() >
current_palette.color(QPalette::Window).lightness());
}
}
void GMainWindow::changeEvent(QEvent* event) {
// PaletteChange event appears to only reach so far into the GUI, explicitly asking to
// UpdateUITheme is a decent work around
if (event->type() == QEvent::PaletteChange ||
event->type() == QEvent::ApplicationPaletteChange) {
LOG_DEBUG(Frontend,
"Window color palette changed by event: {} (QEvent::PaletteChange is: {})",
event->type(), QEvent::PaletteChange);
const QPalette test_palette(qApp->palette());
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
if (last_window_color != window_color) {
last_window_color = window_color;
UpdateUITheme();
}
}
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
QFile f(theme_uri);
if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
setStyleSheet(ts.readAll());
} else {
LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
UISettings::values.theme);
qApp->setStyleSheet({});
setStyleSheet({});
}
QWidget::changeEvent(event);
}
void GMainWindow::LoadTranslation() {
@@ -5136,26 +5412,6 @@ void GMainWindow::SetGamemodeEnabled(bool state) {
}
#endif
void GMainWindow::changeEvent(QEvent* event) {
#ifdef __unix__
// PaletteChange event appears to only reach so far into the GUI, explicitly asking to
// UpdateUITheme is a decent work around
if (event->type() == QEvent::PaletteChange) {
const QPalette test_palette(qApp->palette());
const QString current_theme = QString::fromStdString(UISettings::values.theme);
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
static QColor last_window_color;
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
if (last_window_color != window_color && (current_theme == QStringLiteral("default") ||
current_theme == QStringLiteral("colorful"))) {
UpdateUITheme();
}
last_window_color = window_color;
}
#endif // __unix__
QWidget::changeEvent(event);
}
Service::AM::FrontendAppletParameters GMainWindow::ApplicationAppletParameters() {
return Service::AM::FrontendAppletParameters{
.applet_id = Service::AM::AppletId::Application,
@@ -5282,6 +5538,31 @@ int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName(QStringLiteral("yuzu"));
#ifdef _WIN32
QByteArray current_qt_qpa = qgetenv("QT_QPA_PLATFORM");
// Follow dark mode setting, if the "-platform" launch option is not set.
// Otherwise, just follow dark mode for the window decoration (title bar).
if (!current_qt_qpa.contains(":darkmode=")) {
if (UISettings::values.dark_mode_state == DarkModeState::Auto) {
// When setting is Auto, force adapting window decoration and stylesheet palette to use
// Windows theme. Default is darkmode:0, which always uses light palette
if (current_qt_qpa.isEmpty()) {
// Set the value
qputenv("QT_QPA_PLATFORM", QByteArray("windows:darkmode=2"));
} else {
// Concatenate to the existing value
qputenv("QT_QPA_PLATFORM", current_qt_qpa + ",darkmode=2");
}
} else {
// When setting is no Auto, adapt window decoration to the palette used
if (current_qt_qpa.isEmpty()) {
// Set the value
qputenv("QT_QPA_PLATFORM", QByteArray("windows:darkmode=1"));
} else {
// Concatenate to the existing value
qputenv("QT_QPA_PLATFORM", current_qt_qpa + ",darkmode=1");
}
}
}
// Increases the maximum open file limit to 8192
_setmaxstdio(8192);
#endif
+38 -5
View File
@@ -23,9 +23,8 @@
#include "yuzu/util/controller_navigation.h"
#ifdef __unix__
#include <QSocketNotifier>
#include <QVariant>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QtDBus>
#endif
class QtConfig;
@@ -166,7 +165,7 @@ class GMainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
static bool CheckDarkMode();
explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
~GMainWindow() override;
@@ -258,12 +257,44 @@ private:
void SetDefaultUIGeometry();
void RestoreUIState();
/**
* Load the icons used by the current theme. Use dark icons if the current mode is dark
*/
void UpdateIcons(const QString& theme_used);
/**
* Set the palette used by the stylsheet for the dark/light mode selected, according to the OS
*/
void UpdateThemePalette();
/**
* Try to load a stylesheet from its URI.
* If the path starts with ":/", its embedded in the app, otherwise its in a local directory
* @returns true if the text file could be opened as read-only
*/
bool TryLoadStylesheet(const QString& theme_uri);
/**
* Try to load a stylesheet from filesystem path
* @returns true if the text file could be opened as read-only
*/
bool TryLoadStylesheet(const std::filesystem::path& theme_path);
/**
* Default customizations to the stylesheets
*/
void SetCustomStylesheet();
#ifdef __unix__
/**
* Create a signal to update the UI theme when the OS color scheme is changed
* @returns true if we could connect to dbus
*/
bool ListenColorSchemeChange();
#endif
void ConnectWidgetEvents();
void ConnectMenuEvents();
void UpdateMenuState();
#ifdef __unix__
void SetupPrepareForSleep();
#endif
void PreventOSSleep();
void AllowOSSleep();
@@ -396,6 +427,7 @@ private slots:
void ResetWindowSize720();
void ResetWindowSize900();
void ResetWindowSize1080();
void UpdateUITheme();
void OnAlbum();
void OnCabinet(Service::NFP::CabinetMode mode);
void OnMiiEdit();
@@ -441,7 +473,7 @@ private:
void OpenURL(const QUrl& url);
void LoadTranslation();
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
bool CheckDarkMode();
bool CheckSystemArchiveDecryption();
bool CheckFirmwarePresence();
void SetFirmwareVersion();
void ConfigureFilesystemProvider(const std::string& filepath);
@@ -525,7 +557,8 @@ private:
QTimer update_input_timer;
QString startup_icon_theme;
bool os_dark_mode = false;
bool alternate_base_modified = false;
QColor last_window_color;
// FS
std::shared_ptr<FileSys::VfsFilesystem> vfs;
+1
View File
@@ -5,6 +5,7 @@
#ifdef _WIN32
#include <cstring>
#include <QByteArray>
#include <processthreadsapi.h>
#include <windows.h>
#elif defined(YUZU_UNIX)
+5 -11
View File
@@ -22,21 +22,15 @@ namespace FS = Common::FS;
namespace UISettings {
const Themes themes{{
const Themes included_themes{{
{"Default", "default"},
{"Default Colorful", "colorful"},
{"Dark", "qdarkstyle"},
{"Dark Colorful", "colorful_dark"},
{"Default monochrome", "monochrome"},
{"Mine Shaft", "qdarkstyle"},
{"Mine Shaft monochrome", "qdarkstyle_monochrome"},
{"Midnight Blue", "qdarkstyle_midnight_blue"},
{"Midnight Blue Colorful", "colorful_midnight_blue"},
{"Midnight Blue monochrome", "qdarkstyle_midnight_blue_monochrome"},
}};
bool IsDarkTheme() {
const auto& theme = UISettings::values.theme;
return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") ||
theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue");
}
Values values = {};
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
+6 -20
View File
@@ -18,6 +18,7 @@
using Settings::Category;
using Settings::ConfirmStop;
using Settings::DarkModeState;
using Settings::Setting;
using Settings::SwitchableSetting;
@@ -35,8 +36,6 @@ extern template class Setting<unsigned long long>;
namespace UISettings {
bool IsDarkTheme();
struct ContextualShortcut {
std::string keyseq;
std::string controller_keyseq;
@@ -50,25 +49,10 @@ struct Shortcut {
ContextualShortcut shortcut;
};
enum class Theme {
Default,
DefaultColorful,
Dark,
DarkColorful,
MidnightBlue,
MidnightBlueColorful,
};
static constexpr Theme default_theme{
#ifdef _WIN32
Theme::DarkColorful
#else
Theme::DefaultColorful
#endif
};
static constexpr std::string_view default_theme{"default"};
using Themes = std::array<std::pair<const char*, const char*>, 6>;
extern const Themes themes;
extern const Themes included_themes;
struct GameDir {
std::string path;
@@ -160,7 +144,8 @@ struct Values {
QStringList recent_files;
Setting<std::string> language{linkage, {}, "language", Category::Paths};
std::string theme;
QString theme;
DarkModeState dark_mode_state;
// Shortcut name <Shortcut, context>
std::vector<Shortcut> shortcuts;
@@ -277,3 +262,4 @@ Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
Q_DECLARE_METATYPE(Settings::DarkModeState);