diff --git a/include/ghostty.h b/include/ghostty.h index 0e444a2fa..29da8f37b 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -562,6 +562,7 @@ typedef enum { GHOSTTY_ACTION_QUIT, GHOSTTY_ACTION_NEW_WINDOW, GHOSTTY_ACTION_NEW_TAB, + GHOSTTY_ACTION_CLOSE_TAB, GHOSTTY_ACTION_NEW_SPLIT, GHOSTTY_ACTION_CLOSE_ALL_WINDOWS, GHOSTTY_ACTION_TOGGLE_FULLSCREEN, diff --git a/src/Surface.zig b/src/Surface.zig index 70c32098f..50ef3c0cf 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -4061,6 +4061,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool {}, ), + .close_tab => try self.rt_app.performAction( + .{ .surface = self }, + .close_tab, + {}, + ), + inline .previous_tab, .next_tab, .last_tab, diff --git a/src/apprt/action.zig b/src/apprt/action.zig index df30f7b7b..25e1cd640 100644 --- a/src/apprt/action.zig +++ b/src/apprt/action.zig @@ -82,6 +82,9 @@ pub const Action = union(Key) { /// the tab should be opened in a new window. new_tab, + /// Closes the tab belonging to the currently focused split. + close_tab, + /// Create a new split. The value determines the location of the split /// relative to the target. new_split: SplitDirection, @@ -225,6 +228,7 @@ pub const Action = union(Key) { quit, new_window, new_tab, + close_tab, new_split, close_all_windows, toggle_fullscreen, diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 38c019b3e..e3cffde96 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -477,6 +477,7 @@ pub fn performAction( .toggle_fullscreen => self.toggleFullscreen(target, value), .new_tab => try self.newTab(target), + .close_tab => try self.closeTab(target), .goto_tab => self.gotoTab(target, value), .move_tab => self.moveTab(target, value), .new_split => try self.newSplit(target, value), @@ -532,6 +533,23 @@ fn newTab(_: *App, target: apprt.Target) !void { } } +fn closeTab(_: *App, target: apprt.Target) !void { + switch (target) { + .app => {}, + .surface => |v| { + const window = v.rt_surface.container.window() orelse { + log.info( + "close_tab invalid for container={s}", + .{@tagName(v.rt_surface.container)}, + ); + return; + }; + // TODO: get tab from surface + try window.closeTab(v); + }, + } +} + fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) void { switch (target) { .app => {}, @@ -1743,6 +1761,7 @@ fn initMenu(self: *App) void { c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section))); c.g_menu_append(section, "New Window", "win.new_window"); c.g_menu_append(section, "New Tab", "win.new_tab"); + c.g_menu_append(section, "Close Tab", "win.close_tab"); c.g_menu_append(section, "Split Right", "win.split_right"); c.g_menu_append(section, "Split Down", "win.split_down"); c.g_menu_append(section, "Close Window", "win.close"); diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 64e07e85e..d0a34efe4 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -383,6 +383,9 @@ pub const Action = union(enum) { /// This only works for macOS currently. close_all_windows: void, + /// Closes the tab belonging to the currently focused split. + close_tab: void, + /// Toggle fullscreen mode of window. toggle_fullscreen: void, @@ -750,6 +753,7 @@ pub const Action = union(enum) { .resize_split, .equalize_splits, .inspector, + .close_tab, => .surface, }; }