Почему действия клавиатуры 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

Другие вопросы по тегам