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 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 2 additions & 20 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 All @@ -46,24 +48,11 @@ public class Granite.Demo : Gtk.Application {
main_stack.add_titled (dialogs_view, "dialogs", "Dialogs");
main_stack.add_titled (application_view, "application", "Application");

var gtk_settings = Gtk.Settings.get_default ();

var mode_switch = new Granite.ModeSwitch.from_icon_name (
"display-brightness-symbolic",
"weather-clear-night-symbolic"
) {
primary_icon_tooltip_text = ("Light background"),
secondary_icon_tooltip_text = ("Dark background"),
valign = CENTER
};
mode_switch.bind_property ("active", gtk_settings, "gtk-application-prefer-dark-theme", BIDIRECTIONAL);

var end_header = new Gtk.HeaderBar () {
show_title_buttons = false
};
end_header.add_css_class (Granite.STYLE_CLASS_FLAT);
end_header.pack_end (new Gtk.WindowControls (END));
end_header.pack_end (mode_switch);

var end_box = new Gtk.Box (VERTICAL, 0);
end_box.append (end_header);
Expand Down Expand Up @@ -94,9 +83,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 +91,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
70 changes: 2 additions & 68 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 @@ -24,77 +21,14 @@ namespace Granite {
typeof (Granite.Settings).ensure ();

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

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

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;
}
}
123 changes: 123 additions & 0 deletions lib/StyleManager.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* 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 HashTable<Gdk.Display, StyleManager>? style_managers_by_displays;

public static unowned StyleManager get_default () {
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
return style_managers_by_displays[Gdk.Display.get_default ()];
}

public static unowned StyleManager get_for_display (Gdk.Display display) {
return style_managers_by_displays[display];
}

internal static void init_for_display (Gdk.Display display) {
if (style_managers_by_displays == null) {
style_managers_by_displays = new HashTable<Gdk.Display, StyleManager> (null, null);
}

style_managers_by_displays[display] = new StyleManager (display);
}

/**
* 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.
* Default is NO_PREFERENCE.
*/
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
public Settings.ColorScheme color_scheme_override { get; set; default = NO_PREFERENCE; }
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

/**
* The Gdk.Display this StyleManager handles.
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
*/
public Gdk.Display display { get; construct; }

private StyleManager (Gdk.Display display) {
Object (display: display);
}

construct {
var gtk_settings = Gtk.Settings.get_for_display (display);
gtk_settings.notify["gtk-application-prefer-dark-theme"].connect (set_provider_for_display);
set_provider_for_display ();

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

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

private void set_provider_for_display () {
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 (Gtk.Settings.get_for_display (display).gtk_application_prefer_dark_theme) {
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 gtk_settings = Gtk.Settings.get_for_display (display);
if (color_scheme_override == NO_PREFERENCE) {
var granite_settings = Granite.Settings.get_default ();
gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == DARK;
} else {
gtk_settings.gtk_application_prefer_dark_theme = 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