mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-28 04:18:38 +03:00
Merge pull request #2485 from axdank/move_current_Tab
gui: add move_current_tab action
This commit is contained in:
@ -15,6 +15,7 @@ extern "C" {
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
// Macros
|
// Macros
|
||||||
@ -379,6 +380,11 @@ typedef struct {
|
|||||||
ghostty_action_resize_split_direction_e direction;
|
ghostty_action_resize_split_direction_e direction;
|
||||||
} ghostty_action_resize_split_s;
|
} ghostty_action_resize_split_s;
|
||||||
|
|
||||||
|
// apprt.action.MoveTab
|
||||||
|
typedef struct {
|
||||||
|
ssize_t amount;
|
||||||
|
} ghostty_action_move_tab_s;
|
||||||
|
|
||||||
// apprt.action.GotoTab
|
// apprt.action.GotoTab
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_GOTO_TAB_PREVIOUS = -1,
|
GHOSTTY_GOTO_TAB_PREVIOUS = -1,
|
||||||
@ -517,6 +523,7 @@ typedef enum {
|
|||||||
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
||||||
GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL,
|
GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL,
|
||||||
GHOSTTY_ACTION_TOGGLE_VISIBILITY,
|
GHOSTTY_ACTION_TOGGLE_VISIBILITY,
|
||||||
|
GHOSTTY_ACTION_MOVE_TAB,
|
||||||
GHOSTTY_ACTION_GOTO_TAB,
|
GHOSTTY_ACTION_GOTO_TAB,
|
||||||
GHOSTTY_ACTION_GOTO_SPLIT,
|
GHOSTTY_ACTION_GOTO_SPLIT,
|
||||||
GHOSTTY_ACTION_RESIZE_SPLIT,
|
GHOSTTY_ACTION_RESIZE_SPLIT,
|
||||||
@ -543,6 +550,7 @@ typedef enum {
|
|||||||
typedef union {
|
typedef union {
|
||||||
ghostty_action_split_direction_e new_split;
|
ghostty_action_split_direction_e new_split;
|
||||||
ghostty_action_fullscreen_e toggle_fullscreen;
|
ghostty_action_fullscreen_e toggle_fullscreen;
|
||||||
|
ghostty_action_move_tab_s move_tab;
|
||||||
ghostty_action_goto_tab_e goto_tab;
|
ghostty_action_goto_tab_e goto_tab;
|
||||||
ghostty_action_goto_split_e goto_split;
|
ghostty_action_goto_split_e goto_split;
|
||||||
ghostty_action_resize_split_s resize_split;
|
ghostty_action_resize_split_s resize_split;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
A5333E242B5A22D9008AEFF7 /* Ghostty.Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */; };
|
A5333E242B5A22D9008AEFF7 /* Ghostty.Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */; };
|
||||||
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; };
|
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; };
|
||||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
|
||||||
|
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */; };
|
||||||
A53D0C8E2B53B0EA00305CE6 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
A53D0C8E2B53B0EA00305CE6 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||||
A53D0C942B53B43700305CE6 /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C932B53B43700305CE6 /* iOSApp.swift */; };
|
A53D0C942B53B43700305CE6 /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C932B53B43700305CE6 /* iOSApp.swift */; };
|
||||||
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
@ -115,6 +116,7 @@
|
|||||||
A5333E212B5A2128008AEFF7 /* SurfaceView_AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_AppKit.swift; sourceTree = "<group>"; };
|
A5333E212B5A2128008AEFF7 /* SurfaceView_AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_AppKit.swift; sourceTree = "<group>"; };
|
||||||
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
|
||||||
|
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Action.swift; sourceTree = "<group>"; };
|
||||||
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
||||||
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTerminalController.swift; sourceTree = "<group>"; };
|
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTerminalController.swift; sourceTree = "<group>"; };
|
||||||
@ -317,6 +319,7 @@
|
|||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */,
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */,
|
||||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
||||||
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
||||||
|
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */,
|
||||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
||||||
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
||||||
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
||||||
@ -601,6 +604,7 @@
|
|||||||
A57D79272C9C879B001D522E /* SecureInput.swift in Sources */,
|
A57D79272C9C879B001D522E /* SecureInput.swift in Sources */,
|
||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
|
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */,
|
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */,
|
||||||
|
@ -40,6 +40,11 @@ class TerminalController: BaseTerminalController {
|
|||||||
selector: #selector(onToggleFullscreen),
|
selector: #selector(onToggleFullscreen),
|
||||||
name: Ghostty.Notification.ghosttyToggleFullscreen,
|
name: Ghostty.Notification.ghosttyToggleFullscreen,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(onMoveTab),
|
||||||
|
name: .ghosttyMoveTab,
|
||||||
|
object: nil)
|
||||||
center.addObserver(
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(onGotoTab),
|
selector: #selector(onGotoTab),
|
||||||
@ -482,6 +487,44 @@ class TerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
//MARK: - Notifications
|
//MARK: - Notifications
|
||||||
|
|
||||||
|
@objc private func onMoveTab(notification: SwiftUI.Notification) {
|
||||||
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
|
guard target == self.focusedSurface else { return }
|
||||||
|
guard let window = self.window else { return }
|
||||||
|
|
||||||
|
// Get the move action
|
||||||
|
guard let action = notification.userInfo?[Notification.Name.GhosttyMoveTabKey] as? Ghostty.Action.MoveTab else { return }
|
||||||
|
guard action.amount != 0 else { return }
|
||||||
|
|
||||||
|
// Determine our current selected index
|
||||||
|
guard let windowController = window.windowController else { return }
|
||||||
|
guard let tabGroup = windowController.window?.tabGroup else { return }
|
||||||
|
guard let selectedWindow = tabGroup.selectedWindow else { return }
|
||||||
|
let tabbedWindows = tabGroup.windows
|
||||||
|
guard tabbedWindows.count > 0 else { return }
|
||||||
|
guard let selectedIndex = tabbedWindows.firstIndex(where: { $0 == selectedWindow }) else { return }
|
||||||
|
|
||||||
|
// Determine the final index we want to insert our tab
|
||||||
|
let finalIndex: Int
|
||||||
|
if action.amount < 0 {
|
||||||
|
finalIndex = selectedIndex - min(selectedIndex, -action.amount)
|
||||||
|
} else {
|
||||||
|
let remaining: Int = tabbedWindows.count - 1 - selectedIndex
|
||||||
|
finalIndex = selectedIndex + min(remaining, action.amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our index is the same we do nothing
|
||||||
|
guard finalIndex != selectedIndex else { return }
|
||||||
|
|
||||||
|
// Get our parent
|
||||||
|
let parent = tabbedWindows[finalIndex]
|
||||||
|
|
||||||
|
// Move our current selected window to the proper index
|
||||||
|
tabGroup.removeWindow(selectedWindow)
|
||||||
|
parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||||
|
selectedWindow.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard target == self.focusedSurface else { return }
|
guard target == self.focusedSurface else { return }
|
||||||
|
15
macos/Sources/Ghostty/Ghostty.Action.swift
Normal file
15
macos/Sources/Ghostty/Ghostty.Action.swift
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import GhosttyKit
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
struct Action {}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Ghostty.Action {
|
||||||
|
struct MoveTab {
|
||||||
|
let amount: Int
|
||||||
|
|
||||||
|
init(c: ghostty_action_move_tab_s) {
|
||||||
|
self.amount = c.amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -458,6 +458,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
||||||
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_MOVE_TAB:
|
||||||
|
moveTab(app, target: target, move: action.action.move_tab)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_GOTO_TAB:
|
case GHOSTTY_ACTION_GOTO_TAB:
|
||||||
gotoTab(app, target: target, tab: action.action.goto_tab)
|
gotoTab(app, target: target, tab: action.action.goto_tab)
|
||||||
|
|
||||||
@ -666,6 +669,31 @@ extension Ghostty {
|
|||||||
appDelegate.toggleVisibility(self)
|
appDelegate.toggleVisibility(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func moveTab(
|
||||||
|
_ app: ghostty_app_t,
|
||||||
|
target: ghostty_target_s,
|
||||||
|
move: ghostty_action_move_tab_s) {
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
Ghostty.logger.warning("move tab does nothing with an app target")
|
||||||
|
return
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return }
|
||||||
|
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .ghosttyMoveTab,
|
||||||
|
object: surfaceView,
|
||||||
|
userInfo: [
|
||||||
|
SwiftUI.Notification.Name.GhosttyMoveTabKey: Action.MoveTab(c: move),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func gotoTab(
|
private static func gotoTab(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
@ -203,8 +203,16 @@ extension Ghostty {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Surface Notifications
|
// MARK: Surface Notification
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
/// Goto tab. Has tab index in the userinfo.
|
||||||
|
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
||||||
|
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
||||||
|
// namespace was the old namespace.
|
||||||
extension Ghostty.Notification {
|
extension Ghostty.Notification {
|
||||||
/// Used to pass a configuration along when creating a new tab/window/split.
|
/// Used to pass a configuration along when creating a new tab/window/split.
|
||||||
static let NewSurfaceConfigKey = "com.mitchellh.ghostty.newSurfaceConfig"
|
static let NewSurfaceConfigKey = "com.mitchellh.ghostty.newSurfaceConfig"
|
||||||
|
@ -3913,6 +3913,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
.move_tab => |position| try self.rt_app.performAction(
|
||||||
|
.{ .surface = self },
|
||||||
|
.move_tab,
|
||||||
|
.{ .amount = position },
|
||||||
|
),
|
||||||
|
|
||||||
.new_split => |direction| try self.rt_app.performAction(
|
.new_split => |direction| try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.new_split,
|
.new_split,
|
||||||
|
@ -100,6 +100,13 @@ pub const Action = union(Key) {
|
|||||||
/// Toggle the visibility of all Ghostty terminal windows.
|
/// Toggle the visibility of all Ghostty terminal windows.
|
||||||
toggle_visibility,
|
toggle_visibility,
|
||||||
|
|
||||||
|
/// Moves a tab by a relative offset.
|
||||||
|
///
|
||||||
|
/// Adjusts the tab position based on `offset` (e.g., -1 for left, +1
|
||||||
|
/// for right). If the new position is out of bounds, it wraps around
|
||||||
|
/// cyclically within the tab range.
|
||||||
|
move_tab: MoveTab,
|
||||||
|
|
||||||
/// Jump to a specific tab. Must handle the scenario that the tab
|
/// Jump to a specific tab. Must handle the scenario that the tab
|
||||||
/// value is invalid.
|
/// value is invalid.
|
||||||
goto_tab: GotoTab,
|
goto_tab: GotoTab,
|
||||||
@ -190,6 +197,7 @@ pub const Action = union(Key) {
|
|||||||
toggle_window_decorations,
|
toggle_window_decorations,
|
||||||
toggle_quick_terminal,
|
toggle_quick_terminal,
|
||||||
toggle_visibility,
|
toggle_visibility,
|
||||||
|
move_tab,
|
||||||
goto_tab,
|
goto_tab,
|
||||||
goto_split,
|
goto_split,
|
||||||
resize_split,
|
resize_split,
|
||||||
@ -308,6 +316,10 @@ pub const ResizeSplit = extern struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const MoveTab = extern struct {
|
||||||
|
amount: isize,
|
||||||
|
};
|
||||||
|
|
||||||
/// The tab to jump to. This is non-exhaustive so that integer values represent
|
/// The tab to jump to. This is non-exhaustive so that integer values represent
|
||||||
/// the index (zero-based) of the tab to jump to. Negative values are special
|
/// the index (zero-based) of the tab to jump to. Negative values are special
|
||||||
/// values.
|
/// values.
|
||||||
|
@ -213,6 +213,7 @@ pub const App = struct {
|
|||||||
.toggle_quick_terminal,
|
.toggle_quick_terminal,
|
||||||
.toggle_visibility,
|
.toggle_visibility,
|
||||||
.goto_tab,
|
.goto_tab,
|
||||||
|
.move_tab,
|
||||||
.inspector,
|
.inspector,
|
||||||
.render_inspector,
|
.render_inspector,
|
||||||
.quit_timer,
|
.quit_timer,
|
||||||
|
@ -456,6 +456,7 @@ pub fn performAction(
|
|||||||
|
|
||||||
.new_tab => try self.newTab(target),
|
.new_tab => try self.newTab(target),
|
||||||
.goto_tab => self.gotoTab(target, value),
|
.goto_tab => self.gotoTab(target, value),
|
||||||
|
.move_tab => self.moveTab(target, value),
|
||||||
.new_split => try self.newSplit(target, value),
|
.new_split => try self.newSplit(target, value),
|
||||||
.resize_split => self.resizeSplit(target, value),
|
.resize_split => self.resizeSplit(target, value),
|
||||||
.equalize_splits => self.equalizeSplits(target),
|
.equalize_splits => self.equalizeSplits(target),
|
||||||
@ -527,6 +528,23 @@ fn gotoTab(_: *App, target: apprt.Target, tab: apprt.action.GotoTab) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn moveTab(_: *App, target: apprt.Target, move_tab: apprt.action.MoveTab) void {
|
||||||
|
switch (target) {
|
||||||
|
.app => {},
|
||||||
|
.surface => |v| {
|
||||||
|
const window = v.rt_surface.container.window() orelse {
|
||||||
|
log.info(
|
||||||
|
"moveTab invalid for container={s}",
|
||||||
|
.{@tagName(v.rt_surface.container)},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.moveTab(v.rt_surface, @intCast(move_tab.amount));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn newSplit(
|
fn newSplit(
|
||||||
self: *App,
|
self: *App,
|
||||||
target: apprt.Target,
|
target: apprt.Target,
|
||||||
|
@ -456,6 +456,15 @@ pub fn gotoNextTab(self: *Window, surface: *Surface) void {
|
|||||||
self.focusCurrentTab();
|
self.focusCurrentTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move the current tab for a surface.
|
||||||
|
pub fn moveTab(self: *Window, surface: *Surface, position: c_int) void {
|
||||||
|
const tab = surface.container.tab() orelse {
|
||||||
|
log.info("surface is not attached to a tab bar, cannot navigate", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.notebook.moveTab(tab, position);
|
||||||
|
}
|
||||||
|
|
||||||
/// Go to the next tab for a surface.
|
/// Go to the next tab for a surface.
|
||||||
pub fn gotoLastTab(self: *Window) void {
|
pub fn gotoLastTab(self: *Window) void {
|
||||||
const max = self.notebook.nPages() -| 1;
|
const max = self.notebook.nPages() -| 1;
|
||||||
|
@ -183,6 +183,35 @@ pub const Notebook = union(enum) {
|
|||||||
self.gotoNthTab(next_idx);
|
self.gotoNthTab(next_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn moveTab(self: Notebook, tab: *Tab, position: c_int) void {
|
||||||
|
const page_idx = self.getTabPosition(tab) orelse return;
|
||||||
|
|
||||||
|
const max = self.nPages() -| 1;
|
||||||
|
var new_position: c_int = page_idx + position;
|
||||||
|
|
||||||
|
if (new_position < 0) {
|
||||||
|
new_position = max + new_position + 1;
|
||||||
|
} else if (new_position > max) {
|
||||||
|
new_position = new_position - max - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_position == page_idx) return;
|
||||||
|
self.reorderPage(tab, new_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reorderPage(self: Notebook, tab: *Tab, position: c_int) void {
|
||||||
|
switch (self) {
|
||||||
|
.gtk_notebook => |notebook| {
|
||||||
|
c.gtk_notebook_reorder_child(notebook, @ptrCast(tab.box), position);
|
||||||
|
},
|
||||||
|
.adw_tab_view => |tab_view| {
|
||||||
|
if (comptime !adwaita.versionAtLeast(0, 0, 0)) unreachable;
|
||||||
|
const page = c.adw_tab_view_get_page(tab_view, @ptrCast(tab.box));
|
||||||
|
_ = c.adw_tab_view_reorder_page(tab_view, page, position);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setTabLabel(self: Notebook, tab: *Tab, title: [:0]const u8) void {
|
pub fn setTabLabel(self: Notebook, tab: *Tab, title: [:0]const u8) void {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
.adw_tab_view => |tab_view| {
|
.adw_tab_view => |tab_view| {
|
||||||
|
@ -301,6 +301,11 @@ pub const Action = union(enum) {
|
|||||||
/// is higher than the number of tabs, this will go to the last tab.
|
/// is higher than the number of tabs, this will go to the last tab.
|
||||||
goto_tab: usize,
|
goto_tab: usize,
|
||||||
|
|
||||||
|
/// Moves a tab by a relative offset.
|
||||||
|
/// Adjusts the tab position based on `offset` (e.g., -1 for left, +1 for right).
|
||||||
|
/// If the new position is out of bounds, it wraps around cyclically within the tab range.
|
||||||
|
move_tab: isize,
|
||||||
|
|
||||||
/// Toggle the tab overview.
|
/// Toggle the tab overview.
|
||||||
/// This only works with libadwaita enabled currently.
|
/// This only works with libadwaita enabled currently.
|
||||||
toggle_tab_overview: void,
|
toggle_tab_overview: void,
|
||||||
@ -648,6 +653,7 @@ pub const Action = union(enum) {
|
|||||||
.next_tab,
|
.next_tab,
|
||||||
.last_tab,
|
.last_tab,
|
||||||
.goto_tab,
|
.goto_tab,
|
||||||
|
.move_tab,
|
||||||
.toggle_tab_overview,
|
.toggle_tab_overview,
|
||||||
.new_split,
|
.new_split,
|
||||||
.goto_split,
|
.goto_split,
|
||||||
|
Reference in New Issue
Block a user