Предотвращение Escape-ключа от закрытия NSPanel с помощью окна закрытия
Кто-нибудь знает лучший способ предотвратить закрытие клавиши escape NSPanel
когда это ключевое окно? Моя панель является дочерним окном, и я хочу, чтобы она немного больше походила на полупостоянную часть окна, больше напоминала ящик, а для элементов управления текстом в ней я хочу, чтобы клавиша Escape отменила редактирование.
Недавно я узнал больше об окнах и ключе Escape в документации по Какао. В NSResponder класс ссылки под cancelOperation:
где он говорит "окно отправляет сообщение о действии по умолчанию cancelOperation:
к первому респонденту, и оттуда сообщение перемещается вверх по цепочке респондента ". Кажется, для NSPanel
, и окно закрывается без первого ответчика, получающего cancelOperation:
вызов или делегаты NSTextView получают свои doCommandBySelector:
вызов.
Мои познания в цепочке респондентов in & out позорны, учитывая, что я работаю в OS X так долго, как и раньше. Я думал, что мне нужно сделать keyDown:
в моем NSPanel
подкласс ведет себя как у обычного окна. Я пытался переопределить NSPanel
и может поймать keyDown:
, переадресация вызова на NSWindow
"s keyDown:
вместо super
, но изменений не было, Escape все равно закрыл окно без сообщений первому ответчику. Было ли это даже разумно попробовать?
Затем я попытался полностью переопределить мой подкласс панели keyDown:
, заставляя это сделать это:
[self.firstResponder cancelOperation:self]
Я бы подумал, что это позволит моему текстовому полю обрабатывать экранирование, как оно обычно ожидает, и, возможно, если текстовое поле не было первым отвечающим, то вызов был бы тупиком. Однако я попробовал, и панель просто закрывается, как и раньше. Очевидно, я не перехватываю вещи на должном уровне.
Кто-нибудь знает, какова последовательность методов, которые запускаются между событиями нажатия клавиши низкого уровня и закрытием панели, или что мне нужно переопределить, чтобы перехватить и обеспечить cancelOperation:
идет к моему первому ответчику?
1 ответ
Быстрый перенос ответа Кита-Кнобера:
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!
Где -нибудь в вашем кончике или в коде, установите ваш делегат NSTableView на ваш контроллер.
Обратите внимание, что setDelegate: отличается от setDatasource:!
В моем случае: @interface ValueEditor: 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;
}