Search code examples
cocoanstableviewkeydownquicklook

Cocoa: react to keyDown in QLPreviewPanel


I implemented quick look in my project in the following way in Swift 2 (I'm including this here for reference and because it might help someone else set it up).

My NSViewController contains a NSTableView subclass where I implemented keyDown to listen to the spacebar key being pressed (maybe not the best way but it works):

override func keyDown(theEvent: NSEvent) {
    let s   =   theEvent.charactersIgnoringModifiers!
    let s1  =   s.unicodeScalars
    let s2  =   s1[s1.startIndex].value
    let s3  =   Int(s2)
    if s3 == Int(" ".utf16.first!) {
        NSNotificationCenter.defaultCenter().postNotification(NSNotification(name: "MyTableViewSpacebar", object: nil))
        return
    }
    super.keyDown(theEvent)
}

In my view controller, I have an observer for this notification and the functions required by the QLPreviewPanel:

//...
class ViewController: NSViewController {

    @IBOutlet weak var myTableView: MyTableView!
    var files = [FilesListData]() //array of custom class
    //...

    override func viewDidLoad() {
        //...

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "spaceBarKeyDown:", name: "MyTableViewSpacebar", object: nil)
    }

    func spaceBarKeyDown(notification: NSNotification) {
        if let panel = QLPreviewPanel.sharedPreviewPanel() {
            panel.makeKeyAndOrderFront(self)
        }
    }

    override func acceptsPreviewPanelControl(panel: QLPreviewPanel!) -> Bool {
        return true
    }

    override func beginPreviewPanelControl(panel: QLPreviewPanel!) {
        panel.delegate = self
        panel.dataSource = self
    }

    override func endPreviewPanelControl(panel: QLPreviewPanel!) {
    }

}

extension ViewController: QLPreviewPanelDataSource {
    func numberOfPreviewItemsInPreviewPanel(panel: QLPreviewPanel!) -> Int {
        return self.myTableView.selectedRowIndexes.count
    }

    func previewPanel(panel: QLPreviewPanel!, previewItemAtIndex index: Int) -> QLPreviewItem! {
        if self.myTableView.selectedRow != -1 {
            var items = [QLPreviewItem]()

            let manager = NSFileManager.defaultManager()
            for i in self.myTableView.selectedRowIndexes {
                let path = self.files[i].path //path to a MP3 file
                if manager.fileExistsAtPath(path) {
                    items.append(NSURL(fileURLWithPath: path))
                } else {
                    items.append(qm_url) //image of a question mark used as placeholder
                }
            }

            return items[index]
        } else {
            return qm_url //image of a question mark used as placeholder
        }
    }
}

What I would like to do now is listen to the keys "up arrow" and "down arrow" being pressed while the quick look panel is open, in order to change the selected row in the NSTableView, much like Finder behaves when you preview files with quick look. I have no clue as to how I could implement this. Any ideas?

Thanks.


Solution

  • Finally found what I was looking for and it's actually pretty simple.

    Since my main view controller is also my delegate for the QLPreviewPanel, I added this:

    extension ViewController: QLPreviewPanelDelegate {
        func previewPanel(panel: QLPreviewPanel!, handleEvent event: NSEvent!) -> Bool {
            let kc = event.keyCode
            if (kc == 126 || kc == 125) { //up and down arrows
                if event.type == NSEventType.KeyDown {
                    self.myTableView.keyDown(event) //send the event to the table
                } else if event.type == NSEventType.KeyUp {
                    self.myTableView.keyUp(event)
                }
                return true
            }
            return false
        }
    }
    

    Then in my table view delegate:

    func tableViewSelectionDidChange(notification: NSNotification) {
        guard myTableView.numberOfSelectedRows > 0 else {
            if let panel = QLPreviewPanel.sharedPreviewPanel() {
                if panel.visible {
                    panel.close()
                }
            }
            return
        }
    
        if let panel = QLPreviewPanel.sharedPreviewPanel() {
            if panel.visible {
                panel.reloadData()
            }
        }
    }
    

    That's it! The QLPreviewPanelDataSource handles the rest.