apprt/gtk-ng: title bindings

This commit is contained in:
Mitchell Hashimoto
2025-07-28 12:15:57 -07:00
parent 775f3dfca3
commit fa45f971f4
3 changed files with 100 additions and 4 deletions

View File

@ -72,12 +72,36 @@ pub const Tab = extern struct {
},
);
};
pub const title = struct {
pub const name = "title";
pub const get = impl.get;
pub const set = impl.set;
const impl = gobject.ext.defineProperty(
name,
Self,
?[:0]const u8,
.{
.nick = "Title",
.blurb = "The title of the active surface.",
.default = null,
.accessor = C.privateStringFieldAccessor("title"),
},
);
};
};
const Private = struct {
/// The configuration that this surface is using.
config: ?*Config = null,
/// The title to show for this tab. This is usally set to a binding
/// with the active surface but can be manually set to anything.
title: ?[:0]const u8 = null,
/// The binding groups for the current active surface.
surface_bindings: *gobject.BindingGroup,
// Template bindings
surface: *Surface,
@ -106,6 +130,19 @@ pub const Tab = extern struct {
priv.config = app.getConfig();
}
// Setup binding groups for surface properties
priv.surface_bindings = gobject.BindingGroup.new();
priv.surface_bindings.bind(
"title",
self.as(gobject.Object),
"title",
.{},
);
// TODO: Eventually this should be set dynamically based on the
// current active surface.
priv.surface_bindings.setSource(priv.surface.as(gobject.Object));
// We need to do this so that the title initializes properly,
// I think because its a dynamic getter.
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
@ -130,6 +167,7 @@ pub const Tab = extern struct {
v.unref();
priv.config = null;
}
priv.surface_bindings.setSource(null);
gtk.Widget.disposeTemplate(
self.as(gtk.Widget),
@ -142,6 +180,19 @@ pub const Tab = extern struct {
);
}
fn finalize(self: *Self) callconv(.C) void {
const priv = self.private();
if (priv.title) |v| {
glib.free(@constCast(@ptrCast(v)));
priv.title = null;
}
priv.surface_bindings.unref();
gobject.Object.virtual_methods.finalize.call(
Class.parent,
self.as(Parent),
);
}
//---------------------------------------------------------------
// Signal handlers
@ -171,6 +222,7 @@ pub const Tab = extern struct {
gobject.ext.registerProperties(class, &.{
properties.@"active-surface".impl,
properties.config.impl,
properties.title.impl,
});
// Bindings
@ -181,6 +233,7 @@ pub const Tab = extern struct {
// Virtual methods
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
}
pub const as = C.Class.as;

View File

@ -204,6 +204,9 @@ pub const Window = extern struct {
};
const Private = struct {
/// Binding group for our active tab.
tab_bindings: *gobject.BindingGroup,
/// The configuration that this surface is using.
config: ?*Config = null,
@ -221,7 +224,9 @@ pub const Window = extern struct {
.application = app,
});
// Create our initial tab
// Create our initial tab. This will trigger the selected-page
// signal handler which will setup the remainder of the bindings
// for this to all work.
const priv = self.private();
const tab = gobject.ext.newInstance(Tab, .{
.config = priv.config,
@ -248,6 +253,10 @@ pub const Window = extern struct {
self.as(gtk.Widget).addCssClass("devel");
}
// Setup some of our objects that are never null
priv.tab_bindings = gobject.BindingGroup.new();
priv.tab_bindings.bind("title", self.as(gobject.Object), "title", .{});
// Set our window icon. We can't set this in the blueprint file
// because its dependent on the build config.
self.as(gtk.Window).setIconName(build_config.bundle_id);
@ -517,6 +526,7 @@ pub const Window = extern struct {
v.unref();
priv.config = null;
}
priv.tab_bindings.setSource(null);
gtk.Widget.disposeTemplate(
self.as(gtk.Widget),
@ -529,6 +539,16 @@ pub const Window = extern struct {
);
}
fn finalize(self: *Self) callconv(.C) void {
const priv = self.private();
priv.tab_bindings.unref();
gobject.Object.virtual_methods.finalize.call(
Class.parent,
self.as(Parent),
);
}
//---------------------------------------------------------------
// Signal handlers
@ -570,6 +590,26 @@ pub const Window = extern struct {
self.as(gtk.Window).destroy();
}
fn tabViewSelectedPage(
_: *adw.TabView,
_: *gobject.ParamSpec,
self: *Self,
) callconv(.c) void {
const priv = self.private();
// Always reset our binding source in case we have no pages.
priv.tab_bindings.setSource(null);
// Get our current page which MUST be a Tab object.
const page = priv.tab_view.getSelectedPage() orelse return;
const child = page.getChild();
assert(gobject.ext.isA(child, Tab));
// Setup our binding group. This ensures things like the title
// are synced from the active tab.
priv.tab_bindings.setSource(child.as(gobject.Object));
}
fn surfaceClipboardWrite(
_: *Surface,
clipboard_type: apprt.Clipboard,
@ -768,6 +808,7 @@ pub const Window = extern struct {
// Template Callbacks
class.bindTemplateCallback("close_request", &windowCloseRequest);
class.bindTemplateCallback("selected_page", &tabViewSelectedPage);
class.bindTemplateCallback("surface_clipboard_write", &surfaceClipboardWrite);
class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest);
class.bindTemplateCallback("surface_toggle_fullscreen", &surfaceToggleFullscreen);
@ -780,6 +821,7 @@ pub const Window = extern struct {
// Virtual methods
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
}
pub const as = C.Class.as;

View File

@ -16,7 +16,6 @@ template $GhosttyWindow: Adw.ApplicationWindow {
// GTK4 grabs F10 input by default to focus the menubar icon. We want
// to disable this so that terminal programs can capture F10 (such as htop)
handle-menubar-accel: false;
title: bind (template.active-surface as <$GhosttySurface>).title;
content: Adw.TabOverview tab_overview {
enable-new-tab: true;
@ -31,7 +30,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
visible: bind template.headerbar-visible;
title-widget: Adw.WindowTitle {
title: bind (template.active-surface as <$GhosttySurface>).title;
title: bind template.title;
};
[start]
@ -78,7 +77,9 @@ template $GhosttyWindow: Adw.ApplicationWindow {
}
Adw.ToastOverlay toast_overlay {
Adw.TabView tab_view {}
Adw.TabView tab_view {
notify::selected-page => $selected_page();
}
}
}
}