本文拟结合POWERBUILDER语言,简述如何实现类似QQ的自动显示/隐藏窗口,即:鼠标移入时自动弹出窗口,鼠标移出后自动隐藏窗口,同时当隐藏窗口后实现WINDOWS操作系统启动画面时显示的不同颜色条滚动效果以提示用户。
本文拟分以下四部分论述分三次完成:
(一)基本思路
(二)基础准备工作
(三)自动显示/隐藏窗口功能实现
(四)颜色条滚动效果实现
一、基本思路
(一)利用API:TrackMouseEvent函数捕获Wm_MouseLeave消息,来获取并处理鼠标移出事件;利用WINDOW的MOUSEMOVE事件来处理鼠标移入事件。
(二)利用API:CreateSolidBrush、FillRect函数来动态创建颜色渐变区域,以实现不同颜色条滚动效果。
注:
(1)通过调用TrackMouseEvent函数能够捕获到WM_NCMOUSEHOVER、WM_NCMOUSELEAVE、WM_MOUSEHOVER、WM_MOUSEHOVER四类消息;
(2)同样也可以捕获WM_MOUSEHOVER消息来处理鼠标移入事件,本例为简单起见直接处理MOUSEMOVE事件;
二、基础准备工作
1、新建一窗口,为便于说明问题,本例将窗口的WINDOW TYPE属性设置为POPUP类型,同时将TITLE BAR属性设置为无,这样可以减少鼠标进入非客户区域时也触发Wm_MouseLeave消息的影响;
2、声明本地外部函数:
function integer TrackMouseEvent(ref str_Track_Mouse str_Trm) library 'user32.dll'
function integer GetCursorPos(ref str_Point lppoint) library 'user32.dll'
function integer GetWindowRect(long ll_hwnd,ref str_Rect lpRect) library 'user32.dll'
function ulong PtInRect(ref str_Rect lpRect,ulong Pt_x,ulong Pt_y) library "user32.dll"
subroutine Sleep(ulong dwMilliseconds) library "kernel32.dll"
function ulong GetClientRect(ulong hwnd,ref str_Rect lpRect) library "user32.dll"
function ulong ClientToScreen(ulong hwnd,ref str_Point lpPoint) library "user32.dll"
function ulong OffsetRect(ref str_Rect lpRect,ulong Pt_x,ulong Pt_y) library "user32.dll"
Function ulong ReleaseCapture() LIBRARY "user32.dll"
Function ulong SendMessage(ulong hwnd,ulong wMsg,ulong wParam,ref ulong lParam) LIBRARY "user32.dll" ALIAS FOR "SendMessageA"
Function ulong GetDC(ulong hwnd) LIBRARY "user32.dll"
Function ulong DeleteObject(ulong hObject) LIBRARY "gdi32.dll"
Function ulong CreateSolidBrush(ulong crColor) LIBRARY "gdi32.dll"
Function ulong ReleaseDC(ulong hwnd,ulong hdc) LIBRARY "user32.dll"
Function ulong FillRect(ulong hdc,ref str_rect lpRect,ulong hBrush) LIBRARY "user32.dll"
注:上述API声明涉及到的结构请查阅MSDN或其他技术资料。
3、声明实例变量(Instance Variables):
boolean ib_onform = false,ib_display = true,ib_first_display = true,ib_first_hide = true
constant integer wm_mouseleave = 675
constant integer WM_NCLBUTTONDOWN = 161
constant integer HTCAPTION = 2
三、自动显示/隐藏窗口功能实现
1、处理该WINDOW的OTHER事件,借助此事件来捕获Wm_MouseLeave消息,来获取并处理鼠标移出事件:
str_Rect ls_rect
str_Point ls_point,ls_tmp
//注:Wm_MouseLeave消息一旦离开窗口的CLIENT区域就会发送,如:当鼠标移至窗口上的控件时也会发送此消息,当鼠标移到窗口的CAPTION或者MENU或者BORDER时也会发送此消息,故不能不加任何判断而直接隐藏窗口,并且此消息只发送一遍,若需继续跟踪鼠标,则需再次调用TRACKMOUSEEVENT函数;
if Message.number = Wm_MouseLeave then
ib_onform = false
GetCursorPos(ls_point)
GetClientRect(handle(this),ls_rect)
ls_tmp.x = ls_rect.left
ls_tmp.y = ls_rect.top
ClientToScreen(handle(this),ls_tmp)
OffsetRect(ls_rect,ls_tmp.x,ls_tmp.y)
//判断鼠标如果超出客户区,则自动隐藏窗口
//只能使用客户区判断,不能使用整个窗口RECT,否则当鼠标移至BORDER时可能会无法隐藏窗口
if (PtInRect(ls_rect,ls_point.x,ls_point.y) = 0) and ((this.x <= 0) or (this.y <= 0)) then
if this.y <= 0 then
wf_hide_v(this) //首先保证在V方向收缩滑动
else
wf_hide_h(this) //其次保证在H方向收缩滑动
end if
ib_display = false
end if
end if
2、处理该WINDOW的MOUSEMOVE事件来处理鼠标移入事件:
if ib_onform = false then
ib_onform = true
if ib_display = false then
if this.y <= 0 then
wf_display_v(this)
else
wf_display_h(this)
end if
ib_display = true
end if
wf_capmouse(this)
end if
3、创建该窗口的OPEN事件,以设置该窗口运行的初始位置:
this.backcolor = 16574393
this.title = "自动隐藏窗口示例___双击关闭窗口"
this.setposition(TopMost!)
this.width = p_1.width
this.height = p_1.height
this.x = 100
this.y = -this.height
wf_display_v(this)
4、创建相应的窗口函数wf_capmouse以调用鼠标消息捕获函数:
str_Track_Mouse str_trm
str_trm.nSize = 16
str_trm.hwndTrack = handle(ag_dest)
str_trm.nFlags = 2
TrackMouseEvent(str_trm)
5、创建相应的窗口函数wf_display_h以设置窗口如何在水平方向滑动显示:
integer li_left
str_Rect ls_rect1,ls_rect2
str_Point ls_tmp
GetWindowRect(handle(this),ls_rect1)
GetClientRect(handle(this),ls_rect2)
ls_tmp.x = ls_rect2.left
ls_tmp.y = ls_rect2.top
ClientToScreen(handle(this),ls_tmp)
OffsetRect(ls_rect2,ls_tmp.x,ls_tmp.y)
li_left = ls_rect2.left - ls_rect1.left //计算出窗口边框宽度
//计算窗口边框还可以用GetSystemMetrics+SM_CXBORDER/SM_CYBORDER/SM_CXFRAME/SM_CYFRAME,但是需要判断窗口状态是可变边框还是不可变边框还是无边框,因此不如直接采用上述方法
do while as_win.x < -15
as_win.x = as_win.x + 10 //这里的10主要用于控制窗口滑动速度
if ib_first_display then
p_1.draw(0,0) //这里主要防止第一次滑动窗口时不显示图象
end if
sleep(0.01) //这里的SLEEP函数主要用于控制窗口滑动速度
loop
as_win.x = -3*li_left //这里值不能太小否则,鼠标移到左侧时易出边界
ib_first_display = false
注:用于设置窗口如何在垂直方向滑动显示的wf_display_v函数不再赘述,修改as_win.y属性即可。
6、创建相应的窗口函数wf_display_h以设置窗口如何在水平方向滑动隐藏:
do while as_win.x > -as_win.width + 25
as_win.x = as_win.x - 10
if ib_first_hide then
p_1.draw(0,0) //这里主要防止第一次隐藏窗口时不显示图象
end if
sleep(0.005)
loop
as_win.x = -as_win.width + 10 //这里的10用于控制最后窗口隐藏后留在外侧的边距
ib_first_hide = false
注:用于设置窗口如何在垂直方向滑动显示的wf_display_v函数不再赘述,修改as_win.y属性即可。
7、由于该窗口为NO TITLEBAR窗口,因此无法拖动,需要专门编写脚本来实现拖动效果,方法为处理窗口的MOUSEDOWN事件:
ulong ll_arg
ReleaseCapture() //释放对MOUSE消息的捕获,故在拖动期间不会发生WM_MOUSELEAVE事件
ll_arg = 0
SendMessage(handle(this),WM_NCLBUTTONDOWN,HTCAPTION,ll_arg)
//即发送WM_NCLBUTTONDOWN消息,模拟用户拖动TITLEBAR效果
四、颜色条滚动效果实现
1、获取该窗口的HDC(设备句柄),即在窗口的OPEN事件当中编写:
声明全局变量:long gl_hdc
声明外部函数:Function ulong GetDC(ulong hwnd) LIBRARY "user32.dll"
gl_hdc = getdc(handle(this))
2、创建一个TIMING类型用户对象USER OBJECT,用于动态绘制彩色矩形:
(1)首先声明该对象实例变量:
integer ii_turn
str_rect is_rect_all,is_rect_form
long il_hbrush,il_target_height,il_target_width,il_target_pace,il_height_base
(2)初始化该用户对象,编写其CONSTRUCTOR事件:
ii_turn = 1
il_target_width = UnitsToPixels(w_function.width , XUnitsToPixels!)
//设置整个目标区域的宽度
il_target_height = UnitsToPixels(w_function.height , YUnitsToPixels!)
//设置整个目标区域的高度
il_target_pace = 15 //设置彩色颜色条滚动步距
il_height_base = 100 //设置彩色颜色条宽度
is_rect_all.left = il_target_width - UnitsToPixels(30 , XUnitsToPixels!)
is_rect_all.right = il_target_width
is_rect_form.top = 0
is_rect_form.bottom = il_target_height
is_rect_form.left = is_rect_all.left
is_rect_form.right = is_rect_all.right
il_hbrush = CreateSolidBrush(gl_backcolor)
(3)开始具体编写动态颜色条滚动效果代码,即处理该对象的TIMER事件:
str_rect ls_rect_tmp
long ll_tmp,ll_ret,ll_hbrush
integer li_red,li_green,li_blue
if ii_turn = 1 then
if is_rect_all.top < il_target_height - il_height_base - il_target_pace then
is_rect_all.top = is_rect_all.top + il_target_pace
else
is_rect_all.top = il_target_height - il_height_base
ii_turn = -1
end if
else
if is_rect_all.top > il_target_pace then
is_rect_all.top = is_rect_all.top - il_target_pace
else
is_rect_all.top = 0
ii_turn = 1
end if
end if
is_rect_all.bottom = is_rect_all.top + il_height_base
//w_function.backcolor = gl_backcolor
ll_ret = FillRect(gl_hdc, is_rect_form, il_hbrush) //注:这样比上面一句效率高
li_red = mod(gl_backcolor,256)
li_green = Truncate(mod((gl_backcolor - li_red),65536) / 256 , 0)
li_blue = Truncate(gl_backcolor / 65536 , 0)
ls_rect_tmp.left = is_rect_all.left
ls_rect_tmp.right = is_rect_all.right
if ii_turn = 1 then
ls_rect_tmp.bottom = is_rect_all.top
do while ls_rect_tmp.bottom < is_rect_all.bottom
li_red = li_red - 20 //注:这里的20决定了颜色的深浅程度 li_green = li_green - 20
li_blue = li_blue - 20
if li_red < 0 then
li_red = 0
end if
if li_green < 0 then
li_green = 0
end if
if li_blue < 0 then
li_blue = 0
end if
ll_hbrush = CreateSolidBrush(rgb(li_red,li_green,li_blue))
ls_rect_tmp.top = ls_rect_tmp.bottom
ls_rect_tmp.bottom = ls_rect_tmp.top + 25 //注:这里的25决定了渐变的快慢程度
ll_ret = FillRect(gl_hdc, ls_rect_tmp, ll_hbrush)
ll_ret = Deleteobject(ll_hbrush)
loop
else
ls_rect_tmp.top = is_rect_all.bottom
do while ls_rect_tmp.top > is_rect_all.top
li_red = li_red - 20
li_green = li_green - 20
li_blue = li_blue - 20
if li_red < 0 then
li_red = 0
end if
if li_green < 0 then
li_green = 0
end if
if li_blue < 0 then
li_blue = 0
end if
ll_hbrush = CreateSolidBrush(rgb(li_red,li_green,li_blue))
ls_rect_tmp.bottom = ls_rect_tmp.top
ls_rect_tmp.top = ls_rect_tmp.bottom - 25
ll_ret = FillRect(gl_hdc, ls_rect_tmp, ll_hbrush)
ll_ret = Deleteobject(ll_hbrush)
loop
end if
3、在主WINDOW窗口中调用此TIMING对象:
(1)处理OPEN事件:iuo_timer = create uo_timer
(2)处理wf_hide_h/v事件:iuo_timer.start(0.2)
(3)处理CLOSE事件:destroy iuo_timer
至此,仿QQ自动显示/隐藏加颜色条滚动效果窗口全部完成。
由于我自己是分在两个程序当中实现上述两种效果,故无法给出完整的示意图,仅以如下两图示例: