Add more comments.
These are important memo, it's better to write them in our code. Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
63315acf75
commit
5335114258
|
@ -306,6 +306,8 @@ WNEF_GENERATE_WINAPI(BeginBufferedPaint, HPAINTBUFFER, HDC, CONST RECT *,
|
||||||
BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *)
|
BP_BUFFERFORMAT, BP_PAINTPARAMS *, HDC *)
|
||||||
WNEF_GENERATE_WINAPI(CreateRectRgnIndirect, HRGN, CONST RECT *)
|
WNEF_GENERATE_WINAPI(CreateRectRgnIndirect, HRGN, CONST RECT *)
|
||||||
|
|
||||||
|
// Some APIs are not available on old systems, so we will load them
|
||||||
|
// dynamically at run-time to get maximum compatibility.
|
||||||
void ResolveWin32APIs() {
|
void ResolveWin32APIs() {
|
||||||
static bool resolved = false;
|
static bool resolved = false;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
|
@ -606,9 +608,11 @@ RECT GetFrameSizeForWindow(HWND handle, bool includingTitleBar = false) {
|
||||||
rect.left = std::round(rect.left * dpr);
|
rect.left = std::round(rect.left * dpr);
|
||||||
rect.right = std::round(rect.right * dpr);
|
rect.right = std::round(rect.right * dpr);
|
||||||
}
|
}
|
||||||
// These are negative values. Make them positive.
|
// Some values may be negative. Make them positive unconditionally.
|
||||||
rect.top = -rect.top;
|
rect.top = std::abs(rect.top);
|
||||||
rect.left = -rect.left;
|
rect.bottom = std::abs(rect.bottom);
|
||||||
|
rect.left = std::abs(rect.left);
|
||||||
|
rect.right = std::abs(rect.right);
|
||||||
}
|
}
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
@ -763,7 +767,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
void *message, long *result)
|
void *message, long *result)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// The example code in Qt's documentation has this check.
|
// The example code in Qt's documentation has this check. I don't know
|
||||||
|
// whether we really need this check or not, but adding this check won't
|
||||||
|
// bring us harm anyway.
|
||||||
if (eventType == "windows_generic_MSG") {
|
if (eventType == "windows_generic_MSG") {
|
||||||
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
|
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
|
||||||
// Work-around a bug caused by typo which only exists in Qt 5.11.1
|
// Work-around a bug caused by typo which only exists in Qt 5.11.1
|
||||||
|
@ -788,6 +794,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
m_lpGetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
|
m_lpGetWindowLongPtrW(msg->hwnd, GWLP_USERDATA));
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// Work-around a long existing Windows bug.
|
// Work-around a long existing Windows bug.
|
||||||
|
// Overlapped windows will receive a WM_GETMINMAXINFO message before
|
||||||
|
// WM_NCCREATE. This is safe to ignore. It doesn't need any special
|
||||||
|
// handling anyway.
|
||||||
if (msg->message == WM_NCCREATE) {
|
if (msg->message == WM_NCCREATE) {
|
||||||
const auto userData =
|
const auto userData =
|
||||||
reinterpret_cast<LPCREATESTRUCTW>(msg->lParam)
|
reinterpret_cast<LPCREATESTRUCTW>(msg->lParam)
|
||||||
|
@ -809,13 +818,25 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
// Avoid initializing a same window twice.
|
// Avoid initializing a same window twice.
|
||||||
data->initialized = TRUE;
|
data->initialized = TRUE;
|
||||||
// Restore default window style.
|
// Restore default window style.
|
||||||
|
// WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
|
||||||
|
// WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
|
||||||
|
// Apply the WS_OVERLAPPEDWINDOW window style to restore the window
|
||||||
|
// to a normal native Win32 window.
|
||||||
|
// Don't apply the Qt::FramelessWindowHint flag, it will add the
|
||||||
|
// WS_POPUP window style to the window, which will turn the window
|
||||||
|
// into a popup window, losing all the functions a normal window
|
||||||
|
// should have.
|
||||||
|
// WS_CLIPCHILDREN | WS_CLIPSIBLINGS: work-around strange bugs.
|
||||||
m_lpSetWindowLongPtrW(msg->hwnd, GWL_STYLE,
|
m_lpSetWindowLongPtrW(msg->hwnd, GWL_STYLE,
|
||||||
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
|
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN |
|
||||||
WS_CLIPSIBLINGS);
|
WS_CLIPSIBLINGS);
|
||||||
// The following two functions can help us get rid of the three
|
// The following two functions make our window become a layered
|
||||||
// system buttons (minimize, maximize and close). But they also
|
// window, which can bring us better performance and it can also
|
||||||
// break the Arcylic effect (introduced in Win10 1709), don't know
|
// help us get rid of the three system buttons (minimize, maximize
|
||||||
// why.
|
// and close). But they also break the Arcylic effect (introduced in
|
||||||
|
// Win10 1709), if you use the undocumented API
|
||||||
|
// SetWindowCompositionAttribute to enable it for this window, the
|
||||||
|
// whole window will become totally black. Don't know why currently.
|
||||||
m_lpSetWindowLongPtrW(msg->hwnd, GWL_EXSTYLE,
|
m_lpSetWindowLongPtrW(msg->hwnd, GWL_EXSTYLE,
|
||||||
WS_EX_APPWINDOW | WS_EX_LAYERED);
|
WS_EX_APPWINDOW | WS_EX_LAYERED);
|
||||||
m_lpSetLayeredWindowAttributes(msg->hwnd, RGB(255, 0, 255), 0,
|
m_lpSetLayeredWindowAttributes(msg->hwnd, RGB(255, 0, 255), 0,
|
||||||
|
@ -875,6 +896,32 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
// structure. On entry, the structure contains the proposed window
|
// structure. On entry, the structure contains the proposed window
|
||||||
// rectangle for the window. On exit, the structure should contain
|
// rectangle for the window. On exit, the structure should contain
|
||||||
// the screen coordinates of the corresponding window client area.
|
// the screen coordinates of the corresponding window client area.
|
||||||
|
// The client area is the window's content area, the non-client area
|
||||||
|
// is the area which is provided by the system, such as the title
|
||||||
|
// bar, the four window borders, the frame shadow, the menu bar, the
|
||||||
|
// status bar, the scroll bar, etc. But for Qt, it draws most of the
|
||||||
|
// window area (client + non-client) itself. We now know that the
|
||||||
|
// title bar and the window frame is in the non-client area and we
|
||||||
|
// can set the scope of the client area in this message, so we can
|
||||||
|
// remove the title bar and the window frame by let the non-client
|
||||||
|
// area be covered by the client area (because we can't really get
|
||||||
|
// rid of the non-client area, it will always be there, all we can
|
||||||
|
// do is to hide it) , which means we should let the client area's
|
||||||
|
// size the same with the whole window's size. So there is no room
|
||||||
|
// for the non-client area and then the user won't be able to see it
|
||||||
|
// again. But how to achieve this? Very easy, just leave lParam (the
|
||||||
|
// re-calculated client area) untouched. But of course you can
|
||||||
|
// modify lParam, then the non-client area will be seen and the
|
||||||
|
// window borders and the window frame will show up. However, things
|
||||||
|
// are quite different when you try to modify the top margin of the
|
||||||
|
// client area. DWM will always draw the whole title bar no matter
|
||||||
|
// what margin value you set for the top, unless you don't modify it
|
||||||
|
// and remove the whole top area (the title bar + the one pixel
|
||||||
|
// height window border). This can be confirmed in Windows
|
||||||
|
// Terminal's source code, you can also try yourself to verify
|
||||||
|
// it. So things will become quite complicated if you want to
|
||||||
|
// preserve the four window borders. So we just remove the whole
|
||||||
|
// window frame, otherwise the code will become much more complex.
|
||||||
const auto getClientAreaInsets = [](HWND _hWnd) -> RECT {
|
const auto getClientAreaInsets = [](HWND _hWnd) -> RECT {
|
||||||
// We don't need this correction when we're fullscreen. We will
|
// We don't need this correction when we're fullscreen. We will
|
||||||
// have the WS_POPUP size, so we don't have to worry about
|
// have the WS_POPUP size, so we don't have to worry about
|
||||||
|
@ -1077,6 +1124,43 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
// 候,能明显看到窗口周围多出来一圈边界。我曾经尝试再把那三个区域弄
|
// 候,能明显看到窗口周围多出来一圈边界。我曾经尝试再把那三个区域弄
|
||||||
// 透明,但无一例外都会破坏DWM绘制的边框阴影,因此只好作罢。
|
// 透明,但无一例外都会破坏DWM绘制的边框阴影,因此只好作罢。
|
||||||
|
|
||||||
|
// As you may have found, if you use this code, the resize areas
|
||||||
|
// will be inside the frameless window, however, a normal Win32
|
||||||
|
// window can be resized outside of it. Here is the reason: the
|
||||||
|
// WS_THICKFRAME window style will cause a window has three
|
||||||
|
// transparent areas beside the window's left, right and bottom
|
||||||
|
// edge. Their width or height is eight pixels if the window is not
|
||||||
|
// scaled. In most cases, they are totally invisible. It's DWM's
|
||||||
|
// responsibility to draw and control them. They exist to let the
|
||||||
|
// user resize the window, visually outside of it. They are in the
|
||||||
|
// window area, but not the client area, so they are in the
|
||||||
|
// non-client area actually. But we have turned the whole window
|
||||||
|
// area into client area in WM_NCCALCSIZE, so the three transparent
|
||||||
|
// resize areas also become a part of the client area and thus they
|
||||||
|
// become visible. When we resize the window, it looks like we are
|
||||||
|
// resizing inside of it, however, that's because the transparent
|
||||||
|
// resize areas are visible now, we ARE resizing outside of the
|
||||||
|
// window actually. But I don't know how to make them become
|
||||||
|
// transparent again without breaking the frame shadow drawn by DWM.
|
||||||
|
// If you really want to solve it, you can try to embed your window
|
||||||
|
// into a larger transparent window and draw the frame shadow
|
||||||
|
// yourself. As what we have said in WM_NCCALCSIZE, you can only
|
||||||
|
// remove the top area of the window, this will let us be able to
|
||||||
|
// resize outside of the window and don't need much process in this
|
||||||
|
// message, it looks like a perfect plan, however, the top border is
|
||||||
|
// missing due to the whole top area is removed, and it's very hard
|
||||||
|
// to bring it back because we have to use a trick in WM_PAINT
|
||||||
|
// (learned from Windows Terminal), but no matter what we do in
|
||||||
|
// WM_PAINT, it will always break the backing store mechanism of Qt,
|
||||||
|
// so actually we can't do it. And it's very difficult to do such
|
||||||
|
// things in NativeEventFilters as well. What's worse, if we really
|
||||||
|
// do this, the four window borders will become white and they look
|
||||||
|
// horrible in dark mode. This solution only supports Windows 10
|
||||||
|
// because the border width on Win10 is only one pixel, however it's
|
||||||
|
// eight pixels on Windows 7 so preserving the three window borders
|
||||||
|
// looks terrible on old systems. I'm testing this solution in
|
||||||
|
// another branch, if you are interested in it, you can give it a
|
||||||
|
// try.
|
||||||
if (data->windowData.mouseTransparent) {
|
if (data->windowData.mouseTransparent) {
|
||||||
// Mouse events will be passed to the parent window.
|
// Mouse events will be passed to the parent window.
|
||||||
*result = HTTRANSPARENT;
|
*result = HTTRANSPARENT;
|
||||||
|
@ -1178,7 +1262,8 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case WM_GETMINMAXINFO: {
|
case WM_GETMINMAXINFO: {
|
||||||
// Don't cover the taskbar when maximized.
|
// We can set the maximum and minimum size of the window in this
|
||||||
|
// message.
|
||||||
const MONITORINFO monitorInfo = GetMonitorInfoForWindow(msg->hwnd);
|
const MONITORINFO monitorInfo = GetMonitorInfoForWindow(msg->hwnd);
|
||||||
const RECT rcWorkArea = monitorInfo.rcWork;
|
const RECT rcWorkArea = monitorInfo.rcWork;
|
||||||
const RECT rcMonitorArea = monitorInfo.rcMonitor;
|
const RECT rcMonitorArea = monitorInfo.rcMonitor;
|
||||||
|
@ -1394,6 +1479,10 @@ void WinNativeEventFilter::setWindowGeometry(HWND handle, const int x,
|
||||||
if (handle && m_lpIsWindow(handle) && (x > 0) && (y > 0) && (width > 0) &&
|
if (handle && m_lpIsWindow(handle) && (x > 0) && (y > 0) && (width > 0) &&
|
||||||
(height > 0)) {
|
(height > 0)) {
|
||||||
const qreal dpr = GetDevicePixelRatioForWindow(handle);
|
const qreal dpr = GetDevicePixelRatioForWindow(handle);
|
||||||
|
// Why not use SetWindowPos? Actually we can, but MoveWindow
|
||||||
|
// sends the WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, WM_MOVE,
|
||||||
|
// WM_SIZE, and WM_NCCALCSIZE messages to the window.
|
||||||
|
// SetWindowPos only sends WM_WINDOWPOSCHANGED.
|
||||||
m_lpMoveWindow(handle, x, y, std::round(width * dpr),
|
m_lpMoveWindow(handle, x, y, std::round(width * dpr),
|
||||||
std::round(height * dpr), TRUE);
|
std::round(height * dpr), TRUE);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue