Linux: handle dark system themes nicely

yuzu's default theme doesn't specify everything, which is fine for
windows, but in linux anything unspecified is set to the users theme.

Symptoms of this are that a linux user with a dark theme won't think
to change the theme to a dark theme when first using yuzu

Idea here is to try and support arbitrary themes on linux.

preliminary work on a "default_dark" theme, used only as overlay
for any themes that are measured to be dark mode.

Other work done:

FreeDesktop standard icon names:
plus -> list-add
delete refresh, we use view-refresh

remove duplicated icons for qdarkstyle_midnight_blue
referencing icon aliases in the qrc files is the way to go

Note:
Dynamic style changing doesn't appear to work with AppImage
This commit is contained in:
Kyle K 2022-07-28 07:50:20 -07:00
parent b528c4f3af
commit 6af8034f3e
32 changed files with 819 additions and 35 deletions

View file

@ -257,6 +257,18 @@ static QString PrettyProductName() {
return QSysInfo::prettyProductName();
}
bool GMainWindow::check_dark_mode() {
#ifdef __linux__
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 // __linux__
}
GMainWindow::GMainWindow(bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@ -274,6 +286,13 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
ui->setupUi(this);
statusBar()->hide();
// Check dark mode before a theme is loaded
os_dark_mode = check_dark_mode();
startup_icon_theme = QIcon::themeName();
// fallback can only be set once, colorful theme icons are okay on both light/dark
QIcon::setFallbackThemeName(QStringLiteral("colorful"));
QIcon::setFallbackSearchPaths(QStringList(QStringLiteral(":/icons")));
default_theme_paths = QIcon::themeSearchPaths();
UpdateUITheme();
@ -3930,8 +3949,21 @@ void GMainWindow::filterBarSetChecked(bool state) {
emit(OnToggleFilterBar());
}
static void AdjustLinkColor() {
QPalette new_pal(qApp->palette());
if (UISettings::IsDarkTheme()) {
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
} else {
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
}
if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) {
qApp->setPalette(new_pal);
}
}
void GMainWindow::UpdateUITheme() {
const QString default_theme = QStringLiteral("default");
const QString default_theme =
QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second);
QString current_theme = UISettings::values.theme;
QStringList theme_paths(default_theme_paths);
@ -3939,6 +3971,23 @@ void GMainWindow::UpdateUITheme() {
current_theme = default_theme;
}
#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(theme_paths);
if (check_dark_mode()) {
current_theme = QStringLiteral("default_dark");
}
} else {
QIcon::setThemeName(current_theme);
QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons")));
AdjustLinkColor();
}
#endif
if (current_theme != default_theme) {
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
QFile f(theme_uri);
@ -3961,17 +4010,6 @@ void GMainWindow::UpdateUITheme() {
qApp->setStyleSheet({});
setStyleSheet({});
}
QPalette new_pal(qApp->palette());
if (UISettings::IsDarkTheme()) {
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
} else {
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
}
qApp->setPalette(new_pal);
QIcon::setThemeName(current_theme);
QIcon::setThemeSearchPaths(theme_paths);
}
void GMainWindow::LoadTranslation() {
@ -4022,6 +4060,26 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
discord_rpc->Update();
}
void GMainWindow::changeEvent(QEvent* event) {
#ifdef __linux__
// 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 = 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 // __linux__
QWidget::changeEvent(event);
}
#ifdef main
#undef main
#endif