mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
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:
@ -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.
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user