Cocoa 防止Escape键用关闭框关闭NSPanel

Cocoa 防止Escape键用关闭框关闭NSPanel,cocoa,nswindow,nspanel,responder-chain,Cocoa,Nswindow,Nspanel,Responder Chain,是否有人知道防止逃生钥匙在钥匙窗口关闭NSPanel的最佳方法?我的面板是一个子窗口,我希望它的行为更像窗口的半永久部分,更像抽屉,对于其中的文本控件,我希望使用Escape键取消编辑 我最近在Cocoa文档中找到了更多关于windows和Escape键的信息。在nsrresponder类参考中,在cancelOperation:下,其中显示“窗口将cancelOperation:的默认操作消息发送到第一个响应程序,消息从那里沿响应程序链向上传递”。对于NSPanel,情况似乎有所不同,窗口关闭

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

我最近在Cocoa文档中找到了更多关于windows和Escape键的信息。在nsrresponder类参考中,在
cancelOperation:
下,其中显示“窗口将
cancelOperation:
的默认操作消息发送到第一个响应程序,消息从那里沿响应程序链向上传递”。对于
NSPanel
,情况似乎有所不同,窗口关闭时,第一响应者未获得
取消操作:
调用或NSTextView代理获得其
doCommandBySelector:
调用

考虑到我一直在做OSX工作,我对应答器链的输入和输出的了解是可耻的。我在想,我需要在我的
NSPanel
子类中设置
keyDown:
,使其行为类似于普通窗口。我尝试覆盖
NSPanel
,可以捕捉到
keyDown:
,将呼叫转发到
NSWindow的
keyDown:
,而不是
super
,但没有任何更改,Escape仍然关闭了窗口,没有向第一响应者发送消息。这样做合理吗

然后,我尝试完全重新实现我的面板子类“
keyDown:
”,使它可以这样做:

[self.firstResponder cancelOperation:self]
我想这会让我的文本字段按照它通常期望的方式处理转义,如果没有文本字段作为第一响应者,那么调用可能会死路一条。然而,我试过了,面板像以前一样关闭了。显然,我没有在正确的水平上截取东西


是否有人知道在低级别按键事件和面板关闭之间运行的方法序列,或者我需要覆盖什么来拦截它并确保
取消操作:
转到我的第一响应者?

在nib或代码中的某个地方,将您的NSTableView委托设置为控制器

请注意,setDelegate:与setDatasource:不同

就我而言: @接口值编辑器:NSObject

  + (ValueEditorCmdType)cmdTypeForSelector:(SEL)command
  {
     ValueEditorCmdType cmdType = kCmdTypeNone;
     if ( command == @selector(insertLineBreak:) || command == @selector(insertNewline:) || command == @selector(insertNewlineIgnoringFieldEditor:) || command == @selector(insertParagraphSeparator:))
        cmdType = kCmdTypeAccept;
     else if (  command == @selector(insertTab:) || command == @selector(selectNextKeyView:)  || command == @selector(insertTabIgnoringFieldEditor:))
        cmdType = kCmdTypeNext;
     else if ( command == @selector(insertBacktab:) || command == @selector(selectPreviousKeyView:))
        cmdType = kCmdTypePrev;
     else if ( command == @selector(cancelOperation:) )   
        cmdType = kCmdTypeCancel;
     return cmdType;
  }

  #pragma mark - NSControl delegate
  - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
  {
     ValueEditorCmdType cmdType = [ValueEditor cmdTypeForSelector:command];
     if ( cmdType == kCmdTypeCancel )
     {
        [control abortEditing];  

        // when user hits 'ESC' key with a field editor active, cancel the field editor,
        // but return YES here so that NSPanel doesn't close.  
        // Hitting 'ESC' a 2nd time will close the NSPanel.
        return YES;
     }
     return NO;
  }

基思·克努伯的答案是:

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对象的委托