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 {
|
const Private = struct {
|
||||||
/// The configuration that this surface is using.
|
/// The configuration that this surface is using.
|
||||||
config: ?*Config = null,
|
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
|
// Template bindings
|
||||||
surface: *Surface,
|
surface: *Surface,
|
||||||
|
|
||||||
@ -106,6 +130,19 @@ pub const Tab = extern struct {
|
|||||||
priv.config = app.getConfig();
|
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,
|
// We need to do this so that the title initializes properly,
|
||||||
// I think because its a dynamic getter.
|
// I think because its a dynamic getter.
|
||||||
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
|
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
|
||||||
@ -130,6 +167,7 @@ pub const Tab = extern struct {
|
|||||||
v.unref();
|
v.unref();
|
||||||
priv.config = null;
|
priv.config = null;
|
||||||
}
|
}
|
||||||
|
priv.surface_bindings.setSource(null);
|
||||||
|
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
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
|
// Signal handlers
|
||||||
|
|
||||||
@ -171,6 +222,7 @@ pub const Tab = extern struct {
|
|||||||
gobject.ext.registerProperties(class, &.{
|
gobject.ext.registerProperties(class, &.{
|
||||||
properties.@"active-surface".impl,
|
properties.@"active-surface".impl,
|
||||||
properties.config.impl,
|
properties.config.impl,
|
||||||
|
properties.title.impl,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
@ -181,6 +233,7 @@ pub const Tab = extern struct {
|
|||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const as = C.Class.as;
|
pub const as = C.Class.as;
|
||||||
|
@ -204,6 +204,9 @@ pub const Window = extern struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Private = struct {
|
const Private = struct {
|
||||||
|
/// Binding group for our active tab.
|
||||||
|
tab_bindings: *gobject.BindingGroup,
|
||||||
|
|
||||||
/// The configuration that this surface is using.
|
/// The configuration that this surface is using.
|
||||||
config: ?*Config = null,
|
config: ?*Config = null,
|
||||||
|
|
||||||
@ -221,7 +224,9 @@ pub const Window = extern struct {
|
|||||||
.application = app,
|
.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 priv = self.private();
|
||||||
const tab = gobject.ext.newInstance(Tab, .{
|
const tab = gobject.ext.newInstance(Tab, .{
|
||||||
.config = priv.config,
|
.config = priv.config,
|
||||||
@ -248,6 +253,10 @@ pub const Window = extern struct {
|
|||||||
self.as(gtk.Widget).addCssClass("devel");
|
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
|
// Set our window icon. We can't set this in the blueprint file
|
||||||
// because its dependent on the build config.
|
// because its dependent on the build config.
|
||||||
self.as(gtk.Window).setIconName(build_config.bundle_id);
|
self.as(gtk.Window).setIconName(build_config.bundle_id);
|
||||||
@ -517,6 +526,7 @@ pub const Window = extern struct {
|
|||||||
v.unref();
|
v.unref();
|
||||||
priv.config = null;
|
priv.config = null;
|
||||||
}
|
}
|
||||||
|
priv.tab_bindings.setSource(null);
|
||||||
|
|
||||||
gtk.Widget.disposeTemplate(
|
gtk.Widget.disposeTemplate(
|
||||||
self.as(gtk.Widget),
|
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
|
// Signal handlers
|
||||||
|
|
||||||
@ -570,6 +590,26 @@ pub const Window = extern struct {
|
|||||||
self.as(gtk.Window).destroy();
|
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(
|
fn surfaceClipboardWrite(
|
||||||
_: *Surface,
|
_: *Surface,
|
||||||
clipboard_type: apprt.Clipboard,
|
clipboard_type: apprt.Clipboard,
|
||||||
@ -768,6 +808,7 @@ pub const Window = extern struct {
|
|||||||
|
|
||||||
// Template Callbacks
|
// Template Callbacks
|
||||||
class.bindTemplateCallback("close_request", &windowCloseRequest);
|
class.bindTemplateCallback("close_request", &windowCloseRequest);
|
||||||
|
class.bindTemplateCallback("selected_page", &tabViewSelectedPage);
|
||||||
class.bindTemplateCallback("surface_clipboard_write", &surfaceClipboardWrite);
|
class.bindTemplateCallback("surface_clipboard_write", &surfaceClipboardWrite);
|
||||||
class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest);
|
class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest);
|
||||||
class.bindTemplateCallback("surface_toggle_fullscreen", &surfaceToggleFullscreen);
|
class.bindTemplateCallback("surface_toggle_fullscreen", &surfaceToggleFullscreen);
|
||||||
@ -780,6 +821,7 @@ pub const Window = extern struct {
|
|||||||
|
|
||||||
// Virtual methods
|
// Virtual methods
|
||||||
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
gobject.Object.virtual_methods.dispose.implement(class, &dispose);
|
||||||
|
gobject.Object.virtual_methods.finalize.implement(class, &finalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const as = C.Class.as;
|
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
|
// 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)
|
// to disable this so that terminal programs can capture F10 (such as htop)
|
||||||
handle-menubar-accel: false;
|
handle-menubar-accel: false;
|
||||||
title: bind (template.active-surface as <$GhosttySurface>).title;
|
|
||||||
|
|
||||||
content: Adw.TabOverview tab_overview {
|
content: Adw.TabOverview tab_overview {
|
||||||
enable-new-tab: true;
|
enable-new-tab: true;
|
||||||
@ -31,7 +30,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
|
|||||||
visible: bind template.headerbar-visible;
|
visible: bind template.headerbar-visible;
|
||||||
|
|
||||||
title-widget: Adw.WindowTitle {
|
title-widget: Adw.WindowTitle {
|
||||||
title: bind (template.active-surface as <$GhosttySurface>).title;
|
title: bind template.title;
|
||||||
};
|
};
|
||||||
|
|
||||||
[start]
|
[start]
|
||||||
@ -78,7 +77,9 @@ template $GhosttyWindow: Adw.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Adw.TabView tab_view {}
|
Adw.TabView tab_view {
|
||||||
|
notify::selected-page => $selected_page();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user