From 0e3541354026ddfcf48c0c93d4d1415a3e08d635 Mon Sep 17 00:00:00 2001 From: StarHack Date: Sat, 25 Feb 2023 12:44:16 +0100 Subject: [PATCH] darwin-external-dragndrop: implement external drag n' drop for darwin --- app/os_macos.go | 21 +++++++++++++++++++++ app/os_macos.m | 15 +++++++++++++++ io/router/pointer.go | 18 +++++++++++------- io/router/router.go | 2 ++ io/transfer/transfer.go | 2 +- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/app/os_macos.go b/app/os_macos.go index 516157aaa..51f0bc2ba 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -8,6 +8,10 @@ package app import ( "errors" "image" + "io" + "mime" + "os" + "path/filepath" "runtime" "time" "unicode" @@ -18,6 +22,7 @@ import ( "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" + "gioui.org/io/transfer" "gioui.org/unit" _ "gioui.org/internal/cocoainit" @@ -557,6 +562,22 @@ func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtn C.NSInteger, x, y, dx, }) } +//export gio_onExternalDrop +func gio_onExternalDrop(view C.CFTypeRef, path *C.char) { + fileUrl := C.GoString(path) + w := mustView(view) + + fileExtension := filepath.Ext(fileUrl) + mime := mime.TypeByExtension(fileExtension) + + w.w.Event(transfer.DataEvent{ + Type: mime, + Open: func() (io.ReadCloser, error) { + return os.Open(fileUrl) + }, + }) +} + //export gio_onDraw func gio_onDraw(view C.CFTypeRef) { w := mustView(view) diff --git a/app/os_macos.m b/app/os_macos.m index 49964fbf1..dc62b5eaf 100644 --- a/app/os_macos.m +++ b/app/os_macos.m @@ -110,6 +110,20 @@ - (void)mouseMoved:(NSEvent *)event { - (void)mouseDragged:(NSEvent *)event { handleMouse(self, event, MOUSE_MOVE, 0, 0); } +-(NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender +{ + return NSDragOperationCopy; +} +- (void)draggingEnded:(id )sender +{ + NSPasteboard* pbrd = [sender draggingPasteboard]; + NSArray* droppedFiles = [pbrd propertyListForType:NSFilenamesPboardType]; + + for (NSString* filePath in droppedFiles) { + NSURL* url = [NSURL fileURLWithPath:filePath]; + gio_onExternalDrop((__bridge CFTypeRef)self, (char*)[[url path] UTF8String]); + } +} - (void)scrollWheel:(NSEvent *)event { CGFloat dx = -event.scrollingDeltaX; CGFloat dy = -event.scrollingDeltaY; @@ -366,6 +380,7 @@ CFTypeRef gio_createView(void) { @autoreleasepool { NSRect frame = NSMakeRect(0, 0, 0, 0); GioView* view = [[GioView alloc] initWithFrame:frame]; + [view registerForDraggedTypes: [NSArray arrayWithObjects:NSTIFFPboardType, NSFilenamesPboardType, nil]]; view.wantsLayer = YES; view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; return CFBridgingRetain(view); diff --git a/io/router/pointer.go b/io/router/pointer.go index e95bc6e7a..0600fc2a3 100644 --- a/io/router/pointer.go +++ b/io/router/pointer.go @@ -801,6 +801,14 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, events *handlerEv p.entered = append(p.entered[:0], hits...) } +func (q *pointerQueue) notifyPotentialTargets(src *pointerHandler, events *handlerEvents, event event.Event) { + for k, tgt := range q.handlers { + if _, ok := firstMimeMatch(src, tgt); ok { + events.Add(k, event) + } + } +} + func (q *pointerQueue) deliverDragEvent(p *pointerInfo, events *handlerEvents) { if p.dataSource != nil { return @@ -814,11 +822,7 @@ func (q *pointerQueue) deliverDragEvent(p *pointerInfo, events *handlerEvents) { // One data source handler per pointer. p.dataSource = k // Notify all potential targets. - for k, tgt := range q.handlers { - if _, ok := firstMimeMatch(src, tgt); ok { - events.Add(k, transfer.InitiateEvent{}) - } - } + q.notifyPotentialTargets(src, events, transfer.InitiateEvent{}) break } } @@ -858,9 +862,9 @@ func (q *pointerQueue) deliverTransferDataEvent(p *pointerInfo, events *handlerE transferIdx := len(q.transfers) events.Add(p.dataTarget, transfer.DataEvent{ Type: src.offeredMime, - Open: func() io.ReadCloser { + Open: func() (io.ReadCloser, error) { q.transfers[transferIdx] = nil - return src.data + return src.data, nil }, }) q.transfers = append(q.transfers, src.data) diff --git a/io/router/router.go b/io/router/router.go index e3b3be8d9..a33d5bc86 100644 --- a/io/router/router.go +++ b/io/router/router.go @@ -182,6 +182,8 @@ func (q *Router) Queue(events ...event.Event) bool { } case clipboard.Event: q.cqueue.Push(e, &q.handlers) + case transfer.DataEvent: + q.pointer.queue.notifyPotentialTargets(&pointerHandler{sourceMimes: []string{e.Type}}, &q.handlers, e) } } return q.handlers.HadEvents() diff --git a/io/transfer/transfer.go b/io/transfer/transfer.go index 78e2b9005..2ec700c7c 100644 --- a/io/transfer/transfer.go +++ b/io/transfer/transfer.go @@ -103,7 +103,7 @@ type DataEvent struct { Type string // Open returns the transfer data. It is only valid to call Open in the frame // the DataEvent is received. The caller must close the return value after use. - Open func() io.ReadCloser + Open func() (io.ReadCloser, error) } func (DataEvent) ImplementsEvent() {}