Skip to content

Commit

Permalink
Bug 1527048 - [Linux/GTK] Fallback to Adwaita light GTK theme for con…
Browse files Browse the repository at this point in the history
…tent when system theme has no light variant, r=karlt a=jcristau

We previously disabled system dark themes for Firefox chrome and content by gtk-application-prefer-dark-theme settings.
That option is no longer preferred by gnome project and was removed from tweaks tools. Theme makers are encouraged to use
a different name for the dark theme variants, like Adwaita-dark, Yaru-dark and so on. This option also does not
work when the GTK theme is missing the light variant completely.

To address that, this patch implements heuristics based on https://www.w3.org/TR/AERT/#color-contrast
to check if the system GTK theme has good contrast/visibility with default HTML colors
(white background and black text).

If widget.content.gtk-theme-override is empty and the system theme fails the test with gtk-application-prefer-dark-theme set to false, then Adwaita:light theme is used.

This patch was tested with some distro default light themes (Ambiance, Radiance, Yaru - Ubuntu, Arc - KDE, Menta - MATE)
and dark/light themes are recognized correctly.

Differential Revision: https://phabricator.services.mozilla.com/D29823

--HG--
extra : source : 03970b83ea43d59feb24a24dc37f33a5460b61da
  • Loading branch information
stransky committed May 28, 2019
1 parent 62bb0e2 commit 64632a7
Showing 1 changed file with 94 additions and 31 deletions.
125 changes: 94 additions & 31 deletions widget/gtk/nsLookAndFeel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,83 @@ bool nsLookAndFeel::GetFontImpl(FontID aID, nsString& aFontName,
return true;
}

const gchar* dark_theme_setting = "gtk-application-prefer-dark-theme";
static bool SystemPrefersDarkVariant(GtkSettings* aSettings) {
gboolean darkThemeDefault;
g_object_get(aSettings, dark_theme_setting, &darkThemeDefault, nullptr);
return darkThemeDefault;
}

// Check color contrast according to
// https://www.w3.org/TR/AERT/#color-contrast
static bool HasGoodContrastVisibility(GdkRGBA& aColor1, GdkRGBA& aColor2) {
int32_t luminosityDifference = NS_LUMINOSITY_DIFFERENCE(
GDK_RGBA_TO_NS_RGBA(aColor1), GDK_RGBA_TO_NS_RGBA(aColor2));
if (luminosityDifference < NS_SUFFICIENT_LUMINOSITY_DIFFERENCE) {
return false;
}

double colorDifference = std::abs(aColor1.red - aColor2.red) +
std::abs(aColor1.green - aColor2.green) +
std::abs(aColor1.blue - aColor2.blue);
return (colorDifference * 255.0 > 500.0);
}

// Check if the foreground/background colors match with default white/black
// html page colors.
static bool IsGtkThemeCompatibleWithHTMLColors() {
GdkRGBA white = {1.0, 1.0, 1.0};
GdkRGBA black = {0.0, 0.0, 0.0};

GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW);

GdkRGBA textColor;
gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &textColor);

// Theme text color and default white html page background
if (!HasGoodContrastVisibility(textColor, white)) {
return false;
}

GdkRGBA backgroundColor;
gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL,
&backgroundColor);

// Theme background color and default white html page background
if (HasGoodContrastVisibility(backgroundColor, white)) {
return false;
}

// Theme background color and default black text color
return HasGoodContrastVisibility(backgroundColor, black);
}

static void ConfigureContentGtkTheme() {
GtkSettings* settings = gtk_settings_get_for_screen(gdk_screen_get_default());
nsAutoCString contentThemeName;
mozilla::Preferences::GetCString("widget.content.gtk-theme-override",
contentThemeName);
if (!contentThemeName.IsEmpty()) {
g_object_set(settings, "gtk-theme-name", contentThemeName.get(), nullptr);
}

// Dark theme is active but user explicitly enables it so we're done now.
if (mozilla::Preferences::GetBool("widget.content.allow-gtk-dark-theme",
false)) {
return;
}

// Try to disable 'gtk-application-prefer-dark-theme' first...
if (SystemPrefersDarkVariant(settings)) {
g_object_set(settings, dark_theme_setting, FALSE, nullptr);
}

// ...and use a default Gtk theme as a fallback.
if (contentThemeName.IsEmpty() && !IsGtkThemeCompatibleWithHTMLColors()) {
g_object_set(settings, "gtk-theme-name", "Adwaita", nullptr);
}
}

void nsLookAndFeel::EnsureInit() {
GdkColor colorValue;
GdkColor* colorValuePtr;
Expand Down Expand Up @@ -865,38 +942,24 @@ void nsLookAndFeel::EnsureInit() {
(RelativeLuminanceUtils::Compute(GDK_RGBA_TO_NS_RGBA(bg)) <
RelativeLuminanceUtils::Compute(GDK_RGBA_TO_NS_RGBA(fg)));

// Dark themes interacts poorly with widget styling (see bug 1216658).
// We disable dark themes by default for all processes (chrome, web content)
// but allow user to overide it by prefs.
const gchar* dark_setting = "gtk-application-prefer-dark-theme";
gboolean darkThemeDefault;
g_object_get(settings, dark_setting, &darkThemeDefault, nullptr);

// To avoid triggering reload of theme settings unnecessarily, only set the
// setting when necessary.
if (darkThemeDefault) {
bool allowDarkTheme;
if (XRE_IsContentProcess()) {
allowDarkTheme = mozilla::Preferences::GetBool(
"widget.content.allow-gtk-dark-theme", false);
} else {
allowDarkTheme = (PR_GetEnv("MOZ_ALLOW_GTK_DARK_THEME") != nullptr) ||
mozilla::Preferences::GetBool(
"widget.chrome.allow-gtk-dark-theme", false);
}
if (!allowDarkTheme) {
g_object_set(settings, dark_setting, FALSE, nullptr);
}
}

// Allow content Gtk theme override by pref, it's useful when styled Gtk+
// widgets break web content.
if (XRE_IsContentProcess()) {
nsAutoCString contentThemeName;
mozilla::Preferences::GetCString("widget.content.gtk-theme-override",
contentThemeName);
if (!contentThemeName.IsEmpty()) {
g_object_set(settings, "gtk-theme-name", contentThemeName.get(), nullptr);
// Dark themes interacts poorly with widget styling (see bug 1216658).
// We disable dark themes by default for web content
// but allow user to overide it by prefs.
ConfigureContentGtkTheme();
} else {
// To avoid triggering reload of theme settings unnecessarily, only set the
// setting when necessary.
GtkSettings* settings =
gtk_settings_get_for_screen(gdk_screen_get_default());
if (SystemPrefersDarkVariant(settings)) {
bool allowDarkTheme =
(PR_GetEnv("MOZ_ALLOW_GTK_DARK_THEME") != nullptr) ||
mozilla::Preferences::GetBool("widget.chrome.allow-gtk-dark-theme",
false);
if (!allowDarkTheme) {
g_object_set(settings, dark_theme_setting, FALSE, nullptr);
}
}
}

Expand Down

0 comments on commit 64632a7

Please sign in to comment.