I'm building a mac app with status bar icon. I want to handle right-click event on the icon.
class StatusBar {
var statusBar: NSStatusBar!
var statusBarItem: NSStatusItem!
init() {
statusBar = NSStatusBar()
statusBarItem = statusBar.statusItem(
withLength: NSStatusItem.variableLength
)
if let button = statusBarItem.button {
// code to set up icon is ignored
// Doesn't work
button.sendAction(on: .any)
button.action = #selector(handleTwoFingerTap(_:))
button.target = self
}
}
@objc private func handleTwoFingerTap(_: Any?) {
// this function is not called
}
}
I suspect this is due to the limitation of sendAction
, quote
The only conditions that are actually checked are associated with the NSLeftMouseDownMask, NSLeftMouseUpMask, NSLeftMouseDraggedMask, and NSPeriodicMask bits.
In that case, how to make this work?
I'm building a mac app with status bar icon. I want to handle right-click event on the icon.
class StatusBar {
var statusBar: NSStatusBar!
var statusBarItem: NSStatusItem!
init() {
statusBar = NSStatusBar()
statusBarItem = statusBar.statusItem(
withLength: NSStatusItem.variableLength
)
if let button = statusBarItem.button {
// code to set up icon is ignored
// Doesn't work
button.sendAction(on: .any)
button.action = #selector(handleTwoFingerTap(_:))
button.target = self
}
}
@objc private func handleTwoFingerTap(_: Any?) {
// this function is not called
}
}
I suspect this is due to the limitation of sendAction
, quote
The only conditions that are actually checked are associated with the NSLeftMouseDownMask, NSLeftMouseUpMask, NSLeftMouseDraggedMask, and NSPeriodicMask bits.
In that case, how to make this work?
Share Improve this question edited 3 hours ago laike9m asked Dec 13, 2024 at 7:18 laike9mlaike9m 19.5k23 gold badges116 silver badges154 bronze badges 6- This doesn't make sense to me. How do you "tap" on the status bar with "two fingers" no less? MacBook screens are not touch screens. – Sweeper Commented Dec 13, 2024 at 9:18
- Does two-finger tap on a trackpad do the same as right-click with a mouse? – Willeke Commented Dec 14, 2024 at 6:09
- @Sweeper I meat tap the touch pad not screen. – laike9m Commented Dec 16, 2024 at 22:27
- @Willeke it's different. PopClip has this feature, if tap with two fingers it will toggle app's status (on/off), if right or left click, it opens the menu. – laike9m Commented Dec 16, 2024 at 22:28
- Turn PopClip On and Off: "As a short-cut, you can also toggle PopClip on and off by secondary clicking (Right-click or Control-click) the PopClip icon in the menu bar.". Mac Trackpad gestures: "Secondary click (right-click): Click or tap with two fingers.". – Willeke Commented Dec 17, 2024 at 6:29
2 Answers
Reset to default 0Be sure to keep a strong reference to the StatusBar, this may be the reason why the selector is not called
private var statusBar: StatusBar = StatusBar()
I used this code to manually display the custom menu, it worked correctly on macOS 14 and 15
statusBarItem.button?.target = self
statusBarItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
statusBarItem.button?.action = #selector(handleStatusBarClick)
Be sure to make sure the statusbar does not have a menu installed
One possible problem: it is better to use the system statusbar
statusBar = NSStatusBar.system
This is what works for me after many trials and errors. I use it in my app.
What I did is to handle left click and right click differently. Got a lot of help from this answer Show NSMenu only on NSStatusBarButton right click?. When right click, it will turn the app on and off. When left click, it shows the status menu.
class StatusBar: NSObject, NSMenuDelegate {
var statusBar = NSStatusBar()
var statusItem: NSStatusItem!
var statusMenu: NSMenu!
var button: NSStatusBarButton!
@objc func menuDidClose(_: NSMenu) {
statusItem.menu = nil
}
// Left click to trigger menu
// Right click to turn app on/off
// Solution from https://stackoverflow/q/59635971
@objc func statusBarButtonClicked(sender _: NSStatusBarButton) {
switch NSApp.currentEvent!.type {
case .leftMouseUp:
statusItem.menu = statusMenu
statusItem.button?.performClick(nil)
case .rightMouseDown:
Defaults[.appEnabled].toggle()
updateButtonStatus()
default:
return
}
}
override init() {
super.init()
statusItem = statusBar.statusItem(
withLength: NSStatusItem.variableLength
)
setUpStatusButton()
setupMenu()
}
func setUpStatusButton() {
button = statusItem.button
button.toolTip = "Clicknow"
button.image = NSImage(named: "status bar")
button.action = #selector(statusBarButtonClicked(sender:))
button.sendAction(on: [.leftMouseUp, .rightMouseDown])
button.target = self
updateButtonStatus()
}
func updateButtonStatus() {
if Defaults[.appEnabled] {
button.appearsDisabled = false
} else {
button.appearsDisabled = true
}
}
func setupMenu() {
statusMenu = NSMenu()
statusMenu.delegate = self // important
...
}
}