diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index f0ba41d3a..51249af69 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -70,9 +70,8 @@ x11_xkb: ?x11.Xkb = null, /// and initialization was successful. transient_cgroup_base: ?[]const u8 = null, -/// True if we have initialized runtime CSS. We only need to do this once for the application, but -/// we can't perform the intialization until we have created a window -runtime_css_intialized: bool = false, +/// CSS Provider for any styles based on ghostty configuration values +css_provider: *c.GtkCssProvider, pub fn init(core_app: *CoreApp, opts: Options) !App { _ = opts; @@ -272,6 +271,9 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { ); } + const css_provider = c.gtk_css_provider_new(); + try loadRuntimeCss(&config, css_provider); + return .{ .core_app = core_app, .app = app, @@ -284,6 +286,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // This means that another instance of the GTK app is running and // our "activate" call above will open a window. .running = c.g_application_get_is_remote(gapp) == 0, + .css_provider = css_provider, }; } @@ -331,6 +334,7 @@ pub fn reloadConfig(self: *App) !?*const Config { fn syncConfigChanges(self: *App) !void { try self.updateConfigErrors(); try self.syncActionAccelerators(); + try loadRuntimeCss(&self.config, self.css_provider); } /// This should be called whenever the configuration changes to update @@ -383,6 +387,23 @@ fn syncActionAccelerator( ); } +fn loadRuntimeCss(config: *const Config, provider: *c.GtkCssProvider) !void { + const fill: Config.Color = config.@"unfocused-split-fill" orelse config.background; + var css_buf: [128]u8 = undefined; + const css = try std.fmt.bufPrintZ( + &css_buf, + "widget.unfocused-split {{ opacity: {d:.2}; }}\n.ghostty-surface>stack {{ background-color: rgb({d},{d},{d});}}", + .{ + config.@"unfocused-split-opacity", + fill.r, + fill.g, + fill.b, + }, + ); + // Clears any previously loaded CSS from this provider + c.gtk_css_provider_load_from_string(provider, css); +} + /// Called by CoreApp to wake up the event loop. pub fn wakeup(self: App) void { _ = self; diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index b68d0370e..a151fe92a 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -72,34 +72,10 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_widget_set_opacity(@ptrCast(window), app.config.@"background-opacity"); } - if (!app.runtime_css_intialized) { - // Intialize runtime CSS. This CSS requires ghostty configuration values so we don't set it - // in style.css. We also have to have a window in order to add a style_context to a display, - // so we intialize after creation of our first window - app.runtime_css_intialized = true; - - const display = c.gdk_display_get_default(); - const provider = c.gtk_css_provider_new(); - defer c.g_object_unref(provider); - const fill: Color = app.config.@"unfocused-split-fill" orelse app.config.background; - var css_buf: [128]u8 = undefined; - - // We will add the unfocused-split class in our focus callbacks. We unconditionally add the - // background-color to the notebook stack because it only comes into play if we have an - // unfocused split - const css = try std.fmt.bufPrintZ( - &css_buf, - "widget.unfocused-split {{ opacity: {d:.2}; }}\nstack {{ background-color: rgb({d},{d},{d});}}", - .{ - app.config.@"unfocused-split-opacity", - fill.r, - fill.g, - fill.b, - }, - ); - c.gtk_css_provider_load_from_string(provider, css); - c.gtk_style_context_add_provider_for_display(display, @ptrCast(provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } + // Internally, GTK ensures that only one instance of this provider exists in the provider list + // for the display. + const display = c.gdk_display_get_default(); + c.gtk_style_context_add_provider_for_display(display, @ptrCast(app.css_provider), c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); // Use the new GTK4 header bar. We only create a header bar if we have // window decorations. @@ -131,6 +107,7 @@ pub fn init(self: *Window, app: *App) !void { // Create a notebook to hold our tabs. const notebook_widget = c.gtk_notebook_new(); + c.gtk_widget_add_css_class(notebook_widget, "ghostty-surface"); const notebook: *c.GtkNotebook = @ptrCast(notebook_widget); self.notebook = notebook; const notebook_tab_pos: c_uint = switch (app.config.@"gtk-tabs-location") {