Implement loading custom css in the GTK app

This commit is contained in:
Maciej Bartczak
2024-12-31 15:53:10 +01:00
parent a30b2eda39
commit 27c3382a6a
2 changed files with 62 additions and 1 deletions

View File

@ -81,6 +81,9 @@ transient_cgroup_base: ?[]const u8 = null,
/// CSS Provider for any styles based on ghostty configuration values
css_provider: *c.GtkCssProvider,
/// Providers for loading custom stylesheets defined by user
custom_css_providers: std.ArrayList(*c.GtkCssProvider),
/// The timer used to quit the application after the last window is closed.
quit_timer: union(enum) {
off: void,
@ -425,6 +428,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
// our "activate" call above will open a window.
.running = c.g_application_get_is_remote(gapp) == 0,
.css_provider = css_provider,
.custom_css_providers = std.ArrayList(*c.GtkCssProvider).init(core_app.alloc),
};
}
@ -441,6 +445,11 @@ pub fn terminate(self: *App) void {
if (self.context_menu) |context_menu| c.g_object_unref(context_menu);
if (self.transient_cgroup_base) |path| self.core_app.alloc.free(path);
for (self.custom_css_providers.items) |provider| {
c.g_object_unref(provider);
}
self.custom_css_providers.deinit();
self.config.deinit();
}
@ -892,7 +901,7 @@ fn syncConfigChanges(self: *App) !void {
try self.updateConfigErrors();
try self.syncActionAccelerators();
// Load our runtime CSS. If this fails then our window is just stuck
// Load our runtime and custom CSS. If this fails then our window is just stuck
// with the old CSS but we don't want to fail the entire sync operation.
self.loadRuntimeCss() catch |err| switch (err) {
error.OutOfMemory => log.warn(
@ -900,6 +909,12 @@ fn syncConfigChanges(self: *App) !void {
.{},
),
};
self.loadCustomCss() catch |err| switch (err) {
error.OutOfMemory => log.warn(
"out of memory loading custom CSS, no custom CSS applied",
.{},
),
};
}
/// This should be called whenever the configuration changes to update
@ -1040,6 +1055,44 @@ fn loadRuntimeCss(
);
}
fn loadCustomCss(self: *App) Allocator.Error!void {
const display = c.gdk_display_get_default();
// unload the previously loaded style providers
for (self.custom_css_providers.items) |provider| {
c.gtk_style_context_remove_provider_for_display(
display,
@ptrCast(provider),
);
c.g_object_unref(provider);
}
self.custom_css_providers.clearRetainingCapacity();
for (self.config.@"gtk-custom-css".value.items) |p| {
const path, const optional = switch (p) {
.optional => |path| .{ path, true },
.required => |path| .{ path, false },
};
std.fs.accessAbsolute(path, .{}) catch |err| {
if (err != error.FileNotFound or !optional) {
log.err("error opening gtk-custom-css file {s}: {}", .{ path, err });
}
continue;
};
const provider = c.gtk_css_provider_new();
c.gtk_style_context_add_provider_for_display(
display,
@ptrCast(provider),
c.GTK_STYLE_PROVIDER_PRIORITY_USER,
);
c.gtk_css_provider_load_from_path(provider, path);
try self.custom_css_providers.append(provider);
}
}
/// Called by CoreApp to wake up the event loop.
pub fn wakeup(self: App) void {
_ = self;

View File

@ -1925,6 +1925,14 @@ keybind: Keybinds = .{},
/// Adwaita support.
@"gtk-adwaita": bool = true,
/// Custom CSS files to be loaded.
///
/// This configuration can be repeated multiple times to load multiple files.
/// Prepend a ? character to the file path to suppress errors if the file does
/// not exist. If you want to include a file that begins with a literal ?
/// character, surround the file path in double quotes (").
@"gtk-custom-css": RepeatablePath = .{},
/// If `true` (default), applications running in the terminal can show desktop
/// notifications using certain escape sequences such as OSC 9 or OSC 777.
@"desktop-notifications": bool = true,