mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-21 06:45:22 +00:00
macos: hook up onDrop to move splits
This commit is contained in:
@@ -4,7 +4,7 @@ import os
|
||||
struct TerminalSplitTreeView: View {
|
||||
let tree: SplitTree<Ghostty.SurfaceView>
|
||||
let onResize: (SplitTree<Ghostty.SurfaceView>.Node, Double) -> Void
|
||||
let onDrop: (Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
let onDrop: (_ source: Ghostty.SurfaceView, _ destination: Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
|
||||
var body: some View {
|
||||
if let node = tree.zoomed ?? tree.root {
|
||||
@@ -28,7 +28,7 @@ struct TerminalSplitSubtreeView: View {
|
||||
let node: SplitTree<Ghostty.SurfaceView>.Node
|
||||
var isRoot: Bool = false
|
||||
let onResize: (SplitTree<Ghostty.SurfaceView>.Node, Double) -> Void
|
||||
let onDrop: (Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
let onDrop: (_ source: Ghostty.SurfaceView, _ destination: Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
|
||||
var body: some View {
|
||||
switch (node) {
|
||||
@@ -68,7 +68,7 @@ struct TerminalSplitSubtreeView: View {
|
||||
struct TerminalSplitLeaf: View {
|
||||
let surfaceView: Ghostty.SurfaceView
|
||||
let isSplit: Bool
|
||||
let onDrop: (Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
let onDrop: (_ source: Ghostty.SurfaceView, _ destination: Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
|
||||
@State private var dropState: DropState = .idle
|
||||
|
||||
@@ -86,7 +86,8 @@ struct TerminalSplitLeaf: View {
|
||||
.onDrop(of: [.ghosttySurfaceId], delegate: SplitDropDelegate(
|
||||
dropState: $dropState,
|
||||
viewSize: geometry.size,
|
||||
onDrop: { zone in onDrop(surfaceView, zone) }
|
||||
destinationSurface: surfaceView,
|
||||
onDrop: onDrop
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -110,7 +111,8 @@ struct TerminalSplitLeaf: View {
|
||||
private struct SplitDropDelegate: DropDelegate {
|
||||
@Binding var dropState: DropState
|
||||
let viewSize: CGSize
|
||||
let onDrop: (TerminalSplitDropZone) -> Void
|
||||
let destinationSurface: Ghostty.SurfaceView
|
||||
let onDrop: (_ source: Ghostty.SurfaceView, _ destination: Ghostty.SurfaceView, TerminalSplitDropZone) -> Void
|
||||
|
||||
func validateDrop(info: DropInfo) -> Bool {
|
||||
info.hasItemsConforming(to: [.ghosttySurfaceId])
|
||||
@@ -134,8 +136,27 @@ struct TerminalSplitLeaf: View {
|
||||
}
|
||||
|
||||
func performDrop(info: DropInfo) -> Bool {
|
||||
let zone = TerminalSplitDropZone.calculate(at: info.location, in: viewSize)
|
||||
dropState = .idle
|
||||
onDrop(.calculate(at: info.location, in: viewSize))
|
||||
|
||||
// Load the dropped surface asynchronously using Transferable
|
||||
let providers = info.itemProviders(for: [.ghosttySurfaceId])
|
||||
guard let provider = providers.first else { return false }
|
||||
_ = provider.loadTransferable(type: Ghostty.SurfaceView.self) { [weak destinationSurface] result in
|
||||
switch result {
|
||||
case .success(let sourceSurface):
|
||||
DispatchQueue.main.async {
|
||||
// Don't allow dropping on self
|
||||
guard let destinationSurface else { return }
|
||||
guard sourceSurface !== destinationSurface else { return }
|
||||
onDrop(sourceSurface, destinationSurface, zone)
|
||||
}
|
||||
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,6 +827,38 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
func splitDidDrop(source: Ghostty.SurfaceView, destination: Ghostty.SurfaceView, zone: TerminalSplitDropZone) {
|
||||
// Find the source node in the tree
|
||||
guard let sourceNode = surfaceTree.root?.node(view: source) else {
|
||||
Ghostty.logger.warning("source surface not found in tree during drop")
|
||||
return
|
||||
}
|
||||
|
||||
// Map drop zone to split direction
|
||||
let direction: SplitTree<Ghostty.SurfaceView>.NewDirection = switch zone {
|
||||
case .top: .up
|
||||
case .bottom: .down
|
||||
case .left: .left
|
||||
case .right: .right
|
||||
}
|
||||
|
||||
// Remove source from its current position first
|
||||
let treeWithoutSource = surfaceTree.remove(sourceNode)
|
||||
|
||||
// Insert source at destination in the appropriate direction
|
||||
do {
|
||||
let newTree = try treeWithoutSource.insert(view: source, at: destination, direction: direction)
|
||||
replaceSurfaceTree(
|
||||
newTree,
|
||||
moveFocusTo: source,
|
||||
moveFocusFrom: focusedSurface,
|
||||
undoAction: "Move Split")
|
||||
} catch {
|
||||
Ghostty.logger.warning("failed to insert surface during drop: \(error)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func performAction(_ action: String, on surfaceView: Ghostty.SurfaceView) {
|
||||
guard let surface = surfaceView.surface else { return }
|
||||
let len = action.utf8CString.count
|
||||
|
||||
@@ -20,6 +20,9 @@ protocol TerminalViewDelegate: AnyObject {
|
||||
|
||||
/// A split is resizing to a given value.
|
||||
func splitDidResize(node: SplitTree<Ghostty.SurfaceView>.Node, to newRatio: Double)
|
||||
|
||||
/// A surface was dropped onto another surface to create a split.
|
||||
func splitDidDrop(source: Ghostty.SurfaceView, destination: Ghostty.SurfaceView, zone: TerminalSplitDropZone)
|
||||
}
|
||||
|
||||
/// The view model is a required implementation for TerminalView callers. This contains
|
||||
@@ -83,8 +86,8 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
TerminalSplitTreeView(
|
||||
tree: viewModel.surfaceTree,
|
||||
onResize: { delegate?.splitDidResize(node: $0, to: $1) },
|
||||
onDrop: { surface, zone in
|
||||
Ghostty.logger.info("Drop on surface \(surface) in zone \(zone.rawValue)")
|
||||
onDrop: { source, destination, zone in
|
||||
delegate?.splitDidDrop(source: source, destination: destination, zone: zone)
|
||||
})
|
||||
.environmentObject(ghostty)
|
||||
.focused($focused)
|
||||
|
||||
Reference in New Issue
Block a user