apprt/gtk: adw tab view close confirmation

This commit is contained in:
Mitchell Hashimoto
2025-01-10 15:32:25 -08:00
parent 8c1ad59de7
commit 00137c4189
3 changed files with 62 additions and 26 deletions

View File

@ -130,7 +130,7 @@ fn needsConfirm(elem: Surface.Container.Elem) bool {
}
/// Close the tab, asking for confirmation if any surface requests it.
fn closeWithConfirmation(tab: *Tab) void {
pub fn closeWithConfirmation(tab: *Tab) void {
switch (tab.elem) {
.surface => |s| s.close(s.core_surface.needsConfirmQuit()),
.split => |s| {
@ -169,21 +169,15 @@ fn closeWithConfirmation(tab: *Tab) void {
}
}
pub fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
const tab: *Tab = @ptrCast(@alignCast(ud));
tab.closeWithConfirmation();
}
fn gtkTabCloseConfirmation(
alert: *c.GtkMessageDialog,
response: c.gint,
ud: ?*anyopaque,
) callconv(.C) void {
const tab: *Tab = @ptrCast(@alignCast(ud));
c.gtk_window_destroy(@ptrCast(alert));
if (response == c.GTK_RESPONSE_YES) {
const tab: *Tab = @ptrCast(@alignCast(ud));
tab.remove();
}
if (response != c.GTK_RESPONSE_YES) return;
tab.remove();
}
fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
@ -194,17 +188,3 @@ fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
const tab: *Tab = @ptrCast(@alignCast(ud));
tab.destroy(tab.window.app.core_app.alloc);
}
pub fn gtkTabClick(
gesture: *c.GtkGestureClick,
_: c.gint,
_: c.gdouble,
_: c.gdouble,
ud: ?*anyopaque,
) callconv(.C) void {
const self: *Tab = @ptrCast(@alignCast(ud));
const gtk_button = c.gtk_gesture_single_get_current_button(@ptrCast(gesture));
if (gtk_button == c.GDK_BUTTON_MIDDLE) {
self.closeWithConfirmation();
}
}

View File

@ -17,6 +17,14 @@ pub const NotebookAdw = struct {
/// the tab view
tab_view: *AdwTabView,
/// Set to true so that the adw close-page handler knows we're forcing
/// and to allow a close to happen with no confirm. This is a bit of a hack
/// because we currently use GTK alerts to confirm tab close and they
/// don't carry with them the ADW state that we are confirming or not.
/// Long term we should move to ADW alerts so we can know if we are
/// confirming or not.
forcing_close: bool = false,
pub fn init(notebook: *Notebook) void {
const window: *Window = @fieldParentPtr("notebook", notebook);
const app = window.app;
@ -38,6 +46,7 @@ pub const NotebookAdw = struct {
};
_ = c.g_signal_connect_data(tab_view, "page-attached", c.G_CALLBACK(&adwPageAttached), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(tab_view, "close-page", c.G_CALLBACK(&adwClosePage), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(tab_view, "create-window", c.G_CALLBACK(&adwTabViewCreateWindow), window, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(tab_view, "notify::selected-page", c.G_CALLBACK(&adwSelectPage), window, null, c.G_CONNECT_DEFAULT);
}
@ -112,6 +121,12 @@ pub const NotebookAdw = struct {
pub fn closeTab(self: *NotebookAdw, tab: *Tab) void {
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
// closeTab always expects to close unconditionally so we mark this
// as true so that the close_page call below doesn't request
// confirmation.
self.forcing_close = true;
defer self.forcing_close = false;
const page = c.adw_tab_view_get_page(self.tab_view, @ptrCast(tab.box)) orelse return;
c.adw_tab_view_close_page(self.tab_view, page);
@ -143,6 +158,28 @@ fn adwPageAttached(_: *AdwTabView, page: *c.AdwTabPage, _: c_int, ud: ?*anyopaqu
window.focusCurrentTab();
}
fn adwClosePage(
_: *AdwTabView,
page: *c.AdwTabPage,
ud: ?*anyopaque,
) callconv(.C) c.gboolean {
const child = c.adw_tab_page_get_child(page);
const tab: *Tab = @ptrCast(@alignCast(c.g_object_get_data(
@ptrCast(child),
Tab.GHOSTTY_TAB,
) orelse return 0));
const window: *Window = @ptrCast(@alignCast(ud.?));
const notebook = window.notebook.adw;
c.adw_tab_view_close_page_finish(
notebook.tab_view,
page,
@intFromBool(notebook.forcing_close),
);
if (!notebook.forcing_close) tab.closeWithConfirmation();
return 1;
}
fn adwTabViewCreateWindow(
_: *AdwTabView,
ud: ?*anyopaque,

View File

@ -157,8 +157,8 @@ pub const NotebookGtk = struct {
c.gtk_gesture_single_set_button(@ptrCast(gesture_tab_click), 0);
c.gtk_widget_add_controller(label_box_widget, @ptrCast(gesture_tab_click));
_ = c.g_signal_connect_data(label_close, "clicked", c.G_CALLBACK(&Tab.gtkTabCloseClick), tab, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(gesture_tab_click, "pressed", c.G_CALLBACK(&Tab.gtkTabClick), tab, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(label_close, "clicked", c.G_CALLBACK(&gtkTabCloseClick), tab, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(gesture_tab_click, "pressed", c.G_CALLBACK(&gtkTabClick), tab, null, c.G_CONNECT_DEFAULT);
// Tab settings
c.gtk_notebook_set_tab_reorderable(self.notebook, box_widget, 1);
@ -283,3 +283,22 @@ fn gtkNotebookCreateWindow(
return newWindow.notebook.gtk.notebook;
}
fn gtkTabCloseClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {
const tab: *Tab = @ptrCast(@alignCast(ud));
tab.closeWithConfirmation();
}
fn gtkTabClick(
gesture: *c.GtkGestureClick,
_: c.gint,
_: c.gdouble,
_: c.gdouble,
ud: ?*anyopaque,
) callconv(.C) void {
const self: *Tab = @ptrCast(@alignCast(ud));
const gtk_button = c.gtk_gesture_single_get_current_button(@ptrCast(gesture));
if (gtk_button == c.GDK_BUTTON_MIDDLE) {
self.closeWithConfirmation();
}
}