Skip to content

Commit 1d64af7

Browse files
committed
gtk: apply initial appearance settings before presenting
Fixes ghostty-org#5934 (only on the GTK side), ghostty-org#5960
1 parent 61f41e5 commit 1d64af7

File tree

1 file changed

+55
-33
lines changed

1 file changed

+55
-33
lines changed

src/apprt/gtk/Window.zig

+55-33
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ const Window = @This();
77

88
const std = @import("std");
99
const builtin = @import("builtin");
10+
11+
const gtk = @import("gtk");
12+
const gobject = @import("gobject");
13+
1014
const build_config = @import("../../build_config.zig");
1115
const Allocator = std.mem.Allocator;
1216
const assert = std.debug.assert;
@@ -73,11 +77,12 @@ pub const DerivedConfig = struct {
7377
gtk_wide_tabs: bool,
7478
gtk_toolbar_style: configpkg.Config.GtkToolbarStyle,
7579

80+
title: ?[:0]const u8,
7681
maximize: bool,
7782
fullscreen: bool,
7883
window_decoration: configpkg.Config.WindowDecoration,
7984

80-
pub fn init(config: *const configpkg.Config) DerivedConfig {
85+
pub fn init(config: *const configpkg.Config, alloc: Allocator) !DerivedConfig {
8186
return .{
8287
.background_opacity = config.@"background-opacity",
8388
.background_blur = config.@"background-blur",
@@ -88,11 +93,16 @@ pub const DerivedConfig = struct {
8893
.gtk_wide_tabs = config.@"gtk-wide-tabs",
8994
.gtk_toolbar_style = config.@"gtk-toolbar-style",
9095

96+
.title = if (config.title) |t| alloc.dupe(u8, t) else null,
9197
.maximize = config.maximize,
9298
.fullscreen = config.fullscreen,
9399
.window_decoration = config.@"window-decoration",
94100
};
95101
}
102+
103+
pub fn deinit(self: *DerivedConfig, alloc: Allocator) void {
104+
if (self.title) |t| alloc.free(t);
105+
}
96106
};
97107

98108
pub fn create(alloc: Allocator, app: *App) !*Window {
@@ -114,7 +124,7 @@ pub fn init(self: *Window, app: *App) !void {
114124
self.* = .{
115125
.app = app,
116126
.last_config = @intFromPtr(&app.config),
117-
.config = DerivedConfig.init(&app.config),
127+
.config = DerivedConfig.init(&app.config, app.core_app.alloc),
118128
.window = undefined,
119129
.headerbar = undefined,
120130
.tab_overview = null,
@@ -130,17 +140,10 @@ pub fn init(self: *Window, app: *App) !void {
130140

131141
self.window = @ptrCast(@alignCast(gtk_widget));
132142

133-
c.gtk_window_set_title(self.window, "Ghostty");
134-
c.gtk_window_set_default_size(self.window, 1000, 600);
135-
c.gtk_widget_add_css_class(gtk_widget, "window");
136-
c.gtk_widget_add_css_class(gtk_widget, "terminal-window");
137-
138143
// GTK4 grabs F10 input by default to focus the menubar icon. We want
139144
// to disable this so that terminal programs can capture F10 (such as htop)
140145
c.gtk_window_set_handle_menubar_accel(self.window, 0);
141146

142-
c.gtk_window_set_icon_name(self.window, build_config.bundle_id);
143-
144147
// Create our box which will hold our widgets in the main content area.
145148
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
146149

@@ -353,11 +356,7 @@ pub fn init(self: *Window, app: *App) !void {
353356
if (!self.config.gtk_wide_tabs) c.adw_tab_bar_set_expand_tabs(tab_bar, 0);
354357
}
355358

356-
// If we want the window to be maximized, we do that here.
357-
if (self.config.maximize) c.gtk_window_maximize(self.window);
358-
359-
// If we are in fullscreen mode, new windows start fullscreen.
360-
if (self.config.fullscreen) c.gtk_window_fullscreen(self.window);
359+
try self.initAppearance();
361360

362361
// Show the window
363362
c.gtk_widget_show(gtk_widget);
@@ -367,36 +366,64 @@ pub fn updateConfig(
367366
self: *Window,
368367
config: *const configpkg.Config,
369368
) !void {
370-
// avoid multiple reconfigs when we have many surfaces contained in this
371-
// window using the integer value of config as a simple marker to know if
372-
// we've "seen" this particular config before
369+
// HACK:
370+
// Avoid multiple reconfigs when we have many surfaces contained in this
371+
// window using the integer value of the config pointer as a simple marker
372+
// to know if we've "seen" this particular config before.
373+
//
374+
// For the long run we should try to make the flow of config updates saner
375+
// and avoid manual deduplication. See #5947 for more information.
373376
const this_config = @intFromPtr(config);
374377
if (self.last_config == this_config) return;
375378
self.last_config = this_config;
376379

377-
self.config = DerivedConfig.init(config);
380+
self.config = DerivedConfig.init(config, self.app.core_app.alloc);
378381

379382
// We always resync our appearance whenever the config changes.
380383
try self.syncAppearance();
381384
}
382385

386+
/// Initializes the appearance of the window.
387+
///
388+
/// Not all appearance changes *should* be applied when a config option is
389+
/// reloaded (for instance, the maximize and fullscreen settings should only
390+
/// apply to new windows), so we apply them here.
391+
pub fn initAppearance(self: *Window) !void {
392+
// FIXME: Remove once self.window has been migrated to use zig-gobject
393+
const window: *gtk.Window = @ptrCast(self.window);
394+
395+
window.setTitle(self.config.title orelse "Ghostty");
396+
window.setDefaultSize(1000, 600);
397+
window.as(gtk.Widget).addCssClass("window");
398+
window.as(gtk.Widget).addCssClass("terminal-window");
399+
window.setIconName(build_config.bundle_id);
400+
401+
if (self.config.maximize) window.maximize();
402+
if (self.config.fullscreen) window.fullscreen();
403+
404+
try self.syncAppearance();
405+
}
406+
383407
/// Updates appearance based on config settings. Will be called once upon window
384408
/// realization, every time the config is reloaded, and every time a window state
385409
/// is toggled (un-/maximized, un-/fullscreened, window decorations toggled, etc.)
386410
///
387411
/// TODO: Many of the initial style settings in `create` could possibly be made
388412
/// reactive by moving them here.
389413
pub fn syncAppearance(self: *Window) !void {
414+
// FIXME: Remove once self.window has been migrated to use zig-gobject
415+
const window: *gtk.Window = @ptrCast(self.window);
416+
390417
const csd_enabled = self.winproto.clientSideDecorationEnabled();
391-
c.gtk_window_set_decorated(self.window, @intFromBool(csd_enabled));
418+
window.setDecorated(@intFromBool(csd_enabled));
392419

393420
// Fix any artifacting that may occur in window corners. The .ssd CSS
394421
// class is defined in the GtkWindow documentation:
395422
// https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition
396423
// for .ssd is provided by GTK and Adwaita.
397-
toggleCssClass(@ptrCast(self.window), "csd", csd_enabled);
398-
toggleCssClass(@ptrCast(self.window), "ssd", !csd_enabled);
399-
toggleCssClass(@ptrCast(self.window), "no-border-radius", !csd_enabled);
424+
toggleCssClass(window, "csd", csd_enabled);
425+
toggleCssClass(window, "ssd", !csd_enabled);
426+
toggleCssClass(window, "no-border-radius", !csd_enabled);
400427

401428
self.headerbar.setVisible(visible: {
402429
// Never display the header bar when CSDs are disabled.
@@ -414,7 +441,7 @@ pub fn syncAppearance(self: *Window) !void {
414441
});
415442

416443
toggleCssClass(
417-
@ptrCast(self.window),
444+
window,
418445
"background",
419446
self.config.background_opacity >= 1,
420447
);
@@ -423,7 +450,7 @@ pub fn syncAppearance(self: *Window) !void {
423450
// GTK version is before 4.16. The conditional is because above 4.16
424451
// we use GTK CSS color variables.
425452
toggleCssClass(
426-
@ptrCast(self.window),
453+
window,
427454
"window-theme-ghostty",
428455
!version.atLeast(4, 16, 0) and self.config.window_theme == .ghostty,
429456
);
@@ -451,23 +478,17 @@ pub fn syncAppearance(self: *Window) !void {
451478
self.winproto.syncAppearance() catch |err| {
452479
log.warn("failed to sync winproto appearance error={}", .{err});
453480
};
454-
455-
toggleCssClass(
456-
@ptrCast(self.window),
457-
"background",
458-
self.config.background_opacity >= 1,
459-
);
460481
}
461482

462483
fn toggleCssClass(
463-
widget: *c.GtkWidget,
484+
widget: anytype,
464485
class: [:0]const u8,
465486
v: bool,
466487
) void {
467488
if (v) {
468-
c.gtk_widget_add_css_class(widget, class);
489+
widget.as(gtk.Widget).addCssClass(class);
469490
} else {
470-
c.gtk_widget_remove_css_class(widget, class);
491+
widget.as(gtk.Widget).removeCssClass(class);
471492
}
472493
}
473494

@@ -510,6 +531,7 @@ fn initActions(self: *Window) void {
510531

511532
pub fn deinit(self: *Window) void {
512533
self.winproto.deinit(self.app.core_app.alloc);
534+
self.config.deinit(self.app.core_app.alloc);
513535

514536
if (self.adw_tab_overview_focus_timer) |timer| {
515537
_ = c.g_source_remove(timer);

0 commit comments

Comments
 (0)