mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
apprt/gtk-ng: title bindings
This commit is contained in:
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user