gtk/TabView: do not closeTab within close-page signal handler

`TabView` assumes to be the sole owner of all `Tab`s within a Window.
As such, it could close the managed `Window` once all tabs are removed
from its widget.

However, during `AdwTabView::close-page` signal triggered by libadwaita,
the `Tab` to be closed will gain an another reference for the duration
of the signal, breaking `TabView.closeTab` (called via
`Tab.closeWithConfirmation`) assumptions that having no tabs meant they
are all destroyed.

This commit solves the issue by scheduling `Tab.closeWithConfirmation`
to be run after `AdwTabView::close-page` signal has finished processing.
This commit is contained in:
Leorize
2025-06-03 01:34:37 -05:00
parent d993588263
commit 4e39144d39

View File

@@ -7,6 +7,7 @@ const std = @import("std");
const gtk = @import("gtk"); const gtk = @import("gtk");
const adw = @import("adw"); const adw = @import("adw");
const gobject = @import("gobject"); const gobject = @import("gobject");
const glib = @import("glib");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
@@ -243,7 +244,14 @@ fn adwClosePage(
const child = page.getChild().as(gobject.Object); const child = page.getChild().as(gobject.Object);
const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return 0)); const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return 0));
self.tab_view.closePageFinish(page, @intFromBool(self.forcing_close)); self.tab_view.closePageFinish(page, @intFromBool(self.forcing_close));
if (!self.forcing_close) tab.closeWithConfirmation(); if (!self.forcing_close) {
// We cannot trigger a close directly in here as the page will stay
// alive until this handler returns, breaking the assumption where
// no pages means they are all destroyed.
//
// Schedule the close request to happen in the next event cycle.
_ = glib.idleAddOnce(glibIdleOnceCloseTab, tab);
}
return 1; return 1;
} }
@@ -269,3 +277,8 @@ fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callcon
const title = page.getTitle(); const title = page.getTitle();
self.window.setTitle(std.mem.span(title)); self.window.setTitle(std.mem.span(title));
} }
fn glibIdleOnceCloseTab(data: ?*anyopaque) callconv(.c) void {
const tab: *Tab = @ptrCast(@alignCast(data orelse return));
tab.closeWithConfirmation();
}