From aa2dbe29190f64a82f11b9381c4ad0626eabec2c Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Fri, 24 Jan 2025 16:26:15 +0100 Subject: [PATCH 1/2] gtk: apply all window appearance changes in syncAppearance The GTK side of appearance code is kind of a mess with several different functions all having the responsibility of interacting with each other and setting the appropriate window appearance. It should solely be the responsibility of the `syncAppearance` function to apply appearance changes, with other callbacks/functions calling it instead: much like what we already do for the macOS apprt. --- src/apprt/gtk/Window.zig | 265 ++++++++++++++++------------- src/apprt/gtk/winproto.zig | 16 +- src/apprt/gtk/winproto/noop.zig | 6 +- src/apprt/gtk/winproto/wayland.zig | 43 ++--- src/apprt/gtk/winproto/x11.zig | 39 ++--- 5 files changed, 180 insertions(+), 189 deletions(-) diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 0fd1e7429e..3b415435a5 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -31,6 +31,8 @@ const log = std.log.scoped(.gtk); app: *App, +config: DerivedConfig, + /// Our window window: *c.GtkWindow, @@ -55,6 +57,38 @@ adw_tab_overview_focus_timer: ?c.guint = null, /// State and logic for windowing protocol for a window. winproto: winproto.Window, +pub const DerivedConfig = struct { + background_opacity: f64, + background_blur: configpkg.Config.BackgroundBlur, + window_theme: configpkg.Config.WindowTheme, + gtk_titlebar: bool, + gtk_titlebar_hide_when_maximized: bool, + gtk_tabs_location: configpkg.Config.GtkTabsLocation, + gtk_wide_tabs: bool, + gtk_toolbar_style: configpkg.Config.GtkToolbarStyle, + + maximize: bool, + fullscreen: bool, + window_decoration: configpkg.Config.WindowDecoration, + + pub fn init(config: *const configpkg.Config) DerivedConfig { + return .{ + .background_opacity = config.@"background-opacity", + .background_blur = config.@"background-blur", + .window_theme = config.@"window-theme", + .gtk_titlebar = config.@"gtk-titlebar", + .gtk_titlebar_hide_when_maximized = config.@"gtk-titlebar-hide-when-maximized", + .gtk_tabs_location = config.@"gtk-tabs-location", + .gtk_wide_tabs = config.@"gtk-wide-tabs", + .gtk_toolbar_style = config.@"gtk-toolbar-style", + + .maximize = config.maximize, + .fullscreen = config.fullscreen, + .window_decoration = config.@"window-decoration", + }; + } +}; + pub fn create(alloc: Allocator, app: *App) !*Window { // Allocate a fixed pointer for our window. We try to minimize // allocations but windows and other GUI requirements are so minimal @@ -73,6 +107,7 @@ pub fn init(self: *Window, app: *App) !void { // Set up our own state self.* = .{ .app = app, + .config = DerivedConfig.init(&app.config), .window = undefined, .headerbar = undefined, .tab_overview = null, @@ -99,13 +134,6 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_window_set_icon_name(self.window, build_config.bundle_id); - // Apply class to color headerbar if window-theme is set to `ghostty` and - // GTK version is before 4.16. The conditional is because above 4.16 - // we use GTK CSS color variables. - if (!version.atLeast(4, 16, 0) and app.config.@"window-theme" == .ghostty) { - c.gtk_widget_add_css_class(gtk_widget, "window-theme-ghostty"); - } - // Create our box which will hold our widgets in the main content area. const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0); @@ -153,7 +181,7 @@ pub fn init(self: *Window, app: *App) !void { // If we're using an AdwWindow then we can support the tab overview. if (self.tab_overview) |tab_overview| { if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; - const btn = switch (app.config.@"gtk-tabs-location") { + const btn = switch (self.config.gtk_tabs_location) { .top, .bottom => btn: { const btn = c.gtk_toggle_button_new(); c.gtk_widget_set_tooltip_text(btn, "View Open Tabs"); @@ -188,7 +216,6 @@ pub fn init(self: *Window, app: *App) !void { self.headerbar.packStart(btn); } - _ = c.g_signal_connect_data(self.window, "notify::decorated", c.G_CALLBACK(>kWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(self.window, "notify::maximized", c.G_CALLBACK(>kWindowNotifyMaximized), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(self.window, "notify::fullscreened", c.G_CALLBACK(>kWindowNotifyFullscreened), self, null, c.G_CONNECT_DEFAULT); @@ -237,12 +264,6 @@ pub fn init(self: *Window, app: *App) !void { c.gtk_popover_set_has_arrow(@ptrCast(@alignCast(self.context_menu)), 0); c.gtk_widget_set_halign(self.context_menu, c.GTK_ALIGN_START); - // If we want the window to be maximized, we do that here. - if (app.config.maximize) c.gtk_window_maximize(self.window); - - // If we are in fullscreen mode, new windows start fullscreen. - if (app.config.fullscreen) c.gtk_window_fullscreen(self.window); - // We register a key event controller with the window so // we can catch key events when our surface may not be // focused (i.e. when the libadw tab overview is shown). @@ -265,14 +286,14 @@ pub fn init(self: *Window, app: *App) !void { c.adw_toolbar_view_add_top_bar(toolbar_view, self.headerbar.asWidget()); - if (self.app.config.@"gtk-tabs-location" != .hidden) { + if (self.config.gtk_tabs_location != .hidden) { const tab_bar = c.adw_tab_bar_new(); c.adw_tab_bar_set_view(tab_bar, @ptrCast(@alignCast(self.notebook.tab_view))); - if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); + if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0); const tab_bar_widget: *c.GtkWidget = @ptrCast(@alignCast(tab_bar)); - switch (self.app.config.@"gtk-tabs-location") { + switch (self.config.gtk_tabs_location) { .top => c.adw_toolbar_view_add_top_bar(toolbar_view, tab_bar_widget), .bottom => c.adw_toolbar_view_add_bottom_bar(toolbar_view, tab_bar_widget), .hidden => unreachable, @@ -280,7 +301,7 @@ pub fn init(self: *Window, app: *App) !void { } c.adw_toolbar_view_set_content(toolbar_view, box); - const toolbar_style: c.AdwToolbarStyle = switch (self.app.config.@"gtk-toolbar-style") { + const toolbar_style: c.AdwToolbarStyle = switch (self.config.gtk_toolbar_style) { .flat => c.ADW_TOOLBAR_FLAT, .raised => c.ADW_TOOLBAR_RAISED, .@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER, @@ -298,12 +319,12 @@ pub fn init(self: *Window, app: *App) !void { @ptrCast(@alignCast(self.tab_overview)), ); } else tab_bar: { - if (app.config.@"gtk-tabs-location" == .hidden) break :tab_bar; + if (self.config.gtk_tabs_location == .hidden) break :tab_bar; // In earlier adwaita versions, we need to add the tabbar manually since we do not use // an AdwToolbarView. const tab_bar: *c.AdwTabBar = c.adw_tab_bar_new().?; c.gtk_widget_add_css_class(@ptrCast(@alignCast(tab_bar)), "inline"); - switch (app.config.@"gtk-tabs-location") { + switch (self.config.gtk_tabs_location) { .top => c.gtk_box_insert_child_after( @ptrCast(box), @ptrCast(@alignCast(tab_bar)), @@ -317,9 +338,15 @@ pub fn init(self: *Window, app: *App) !void { } c.adw_tab_bar_set_view(tab_bar, @ptrCast(@alignCast(self.notebook.tab_view))); - if (!app.config.@"gtk-wide-tabs") c.adw_tab_bar_set_expand_tabs(tab_bar, 0); + if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0); } + // If we want the window to be maximized, we do that here. + if (self.config.maximize) c.gtk_window_maximize(self.window); + + // If we are in fullscreen mode, new windows start fullscreen. + if (self.config.fullscreen) c.gtk_window_fullscreen(self.window); + // Show the window c.gtk_widget_show(gtk_widget); } @@ -328,53 +355,89 @@ pub fn updateConfig( self: *Window, config: *const configpkg.Config, ) !void { - self.winproto.updateConfigEvent(config) catch |err| { - // We want to continue attempting to make the other config - // changes necessary so we just log the error and continue. - log.warn("failed to update window protocol config error={}", .{err}); - }; + self.config = DerivedConfig.init(config); // We always resync our appearance whenever the config changes. - try self.syncAppearance(config); + try self.syncAppearance(); } /// Updates appearance based on config settings. Will be called once upon window -/// realization, and every time the config is reloaded. +/// realization, every time the config is reloaded, and every time a window state +/// is toggled (un-/maximized, un-/fullscreened, window decorations toggled, etc.) /// /// TODO: Many of the initial style settings in `create` could possibly be made /// reactive by moving them here. -pub fn syncAppearance(self: *Window, config: *const configpkg.Config) !void { - self.winproto.syncAppearance() catch |err| { - log.warn("failed to sync winproto appearance error={}", .{err}); - }; +pub fn syncAppearance(self: *Window) !void { + const csd_enabled = self.winproto.clientSideDecorationEnabled(); + c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled)); + + // Fix any artifacting that may occur in window corners. The .ssd CSS + // class is defined in the GtkWindow documentation: + // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition + // for .ssd is provided by GTK and Adwaita. + toggleCssClass(@ptrCast(self.window), "csd", csd_enabled); + toggleCssClass(@ptrCast(self.window), "ssd", !csd_enabled); + toggleCssClass(@ptrCast(self.window), "no-border-radius", !csd_enabled); + + self.headerbar.setVisible(visible: { + // Never display the header bar when CSDs are disabled. + if (!csd_enabled) break :visible false; + + // Unconditionally disable the header bar when fullscreened. + if (self.config.fullscreen) break :visible false; + + // *Conditionally* disable the header bar when maximized, + // and gtk-titlebar-hide-when-maximized is set + if (self.config.maximize and self.config.gtk_titlebar_hide_when_maximized) + break :visible false; + + break :visible self.config.gtk_titlebar; + }); toggleCssClass( @ptrCast(self.window), "background", - config.@"background-opacity" >= 1, + self.config.background_opacity >= 1, ); - // If we are disabling CSDs then disable them right away. - const csd_enabled = self.winproto.clientSideDecorationEnabled(); - c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled)); - - // If we are not decorated then we hide the titlebar. - self.headerbar.setVisible(config.@"gtk-titlebar" and csd_enabled); + // Apply class to color headerbar if window-theme is set to `ghostty` and + // GTK version is before 4.16. The conditional is because above 4.16 + // we use GTK CSS color variables. + toggleCssClass( + @ptrCast(self.window), + "window-theme-ghostty", + !version.atLeast(4, 16, 0) and self.config.window_theme == .ghostty, + ); - // Disable the title buttons (close, maximize, minimize, ...) - // *inside* the tab overview if CSDs are disabled. - // We do spare the search button, though. if (self.tab_overview) |tab_overview| { if (!adwaita.versionAtLeast(1, 4, 0)) unreachable; - c.adw_tab_overview_set_show_start_title_buttons( - @ptrCast(tab_overview), - @intFromBool(csd_enabled), - ); - c.adw_tab_overview_set_show_end_title_buttons( - @ptrCast(tab_overview), - @intFromBool(csd_enabled), - ); + + // Disable the title buttons (close, maximize, minimize, ...) + // *inside* the tab overview if CSDs are disabled. + // We do spare the search button, though. + c.adw_tab_overview_set_show_start_title_buttons(@ptrCast(tab_overview), @intFromBool(csd_enabled)); + c.adw_tab_overview_set_show_end_title_buttons(@ptrCast(tab_overview), @intFromBool(csd_enabled)); + + // Update toolbar view style + const toolbar_view: *c.AdwToolbarView = @ptrCast(c.adw_tab_overview_get_child(@ptrCast(tab_overview))); + const toolbar_style: c.AdwToolbarStyle = switch (self.config.gtk_toolbar_style) { + .flat => c.ADW_TOOLBAR_FLAT, + .raised => c.ADW_TOOLBAR_RAISED, + .@"raised-border" => c.ADW_TOOLBAR_RAISED_BORDER, + }; + c.adw_toolbar_view_set_top_bar_style(toolbar_view, toolbar_style); + c.adw_toolbar_view_set_bottom_bar_style(toolbar_view, toolbar_style); } + + self.winproto.syncAppearance() catch |err| { + log.warn("failed to sync winproto appearance error={}", .{err}); + }; + + toggleCssClass( + @ptrCast(self.window), + "background", + self.config.background_opacity >= 1, + ); } fn toggleCssClass( @@ -518,30 +581,42 @@ pub fn toggleTabOverview(self: *Window) void { /// Toggle the maximized state for this window. pub fn toggleMaximize(self: *Window) void { - if (c.gtk_window_is_maximized(self.window) == 0) { - c.gtk_window_maximize(self.window); - } else { + if (self.config.maximize) { c.gtk_window_unmaximize(self.window); + } else { + c.gtk_window_maximize(self.window); } + // We update the config and call syncAppearance + // in the gtkWindowNotifyMaximized callback } /// Toggle fullscreen for this window. pub fn toggleFullscreen(self: *Window) void { - const is_fullscreen = c.gtk_window_is_fullscreen(self.window); - if (is_fullscreen == 0) { - c.gtk_window_fullscreen(self.window); - } else { + if (self.config.fullscreen) { c.gtk_window_unfullscreen(self.window); + } else { + c.gtk_window_fullscreen(self.window); } + // We update the config and call syncAppearance + // in the gtkWindowNotifyFullscreened callback } /// Toggle the window decorations for this window. pub fn toggleWindowDecorations(self: *Window) void { - self.app.config.@"window-decoration" = switch (self.app.config.@"window-decoration") { + self.config.window_decoration = switch (self.config.window_decoration) { + .none => switch (self.app.config.@"window-decoration") { + // If we started as none, then we switch to auto + .none => .auto, + // Switch back + .auto, .client, .server => |v| v, + }, + // Always set to none .auto, .client, .server => .none, - .none => .client, }; - self.updateConfig(&self.app.config) catch {}; + + self.syncAppearance() catch |err| { + log.err("failed to sync appearance={}", .{err}); + }; } /// Grabs focus on the currently selected tab. @@ -562,23 +637,22 @@ pub fn sendToast(self: *Window, title: [:0]const u8) void { c.adw_toast_overlay_add_toast(@ptrCast(self.toast_overlay), toast); } -fn gtkRealize(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { +fn gtkRealize(_: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { const self = userdataSelf(ud.?); // Initialize our window protocol logic if (winproto.Window.init( self.app.core_app.alloc, &self.app.winproto, - v, - &self.app.config, - )) |winproto_win| { - self.winproto = winproto_win; + self, + )) |wp| { + self.winproto = wp; } else |err| { log.warn("failed to initialize window protocol error={}", .{err}); } // When we are realized we always setup our appearance - self.syncAppearance(&self.app.config) catch |err| { + self.syncAppearance() catch |err| { log.err("failed to initialize appearance={}", .{err}); }; @@ -591,61 +665,22 @@ fn gtkWindowNotifyMaximized( ud: ?*anyopaque, ) callconv(.C) void { const self = userdataSelf(ud orelse return); - - // Only toggle visibility of the header bar when we're using CSDs, - // and actually intend on displaying the header bar - if (!self.winproto.clientSideDecorationEnabled()) return; - - // If we aren't maximized, we should show the headerbar again - // if it was originally visible. - const maximized = c.gtk_window_is_maximized(self.window) != 0; - if (!maximized) { - self.headerbar.setVisible(self.app.config.@"gtk-titlebar"); - return; - } - - // If we are maximized, we should hide the headerbar if requested. - if (self.app.config.@"gtk-titlebar-hide-when-maximized") { - self.headerbar.setVisible(false); - } -} - -fn gtkWindowNotifyDecorated( - object: *c.GObject, - _: *c.GParamSpec, - ud: ?*anyopaque, -) callconv(.C) void { - const self = userdataSelf(ud orelse return); - const is_decorated = c.gtk_window_get_decorated(@ptrCast(object)) == 1; - - // Fix any artifacting that may occur in window corners. The .ssd CSS - // class is defined in the GtkWindow documentation: - // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition - // for .ssd is provided by GTK and Adwaita. - toggleCssClass(@ptrCast(object), "csd", is_decorated); - toggleCssClass(@ptrCast(object), "ssd", !is_decorated); - toggleCssClass(@ptrCast(object), "no-border-radius", !is_decorated); - - // FIXME: This is to update the blur region offset on X11. - // Remove this when we move everything related to window appearance - // to `syncAppearance` for Ghostty 1.2. - self.winproto.syncAppearance() catch {}; + self.config.maximize = c.gtk_window_is_maximized(self.window) != 0; + self.syncAppearance() catch |err| { + log.err("failed to sync appearance={}", .{err}); + }; } fn gtkWindowNotifyFullscreened( - object: *c.GObject, + _: *c.GObject, _: *c.GParamSpec, ud: ?*anyopaque, ) callconv(.C) void { const self = userdataSelf(ud orelse return); - const fullscreened = c.gtk_window_is_fullscreen(@ptrCast(object)) != 0; - if (!fullscreened) { - const csd_enabled = self.winproto.clientSideDecorationEnabled(); - self.headerbar.setVisible(self.app.config.@"gtk-titlebar" and csd_enabled); - return; - } - - self.headerbar.setVisible(false); + self.config.fullscreen = c.gtk_window_is_fullscreen(self.window) != 0; + self.syncAppearance() catch |err| { + log.err("failed to sync appearance={}", .{err}); + }; } // Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab diff --git a/src/apprt/gtk/winproto.zig b/src/apprt/gtk/winproto.zig index c752ee6927..81340cf26a 100644 --- a/src/apprt/gtk/winproto.zig +++ b/src/apprt/gtk/winproto.zig @@ -5,6 +5,7 @@ const c = @import("c.zig").c; const Config = @import("../../config.zig").Config; const input = @import("../../input.zig"); const key = @import("key.zig"); +const ApprtWindow = @import("Window.zig"); pub const noop = @import("winproto/noop.zig"); pub const x11 = @import("winproto/x11.zig"); @@ -74,8 +75,7 @@ pub const Window = union(Protocol) { pub fn init( alloc: Allocator, app: *App, - window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { return switch (app.*) { inline else => |*v, tag| { @@ -90,8 +90,7 @@ pub const Window = union(Protocol) { try field.type.init( alloc, v, - window, - config, + apprt_window, ), ); } @@ -111,15 +110,6 @@ pub const Window = union(Protocol) { } } - pub fn updateConfigEvent( - self: *Window, - config: *const Config, - ) !void { - switch (self.*) { - inline else => |*v| try v.updateConfigEvent(config), - } - } - pub fn syncAppearance(self: *Window) !void { switch (self.*) { inline else => |*v| try v.syncAppearance(), diff --git a/src/apprt/gtk/winproto/noop.zig b/src/apprt/gtk/winproto/noop.zig index cb1c0e9ebb..82cab206e1 100644 --- a/src/apprt/gtk/winproto/noop.zig +++ b/src/apprt/gtk/winproto/noop.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const c = @import("../c.zig").c; const Config = @import("../../../config.zig").Config; const input = @import("../../../input.zig"); +const ApprtWindow = @import("../Window.zig"); const log = std.log.scoped(.winproto_noop); @@ -34,8 +35,7 @@ pub const Window = struct { pub fn init( _: Allocator, _: *App, - _: *c.GtkWindow, - _: *const Config, + _: *ApprtWindow, ) !Window { return .{}; } @@ -47,7 +47,7 @@ pub const Window = struct { pub fn updateConfigEvent( _: *Window, - _: *const Config, + _: *const ApprtWindow.DerivedConfig, ) !void {} pub fn resizeEvent(_: *Window) !void {} diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig index f2ef17d73a..fa2bb3b0b8 100644 --- a/src/apprt/gtk/winproto/wayland.zig +++ b/src/apprt/gtk/winproto/wayland.zig @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; const c = @import("../c.zig").c; const Config = @import("../../../config.zig").Config; const input = @import("../../../input.zig"); +const ApprtWindow = @import("../Window.zig"); const wl = wayland.client.wl; const org = wayland.client.org; @@ -155,7 +156,7 @@ pub const App = struct { /// Per-window (wl_surface) state for the Wayland protocol. pub const Window = struct { - config: DerivedConfig, + config: *const ApprtWindow.DerivedConfig, /// The Wayland surface for this window. surface: *wl.Surface, @@ -170,28 +171,15 @@ pub const Window = struct { /// of the window. decoration: ?*org.KdeKwinServerDecoration, - const DerivedConfig = struct { - blur: bool, - window_decoration: Config.WindowDecoration, - - pub fn init(config: *const Config) DerivedConfig { - return .{ - .blur = config.@"background-blur".enabled(), - .window_decoration = config.@"window-decoration", - }; - } - }; - pub fn init( alloc: Allocator, app: *App, - gtk_window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { _ = alloc; const gdk_surface = c.gtk_native_get_surface( - @ptrCast(gtk_window), + @ptrCast(apprt_window.window), ) orelse return error.NotWaylandSurface; // This should never fail, because if we're being called at this point @@ -222,7 +210,7 @@ pub const Window = struct { }; return .{ - .config = DerivedConfig.init(config), + .config = &apprt_window.config, .surface = wl_surface, .app_context = app.context, .blur_token = null, @@ -236,18 +224,15 @@ pub const Window = struct { if (self.decoration) |deco| deco.release(); } - pub fn updateConfigEvent( - self: *Window, - config: *const Config, - ) !void { - self.config = DerivedConfig.init(config); - } - pub fn resizeEvent(_: *Window) !void {} pub fn syncAppearance(self: *Window) !void { - try self.syncBlur(); - try self.syncDecoration(); + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + self.syncDecoration() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; } pub fn clientSideDecorationEnabled(self: Window) bool { @@ -270,18 +255,18 @@ pub const Window = struct { /// Update the blur state of the window. fn syncBlur(self: *Window) !void { const manager = self.app_context.kde_blur_manager orelse return; - const blur = self.config.blur; + const blur = self.config.background_blur; if (self.blur_token) |tok| { // Only release token when transitioning from blurred -> not blurred - if (!blur) { + if (!blur.enabled()) { manager.unset(self.surface); tok.release(); self.blur_token = null; } } else { // Only acquire token when transitioning from not blurred -> blurred - if (blur) { + if (blur.enabled()) { const tok = try manager.create(self.surface); tok.commit(); self.blur_token = tok; diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig index 6b60b0edf8..c7c47b65d7 100644 --- a/src/apprt/gtk/winproto/x11.zig +++ b/src/apprt/gtk/winproto/x11.zig @@ -7,6 +7,7 @@ const c = @import("../c.zig").c; const input = @import("../../../input.zig"); const Config = @import("../../../config.zig").Config; const adwaita = @import("../adwaita.zig"); +const ApprtWindow = @import("../Window.zig"); const log = std.log.scoped(.gtk_x11); @@ -151,33 +152,21 @@ pub const App = struct { pub const Window = struct { app: *App, - alloc: Allocator, - config: DerivedConfig, + config: *const ApprtWindow.DerivedConfig, window: c.Window, gtk_window: *c.GtkWindow, blur_region: Region = .{}, - const DerivedConfig = struct { - blur: bool, - window_decoration: Config.WindowDecoration, - - pub fn init(config: *const Config) DerivedConfig { - return .{ - .blur = config.@"background-blur".enabled(), - .window_decoration = config.@"window-decoration", - }; - } - }; - pub fn init( alloc: Allocator, app: *App, - gtk_window: *c.GtkWindow, - config: *const Config, + apprt_window: *ApprtWindow, ) !Window { + _ = alloc; + const surface = c.gtk_native_get_surface( - @ptrCast(gtk_window), + @ptrCast(apprt_window.window), ) orelse return error.NotX11Surface; // Check if we're actually on X11 @@ -188,10 +177,9 @@ pub const Window = struct { return .{ .app = app, - .alloc = alloc, - .config = DerivedConfig.init(config), + .config = &apprt_window.config, .window = c.gdk_x11_surface_get_xid(surface), - .gtk_window = gtk_window, + .gtk_window = apprt_window.window, }; } @@ -200,13 +188,6 @@ pub const Window = struct { _ = alloc; } - pub fn updateConfigEvent( - self: *Window, - config: *const Config, - ) !void { - self.config = DerivedConfig.init(config); - } - pub fn resizeEvent(self: *Window) !void { // The blur region must update with window resizes self.blur_region.width = c.gtk_widget_get_width(@ptrCast(self.gtk_window)); @@ -257,14 +238,14 @@ pub const Window = struct { // and I think it's not really noticeable enough to justify the effort. // (Wayland also has this visual artifact anyway...) - const blur = self.config.blur; + const blur = self.config.background_blur; log.debug("set blur={}, window xid={}, region={}", .{ blur, self.window, self.blur_region, }); - if (blur) { + if (blur.enabled()) { try self.changeProperty( Region, self.app.atoms.kde_blur, From 8eaa901aecbccb13f8cfeedec508729098c4021a Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Mon, 3 Feb 2025 09:19:29 +0100 Subject: [PATCH 2/2] config: update reload ability information for several keys Several keys are now able to affect existing windows (especially window-decoration, whose config documentation got a greater overhaul) --- src/config/Config.zig | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 11cebb351d..60a95f33b5 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1212,12 +1212,7 @@ keybind: Keybinds = .{}, /// Windows). /// /// The "toggle_window_decorations" keybind action can be used to create -/// a keybinding to toggle this setting at runtime. This will always toggle -/// back to "auto" if the current value is "none" (this is an issue -/// that will be fixed in the future). -/// -/// Changing this configuration in your configuration and reloading will -/// only affect new windows. Existing windows will not be affected. +/// a keybinding to toggle this setting at runtime. /// /// macOS: To hide the titlebar without removing the native window borders /// or rounded corners, use `macos-titlebar-style = hidden` instead. @@ -1323,8 +1318,8 @@ keybind: Keybinds = .{}, /// window will be placed below the menu bar. /// /// Note: this is only supported on macOS and Linux GLFW builds. The GTK -/// runtime does not support setting the window position (this is a limitation -/// of GTK 4.0). +/// runtime does not support setting the window position, as windows are +/// only allowed position themselves in X11 and not Wayland. @"window-position-x": ?i16 = null, @"window-position-y": ?i16 = null, @@ -2180,9 +2175,6 @@ keybind: Keybinds = .{}, /// /// This option does nothing when `window-decoration` is false or when running /// under macOS. -/// -/// Changing this value at runtime and reloading the configuration will only -/// affect new windows. @"gtk-titlebar": bool = true, /// Determines the side of the screen that the GTK tab bar will stick to. @@ -2207,8 +2199,6 @@ keybind: Keybinds = .{}, /// * `raised` - Top and bottom bars cast a shadow on the terminal area. /// * `raised-border` - Similar to `raised` but the shadow is replaced with a /// more subtle border. -/// -/// Changing this value at runtime will only affect new windows. @"gtk-toolbar-style": GtkToolbarStyle = .raised, /// If `true` (default), then the Ghostty GTK tabs will be "wide." Wide tabs