Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a style manager #731

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
9 changes: 2 additions & 7 deletions demo/GraniteDemo.vala
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ public class Granite.Demo : Gtk.Application {
var overlaybar_view = new OverlayBarView ();
var toast_view = new ToastView ();
var settings_uris_view = new SettingsUrisView ();
var style_manager_view = new StyleManagerView ();
var utils_view = new UtilsView ();
var placeholder = new WelcomeView ();
var dialogs_view = new DialogsView (window);
var application_view = new ApplicationView ();

var main_stack = new Gtk.Stack ();
main_stack.add_titled (placeholder, "placeholder", "Placeholder");
main_stack.add_titled (style_manager_view, "style_manager", "StyleManager");
main_stack.add_titled (accel_label_view, "accel_label", "AccelLabel");
main_stack.add_titled (css_view, "css", "Style Classes");
main_stack.add_titled (date_time_picker_view, "pickers", "Date & Time");
Expand Down Expand Up @@ -94,9 +96,6 @@ public class Granite.Demo : Gtk.Application {
shrink_start_child = false
};

var granite_settings = Granite.Settings.get_default ();
gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK;

window.child = paned;
window.set_default_size (900, 600);
window.set_size_request (750, 500);
Expand All @@ -105,10 +104,6 @@ public class Granite.Demo : Gtk.Application {

add_window (window);
window.show ();

granite_settings.notify["prefers-color-scheme"].connect (() => {
gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK;
});
}

public static int main (string[] args) {
Expand Down
51 changes: 51 additions & 0 deletions demo/Views/StyleManagerView.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

public class StyleManagerView : Gtk.Box {
construct {
var label = new Gtk.Label (_("Override the application theme:"));

var dont_button = new Gtk.CheckButton.with_label (_("Use system theme")) {
active = true
};

var force_light = new Gtk.CheckButton.with_label (_("Force light theme")) {
group = dont_button
};

var force_dark = new Gtk.CheckButton.with_label (_("Force dark theme")) {
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
group = force_light
};

halign = CENTER;
valign = CENTER;
orientation = VERTICAL;
spacing = 6;
append (label);
append (dont_button);
append (force_light);
append (force_dark);

var style_manager = Granite.StyleManager.get_default ();

dont_button.toggled.connect (() => {
if (dont_button.active) {
style_manager.color_scheme_override = NO_PREFERENCE;
}
});

force_light.toggled.connect (() => {
if (force_light.active) {
style_manager.color_scheme_override = LIGHT;
}
});

force_dark.toggled.connect (() => {
if (force_dark.active) {
style_manager.color_scheme_override = DARK;
}
});
}
}
1 change: 1 addition & 0 deletions demo/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ executable(
'Views/ModeButtonView.vala',
'Views/OverlayBarView.vala',
'Views/SettingsUrisView.vala',
'Views/StyleManagerView.vala',
'Views/ToastView.vala',
'Views/UtilsView.vala',
'Views/WelcomeView.vala',
Expand Down
73 changes: 1 addition & 72 deletions lib/Init.vala
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

namespace Granite {
private static bool initialized = false;
private static Gtk.CssProvider? base_provider = null;
private static Gtk.CssProvider? dark_provider = null;
private static Gtk.CssProvider? app_provider = null;

/**
* Initializes Granite.
Expand All @@ -23,78 +20,10 @@ namespace Granite {

typeof (Granite.Settings).ensure ();

unowned var display_manager = Gdk.DisplayManager.@get ();
display_manager.display_opened.connect (register_display);

foreach (unowned var display in display_manager.list_displays ()) {
register_display (display);
}
Granite.StyleManager.get_default (); // Make sure everything is being set up there

GLib.Intl.bindtextdomain (Granite.GETTEXT_PACKAGE, Granite.LOCALEDIR);
GLib.Intl.bind_textdomain_codeset (Granite.GETTEXT_PACKAGE, "UTF-8");
initialized = true;
}

private static void register_display (Gdk.Display display) {
var gtk_settings = Gtk.Settings.get_for_display (display);
gtk_settings.notify["gtk-application-prefer-dark-theme"].connect (() => {
set_provider_for_display (display, gtk_settings.gtk_application_prefer_dark_theme);
});

set_provider_for_display (display, gtk_settings.gtk_application_prefer_dark_theme);

var icon_theme = Gtk.IconTheme.get_for_display (display);
icon_theme.add_resource_path ("/io/elementary/granite");
}

private static void set_provider_for_display (Gdk.Display display, bool prefer_dark_style) {
if (app_provider == null) {
var base_path = Application.get_default ().resource_base_path;
if (base_path != null) {
var base_uri = "resource://" + base_path;
var base_file = File.new_for_uri (base_uri);

app_provider = init_provider_from_file (base_file.get_child ("Application.css"));
}

if (app_provider != null) {
Gtk.StyleContext.add_provider_for_display (display, app_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}

if (prefer_dark_style) {
if (base_provider != null) {
Gtk.StyleContext.remove_provider_for_display (display, base_provider);
}

if (dark_provider == null) {
dark_provider = new Gtk.CssProvider ();
dark_provider.load_from_resource ("/io/elementary/granite/Granite-dark.css");
}

Gtk.StyleContext.add_provider_for_display (display, dark_provider, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
} else {
if (dark_provider != null) {
Gtk.StyleContext.remove_provider_for_display (display, dark_provider);
}

if (base_provider == null) {
base_provider = new Gtk.CssProvider ();
base_provider.load_from_resource ("/io/elementary/granite/Granite.css");
}

Gtk.StyleContext.add_provider_for_display (display, base_provider, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
}
}

private static Gtk.CssProvider? init_provider_from_file (File file) {
if (file.query_exists ()) {
var provider = new Gtk.CssProvider ();
provider.load_from_file (file);

return provider;
}

return null;
}
}
125 changes: 125 additions & 0 deletions lib/StyleManager.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-2.0-or-later
*/

/**
* A class for managing the style of the application. This handles switching light and dark mode based
* based on system preference or application preference (see {@link color_scheme_override}), etc.
*/
public class Granite.StyleManager : Object {
private static Gtk.CssProvider? base_provider = null;
private static Gtk.CssProvider? dark_provider = null;
private static Gtk.CssProvider? app_provider = null;

private static GLib.Once<Granite.StyleManager> instance;
public static unowned Granite.StyleManager get_default () {
return instance.once (() => {
return new Granite.StyleManager ();
});
}

/**
* If this is set to NO_PREFERENCE the systems preferred color scheme will be used.
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
* Otherwise the color scheme set here will be used.
*/
public Settings.ColorScheme color_scheme_override { get; set; default = NO_PREFERENCE; }
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

private StyleManager () { }

construct {
unowned var display_manager = Gdk.DisplayManager.@get ();
display_manager.display_opened.connect (register_display);

foreach (unowned var display in display_manager.list_displays ()) {
register_display (display);
}

Granite.Settings.get_default ().notify["prefers-color-scheme"].connect (update_color_scheme);
update_color_scheme ();

notify["color-scheme-override"].connect (update_color_scheme);
}

private void register_display (Gdk.Display display) {
var gtk_settings = Gtk.Settings.get_for_display (display);
gtk_settings.gtk_application_prefer_dark_theme = prefers_dark ();
gtk_settings.notify["gtk-application-prefer-dark-theme"].connect (() => {
set_provider_for_display (display, gtk_settings.gtk_application_prefer_dark_theme);
});

set_provider_for_display (display, gtk_settings.gtk_application_prefer_dark_theme);

var icon_theme = Gtk.IconTheme.get_for_display (display);
icon_theme.add_resource_path ("/io/elementary/granite");
}

private void set_provider_for_display (Gdk.Display display, bool prefer_dark_style) {
if (app_provider == null) {
var base_path = Application.get_default ().resource_base_path;
if (base_path != null) {
var base_uri = "resource://" + base_path;
var base_file = File.new_for_uri (base_uri);

app_provider = init_provider_from_file (base_file.get_child ("Application.css"));
}

if (app_provider != null) {
Gtk.StyleContext.add_provider_for_display (display, app_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}

if (prefer_dark_style) {
if (base_provider != null) {
Gtk.StyleContext.remove_provider_for_display (display, base_provider);
}

if (dark_provider == null) {
dark_provider = new Gtk.CssProvider ();
dark_provider.load_from_resource ("/io/elementary/granite/Granite-dark.css");
}

Gtk.StyleContext.add_provider_for_display (display, dark_provider, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
} else {
if (dark_provider != null) {
Gtk.StyleContext.remove_provider_for_display (display, dark_provider);
}

if (base_provider == null) {
base_provider = new Gtk.CssProvider ();
base_provider.load_from_resource ("/io/elementary/granite/Granite.css");
}

Gtk.StyleContext.add_provider_for_display (display, base_provider, Gtk.STYLE_PROVIDER_PRIORITY_THEME);
}
}

private Gtk.CssProvider? init_provider_from_file (File file) {
if (file.query_exists ()) {
var provider = new Gtk.CssProvider ();
provider.load_from_file (file);

return provider;
}

return null;
}

private void update_color_scheme () {
var dark = prefers_dark ();

foreach (var display in Gdk.DisplayManager.@get ().list_displays ()) {
var gtk_settings = Gtk.Settings.get_for_display (display);
gtk_settings.gtk_application_prefer_dark_theme = dark;
}
}

private bool prefers_dark () {
if (color_scheme_override == NO_PREFERENCE) {
var granite_settings = Granite.Settings.get_default ();
return granite_settings.prefers_color_scheme == DARK;
} else {
return color_scheme_override == DARK;
}
}
}
1 change: 1 addition & 0 deletions lib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ libgranite_sources = files(
'DateTime.vala',
'Constants.vala',
'Init.vala',
'StyleManager.vala',

'Services/Application.vala',
'Services/AsyncMutex.vala',
Expand Down