macOS 开发之状态栏小工具分别响应鼠标左右键单击

上一篇文章 macOS 开发之菜单栏形式的状态栏小工具 讲述了如何开发菜单栏形式的状态栏小工具,大家可能已经发现,我们无论使用鼠标左键还是右键单击菜单栏图标,都会弹出我们添加的菜单,考虑到我们可能会需要分别对鼠标左右键单击菜单栏按钮进行不同的处理,所以写此文讲解一下如何实现。

工程

我们继续使用上一篇文章最后完成的工程:

MenuToolDemo

探索

为了展示左键和右键点击处理的区别,指定不同的行为:

  • 左键单击:显示一个提示窗口
  • 右键单击:弹出之前的菜单

我们第一反应就是,为按钮设置点击事件直接分开处理就可以了,我们试一下。

  1. 打开文件 AppDelegate.swift,修改之前工程中指定 button 图标的代码如下:

    if let button = statusItem.button {
        button.image = NSImage(named: "StatusIcon")
        button.action = #selector(mouseClickHandler)
        button.sendAction(on: [.leftMouseUp, .rightMouseUp])
    }
    
  2. 在方法 applicationDidFinishLaunching(_ aNotification:) 后面添加鼠标点击处理方法 mouseClickHandler

    @objc func mouseClickHandler() {
        if let event = NSApp.currentEvent {
            switch event.type {
            case .leftMouseUp:
                print("左键")
            default:
                print("右键")
            }
        }
    }
    

运行程序,是不是发现,左键和右键单击还都是显示菜单,而没有打印出 左键右键,这是为什么呢?

我们尝试把下面的代码注释掉:

statusItem.menu = menu

再次运行程序,Amazing!是不是左键单击就打印 左键 右键单击就打印出 右键!所以我们能想到的原因就是当指定 menu 后,系统会忽略按钮的事件设置,都只是弹出菜单

思路

有了上面的探索,是不是就有思路了!目标中右键还是要显示菜单的,所以我们可以在右键单击的时候指定 menu,菜单关闭的时候将 statusItemmenu 设置为 nil,这样就保证常态下,menu 是不指定的,就可以响应我们设置的左右键单击的处理了。

实现

  1. 删除 applicationDidFinishLaunching 中的 statusItem.menu = menu

  2. 修改 mouseClickHandler 的处理如下:

    @objc func mouseClickHandler() {
        if let event = NSApp.currentEvent {
            switch event.type {
            case .leftMouseUp:
                // 使用警告窗口示意左键单击
                let alert = NSAlert()
                alert.messageText = "鼠标事件"
                alert.informativeText = "左键单击"
                alert.addButton(withTitle: "关闭")
                alert.window.titlebarAppearsTransparent = true
                alert.runModal()
            default:
                statusItem.menu = menu
                statusItem.button?.performClick(nil)
            }
        }
    }
    
  3. 保证菜单关闭的时候,将 statusItemmenu 设置为 nil,扩展一下 AppDelegate,遵循 NSMenuDelegate 在方法 Menu 关闭后指定 nil:

    extension AppDelegate: NSMenuDelegate {
        // 为了保证按钮的单击事件设置有效,menu要去除
        func menuDidClose(_ menu: NSMenu) {
            self.statusItem.menu = nil
        }
    }
    

    需要指定 menu 的代理为 AppDelegate,在 applicationDidFinishLaunching 方法中添加下面的代码:

    menu.delegate = self
    

此时运行程序,鼠标左键单击菜单栏按钮,便会弹出警告窗口,右键单击就能显示菜单了。