【问题标题】:Receiving keyDown and keyUp events in a Cocoa Status Bar app在 Cocoa 状态栏应用程序中接收 keyDown 和 keyUp 事件
【发布时间】:2010-01-06 06:46:01
【问题描述】:

如果你创建一个没有窗口的状态栏应用,你如何响应事件?

我的第一个猜测是创建 NSResponder 的子类并覆盖适当的方法。 然而,他们永远不会被调用。

此线索明确调用:

[self becomeFirstResponder];

这也不起作用(我不相信 Apple Docs 推荐)

有没有办法让我的 NSResponder 子类进入响应者链?

【问题讨论】:

    标签: objective-c cocoa input


    【解决方案1】:

    如果您没有窗口,您希望收到什么样的关键事件?

    如果您需要全局拦截关键事件,则需要使用 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;
    }
    

    【讨论】:

    • 我截取option和cmd按下,类似于apple menu改变菜单项的功能。看来我可能需要走这条路。
    • 如果您需要在菜单已打开的情况下更改菜单标题,那么您肯定需要这样做。但是,您可以只在您的菜单委托中实现 -menuNeedsUpdate: 并根据此时的活动修饰符调整菜单标题。当您按下/释放修饰键时,这不会给您“实时”更改菜单,但它比使用 CGEventTap 简单得多。
    • 我不太关心 MSMenuItems 的状态,因为我在单击时执行了正确的功能。从技术上讲,我不需要更新,但是当用户单击菜单项时,我需要正确的键盘状态。我不想让用户通过要求“辅助设备”来完成额外的步骤。我找到了 Dave Dribin 的 HidLib,过了一会儿我就可以开始工作了,但这似乎有点矫枉过正。是否没有 Cocoa 抽象可以用来输入:[keyboard activeKeys];看起来应该是微不足道的。
    • 是的,你可以使用 [[NSApp currentEvent] modifierFlags] 来获取当前的修饰键。
    【解决方案2】:

    答案当然很简单:

    [NSApp currentEvent]; 
    

    返回任何可可应用程序中的当前事件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多