mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
feat: ensure text, files and URLs can be drag and dropped to terminal (#4962)
This PR ensures one can drag and drop the following things to the terminal window: - File (Inserts the file path into the terminal) - URL - Text This resolves #4932
This commit is contained in:
@ -92,22 +92,6 @@ extension Ghostty {
|
||||
windowFocus = false
|
||||
}
|
||||
}
|
||||
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
|
||||
providers.forEach { provider in
|
||||
_ = provider.loadObject(ofClass: URL.self) { url, _ in
|
||||
guard let url = url else { return }
|
||||
let path = Shell.escape(url.path)
|
||||
DispatchQueue.main.async {
|
||||
surfaceView.insertText(
|
||||
path,
|
||||
replacementRange: NSMakeRange(0, 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
#endif
|
||||
|
||||
// If our geo size changed then we show the resize overlay as configured.
|
||||
|
@ -1,3 +1,4 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import CoreText
|
||||
import UserNotifications
|
||||
@ -230,6 +231,9 @@ extension Ghostty {
|
||||
|
||||
ghostty_surface_set_color_scheme(surface, scheme)
|
||||
}
|
||||
|
||||
// The UTTypes that can be dragged onto this view.
|
||||
registerForDraggedTypes(Array(Self.dropTypes))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -1509,3 +1513,62 @@ extension Ghostty.SurfaceView: NSMenuItemValidation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSDraggingDestination
|
||||
|
||||
extension Ghostty.SurfaceView {
|
||||
static let dropTypes: Set<NSPasteboard.PasteboardType> = [
|
||||
.string,
|
||||
.fileURL,
|
||||
.URL
|
||||
]
|
||||
|
||||
override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation {
|
||||
guard let types = sender.draggingPasteboard.types else { return [] }
|
||||
|
||||
// If the dragging object contains none of our types then we return none.
|
||||
// This shouldn't happen because AppKit should guarantee that we only
|
||||
// receive types we registered for but its good to check.
|
||||
if Set(types).isDisjoint(with: Self.dropTypes) {
|
||||
return []
|
||||
}
|
||||
|
||||
// We use copy to get the proper icon
|
||||
return .copy
|
||||
}
|
||||
|
||||
override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool {
|
||||
let pb = sender.draggingPasteboard
|
||||
|
||||
let content: String?
|
||||
if let url = pb.string(forType: .URL) {
|
||||
// URLs first, they get escaped as-is.
|
||||
content = Ghostty.Shell.escape(url)
|
||||
} else if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||
urls.count > 0 {
|
||||
// File URLs next. They get escaped individually and then joined by a
|
||||
// space if there are multiple.
|
||||
content = urls
|
||||
.map { Ghostty.Shell.escape($0.path) }
|
||||
.joined(separator: " ")
|
||||
} else if let str = pb.string(forType: .string) {
|
||||
// Strings are not escaped because they may be copy/pasting a
|
||||
// command they want to execute.
|
||||
content = str
|
||||
} else {
|
||||
content = nil
|
||||
}
|
||||
|
||||
if let content {
|
||||
DispatchQueue.main.async {
|
||||
self.insertText(
|
||||
content,
|
||||
replacementRange: NSMakeRange(0, 0)
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user