cocoa - 防止 Escape 键通过关闭框关闭 NSPanel

标签 cocoa nswindow nspanel responder-chain

有谁知道防止转义键关闭 NSPanel 当它是关键窗口时的最佳方法?我的面板是一个子窗口,我希望它的行为更像窗口的半永久部分,更像一个抽屉,对于其中的文本控件,我希望使用 Escape 键取消编辑。

我最近在 Cocoa 文档中发现了有关 Windows 和 Escape 键的更多信息。在 cancelOperation: 下的 NSResponder 类引用中,它表示“窗口将 cancelOperation: 的默认操作消息发送到第一个响应者,然后消息从那里沿着响应者链向上传播” 。对于 NSPanel 来说似乎有所不同,窗口关闭时没有第一个响应者获得 cancelOperation: 调用或 NSTextView 委托(delegate)获得他们的 doCommandBySelector:打电话。

考虑到我一直在做 OS X 工作,我对响应者链的进出的了解是可耻的。我想我需要让我的 NSPanel 子类中的 keyDown: 表现得像普通窗口一样。我尝试覆盖 NSPanel 并可以捕获 keyDown:,将调用转发到 NSWindowkeyDown: 而不是super,但是没有任何变化,Escape仍然关闭了窗口,没有向第一响应者发送消息。这样的尝试是否合理?

然后我尝试完全重新实现我的面板子类'keyDown:,使其执行以下操作:

[self.firstResponder cancelOperation:self]

我认为这会让我的文本字段按照通常预期的方式处理转义,也许如果没有文本字段是第一响应者,那么调用就会死胡同。然而,我尝试了一下,面板就像以前一样简单地关闭了。显然我没有在正确的级别拦截东西。

有谁知道在低级按键事件和面板关闭之间运行的方法序列是什么,或者我需要重写什么来拦截它并确保 cancelOperation: 进入我的第一响应者?

最佳答案

keith-knauber 答案的快速移植:

class ValueEditor : NSObject, NSControlTextEditingDelegate {
enum CommandType {
    case none
    case accept
    case next
    case prev
    case cancel
}

class func commandTypeType(for command: Selector) -> CommandType {
    let commandType: CommandType

    switch command {
    case #selector(NSStandardKeyBindingResponding.insertLineBreak(_:)) :
        fallthrough
    case #selector(NSStandardKeyBindingResponding.insertNewline(_:)) :
        fallthrough
    case #selector(NSStandardKeyBindingResponding.insertNewlineIgnoringFieldEditor(_:)) :
        fallthrough
    case #selector(NSStandardKeyBindingResponding.insertParagraphSeparator(_:)) :
        commandType = .accept

    case #selector(NSStandardKeyBindingResponding.insertTab(_:)) :
        fallthrough
    case #selector(NSWindow.selectNextKeyView(_:)) :
        fallthrough
    case #selector(NSStandardKeyBindingResponding.insertTabIgnoringFieldEditor(_:)) :
        commandType = .next

    case #selector(NSStandardKeyBindingResponding.insertBacktab(_:)) :
        fallthrough
    case #selector(NSWindow.selectPreviousKeyView(_:)) :
        commandType = .prev

    case #selector(NSStandardKeyBindingResponding.cancelOperation(_:)) :
        commandType = .cancel

    default:
        commandType = .none
    }

    return commandType
}


// MARK: - NSControl delegate

func control(_ control: NSControl,
             textView: NSTextView,
             doCommandBy commandSelector: Selector) -> Bool {
    let commandType: CommandType = ValueEditor.commandTypeType(for: commandSelector)

    switch commandType {
    case .cancel:
        control.abortEditing()

        // When the user hits 'ESC' key with a field editor active, cancel the field editor,
        // but return `true` here so that the NSPanel doesn’t close.
        // Hitting 'ESC' a second time will close the NSPanel.
        return true

    default:
        return false
    }

}

}

不要忘记将 ValueEditor 实例设置为 NSTextView 对象的委托(delegate)!

关于cocoa - 防止 Escape 键通过关闭框关闭 NSPanel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12965798/

相关文章:

objective-c - 在 Cocoa 中使用 NSTimer 关闭自定义 NSPanel

macos - 如何从顶部制作它? ( cocoa 片)

cocoa - NSTableView 标题高度与 OS X 10.11 的向后兼容性

ios - KVC 点键问题

macos - 与桌面/图标级别的应用程序交互 (OS X)

c++ - 如何用 C++ 语法编写 Cocoa NSWindow 代码?

objective-c - 窗口关闭时 NSPopover 崩溃

objective-c - 在 xcode 上验证 mac 应用程序时出现 'com.apple.developr.aps-environment' 错误

cocoa - CALayer 过滤器覆盖其他子层

objective-c - 带有 NSWindowController 的 NSPanel 显示在 xcode 中,但不在存档应用程序中