承接上篇,本文將深入解析 QQ 經典界面的三大核心模組:動感十足的抽屜式導航選單、動態建立的訊息對話框、以及桌面頂端的智慧型吸附/隱藏機制。所有程式碼基於 MFC 對話框架構,經實測可在 VC6.0/VC++.NET 環境下穩定執行。
QQ 的側邊選單以「刷」的一聲滑動展開/收合,極具辨識度。核心思路:預先記錄各分頁按鈕的初始位置,點擊時透過 MoveWindow 重新計算並移動按鈕與內容區域,配合計時器可實現漸變動畫。
OnInitDialog 中取得首尾按鈕的客戶區座標(rect0, rect1)。ChangeView(): 根據 clicknum(目標分頁)與 TopButtonNum(當前頂層分頁)判斷移動邏輯。CListCtrl 或 CTreeCtrl)。void CMyQQDlg::ChangeView()
{
if (TopButtonNum != 1 && clicknum == 1) // QQ好友分頁
{
m_QQothers.MoveWindow(0, rect1.bottom, rect0.Width(), rect0.Height(), TRUE);
m_QQqun.MoveWindow(0, rect1.bottom - rect0.Height(), rect0.Width(), rect0.Height(), TRUE);
// 切換圖示列表大小
m_List1.SetImageList(&m_imagelist2, LVSIL_SMALL);
m_List1.DeleteAllItems();
for (int i = 1; i < 34; i++) {
m_List1.InsertItem(0xFFFF, _T(""));
m_List1.InsertItem(0xFFFF, _T("\n") + myClass[i-1], i);
}
m_List1.ShowWindow(SW_SHOW);
m_List2.ShowWindow(SW_HIDE);
treeCtrl.ShowWindow(SW_HIDE);
return;
}
// ... 群組、最近聯絡人分頁邏輯類似
}
在對話框最上/下方放置兩個極小、不可見的 Picture 控制項,作為按鈕移動的目標座標錨點,配合所有按鈕統一寬高,大幅簡化座標計算。
void CMyQQDlg::OnDblclkMyFriend(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
int nSel = pNMListView->iItem;
CString strName = m_List1.GetItemText(nSel, 0);
CQQSendMessage* pDlg = new CQQSendMessage;
pDlg->msg = strName;
pDlg->Create(IDD_QQ_MESSAGE); // 以模板建立無模式對話框
pDlg->SetWindowText(_T("你正在與 ") + strName + _T(" 聊天中"));
pDlg->SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE);
pDlg->ShowWindow(SW_SHOW);
*pResult = 0;
}
在訊息對話框類別中重寫 PreTranslateMessage 攔截鍵盤訊息:
BOOL CQQSendMessage::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN) {
if (pMsg->wParam == VK_RETURN && (GetKeyState(VK_CONTROL) & 0x8000)) {
OnSend(); // 呼叫發送處理函數
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
QQ 主面板可「掛」在螢幕頂端,僅露一條細線;滑鼠靠近即展開,移開自動收合。核心邏輯:
WM_TIMER 定期檢測滑鼠座標(GetCursorPos)。InflateRect 擴大判定範圍)。MoveWindow(..., 3, TRUE))。void CMyQQDlg::OnTimer(UINT nIDEvent)
{
if (nIDEvent == 1) // 滑鼠離開偵測
{
POINT pt; GetCursorPos(&pt);
CRect rc; GetWindowRect(&rc);
rc.InflateRect(10, 10); // 擴大 10 像素判定區
if (!rc.PtInRect(pt)) {
KillTimer(1);
m_hiding = TRUE; // 進入收合狀態
SetTimer(2, 5, NULL); // 啟動動畫計時器
}
}
else if (nIDEvent == 2) // 動畫幀
{
m_hiding ? DoHide() : DoShow();
}
CDialog::OnTimer(nIDEvent);
}
本系列完整實現了 QQ 2005 風格的主面板、抽屜選單、動態聊天視窗、熱鍵發送、桌面吸附等核心體驗。完整原始碼請至知識庫下載,歡迎交流指正:xuwenq88@126.com。