mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: navigate splits directionally
This commit is contained in:
@ -92,6 +92,21 @@ extension Ghostty {
|
||||
/// No neighbors, used by the root node.
|
||||
static let empty: Self = .init()
|
||||
|
||||
/// Get the node for a given direction.
|
||||
func get(direction: SplitFocusDirection) -> SplitNode? {
|
||||
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
|
||||
.previous: \.previous,
|
||||
.next: \.next,
|
||||
.top: \.top,
|
||||
.bottom: \.bottom,
|
||||
.left: \.left,
|
||||
.right: \.right,
|
||||
]
|
||||
|
||||
guard let path = map[direction] else { return nil }
|
||||
return self[keyPath: path]
|
||||
}
|
||||
|
||||
/// Update multiple keys and return a new copy.
|
||||
func update(_ attrs: [WritableKeyPath<Self, SplitNode?>: SplitNode?]) -> Self {
|
||||
var clone = self
|
||||
@ -216,15 +231,8 @@ extension Ghostty {
|
||||
// Determine our desired direction
|
||||
guard let directionAny = notification.userInfo?[Notification.SplitDirectionKey] else { return }
|
||||
guard let direction = directionAny as? SplitFocusDirection else { return }
|
||||
switch (direction) {
|
||||
case .previous:
|
||||
guard let next = neighbors.previous else { return }
|
||||
Self.moveFocus(next, previous: node)
|
||||
|
||||
case .next:
|
||||
guard let next = neighbors.next else { return }
|
||||
Self.moveFocus(next, previous: node)
|
||||
}
|
||||
guard let next = neighbors.get(direction: direction) else { return }
|
||||
Self.moveFocus(next, previous: node)
|
||||
}
|
||||
|
||||
/// There is a bug I can't figure out where when changing the split state, the terminal view
|
||||
|
@ -11,7 +11,7 @@ struct Ghostty {
|
||||
extension Ghostty {
|
||||
/// An enum that is used for the directions that a split focus event can change.
|
||||
enum SplitFocusDirection {
|
||||
case previous, next
|
||||
case previous, next, top, bottom, left, right
|
||||
|
||||
/// Initialize from a Ghostty API enum.
|
||||
static func from(direction: ghostty_split_focus_direction_e) -> Self? {
|
||||
@ -22,6 +22,18 @@ extension Ghostty {
|
||||
case GHOSTTY_SPLIT_FOCUS_NEXT:
|
||||
return .next
|
||||
|
||||
case GHOSTTY_SPLIT_FOCUS_TOP:
|
||||
return .top
|
||||
|
||||
case GHOSTTY_SPLIT_FOCUS_BOTTOM:
|
||||
return .bottom
|
||||
|
||||
case GHOSTTY_SPLIT_FOCUS_LEFT:
|
||||
return .left
|
||||
|
||||
case GHOSTTY_SPLIT_FOCUS_RIGHT:
|
||||
return .right
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -34,6 +46,18 @@ extension Ghostty {
|
||||
|
||||
case .next:
|
||||
return GHOSTTY_SPLIT_FOCUS_NEXT
|
||||
|
||||
case .top:
|
||||
return GHOSTTY_SPLIT_FOCUS_TOP
|
||||
|
||||
case .bottom:
|
||||
return GHOSTTY_SPLIT_FOCUS_BOTTOM
|
||||
|
||||
case .left:
|
||||
return GHOSTTY_SPLIT_FOCUS_LEFT
|
||||
|
||||
case .right:
|
||||
return GHOSTTY_SPLIT_FOCUS_RIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,21 @@ struct GhosttyApp: App {
|
||||
|
||||
CommandGroup(before: .windowArrangement) {
|
||||
Divider()
|
||||
Button("Select Previous Split", action: splitMoveFocusPrevious).keyboardShortcut("[", modifiers: .command)
|
||||
Button("Select Next Split", action: splitMoveFocusNext).keyboardShortcut("]", modifiers: .command)
|
||||
Button("Select Previous Split") { splitMoveFocus(direction: .previous) }
|
||||
.keyboardShortcut("[", modifiers: .command)
|
||||
Button("Select Next Split") { splitMoveFocus(direction: .next) }
|
||||
.keyboardShortcut("]", modifiers: .command)
|
||||
Menu("Select Split") {
|
||||
Button("Select Split Above") { splitMoveFocus(direction: .top) }
|
||||
.keyboardShortcut(.upArrow, modifiers: [.command, .option])
|
||||
Button("Select Split Below") { splitMoveFocus(direction: .bottom) }
|
||||
.keyboardShortcut(.downArrow, modifiers: [.command, .option])
|
||||
Button("Select Split Left") { splitMoveFocus(direction: .left) }
|
||||
.keyboardShortcut(.leftArrow, modifiers: [.command, .option])
|
||||
Button("Select Split Right") { splitMoveFocus(direction: .right)}
|
||||
.keyboardShortcut(.rightArrow, modifiers: [.command, .option])
|
||||
}
|
||||
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
@ -84,16 +97,10 @@ struct GhosttyApp: App {
|
||||
ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DOWN)
|
||||
}
|
||||
|
||||
func splitMoveFocusPrevious() {
|
||||
func splitMoveFocus(direction: Ghostty.SplitFocusDirection) {
|
||||
guard let surfaceView = focusedSurface else { return }
|
||||
guard let surface = surfaceView.surface else { return }
|
||||
ghostty.splitMoveFocus(surface: surface, direction: .previous)
|
||||
}
|
||||
|
||||
func splitMoveFocusNext() {
|
||||
guard let surfaceView = focusedSurface else { return }
|
||||
guard let surface = surfaceView.surface else { return }
|
||||
ghostty.splitMoveFocus(surface: surface, direction: .next)
|
||||
ghostty.splitMoveFocus(surface: surface, direction: direction)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,6 +321,26 @@ pub const Config = struct {
|
||||
.{ .key = .right_bracket, .mods = .{ .super = true } },
|
||||
.{ .goto_split = .next },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .up, .mods = .{ .super = true, .alt = true } },
|
||||
.{ .goto_split = .top },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .down, .mods = .{ .super = true, .alt = true } },
|
||||
.{ .goto_split = .bottom },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .left, .mods = .{ .super = true, .alt = true } },
|
||||
.{ .goto_split = .left },
|
||||
);
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .right, .mods = .{ .super = true, .alt = true } },
|
||||
.{ .goto_split = .right },
|
||||
);
|
||||
{
|
||||
// Cmd+N for goto tab N
|
||||
const start = @enumToInt(inputpkg.Key.one);
|
||||
|
Reference in New Issue
Block a user