非NSResponder类捕捉按键消息[How to capture ESC key in a Cocoa application]

From: http://www.ideawu.com/blog/2013/04/how-to-capture-esc-key-in-a-cocoa-application.html

  http://forums.macrumors.com/showthread.php?t=780577


You may want to capture(or intercept, detect) the ESC key press in a Cocoa application, capture the ESC key down in Application scope for the whole app, or just in Window scope for some certain windows.

The NSApplicationDelegate is not a subclass of NSResponder, so you can not implement the keyDown: method int app delegate. The NSWindowController is a subclass of NSResponder, you may implement keyDown: method in it, but it will not get invoked when user press the ESC key(with exceptions) and not when user is typing in a text input.

The solution is to use a Event Monitor.

Here’s the sample codes:

 1  @interface MyController : NSWindowController{
 2      id eventMonitor;
 3  }
 4  @end
 5  
 6  @implementation MyController
 7  - (void)windowDidLoad{
 8      NSEvent* (^handler)(NSEvent*) = ^(NSEvent *theEvent) {
 9          NSWindow *targetWindow = theEven.window;
10          if (targetWindow != self.window) {
11              return theEvent;
12          }
13          
14          NSEvent *result = theEvent;
15          NSLog(@"event monitor: %@", theEvent);
16          if (theEven.keyCode == 53) {
17              [self myProcessEscKeyDown];
18              result = nil;
19          }
20  
21          return result;
22      };
23      eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:handler];
24  }
25  
26  - (void)windowWillClose:(NSNotification *)notification{
27      [NSEvent removeMonitor:eventMonitor];
28  }
@end

All the stuff is down in a NSWindowController subclass, if you want to implement a key capture in application scope, put these codes in the application delegate, replace windowDidLoad: withapplicationDidFinishLaunching:windowWillClose: with applicationWillTerminate:.

 7  - (void)windowDidLoad{

Register a keyboard event handler just as window is loaded.

10          if (targetWindow != self.window) {
11              return theEvent;
12          }

Let events in other windows pass, we only handle events of the window this controller managed.

16          if (theEven.keyCode == 53) {
17              [self myProcessEscKeyDown: theEvent];
18              result = nil;
19          }

Detect ESC key down event, process it, and drop the event(return nil) so the responder chain will never know.

23      eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:handler];

Besides NSKeyDown, there are NSKeyUp, NSLeftMouseUp, NSLeftMouseDown, etc. But we only monitor key down event in this example, so set the mask to NSKeyDownMask.

26  - (void)windowWillClose:(NSNotification *)notification{
27      [NSEvent removeMonitor:eventMonitor];
28  }

Deattach the event monitor when window close.

Discussions on other approaches

You can achieve it by implementing the sendEvent: of a NSWindow subclass, and you need to use this NSWindow subclass to build up UI in Interface Builder, not the default NSWindow class. I don’t recommend you to do in this way, because it will add two extra files in you project.

A NSWindowController’s keyDown: will get a ESC press event if the first responder is a WebView, I don’t know what have the WebView done to hand over the event to the controller even there are still other views in the view hierachy. Please let me know if you know the reason.

Cocoa Event Architecture and Responder Chain


Responder chain

NSEvent keyCode list

/*
	 *  Summary:
	 *    Virtual keycodes
	 *  
	 *  Discussion:
	 *    These constants are the virtual keycodes defined originally in
	 *    Inside Mac Volume V, pg. V-191. They identify physical keys on a
	 *    keyboard. Those constants with "ANSI" in the name are labeled
	 *    according to the key position on an ANSI-standard US keyboard.
	 *    For example, kVK_ANSI_A indicates the virtual keycode for the key
	 *    with the letter 'A' in the US keyboard layout. Other keyboard
	 *    layouts may have the 'A' key label on a different physical key;
	 *    in this case, pressing 'A' will generate a different virtual
	 *    keycode.
	 */
	enum {
	  kVK_ANSI_A                    = 0x00,
	  kVK_ANSI_S                    = 0x01,
	  kVK_ANSI_D                    = 0x02,
	  kVK_ANSI_F                    = 0x03,
	  kVK_ANSI_H                    = 0x04,
	  kVK_ANSI_G                    = 0x05,
	  kVK_ANSI_Z                    = 0x06,
	  kVK_ANSI_X                    = 0x07,
	  kVK_ANSI_C                    = 0x08,
	  kVK_ANSI_V                    = 0x09,
	  kVK_ANSI_B                    = 0x0B,
	  kVK_ANSI_Q                    = 0x0C,
	  kVK_ANSI_W                    = 0x0D,
	  kVK_ANSI_E                    = 0x0E,
	  kVK_ANSI_R                    = 0x0F,
	  kVK_ANSI_Y                    = 0x10,
	  kVK_ANSI_T                    = 0x11,
	  kVK_ANSI_1                    = 0x12,
	  kVK_ANSI_2                    = 0x13,
	  kVK_ANSI_3                    = 0x14,
	  kVK_ANSI_4                    = 0x15,
	  kVK_ANSI_6                    = 0x16,
	  kVK_ANSI_5                    = 0x17,
	  kVK_ANSI_Equal                = 0x18,
	  kVK_ANSI_9                    = 0x19,
	  kVK_ANSI_7                    = 0x1A,
	  kVK_ANSI_Minus                = 0x1B,
	  kVK_ANSI_8                    = 0x1C,
	  kVK_ANSI_0                    = 0x1D,
	  kVK_ANSI_RightBracket         = 0x1E,
	  kVK_ANSI_O                    = 0x1F,
	  kVK_ANSI_U                    = 0x20,
	  kVK_ANSI_LeftBracket          = 0x21,
	  kVK_ANSI_I                    = 0x22,
	  kVK_ANSI_P                    = 0x23,
	  kVK_ANSI_L                    = 0x25,
	  kVK_ANSI_J                    = 0x26,
	  kVK_ANSI_Quote                = 0x27,
	  kVK_ANSI_K                    = 0x28,
	  kVK_ANSI_Semicolon            = 0x29,
	  kVK_ANSI_Backslash            = 0x2A,
	  kVK_ANSI_Comma                = 0x2B,
	  kVK_ANSI_Slash                = 0x2C,
	  kVK_ANSI_N                    = 0x2D,
	  kVK_ANSI_M                    = 0x2E,
	  kVK_ANSI_Period               = 0x2F,
	  kVK_ANSI_Grave                = 0x32,
	  kVK_ANSI_KeypadDecimal        = 0x41,
	  kVK_ANSI_KeypadMultiply       = 0x43,
	  kVK_ANSI_KeypadPlus           = 0x45,
	  kVK_ANSI_KeypadClear          = 0x47,
	  kVK_ANSI_KeypadDivide         = 0x4B,
	  kVK_ANSI_KeypadEnter          = 0x4C,
	  kVK_ANSI_KeypadMinus          = 0x4E,
	  kVK_ANSI_KeypadEquals         = 0x51,
	  kVK_ANSI_Keypad0              = 0x52,
	  kVK_ANSI_Keypad1              = 0x53,
	  kVK_ANSI_Keypad2              = 0x54,
	  kVK_ANSI_Keypad3              = 0x55,
	  kVK_ANSI_Keypad4              = 0x56,
	  kVK_ANSI_Keypad5              = 0x57,
	  kVK_ANSI_Keypad6              = 0x58,
	  kVK_ANSI_Keypad7              = 0x59,
	  kVK_ANSI_Keypad8              = 0x5B,
	  kVK_ANSI_Keypad9              = 0x5C
	};
	
	/* keycodes for keys that are independent of keyboard layout*/
	enum {
	  kVK_Return                    = 0x24,
	  kVK_Tab                       = 0x30,
	  kVK_Space                     = 0x31,
	  kVK_Delete                    = 0x33,
	  kVK_Escape                    = 0x35,
	  kVK_Command                   = 0x37,
	  kVK_Shift                     = 0x38,
	  kVK_CapsLock                  = 0x39,
	  kVK_Option                    = 0x3A,
	  kVK_Control                   = 0x3B,
	  kVK_RightShift                = 0x3C,
	  kVK_RightOption               = 0x3D,
	  kVK_RightControl              = 0x3E,
	  kVK_Function                  = 0x3F,
	  kVK_F17                       = 0x40,
	  kVK_VolumeUp                  = 0x48,
	  kVK_VolumeDown                = 0x49,
	  kVK_Mute                      = 0x4A,
	  kVK_F18                       = 0x4F,
	  kVK_F19                       = 0x50,
	  kVK_F20                       = 0x5A,
	  kVK_F5                        = 0x60,
	  kVK_F6                        = 0x61,
	  kVK_F7                        = 0x62,
	  kVK_F3                        = 0x63,
	  kVK_F8                        = 0x64,
	  kVK_F9                        = 0x65,
	  kVK_F11                       = 0x67,
	  kVK_F13                       = 0x69,
	  kVK_F16                       = 0x6A,
	  kVK_F14                       = 0x6B,
	  kVK_F10                       = 0x6D,
	  kVK_F12                       = 0x6F,
	  kVK_F15                       = 0x71,
	  kVK_Help                      = 0x72,
	  kVK_Home                      = 0x73,
	  kVK_PageUp                    = 0x74,
	  kVK_ForwardDelete             = 0x75,
	  kVK_F4                        = 0x76,
	  kVK_End                       = 0x77,
	  kVK_F2                        = 0x78,
	  kVK_PageDown                  = 0x79,
	  kVK_F1                        = 0x7A,
	  kVK_LeftArrow                 = 0x7B,
	  kVK_RightArrow                = 0x7C,
	  kVK_DownArrow                 = 0x7D,
	  kVK_UpArrow                   = 0x7E
	};
	
	/* ISO keyboards only*/
	enum {
	  kVK_ISO_Section               = 0x0A
	};
	
	/* JIS keyboards only*/
	enum {
	  kVK_JIS_Yen                   = 0x5D,
	  kVK_JIS_Underscore            = 0x5E,
	  kVK_JIS_KeypadComma           = 0x5F,
	  kVK_JIS_Eisu                  = 0x66,
	  kVK_JIS_Kana                  = 0x68
	};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值