Delphi下QQ窗體自動隱藏探索

字號:

一、問題的提出
    熟悉QQ使用的朋友都知道,當QQ窗體區(qū)域超出屏幕四邊時,窗體就會自動“消失”,只留下窗體一邊的小部分顯露在桌面上。當用鼠標移動到顯露部分之上,窗體就會在隱藏位置重新完整顯示;但當鼠標離開窗體區(qū)域后,窗體便會重新進入隱藏狀態(tài)。
    對隱藏的全過程進行分析,可以得出兩點推測:第一,窗體隱藏的處理是與窗體移動過程有關;第二,窗體隱藏的觸發(fā)條件。
    對第一點推測,可以通過對窗體移動時產(chǎn)生的Windows消息進行攔截處理加以實現(xiàn)。對第二點推測,如何去表示“窗體區(qū)域已經(jīng)超出屏幕可視范圍”這一條件為實現(xiàn)的關鍵。
    二、基本的分析
    讓我們先留意一下Windows環(huán)境下窗體移動的過程與效果。當使用鼠標移動窗體的時候,窗體本身并沒有立刻隨鼠標的移動而發(fā)生位置的改變;相反,鼠標正在拖動的是一個大小與窗體一致的透明區(qū)域(確切的說一個虛線邊框的矩形),如圖一所示。當鼠標釋放矩形后,窗體本身才會在矩形最后停留的地方出現(xiàn),從而完成整個移動的過程,如圖二所示。(注意:在Windows 2000及XP環(huán)境下,如果在顯示屬性中選中“拖動時顯示窗體內容”的顯示效果選項,則上述過程無法觀察。)
    對QQ窗體,其移動過程與上述無異,但卻有一處不同。當我們把矩形移動到屏幕四邊且已有部分超出時,矩形就會自動地停留在超出位置上并完整顯示。此時不論我們怎樣試圖把矩形再向超出方向上移動,矩形也只保持在該位置,如圖三所示。當釋放鼠標之后,窗體的隱藏效果也就出現(xiàn)了,如圖四所示。
    從上述過程可以推斷,觸發(fā)隱藏條件后,即使仍處于移動過程但矩形本身卻已經(jīng)被鎖定,因此對窗體位置的判斷是發(fā)生在移動過程中,也就是說我們要攔截處理的Windows消息是WM_MOVING。其次,在移動過程中首先發(fā)生位置變化的是矩形而不是窗體本身,因此實現(xiàn)隱藏的關鍵是對矩形參數(shù)的判斷與設置。
    我們可以先留意一下WM_MOVING消息的語法結構:
    WM_MOVING
    WPARAM wParam
    LPARAM lParam,
    其中,WPARAM不被使用,而LPARAM則是一個指針,所指向的是一個RECT結構。RECT結構中包含了Left、Top、Right、Bottom四個參數(shù),分別用于描述矩形的左上角與右下角,“該RECT記錄了窗體相對于屏幕的當前位置;當要改變拖動矩形的位置時,程序本身必須改變RECT結構中各成員變量的相關值”。由此可知,我們要處理的矩形其實已經(jīng)在WM_MOVING消息中被提到,我們要處理的也就是LPARAM所指向的RECT結構的有關參數(shù)。
    接下來我們要設置一個由隱藏條件激活的計時器,目的是監(jiān)控鼠標相對窗體的位置。因為窗體隱藏后的隱現(xiàn)是靠鼠標激活的,所以若檢測到鼠標位于窗體之上,則說明窗體在顯示狀態(tài);反之,窗體在隱藏狀態(tài)。我們只需在相關的判斷下加入對窗體Top和Left屬性的賦值即可實現(xiàn)隱現(xiàn)效果。
    至此,有關自動隱藏效果的實現(xiàn)分析就基本完成了。不過還要注意一點,因為我們是在WM_MOVING消息的攔截處理中判斷隱藏條件,而通過計時器的OnTimer事件處理隱現(xiàn)效果。在此隱藏條件是否滿足在兩個過程中的傳遞將成為關鍵。同時我們要知道的不僅是隱藏條件是否滿足,還必須知道窗體是在屏幕的那一邊上發(fā)生隱藏。為此,我們需要定義一個集合去描述窗體隱藏的位置,例如:
    type
    HidePosKind = (hpTop,hpLeft,hPBottom,hpRight);
    type
    THidePos = set of HidePosKind;
    不過,類似的集合在Delphi本身就已經(jīng)存在,譬如TAnchors集合。TAnchors集合原來是用于指明一個控件如何錨定于其父類控件的位置,我們在這里則借用來描述窗體對屏幕的隱藏位置。
    在TAnchors集合中也包含了四個值,其定義如下:
    type TAnchorKind = (akTop, akLeft, akRight, akBottom);
    type TAnchors = set of TAnchorKind;
    在代碼的實現(xiàn)中,我們將定義一個TAnchors類型的全局變量FAnchors去描述窗體隱藏的位置。