apprt/gtk-ng: hook up all the syncAppearance calls for winproto

pull/8123/head
Mitchell Hashimoto 2025-08-01 14:55:53 -07:00
parent c7eee9ee7a
commit 084a20c865
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
5 changed files with 102 additions and 17 deletions

View File

@ -503,7 +503,17 @@ pub const Window = extern struct {
/// happens that might affect how the window appears (config change,
/// fullscreen, etc.).
fn syncAppearance(self: *Self) void {
// TODO: CSD/SSD
const priv = self.private();
const csd_enabled = priv.winproto.clientSideDecorationEnabled();
self.as(gtk.Window).setDecorated(@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.
self.toggleCssClass("csd", csd_enabled);
self.toggleCssClass("ssd", !csd_enabled);
self.toggleCssClass("no-border-radius", !csd_enabled);
// Trigger all our dynamic properties that depend on the config.
inline for (&.{
@ -520,15 +530,28 @@ pub const Window = extern struct {
}
// Remainder uses the config
const priv = self.private();
const config = if (priv.config) |v| v.get() else return;
// 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.
self.toggleCssClass(
"window-theme-ghostty",
!gtk_version.atLeast(4, 16, 0) and
config.@"window-theme" == .ghostty,
);
// Move the tab bar to the proper location.
priv.toolbar.remove(priv.tab_bar.as(gtk.Widget));
switch (config.@"gtk-tabs-location") {
.top => priv.toolbar.addTopBar(priv.tab_bar.as(gtk.Widget)),
.bottom => priv.toolbar.addBottomBar(priv.tab_bar.as(gtk.Widget)),
}
// Do our window-protocol specific appearance sync.
priv.winproto.syncAppearance() catch |err| {
log.warn("failed to sync winproto appearance error={}", .{err});
};
}
fn toggleCssClass(self: *Self, class: [:0]const u8, value: bool) void {
@ -614,8 +637,14 @@ pub const Window = extern struct {
}
fn getHeaderbarVisible(self: *Self) bool {
// TODO: CSD/SSD
// TODO: QuickTerminal
const priv = self.private();
// Never display the header bar when CSDs are disabled.
const csd_enabled = priv.winproto.clientSideDecorationEnabled();
if (!csd_enabled) return false;
// Never display the header bar as a quick terminal.
if (priv.quick_terminal) return false;
// If we're fullscreen we never show the header bar.
if (self.as(gtk.Window).isFullscreen() != 0) return false;
@ -749,6 +778,24 @@ pub const Window = extern struct {
self.toggleCssClass("background", self.getBackgroundOpaque());
}
fn propScaleFactor(
_: *adw.ApplicationWindow,
_: *gobject.ParamSpec,
self: *Self,
) callconv(.c) void {
// On some platforms (namely X11) we need to refresh our appearance when
// the scale factor changes. In theory this could be more fine-grained as
// a full refresh could be expensive, but a) this *should* be rare, and
// b) quite noticeable visual bugs would occur if this is not present.
self.private().winproto.syncAppearance() catch |err| {
log.warn(
"failed to sync appearance after scale factor has been updated={}",
.{err},
);
return;
};
}
//---------------------------------------------------------------
// Virtual methods
@ -1451,11 +1498,12 @@ pub const Window = extern struct {
class.bindTemplateCallback("tab_create_window", &tabViewCreateWindow);
class.bindTemplateCallback("notify_n_pages", &tabViewNPages);
class.bindTemplateCallback("notify_selected_page", &tabViewSelectedPage);
class.bindTemplateCallback("notify_background_opaque", &propBackgroundOpaque);
class.bindTemplateCallback("notify_config", &propConfig);
class.bindTemplateCallback("notify_fullscreened", &propFullscreened);
class.bindTemplateCallback("notify_maximized", &propMaximized);
class.bindTemplateCallback("notify_menu_active", &propMenuActive);
class.bindTemplateCallback("notify_background_opaque", &propBackgroundOpaque);
class.bindTemplateCallback("notify_scale_factor", &propScaleFactor);
// Virtual methods
gobject.Object.virtual_methods.dispose.implement(class, &dispose);

View File

@ -4,6 +4,14 @@
* https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/styles-and-appearance.html#custom-styles
*/
window.ssd.no-border-radius {
/* Without clearing the border radius, at least on Mutter with
* gtk-titlebar=true and gtk-adwaita=false, there is some window artifacting
* that this will mitigate.
*/
border-radius: 0 0;
}
/*
* GhosttySurface URL overlay
*/

View File

@ -8,10 +8,11 @@ template $GhosttyWindow: Adw.ApplicationWindow {
close-request => $close_request();
realize => $realize();
notify::background-opaque => $notify_background_opaque();
notify::config => $notify_config();
notify::fullscreened => $notify_fullscreened();
notify::maximized => $notify_maximized();
notify::background-opaque => $notify_background_opaque();
notify::scale-factor => $notify_scale_factor();
default-width: 800;
default-height: 600;
// GTK4 grabs F10 input by default to focus the menubar icon. We want
@ -21,8 +22,13 @@ template $GhosttyWindow: Adw.ApplicationWindow {
content: Adw.TabOverview tab_overview {
create-tab => $overview_create_tab();
notify::open => $overview_notify_open();
enable-new-tab: true;
view: tab_view;
enable-new-tab: true;
// Disable the title buttons (close, maximize, minimize, ...)
// *inside* the tab overview if CSDs are disabled.
// We do spare the search button, though.
show-start-title-buttons: bind template.decorated;
show-end-title-buttons: bind template.decorated;
Adw.ToolbarView toolbar {
top-bar-style: bind template.toolbar-style;

View File

@ -364,7 +364,11 @@ 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.apprt_window.config.background_blur;
const config = if (self.apprt_window.getConfig()) |v|
v.get()
else
return;
const blur = config.@"background-blur";
if (self.blur_token) |tok| {
// Only release token when transitioning from blurred -> not blurred
@ -392,7 +396,12 @@ pub const Window = struct {
}
fn getDecorationMode(self: Window) org.KdeKwinServerDecorationManager.Mode {
return switch (self.apprt_window.config.window_decoration) {
const config = if (self.apprt_window.getConfig()) |v|
v.get()
else
return .Client;
return switch (config.@"window-decoration") {
.auto => self.app_context.default_deco_mode orelse .Client,
.client => .Client,
.server => .Server,
@ -401,12 +410,15 @@ pub const Window = struct {
}
fn syncQuickTerminal(self: *Window) !void {
const window = self.apprt_window.window.as(gtk.Window);
const config = &self.apprt_window.config;
const window = self.apprt_window.as(gtk.Window);
const config = if (self.apprt_window.getConfig()) |v|
v.get()
else
return;
layer_shell.setKeyboardMode(
window,
switch (config.quick_terminal_keyboard_interactivity) {
switch (config.@"quick-terminal-keyboard-interactivity") {
.none => .none,
.@"on-demand" => on_demand: {
if (layer_shell.getProtocolVersion() < 4) {
@ -419,7 +431,7 @@ pub const Window = struct {
},
);
const anchored_edge: ?layer_shell.ShellEdge = switch (config.quick_terminal_position) {
const anchored_edge: ?layer_shell.ShellEdge = switch (config.@"quick-terminal-position") {
.left => .left,
.right => .right,
.top => .top,

View File

@ -239,7 +239,12 @@ pub const Window = struct {
}
pub fn clientSideDecorationEnabled(self: Window) bool {
return switch (self.config.window_decoration) {
const config = if (self.apprt_window.getConfig()) |v|
v.get()
else
return true;
return switch (config.@"window-decoration") {
.auto, .client => true,
.server, .none => false,
};
@ -255,13 +260,14 @@ pub const Window = struct {
// (Wayland also has this visual artifact anyway...)
const gtk_widget = self.apprt_window.as(gtk.Widget);
const config = if (self.apprt_window.getConfig()) |v| v.get() else return;
// Transform surface coordinates to device coordinates.
const scale = self.apprt_window.as(gtk.Widget).getScaleFactor();
const scale = gtk_widget.getScaleFactor();
self.blur_region.width = gtk_widget.getWidth() * scale;
self.blur_region.height = gtk_widget.getHeight() * scale;
const blur = self.config.background_blur;
const blur = config.@"background-blur";
log.debug("set blur={}, window xid={}, region={}", .{
blur,
self.x11_surface.getXid(),
@ -283,6 +289,11 @@ pub const Window = struct {
}
fn syncDecorations(self: *Window) !void {
const config = if (self.apprt_window.getConfig()) |v|
v.get()
else
return;
var hints: MotifWMHints = .{};
self.getWindowProperty(
@ -303,7 +314,7 @@ pub const Window = struct {
};
hints.flags.decorations = true;
hints.decorations.all = switch (self.config.window_decoration) {
hints.decorations.all = switch (config.@"window-decoration") {
.server => true,
.auto, .client, .none => false,
};