Как получать события клавиатуры для NSView после выхода из полноэкранного режима? - PullRequest
5 голосов
/ 11 мая 2011

Я создаю подкласс NSView и запускаю полноэкранный режим, когда приложение завершает запуск.Представление доступно в виде свойства fooView в делегате приложения.

// AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
  [[self window] makeKeyAndOrderFront:self];
  [[self fooView] enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
}

Сам класс FooView реализует следующие функции.

// FooView.m
- (void)keyDown:(NSEvent*)event {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), event);
  [self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
}

Покинув полноэкранный режим,представление больше не получает события клавиатуры.Почему?

Редактировать:
Похоже, что-то связано с выходом из полноэкранного режима.Когда я щелкаю в представлении (не в окне), keyDown: и cancelOperation: отвечают следующим образом.

Ответы [ 2 ]

5 голосов
/ 05 июля 2011

Проблема заключалась в том, что окно, содержащее представление, получало какие-либо события клавиатуры. Необходимо сделать окно первым респондентом после выхода из полноэкранного режима.

- (void)cancelOperation:(id)sender {
  NSLog(@"%@ %@ - %@", self.className, NSStringFromSelector(_cmd), sender);
  [self exitFullScreenModeWithOptions:nil];
  [self.window makeFirstResponder:self];
}
3 голосов
/ 25 июля 2017

У меня была похожая проблема. После вызова -[NSView enterFullScreenMode:withOptions:] я не смог получить все события keyDown: (в частности, клавишу Escape вниз), пока не нажал на полноэкранный режим.

Я разыскал проблему, установив символическую точку останова на -[NSResponder doCommandBySelector:], которая показала трассировку стека:

  1. -[NSApplication sendEvent:]
  2. -[NSWindow sendEvent:]
  3. -[NSWindow keyDown:]
  4. -[NSWindow doCommandBySelector:]
  5. -[NSResponder doCommandBySelector:]

После этой точки прозвучал системный звуковой сигнал, указывающий, что не было объекта, который мог бы обработать событие keyDown.

Просмотр вывода сборки показал, что он проверяет ключ и основное состояние окна. Основная проблема заключалась в том, что частное полноэкранное окно (к которому вид AppKit прикрепляет) автоматически не превращается в главное окно или окно ключа и, следовательно, не получает события клавиш, как ожидалось.

Исправлено было вызвать -makeKeyAndOrderFront: в частном полноэкранном окне после вызова -[NSView enterFullScreenMode:withOptions:].

Это было сделано с использованием -[NSObject performSelector:withObject:afterDelay:], потому что только до следующей итерации цикла выполнения свойство представления window устанавливается в закрытое полноэкранное окно (вместо его исходного окна). Я не уверен в другом способе ссылки на приватное окно.

   [self.view.window performSelector:@selector(makeKeyAndOrderFront:)
                          withObject:nil
                          afterDelay:0];

Полноэкранный режим на NSView работает с помощью AppKit, удаляющего представление из его исходного окна, затем устанавливающего его как contentView частного окна типа _NSFullScreenWindow (которое, помимо прочего, не имеет строки заголовка) , Это можно увидеть, выбрав Debug > View Debugging > Capture View Hierarchy, когда просмотр находится в полноэкранном режиме. Выход из полноэкранного режима удаляет его из _NSFullScreenWindow и устанавливает его как contentView исходного окна.

EDIT:

Я удалил свое предыдущее исправление, описанное выше, так как оно больше не работало после того, как я изменил способ обработки ключевых событий. Теперь ключевые события в приложении обрабатываются через представление содержимого окна, которое является пользовательским подклассом NSView. При запуске приложения в contentView создаются окна initialResponder и firstResponder. Оба эти свойства окна должны быть установлены снова после вызова:

-[NSView exitFullScreenModeWithOptions:]

, потому что AppKit меняет их во время полноэкранного процесса.

В моем подклассе NSView, который обрабатывает ключевые события, а также в полноэкранном режиме:

[self exitFullScreenModeWithOptions:nil];
[self.window setInitialResponder:self];
[self.window makeFirstResponder:self];

Я также столкнулся с проблемой, когда события клавиатуры не работали на 10.9.5, когда представление было в полноэкранном режиме .

Проблема заключалась в том, что в закрытом окне, используемом для полноэкранного режима, не был установлен следующий респондент в качестве следующего респондента исходного окна , как AppKit делает это автоматически в 10.11+ (я не уверен в поведение на 10.10). Следующее исправило проблему:

// Get a reference to the window controller from the window BEFORE full screen mode is enabled
// and the view's window is set to the private AppKit "_NSFullScreenWindow" instance.
NSWindowController *windowController = self.window.windowController;

// Enable full screen mode on the view
[self enterFullScreenMode:screen withOptions:opts];

// Compatibility: On 10.9.5 the window controller is not set as the nextResponder on the private full-screen window automatically
// Set the existing window controller as the next responder for the private full screen window to ensure it is placed in the responder chain
[self.window setNextResponder:windowController];
...