一、相关结构体和变量

窗口管理结构体

/* 窗口管理结构体 共30个字节 */
struct WM_Obj {
GUI_RECT Rect; //窗口尺寸(x0,y0,x1,y1) 8个字节
GUI_RECT InvalidRect; //无效区域(x0,y0,x1,y1) 8个字节
WM_CALLBACK* cb; //回调函数 4个字节 WM_HWIN hNextLin; //指向链表中的下一个窗口 2个字节 WM_HWIN hParent; //当前窗口的父窗口 2个字节
WM_HWIN hFirstChild; //当前窗口的第一个子窗口 2个字节
WM_HWIN hNext; //下一个兄弟窗口 2个字节 U16 Status; //标志位 2个字节
};

窗口创建的标志

#define WM_CF_HASTRANS         (1<<0)  /* Has transparency. Needs to be defined for windows which do not fill the entire
section of their (client) rectangle. */
#define WM_CF_HIDE (0<<1) /* Hide window after creation (default !) */
#define WM_CF_SHOW (1<<1) /* Show window after creation */
#define WM_CF_MEMDEV (1<<2) /* Use memory device for redraws */
#define WM_CF_STAYONTOP (1<<3) /* Stay on top */
#define WM_CF_DISABLED (1<<4) /* Disabled: Does not receive PID (mouse & touch) input */
/* Create only flags ... Not available as status flags */
#define WM_CF_ACTIVATE (1<<5) /* If automatic activation upon creation of window is desired */
#define WM_CF_FGND (0<<6) /* Put window in foreground after creation (default !) */
#define WM_CF_BGND (1<<6) /* Put window in background after creation */ /* Anchor flags */
#define WM_CF_ANCHOR_RIGHT (1<<7) /* Right anchor ... If parent is resized, distance to right will remain const (left is default) */
#define WM_CF_ANCHOR_BOTTOM (1<<8) /* Bottom anchor ... If parent is resized, distance to bottom will remain const (top is default) */
#define WM_CF_ANCHOR_LEFT (1<<9) /* Left anchor ... If parent is resized, distance to left will remain const (left is default) */
#define WM_CF_ANCHOR_TOP (1<<10) /* Top anchor ... If parent is resized, distance to top will remain const (top is default) */ #define WM_CF_CONST_OUTLINE (1<<11) /* Constant outline. This is relevant for transparent windows only. If a window is transparent
and does not have a constant outline, its background is invalided instead of the window itself.
This causes add. computation time when redrawing. */
#define WM_CF_LATE_CLIP (1<<12)
#define WM_CF_MEMDEV_ON_REDRAW (1<<13)
#define WM_CF_RESERVED3 (1<<14)
#define WM_CF_RESERVED4 (1<<15)

WM_CF_SHOW、WM_CF_STAYONTOP、WM_CF_HIDE、WM_CF_ACTIVATE这几个标志是经常用到的。

二、窗口创建的过程分析

1、WM_CreateWindowAsChild

WM_HWIN WM_CreateWindowAsChild( int x0, int y0, int width, int height
,WM_HWIN hParent, U16 Style, WM_CALLBACK* cb
,int NumExtraBytes) {
WM_Obj* pWin;
WM_HWIN hWin;
WM_ASSERT_NOT_IN_PAINT(); //断言,这里没有使用
WM_LOCK();
Style |= WM__CreateFlags; //给窗口的标志增加一个创建标志
/* Default parent is Desktop 0 */
if (!hParent) { //如果不存在父窗口,比如说桌面窗口
if (WM__NumWindows) { //创建桌面窗口,这个不会执行的
#if GUI_NUM_LAYERS == 1
hParent = WM__ahDesktopWin[]; //如果用户没有指定当前创建窗口的父窗口,而且该窗口
//又不是桌面窗口,默认的将桌面窗口作为其父窗口
#else
hParent = WM__ahDesktopWin[GUI_Context.SelLayer];
#endif
}
}
if (hParent == WM_UNATTACHED) {
hParent = WM_HWIN_NULL;
}
if (hParent) {
WM_Obj* pParent = WM_H2P(hParent);
x0 += pParent->Rect.x0;
y0 += pParent->Rect.y0;
if (width==) {
width = pParent->Rect.x1 - pParent->Rect.x0+;
}
if (height==) {
height = pParent->Rect.y1 - pParent->Rect.y0+;
}
}
if ((hWin = (WM_HWIN) GUI_ALLOC_AllocZero(NumExtraBytes + sizeof(WM_Obj))) == ) {
GUI_DEBUG_ERROROUT("WM_CreateWindow: No memory to create window");
//如果没有空间来创建需要的动态内存块
} else { //申请动态内存成功
WM__NumWindows++; //保存系统总窗口数目的计数器加1
pWin = WM_H2P(hWin); //计算获取动态内存数据区的地址
/* 向动态内存区写入当前窗口的参数 */
pWin->Rect.x0 = x0;
pWin->Rect.y0 = y0;
pWin->Rect.x1 = x0 + width - ;
pWin->Rect.y1 = y0 + height - ;
pWin->cb = cb; //保存回调函数
/* Copy the flags which can simply be accepted */
pWin->Status |= (Style & (WM_CF_SHOW |
WM_SF_MEMDEV |
WM_CF_MEMDEV_ON_REDRAW |
WM_SF_STAYONTOP |
WM_CF_DISABLED |
WM_SF_CONST_OUTLINE |
WM_SF_HASTRANS |
WM_CF_ANCHOR_RIGHT |
WM_CF_ANCHOR_BOTTOM |
WM_CF_ANCHOR_LEFT |
WM_CF_ANCHOR_TOP |
WM_CF_LATE_CLIP));
/* Add to linked lists */
_AddToLinList(hWin); //将窗口插入到窗口管理链表当中
WM__InsertWindowIntoList(hWin, hParent); //插入到父窗口管理链表当中 /* 根据用户定义的窗口风格进行一些列的操作 */
/* Activate window if WM_CF_ACTIVATE is specified */
if (Style & WM_CF_ACTIVATE) { //如果带激活标志的话,就激活窗口
WM_SelectWindow(hWin); /* This is not needed if callbacks are being used, but it does not cost a lot and makes life easier ... */
}
/* Handle the Style flags, one at a time */
#if WM_SUPPORT_TRANSPARENCY
if (Style & WM_SF_HASTRANS) { //透明窗口
WM__TransWindowCnt++; /* Increment counter for transparency windows */
}
#endif
if (Style & WM_CF_BGND) {
WM_BringToBottom(hWin);
} if (Style & WM_CF_SHOW) { //显示窗口
pWin->Status |= WM_SF_ISVIS; //设置可视状态位
WM_InvalidateWindow(hWin); //如果有显示命令,还会设置窗口为无效,等待重绘
}
WM__SendMsgNoData(hWin, WM_CREATE); //发一个创建消息,这样创建的时候就可以在回调函数中进行处理
}
WM_UNLOCK();
return hWin;
}

   首先根据其父窗口的坐标计算出当前窗口的坐标、高度和宽度。从动态内存区中开辟出一块窗口管理区域,然后向其中填入当前窗口的参数值。比较重要的是接下来的两部,将当前窗口插入到窗口管理链表当中以及将窗口插入到其父窗口的同胞链表当中。最后,如果创建的时候以显示模式WM_CF_SHOW创建,那么要为此窗口加入了可视标志WM_SF_ISVIS,而且还要设置窗口为无效。这样在执行GUI_Exec()或者WM_Exec()的时候就会对该窗口进行重绘。

2、_AddToLinList()

static void _AddToLinList(WM_HWIN hNew) {
WM_Obj* pFirst;
WM_Obj* pNew;
if (WM__FirstWin) { //如果不是桌面窗口(事实上桌面窗口肯定存在了)
pFirst = WM_H2P(WM__FirstWin); //首先获取桌面窗口的动态内存地址
pNew = WM_H2P(hNew); //获取要插入窗口的动态内存地址 /*
* 桌面窗口--->最近创建的窗口1--->更早创建的窗口2~~~~~~~--->0 ==>
* 桌面窗口--->当前要插入的窗口--->最近创建的窗口1--->更早创建的窗口2~~~--->0
*/
pNew->hNextLin = pFirst->hNextLin; //
pFirst->hNextLin = hNew; } else {
WM__FirstWin = hNew; //创建桌面窗口时,将桌面窗口的句柄赋给此变量
}
}

将新建窗口添加到窗口管理链表中。这个窗口管理链表是建立在uCGUI的动态内存中,利用WM_obj类型中的成员hNextLin连接成一个单向链表。链表的构建过程如下:

插入之前:桌面窗口--->最近创建的窗口1--->更早创建的窗口2~~~~~~~--->0  ==>  
插入之后:桌面窗口--->当前要插入的窗口--->最近创建的窗口1--->更早创建的窗口2~~~--->0

3、WM__InsertWindowIntoList()

/*********************************************************************
*
* WM__InsertWindowIntoList
*
* Routine describtion
* This routine inserts the window in the list of child windows for
* a particular parent window.
* The window is placed on top of all siblings with the same level.
*/
void WM__InsertWindowIntoList(WM_HWIN hWin, WM_HWIN hParent) {
int OnTop;
WM_HWIN hi;
WM_Obj * pWin;
WM_Obj * pParent;
WM_Obj * pi; if (hParent) { //桌面窗口是不存在父窗口的
pWin = WM_H2P(hWin); //获取当前窗口的动态内存地址
pWin->hNext = ; //它的下一个兄弟窗口为0
pWin->hParent = hParent; //记录它的父窗口
pParent = WM_H2P(hParent); //获得它父窗口的动态内存地址
OnTop = pWin->Status & WM_CF_STAYONTOP; //可以用来判断此窗口是否有在最顶层的标志
hi = pParent->hFirstChild; //父窗口的第一个子窗口
/* Put it at beginning of the list if there is no child */
if (hi == ) { /* No child yet ... Makes things easy ! */
/*
* 父窗口--->0 ====>
* 父窗口--->当前窗口--->0
*/
pParent->hFirstChild = hWin; //没有子窗口,就把它作为父窗口的第一个子窗口
return; /* Early out ... We are done */
}
/* Put it at beginning of the list if first child is a TOP window and new one is not */
pi = WM_H2P(hi); //获取父窗口第一个子窗口的动态内存地址
if (!OnTop) { //如果此窗口没有在最顶层的标志
if (pi->Status & WM_SF_STAYONTOP) { //判断长兄是否有在最顶层的标志
/*
* 父窗口--->长兄--->~~~--->0 =>
* 父窗口--->当前窗口--->长兄--->~~~--->0
*/
pWin->hNext = hi; //当前窗口的下一个窗口指向其长兄
pParent->hFirstChild = hWin; //父窗口的第一个子窗口为当前窗口
return; /* Early out ... We are done */
}
}
/* 把它放在链表的最顶端或者在第一个“最顶层”窗口之前 */
do {
WM_Obj* pNext;
WM_HWIN hNext;
if ((hNext = pi->hNext) == ) { /* End of sibling list ? */
pi->hNext = hWin; /* 放在链表的最顶端 */
break;
}
pNext = WM_H2P(hNext);
if (!OnTop) { //如果当前窗口没有“在最顶层”的标志
if (pNext->Status & WM_SF_STAYONTOP) {
pi->hNext = hWin;
pWin->hNext = hNext;
break;
}
}
pi = pNext;
} while ();
#if WM_SUPPORT_NOTIFY_VIS_CHANGED
WM__NotifyVisChanged(hWin, &pWin->Rect);
#endif
}
}

 将新建窗口添加到父窗口的同胞窗口管理链表中。这个窗口管理链表是建立在uCGUI的动态内存中,利用WM_obj类型中的成员hFirstChild和hNext连接成一个单向链表。

插入的原则是:

1、如果父窗口没有孩子,直接将其作为父窗口的孩子即可。

插入之前: 父窗口--->0  ====>
插入之后: 父窗口--->当前窗口--->0

2、如果父窗口有孩子,第一个孩子有“在顶层”的标志,而当前创建的窗口没有这个标志,则将其作为其父窗口的第一个孩子。

插入之前:父窗口--->长兄--->~~~--->0 =>
插入之后:父窗口--->当前窗口--->长兄--->~~~--->0

3、如果父窗口有孩子,第一个孩子没有“在顶层”的标志,而当前创建的窗口也没有这个标志,则将其放在同胞链表中有“在顶层”标志窗口的前边,也就是所有没有“在顶层”标志窗口的后边。

插入之前:

  父窗口--->长兄(没标志)--->次兄(没标志)--->三兄(有标志)--->~~~--->0 =>
插入之后:

  父窗口--->长兄(没标志)--->次兄(没标志)--->当前窗口--->三兄(有标志)--->~~~--->0

4、如果父窗口有孩子,第一个孩子没有“在顶层”的标志,而当前创建的窗口有这个标志,则将其放在同胞链表的最后边。

插入之前:

  父窗口--->长兄(没标志)--->次兄(没标志)--->三兄(有标志)--->~~~--->0 =>
插入之后:

  父窗口--->长兄(没标志)--->次兄(没标志)--->三兄(有标志)--->~~~--->当前窗口--->0

三、窗口顺序排列分析

1、按照窗口分层的概念来说,链表的头部窗口是显示在最底层,链表的尾部窗口是显示在最顶层。在进行窗口重绘的时候,正是从链表的头部开始依次往尾部进行重绘。

2、子窗口相对于父窗口来说,子窗口在父窗口的顶部。

 四、桌面窗口的创建

  桌面窗口的创建发生在GUI_Init函数执行过程中。如果系统开启了窗口管理功能,GUI_Init函数会调用WM_Init函数,在WM_Init初始化窗口管理器的时候,默认的会创建桌面窗口。相关代码如下:

/*********************************************************************
*
* WM_Init
*/
void WM_Init(void) {
......
WM__ahDesktopWin[] = WM_CreateWindow(, , GUI_XMAX, GUI_YMAX, WM_CF_SHOW, cbBackWin, );
......
WM_InvalidateWindow(WM__ahDesktopWin[i]);
......
WM_SelectWindow(WM__ahDesktopWin[]);
}

桌面窗口默认的回调函数

static void cbBackWin( WM_MESSAGE* pMsg) {
const WM_KEY_INFO* pKeyInfo;
switch (pMsg->MsgId) {
case WM_KEY:
pKeyInfo = (const WM_KEY_INFO*)pMsg->Data.p;
if (pKeyInfo->PressedCnt == ) {
GUI_StoreKey(pKeyInfo->Key);
}
break;
case WM_PAINT:
{
int LayerIndex;
#if GUI_NUM_LAYERS > 1
LayerIndex = _DesktopHandle2Index(pMsg->hWin);
#else
LayerIndex = ;
#endif
if (WM__aBkColor[LayerIndex] != GUI_INVALID_COLOR) {
GUI_SetBkColor(WM__aBkColor[LayerIndex]);
GUI_Clear();
}
}
default:
WM_DefaultProc(pMsg);
}
}

Attention:

桌面窗口的句柄默认是1,也就是说桌面窗口使用的是动态内存节点信息结构体数组的第二个元素。动态内存分配的时候,从动态内存节点信息结构体数组的第二个元素开始分配的。

uCGUI窗口的创建过程分析的更多相关文章

  1. Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

    从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口.每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图.应用程序窗口视图是真正用来 ...

  2. Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析(转)

    在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程.由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一 ...

  3. Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8303098 在前文中,我们分析了应用程序窗口连 ...

  4. Android应用程序窗口(Activity)的窗口对象(Window) 的创建过程分析

    每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口. 每一个Activity组件都有一个关联的ContextImp ...

  5. Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8201936 在前文中,我们简要介绍了Andro ...

  6. uCGUI窗口初始化过程

    一.相关结构体和变量 重要的uCGUI系统全局变量 NextDrawWin                      下一个需要重绘的窗口句柄 WM__NumWindows       系统当前的总共 ...

  7. uCGUI窗口操作要点

    uCGUI窗口操作要点 1. 创建一个窗口的时候,会给此窗口发送“创建(WM_CREATE)”消息,从而执行它的回调函数:如果创建窗口的标志带有“可视标志(WM_CF_SHOW)”,那么在后续执行GU ...

  8. Android应用程序资源管理器(Asset Manager)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8791064 在前面一篇文章中,我们分析了And ...

  9. Chromium网页Frame Tree创建过程分析

         Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree.Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容.Frame ...

随机推荐

  1. 基于ArcGIS的栅格图像平滑处理(转)

    基于ArcGIS的栅格图像平滑处理 栅格数据获取的途径多种多样,造成了栅格数据质量的很大差异,一些质量较差的栅格数据存在大量“噪音”象元,即在表达同类型的地理要素时,出现个别像元值与周边像元不一致的情 ...

  2. ios代理设计模式

    代理设计模式的作用:     1.A对象监听B对象的一些行为,A成为B的代理     2.B对象想告诉A对象一些事情,A成为B的代理   代理设计模式的总结:     如果你想监听别人的一些行为,那么 ...

  3. android开发之路02(浅谈BroadcastReceiver)

    一.BroadcastReceiver (广播接收者)的作用是用来接收来自系统和应用中的广播.应用如下: 1.开机完成后系统会产生一条广播----->接收到这条广播就能实现开机启动服务的功能: ...

  4. java基础学习总结五(递归算法、冒泡排序、查看生成API)

    一:递归算法 概念:自己调用自己的方法 示例代码如下: @Test /** * 递归求和 * 5+4+3+2+1=15 */ public void getSum() { long sum = sum ...

  5. git 配置用户名和邮箱

    在安装了git for windows之后,个人总是忘记配置git config的命令,以此记录一下: 配置用户名和邮箱的命令 git config --global user.name " ...

  6. 使用VERT.X构建分布式企业级应用

    谈到企业应用,就得谈分布式.低耦合.模块化.面向服务.可扩展性等等.早些时候的技术有CORBA和EJB,后面兴起的有WebService和MDB.但是这些技术不是学习.开发门槛高就是不那么轻量化.我现 ...

  7. 关于error: cannot connect to daemon的解决办法

    执行adb devices时,如果出现以下错误: * daemon not running. starting it now on port 5037 * ADB server didn't ACK ...

  8. Java_字符类(Character、String、StringBuffer)_char是基本数据类型,Character是其包装类型。

         在java中有三个类负责对字符的操作:Character.String.StringBuffer.其中,Character类是对单个字符进行操作,String是对一个字符序列的操作,Stri ...

  9. 第十二篇、高度自适应的textView

    高度根据输入内容变化输入框,我们在很多的应用上都可以见到,如微信.QQ聊天,QQ空间评论等等,该输入框可以用xib,纯代码编写,但是个人觉得纯代码编写用起来更加方便一些. 1.使用自定义的UIView ...

  10. C# 并行开发总结

    本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...