From ca42b4ca1c7a9319b7fbc17cabab0cbb4120f0fb Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 14 Oct 2024 18:24:05 -0500 Subject: [PATCH] gtk: use CSS variables and color calcs introduced in 4.16 This takes advantage of CSS variables and color expressions to improve the `window-theme=ghostty` support. The only visibile difference from the previous implementation is that the header bar will darken if the Ghostty window is in the background, which is standard for GTK apps. This is conditional at runtime. If Ghostty detects that you're running against GTK 4.16 or newer it will use the CSS variables and color calcs. If you're running against older versions it will use CSS classes to achieve nearly the same effect. --- src/apprt/gtk/App.zig | 85 ++++++++++++++++++++++++++-------------- src/apprt/gtk/Window.zig | 6 ++- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 155702296..16febf779 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -852,45 +852,72 @@ fn syncActionAccelerator( } fn loadRuntimeCss(config: *const Config, provider: *c.GtkCssProvider) !void { + var buf: [4096]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + const writer = fbs.writer(); + + const window_theme = config.@"window-theme"; const unfocused_fill: Config.Color = config.@"unfocused-split-fill" orelse config.background; const headerbar_background = config.background; const headerbar_foreground = config.foreground; - const fmt = + try writer.print( \\widget.unfocused-split {{ \\ opacity: {d:.2}; \\ background-color: rgb({d},{d},{d}); \\}} - \\window.window-theme-ghostty .top-bar, - \\window.window-theme-ghostty .bottom-bar, - \\window.window-theme-ghostty box > tabbar {{ - \\ background-color: rgb({d},{d},{d}); - \\ color: rgb({d},{d},{d}); - \\}} - ; - // The length required is always less than the length of the pre-formatted string: - // -> '{d:.2}' gets replaced with max 4 bytes (0.00) - // -> each {d} could be replaced with max 3 bytes - var css_buf: [fmt.len]u8 = undefined; + , .{ + 1.0 - config.@"unfocused-split-opacity", + unfocused_fill.r, + unfocused_fill.g, + unfocused_fill.b, + }); + + // this is specifically a runtime-only check + if (version.atLeast(4, 16, 0)) { + switch (window_theme) { + .ghostty => try writer.print( + \\:root {{ + \\ --headerbar-fg-color: rgb({d},{d},{d}); + \\ --headerbar-bg-color: rgb({d},{d},{d}); + \\ --headerbar-backdrop-color: oklab(from var(--headerbar-bg-color) calc(l * 0.9) a b / alpha); + \\}} + , + .{ + headerbar_foreground.r, + headerbar_foreground.g, + headerbar_foreground.b, + headerbar_background.r, + headerbar_background.g, + headerbar_background.b, + }, + ), + else => {}, + } + } else { + try writer.print( + \\window.window-theme-ghostty .top-bar, + \\window.window-theme-ghostty .bottom-bar, + \\window.window-theme-ghostty box > tabbar {{ + \\ background-color: rgb({d},{d},{d}); + \\ color: rgb({d},{d},{d}); + \\}} + , + .{ + headerbar_background.r, + headerbar_background.g, + headerbar_background.b, + headerbar_foreground.r, + headerbar_foreground.g, + headerbar_foreground.b, + }, + ); + } + + const css = fbs.getWritten(); - const css = try std.fmt.bufPrintZ( - &css_buf, - fmt, - .{ - 1.0 - config.@"unfocused-split-opacity", - unfocused_fill.r, - unfocused_fill.g, - unfocused_fill.b, - headerbar_background.r, - headerbar_background.g, - headerbar_background.b, - headerbar_foreground.r, - headerbar_foreground.g, - headerbar_foreground.b, - }, - ); // Clears any previously loaded CSS from this provider - c.gtk_css_provider_load_from_data(provider, css, @intCast(css.len)); + c.gtk_css_provider_load_from_data(provider, css.ptr, @intCast(css.len)); } /// Called by CoreApp to wake up the event loop. diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 9f5bc0bad..14bb68b8e 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -23,6 +23,7 @@ const c = @import("c.zig").c; const adwaita = @import("adwaita.zig"); const gtk_key = @import("key.zig"); const Notebook = @import("notebook.zig").Notebook; +const version = @import("version.zig"); const log = std.log.scoped(.gtk); @@ -108,8 +109,9 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_window_set_icon_name(gtk_window, "com.mitchellh.ghostty"); - // Apply class to color headerbar if window-theme is set to `ghostty`. - if (app.config.@"window-theme" == .ghostty) { + // Apply class to color headerbar if window-theme is set to `ghostty` and + // GTK version is before 4.16. + if (!version.atLeast(4, 16, 0) and app.config.@"window-theme" == .ghostty) { c.gtk_widget_add_css_class(@ptrCast(gtk_window), "window-theme-ghostty"); }