Visual C++设计超强仿QQ自动伸缩窗口.docx
- 文档编号:30103244
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:12
- 大小:21.42KB
Visual C++设计超强仿QQ自动伸缩窗口.docx
《Visual C++设计超强仿QQ自动伸缩窗口.docx》由会员分享,可在线阅读,更多相关《Visual C++设计超强仿QQ自动伸缩窗口.docx(12页珍藏版)》请在冰豆网上搜索。
VisualC++设计超强仿QQ自动伸缩窗口
Visual C++设计超强仿QQ自动伸缩窗口
作者:
未知文章来源:
网络
某天在论坛上看到有人发帖询问QQ自动伸缩窗口是怎么实现的,我也好想知道,于是到XX一搜索,结果不多,来来去去都是那几篇,下载那些demo运行一下,发觉效果与QQ相差很大,于是决定自己动手做个,要求要近乎完美地模仿这个功能。
由于是些效果的东西,贴图也看不出来,所以文章里就不截图了,想看效果的就直接运行源代码的demo吧。
一、观察
模仿前最重要的一步就是观察,经过半天对QQ的摆弄和摸索,总结出了以下一些特点:
1、窗口开始粘附时,检测的是鼠标坐标与桌面边界的距离,特别地,粘附在下面的时候,检测的是与任务栏的距离;
2、在向上移动窗口时,窗口边界永远不会超出桌面上面边界;
3、窗口是个TopMost风格;
4、当窗口粘附在上面、左边或右边并显示时,你把鼠标移动到最顶端,光标变成改变窗口大小的图标,而单单是把窗口的top坐标设置为0是不行的;
5、粘附在下面的时候,当处于移动状态,那么窗口的底边是与任务栏顶边对齐的,但从隐藏到显示的时候,窗口的底端是与屏幕底边对齐的;
6、隐藏后显露出来的那条线可能是一个Border,但肯定的是绝不包含Client区域;
7、关于响应鼠标的进入与移出窗口,绝对不是WM_MOUSEMOVE、WM_MOUSELEAVE。
证明:
你以及其慢的速度接触隐藏状态的QQ边界,你会发现几乎是“一触即发”,你又以及其慢的速度移出显示状态的QQ,你会发现它的收缩反而不是“一触即发”的,而是离边缘10象素左右。
而WM_MOUSEMOVE,WM_MOUSELEAVE,只有在进入、移出Client区域才响应,明显和QQ不同,其实从第6点也可以知道;
8、粘附在两边的时候,高度会调整为桌面上边界到任务栏下边界的距离;
9、在“拖动时显示窗口内容”模式下(桌面属性-外观-效果),粘附在两边的拖动出来时;如果收缩之前高度比收缩后小则回复原来高度,在非“拖动时显示窗口内容”模式下,光栅会回复原来高度,但释放左键时,高度却是收缩时调整后的高度,一开始我以为这是个BUG,但我编写时同样出现这个问题,发现这两种模式会影响WM_MOVING参数的意义;
10、粘附在两边的时候当你设置任务栏自动隐藏,QQ窗口会自动调整高度充满屏幕高度;
11、窗口显示或隐藏不是一瞬间的,这点在第9点提到的两种模式下,会有所不同;
12、任务栏并不显示QQ窗口;
二、编写代码
观察完毕,就开始编写了。
首先新建一个基于对话框的MFC程序,命名为QQHideWnd,在对话框属性的styles页把border改为Resizing,你也可同时把Entendedstyles的toolwindow钩上,对于这点我在程序了动态修改了。
在QQHideWndDlg.h头文件添加以下成员函数:
protected:
//修正移动时窗口的大小
voidFixMoving(UINTfwSide,LPRECTpRect);
//从收缩状态显示窗口
voidDoShow();
//从显示状态收缩窗口
voidDoHide();
//重载函数,只是为了方便调用,实际调用CWnd的SetWindowPos(…)
BOOLSetWindowPos(constCWnd*pWndInsertAfter,LPCRECTpCRect,UINTnFlags=SWP_SHOWWINDOW);
继续添加成员变量:
private:
:
BOOLm_isSizeChanged;//窗口大小是否改变了
BOOLm_isSetTimer;//是否设置了检测鼠标的Timer
INTm_oldWndHeight;//旧的窗口宽度INTm_taskBarHeight;//任务栏高度INTm_edgeHeight;//边缘高度
INTm_edgeWidth;//边缘宽度
INTm_hideMode;//隐藏模式
BOOLm_hsFinished;//隐藏或显示过程是否完成
BOOLm_hiding;//该参数只有在!
m_hsFinished才有效
//真:
正在隐藏,假:
正在显示
增加消息响应,需要注意的是有些消息你只有把右下角的Filterformessage设置为window才能看到。
WM_NCHITTEST
WM_MOVING
WM_CREATE
WM_TIMER
然后来到对应的cpp文件,在头部定义一些宏:
//收缩模式#defineHM_NONE0//不收缩
#defineHM_TOP1//向上收缩
#defineHM_BOTTOM2//向下收缩
#defineHM_LEFT3//向左收缩
#defineHM_RIGHT4//向右收缩
#defineCM_ELAPSE200//检测鼠标是否离开窗口的时间间隔
#defineHS_ELAPSE5//伸缩过程每步的时间间隔
#defineHS_STEPS10//伸缩过程分成多少步完成
#defineINTERVAL20//触发粘附时鼠标与屏幕边界的最小间隔,单位为象素
#defineINFALTE10//触发收缩时鼠标与窗口边界的最小间隔,单位为象素
然后在构造函数初始化成员变量:
m_isSizeChanged=FALSE;
m_isSetTimer=FALSE;m_hsFinished=TRUE;
m_hiding=FALSE;m_oldWndHeight=MINCY;
m_taskBarHeight=30;
m_edgeHeight=0;
m_edgeWidth=0;
m_hideMode=HM_NONE;
完成了一些初始的工作,那么就开始进入关键的函数实现了。
首先是在OnCreate做些窗口的初始化和获得一些系统信息。
【代码一】
intCQQHideWndDlg:
:
OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CDialog:
:
OnCreate(lpCreateStruct)==-1)
return-1;
//TODO:
Addyourspecializedcreationcodehere
//获得任务栏高度
CWnd*p;
p=this->FindWindow("Shell_TrayWnd",NULL);
if(p!
=NULL)
{
CRecttRect;
p->GetWindowRect(tRect);
m_taskBarHeight=tRect.Height();
}
//修改风格使得他不在任务栏显示
ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);
//去掉关闭按键(如果想画3个按键的话)
//ModifyStyle(WS_SYSMENU,NULL);
//获得边缘高度和宽度
m_edgeHeight=GetSystemMetrics(SM_CYEDGE);
m_edgeWidth=GetSystemMetrics(SM_CXFRAME);
return0;
}
接着如何知道鼠标进入或移出窗口呢?
在前面我已经证明了WM_MOUSEMOVE和WM_MOUSELEAVE不符合我们的要求,于是我用了WM_NCHITTEST这个消息,你可以看到我在这个消息响应函数中用了两个SetTimer,一个用于检测鼠标是否离开,一个用于伸缩过程,不管你喜欢不喜欢,要达到第7点和第11点,这个是必须的,考虑的效率问题,在不需要的时候关闭这些Timer就好了。
【代码二】
UINTCQQHideWndDlg:
:
OnNcHitTest(CPointpoint)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
CStringstr;
str.Format("Mouse(%d,%d)",point.x,point.y);
GetDlgItem(IDC_CURSOR)->SetWindowText(str);
if(m_hideMode!
=HM_NONE&&!
m_isSetTimer&&
//防止鼠标超出屏幕右边时向右边收缩造成闪烁
point.x {//鼠标进入时,如果是从收缩状态到显示状态则开启Timer SetTimer(1,CM_ELAPSE,NULL); m_isSetTimer=TRUE; m_hsFinished=FALSE; m_hiding=FALSE; SetTimer(2,HS_ELAPSE,NULL);//开启显示过程 } returnCDialog: : OnNcHitTest(point); } 然后在OnTimer中 【代码三】 voidCQQHideWndDlg: : OnTimer(UINTnIDEvent) { //TODO: Addyourmessagehandlercodehereand/orcalldefault if(nIDEvent==1) { POINTcurPos; GetCursorPos(&curPos); CStringstr; str.Format("TimerOn(%d,%d)",curPos.x,curPos.y); GetDlgItem(IDC_TIMER)->SetWindowText(str); CRecttRect; //获取此时窗口大小 GetWindowRect(tRect); //膨胀tRect,以达到鼠标离开窗口边沿一定距离才触发事件 tRect.InflateRect(INFALTE,INFALTE); if(! tRect.PtInRect(curPos))//如果鼠标离开了这个区域 { KillTimer (1);//关闭检测鼠标Timer m_isSetTimer=FALSE; GetDlgItem(IDC_TIMER)->SetWindowText("TimerOff"); m_hsFinished=FALSE; m_hiding=TRUE; SetTimer(2,HS_ELAPSE,NULL);//开启收缩过程 } } if(nIDEvent==2) { if(m_hsFinished)//如果收缩或显示过程完毕则关闭Timer KillTimer (2); else m_hiding? DoHide(): DoShow(); } CDialog: : OnTimer(nIDEvent); } 暂时不管OnTimer中的DoHide();DoShow(); 先来看看核心的函数之一的FixMoving,该函数在OnMoving中被调用,FixMoving通过检测鼠标位置和窗口位置来决定窗口的收缩模式,并修正粘附边界时窗口的位置,从而达到像移动QQ时出现的效果。 【代码四】 voidCQQHideWndDlg: : FixMoving(UINTfwSide,LPRECTpRect) { POINTcurPos; GetCursorPos(&curPos); INTscreenHeight=GetSystemMetrics(SM_CYSCREEN); INTscreenWidth=GetSystemMetrics(SM_CXSCREEN); INTheight=pRect->bottom-pRect->top; INTwidth=pRect->right-pRect->left; if(curPos.y<=INTERVAL) {//粘附在上边 pRect->bottom=height-m_edgeHeight; pRect->top=-m_edgeHeight; m_hideMode=HM_TOP; } elseif(curPos.y>=(screenHeight-INTERVAL-m_taskBarHeight)) {//粘附在下边 pRect->top=screenHeight-m_taskBarHeight-height; pRect->bottom=screenHeight-m_taskBarHeight; m_hideMode=HM_BOTTOM; } elseif(curPos.x {//粘附在左边 if(! m_isSizeChanged) { CRecttRect; GetWindowRect(tRect); m_oldWndHeight=tRect.Height(); } pRect->right=width; pRect->left=0; pRect->top=-m_edgeHeight; pRect->bottom=screenHeight-m_taskBarHeight; m_isSizeChanged=TRUE; m_hideMode=HM_LEFT; } elseif(curPos.x>=(screenWidth-INTERVAL)) {//粘附在右边 if(! m_isSizeChanged) { CRecttRect; GetWindowRect(tRect); m_oldWndHeight=tRect.Height(); } pRect->left=screenWidth-width; pRect->right=screenWidth; pRect->top=-m_edgeHeight; pRect->bottom=screenHeight-m_taskBarHeight; m_isSizeChanged=TRUE; m_hideMode=HM_RIGHT; } else {//不粘附 if(m_isSizeChanged) {//如果收缩到两边,则拖出来后会变回原来大小 //在"拖动不显示窗口内容下"只有光栅变回原来大小 pRect->bottom=pRect->top+m_oldWndHeight; m_isSizeChanged=FALSE; } if(m_isSetTimer) {//如果Timer开启了,则关闭之 if(KillTimer (1)==1) m_isSetTimer=FALSE; } m_hideMode=HM_NONE; GetDlgItem(IDC_TIMER)->SetWindowText("Timeroff"); } } 收缩模式和位置决定后,剩下的工作就由最后两个核心函数完成了: 实现收缩的DoHide(),实现伸展的DoShow()。 在这两个过程中m_hsFinished,m_hiding这两个变量起到很重要的控制作用。 由于伸缩过程没完成时,hsFinished始终为FALSE,所以Timer2不会关闭,于是在OnTimer中会重复调用这两个函数之一,在这两个函数体内,窗口位置有规律地递减或递增就可以达到QQ的“抽屉”效果了,有趣的是即使伸缩过程还没完成,你也可以在这个过程中改变m_hiding这个值来决定他是伸还是缩,正如QQ一样。 你可以把Timer2的事件间隔调大一点,然后在窗口伸缩时,鼠标来回地进出窗口就会很容易看到这样有趣的效果(还没缩进去又被拉了出来,或者还没拉出来又缩进去了)。 【代码五】 voidCQQHideWndDlg: : DoHide() { if(m_hideMode==HM_NONE) return; CRecttRect; GetWindowRect(tRect); INTheight=tRect.Height(); INTwidth=tRect.Width(); INTsteps=0; switch(m_hideMode) { caseHM_TOP: steps=height/HS_STEPS; tRect.bottom-=steps; if(tRect.bottom<=m_edgeWidth) {//你可以把下面一句替换上面的...+=|-=steps达到取消抽屉效果 //更好的办法是添加个BOOL值来控制,其他case同样. tRect.bottom=m_edgeWidth; m_hsFinished=TRUE;//完成隐藏过程 } tRect.top=tRect.bottom-height; break; caseHM_BOTTOM: steps=height/HS_STEPS; tRect.top+=steps; if(tRect.top>=(GetSystemMetrics(SM_CYSCREEN)-m_edgeWidth)) { tRect.top=GetSystemMetrics(SM_CYSCREEN)-m_edgeWidth; m_hsFinished=TRUE; } tRect.bottom=tRect.top+height; break; caseHM_LEFT: steps=width/HS_STEPS; tRect.right-=steps; if(tRect.right<=m_edgeWidth) { tRect.right=m_edgeWidth; m_hsFinished=TRUE; } tRect.left=tRect.right-width; tRect.top=-m_edgeHeight; tRect.bottom=GetSystemMetrics(SM_CYSCREEN)-m_taskBarHeight; break; caseHM_RIGHT: steps=width/HS_STEPS; tRect.left+=steps; if(tRect.left>=(GetSystemMetrics(SM_CXSCREEN)-m_edgeWidth)) { tRect.left=GetSystemMetrics(SM_CXSCREEN)-m_edgeWidth; m_hsFinished=TRUE; } tRect.right=tRect.left+width; tRect.top=-m_edgeHeight; tRect.bottom=GetSystemMetrics(SM_CYSCREEN)-m_taskBarHeight; break; default: break; } SetWindowPos(&wndTopMost,tRect); } 【代码六】 voidCQQHideWndDlg: : DoShow() { if(m_hideMode==HM_NONE) return; CRecttRect; GetWindowRect(tRect); INTheight=tRect.Height(); INTwidth=tRect.Width(); INTsteps=0; switch(m_hideMode) { caseHM_TOP: steps=height/HS_STEPS; tRect.top+=steps; if(tRect.top>=-m_edgeHeight) {//你可以把下面一句替换上面的...+=|-=steps达到取消抽屉效果 //更好的办法是添加个BOOL值来控制,其他case同样. tRect.top=-m_edgeHeight; m_hsFinished=TRUE;//完成显示过程 } tRect.bottom=tRect.top+height; break; caseHM_BOTTOM: steps=height/HS_STEPS; tRect.top-=steps; if(tRect.top<=(GetSystemMetrics(SM_CYSCREEN)-height)) { tRect.top=GetSystemMetrics(SM_CYSCREEN)-height; m_hsFinished=TRUE; } tRect.bottom=tRect.top+height; break; caseHM_LEFT: steps=width/HS_STEPS; tRect.right+=steps; if(tRect.right>=width) { tRect.right=width; m_hsFinished=TRUE; } tRect.left=tRect.right-width; tRect.top=-m_edgeHeight; tRect.bottom=GetSystemMetrics(SM_CYSCREEN)-m_taskBarHeight; break; caseHM_RIGHT: steps=width/HS_STEPS; tRect.left-=steps; if(tRect.left<=(GetSystemMetrics(SM_CXSCREEN)-width)) { tRect.left=GetSystemMetrics(SM_CXSCREEN)-width; m_hsFinished=TRUE; } tRect.right=tRect.left+width; tRect.top=-m_edgeHeight; tRect.bottom=GetSystemMetrics(SM_CYSCREEN)-m_taskBarHeight; break; default: break; } SetWindowPos(&wndTopMost,tRect); } BOOLCQQHideWndDlg: : SetWindowPos(constCWnd*pWndInsertAfter,LPCRECTpCRect,UINTnFlags) { returnCDialog: : SetWindowPos(pWndInsertAfter,pCRect->left,pCRect->top, pCRect->right-pCRect->left,pCRect->bottom-pCRect->
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Visual C+设计超强仿QQ自动伸缩窗口 C+ 设计 超强 QQ 自动 伸缩 窗口