From 2d1232878d9ddbf0b67cfd5d059af08ed4fe1e80 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 29 Jul 2025 14:40:23 -0700 Subject: [PATCH] apprt/gtk-ng: goto_tab --- src/apprt/gtk-ng/class/application.zig | 29 ++++++++++++- src/apprt/gtk-ng/class/window.zig | 57 ++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/apprt/gtk-ng/class/application.zig b/src/apprt/gtk-ng/class/application.zig index 0d5f34237..2efd6e211 100644 --- a/src/apprt/gtk-ng/class/application.zig +++ b/src/apprt/gtk-ng/class/application.zig @@ -497,6 +497,8 @@ pub const Application = extern struct { value.config, ), + .goto_tab => return Action.gotoTab(target, value), + .mouse_over_link => Action.mouseOverLink(target, value), .mouse_shape => Action.mouseShape(target, value), .mouse_visibility => Action.mouseVisibility(target, value), @@ -535,7 +537,6 @@ pub const Application = extern struct { .toggle_fullscreen => Action.toggleFullscreen(target), // Unimplemented but todo on gtk-ng branch - .goto_tab, .move_tab, .new_split, .resize_split, @@ -1216,6 +1217,32 @@ const Action = struct { } } + pub fn gotoTab( + target: apprt.Target, + tab: apprt.action.GotoTab, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring new_tab", .{}); + return false; + }; + + return window.selectTab(switch (tab) { + .previous => .previous, + .next => .next, + .last => .last, + else => .{ .n = @intCast(@intFromEnum(tab)) }, + }); + }, + } + } + pub fn mouseOverLink( target: apprt.Target, value: apprt.action.MouseOverLink, diff --git a/src/apprt/gtk-ng/class/window.zig b/src/apprt/gtk-ng/class/window.zig index 70594f53a..8b5074968 100644 --- a/src/apprt/gtk-ng/class/window.zig +++ b/src/apprt/gtk-ng/class/window.zig @@ -350,6 +350,63 @@ pub const Window = extern struct { return page; } + pub const SelectTab = union(enum) { + previous, + next, + last, + n: usize, + }; + + /// Select the tab as requested. Returns true if the tab selection + /// changed. + pub fn selectTab(self: *Self, n: SelectTab) bool { + const priv = self.private(); + const tab_view = priv.tab_view; + + // Get our current tab numeric position + const selected = tab_view.getSelectedPage() orelse return false; + const current = tab_view.getPagePosition(selected); + + // Get our total + const total = tab_view.getNPages(); + + const goto: c_int = switch (n) { + .previous => if (current > 0) + current - 1 + else + total - 1, + + .next => if (current < total - 1) + current + 1 + else + 0, + + .last => total - 1, + + .n => |v| n: { + // 1-indexed + if (v == 0) return false; + + const n_int = std.math.cast( + c_int, + v, + ) orelse return false; + break :n @min(n_int - 1, total - 1); + }, + }; + assert(goto >= 0); + assert(goto < total); + + // If our target is the same as our current then we do nothing. + if (goto == current) return false; + + // Add the page and select it + const page = tab_view.getNthPage(goto); + tab_view.setSelectedPage(page); + + return true; + } + /// Updates various appearance properties. This should always be safe /// to call multiple times. This should be called whenever a change /// happens that might affect how the window appears (config change,