全部博文(2005)
分类: C/C++
2007-09-04 10:08:39
μC/GUI-v3.90a学习笔记9-漂亮的、看着就爽的窗体缩放函数源码--递归经典
void WM_ResizeWindow(WM_HWIN hWin, int dx, int dy) {
GUI_RECT rOld, rNew, rMerge;
WM_Obj* pWin;
if (((dx | dy) == 0) || (hWin == 0)){
//没有真正的大小变化,那么绝不执行下面真正调整大小的代码
return;
}
WM_LOCK();
pWin = WM_HANDLE2PTR(hWin);
rOld = pWin->Rect;//计算之前保存窗体原矩形
rNew = rOld;
if (dx) {//存在x方向上的大小变化
if ((pWin->Status & WM_SF_ANCHOR_RIGHT) && (!(pWin->Status & WM_SF_ANCHOR_LEFT))) {
//WM_SF_ANCHOR_RIGHT属性,表示:x方向右边x1为缩放锚点,那么x方向左边x0将作为变化量的承受着,
//进而保持hWin相对父窗体x方向右边x1缩放锚点处相对位置的不变
//所以最终x0方向相对父窗体有dx量变化[增减],而x1方向相对父窗体位置不变[gliethttp]
rNew.x0 -= dx;
} else {
//WM_SF_ANCHOR_LEFT属性,表示:x方向左边x0为缩放锚点,那么x方向右边x1将作为变化量的承受着,
//进而保持hWin相对父窗体x方向左边x0缩放锚点处相对位置的不变
//所以最终x1方向相对父窗体有dx量变化[增减],而x0方向相对父窗体位置不变[gliethttp]
rNew.x1 += dx;
}
}
if (dy) {//存在y方向上的大小变化
if ((pWin->Status & WM_SF_ANCHOR_BOTTOM) && (!(pWin->Status & WM_SF_ANCHOR_TOP))) {
//WM_SF_ANCHOR_BOTTOM属性,表示:y方向下边y1为缩放锚点,那么y方向上边y0将作为变化量的承受着,
//进而保持hWin相对父窗体y方向下边y1缩放锚点处相对位置的不变
//所以最终y0方向相对父窗体有dy量变化[增减],而y1方向相对父窗体位置不变[gliethttp]
rNew.y0 -= dy;
} else {
//WM_SF_ANCHOR_TOP属性,表示:y方向上边y0为缩放锚点,那么y方向下边y1将作为变化量的承受着,
//进而保持hWin相对父窗体y方向上边y0缩放锚点处相对位置的不变
//所以最终y1方向相对父窗体有dy量变化[增减],而y0方向相对父窗体位置不变[gliethttp]
rNew.y1 += dy;
}
}
GUI_MergeRect(&rMerge, &rOld, &rNew);//计算此次操作之后最大受影响矩形区rMerge
pWin->Rect = rNew;//窗体有了新的大小,传递给他
//失效与rMerge矩形相交的WM__FirstWin单向链表上的每个窗体的相交矩形部分,保证重绘
WM_InvalidateArea(&rMerge);
//因为hWin自己的大小有了变化,所以自己的孩子们也必须跟随着自己的变化来调整他们自己[gliethttp]
//孩子们或者是简单的实现平移自己或者是调整他们自己的大小,如果孩子们自己的大小也变化了
//那么他们自己将主动触发递归调用WM_ResizeWindow(),进而继续调整他们的孩子们,
//而恰恰我感到的代码艺术之美就在这里.
WM__UpdateChildPositions(pWin, rNew.x0 - rOld.x0, rNew.y0 - rOld.y0, rNew.x1 - rOld.x1, rNew.y1 - rOld.y1);
//确保hWin的失效区域不要大于调整后的hWin自身大小
GUI__IntersectRect(&pWin->InvalidRect, &pWin->Rect);
//通知当前hWin[孩子的句柄或者自己的]的回调函数,hWin的大小已经改变了,
//这时hWin下的所有孩子都已经把自己的位置和大小改变完成了
//递归原因,原始的父窗体hWin将等到所有的child孩子都按照预定属性调整完毕之后,
//每个child孩子自己的回调函数都收到WM_SIZE消息之后,原父窗体hWin将最后一个
//收到WM_SIZE消息,他已经收到该消息,说明hWin下所有孩子的调整工作都已经由孩子们
//通过递归调用自己完成,很有面向对象中,"封装"的味道,parent不用关心什么,
//child们会自己把自己打扮的很好[gliethttp]
WM__SendMsgNoData(hWin, WM_SIZE);
WM_UNLOCK();
}
下面看看这个触发递归的"帅小伙"--函数WM__UpdateChildPositions()
void WM__UpdateChildPositions(WM_Obj* pObj, int dx0, int dy0, int dx1, int dy1) {
WM_HWIN hChild;
WM_Obj* pChild;
int dx, dy, dw, dh;
//他们的parent--pObj已经改变了自己的大小,
//(x0,y0)---(dx0,dy0)
//(x1,y1)---(dx1,dy1)
//下面就看parent改变大小之后,孩儿们是打算怎么根据这个改变来调整自己了
for (hChild = pObj->hFirstChild; hChild; hChild = pChild->hNext) {
int Status;
GUI_RECT rOld, rNew;
pChild = WM_H2P(hChild);
rOld = pChild->Rect;//保存child原始矩形
rNew = rOld;
Status = pChild->Status & (WM_SF_ANCHOR_RIGHT | WM_SF_ANCHOR_LEFT);
switch (Status) {
case WM_SF_ANCHOR_RIGHT:
//WM_SF_ANCHOR_RIGHT属性,表示:x方向右边为缩放锚点,那么这个child将死死跟随锚点,
//按dx1大小,平移child在x方向上的坐标[gliethttp]
rNew.x0 += dx1;
rNew.x1 += dx1;
break;
case WM_SF_ANCHOR_RIGHT | WM_SF_ANCHOR_LEFT:
//各跟各的走,那就x0跟着dx0走,x1跟着dx1走,进而窗体的dw就改变了dx1-dx0
rNew.x0 += dx0;
rNew.x1 += dx1;
break;
default:
//WM_SF_ANCHOR_LEFT属性,表示:x方向左边为缩放锚点,那么这个child将死死跟随锚点,
//按dx0大小,平移child在x方向上的坐标[gliethttp]
rNew.x0 += dx0;
rNew.x1 += dx0;
}
Status = pChild->Status & (WM_SF_ANCHOR_TOP | WM_SF_ANCHOR_BOTTOM);
switch (Status) {
case WM_SF_ANCHOR_BOTTOM:
//WM_SF_ANCHOR_BOTTOM属性,表示:y方向下边为缩放锚点,那么这个child将死死跟随锚点,
//按dy1大小,平移child在y方向上的坐标[gliethttp]
rNew.y0 += dy1;
rNew.y1 += dy1;
break;
case WM_SF_ANCHOR_BOTTOM | WM_SF_ANCHOR_TOP:
//各跟各的走,那就y0跟着dy0走,y1跟着dy1走,进而窗体的dh就改变了dy1-dy0
rNew.y0 += dy0;
rNew.y1 += dy1;
break;
default:
//WM_SF_ANCHOR_TOP属性,表示:y方向上边为缩放锚点,那么这个child将死死跟随锚点,
//按dy0大小,平移child在y方向上的坐标[gliethttp]
rNew.y0 += dy0;
rNew.y1 += dy0;
}
//经过以上计算调整之后,(x0,y0)坐标是否有变化,
dx = rNew.x0 - rOld.x0;
dy = rNew.y0 - rOld.y0;
if (dx || dy) {
//如果这个child的左上角坐标(x0,y0)有变化,
//那么平移该child下自己的的所有child和他自己
WM_MoveWindow(hChild, dx, dy);
}
dw = (rNew.x1 - rNew.x0) - (rOld.x1 - rOld.x0);
dh = (rNew.y1 - rNew.y0) - (rOld.y1 - rOld.y0);
if (dw || dh) {
//如果因为该child的属性为WM_SF_ANCHOR_BOTTOM | WM_SF_ANCHOR_TOP或者
//WM_SF_ANCHOR_RIGHT | WM_SF_ANCHOR_LEFT
//那么该child的大小已经变化了,所以需要改变该child下,他的child们,
//当这些child们自己的parent发生大小变化时,这些child们会在
//WM_ResizeWindow()中根据自己的属性,来自己调整自己.
WM_ResizeWindow(hChild, dw, dh);
}
}
}
以WIDGET_Multiedit为例,来看一看执行流:
1.创建_hFrame窗体函数FRAMEWIN_CreateEx()在创建_hFrame的同时创建了一个_hClient
作为_hFrame的孩子.
2.WM_SetCallback(_hFrame,_cbFrameWin);设置用户定义的"控件窗体消息回调处理函数"
在_cbFrameWin回调函数中,主要是截获WM_TOUCH消息,如果发现鼠标在size改变_hFrame的大小,
那么适当处理之后将调用WM_ResizeWindow()来响应WM_TOUCH消息的size动作,调整_hFrame自己的大小
3.FRAMEWIN_AddCloseButton(_hFrame, FRAMEWIN_BUTTON_LEFT, 0);
FRAMEWIN_AddMaxButton(_hFrame, FRAMEWIN_BUTTON_RIGHT, 0);
FRAMEWIN_AddMinButton(_hFrame, FRAMEWIN_BUTTON_RIGHT, 1);
给_hFrame再增加3个孩子,分别是标题栏上的Close按钮,Max按钮和Min按钮.
4._CreateLButton("None", 0, 36, 16, _hClient, ID_NONEWRAP);
_CreateLButton("Word", 37, 36, 16, _hClient, ID_WORDWRAP);
_CreateLButton("Char", 74, 36, 16, _hClient, ID_CHARWRAP);
_CreateRButton("PSW", 52, 25, 16, _hClient, ID_PASSWORD);
_CreateRButton("OVR", 26, 25, 16, _hClient, ID_OVERWRITE);
_CreateRButton("R/O", 0, 25, 16, _hClient, ID_READONLY);
MULTIEDIT_CreateEx(0, 0, 0, Rect.y1 - 16 + 1, _hClient, WinFlags,...);
给_hClient添加7个孩子.
亲子图如下:
WM_ResizeWindow(_hFrame,dx,dy)更新完_hFrame的大小之后,调用WM__UpdateChildPositions()
更新_hFrame的孩儿们:_hClient,CloseButton,MaxButton,MinButton,
因为CloseButton,MaxButton,MinButton,这三个孩子属性分别为
WM_CF_ANCHOR_LEFT,WM_CF_ANCHOR_RIGHT和WM_CF_ANCHOR_RIGHT,所以
他们将仅仅由WM_MoveWindow()函数完成自己的平移操作,
_hClient的属性为WM_CF_ANCHOR_RIGHT | WM_CF_ANCHOR_LEFT | WM_CF_ANCHOR_TOP | WM_CF_ANCHOR_BOTTOM;
所以_hClient将永远填充满_hFrame的客户区空间,因此大小会改变,故而
_hClient除了使用WM_MoveWindow()函数把自己和它的7个孩子进行平移之外,改变了大小的_hClient
会影响到他的7个孩子的位置状况,所以递归调用WM_ResizeWindow(_hClient,dw,dh);
先改变_hClient自己的大小,之后调用WM__UpdateChildPositions()更新7个对象的位置,
因为7个对象的属性都不是size自己的属性,所以都仅仅是平移而已,
所以这7个child将分7次调用WM_MoveWindow()函数来平移自己.[gliethttp 2007-09-04]