Qt無邊框窗體實現方案

Qt雖然提供了諸如Qt::FramelessWindowHint之類的屬性可以移除窗體的邊框,但是移除邊框之後,窗體的一些默認行爲也被移除了,如鼠標拖動改變大小、雙擊標題欄最大化等,這些行爲需要開發者自己來實現。

本文主要介紹實現無邊框窗體的幾種方案。

筆者認爲,一個完美的無邊框窗體解決方案需要支持如下功能:

  1. 支持通過使用鼠標拖拽來改變窗體位置和大小;
  2. 支持雙擊標題欄最大化窗體和還原窗體;
  3. 支持Windows Areo Snap特性;
  4. 支持系統陰影;
  5. 支持跨不同DPI的屏幕拖拽;
  6. 適應分辨率和DPI改變;

在Qt中實現無邊框窗體有2種方案:

一、Hook方案

通過重載nativeEvent函數攔截Windows消息(如WM_NCHITTEST)來實現,大致步驟如下:

  1. 給窗體設置WS_THICKFRAME | WS_CAPTION屬性從而還原窗體邊框和標題欄,這樣窗體就可以接收到WM_NCHITTEST消息。
  2. WM_NCCALCSIZE消息處理中再移除邊框和標題欄。
  3. WM_NCHITTEST消息處理中通過判斷鼠標位置來設置鼠標行爲(HTLEFT, HTRIGHT等)。

這種方案的優點是可以支持Windows Areo Snap系統陰影的特性,但是針對Windows消息處理起來很複雜而且需要兼容Qt的各個版本,目前我還沒有找到一個通過這種方案來完美實現無邊框的解決方案。

據我所知,有如下的開源項目是通過這種方式來實現的,但都有些許問題,如不支持跨不同DPI屏幕拖拽、不能適應分辨率和DPI改變、WM_NCHITTEST有時無響應等。另外,在設置了背景透明屬性之後(如Qt::WA_TranslucentBackground),系統陰影特性也將消失。

二、 純Qt方案

這種方案不Hook windows的 WM_NCHITTESTWM_NCCALCSIZE消息,也不改變窗體樣式,通過純Qt方式實現。通過對每個Widget設置MouseTracking,來使每個Widget都可以響應鼠標事件(mouseMoveEvent、mousePressEvent、mouseReleaseEvent等),然後這些事件中判斷鼠標位置來設置鼠標的形狀和行爲。

這種方式雖然對鼠標位置的判斷邏輯比較繁瑣,但兼容性較好,較純粹,不需要處理Windows的各個消息。

👉 筆者根傾向於使用這種方案,簡單、穩定、兼容性好,不用關注那些令人頭疼的Windows消息。針對該方案的實現,可以參考筆者的開源項目【Qt-FramelessWindow】,目前可以支持上述除“Windows Areo Snap”和“系統陰影”特性之外的所有無邊框窗體的特性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章