繁体   English   中英

NSApplication 箭头键的响应链

[英]NSApplication responder chain for arrow keys

我的 window 中有一个NSTextField和 4 个具有等效键 的菜单项。

When the text field is selected and I press an arrow key, I would expect the cursor to move in the text field but instead the corresponding menu item action is performed.

因此,响应者链中必须存在问题。 为了找出问题所在,我查看了WWDC 2010 Session 145 – Cocoa 应用程序中的关键事件处理在这个NSMenuItem KeyEquivalent 空格“”错误线程中提到的应用程序。


键(热键)的事件流在 session 中显示如下:

事件流



因此,我使用具有keyEquivalent = K (只是任何普通键)的菜单项和具有keyEquivalent = → (右箭头键)的菜单项检查了调用堆栈

K键事件调用栈 右箭头键事件调用堆栈

所以当按下箭头键时,事件被直接发送到mainMenu.performKeyEquivalent ,但它实际上应该被发送到keyWindow对吗?

为什么会这样,我该如何解决这种行为,以便我的NSTextFieldmainMenu之前接收到箭头键事件?

关于调用堆栈差异的有趣观察。 由于箭头键在导航中起着最重要的作用,因此它们的处理方式可能与 rest 的键不同,就像您在NSMenuItem KeyEquivalent 空格“”错误线程中看到的那样。 同样,这是 AppKit 在 99.9% 的情况下处理幕后一切以使您的生活更轻松的情况之一。

您可以通过在文本字段具有焦点时按k来查看行为的实际差异。 与箭头不同,菜单项的等效键不会被触发,输入直接进入控件。

对于您的情况,您可以使用NSMenuItemValidation协议来覆盖启用或禁用特定菜单项的默认操作。 AFAIK 这可以 go 进入链中的任何响应者,例如,查看 controller、window 或应用程序。 因此,当窗口的第一响应者是文本字段或使用这些事件正确操作的任何其他控件时,您可以在一个地方启用/禁用菜单项。

extension ViewController: NSMenuItemValidation {
    func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        // Filter menu item by it's assigned action, just as an exampe.
        if menuItem.action != #selector(ViewController.menuActionLeftArrowKey(_:)) { return true }
        Swift.print("Validating menu item:", menuItem)
        // Disable the menu item if first responder is text view.
        let isTextView = self.view.window?.firstResponder is NSTextView
        return !isTextView
    }
}

这将在显示菜单之前调用以更新项目 state,在调用菜单项等效键之前以检查是否需要发送操作,并且可能在 AppKit 需要检查项目的 state 的其他情况下 - 无法思考任何从我的头顶。

PS 以上第一响应者检查是针对NSTextView而不是NSTextField进行的,这就是原因。

这是我选择的解决方案,来自@Willeke 的评论。

我创建了NSWindow的子类并覆盖了keyDown(with:)方法。 我的应用程序中的每个 Window(当前是 2 个)子类化了这个新的NavigationWindow ,以便您可以在每个 window 中使用箭头键。

class NavigationWindow: NSWindow {

    override func keyDown(with event: NSEvent) {
        if event.keyCode == 123 || event.keyCode == 126 || event.specialKey == NSEvent.SpecialKey.pageUp {
            print("navigate back")
        } else if event.keyCode == 124 || event.keyCode == 125 || event.specialKey == NSEvent.SpecialKey.pageDown {
            print("navigate forward")
        } else {
            super.keyDown(with: event)
        }
    }
}

此实现注册所有四个箭头键以及用于导航的向上和向下翻页键。

这些是关键代码

  • 123 :右箭头
  • 124 :左箭头
  • 125 :向下箭头
  • 126 :向上箭头

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM