Qt雖然提供了諸如Qt::FramelessWindowHint
之類的屬性可以移除窗體的邊框,但是移除邊框之後,窗體的一些默認行爲也被移除了,如鼠標拖動改變大小、雙擊標題欄最大化等,這些行爲需要開發者自己來實現。
本文主要介紹實現無邊框窗體的幾種方案。
筆者認爲,一個完美的無邊框窗體解決方案需要支持如下功能:
- 支持通過使用鼠標拖拽來改變窗體位置和大小;
- 支持雙擊標題欄最大化窗體和還原窗體;
- 支持
Windows Areo Snap
特性; - 支持系統陰影;
- 支持跨不同DPI的屏幕拖拽;
- 適應分辨率和DPI改變;
在Qt中實現無邊框窗體有2種方案:
一、Hook方案
通過重載nativeEvent
函數攔截Windows消息(如WM_NCHITTEST
)來實現,大致步驟如下:
- 給窗體設置
WS_THICKFRAME | WS_CAPTION
屬性從而還原窗體邊框和標題欄,這樣窗體就可以接收到WM_NCHITTEST
消息。 - 在
WM_NCCALCSIZE
消息處理中再移除邊框和標題欄。 - 在
WM_NCHITTEST
消息處理中通過判斷鼠標位置來設置鼠標行爲(HTLEFT
,HTRIGHT
等)。
這種方案的優點是可以支持Windows Areo Snap
和系統陰影
的特性,但是針對Windows消息處理起來很複雜而且需要兼容Qt的各個版本,目前我還沒有找到一個通過這種方案來完美實現無邊框的解決方案。
據我所知,有如下的開源項目是通過這種方式來實現的,但都有些許問題,如不支持跨不同DPI屏幕拖拽、不能適應分辨率和DPI改變、WM_NCHITTEST
有時無響應等。另外,在設置了背景透明屬性之後(如Qt::WA_TranslucentBackground
),系統陰影特性也將消失。
- qtdevs/FramelessHelper: https://github.com/qtdevs/FramelessHelper
- wangwenx190/framelesshelper: https://github.com/wangwenx190/framelesshelper
二、 純Qt方案
這種方案不Hook windows的 WM_NCHITTEST
、WM_NCCALCSIZE
消息,也不改變窗體樣式,通過純Qt方式實現。通過對每個Widget設置MouseTracking
,來使每個Widget都可以響應鼠標事件(mouseMoveEvent、mousePressEvent、mouseReleaseEvent等),然後這些事件中判斷鼠標位置來設置鼠標的形狀和行爲。
這種方式雖然對鼠標位置的判斷邏輯比較繁瑣,但兼容性較好,較純粹,不需要處理Windows的各個消息。
👉 筆者根傾向於使用這種方案,簡單、穩定、兼容性好,不用關注那些令人頭疼的Windows消息。針對該方案的實現,可以參考筆者的開源項目【Qt-FramelessWindow】,目前可以支持上述除“Windows Areo Snap”和“系統陰影”
特性之外的所有無邊框窗體的特性。
- Qt-FramelessWindow: https://github.com/winsoft666/Qt-FramelessWindow