环境:Desktop Qt 5.4.1 MSVC2013 32bit
需要的库:dwmapi.lib
、user32.lib
需要头文件:<dwmapi.h>
、<windowsx.h>
在要处理的QWidget
构造函数中,添加以下两行:
1 2
|
setWindowFlags(Qt::Window | Qt::FramelessWindowHint); SetWidgetBorderless(this);
|
SetWidgetBorderless
的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
void SetWidgetBorderless(const QWidget *widget) { #ifdef Q_OS_WIN HWND hwnd = reinterpret_cast<HWND>(widget->winId());
const LONG style = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN ); SetWindowLongPtr(hwnd, GWL_STYLE, style);
const MARGINS shadow = {1, 1, 1, 1}; DwmExtendFrameIntoClientArea(hwnd, &shadow);
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); #endif }
|
这个函数的作用是给无边框窗口加上阴影、Aero Snap以及其他动画特效。
这时窗口还无法手动更改大小,需要更改的话,需要自己实现一个QAbstractNativeEventFilter
类,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
class NativeEventFilter : public QAbstractNativeEventFilter { public: bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE { #ifdef Q_OS_WIN if (eventType != "windows_generic_MSG") return false;
MSG* msg = static_cast<MSG*>(message); QWidget* widget = QWidget::find(reinterpret_cast<WId>(msg->hwnd)); if (!widget) return false;
switch (msg->message) { case WM_NCCALCSIZE: { *result = 0; return true; }
case WM_NCHITTEST: { const LONG borderWidth = 9; RECT winrect; GetWindowRect(msg->hwnd, &winrect); long x = GET_X_LPARAM(msg->lParam); long y = GET_Y_LPARAM(msg->lParam);
// bottom left if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) { *result = HTBOTTOMLEFT; return true; }
// bottom right if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth) { *result = HTBOTTOMRIGHT; return true; }
// top left if (x >= winrect.left && x < winrect.left + borderWidth && y >= winrect.top && y < winrect.top + borderWidth) { *result = HTTOPLEFT; return true; }
// top right if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth) { *result = HTTOPRIGHT; return true; }
// left if (x >= winrect.left && x < winrect.left + borderWidth) { *result = HTLEFT; return true; }
// right if (x < winrect.right && x >= winrect.right - borderWidth) { *result = HTRIGHT; return true; }
// bottom if (y < winrect.bottom && y >= winrect.bottom - borderWidth) { *result = HTBOTTOM; return true; }
// top if (y >= winrect.top && y < winrect.top + borderWidth) { *result = HTTOP; return true; }
return false; } default: break; }
return false; #else return false; #endif } };
|
然后在窗口创建之前,使用QApplication::installNativeEventFilter
方法把监听器注册给主程序。
要手动移动窗口位置的话,还要重截QWidget::mousePressEvent
方法:
1 2 3 4 5 6 7 8 9 10
|
void MyWidget::mousePressEvent(QMouseEvent *ev) { QWidget::mousePressEvent(ev); if (!ev->isAccepted()) { #ifdef Q_OS_WIN ReleaseCapture(); SendMessage(reinterpret_cast<HWND>(winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0); } #endif }
|
在实际操作时,有时还需要添加最大化和关闭按扭,这时正常调用QWidget::showMaximized()
和QWidget::close()
等Qt自带方法即可。
最终实现效果大概是这样:
缩放动画、阴影什么的和Windows本地窗口一样。
参考链接:https://github.com/deimos1877/BorderlessWindow
https://blog.yeatse.com/2015/03/01/qt-frameless-window/