GitHub

Source codes

The default frameless window style of Qt will lose the native animation and window shadow in Windows system. This is due to it does not use Desktop Window Manager (DWM) APIs to enable borderless effect.

Demo

demo

How to enable borderless window with native animation and shadow

Here is a helper function enableWindowBorderlessWin that can be used to enable native shadow for a window:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void enableWindowBorderlessWin(qulonglong wId)
{
HWND wnd = (HWND)wId;
// enable borderless and keep aero effects.
SetWindowLongPtr(wnd, GWL_STYLE, static_cast<LONG>(Style::aero_borderless));

// enable shadow
const MARGINS shadow_on = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(wnd, &shadow_on);

//redraw frame
SetWindowPos(wnd, Q_NULLPTR, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(wnd, SW_SHOW);
}

How to setup window caption area and keep window border events

After we applying the helper function above, the window will lose its caption area and the window border events, we can set them by hooking the Windows native messages.

Here is nativeEventFilter to handle these issues:

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
bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
#ifdef Q_OS_WIN
if (eventType == "windows_generic_MSG") {
MSG* msg = static_cast<MSG *>(message);
if (msg == Q_NULLPTR)
return false;

switch(msg->message) {
case WM_COMMAND: {
SendMessage(msg->hwnd, WM_SYSCOMMAND, msg->wParam, msg->lParam);
*result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
return true;
}
case WM_NCCALCSIZE:{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
params.rgrc[0].top -= 1;

//this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION
*result = WVR_REDRAW;
return true;
}
case WM_NCHITTEST: {
const LONG borderWidth = 8; //in pixels
RECT winrect;
GetWindowRect(msg->hwnd, &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);

// caption, a.k.a. title bar
auto wndScaleFactor = qgetenv("QT_SCALE_FACTOR").toDouble();
int titleBarHeight = m_wndsParams[(WId)msg->hwnd]["TitleHeight"].toInt();
titleBarHeight = static_cast<int>(titleBarHeight * wndScaleFactor);
int titleBarBtnsWidth = m_wndsParams[(WId)msg->hwnd]["TitleBarButtonsArea"].toSize().width();
titleBarBtnsWidth = static_cast<int>(titleBarBtnsWidth * wndScaleFactor);
if (x >= winrect.left && x < winrect.right - titleBarBtnsWidth &&
y > winrect.top + borderWidth && y < winrect.top + titleBarHeight) {
*result = HTCAPTION;
return true;
}
//bottom left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth) {
*result = HTBOTTOMLEFT;
return true;
}
//bottom right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth) {
*result = HTBOTTOMRIGHT;
return true;
}
//top left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth) {
*result = HTTOPLEFT;
return true;
}
//top right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth) {
*result = HTTOPRIGHT;
return true;
}
//left border
if (x >= winrect.left && x < winrect.left + borderWidth) {
*result = HTLEFT;
return true;
}
//right border
if (x < winrect.right && x >= winrect.right - borderWidth) {
*result = HTRIGHT;
return true;
}
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - borderWidth) {
*result = HTBOTTOM;
return true;
}
//top border
if (y >= winrect.top && y < winrect.top + borderWidth) {
*result = HTTOP;
return true;
}
return false;
}
default:
break;
}
}
#endif

return false;
}

Comments

2020-08-10