【问题标题】:Prevent Escape key from closing an NSPanel with a close box防止 Escape 键关闭带有关闭框的 NSPanel
【发布时间】:2012-10-19 00:38:53
【问题描述】:

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

我最近在 Cocoa 文档中找到了有关 windows 和 Escape 键的更多信息。在cancelOperation: 下的 NSResponder 类引用中,它说“窗口将cancelOperation: 的默认操作消息发送给第一响应者,然后消息从那里向上传播到响应者链”。 NSPanel 似乎有所不同,并且在没有第一响应者获得 cancelOperation: 调用或 NSTextView 代表获得 doCommandBySelector: 调用的情况下,窗口关闭。

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

然后我尝试完全重新实现我的面板子类'keyDown:,让它这样做:

[self.firstResponder cancelOperation:self]

我认为这会让我的文本字段按照它通常期望的方式处理转义,并且如果没有文本字段是第一响应者,那么呼叫将结束。但是,我试过了,面板就像以前一样简单地关闭了。显然我没有在正确的水平上拦截东西。

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

【问题讨论】:

    标签: cocoa nswindow nspanel responder-chain


    【解决方案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;
        }
      

      【讨论】:

        猜你喜欢
        • 2013-07-23
        • 2012-10-03
        • 1970-01-01
        • 2012-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多