本文转自:http://www.macdev.io/ebook/event.html

  • 事件分发过程

OSX 与用户交互的主要外设是鼠标,键盘。鼠标键盘的活动会产生底层系统事件。这个事件首先传递到IOKit框架处理后存储到队列,通知Window Server服务层处理。Window Server存储到FIFO先进先出队列中,逐一转发到当前的活动窗口或能响应这个事件的应用程序去处理。

每个应用都有自己的Main Run Loop线程,Run Loop会遍历event消息队列,逐一分发这些事件到应用中合适的对象去处理。具体来说就是调用NSApp 的 sendEvent:方法发送消息到NSWindow,NSWindow再分发到NSView视图对象,由其鼠标或键盘事件响应方法去处理。

事件响应者(Responders):能处理鼠标键盘等事件的对象,包括NSApplication, NSWindow, NSDrawer, NSWindowController, NSView 以及继承于NSView的所有控件对象。

第一响应者(First Responders):鼠标按下或者键盘输入激活的当前对象称之为第一响应者。

响应者链:能处理事件响应的一组按优先级排序的对象,从层级上看离观察者最近的视图优先响应事件,通过view的hitTest方法检测,满足hitTest方法的的子视图优先响应事件。

  • 事件中的几个关键类

NSResponder

NSResponder定义了鼠标键盘触控板等多种事件方法,下面列出一些鼠标键盘主要的方法

1.鼠标按下事件响应方法

-(void)mouseDown:(NSEvent *)theEvent;

2.鼠标右键按下事件响应方法

-(void)rightMouseDown:(NSEvent *)theEvent;

3.鼠标松开事件响应方法

-(void)mouseUp:(NSEvent *)theEvent;

4.鼠标拖放事件响应方法

-(void)mouseDragged:(NSEvent *)theEvent;

5.鼠标进入跟踪区域事件响应方法

-(void)mouseEntered:(NSEvent *)theEvent;

6.鼠标退出跟踪区域事件响应方法

-(void)mouseExited:(NSEvent *)theEvent;

7.鼠标拖放事件响应方法

-(void)mouseMoved:(NSEvent *)theEvent;

8.键盘按键按下事件响应方法

-(void)keyDown:(NSEvent *)theEvent;

9.键盘按键松开事件响应方法

-(void)keyUp:(NSEvent *)theEvent;

NSResponder除了定义基本的响应事件外,还定义了很多key绑定事件方法。具体请参考NSResponder.h的头文件定义。

NSEvent

1.事件类型,指示鼠标,键盘,触控板不同的事件源

@property (readonly) NSEventType type;

2.键盘不同功能区的标志,可以用来区分数字键,F1-F2功能键,Command,Optioan,Control,Shift不同的功能键

@property (readonly) NSEventModifierFlags modifierFlags;

3.鼠标,键盘等事件发生的时间

@property (readonly) NSTimeInterval timestamp;

4.事件发生的窗口

@property (nullable, readonly, assign) NSWindow *window;

5.鼠标点击次数

@property (readonly) NSInteger clickCount;

@property (readonly) NSInteger buttonNumber;

6.鼠标在窗口的位置

@property (readonly) NSPoint locationInWindow;

7.输入的字符串

@property (nullable, readonly, copy) NSString *characters;

8.输入的字符串不包括控制键(Ctrl,Option,Command,Shift)

@property (nullable, readonly, copy) NSString *charactersIgnoringModifiers;

9.按键编码

@property (readonly) unsigned short keyCode;

鼠标事件

NSApp对于激活/去激活/隐藏/显示应用的鼠标消息,会自己处理。其他鼠标消息转发到NSWindow。
NSWindow窗口接收到鼠标event事件,NSWindow调用sendEvent: 发送到鼠标事件发生位置最顶层的View视图上。

从NSWindow的sendEvent方法中可以拦截到所有的事件消息,可以在这里做特殊流程处理。

  1. - (void)sendEvent:(NSEvent *)theEvent {
  2. NSLog(@"theEvent %@ ",theEvent);
  3. [super sendEvent:theEvent];
  4. }

从操作行为和处理机制上把鼠标事件分为鼠标点击/鼠标拖放/鼠标区域跟踪事件,下面逐一介绍说明。

鼠标事件发生的位置

先获取event发生的window中的坐标,在转换成view视图坐标系的坐标。

  1. NSPoint eventLocation = [event locationInWindow];
  2. NSPoint center = [self convertPoint:eventLocation fromView:nil];

鼠标点击事件

鼠标按下,鼠标松开一个连续的动作或者鼠标右键按下被认为是一个鼠标点击事件。
mouseDown对应鼠标按下事件响应方法,mouseUp对应鼠标松开事件响应方法。

鼠标左键按下

  1. - (void)mouseDown:(NSEvent *)theEvent {
  2. //获取鼠标点击位置坐标
  3. NSPoint clickLocation = [self convertPoint:[event locationInWindow]
  4. fromView:nil];
  5. //逻辑处理代码...
  6. }

鼠标右键按下

-(void)rightMouseDown:(NSEvent *)theEvent

鼠标左键松开

-(void)mouseUp:(NSEvent *)theEvent;

鼠标右键松开

-(void)rightMouseUp:(NSEvent *)theEvent;

判断是否按下了Command键,如果满足条件则处理,否则转由super父类去处理。

  1. - (void)mouseDown:(NSEvent *)theEvent {
  2. if ([theEvent modifierFlags] & NSCommandKeyMask) {
  3. [self setFrameRotation:[self frameRotation]+90.0];
  4. [self setNeedsDisplay:YES];
  5. }
  6. else{
  7. [super mouseDown:theEvent];
  8. }
  9. }

判断是否鼠标双击

  1. - (void)mouseDown:(NSEvent *)theEvent {
  2. if ([theEvent clickCount] > ) {
  3. //双击相关处理
  4. }
  5. else{
  6. [super mouseDown:theEvent];
  7. }
  8. }

鼠标拖放

鼠标按下,接着移动到某一个位置,最后松开鼠标按键。这样一个过程,称之为鼠标拖放3阶段。
对应的事件过程:mouseDown->mouseDragged->mouseUp

判断鼠标位置是否在点击准备拖放的控件的中心点范围内,如果是置拖放标记为YES。
(实际项目中可以自行设置满足拖放的条件)

  1. - (void)mouseDown:(NSEvent *)theEvent {
  2. NSPoint eventLocation = [theEvent locationInWindow];
  3. NSPoint point = [self convertPoint:eventLocation fromView:nil];
  4. //判断当前鼠标位置是否在中心点范围内
  5. if (NSPointInRect(point, centerBox)) {
  6. draged = YES;
  7. }
  8. }

如果拖放标记为YES,修改正在拖放的控件的位置

  1. - (void)mouseDragged:(NSEvent *)theEvent {
  2. if (draged) {
  3. NSPoint eventLocation = [theEvent locationInWindow];
  4. CGRect positionBox = CGRectMake(eventLocation.x, eventLocation.y, self.frame.size.width, self.frame.size.height);
  5. self.frame = positionBox;
  6. }
  7. }

拖放接收,修改拖放标记为NO

  1. - (void)mouseUp:(NSEvent *)theEvent {
  2. draged = NO;
  3. }

鼠标跟踪

为了高效的处理鼠标事件,避免无效的区域被监测。可以定义一个矩形区域,在这个区域内鼠标的任何活动(进入/移动/退出)都会收到鼠标事件。

1.使用NSTrackingArea定义跟踪区域

  1. CGRect eyeBox = CGRectMake(, , , );
  2.  
  3. NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:eyeBox
  4. options: (NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
  5. NSTrackingActiveInKeyWindow )
  6. owner:self userInfo:nil];
  7.  
  8. [self addTrackingArea:trackingArea];

各种options选项,根据需要可以按位或来表示需要跟踪哪些事件

  1. NSTrackingMouseEnteredAndExited:鼠标进入/退出
  2. NSTrackingMouseMoved:鼠标移动
  3. NSTrackingActiveWhenFirstResponder:第一响应者时跟踪所有事件
  4. NSTrackingActiveInKeyWindow:应用是key Window 跟踪所有事件
  5. NSTrackingActiveInActiveApp:应用是激活状态时跟踪所有事件
  6. NSTrackingActiveAlways:跟踪所有事件(鼠标进入/退出/移动)
  7. NSTrackingCursorUpdate:更新鼠标光标形状

2.监测鼠标事件

鼠标进入跟踪区域

  1. - (void)mouseEntered:(NSEvent *)theEvent {
  2. NSLog(@"mouseEntered");
  3. }

鼠标在跟踪区域移动

  1. - (void)mouseMoved:(NSEvent *)theEvent {
  2. NSLog(@"mouseMoved");
  3. }

鼠标离开跟踪区域

  1. - (void)mouseExited:(NSEvent *)theEvent {
  2. NSLog(@"mouseExited");
  3. }

鼠标光标更新为十字架形状

  1. - (void)cursorUpdate:(NSEvent *)theEvent {
  2.  
  3. [[NSCursor crosshairCursor] set];
  4. }

事件监控

系统提供了2种事件监控处理方法,一种是不包括应用本身事件的全局监控,一个是只监控应用中发生的事件的局部监控。

1.全局监控

第一个参数为事件类型,可以增加多种事件类型,第二个参数是事件回调函数。

  1. id eventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler: ^ (NSEvent *theEvent) {
  2. return theEvent;
  3. }

2.局部监控

  1. id eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler: ^ (NSEvent *theEvent) {
  2. return theEvent;
  3. }

3.监控删除

窗口关闭或页面关闭时删除监控

[NSEvent removeMonitor:eventMonitor];

比如鼠标离开一个window,需要关闭这个window时至少有2种方法可以解决:

1)注册NSNotificationCenter消息中心的 NSApplicationDidResignActiveNotification 消息通知来处理。

2)使用NSEvent的局部监控事件的方法

注册NSEvent事件监控会接收大量的系统事件,从性能上考虑事件监控不是解决问题的最优方案,尽量不要使用事件监控。

Action消息

Action消息是一种特殊的系统事件,不同于普通的鼠标键盘事件NSApp使用sendEvent做消息转发,Action消息是NSApp 的sendAction方法转发的。
-(BOOL)sendAction:(SEL)theAction to:(id)theTarget from:(id)sender;
theAction参数是事件响应方法,theTarget参数是事件响应关联的controller或其他对象, sender是事件发生的控件本身。

Action事件是MouseDown事件的2次转发。鼠标点击首先触发控件的MouseDown方法,MouseDown中会执执行sendAction:to:方法将分发到实现了action事件的target对象中。

可以看出普通的事件消息是在控件内部处理,KeyDown,MouseDown等事件响应方法是定义在控件内部。而Action消息的事件响应方法一般是在target对象的内部定义实现的。

NSControl ,NSMenu ,NSToolbar等控件都是以Action消息形式响应事件。

OSX 鼠标和键盘事件的更多相关文章

  1. C#/winform 自动触发鼠标、键盘事件

    要在C#程序中触发鼠标.键盘事件必须要调用windows函数. 一.鼠标事件的触发 1.引用windows函数mouse_event /// <summary> /// 鼠标事件 /// ...

  2. Selenium WebDriver 中鼠标和键盘事件分析及扩展(转)

    本文将总结 Selenium WebDriver 中的一些鼠标和键盘事件的使用,以及组合键的使用,并且将介绍 WebDriver 中没有实现的键盘事件(Keys 枚举中没有列举的按键)的扩展.举例说明 ...

  3. java鼠标与键盘事件监听

    package cn.stat.p3.windowdemo; import java.awt.Button; import java.awt.FlowLayout; import java.awt.F ...

  4. HTML5 Canvas鼠标与键盘事件

    演示HTML5 Canvas鼠标事件,获取Canvas对象上的鼠标坐标,演示键盘事件 通过键盘控制Canvas上对象移动. Canvas对象支持所有的JavaScript的鼠标事件,包括鼠标点击(Mo ...

  5. tkinter中鼠标与键盘事件(十五)

    鼠标与键盘事件 import tkinter wuya = tkinter.Tk() wuya.title("wuya") wuya.geometry("300x200+ ...

  6. 50-用Python监听鼠标和键盘事件

    转自:https://www.cnblogs.com/qiernonstop/p/3654021.html 用Python监听鼠标和键盘事件 PyHook是一个基于Python的“钩子”库,主要用于监 ...

  7. JavaScript事件基础-10-2.HTML事件; DOM0级事件; 掌握常用的鼠标与键盘事件 ; 掌握this的指向;

    JavaScript事件基础 学习目标 1.掌握什么是事件 2.掌握HTML事件 3.掌握DOM0级事件 4.掌握常用的鼠标与键盘事件 5.掌握this的指向 什么是事件 事件就是文档或浏览器窗口中发 ...

  8. C# 自动触发鼠标、键盘事件

    要在C#程序中触发鼠标.键盘事件必须要调用windows函数. 一.鼠标事件的触发 1.引用windows函数mouse_event /// <summary> /// 鼠标事件 /// ...

  9. 使用Robot类模拟鼠标、键盘事件

    Robot类用于模拟鼠标.键盘事件,生成本机系统输入事件.Robot 的主要用于自动化.自运行的程序和其他需要自动控制鼠标和键盘的程序 相当于实际操作的效果,不仅仅只是生成对应的鼠标.键盘事件.比如R ...

随机推荐

  1. uvalive 3213 Ancient Cipher

    https://vjudge.net/problem/UVALive-3213 题意: 输入两个字符串,问是否可以由第一个字符串的每个字符一一映射得到第二个字符串,字符是可以随意移动的. 思路: 统计 ...

  2. 爆炸,解体,入侵,你想得到的你想不到的大BUG们

    郑昀 创建于2017/9/29 最后更新于2017/10/6 提纲: 阿丽亚娜火箭的解体 阿波罗飞船的P01模式 德勤的Google+ 麻省理工的500英里邮件 又到了扶额兴叹的节气.(前文回顾:5年 ...

  3. 洛谷 P3258 [JLOI2014]松鼠的新家(树链剖分)

    题目描述松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来 ...

  4. 网络流入门-POJ1459PowerNetwork-Dinic模板

    (我有什么错误或者你有什么意见,欢迎留言或私聊!谢谢!) (Ps:以前听说过网络流,想着以后再学,这次中南多校赛也碰到有关网络流的题目,想着这两天试着学学这个吧~~ 这是本人网络流入门第二题,不知道怎 ...

  5. 使用控制台调试WinForm窗体程序

    .程序代码结构 .Win32DebuggerHelper.cs using System.Runtime.InteropServices; /* TODO:使用方法 Win32.AllocConsol ...

  6. Hive优化案例

    1.Hadoop计算框架的特点 数据量大不是问题,数据倾斜是个问题. jobs数比较多的作业效率相对比较低,比如即使有几百万的表,如果多次关联多次汇总,产生十几个jobs,耗时很长.原因是map re ...

  7. [LeetCode] Longest Word in Dictionary through Deleting 删除后得到的字典中的最长单词

    Given a string and a string dictionary, find the longest string in the dictionary that can be formed ...

  8. 用AJAX实现上传图片或者文件的方法

    大家好,我是小C,最近在项目中用到ajax上传图片文件,本篇我们就说说ajax上传文件. 我们平时用到的AJAX,大部分都是传几个参数就可以了.简单说就是传几个字符串. $.ajax({ url: u ...

  9. Python默认版本切换

    Mac上自带python2.7 版本,但是我又下了一个3.7版本(下载的版本默认安装在 /Library/Frameworks/Python.framework/Versions/3.7/bin/py ...

  10. 【Matplotlib-01】Python 绘图库 Matplotlib 入门教程

    环境: Windows10 python3.6.4 numpy1.14.1 matplotlib2.1.2 工具:Cmder 目录: 1.线性图 2.散点图 3.饼状图 4.条形图 5.直方图 例1: ...