apprt/gtk-ng: surface inheritance, new window (#8067)

This makes the `new_window` action properly inherit properties from the
parent surface that initiated the action. Today, that is only the pwd
and font size.
pull/8068/head
Mitchell Hashimoto 2025-07-25 10:29:45 -07:00 committed by GitHub
commit 3cdd6f4f5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 98 additions and 5 deletions

View File

@ -1085,9 +1085,7 @@ const Action = struct {
self: *Application, self: *Application,
parent: ?*CoreSurface, parent: ?*CoreSurface,
) !void { ) !void {
_ = parent; const win = Window.new(self, parent);
const win = Window.new(self);
gtk.Window.present(win.as(gtk.Window)); gtk.Window.present(win.as(gtk.Window));
} }

View File

@ -9,6 +9,7 @@ const gobject = @import("gobject");
const gtk = @import("gtk"); const gtk = @import("gtk");
const apprt = @import("../../../apprt.zig"); const apprt = @import("../../../apprt.zig");
const font = @import("../../../font/main.zig");
const input = @import("../../../input.zig"); const input = @import("../../../input.zig");
const internal_os = @import("../../../os/main.zig"); const internal_os = @import("../../../os/main.zig");
const renderer = @import("../../../renderer.zig"); const renderer = @import("../../../renderer.zig");
@ -79,6 +80,25 @@ pub const Surface = extern struct {
); );
}; };
pub const @"font-size-request" = struct {
pub const name = "font-size-request";
const impl = gobject.ext.defineProperty(
name,
Self,
?*font.face.DesiredSize,
.{
.nick = "Desired Font Size",
.blurb = "The desired font size, only affects initialization.",
.accessor = gobject.ext.privateFieldAccessor(
Self,
Private,
&Private.offset,
"font_size_request",
),
},
);
};
pub const focused = struct { pub const focused = struct {
pub const name = "focused"; pub const name = "focused";
const impl = gobject.ext.defineProperty( const impl = gobject.ext.defineProperty(
@ -261,6 +281,10 @@ pub const Surface = extern struct {
/// if `Application.transient_cgroup_base` is set. /// if `Application.transient_cgroup_base` is set.
cgroup_path: ?[]const u8 = null, cgroup_path: ?[]const u8 = null,
/// The requested font size. This only applies to initialization
/// and has no effect later.
font_size_request: ?*font.face.DesiredSize = null,
/// The mouse shape to show for the surface. /// The mouse shape to show for the surface.
mouse_shape: terminal.MouseShape = .default, mouse_shape: terminal.MouseShape = .default,
@ -273,6 +297,10 @@ pub const Surface = extern struct {
/// The current working directory. This has to be reported externally, /// The current working directory. This has to be reported externally,
/// usually by shell integration which then talks to libghostty /// usually by shell integration which then talks to libghostty
/// which triggers this property. /// which triggers this property.
///
/// If this is set prior to initialization then the surface will
/// start in this pwd. If it is set after, it has no impact on the
/// core surface.
pwd: ?[:0]const u8 = null, pwd: ?[:0]const u8 = null,
/// The title of this surface, if any has been set. /// The title of this surface, if any has been set.
@ -349,6 +377,38 @@ pub const Surface = extern struct {
return &priv.rt_surface; return &priv.rt_surface;
} }
/// Set the parent of this surface. This will extract the information
/// required to initialize this surface with the proper values but doesn't
/// retain any memory.
///
/// If the surface is already realized this does nothing.
pub fn setParent(
self: *Self,
parent: *CoreSurface,
) void {
const priv = self.private();
// This is a mistake! We can only set a parent before surface
// realization. We log this because this is probably a logic error.
if (priv.core_surface != null) {
log.warn("setParent called after surface is already realized", .{});
return;
}
// Setup our font size
const font_size_ptr = glib.ext.create(font.face.DesiredSize);
errdefer glib.ext.destroy(font_size_ptr);
font_size_ptr.* = parent.font_size;
priv.font_size_request = font_size_ptr;
self.as(gobject.Object).notifyByPspec(properties.@"font-size-request".impl.param_spec);
// Setup our pwd
if (parent.rt_surface.surface.getPwd()) |pwd| {
priv.pwd = glib.ext.dupeZ(u8, pwd);
self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec);
}
}
/// Force the surface to redraw itself. Ghostty often will only redraw /// Force the surface to redraw itself. Ghostty often will only redraw
/// the terminal in reaction to internal changes. If there are external /// the terminal in reaction to internal changes. If there are external
/// events that invalidate the surface, such as the widget moving parents, /// events that invalidate the surface, such as the widget moving parents,
@ -1029,6 +1089,10 @@ pub const Surface = extern struct {
glib.free(@constCast(@ptrCast(v))); glib.free(@constCast(@ptrCast(v)));
priv.mouse_hover_url = null; priv.mouse_hover_url = null;
} }
if (priv.font_size_request) |v| {
glib.ext.destroy(v);
priv.font_size_request = null;
}
if (priv.pwd) |v| { if (priv.pwd) |v| {
glib.free(@constCast(@ptrCast(v))); glib.free(@constCast(@ptrCast(v)));
priv.pwd = null; priv.pwd = null;
@ -1053,6 +1117,11 @@ pub const Surface = extern struct {
return self.private().title; return self.private().title;
} }
/// Returns the pwd property without a copy.
pub fn getPwd(self: *Self) ?[:0]const u8 {
return self.private().pwd;
}
fn propConfig( fn propConfig(
self: *Self, self: *Self,
_: *gobject.ParamSpec, _: *gobject.ParamSpec,
@ -1893,6 +1962,10 @@ pub const Surface = extern struct {
); );
defer config.deinit(); defer config.deinit();
// Properties that can impact surface init
if (priv.font_size_request) |size| config.@"font-size" = size.points;
if (priv.pwd) |pwd| config.@"working-directory" = pwd;
// Initialize the surface // Initialize the surface
surface.init( surface.init(
alloc, alloc,
@ -1996,6 +2069,7 @@ pub const Surface = extern struct {
gobject.ext.registerProperties(class, &.{ gobject.ext.registerProperties(class, &.{
properties.config.impl, properties.config.impl,
properties.@"child-exited".impl, properties.@"child-exited".impl,
properties.@"font-size-request".impl,
properties.focused.impl, properties.focused.impl,
properties.@"mouse-shape".impl, properties.@"mouse-shape".impl,
properties.@"mouse-hidden".impl, properties.@"mouse-hidden".impl,

View File

@ -4,6 +4,7 @@ const adw = @import("adw");
const gobject = @import("gobject"); const gobject = @import("gobject");
const gtk = @import("gtk"); const gtk = @import("gtk");
const CoreSurface = @import("../../../Surface.zig");
const gresource = @import("../build/gresource.zig"); const gresource = @import("../build/gresource.zig");
const Common = @import("../class.zig").Common; const Common = @import("../class.zig").Common;
const Application = @import("application.zig").Application; const Application = @import("application.zig").Application;
@ -30,8 +31,17 @@ pub const Window = extern struct {
pub var offset: c_int = 0; pub var offset: c_int = 0;
}; };
pub fn new(app: *Application) *Self { pub fn new(app: *Application, parent_: ?*CoreSurface) *Self {
return gobject.ext.newInstance(Self, .{ .application = app }); const self = gobject.ext.newInstance(Self, .{
.application = app,
});
if (parent_) |parent| {
const priv = self.private();
priv.surface.setParent(parent);
}
return self;
} }
fn init(self: *Self, _: *Class) callconv(.C) void { fn init(self: *Self, _: *Class) callconv(.C) void {

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_config = @import("../build_config.zig");
const options = @import("main.zig").options; const options = @import("main.zig").options;
const Metrics = @import("main.zig").Metrics; const Metrics = @import("main.zig").Metrics;
const config = @import("../config.zig"); const config = @import("../config.zig");
@ -55,6 +56,16 @@ pub const DesiredSize = struct {
// 1 point = 1/72 inch // 1 point = 1/72 inch
return @intFromFloat(@round((self.points * @as(f32, @floatFromInt(self.ydpi))) / 72)); return @intFromFloat(@round((self.points * @as(f32, @floatFromInt(self.ydpi))) / 72));
} }
/// Make this a valid gobject if we're in a GTK environment.
pub const getGObjectType = switch (build_config.app_runtime) {
.gtk, .@"gtk-ng" => @import("gobject").ext.defineBoxed(
DesiredSize,
.{ .name = "GhosttyFontDesiredSize" },
),
.none => void,
};
}; };
/// A font variation setting. The best documentation for this I know of /// A font variation setting. The best documentation for this I know of