16

I have been trying to develop a simple program that sits in the Mac's status bar. I need it so that if you left click, it runs a function, but if you right click it displays a menu with an About and Quit item.

I have been looking but all I could find was command or control click suggestions however I would prefer not to go this route.

Thanks in advance and any help appreciated!

Lorenzo
  • 3,293
  • 4
  • 29
  • 56
Joe Izzard
  • 263
  • 3
  • 7

3 Answers3

25

Swift 3

let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)

if let button = statusItem.button {
    button.action = #selector(self.statusBarButtonClicked(sender:))
    button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}

func statusBarButtonClicked(sender: NSStatusBarButton) {
    let event = NSApp.currentEvent!

    if event.type == NSEventType.rightMouseUp {
        print("Right click")
    } else {
        print("Left click")
    }
}

Swift 4

let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

if let button = statusItem.button {
    button.action = #selector(self.statusBarButtonClicked(_:))
    button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}

func statusBarButtonClicked(sender: NSStatusBarButton) {
    let event = NSApp.currentEvent!

    if event.type == NSEvent.EventType.rightMouseUp {
        print("Right click")
    } else {
        print("Left click")
    }
}

A longer post is available at https://samoylov.eu/2016/09/14/handling-left-and-right-click-at-nsstatusbar-with-swift-3/

Florian Dreier
  • 419
  • 6
  • 9
Michael Samoylov
  • 2,933
  • 3
  • 25
  • 33
  • 1
    `.popUpMenu()` is depreciated as of macOS 10.14 – Ryan Nov 14 '18 at 14:42
  • Also, bear in mind that, in the official docs, the `menu` property of NSStatusItem says "When non-nil, the status item’s single click action behavior is not used. Setting the value of this property to nil removes the menu." This means that if your menu is not nil then `action` won't work. – Ram Patra May 19 '20 at 22:38
  • looks like you're missing setting your target: `button.target = self` – Andres Canella Aug 12 '21 at 19:14
10

for this you can use statusItem button property.

    let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)  
    let statusButton = statusItem!.button!
    statusButton?.target = self // or wherever you implement the action method
    statusButton?.action = "statusItemClicked:" // give any name you want
    statusButton?.sendActionOn(Int((NSEventMask.LeftMouseUpMask | NSEventMask.RightMouseUpMask).rawValue)) // what type of action to observe

then you implement the action function, in the above code I named it "statusItemClicked"

func statusItemClicked(sender: NSStatusBarButton!){
    var event:NSEvent! = NSApp.currentEvent!
    if (event.type == NSEventType.RightMouseUp) {
        statusItem?.menu = myMenu //set the menu
        statusItem?.popUpStatusItemMenu(myMenu)// show the menu 
    }
    else{
        // call your function here
    }
}
Nasim Saleh
  • 181
  • 1
  • 7
  • 1
    In Swift 2, using the `|` operator on `NSEventMask` operands gives an error. – beeb Mar 05 '16 at 21:32
  • 1
    statusButton?.sendActionOn(Int(NSEventMask.RightMouseUpMask.rawValue | NSEventMask.LeftMouseUpMask.rawValue)) is working with Swift 2 – Hampus Jun 06 '16 at 15:02
1

In Swift 5,

I worked this way. I was able to detect whether it was a right click or a left click from refering to the above answer.

However, once you right click, you can only display the NSMenu all the time.

Then, I add statusItem.button?.performClick(nil) & statusItem?.menu = nil

You can add statusItem?.popUpMenu(menu), but it's depreciated in macOS 10.14. (But it worked well.)

Finally, if left click, it runs a function. And if right click, it runs menu items like Quit.

Thank you to those who answered earlier !

class AppDelegate: NSObject, NSApplicationDelegate {
  func applicationDidFinishedLaunching(_ notification: Notification) {
    // .....
    // .....

    if let button = statusItem?.button {
        button.image = NSImage(named: "iconImage")
        button.action = #selector(self.statusBarButtonClicked(_:))
        button.sendAction(on: [.leftMouseUp, .rightMouseUp])
    }
    // Add Menu Item for NSMenu
    menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
  }
  @objc func statusBarButtonClicked(_ sender: NSStatusBarButton) {
    let event = NSApp.currentEvent!
    if event.type == NSEvent.EventType.rightMouseUp {
      print("Right Click")
      statusItem?.menu = menu
      statusItem?.button?.performClick(nil)
      // statusItem?.popUpMenu(menu)
      statusItem?.menu = nil
    } else {
      print("Left Click")
      togglePopover(sender)
    }
  }
}
coffmark
  • 43
  • 3