Почему действия клавиатуры NSResponder не вызываются?
NSResponder определяет многочисленные действия, связанные с действиями клавиатуры, такими как moveUp:
, moveUpAndModifySelection:
а также moveToBeginningOfLine:
, За исключением действий, которые не запускаются нажатием клавиши-модификатора (например, moveUp:
это просто Up
клавиша со стрелкой) Я не могу получить какие-либо действия, которые будут вызваны в моем обычае NSView
,
Вот настройки:
- Xcode 5.1 / 10.9
- Базовое приложение Какао с ничего более, чем
Custom View
добавлено в главное окно. - Это пользовательское представление просто
NSView
классаSTView
STView
не делает ничего, кроме переопределенияacceptsFirstResponder
возвращатьYES
В этой настройке мой STView
Экземпляр будет правильно получать все события клавиатуры (в том числе с таким модификатором, как Ctl-A
в обоих performKeyEquivalent:
а также keyDown:
, как и ожидалось. (Оба метода закомментированы для обсуждения ниже.)
Если я предоставлю реализацию для moveUp:
как определено в NSResponder
затем moveUp:
правильно вызывается, когда пользователь нажимает Up
клавишу со стрелкой.
Смутно, moveUp:
также вызывается всякий раз, когда Up
клавиша со стрелкой нажата с ключом модификатора. Когда клавиша-модификатор удерживается (например: Ctl-Up
) Я ожидаю, что будет вызван соответствующий метод действия клавиатуры.
Пример:
Ctl-Up
->moveUp:
(Ожидается:scrollPageUp:
)Alt-Up
->moveUp:
(Ожидается:moveToBeginningOfParagraph:
)Shift-Up
->moveUp:
(Ожидается:moveUpAndModifySelection:
)Command-Up
->moveUp:
(Ожидается:moveToBeginningOfDocument:
)
Этот шаблон повторяется для всех комбинаций клавиш / модификаторов.
Две области в документации Apple кажутся актуальными:
Читая эту документацию, похоже, что система отвечает за сопоставление ключевых событий с их соответствующими действиями. И действительно, это работает для меня, когда клавиша-модификатор не нажата. Но когда нажимается клавиша-модификатор, событие обрабатывается как любое другое событие и передается по обычной цепочке респондента.
(Я вижу, как событие-модификатор поступает через NSApplication sendEvent
, хоть NSApplication sendAction:to:from:
никогда не вызывается, и продолжается вниз по иерархии представлений, чего я и ожидал, прочитав приведенную выше документацию.
Документация намекает на то, что представления редактирования текста могут рассматриваться по-разному Эти действия клавиатуры отправляются только в представления, которые редактируют текст? Если нет, то как получить сообщение, как moveUpAndModifySelection:
быть призванным по их обычаю NSView
класс, когда пользователь нажимает Shift-Up
?
Для справки, я использую эту таблицу для привязок клавиатуры, хотя, честно говоря, я пробовал почти все комбинации, перечисленные или нет:
В конце дня я мог бы просто переопределить keyDown:
и вручную обработать все комбинации, которые меня интересуют, но я надеялся хотя бы понять, почему это не работает так, как задумано для меня.
(Обратите внимание, что предполагаемое применение здесь - позволить пользователю перемещаться по сетке, как элементы, очень похоже на коллекцию.)
Любые советы, советы или комментарии будут высоко оценены.
Обновить:
Если вы делаете следующее в вашем keyDown
- (void)keyDown:(NSEvent *)theEvent {
...
[self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
}
Тогда "правильные" действия будут вызваны в вашем пользовательском представлении. Я предполагаю, что это потому, что действия, которые меня интересуют, являются текстоподобными действиями, которые требуют, чтобы менеджер ввода интерпретировал и преобразовал их в соответствующие действия.
Мне до сих пор не совсем понятно, почему moveUp:
правильно вызывается. Кто в цепочке респондента признает Up
нажатие клавиши со стрелкой, а затем отправка moveUp:
сообщение?
2 ответа
Это своего рода ответ на ваш ответ, который является скорее расширением вопроса.
Да, в большинстве случаев необходимо позвонить -[NSResponder interpretKeyEvents:]
или же -[NSTextInputContext handleEvent:]
чтобы заставить систему привязок клавиш отправлять вам связанные методы действий.
Что касается того, почему вы получаете -moveUp:
Я реализовал следующее в простом пользовательском классе представления:
- (BOOL) acceptsFirstResponder
{
return YES;
}
- (void) moveUp:(id)sender
{
NSLog(@"%@", [NSThread callStackSymbols]);
}
Это записало следующий стек вызовов:
0 TestKeyUp 0x000000010000182d -[KeyView moveUp:] + 48
1 AppKit 0x00007fff85e44012 -[NSWindow _processKeyboardUIKey:] + 325
2 AppKit 0x00007fff85ab1075 -[NSWindow keyDown:] + 94
3 AppKit 0x00007fff8589e206 forwardMethod + 104
4 AppKit 0x00007fff8589e206 forwardMethod + 104
5 AppKit 0x00007fff8596c0c7 -[NSWindow sendEvent:] + 8769
6 AppKit 0x00007fff858a0afa -[NSApplication sendEvent:] + 4719
7 AppKit 0x00007fff858376de -[NSApplication run] + 474
8 AppKit 0x00007fff858303b0 NSApplicationMain + 364
9 TestKeyUp 0x000000010000176d main + 33
10 TestKeyUp 0x0000000100001744 start + 52
11 ??? 0x0000000000000001 0x0 + 1
NSWindow
обрабатывает клавишу со стрелкой вверх как часть "управления интерфейсом клавиатуры". Это частично задокументировано, но неясно, что он будет вызывать такие методы, как -moveUp:
, Видимо, вот как это реализовано.
Читая эту документацию, похоже, что система отвечает за сопоставление ключевых событий с их соответствующими действиями.
Там нет "системы". NSResponder работает следующим образом: произвольное сообщение (просто строка символов) помещается в начало цепочки респондента (независимо от того, имеет фокус клавиатуры). Обычно он ничего не делает и передает его "следующему" респонденту. Он идет по цепочке респондента, ничего не делая, пока после 50 или 60 нулевых операций что-то, наконец, не скажет: "Да, я отвечу на это!" и это что-то делает. Как только это происходит, ничто больше не обнаруживает, что событие произошло.
Часто респондент вызывает другого респондента. Так, например, если вы отправляете keyDown:
сообщение для Cmd-S
он ничего не будет делать до тех пор, пока не достигнет пункта меню "сохранить как", который затем помещает saveDocument:
в начале цепочки респондента, снова и снова спускаясь, пока не дойдет до NSDocument, который выполнит операцию сохранения.
Смутно,
moveUp:
также вызывается всякий раз, когдаUp
клавиша со стрелкой нажата с ключом модификатора. Когда клавиша-модификатор удерживается (например:Ctl-Up
) Я ожидаю, что будет вызван соответствующий метод действия клавиатуры.Пример:
Ctl-Up
->moveUp:
(Ожидается:scrollPageUp:
)Alt-Up
->moveUp:
(Ожидается:moveToBeginningOfParagraph:
)Shift-Up
->moveUp:
(Ожидается:moveUpAndModifySelection:
)Command-Up
->moveUp:
(Ожидается:moveToBeginningOfDocument:
)
Это не сбивает с толку, цепочка респондента ничего не знает о представлениях прокрутки, текстовых представлениях или о чем-либо вообще. Он просто знает, что приложение подтолкнуло keyDown
или же mouseDown:
сообщение на цепь и миллисекунду спустя scrollPageUp:
сообщение пошло по цепочке, а может и нет. В цепочке респондентов есть много вещей, которые никогда не привыкают. Они доступны для запуска, если они вам нужны, и некоторые вещи будут запускаться AppKit, но многие из них - нет.
Остерегайтесь списка методов в NSResponder
в основном только для того, чтобы упростить жизнь разработчикам с автозаполнением кода и заполнить список доступных действий при создании интерфейса с графическим интерфейсом. Вы можете поставить любой @selector()
на цепочке респондента (пока у него есть один sender
параметр). Не все в активном использовании задокументированы.
В конце дня я, вероятно, мог бы просто переопределить keyDown: и вручную обработать все интересующие меня комбинации, но я надеялся хотя бы понять, почему это не работает, как задумано для меня.
Ага. Если возможно, вы должны иметь keyDown:
просто сделай что-то вроде [self moveUpAndModifySelection:self];
, Так что в основном это большой оператор switch или оператор if без особой логики.
Вот как я это сделал: https://github.com/abhibeckert/Dux/blob/master/Dux/DuxTextView.m#L797