From d9c4bd7e25d8f1ef4315ca91bb7c9fbaf74dafa0 Mon Sep 17 00:00:00 2001 From: Ryota Date: Fri, 12 Jan 2024 00:52:17 +0000 Subject: [PATCH 1/5] Add helper functions for finding root --- macos/Sources/Ghostty/Ghostty.SplitNode.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index d9403b62f..daf059864 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -264,6 +264,13 @@ extension Ghostty { return weight } + /// Returns the top most parent, or this container. Because this + /// would fall back to use to self, the return value is guaranteed. + func rootContainer() -> Container { + guard let parent = self.parent else { return self } + return parent.rootContainer() + } + // MARK: - Hashable func hash(into hasher: inout Hasher) { From ab8dfab9de0d2900808fb9dd7a133d840fd30f30 Mon Sep 17 00:00:00 2001 From: Ryota Date: Fri, 12 Jan 2024 00:52:26 +0000 Subject: [PATCH 2/5] Add helper func for first and last leaf --- macos/Sources/Ghostty/Ghostty.SplitNode.swift | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index daf059864..8c0fe406d 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -271,6 +271,30 @@ extension Ghostty { return parent.rootContainer() } + /// Returns the first leaf from the given container. This is most + /// useful for root container, so that we can find the top-left-most + /// leaf. + func firstLeaf() -> Leaf { + switch (self.topLeft) { + case .leaf(let leaf): + return leaf + case .split(let s): + return s.firstLeaf() + } + } + + /// Returns the last leaf from the given container. This is most + /// useful for root container, so that we can find the bottom-right- + /// most leaf. + func lastLeaf() -> Leaf { + switch (self.bottomRight) { + case .leaf(let leaf): + return leaf + case .split(let s): + return s.lastLeaf() + } + } + // MARK: - Hashable func hash(into hasher: inout Hasher) { From 3500293bac5b27b7c0049c9b8fced9f06250b19d Mon Sep 17 00:00:00 2001 From: Ryota Date: Fri, 12 Jan 2024 00:53:19 +0000 Subject: [PATCH 3/5] Add firstOrLast search based on next and previous --- macos/Sources/Ghostty/Ghostty.SplitNode.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index 8c0fe406d..005f060fe 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -64,6 +64,25 @@ extension Ghostty { return node.preferredFocus(direction) } + /// When direction is either next or previous, return the first or last + /// leaf. This can be used when the focus needs to move to a leaf even + /// after hitting the bottom-right-most or top-left-most surface. + /// When the direction is not next or previous (such as top, bottom, + /// left, right), it will be ignored and no leaf will be returned. + func firstOrLast(_ direction: SplitFocusDirection) -> Leaf? { + // If there is no parent, simply ignore. + guard let root = self.parent?.rootContainer() else { return nil } + + switch (direction) { + case .next: + return root.firstLeaf() + case .previous: + return root.lastLeaf() + default: + return nil + } + } + /// Close the surface associated with this node. This will likely deinitialize the /// surface. At this point, the surface view in this node tree can never be used again. func close() { From 3baae1dce82a97ba96703ec18797a8890a018f7f Mon Sep 17 00:00:00 2001 From: Ryota Date: Fri, 12 Jan 2024 00:53:47 +0000 Subject: [PATCH 4/5] Wrap around split focus with next and previous --- macos/Sources/Ghostty/Ghostty.TerminalSplit.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift index 2d4b7881e..0b7eb9bb8 100644 --- a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift +++ b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift @@ -247,9 +247,19 @@ extension Ghostty { // Determine our desired direction guard let directionAny = notification.userInfo?[Notification.SplitDirectionKey] else { return } guard let direction = directionAny as? SplitFocusDirection else { return } - guard let next = neighbors.get(direction: direction) else { return } + + // Find the next surface to move to. In most cases this should be + // finding the neighbor in provided direction, and focus it. When + // the neighbor cannot be found based on next or previous direction, + // this would instead search for first or last leaf and focus it + // instead, giving the wrap around effect. + // When other directions are provided, this can be nil, and early + // returned. + guard let nextSurface = neighbors.get(direction: direction)?.preferredFocus(direction) + ?? node?.firstOrLast(direction)?.surface else { return } + Ghostty.moveFocus( - to: next.preferredFocus(direction) + to: nextSurface ) } From ad9de6707db4fb613f2e138905b0a91fc482e2c9 Mon Sep 17 00:00:00 2001 From: Ryota Date: Fri, 12 Jan 2024 00:54:16 +0000 Subject: [PATCH 5/5] Fix comment --- macos/Sources/Ghostty/Ghostty.TerminalSplit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift index 0b7eb9bb8..e7edb041c 100644 --- a/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift +++ b/macos/Sources/Ghostty/Ghostty.TerminalSplit.swift @@ -152,7 +152,7 @@ extension Ghostty { /// The neighbors, used for navigation. let neighbors: SplitNode.Neighbors - /// The SplitNode that the leaf belongs to. This will be set to nil but when leaf is closed. + /// The SplitNode that the leaf belongs to. This will be set to nil when leaf is closed. @Binding var node: SplitNode? var body: some View {