Получение событий keyDown и keyUp в приложении строки состояния какао

Если вы создаете приложение строки состояния без окон, как вы реагируете на события?

Моей первой догадкой было создание подкласса NSResponder и переопределение соответствующих методов. Однако им никогда не звонят.

Этот лид явно вызывает:

[self becomeFirstResponder];

Что также не сработало (и я не верю, что это рекомендовано Apple Docs)

Есть ли способ добавить мой подкласс NSResponder в цепочку респондентов?


person Corey Floyd    schedule 25.09.2009    source источник


Ответы (2)


Какие ключевые события вы ожидаете получить, если у вас нет окна?

Если вам нужно перехватить ключевые события глобально, вам нужно будет использовать Quartz Event Tap. Вы должны быть очень осторожны с ними, так как выброс исключения в обработчике касания события может заморозить сервер окна, поэтому у вас должна быть обработка исключений на месте.

#import <ApplicationServices/ApplicationServices.h>

//assume CGEventTap eventTap is an ivar or other global

void createEventTap(void)
{
 CFRunLoopSourceRef runLoopSource;

 ///we only want keydown events
 CGEventMask eventMask = (1 << kCGEventKeyDown);

 // Keyboard event taps need Universal Access enabled, 
 // check whether we're allowed to attach an event tap
 if (!AXAPIEnabled()&&!AXIsProcessTrusted()) { 
  // error dialog here 
  NSAlert *alert = [[[NSAlert alloc] init] autorelease];
  [alert addButtonWithTitle:@"OK"];
  [alert setMessageText:@"Could not start event monitoring."];
  [alert setInformativeText:@"Please enable \"access for assistive devices\" in the Universal Access pane of System Preferences."];
  [alert runModal];
  return;
 } 


 //create the event tap
 eventTap = CGEventTapCreate(kCGHIDEventTap, //this intercepts events at the lowest level, where they enter the window server
        kCGHeadInsertEventTap, 
        kCGEventTapOptionDefault, 
        eventMask,
        myCGEventCallback, //this is the callback that we receive when the event fires
        nil); 

 // Create a run loop source.
 runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

 // Add to the current run loop.
 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

 // Enable the event tap.
 CGEventTapEnable(eventTap, true);
}


//the CGEvent callback that does the heavy lifting
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef theEvent, void *refcon)
{
 //handle the event here
 //if you want to capture the event and prevent it propagating as normal, return NULL.

 //if you want to let the event process as normal, return theEvent.
 return theEvent;
}
person Rob Keniger    schedule 25.09.2009
comment
Я перехватываю опцию и нажимаю cmd, аналогично яблочному меню для изменения функции пунктов меню. Похоже, мне нужно пойти этим путем. - person Corey Floyd; 26.09.2009
comment
Если вам нужно изменить заголовок меню, когда оно уже открыто, вам обязательно нужно сделать это таким образом. Однако вы можете просто реализовать -menuNeedsUpdate: в своем делегате меню и вместо этого настроить заголовки меню на основе активных модификаторов в этой точке. Это не даст вам живых изменений в меню при нажатии / отпускании клавиш-модификаторов, но это намного проще, чем использование CGEventTap. - person Rob Keniger; 26.09.2009
comment
Меня не так беспокоит состояние MSMenuItems, поскольку я выполняю правильную функцию при нажатии. Технически мне не нужны обновления, но мне нужно правильное состояние клавиатуры, когда пользователь нажимает на пункт меню. Я бы предпочел не заставлять пользователя проходить дополнительный этап, требуя вспомогательных устройств. Я нашел HidLib Дэйва Дрибина, который через некоторое время я могу приступить к работе, но это кажется излишним. Нет ли абстракции Какао, которую можно использовать, чтобы просто набрать: [keyboard activeKeys]; Похоже, это должно быть тривиально. - person Corey Floyd; 28.09.2009
comment
Да, вы можете использовать [[NSApp currentEvent] modifierFlags] для получения текущих ключей-модификаторов. - person Rob Keniger; 29.09.2009

Конечно, ответ прост:

[NSApp currentEvent]; 

возвращает текущее событие в любом какао-приложении.

person Corey Floyd    schedule 05.01.2010