防止 Escape 键关闭带有关闭框的 NSPanel
Posted
技术标签:
【中文标题】防止 Escape 键关闭带有关闭框的 NSPanel【英文标题】:Prevent Escape key from closing an NSPanel with a close box 【发布时间】:2012-10-19 00:38:53 【问题描述】:有谁知道防止转义键关闭NSPanel
的最佳方法?我的面板是一个子窗口,我希望它的行为更像是窗口的半永久性部分,更像是一个抽屉,对于其中的文本控件,我想让 Escape 键取消编辑。
我最近在 Cocoa 文档中找到了有关 windows 和 Escape 键的更多信息。在cancelOperation:
下的 NSResponder 类引用中,它说“窗口将cancelOperation:
的默认操作消息发送给第一响应者,然后消息从那里向上传播到响应者链”。 NSPanel
似乎有所不同,并且在没有第一响应者获得 cancelOperation:
调用或 NSTextView 代表获得 doCommandBySelector:
调用的情况下,窗口关闭。
考虑到我一直在做 OS X 工作,我对响应者链的进出的了解是可耻的。我在想我需要在我的NSPanel
子类中使keyDown:
表现得像普通窗口一样。我尝试覆盖NSPanel
并且可以捕获keyDown:
,将呼叫转发到NSWindow
的keyDown:
而不是super
,但是没有任何变化,Escape 仍然关闭了窗口,没有消息给第一响应者。尝试这样做是否合理?
然后我尝试完全重新实现我的面板子类'keyDown:
,让它这样做:
[self.firstResponder cancelOperation:self]
我认为这会让我的文本字段按照它通常期望的方式处理转义,并且如果没有文本字段是第一响应者,那么呼叫将结束。但是,我试过了,面板就像以前一样简单地关闭了。显然我没有在正确的水平上拦截东西。
有谁知道在低级按键事件和面板关闭之间运行的方法序列,或者我需要重写以拦截它并确保cancelOperation:
到达我的第一响应者?
【问题讨论】:
【参考方案1】:keith-knauber 答案的 Swift 端口:
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 对象的委托!
【讨论】:
【参考方案2】:在您的 nib 或代码中的某处,将您的 NSTableView 委托设置为您的控制器。
注意 setDelegate: 和 setDatasource: 不一样!
就我而言: @interface 值编辑器: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;
【讨论】:
以上是关于防止 Escape 键关闭带有关闭框的 NSPanel的主要内容,如果未能解决你的问题,请参考以下文章