GTK Fix unfocused-split-fill (#8813)

Attempts a resolution for
https://github.com/ghostty-org/ghostty/discussions/8572

This matches the behavior of the old GTK apprt where
unfocused-split-fill /opacity doesn't apply when there is only one
active surface.
pull/8858/head
Mitchell Hashimoto 2025-09-22 19:58:50 -07:00 committed by GitHub
commit e951dedc66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 91 additions and 0 deletions

View File

@ -112,6 +112,25 @@ pub const SplitTree = extern struct {
},
);
};
pub const @"is-split" = struct {
pub const name = "is-split";
const impl = gobject.ext.defineProperty(
name,
Self,
bool,
.{
.default = false,
.accessor = gobject.ext.typedAccessor(
Self,
bool,
.{
.getter = getIsSplit,
},
),
},
);
};
};
pub const signals = struct {
@ -210,6 +229,14 @@ pub const SplitTree = extern struct {
}
}
// Bind is-split property for new surface
_ = self.as(gobject.Object).bindProperty(
"is-split",
surface.as(gobject.Object),
"is-split",
.{ .sync_create = true },
);
// Create our tree
var single_tree = try Surface.Tree.init(alloc, surface);
defer single_tree.deinit();
@ -511,6 +538,18 @@ pub const SplitTree = extern struct {
));
}
fn getIsSplit(self: *Self) bool {
const tree: *const Surface.Tree = self.private().tree orelse &.empty;
if (tree.isEmpty()) return false;
const root_handle: Surface.Tree.Node.Handle = .root;
const root = tree.nodes[root_handle.idx()];
return switch (root) {
.leaf => false,
.split => true,
};
}
//---------------------------------------------------------------
// Virtual methods
@ -816,6 +855,9 @@ pub const SplitTree = extern struct {
v.grabFocus();
}
// Our split status may have changed
self.as(gobject.Object).notifyByPspec(properties.@"is-split".impl.param_spec);
// Our active surface may have changed
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
@ -873,6 +915,7 @@ pub const SplitTree = extern struct {
properties.@"has-surfaces".impl,
properties.@"is-zoomed".impl,
properties.tree.impl,
properties.@"is-split".impl,
});
// Bindings

View File

@ -275,6 +275,24 @@ pub const Surface = extern struct {
},
);
};
pub const @"is-split" = struct {
pub const name = "is-split";
const impl = gobject.ext.defineProperty(
name,
Self,
bool,
.{
.default = false,
.accessor = gobject.ext.privateFieldAccessor(
Self,
Private,
&Private.offset,
"is_split",
),
},
);
};
};
pub const signals = struct {
@ -503,6 +521,10 @@ pub const Surface = extern struct {
/// A weak reference to an inspector window.
inspector: ?*InspectorWindow = null,
// True if the current surface is a split, this is used to apply
// unfocused-split-* options
is_split: bool = false,
// Template binds
child_exited_overlay: *ChildExited,
context_menu: *gtk.PopoverMenu,
@ -601,6 +623,16 @@ pub const Surface = extern struct {
return @intFromBool(config.@"bell-features".border);
}
/// Callback used to determine whether unfocused-split-fill / unfocused-split-opacity
/// should be applied to the surface
fn closureShouldUnfocusedSplitBeShown(
_: *Self,
focused: c_int,
is_split: c_int,
) callconv(.c) c_int {
return @intFromBool(focused == 0 and is_split != 0);
}
pub fn toggleFullscreen(self: *Self) void {
signals.@"toggle-fullscreen".impl.emit(
self,
@ -2829,6 +2861,7 @@ pub const Surface = extern struct {
class.bindTemplateCallback("notify_mouse_shape", &propMouseShape);
class.bindTemplateCallback("notify_bell_ringing", &propBellRinging);
class.bindTemplateCallback("should_border_be_shown", &closureShouldBorderBeShown);
class.bindTemplateCallback("should_unfocused_split_be_shown", &closureShouldUnfocusedSplitBeShown);
// Properties
gobject.ext.registerProperties(class, &.{
@ -2847,6 +2880,7 @@ pub const Surface = extern struct {
properties.title.impl,
properties.@"title-override".impl,
properties.zoom.impl,
properties.@"is-split".impl,
});
// Signals

View File

@ -115,6 +115,20 @@ Overlay terminal_page {
label: bind template.mouse-hover-url;
}
[overlay]
// Apply unfocused-split-fill and unfocused-split-opacity to current surface
// this is only applied when a tab has more than one surface
Revealer {
reveal-child: bind $should_unfocused_split_be_shown(template.focused, template.is-split) as <bool>;
transition-duration: 0;
DrawingArea {
styles [
"unfocused-split",
]
}
}
// Event controllers for interactivity
EventControllerFocus {
enter => $focus_enter();