如何创建一个包含仅在按住键盘修改键时出现的 NSMenuItem 的 NSMenu?
How to create an NSMenu containing an NSMenuItem which only appears while holding a keyboard modifier key?
我想创建一个 NSMenu
,其中包含默认隐藏的 NSMenuItem
,并且仅在用户按住键盘修改键时出现。
基本上,我正在寻找与 Finder 'Go' 菜单中的 'Library' 选项相同的行为:
不持有 Option (⌥):
按住 Option (⌥):
我已经尝试使用 [NSEvent addGlobalMonitorForEventsMatchingMask: handler:]
安装密钥侦听器,通过将 NSMenuItem
设置为 hidden
属性 以编程方式隐藏和取消隐藏 NSMenuItem
。这种方法可行,但问题是 hiding/unhiding 在 NSMenu
打开时不起作用。显然,NSMenu
在打开时完全接管了事件处理循环,从而阻止了关键侦听器的工作。
我可能可以使用 CGEventTap 在 NSMenu
打开时仍然接收事件,但这似乎完全矫枉过正。
我发现的另一个与我想要的类似的东西是 NSMenu
的“alternate”机制。但我只能让它切换 NSMenuItems
,而不是 hide/unhide 它们。
如有任何帮助,我们将不胜感激。谢谢!
假设您的仅选项菜单项的操作是(在 Swift 中)performOptionOnlyMenuItem(_:)
,其目标是您的 AppDelegate
。
您需要做的第一件事是确保 AppDelegate
符合 NSMenuItemValidation
协议。
您需要做的第二件事是实现 validateMenuItem(_:)
方法,并让它检查菜单项是否发送 performOptionOnlyMenuItem(_:)
动作。如果是,则根据当前是否按下选项键设置项目的isHidden
属性。
如果您不需要验证任何其他菜单项,代码可以如下所示:
extension AppDelegate: NSMenuItemValidation {
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
switch menuItem.action {
case #selector(performOptionOnlyMenuItem(_:)):
let flags = NSApp.currentEvent?.modifierFlags ?? []
menuItem.isHidden = !flags.contains(.option)
return true
default:
return true
}
}
}
如果操作被发送到其他目标,您需要在该目标上实施验证(包括协议一致性)。每个菜单项仅由项目的目标验证。
我找到了一个完美的解决方案!
在你想要隐藏的NSMenuItem
上,将alternate
属性设置为YES
,然后设置keyEquivalentModifierMask
属性 到您想要 取消隐藏 项目的键盘修饰符。
在您要隐藏的 NSMenu
、 之前 NSMenuItem
,插入另一个 NSMenuItem
高度为 0.
在 Objc 中,您可以像这样创建高度为 0 的 NSMenuItem
:
NSMenuItem *i = [[NSMenuItem alloc] init];
i.view = [[NSView alloc] initWithFrame:NSZeroRect];
可隐藏的 NSMenuItem
现在将 'alternate' 到它前面的零高度 NSMenuItem
。零高度项目将默认显示,但当您按住指定的键盘修饰符时,零高度项目将与可隐藏项目交换。因为零高度项目是不可见的,所以这具有取消隐藏可隐藏项目的效果。
我想创建一个 NSMenu
,其中包含默认隐藏的 NSMenuItem
,并且仅在用户按住键盘修改键时出现。
基本上,我正在寻找与 Finder 'Go' 菜单中的 'Library' 选项相同的行为:
不持有 Option (⌥):
按住 Option (⌥):
我已经尝试使用 [NSEvent addGlobalMonitorForEventsMatchingMask: handler:]
安装密钥侦听器,通过将 NSMenuItem
设置为 hidden
属性 以编程方式隐藏和取消隐藏 NSMenuItem
。这种方法可行,但问题是 hiding/unhiding 在 NSMenu
打开时不起作用。显然,NSMenu
在打开时完全接管了事件处理循环,从而阻止了关键侦听器的工作。
我可能可以使用 CGEventTap 在 NSMenu
打开时仍然接收事件,但这似乎完全矫枉过正。
我发现的另一个与我想要的类似的东西是 NSMenu
的“alternate”机制。但我只能让它切换 NSMenuItems
,而不是 hide/unhide 它们。
如有任何帮助,我们将不胜感激。谢谢!
假设您的仅选项菜单项的操作是(在 Swift 中)performOptionOnlyMenuItem(_:)
,其目标是您的 AppDelegate
。
您需要做的第一件事是确保
AppDelegate
符合NSMenuItemValidation
协议。您需要做的第二件事是实现
validateMenuItem(_:)
方法,并让它检查菜单项是否发送performOptionOnlyMenuItem(_:)
动作。如果是,则根据当前是否按下选项键设置项目的isHidden
属性。
如果您不需要验证任何其他菜单项,代码可以如下所示:
extension AppDelegate: NSMenuItemValidation {
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
switch menuItem.action {
case #selector(performOptionOnlyMenuItem(_:)):
let flags = NSApp.currentEvent?.modifierFlags ?? []
menuItem.isHidden = !flags.contains(.option)
return true
default:
return true
}
}
}
如果操作被发送到其他目标,您需要在该目标上实施验证(包括协议一致性)。每个菜单项仅由项目的目标验证。
我找到了一个完美的解决方案!
在你想要隐藏的
NSMenuItem
上,将alternate
属性设置为YES
,然后设置keyEquivalentModifierMask
属性 到您想要 取消隐藏 项目的键盘修饰符。在您要隐藏的
NSMenu
、 之前NSMenuItem
,插入另一个NSMenuItem
高度为 0.在 Objc 中,您可以像这样创建高度为 0 的
NSMenuItem
:NSMenuItem *i = [[NSMenuItem alloc] init]; i.view = [[NSView alloc] initWithFrame:NSZeroRect];
可隐藏的 NSMenuItem
现在将 'alternate' 到它前面的零高度 NSMenuItem
。零高度项目将默认显示,但当您按住指定的键盘修饰符时,零高度项目将与可隐藏项目交换。因为零高度项目是不可见的,所以这具有取消隐藏可隐藏项目的效果。