某天在論壇上看到有人發(fā)帖詢問(wèn)QQ自動(dòng)伸縮窗口是怎么實(shí)現(xiàn)的,我也好想知道,于是到百度一搜索,結(jié)果不多,來(lái)來(lái)去去都是那幾篇,下載那些demo運(yùn)行一下,發(fā)覺(jué)效果與QQ相差很大,于是決定自己動(dòng)手做個(gè),
要求要近乎完美地模仿這個(gè)功能。由于是些效果的東西,貼圖也看不出來(lái),所以文章里就不截圖了,想看效果的就直接運(yùn)行源代碼的demo吧。
一、觀察
模仿前最重要的一步就是觀察,經(jīng)過(guò)半天對(duì)QQ的擺弄和摸索,總結(jié)出了以下一些特點(diǎn):
1、窗口開始粘附時(shí),檢測(cè)的是鼠標(biāo)坐標(biāo)與桌面邊界的距離,特別地,粘附在下面的時(shí)候,檢測(cè)的是與任務(wù)欄的距離;
2、在向上移動(dòng)窗口時(shí),窗口邊界永遠(yuǎn)不會(huì)超出桌面上面邊界;
3、窗口是個(gè) TopMost 風(fēng)格;
4、當(dāng)窗口粘附在上面、左邊或右邊并顯示時(shí),你把鼠標(biāo)移動(dòng)到最頂端,光標(biāo)變成改變窗口大小的圖標(biāo),而單單是把窗口的top坐標(biāo)設(shè)置為0是不行的;
5、粘附在下面的時(shí)候,當(dāng)處于移動(dòng)狀態(tài),那么窗口的底邊是與任務(wù)欄頂邊對(duì)齊的,但從隱藏到顯示的時(shí)候,窗口的底端是與屏幕底邊對(duì)齊的;
6、隱藏后顯露出來(lái)的那條線可能是一個(gè)Border,但肯定的是絕不包含Client區(qū)域;
7、關(guān)于響應(yīng)鼠標(biāo)的進(jìn)入與移出窗口,絕對(duì)不是WM_MOUSEMOVE、WM_MOUSELEAVE。證明:你以及其慢的速度接觸隱藏狀態(tài)的QQ邊界,你會(huì)發(fā)現(xiàn)幾乎是“一觸即發(fā)”,你又以及其慢的速度移出顯示狀態(tài)的QQ,你會(huì)發(fā)現(xiàn)它的收縮反而不是“一觸即發(fā)”的,而是離邊緣10象素左右。而WM_MOUSEMOVE,WM_MOUSELEAVE,只有在進(jìn)入、移出Client區(qū)域才響應(yīng),明顯和QQ不同,其實(shí)從第6點(diǎn)也可以知道;
8、粘附在兩邊的時(shí)候,高度會(huì)調(diào)整為桌面上邊界到任務(wù)欄下邊界的距離;
9、在“拖動(dòng)時(shí)顯示窗口內(nèi)容”模式下(桌面屬性-外觀-效果),粘附在兩邊的拖動(dòng)出來(lái)時(shí);如果收縮之前高度比收縮后小則回復(fù)原來(lái)高度,在非“拖動(dòng)時(shí)顯示窗口內(nèi)容”模式下,光柵會(huì)回復(fù)原來(lái)高度,但釋放左鍵時(shí),高度卻是收縮時(shí)調(diào)整后的高度,一開始我以為這是個(gè)BUG,但我編寫時(shí)同樣出現(xiàn)這個(gè)問(wèn)題,發(fā)現(xiàn)這兩種模式會(huì)影響WM_MOVING參數(shù)的意義;
10、粘附在兩邊的時(shí)候當(dāng)你設(shè)置任務(wù)欄自動(dòng)隱藏,QQ窗口會(huì)自動(dòng)調(diào)整高度充滿屏幕高度;
11、窗口顯示或隱藏不是一瞬間的,這點(diǎn)在第9點(diǎn)提到的兩種模式下,會(huì)有所不同;
12、任務(wù)欄并不顯示QQ窗口;
二、編寫代碼
觀察完畢,就開始編寫了。
首先新建一個(gè)基于對(duì)話框的MFC程序,命名為QQHideWnd,在對(duì)話框?qū)傩缘膕tyles頁(yè)把border改為Resizing,你也可同時(shí)把Entended styles 的 tool window 鉤上,對(duì)于這點(diǎn)我在程序了動(dòng)態(tài)修改了。
在QQHideWndDlg.h頭文件添加以下成員函數(shù):
protected:
//修正移動(dòng)時(shí)窗口的大小
void FixMoving(UINT fwSide, LPRECT pRect);
//從收縮狀態(tài)顯示窗口
void DoShow();
//從顯示狀態(tài)收縮窗口
void DoHide();
//重載函數(shù),只是為了方便調(diào)用,實(shí)際調(diào)用CWnd的SetWindowPos(…)
BOOL SetWindowPos(const CWnd* pWndInsertAfter,LPCRECT pCRect, UINT nFlags = SWP_SHOWWINDOW);
繼續(xù)添加成員變量:
private::BOOL m_isSizeChanged;//窗口大小是否改變了
BOOL m_isSetTimer;//是否設(shè)置了檢測(cè)鼠標(biāo)的Timer
INTm_oldWndHeight;//舊的窗口寬度INTm_taskBarHeight;//任務(wù)欄高度INTm_edgeHeight;//邊緣高度
INTm_edgeWidth;//邊緣寬度
INTm_hideMode;//隱藏模式
BOOL m_hsFinished;//隱藏或顯示過(guò)程是否完成
BOOL m_hiding;//該參數(shù)只有在!m_hsFinished才有效
//真:正在隱藏,假:正在顯示
增加消息響應(yīng),需要注意的是有些消息你只有把右下角的 Filter for message設(shè)置為window才能看到。
WM_ NCHITTEST
WM_MOVING
WM_CREATE
WM_TIMER
然后來(lái)到對(duì)應(yīng)的cpp文件,在頭部定義一些宏:
//收縮模式#define HM_NONE0//不收縮
#define HM_1//向上收縮
#define HM_BOTTOM2//向下收縮
#define HM_LEFT3//向左收縮
#define HM_RIGHT4//向右收縮
#define CM_ELAPSE200 //檢測(cè)鼠標(biāo)是否離開窗口的時(shí)間間隔
#define HS_ELAPSE5//伸縮過(guò)程每步的時(shí)間間隔
#define HS_STEPS10//伸縮過(guò)程分成多少步完成
#define INTERVAL20//觸發(fā)粘附時(shí)鼠標(biāo)與屏幕邊界的最小間隔,單位為象素
#define INFALTE10//觸發(fā)收縮時(shí)鼠標(biāo)與窗口邊界的最小間隔,單位為象素
要求要近乎完美地模仿這個(gè)功能。由于是些效果的東西,貼圖也看不出來(lái),所以文章里就不截圖了,想看效果的就直接運(yùn)行源代碼的demo吧。
一、觀察
模仿前最重要的一步就是觀察,經(jīng)過(guò)半天對(duì)QQ的擺弄和摸索,總結(jié)出了以下一些特點(diǎn):
1、窗口開始粘附時(shí),檢測(cè)的是鼠標(biāo)坐標(biāo)與桌面邊界的距離,特別地,粘附在下面的時(shí)候,檢測(cè)的是與任務(wù)欄的距離;
2、在向上移動(dòng)窗口時(shí),窗口邊界永遠(yuǎn)不會(huì)超出桌面上面邊界;
3、窗口是個(gè) TopMost 風(fēng)格;
4、當(dāng)窗口粘附在上面、左邊或右邊并顯示時(shí),你把鼠標(biāo)移動(dòng)到最頂端,光標(biāo)變成改變窗口大小的圖標(biāo),而單單是把窗口的top坐標(biāo)設(shè)置為0是不行的;
5、粘附在下面的時(shí)候,當(dāng)處于移動(dòng)狀態(tài),那么窗口的底邊是與任務(wù)欄頂邊對(duì)齊的,但從隱藏到顯示的時(shí)候,窗口的底端是與屏幕底邊對(duì)齊的;
6、隱藏后顯露出來(lái)的那條線可能是一個(gè)Border,但肯定的是絕不包含Client區(qū)域;
7、關(guān)于響應(yīng)鼠標(biāo)的進(jìn)入與移出窗口,絕對(duì)不是WM_MOUSEMOVE、WM_MOUSELEAVE。證明:你以及其慢的速度接觸隱藏狀態(tài)的QQ邊界,你會(huì)發(fā)現(xiàn)幾乎是“一觸即發(fā)”,你又以及其慢的速度移出顯示狀態(tài)的QQ,你會(huì)發(fā)現(xiàn)它的收縮反而不是“一觸即發(fā)”的,而是離邊緣10象素左右。而WM_MOUSEMOVE,WM_MOUSELEAVE,只有在進(jìn)入、移出Client區(qū)域才響應(yīng),明顯和QQ不同,其實(shí)從第6點(diǎn)也可以知道;
8、粘附在兩邊的時(shí)候,高度會(huì)調(diào)整為桌面上邊界到任務(wù)欄下邊界的距離;
9、在“拖動(dòng)時(shí)顯示窗口內(nèi)容”模式下(桌面屬性-外觀-效果),粘附在兩邊的拖動(dòng)出來(lái)時(shí);如果收縮之前高度比收縮后小則回復(fù)原來(lái)高度,在非“拖動(dòng)時(shí)顯示窗口內(nèi)容”模式下,光柵會(huì)回復(fù)原來(lái)高度,但釋放左鍵時(shí),高度卻是收縮時(shí)調(diào)整后的高度,一開始我以為這是個(gè)BUG,但我編寫時(shí)同樣出現(xiàn)這個(gè)問(wèn)題,發(fā)現(xiàn)這兩種模式會(huì)影響WM_MOVING參數(shù)的意義;
10、粘附在兩邊的時(shí)候當(dāng)你設(shè)置任務(wù)欄自動(dòng)隱藏,QQ窗口會(huì)自動(dòng)調(diào)整高度充滿屏幕高度;
11、窗口顯示或隱藏不是一瞬間的,這點(diǎn)在第9點(diǎn)提到的兩種模式下,會(huì)有所不同;
12、任務(wù)欄并不顯示QQ窗口;
二、編寫代碼
觀察完畢,就開始編寫了。
首先新建一個(gè)基于對(duì)話框的MFC程序,命名為QQHideWnd,在對(duì)話框?qū)傩缘膕tyles頁(yè)把border改為Resizing,你也可同時(shí)把Entended styles 的 tool window 鉤上,對(duì)于這點(diǎn)我在程序了動(dòng)態(tài)修改了。
在QQHideWndDlg.h頭文件添加以下成員函數(shù):
protected:
//修正移動(dòng)時(shí)窗口的大小
void FixMoving(UINT fwSide, LPRECT pRect);
//從收縮狀態(tài)顯示窗口
void DoShow();
//從顯示狀態(tài)收縮窗口
void DoHide();
//重載函數(shù),只是為了方便調(diào)用,實(shí)際調(diào)用CWnd的SetWindowPos(…)
BOOL SetWindowPos(const CWnd* pWndInsertAfter,LPCRECT pCRect, UINT nFlags = SWP_SHOWWINDOW);
繼續(xù)添加成員變量:
private::BOOL m_isSizeChanged;//窗口大小是否改變了
BOOL m_isSetTimer;//是否設(shè)置了檢測(cè)鼠標(biāo)的Timer
INTm_oldWndHeight;//舊的窗口寬度INTm_taskBarHeight;//任務(wù)欄高度INTm_edgeHeight;//邊緣高度
INTm_edgeWidth;//邊緣寬度
INTm_hideMode;//隱藏模式
BOOL m_hsFinished;//隱藏或顯示過(guò)程是否完成
BOOL m_hiding;//該參數(shù)只有在!m_hsFinished才有效
//真:正在隱藏,假:正在顯示
增加消息響應(yīng),需要注意的是有些消息你只有把右下角的 Filter for message設(shè)置為window才能看到。
WM_ NCHITTEST
WM_MOVING
WM_CREATE
WM_TIMER
然后來(lái)到對(duì)應(yīng)的cpp文件,在頭部定義一些宏:
//收縮模式#define HM_NONE0//不收縮
#define HM_1//向上收縮
#define HM_BOTTOM2//向下收縮
#define HM_LEFT3//向左收縮
#define HM_RIGHT4//向右收縮
#define CM_ELAPSE200 //檢測(cè)鼠標(biāo)是否離開窗口的時(shí)間間隔
#define HS_ELAPSE5//伸縮過(guò)程每步的時(shí)間間隔
#define HS_STEPS10//伸縮過(guò)程分成多少步完成
#define INTERVAL20//觸發(fā)粘附時(shí)鼠標(biāo)與屏幕邊界的最小間隔,單位為象素
#define INFALTE10//觸發(fā)收縮時(shí)鼠標(biāo)與窗口邊界的最小間隔,單位為象素

