Qt沒有真正完美的無邊框解決方案嗎?

嘗試過很多種方案,都無法真正完美的實現無邊框方案,

尤其是用nativeEvent方法的時候,在WM_GETMINMAXINFO里,一定要寫成這樣

case WM_GETMINMAXINFO: {

if (::IsZoomed(msg-&>hwnd)) {
RECT frame = { 0, 0, 0, 0 };
AdjustWindowRectEx(frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
frame.left = abs(frame.left);
frame.top = abs(frame.bottom);
this-&>setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);

qDebug("%d,%d,%d,%d",frame.left,frame.top,frame.right, frame.bottom);

}
else {

//但是這樣的話,你會發現,從最大化到雙擊回歸默認的大小的時候,
//窗口會有一下抖動。這是因為setContentsMargins方法導致的。
//難道真的無解了么?
this-&>setContentsMargins(0, 0, 0, 0);

}
*result = ::DefWindowProc(msg-&>hwnd, msg-&>message, msg-&>wParam, msg-&>lParam);
break;


GacUI 完美地解決了這個問題,不過我用的是不一樣的思路。我的做法是,如果需要無邊框,那我其實不去管什麼content margin,我去改WM_NCHITTEST。這個消息改了之後,你就需要處理一大堆關於WM_NC*、WM_SYNCPAINT之類的事件。這些事件跟非NC和非SYNC版本的處理方法是類似的,但是你得好好調整一下滑鼠的坐標信息,讓最後窗口類的事件響應函數覺得那就是一些正常的事件而不是NC或者SYNC事件。

然後就是繪圖的部分,你把系統的邊框繪製攔截下來,換成你自己的圖案就好了。這樣做出來的無邊框將會在各個方面與操作系統完美貼合,以後系統的功能改了(譬如說Windows 7加上了拖到左邊最大化一半屏幕),你的程序也會跟著改。

不過我相信這種改法QT估計做不了,因為這需要從一開始設計框架的時候就照顧到(逃


幾乎完美!

開源項目Github地址:Bringer-of-Light/Qt-Nice-Frameless-Window

注意:最新實現比下面所寫的內容更完善,具體請以Github上的為準。

===============以下為原回答===============

windows系統實現無邊框,同時支持Aero效果:

1.邊框都沒了,標題欄肯定也沒了,三個系統按鈕也沒了,所以需要自己實現最小化、最大化、關閉 三個按鈕。

2.需要自己實現滑鼠拖動窗口效果。原理很簡單,重載mousePressEvent、mouseReleaseEvent、mouseMoveEvent 這三個事件處理函數,自己處理滑鼠拖動邏輯

3.MainWindows構造函數里,使用以下代碼:

setWindowFlags(Qt::Window |
Qt::FramelessWindowHint |
Qt::WindowSystemMenuHint |
Qt::WindowMinMaxButtonsHint
);

SetWindowLongPtr(HWND(winId()), GWL_STYLE, WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX );
//把client區域擴展到標題欄上和邊框上,只留1個像素的餘量
const MARGINS shadow = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(HWND(winId()), shadow);

4.重載nativeEvent函數,其中代碼如下

MSG* msg = (MSG *)message;
switch (msg-&>message)
{
case WM_NCCALCSIZE:
{
//this kills the window frame and title bar we added with
//WS_THICKFRAME and WS_CAPTION
*result = 0;
return true;
}
case WM_NCHITTEST:
{
*result = 0;
const LONG border_width = 5; //in pixels
RECT winrect;
GetWindowRect(HWND(winId()), winrect);

long x = GET_X_LPARAM(msg-&>lParam);
long y = GET_Y_LPARAM(msg-&>lParam);

bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();

if(resizeWidth)
{
//left border
if (x &>= winrect.left x &< winrect.left + border_width) { *result = HTLEFT; } //right border if (x &< winrect.right x &>= winrect.right - border_width)
{
*result = HTRIGHT;
}
}
if(resizeHeight)
{
//bottom border
if (y &< winrect.bottom y &>= winrect.bottom - border_width)
{
*result = HTBOTTOM;
}
//top border
if (y &>= winrect.top y &< winrect.top + border_width) { *result = HTTOP; } } if(resizeWidth resizeHeight) { //bottom left corner if (x &>= winrect.left x &< winrect.left + border_width y &< winrect.bottom y &>= winrect.bottom - border_width)
{
*result = HTBOTTOMLEFT;
}
//bottom right corner
if (x &< winrect.right x &>= winrect.right - border_width
y &< winrect.bottom y &>= winrect.bottom - border_width)
{
*result = HTBOTTOMRIGHT;
}
//top left corner
if (x &>= winrect.left x &< winrect.left + border_width y &>= winrect.top y &< winrect.top + border_width) { *result = HTTOPLEFT; } //top right corner if (x &< winrect.right x &>= winrect.right - border_width
y &>= winrect.top y &< winrect.top + border_width) { *result = HTTOPRIGHT; } } //*result仍然等於0,說明滑鼠位置不在上述的邊框範圍內,則還有可能處於標題欄範圍內 //標題欄效果(win7 及以上): //1. 雙擊標題欄,窗口在最大化、正常化之間切換 //2. 拖動標題欄時,可以移動窗口,移動時如果滑鼠觸碰到桌面頂端,則最大化窗口等類似效果 if(*result==0) { //此處可以定義一個標題欄範圍 //也可以認為整個窗口都是標題欄 *result = HTCAPTION ; //注意:唯一遺留難題(此問題已解決,請到開源項目中查看) //此處如果返回false,則上述所有操作無效,不能實現標題欄效果 //此處如果返回true,則實現標題欄效果,但窗口內的所有控制項,都收不到滑鼠點擊消息。 //欲解決此問題,需要對Qt Event System 有更深刻的了解 return false; } return true; } //end case WM_NCHITTEST default: return QMainWindow::nativeEvent(eventType, msg, result); }

5. 需要的庫

#ifdef Q_OS_WIN
#include &
#include &
#include &
#include &
#include & // Fixes error C2504: "IUnknown" : base class undefined
#include &
#include &
#pragma comment (lib,"Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved external symbol __imp__DwmExtendFrameIntoClientArea
#pragma comment (lib,"user32.lib")
#endif

OSX 系統實現無邊框,同時保留最小化、最大化、關閉按鈕和窗口陰影、窗口圓角:

1. 需要使用C++ 和 object-c 混合編程,參考Mixing Objective-C, C++ and Objective-C++: an Updated Summary

2.需要自己實現滑鼠拖動窗口行為,和上述第2條相同。

3.MainWindow構造函數中使用以下代碼:

//如果當前osx版本老於10.9,則後續代碼不可用。轉為使用自定義的最小化等按鈕,不支持自由縮放窗口及窗口陰影
if (QSysInfo::MV_None == QSysInfo::macVersion())
{
if (QSysInfo::MV_None == QSysInfo::MacintoshVersion) {setWindowFlags(Qt::FramelessWindowHint); return;}
}
if (QSysInfo::MV_10_9 &>= QSysInfo::MacintoshVersion) {setWindowFlags(Qt::FramelessWindowHint); return;}

//以下實現隱藏標題欄,但同時仍保留系統按鈕、可變大小邊框、圓角矩形、窗口陰影等 默認效果
//獲取view所在的window
NSView* view = (NSView*)winId();
if (0 == view) {setWindowFlags(Qt::FramelessWindowHint); return;}
NSWindow *window = view.window;
if (0 == window) {setWindowFlags(Qt::FramelessWindowHint); return;}

//設置標題文字和圖標為不可見
window.titleVisibility = NSWindowTitleHidden; //MAC_10_10及以上版本支持
//設置標題欄為透明
window.titlebarAppearsTransparent = YES; //MAC_10_10及以上版本支持
//設置不可由標題欄拖動,避免與自定義拖動衝突
[window setMovable:NO]; //MAC_10_6及以上版本支持
//window.movableByWindowBackground = YES;
//設置view擴展到標題欄
window.styleMask |= NSFullSizeContentViewWindowMask; //MAC_10_10及以上版本支持

4. 上述方法只支持OSX 10.10及以後版本。老版本仍需要自己實現最小化等按鈕。

5. 使用上述方法後,窗口關閉按鈕是系統提供的,但仍可以重載其行為。參考

stackoverflow: Setting C function as selector for NSButton produces no results

Apple Developer: Target-Action


完全沒問題,該有的特性全都有,windows下表現差不多完美,見https://github.com/JackyDing/QtFlex5


@各位

大家用心去發現一下吧, setContentMargins這個閃爍問題。

具體也可以去看一下 @荷光者 的實現,他實現的功能,我也都實現了,但是同樣有setContentMargins問題。

但是 @落山 的QtFlex5 的確是寫的很不錯(當然BUG有好幾個)!思路設計的很好!贊一個。值得我們學習研究。

==========================================

補充一下:

用Qt寫的幾個Frameless效果的大項目都存在這個問題(YY、Telegram、為知筆記)都有這個問題,但是WPS好像沒有。WPS團隊還是吊吊的啊!


你覺得什麼樣是完美的無邊框呢?

  1. 去掉系統邊框 setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
  2. layered window設置1後 setAttribute(Qt::WA_TranslucentBackground);
  3. 處理WM_NCCALCSIZE, wParam為1時設置處理此消息result返回0
  4. 處理WM_NCHITTEST,實現邊框拖拽大小和拖動窗口
  5. 如果需要系統陰影 MARGINS m{ 0, 0, 0, 1 };DwmExtendFrameIntoClientArea(winId(), m);
  6. 如果自己實現陰影,使用layered window, 處理WM_GETMINMAXINFO消息設置最大化時的坐標尺寸

@vczh

你這個bug改了么?


QT是可以做到的~~

這是用QDialog實現的,如果用QMainWindow,菜單欄和工具欄會一併消失,但是也可以自己再定義~代碼如下:

setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);


有, 在 github 上這樣的項目很多。

起碼 telegram 就是。

只是你沒用心找,想做個伸手黨。


推薦閱讀:

知乎上看到一些人評價c++的exception很難用,想問一下大家寫c++時怎麼處理錯誤?
相同的時間複雜度下,為什麼 C# 運行速度 比 C++ 快?
用樹創建一個家譜,哪種表示法比較好?
在C++編程實踐中,我們是否應該放棄使用realloc這個函數?

TAG:QtC開發框架 | CC | QtProject |