swiftmacoscocoansstatusitem

Swift NStatusBarItem, make button support drag&drop


I know there are a bunch of questions similar to this, but they are all very outdated, basically I have a statusBarItem and I want to make it responsive to dragging and dropping files, here is some of the code I've tried:

extension NSStatusBarButton {
    open override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
        true
    }

    open override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        print("dragging entered")
        return .copy
    }

    open override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        print("Something was dragged!")
        return true
    }

    open override func draggingEnded(_ sender: NSDraggingInfo) {
        print("Draging ended")
    }
}

class DragAndDropButton: NSStatusBarButton {

    open override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
        true
    }

    open override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        print("dragging entered")
        return .copy
    }

    open override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        print("Something was dragged!")
        return true
    }

    open override func draggingEnded(_ sender: NSDraggingInfo) {
        print("Draging ended")
    }
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: NSWindow!
    var popover: NSPopover!
    var statusBarItem: NSStatusItem!


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
        // Add `@Environment(\.managedObjectContext)` in the views that will need the context.
        let popoverView = PopoverView()

        popover = NSPopover()

        popover.contentSize = NSSize(width: AppConstants.popoverWidth, height: AppConstants.popoverHeight)
        popover.animates = true
        popover.behavior = .transient
        popover.contentViewController = NSHostingController(rootView: popoverView)

        statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))

        let dropSubview = DragAndDropButton()
        dropSubview.registerForDraggedTypes([.png])
        dropSubview.image = NSImage(named: "CognitionSmall")

        statusBarItem.button = drop
//        statusBarItem.view = dropSubview
//        statusBarItem.title = "Test"

        if let button = self.statusBarItem.button {
//            button.view

//            button.registerForDraggedTypes([.multipleTextSelection])
            button.addSubview(dropSubview)
            button.imagePosition = NSControl.ImagePosition.imageLeft
            button.image = NSImage(named: "CognitionSmall")
            button.action = #selector(togglePopover(_:))
//            button.window!.delegate = self
//            button.addSubview(cognitoSubview)
//            button.performDragOperation = #selector(onDrag(_:))
        }
    }

as you can see I've tried to extend and override the default NSStatusBarButton behavior as well as replacing it with my own NSView and adding a subview to the button, but nothing seems to work, does anybody have an idea of how to make this work?


Solution

  • Turns out the extension I did to the NSStatusBarButton was fine, with the exception that I did not test it properly, if you declare more registered types it works just fine:

    extension NSStatusBarButton {
        open override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
            true
        }
    
        open override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
            print("dragging entered")
            return .copy
        }
    
        open override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
            print("Something was dragged!")
            return true
        }
    
        open override func draggingEnded(_ sender: NSDraggingInfo) {
            print("Draging ended")
        }
    }
    
    ... more initialization code
    
    button.registerForDraggedTypes([.fileURL, .multipleTextSelection, .png])