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.
This commit is contained in:
Jeffrey C. Ollie
2024-10-14 18:24:05 -05:00
parent 9f50ed07a5
commit ca42b4ca1c
2 changed files with 60 additions and 31 deletions

View File

@ -852,45 +852,72 @@ fn syncActionAccelerator(
} }
fn loadRuntimeCss(config: *const Config, provider: *c.GtkCssProvider) !void { 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 unfocused_fill: Config.Color = config.@"unfocused-split-fill" orelse config.background;
const headerbar_background = config.background; const headerbar_background = config.background;
const headerbar_foreground = config.foreground; const headerbar_foreground = config.foreground;
const fmt = try writer.print(
\\widget.unfocused-split {{ \\widget.unfocused-split {{
\\ opacity: {d:.2}; \\ opacity: {d:.2};
\\ background-color: rgb({d},{d},{d}); \\ background-color: rgb({d},{d},{d});
\\}} \\}}
\\window.window-theme-ghostty .top-bar, , .{
\\window.window-theme-ghostty .bottom-bar, 1.0 - config.@"unfocused-split-opacity",
\\window.window-theme-ghostty box > tabbar {{ unfocused_fill.r,
\\ background-color: rgb({d},{d},{d}); unfocused_fill.g,
\\ color: rgb({d},{d},{d}); unfocused_fill.b,
\\}} });
;
// The length required is always less than the length of the pre-formatted string: // this is specifically a runtime-only check
// -> '{d:.2}' gets replaced with max 4 bytes (0.00) if (version.atLeast(4, 16, 0)) {
// -> each {d} could be replaced with max 3 bytes switch (window_theme) {
var css_buf: [fmt.len]u8 = undefined; .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 // 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. /// Called by CoreApp to wake up the event loop.

View File

@ -23,6 +23,7 @@ const c = @import("c.zig").c;
const adwaita = @import("adwaita.zig"); const adwaita = @import("adwaita.zig");
const gtk_key = @import("key.zig"); const gtk_key = @import("key.zig");
const Notebook = @import("notebook.zig").Notebook; const Notebook = @import("notebook.zig").Notebook;
const version = @import("version.zig");
const log = std.log.scoped(.gtk); 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"); c.gtk_window_set_icon_name(gtk_window, "com.mitchellh.ghostty");
// Apply class to color headerbar if window-theme is set to `ghostty`. // Apply class to color headerbar if window-theme is set to `ghostty` and
if (app.config.@"window-theme" == .ghostty) { // 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"); c.gtk_widget_add_css_class(@ptrCast(gtk_window), "window-theme-ghostty");
} }