1、 UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛1UCGUI 窗体管理及消息处理机制分析-多对话框/模态窗体/透明窗体支持分析作者:ucgui日期: 2005-09-08v1.0.0.0 2005-06-30 完成来源: http:/版本: v1.0.0.1版本 修改说明 时间v1.0.0.0 实现 UCGUI 中多对话框支持。 2005-06-30v1.0.0.1 增加 UCGUI 中各种基本消息介绍 。 增加窗体消息 LOOP 机制介绍。 增加对话框结构说明及其消息 LOOP 处理移到 M ainTask 函数中由用户处理的原因剖析,并详细分析了对话框中按钮的点击消息传送
2、到用户自定义对话框回调函数中处理的传递流程。 增加外部输入设备消息处理机制介绍。如滑动操作外设 MOUSE 及触摸屏输入消息(WM_TOUCH)的处理机制,及按键式操作外设消息(WM_KEY)处理机制。 增加一种更简单的多对话框支持的方法及说明。 增加模态对话框实现原理分析。 增加透明窗体实现原理分析。2005-09-08问题的提出: 求助关于对话框处理程序中,想在 OK 按钮按下后想弹出一个消息框,该怎么做?直接加在程序中好像不行,如何让消息框弹出后成为模态窗体呢?请版主帮帮忙。 解析在 UCGUI 中,对话框只支持单个对话框窗体,不支持多个独立的对话框,现在我们从其源码来分析一下它为什么支
3、持单个对话框窗体以及如何改进它以支持多个独立对话框,要讲解这个问题我们必须首先理解 UCGUI中的窗体消息 LOOP,没有消息 LOOP 窗体就是死水一潭,不能接受任何外界的输入,只是一个画在那里的图画而已。 声明本文中提到的源码均为 UCGUI3.24 版源码,新版 UCGUI 源码会有改动,请下载本文示例代码来结合阅读本文。摘要: 本文主要介绍了 UCGUI 中的对话框的消息处理机制,并指出在现有 UCGUI上如何增加多窗体支持,并在分析解决问题时着重介绍了其输入设备消息WM_TOUTCH 及 WM_KEY 两类消息处理方法,并同时初步指出一种在 UCGUI 中实现UCGUI 技术文集 U
4、CGUI 专业网站:UCGUI 专业论坛2模态对话框以及透明窗体的原理说明,不还有窗体重画消息 WM_PAINT 消息处理原理。一、各种基本消息介绍及处理流程-对话框内部消息流转及外部消息 LOOP 分析.UCGUI 是采用的消息驱动的,它专门有对外的一套收集消息的接口, 我在模似器中, 就是通过 LCD 模拟显示屏窗口的 MOUSE 消息,将 MOUSE 消息传入到这个接口中, 以驱动 UCGUI 中的窗体的。UCGUI 中的消息驱动其实与 WINDOWS 的是类似的,几种基本的消息与WINDOWS 是一样的,但 UCGUI 的更简单且消息更少,对于一些消息的处理得也很简化,没有 WINDO
5、WS 那么多的消息种类及复杂处理。在 WINDOWS 中,如我们处理按钮控件的点击事件的是在 WM_COMMAND 消息中,通过按钮的标志 ID 来区分不同的按钮,所以按钮标志 ID 必须不同的,否则无法区别开(除非不在父窗体的WM_COMMAND 消息中处理)。 UCGUI 中一些基本的消息如下: WM_CREATE-窗体创建消息,每创建一个窗体完后都会向该窗体发送此消息,如 WM_CreateWindowAsChild 创建完窗体均会发一此消息,但在UCGUI 中对于此消息的很少处理,如果用户想在对话框之后做些初始化操作或是创建其它子窗体的动作,可以处理此消息,不过对话框一般有专门的初始化
6、消息 WM_INIT_DIALOG,它是在创建对话框后发送的。 WM_SHOW-显示窗体消息,此消息在 UCGUI 中各控件窗体内均未作处理,如果你通过消息发送函数来发送这类没有在 UCGUI 中各窗体中处理的消息,是没有有什么响应的,不要感到奇怪。要显示窗体一般是通过WM_ShowWindow()函数实现的,这个函数做的也就是改变窗体显示标志WM_SF_ISVIS,并使窗体矩形区域无效WM_InvalidateWindow()以产生重画消息。 WM_SET_ENABLE-设置窗体不能使用消息,UCGUI 中有一种复选框为不可改变的,但是这个功能也不完全,如果你对着 UCGUI 中的按钮使用W
7、M_DisableWindow()来设置其无效,按钮照样还是可以使用,不过要改进这些小毛病还是很容易的,这里只是提醒大家 UCGUI 中很多没有实现的小地方,不要到时候使用时感到很奇怪,感觉到奇怪时最好去看看源码,看看源码中是否实现了此功能,不要郁闷。 WM_PAINT -窗体重画消息,当窗体所在区域全部或是部分区域无效时,系统将会发出该重画消息,将无效区域重画,但 UCGUI 中的处理比较简单,都是将窗体全部区域重画;如果用户自己想在窗体上画上一些信息,一般都在在该消息当中画,UCGUI 中的各种提供的系统控件都必须在其系统的提供的消息回调函数中处理此消息来画出控件。当由外部输入操作引起无效
8、窗体区域产生时,系统都会在消息处理中发送该消息到窗体消息回调函数中,以重画此窗体,在下面讲解消息循环机制时将UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛3会着重讲解到该消息的产生。透明窗体-经常有朋友想知道在 UCGUI 中如何实现透明窗体,透明窗窗体显示在前台时,可以看到部分位于其窗体后的内容,即透过窗体可以看到窗体背后的图象。在 UCGUI 中有关于透明窗体的设置选项,可是没有实现此功能,其实要实现原理如下:第一透明窗体及其所有子窗体都必须透明处理;第二是对于所有有透明属性的窗体,在绘图时必须使用透明填充功能的矩形填充函数,主要是修改窗体的 WM_PAINT 消息中画窗
9、体时的矩形填充函数为透明的矩形填充;第三透明的矩形填充函数的实现,通常情况下的矩形填充是以当前前景色来填充,那么关键就是实现画点函数的透明填充,要使一个透明,可以取当前显存中存点的点的RGB 颜色,然后再与当前要画的颜色按照一个比例进行混合得一个新的RGB 值,再将此值画以屏幕上就可能实现透明填充的效果。 WM_TOUCH-处理类似 MOUSE 的滑动操作方式的输入外设的消息,如触摸屏一般都是将其消息从硬件接收到后转化为该消息形式发送出去,该消息中必须包含消息在屏幕中的发生位置坐标及输入设备状态(按下状态或弹起状态),此消息在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,在后面将详细
10、讲解该消息的处理机制。 WM_KEY-处理类似 KEY 的按键式操作的输入外设的消息,消息中必须包含按键的按下或弹起状态,此消息也是在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,讲解消息 LOOP 时再详细介绍。 WM_SET_FOCUS-讲到刚才上面的两个消息时,就反复提到了当前焦点窗体的概念,所有外部输入设备消息都是发送给当前焦点窗体的,用户可以通过此消息来设定当前的焦点窗体。外部输入操作也会改变当前焦点窗体,如点击某窗体时会在该窗体的 WM_TOUCH 消息处理中设置该窗体本身为当前焦点窗体;当在对话框中按键 TAB 键时,同样也可以将焦点在对话框上各控件间切换,这是在对话框
11、的 WM_KEY 消息中处理实现的了解一下 WM_SetFocusOnNextChild()函数,是根据创建对话框时指定的资源定义数组中的顺序来切换的,并没有 WIN 下面指定的TabIndex 这样一个值来指定次序的值。 WM_NOTIFY_PARENT-这个消息将子窗体的外设输入的消息传送到它的父窗体,因为一般的情况下消息都是在父窗体中统一处理的,如对话框中的按钮点击事件,一般都是在用户自定义的窗体消息处理函数中处理,所以就必须要子窗体将获取的输入外设的消息传送给父窗体,这样才能在父窗体中进行子窗体的点击事件消息的处理,这个消息的机制类似WIN 下面的 WM_COMMAND 消息,处理该消
12、息时通过控件 ID 来区别不同的控件,通过消息中的通知码来区别控件被操作的各种状态,具体这个消息的详细说明请参见后面的分析。 WM_DELETE-要删除窗体时发送的消息,主要清除窗体数据结构所占用UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛4内存,此消息主要由 WM_DeleteWindow()函数发送了,如点击 OK 按钮关闭对话框时,最终会调用此函数来删除窗体,不过 UCGUI 中没有最大化最小化关闭等系统功能按钮。最基础窗体结构注解如下,在该结构中有两个很重要的成员,hNextLin 是记载窗体的下一个窗体,这个成员用于遍历所有已经创建的窗体;hNext 是记载窗体下
13、一个兄弟窗体,这个成员用于遍历每个窗体对应的子窗体;这个结构是最基础,一般的控件在这个结构之上还会有一些扩展的结构,如按钮对应有 BUTTON_Obj 结构。typedef struct WM_OBJ_struct WM_Obj;struct WM_OBJ_struct GUI_RECT Rect; /* 窗体矩形区域 */GUI_RECT InvalidRect; /* 窗体无效矩形区域 */WM_CALLBACK* cb; /* 窗体消息回调函数 */WM_HWIN hNextLin; /* 窗体下一个窗体句柄*/WM_HWIN hParent; /* 父窗体句柄*/WM_HWIN hFi
14、rstChild; /* 第一子窗体句柄*/WM_HWIN hNext; /* 下一个兄弟窗体句柄 */U16 Status; /* 窗体当前状态 */; WIDGET_HandleActive()基础控件共通消息处理,在大部分的 UCGUI 控件中都会在消息回调函数的头部进行这个调用,如果处理了消息后,就直接退出消息回调函数的调用。这个函数中处理如下消息: WM_GET_ID返窗体控件标志 ID. WM_SET_FOCUS设置当前窗体为焦点窗体,设置完后还必须向该窗体的父窗体发送一个 WM_NOTIFY_CHILD_HAS_FOCUS 消息让其父窗体更新它记载的当前焦点子窗体. WM_GET
15、_HAS_FOCUS获取当前窗体是否为焦点窗体. WM_SET_ENABLE设置窗体为不可用窗体 . WM_GET_ACCEPT_FOCUS获取当前窗体是否可设置为焦点窗体. WM_GET_INSIDE_RECT返回窗体内框矩形,如按钮有 3D 效果时会有效果边框宽度,内框矩形就是窗体矩形被边框剪裁后的矩形. WM_DefaultProc()-窗体默认消息处理函数,UCGUI 中提供一些基础的控件,这些控件有些共通的消息均在此处理,如下: WM_GETCLIENTRECT获取窗体矩形区域,相对于矩形自身 WM_GETORG获取窗体矩形左上角坐标. WM_GET_INSIDE_RECT获取窗体矩
16、形区域,相对屏幕. WM_GET_CLIENT_WINDOW获取窗体客户区子窗体句柄,如对话框的中的子窗体 FrameWin 即为此种窗体. WM_KEY铵键消息处理,通知父窗体子窗体的按键消息,有些控件自己要处理这个消息,如 Edit 控件处理完此消息后就没有再UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛5调用 WM_DefaultProc(),从而没有将 WM_KEY 消息通父窗体;如 Button 控件,根本没有对此消息进行处理,直接是通过默认处理发给了父窗体处理;有些控件如 Checkbox 自己处理该消息,同时也调用默认消息处量将此消息通知父窗本,此种消息源窗体为
17、子控件,目标窗体为父窗体。如此处理 WM_KEY 消息完全是UCGUI 中如此做,在 WIN 中并没有这样做. WM_GET_BKCOLOR获取窗体背景色,在此未实现,返回0xfffffff 值,但 FrameWin 窗体实现了此消息处理.在 UCGUI 的对话框的窗口消息处理函数中 OK 按钮的点击事件, UCGUI 的处理方法与 WIN 下面是不同, 它在 WM_NOTIFY_PARENT 消息中处理片段如下:case WM_NOTIFY_PARENT:Id = WM_GetId(pMsg-hWinSrc); /* Id of widget */NCode = pMsg-Data.v; /
18、* Notification code */switch (NCode) case WM_NOTIFICATION_RELEASED: /* React only if released */if (Id = GUI_ID_OK) /* OK Button */GUI_MessageBox(“This text is shownnin a message box“,“Caption/Title“, GUI_MESSAGEBOX_CF_MOVEABLE);if (Id = GUI_ID_CANCEL) /* Cancel Button */GUI_EndDialog(hWin, 1);break
19、;break;UCGUI 中的消息种类不多, 只有差不多不到二十种,但对于嵌入式系统来说已经完全足够了,用户可以自定义消息(从 WM_USER 起)。 WM_NOTIFY_PARENT 这个消息是由子窗体传送给父窗体的, 由消息的名字也可以看出这一点,OK 按钮也是一个窗体,当 MOUSE 点击在它上面时,UCGUI 首先会传递一个 WM_TOUCH 消息到 OK 按钮的窗口消息处理函数,OK 按钮是一个系统提供的控件,系统已经提供了一个默认的消息的窗口消息处理函数,这个函数会处理大部分的默认窗口消息并随后将此消息转发给父窗体,即 WM_NOTIFY_PARENT 消息,它是由函数WM_Not
20、ifyParent(hObj, Notification)实现的.WM_TOUCH 消息在按钮的消息处理函数_BUTTON_Callback 中的_OnTouch 函数中处理,在处理过程完后会调用 WM_NotifyParent 向按钮的父窗体发WM_NOTIFY_PARENT 消息告诉对话框回调函数按钮被点击了,这个过程再说详细一点是这样的: 点击 OK 按钮. 产生按钮 WM_TOUCH 消息. UCGUI 中的消息 LOOP 调用按钮默认的按钮窗口消息处理函数UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛6_BUTTON_Callback. _OnTouch 默认处理按
21、钮点击并发送给父窗体 WM_NOTIFY_PARENT 消息,这里要注意 MOUSE 点击后,有三种情况:第一种是点击后在按钮范围内弹出 MOUSE,这种情况下,会送的消息中还有一个通知码就是WM_NOTIFICATION_RELEASED;第二种情况是点击拖到按钮范围外弹起MOUSE,此时通知码是 WM_NOTIFICATION_MOVED_OUT;第三种情况是点击后一直未弹起 MOUSE 的过程中消息通知码为WM_NOTIFICATION_CLICKED;在这个函数中还会处理设置按钮点击后MOUSE 至未弹起前的按下状态,这样在按钮下一次画出时就会以按下的状态显示出来. 默认的对话框窗体消
22、息处理函数_FRAMEWIN_Callback 收到WM_NOTIFY_PARENT 消息并最终传送该消息到用户自己定义的对话框消息处理函数,这里要注意的一点是,其实对话框主要是由一个FrameWin 子窗体构成的,这个子窗体大小为对话框指定的大小,对话框上的其它控件是都是 FrameWin 的子窗体,由_FRAMEWIN_Callback 传送的消息首先是传送到对话框的默认窗体消息回调函数_cbDialog,然后再经它传送到用户自定义的窗体回调函数当中。 用户在自己的对话框消息处理函数中处理 WM_NOTIFY_PARENT 消息,即按钮的点击消息,该消息参数中含有按钮的 ID 及操作状态,
23、如果通知码是 WM_NOTIFICATION_RELEASED,此时证明一次点击事件完成。void WM_NotifyParent(WM_HWIN hWin, int Notification)WM_MESSAGE Msg;Msg.MsgId = WM_NOTIFY_PARENT;Msg.Data.v = Notification;WM_SendToParent(hWin, 这个函数相当简单, 其主要还是 WM_SendToParent 这个函数的调用, 这个函数再调用 void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg), 这个函数是最基本的
24、一个消息发送处理函数, 它的第一个参数指定了接受这个要处理的消息的句柄, 第二个指定了是什么消息。这个函数的主要作用是调用相应窗口的消息处理函数来处理消息,如果你有消息要发送给指定的窗体处理,那么也可以使用这个函数。在上面, 我们刚刚分析了在对话框内部消息处理的流转,其中分析了我们在自己指定的对话框消息处理函数当中是如何可以获得按钮的点击消息并进行处理的,现在我们就再来分析一下对话框外面的消息接收:首先是来了解一下GUI_ExecDialogBox 函数,这个函数有几个参数: 第一个是对话框的资源定义数组,这个数组定义了对话框的组成子窗体,其中数组第一个成员必须是 FrameWin 窗体,数组
25、每一个成员记载了创建子窗体所用函数/子窗体 Caption/子窗体标志 ID/子窗体的位置及宽UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛7高/创建窗体时样式标志/额外传送的参数. 第二个参数是上述的数组的大小. 第三个参数是用户指定的对话框窗体消息回调函数指针. 第四个参数是对话框的父窗体,默认为 0. 第五、六参数指定对话框的左上角屏幕位置.GUI_ExecDialogBox 主要完成如下几件事: 根据传进来的对话框资源定义数组创建对话框及对话框中的子窗体. 根据传进来的窗口消息处理函数,记载到一全局变量保存,当这个全局变量中记载的函数指针为非空时,执行消息 LOOP,
26、消息 LOOP 中会将当前的 MOUSE 及 KEY 消息发送给当前焦点窗体. 当对话框关闭时,记载对话窗体消息回调函数的全局变量会被清为 0,此时消息 LOOP 就会退出,对话框结束.二、发现存在的问题-点击 OK 后无论先关闭消息框还是对话框,另一个不再响应.点击对话框的 OK 后弹出消息框, 会出现当按下对话框的 Cancel 关闭对话框后, 弹出的消息框就没有任何响应的情况. 或者是关闭掉弹出的消息框, 对话框就没有任何响应的情形:从外部粗步分析的原因是调用 MainTask 的线程已经退出了, 这个线程是在模拟器中开启的专门用于运行 GUI 任务的线程,它的线程函数是 Thread,
27、 Thread 函数里调用 main,main 中再调用 MainTask,所以该线程退出后也就代表 UCGUI 任务已经结束了。这是从模拟器的角度来分析, 现在我们分析一下为什么 MainTask 的调用线程会这么早退出呢?由我们第一节中关于 GUI_ExecDialogBox 所做的几件中可以分析到, 当UCGUI 中有一个独立的窗体退出后_cb 会被清为 0, 此时退出 GUI 窗口 LOOP. 即结束了 UCGUI 窗口消息处理。其实, GUI_MessageBox 弹出的消息框其实也是一种对话框, 这最终调用的还是 GUI_ExecDialogBox,开始我们就分析过,进入这个函数后
28、,会有一个全局变量记录当前对话框窗体的消息处理函数指针,但是目前的问题如下: 已经建立了两个这样的对话框窗体,这样一个全局变量来记载当前对话框的窗体消息处理函数指针显然不够,而且先前打开的对话框的的用户指定的窗体消息回调函数已经不再被调用了,此时第一个对话框的由子窗体回传到父窗体的消息均会传到第二次打开的对话框的用户指定的窗体消息回调函数中. 第二次弹出消息框再次进入 GUI_ExecDialogBox 中的 while 循环后,先前的对话框中的 while 循环就被挂起了,直至第二次的 GUI_ExecDialogBox中的 while 循环退出,无论关闭消息框还是对话框,都会导致知退出第二
29、UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛8次消息 LOOP。第二次消息 LOOP 退出后返回点为弹出消息框后的下一句,直至返回到第一个对话框的 while 循环后退出 GUI_ExecDialogBox.但我们期待的结果是,点击对话框的 OK 弹出消息框, 关闭掉对话框或是消息框,其它的都要对话框继续有反应,下面我们就来分析一下如何达到这个目标,看看要做些什么具体的改动:三、UCGUI 中的消息 LOOP 处理分析-寻找问题的解决办法.在我们发现这个问题, 我们已经粗步分析了,问题不是出在我们编写程序上, 而是 UCGUI 的内部,那么要解决这个问题, 我们就要进一步了
30、解 UCGUI 的窗口体系。其实换一句话说,在嵌入式应用中,窗口的强大直接决定到 GUI 系统的体积大小,并不是所有的情况都要有这种支持,当然我们希望在下一版本可以有多个对话框的直接支持。创建对话框:void MainTask(void)GUI_Init();WM_SetDesktopColor(GUI_RED);WM_SetCreateFlags(WM_CF_MEMDEV);GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate), 上面是我们创建对话框的程序,是我们编写的代码, GUI_ExecDialogBox()这个函数的
31、作用我们已经分析过了,它所做的事用一句话来说就是创建对话框并进入窗体消息 LOOP 处理,下面将详细分析一下 LOOP 消息的处理流程:int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,int x0, int y0)_cb = cb;GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog,hParent, x0, y0);while(_cb)if (!GUI_Exec()GUI_
32、X_ExecIdle();return _r;这个 LOOP 类似我们非常熟悉的 WIN 下面的消息 LOOP, 其原理是一致的. UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛9GUI_CreateDialogBox 负责创建对话框的所有子窗体,特别注意它其中一个参数传入是 Dialog.c 中定义的_cbDialog,这个函数什么也没做,基本上是转而调用_cb,后面我们会提到关于它的修改。_cb 是对话框的用户定义窗口消息处理函数,这里面有一个判断,就是_cb 非空时,才进行消息 LOOP, _cb 在 Dialog.c中的定义为:static WM_CALLBACK*
33、_cb; _cb 是一个全局变量,我们程序中创建对话框与弹出消息框时两次调用了 GUI_ExecDialogBox,后一次的_cb 将会把前面的值冲,它是用户自定义的窗口消息处理函数。在 while 中有判断, 那么可见_cb 是在 GUI_Exec 之中是有使用的,对话框的 FrameWin 子窗体消息流转调如下面的所示,窗口消息处理函数是在WM_SendMessage 中通过函数指针的调用中, 注意内部的就是真正被调用来处理消息的函数:GUI_ExecGUI_Exec1WM_ExecWM_Exec1WM_HandlePIDWM_SendMessage(*pWin-cb)(pMsg)_FRA
34、MEWIN_Callback_OnTouch()(*cb)(pMsg)_cbDialog *_cb)(pMsg)_MESSAGEBOX_cbCallback WM_HandlePID()-专门处理类似 MOUSE 的滑动操作外设消息的函数. WM_SendMessage()-基层的发送消息的函数,即调用相对应的窗体的消息回调函数来处理消息.现在讲到了窗体消息 LOOP,在窗体系统中最根本一点的就是对外部输入消息的处理,窗体就是靠消息驱动的,其处理代码如下:int WM_Exec1(void) if(WM_pfPollPID)/* Poll PID if necessary */WM_pfPol
35、lPID();if(WM_pfHandlePID)if (WM_pfHandlePID()return 1; /* We have done something . */if(GUI_PollKeyMsg()return 1; /* We have done something . */if(WM_IsActive _DrawNext();WM_UNLOCK();return 1; /* We have done something . */return 0; /* There was nothing to do . */UCGUI 技术文集 UCGUI 专业网站:UCGUI 专业论坛10它主
36、要完成如下几件事: Poll PID中Poll个词准确的意思应该是统计/测试的意思,这里是调用用户的统计测试滑动操作外设的一个接口,用户可以通过WM_SetpfPollPID()函数来设置自己用于统计/测试滑动操作外设的具体函数。 处理滑动操作外设 WM_TOUCH消息,真正的处理是在函数WM_HandlePID()中处理的,在后面滑动外设消息处理流程时有详细说明,在新版中更细分此消息为WM_PID_STATE_CHANGED/ WM_MOUSEOVER/ WM_TOUCH三种消息,其实在WIN下面类似消息的处理更为复杂,有移动/滚动/单击DOWN及UP左右键/双击左右键等七八种MOUSE消息
37、,而且这些消息又分为窗体体客户区与标题区的差别,标题区的都会在消息上加上NC的前辍,如WM_NCLBUTTONUP标题区单击弹起消息。从这里我们也可以看到UCGUI中非常简化的处理,简单得不能再简单了,的确是一个微型的GUI图形支持系统。 按键式外设消息处理,GUI_PollKeyMsg()函数在发现有新的按键消息产生时会调用WM_OnKey()将消息发送到当前焦点窗体处理,如果一直处于按键按下状态时则会将前按钮的虚拟码存在一全部变量中,以供GUI_GetKey()调用来返回当前按下键值。UCGUI中有一个外部的键盘接口,外界通过GUI_StoreKeyMsg()发送键盘消息给UCGUI以驱动
38、键盘,在我的模拟器当中就是将LCD模拟显示屏窗口的所有键盘消息通过GUI_StoreKeyMsg()传送到UCGUI中以驱动键盘消息处理,关于键盘消息的处理UCGUI中也是来一个处理一个,没有任何缓冲处理,如果某些按钮消息处理用时过长,就会造成其后的一些按键消息丢失。static int _Key; /记载当前按键,GUI_GetKey时返回此值static int _KeyMsgCnt; /当前键盘消息数量static struct int Key; /键盘虚拟码int PressedCnt; /按键次数 _KeyMsg;上面是键盘消息结构,UCGUI中以一个全局的_KeyMsg键盘变量记载
39、当前最新键盘消息,当前按键值用_Key,每产生按下键时用GUI_StoreKey更新一次此值,UCGUI中没有按键弹起消息的处理。 检测是否有无效窗体,如果有无效窗体,则向该无效窗体发送重画消息,有一个全局变量WM_NumInvalidWindows用于记载当前无效窗体的数目,在函数_DrawNext()中每次重画一个无效窗体,查找无效窗体时是通过遍历查找的方法,先前说过窗体基本结构中有一个成员hNextLin记载下一个窗体,就在是此处用于遍历所有窗体,找出无效的窗体,发送WM_PAINT消息给窗体。注意这里每次画一个窗体的原因就是为了不影响窗体的消息处理,如果在此处用时太多,会严重影响消息处理的反应速度。了解了 UCGUI 中消息处理的具体流程,那么再来分析这个先前提到的问题: