本文介绍如何实现类似QQ的自动隐藏功能,具体的讲,就是当窗口停靠在屏幕边缘并且鼠标离开窗口时,窗口自动隐藏,但是会留有一小截窗口不隐藏。当鼠标移回这一小截不隐藏的窗口时,窗口自动弹出。
这里所谓的隐藏,并不是窗口直接不见了,,而是窗口被移动至屏幕外,留下的一小截也就是未移出屏幕的部分。
另一点,动态的隐藏,其实是多次调用MoveWindow函数向一个方向移动窗口至屏幕外的结果。
下面是具体实现:
1、创建基于对话框的工程,编写实现窗口自动隐藏的函数AutoHiddenWindow(int margin)。因为要留出的“一小截”不好确定,可能有不同的需求,所以添加一个margin参数,表示隐藏后露出的那一小截的宽度(单位为像素)。
上文讲了,要实现动态效果,就要多次调用MoveWindow函数,这里将每次移动的距离定为1个像素。那么要移动的次数也就是要移动的距离了。而要移动的距离即为窗口的高度(上下两个方向)或宽度(左右两个方向)减去留出的边距margin。
下面是具体的函数实现:
void CxxxDlg::AutoHiddenWindow(int margin)
{
// 获取屏幕宽度
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
// 获取屏幕高度
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
CRect rect;
GetWindowRect(&rect);
// 向左收缩
while (rect.left<=0 && rect.right>=margin)
{
// 获取窗口范围
GetWindowRect(&rect);
// 移动窗口
MoveWindow(rect.left-1, rect.top, rect.Width(), rect.Height(), FALSE);
}
// 向上收缩
while (rect.top<=0 && rect.bottom>=margin)
{
// 获取窗口范围
GetWindowRect(&rect);
// 移动窗口
MoveWindow(rect.left, rect.top-1, rect.Width(), rect.Height(), FALSE);
}
// 向右收缩
while (rect.right>=screenWidth && rect.left<=screenWidth-margin)
{
// 获取窗口范围
GetWindowRect(&rect);
// 移动窗口
MoveWindow(rect.left+1, rect.top, rect.Width(), rect.Height(), FALSE);
}
// 向下收缩
while (rect.bottom>=screenHeight && rect.top<=screenHeight-margin)
{
// 获取窗口范围
GetWindowRect(&rect);
// 移动窗口
MoveWindow(rect.left, rect.top+1, rect.Width(), rect.Height(), FALSE);
}
}
上面在判断隐藏的方向时是根据水平或垂直方向上的两个坐标来确定的。rect变量保存了窗口的位置,具体的讲是窗口左上角的坐标(rect.left,rect.top)以及窗口右下角的坐标(rect.right,rect.bottom),当
rect.left, rect.top,rect.right, rect.bottom的值为0时,那么就可以确定窗口贴紧了屏幕的哪一边。
由上可知,窗口可能同时贴紧两个边,这时根据上述函数,会同时向两个方向移动,最后留下的自绘剩下一个角。其实,这样的效果也不错。如果确实只想其向某一边隐藏,加个判断语句就行了。
附上几张图:
窗口停靠在屏幕上侧后隐藏
窗口停靠在屏幕右侧后隐藏
窗口同时停靠在屏幕上侧和右侧后隐藏
2、自动弹出窗口的函数,总的来说,就是自动隐藏函数的反过程。需要注意一点,弹出后要保证窗口仍在隐藏前的位置,即紧靠屏幕的某一侧。这样,鼠标再次离开时,窗口仍能自动隐藏。
下面是具体函数实现:
void CxxxDlg::AutoShowWindow()
{
// 获取屏幕宽度
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
// 获取屏幕高度
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
CRect rect;
GetWindowRect(&rect);
// 向右弹出
while (rect.left<0)
{
// 移动窗口
MoveWindow(rect.left+1, rect.top, rect.Width(), rect.Height(), FALSE);
// 获取窗口范围
GetWindowRect(&rect);
}
// 向下收弹出
while (rect.top<0)
{
// 移动窗口
MoveWindow(rect.left, rect.top+1, rect.Width(), rect.Height(), FALSE);
// 获取窗口范围
GetWindowRect(&rect);
}
// 向左弹出
while (rect.right>screenWidth)
{
// 移动窗口
MoveWindow(rect.left-1, rect.top, rect.Width(), rect.Height(), FALSE);
// 获取窗口范围
GetWindowRect(&rect);
}
// 向上弹出
while (rect.bottom>screenHeight)
{
// 移动窗口
MoveWindow(rect.left, rect.top-1, rect.Width(), rect.Height(), FALSE);
// 获取窗口范围
GetWindowRect(&rect);
}
}
3、另外,还需要一个判断鼠标是否离开窗口的函数JudgeCursorOut:
bool CxxxDlg::JudgeCursorOut()
{
// 获取鼠标位置
POINT point;
GetCursorPos(&point);
// 获取窗口范围
CRect rect;
GetWindowRect(&rect);
// 判断
if (point.x>=rect.left && point.x<=rect.right && point.y>=rect.top && point.y<=rect.bottom)
{
return false; // 鼠标还在窗口内
}
else
{
return true; // 鼠标离开窗口
}
}
void CxxxDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if (0 == nIDEvent)
{
if (JudgeCursorOut())
{
AutoHiddenWindow(10); // 如果鼠标离开窗口,窗口自动隐藏
}
else
{
AutoShowWindow(); // 鼠标移入窗口,窗口自动显示
}
}
CDialog::OnTimer(nIDEvent);
}
5、最后,在窗口初始化函数中添加一个计时器就行了:
BOOL CxxxDlg::OnInitDialog()
{
……
// 启动计时器
SetTimer(0, 400, NULL);
……
}
至此,功能实现,当然还有很多不足之处,但是这里主要介绍隐藏和显示两个函数。