From 431a6328dc572c3659fee190706a63dae87854eb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 28 Jul 2025 21:37:32 -0700 Subject: [PATCH] apprt/gtk-ng: handle surface close request --- src/apprt/gtk-ng/class/tab.zig | 42 +++++++++++++++++++++++++++++-- src/apprt/gtk-ng/class/window.zig | 28 +++++++++++++++++++++ src/apprt/gtk-ng/ui/1.5/tab.blp | 2 +- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/apprt/gtk-ng/class/tab.zig b/src/apprt/gtk-ng/class/tab.zig index 3d8437115..95ba74a0e 100644 --- a/src/apprt/gtk-ng/class/tab.zig +++ b/src/apprt/gtk-ng/class/tab.zig @@ -91,11 +91,25 @@ pub const Tab = extern struct { }; }; + pub const signals = struct { + /// Emitted whenever the tab would like to be closed. + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + 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 + /// The title to show for this tab. This is usually set to a binding /// with the active surface but can be manually set to anything. title: ?[:0]const u8 = null, @@ -196,6 +210,27 @@ pub const Tab = extern struct { //--------------------------------------------------------------- // Signal handlers + fn surfaceCloseRequest( + _: *Surface, + scope: *const Surface.CloseScope, + self: *Self, + ) callconv(.c) void { + switch (scope.*) { + // Handled upstream... we don't control our window close. + .window => return, + + // Presently both the same, results in the tab closing. + .surface, .tab => { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + }, + } + } + const C = Common(Self, Private); pub const as = C.as; pub const ref = C.ref; @@ -229,7 +264,10 @@ pub const Tab = extern struct { class.bindTemplateChildPrivate("surface", .{}); // Template Callbacks - //class.bindTemplateCallback("close_request", &windowCloseRequest); + class.bindTemplateCallback("surface_close_request", &surfaceCloseRequest); + + // Signals + signals.@"close-request".impl.register(.{}); // Virtual methods gobject.Object.virtual_methods.dispose.implement(class, &dispose); diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index 3c4a9112f..18696f32a 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -621,6 +621,15 @@ pub const Window = extern struct { const child = page.getChild(); const tab = gobject.ext.cast(Tab, child) orelse return; + // Attach listeners for the tab. + _ = Tab.signals.@"close-request".connect( + tab, + *Self, + tabCloseRequest, + self, + .{}, + ); + // Attach listeners for the surface. // // Interesting behavior here that was previously undocumented but @@ -682,6 +691,15 @@ pub const Window = extern struct { // We need to get the tab to disconnect the signals. const child = page.getChild(); const tab = gobject.ext.cast(Tab, child) orelse return; + _ = gobject.signalHandlersDisconnectMatched( + tab.as(gobject.Object), + .{ .data = true }, + 0, + 0, + null, + null, + self, + ); // Remove all the signals that have this window as the userdata. const surface = tab.getActiveSurface(); @@ -696,6 +714,16 @@ pub const Window = extern struct { ); } + fn tabCloseRequest( + tab: *Tab, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const page = priv.tab_view.getPage(tab.as(gtk.Widget)); + // TODO: connect close page handler to tab to check for confirmation + priv.tab_view.closePage(page); + } + fn surfaceClipboardWrite( _: *Surface, clipboard_type: apprt.Clipboard, diff --git a/src/apprt/gtk-ng/ui/1.5/tab.blp b/src/apprt/gtk-ng/ui/1.5/tab.blp index cd2d5d254..476244576 100644 --- a/src/apprt/gtk-ng/ui/1.5/tab.blp +++ b/src/apprt/gtk-ng/ui/1.5/tab.blp @@ -10,6 +10,6 @@ template $GhosttyTab: Box { // A tab currently just contains a surface directly. When we introduce // splits we probably want to replace this with the split widget type. $GhosttySurface surface { - // close-request => $surface_close_request(); + close-request => $surface_close_request(); } }