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

Implement loading custom css in the GTK app #4200

Merged
merged 4 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
55 changes: 54 additions & 1 deletion src/apprt/gtk/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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),

maciekbartczak marked this conversation as resolved.
Show resolved Hide resolved
maciekbartczak marked this conversation as resolved.
Show resolved Hide resolved
/// The timer used to quit the application after the last window is closed.
quit_timer: union(enum) {
off: void,
Expand Down Expand Up @@ -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),
};
maciekbartczak marked this conversation as resolved.
Show resolved Hide resolved
maciekbartczak marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -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();
}

Expand Down Expand Up @@ -892,14 +901,20 @@ 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(
"out of memory loading runtime CSS, no runtime CSS applied",
.{},
),
};
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
Expand Down Expand Up @@ -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;
};
maciekbartczak marked this conversation as resolved.
Show resolved Hide resolved

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;
Expand Down
8 changes: 8 additions & 0 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 = .{},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was doing some testing and I had a crash if I didn't explicitly specify an absolute path to the custom CSS file, so for some reason the paths are not being expanded properly.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3811 should fix expansion not sure about the crash

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3811 should fix expansion not sure about the crash

The crash is specifically because we have an assert in place to check for non-absolute paths. And #3811 won't fix it because I wasn't using a ~, just a plain filename --gtk-custom-css=custom.css. RepeatablePath should resolve that relative to Ghostty's config dir I thought but it obviously didn't.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I think that I figured out my problem, if you specify --background-image=custom.css on the CLI, Ghostty tries to resolve that based on the $PWD. If it can't be resolved, I think that the code leaves it as is without any warnings and then we eventually get a panic. So that's not a blocker for this (even though Mitchell has already merged this).


/// 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,
Expand Down