From f31bde48fc556e41b712e69862e74ed56ce73347 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 9 Sep 2023 13:00:23 -0700 Subject: [PATCH] macos: add prev/next tab custom binding support --- include/ghostty.h | 5 +++ .../Features/Primary Window/PrimaryView.swift | 25 +++++++++++--- src/apprt/embedded.zig | 34 +++++++++++++++++-- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index 8d5c8cf8c..d11b49fb1 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -76,6 +76,11 @@ typedef enum { GHOSTTY_NON_NATIVE_FULLSCREEN_VISIBLE_MENU, } ghostty_non_native_fullscreen_e; +typedef enum { + GHOSTTY_TAB_PREVIOUS = -1, + GHOSTTY_TAB_NEXT = -2, +} ghostty_tab_e; + // This is a packed struct (see src/input/mouse.zig) but the C standard // afaik doesn't let us reliably define packed structs so we build it up // from scratch. diff --git a/macos/Sources/Features/Primary Window/PrimaryView.swift b/macos/Sources/Features/Primary Window/PrimaryView.swift index 48950dabd..cd9f8e920 100644 --- a/macos/Sources/Features/Primary Window/PrimaryView.swift +++ b/macos/Sources/Features/Primary Window/PrimaryView.swift @@ -148,11 +148,28 @@ struct PrimaryView: View { guard let tabGroup = windowController.window?.tabGroup else { return } let tabbedWindows = tabGroup.windows - // Tabs are 0-indexed here, so we subtract one from the key the user hit. - let adjustedIndex = Int(tabIndex - 1); - guard adjustedIndex >= 0 && adjustedIndex < tabbedWindows.count else { return } + // This will be the index we want to actual go to + let finalIndex: Int - let targetWindow = tabbedWindows[adjustedIndex] + // An index that is invalid is used to signal some special values. + if (tabIndex <= 0) { + guard let selectedWindow = tabGroup.selectedWindow else { return } + guard let selectedIndex = tabbedWindows.firstIndex(where: { $0 == selectedWindow }) else { return } + + if (tabIndex == GHOSTTY_TAB_PREVIOUS.rawValue) { + finalIndex = selectedIndex - 1 + } else if (tabIndex == GHOSTTY_TAB_NEXT.rawValue) { + finalIndex = selectedIndex + 1 + } else { + return + } + } else { + // Tabs are 0-indexed here, so we subtract one from the key the user hit. + finalIndex = Int(tabIndex - 1) + } + + guard finalIndex >= 0 && finalIndex < tabbedWindows.count else { return } + let targetWindow = tabbedWindows[finalIndex] targetWindow.makeKeyAndOrderFront(nil) } diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 7d246c940..650bee842 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -76,12 +76,19 @@ pub const App = struct { toggle_split_zoom: ?*const fn (SurfaceUD) callconv(.C) void = null, /// Goto tab - goto_tab: ?*const fn (SurfaceUD, usize) callconv(.C) void = null, + goto_tab: ?*const fn (SurfaceUD, GotoTab) callconv(.C) void = null, /// Toggle fullscreen for current window. toggle_fullscreen: ?*const fn (SurfaceUD, configpkg.NonNativeFullscreen) callconv(.C) void = null, }; + /// Special values for the goto_tab callback. + const GotoTab = enum(i32) { + previous = -1, + next = -2, + _, + }; + core_app: *CoreApp, config: *const Config, opts: Options, @@ -637,7 +644,30 @@ pub const Surface = struct { return; }; - func(self.opts.userdata, n); + const idx = std.math.cast(i32, n) orelse { + log.warn("cannot cast tab index to i32 n={}", .{n}); + return; + }; + + func(self.opts.userdata, @enumFromInt(idx)); + } + + pub fn gotoPreviousTab(self: *Surface) void { + const func = self.app.opts.goto_tab orelse { + log.info("runtime embedder does not goto_tab", .{}); + return; + }; + + func(self.opts.userdata, .previous); + } + + pub fn gotoNextTab(self: *Surface) void { + const func = self.app.opts.goto_tab orelse { + log.info("runtime embedder does not goto_tab", .{}); + return; + }; + + func(self.opts.userdata, .next); } pub fn toggleFullscreen(self: *Surface, nonNativeFullscreen: configpkg.NonNativeFullscreen) void {