From 1ebead31484aef28d59274d5f718af483303ffef Mon Sep 17 00:00:00 2001 From: Altair Wei Date: Sun, 14 Nov 2021 22:31:02 +0800 Subject: [PATCH] sync with 1.x changes --- examples/mainwindow/mainwindow.cpp | 7 +- examples/quick/main.cpp | 2 +- src/core/framelesshelper.cpp | 235 +++++++++------------------ src/core/framelesshelper_win32.cpp | 231 +++++++++----------------- src/core/framelesshelper_windows.h | 14 +- src/core/framelesswindowsmanager.cpp | 12 +- src/core/framelesswindowsmanager.h | 2 +- src/core/utilities.cpp | 9 +- src/core/utilities.h | 4 +- src/core/utilities_win32.cpp | 73 +++++---- src/framelesshelper_global.h | 9 +- src/quick/framelessquickhelper.cpp | 2 +- 12 files changed, 217 insertions(+), 383 deletions(-) diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 49d2c0c..3928496 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -167,7 +167,12 @@ void MainWindow::paintEvent(QPaintEvent *event) {0, h, 0, 0} }; painter.save(); - painter.setPen({isActiveWindow() ? Qt::black : Qt::darkGray, 1}); + const ColorizationArea area = Utilities::getColorizationArea(); + const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder) + || (area == ColorizationArea::AllArea)); + const QColor borderColor = (isActiveWindow() ? (colorizedBorder ? Utilities::getColorizationColor() : Qt::black) : Qt::darkGray); + const auto borderThickness = static_cast(Utilities::getWindowVisibleFrameBorderThickness(winId())); + painter.setPen({borderColor, qMax(borderThickness, devicePixelRatioF())}); painter.drawLines(lines); painter.restore(); } diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index e941fe7..91242fa 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -22,7 +22,7 @@ * SOFTWARE. */ -#include "../../framelessquickhelper.h" +#include "quick/framelessquickhelper.h" #include #include #include diff --git a/src/core/framelesshelper.cpp b/src/core/framelesshelper.cpp index 586da14..c60a283 100644 --- a/src/core/framelesshelper.cpp +++ b/src/core/framelesshelper.cpp @@ -654,28 +654,6 @@ void FramelessHelper::handleResizeHandlerDblClicked() } #ifdef Q_OS_WIN -static inline bool shouldHaveWindowFrame() -{ - if (Utilities::shouldUseNativeTitleBar()) { - // We have to use the original window frame unconditionally if we - // want to use the native title bar. - return true; - } - const bool should = qEnvironmentVariableIsSet(Constants::kPreserveNativeFrameFlag); - const bool force = qEnvironmentVariableIsSet(Constants::kForcePreserveNativeFrameFlag); - if (should || force) { - if (force) { - return true; - } - if (should) { - // If you preserve the window frame on Win7~8.1, - // the window will have a terrible appearance. - return Utilities::isWin10OrGreater(); - } - } - return false; -} - /*! This function works like a eventFilter, return \c true means the event has been handled. */ @@ -783,11 +761,7 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event // preserve the four window borders. So we just remove the whole // window frame, otherwise the code will become much more complex. - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - - if (msg->wParam == FALSE) { + if (static_cast(msg->wParam) == FALSE) { *result = 0; return true; } @@ -803,20 +777,6 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event ) ); - if (shouldHaveWindowFrame()) { - // Store the original top before the default window proc - // applies the default frame. - const LONG originalTop = clientRect->top; - // Apply the default frame - const LRESULT ret = DefWindowProcW(msg->hwnd, WM_NCCALCSIZE, TRUE, msg->lParam); - if (ret != 0) { - *result = ret; - return true; - } - // Re-apply the original top from before the size of the - // default frame was applied. - clientRect->top = originalTop; - } bool nonClientAreaExists = false; // 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 @@ -833,11 +793,9 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event const int resizeBorderThickness = Utilities::getSystemMetric( window, SystemMetric::ResizeBorderThickness, true, true); clientRect->top += resizeBorderThickness; - if (!shouldHaveWindowFrame()) { - clientRect->bottom -= resizeBorderThickness; - clientRect->left += resizeBorderThickness; - clientRect->right -= resizeBorderThickness; - } + clientRect->bottom -= resizeBorderThickness; + clientRect->left += resizeBorderThickness; + clientRect->right -= resizeBorderThickness; nonClientAreaExists = true; } // Attempt to detect if there's an autohide taskbar, and if @@ -977,17 +935,13 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event // area. case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWFRAME: { - if (shouldHaveWindowFrame()) { - break; - } else { - *result = 0; - return true; - } + *result = 0; + return true; } case WM_NCPAINT: { // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 - if (!Utilities::isDwmCompositionAvailable() && !shouldHaveWindowFrame()) { + if (!Utilities::isDwmCompositionAvailable()) { // Only block WM_NCPAINT when DWM composition is disabled. If // it's blocked when DWM composition is enabled, the frame // shadow won't be drawn. @@ -998,26 +952,22 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event } } case WM_NCACTIVATE: { - if (shouldHaveWindowFrame()) { - break; + if (Utilities::isDwmCompositionAvailable()) { + // DefWindowProc won't repaint the window border if lParam + // (normally a HRGN) is -1. See the following link's "lParam" + // section: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate + // Don't use "*result = 0" otherwise the window won't respond + // to the window active state change. + *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); } else { - if (Utilities::isDwmCompositionAvailable()) { - // DefWindowProc won't repaint the window border if lParam - // (normally a HRGN) is -1. See the following link's "lParam" - // section: - // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate - // Don't use "*result = 0" otherwise the window won't respond - // to the window active state change. - *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); + if (static_cast(msg->wParam) == FALSE) { + *result = TRUE; } else { - if (msg->wParam == FALSE) { - *result = TRUE; - } else { - *result = FALSE; - } + *result = FALSE; } - return true; } + return true; } case WM_NCHITTEST: { // 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口 @@ -1085,10 +1035,6 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event // another branch, if you are interested in it, you can give it a // try. - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)}; if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient")); @@ -1108,108 +1054,72 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event bool isTitleBar = isInTitlebarArea(QPoint(qRound(localMouse.x() / scaleFactor), qRound(localMouse.y() / scaleFactor))); - /* - if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) { - isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight) - && (localMouse.x() >= 0) && (localMouse.x() <= windowWidth) - && !Utilities::isHitTestVisibleInChrome(window); - } - if (window->windowState() == Qt::WindowNoState) { - isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight) - && (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness)) - && !Utilities::isHitTestVisibleInChrome(window); - } - */ - const bool isTop = localMouse.y() <= resizeBorderThickness; - if (shouldHaveWindowFrame()) { - // This will handle the left, right and bottom parts of the frame - // because we didn't change them. - const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam); - if (originalRet != HTCLIENT) { - *result = originalRet; - return true; - } - // At this point, we know that the cursor is inside the client area - // so it has to be either the little border at the top of our custom - // title bar or the drag bar. Apparently, it must be the drag bar or - // the little border at the top which the user can use to move or - // resize the window. - if (!IsMaximized(msg->hwnd) && isTop) { - *result = HTTOP; - return true; - } - if (isTitleBar) { - *result = HTCAPTION; - return true; - } - *result = HTCLIENT; - return true; - } else { - const LRESULT hitTestResult = [ - clientRect, msg, isTitleBar, &localMouse, - resizeBorderThickness, windowWidth, isTop, - window, scaleFactor] { - if (IsMaximized(msg->hwnd)) { - if (isTitleBar) { - return HTCAPTION; - } - return HTCLIENT; - } - const LONG windowHeight = clientRect.bottom; - const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness)); - // Make the border a little wider to let the user easy to resize on corners. - const qreal factor = (isTop || isBottom) ? 2.0 : 1.0; - const bool isLeft = (localMouse.x() <= qRound(static_cast(resizeBorderThickness) * factor)); - const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast(resizeBorderThickness) * factor))); - const bool fixedSize = Utilities::isWindowFixedSize(window); - const auto getBorderValue = [fixedSize](int value) -> int { - return fixedSize ? HTCLIENT : value; - }; - if (isTop) { - if (isLeft) { - return getBorderValue(HTTOPLEFT); - } - if (isRight) { - return getBorderValue(HTTOPRIGHT); - } - return getBorderValue(HTTOP); - } - if (isBottom) { - if (isLeft) { - return getBorderValue(HTBOTTOMLEFT); - } - if (isRight) { - return getBorderValue(HTBOTTOMRIGHT); - } - return getBorderValue(HTBOTTOM); - } - if (isLeft) { - return getBorderValue(HTLEFT); - } - if (isRight) { - return getBorderValue(HTRIGHT); - } + const LRESULT hitTestResult = [ + clientRect, msg, isTitleBar, &localMouse, + resizeBorderThickness, windowWidth, isTop, + window, scaleFactor] { + if (IsMaximized(msg->hwnd)) { if (isTitleBar) { return HTCAPTION; } return HTCLIENT; - }(); - *result = hitTestResult; - return true; - } + } + const LONG windowHeight = clientRect.bottom; + const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness)); + // Make the border a little wider to let the user easy to resize on corners. + const qreal factor = (isTop || isBottom) ? 2.0 : 1.0; + const bool isLeft = (localMouse.x() <= qRound(static_cast(resizeBorderThickness) * factor)); + const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast(resizeBorderThickness) * factor))); + const bool fixedSize = Utilities::isWindowFixedSize(window); + const auto getBorderValue = [fixedSize](int value) -> int { + return fixedSize ? HTCLIENT : value; + }; + if (isTop) { + if (isLeft) { + return getBorderValue(HTTOPLEFT); + } + if (isRight) { + return getBorderValue(HTTOPRIGHT); + } + return getBorderValue(HTTOP); + } + if (isBottom) { + if (isLeft) { + return getBorderValue(HTBOTTOMLEFT); + } + if (isRight) { + return getBorderValue(HTBOTTOMRIGHT); + } + return getBorderValue(HTBOTTOM); + } + if (isLeft) { + return getBorderValue(HTLEFT); + } + if (isRight) { + return getBorderValue(HTRIGHT); + } + if (isTitleBar) { + return HTCAPTION; + } + return HTCLIENT; + }(); + *result = hitTestResult; + return true; } case WM_SETICON: case WM_SETTEXT: { - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - // Disable painting while these messages are handled to prevent them // from drawing a window caption over the client area. + SetLastError(ERROR_SUCCESS); const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); + if (oldStyle == 0) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); + break; + } // Prevent Windows from drawing the default title bar by temporarily // toggling the WS_VISIBLE style. + SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast(oldStyle & ~WS_VISIBLE)) == 0) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; @@ -1217,6 +1127,7 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event const auto winId = reinterpret_cast(msg->hwnd); Utilities::triggerFrameChange(winId); const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); + SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; diff --git a/src/core/framelesshelper_win32.cpp b/src/core/framelesshelper_win32.cpp index 46847f9..a45e55b 100644 --- a/src/core/framelesshelper_win32.cpp +++ b/src/core/framelesshelper_win32.cpp @@ -32,28 +32,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE -static inline bool shouldHaveWindowFrame() -{ - if (Utilities::shouldUseNativeTitleBar()) { - // We have to use the original window frame unconditionally if we - // want to use the native title bar. - return true; - } - const bool should = qEnvironmentVariableIsSet(Constants::kPreserveNativeFrameFlag); - const bool force = qEnvironmentVariableIsSet(Constants::kForcePreserveNativeFrameFlag); - if (should || force) { - if (force) { - return true; - } - if (should) { - // If you preserve the window frame on Win7~8.1, - // the window will have a terrible appearance. - return Utilities::isWin10OrGreater(); - } - } - return false; -} - struct FramelessHelperWinData { bool create() { @@ -247,29 +225,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // preserve the four window borders. So we just remove the whole // window frame, otherwise the code will become much more complex. - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - - if (msg->wParam == FALSE) { + if (static_cast(msg->wParam) == FALSE) { *result = 0; return true; } const auto clientRect = &(reinterpret_cast(msg->lParam)->rgrc[0]); - if (shouldHaveWindowFrame()) { - // Store the original top before the default window proc - // applies the default frame. - const LONG originalTop = clientRect->top; - // Apply the default frame - const LRESULT ret = DefWindowProcW(msg->hwnd, WM_NCCALCSIZE, TRUE, msg->lParam); - if (ret != 0) { - *result = ret; - return true; - } - // Re-apply the original top from before the size of the - // default frame was applied. - clientRect->top = originalTop; - } bool nonClientAreaExists = false; // 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 @@ -281,13 +241,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // then the window is clipped to the monitor so that the resize handle // do not appear because you don't need them (because you can't resize // a window when it's maximized unless you restore it). - const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, false, true); + const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true); clientRect->top += resizeBorderThickness; - if (!shouldHaveWindowFrame()) { - clientRect->bottom -= resizeBorderThickness; - clientRect->left += resizeBorderThickness; - clientRect->right -= resizeBorderThickness; - } + clientRect->bottom -= resizeBorderThickness; + clientRect->left += resizeBorderThickness; + clientRect->right -= resizeBorderThickness; nonClientAreaExists = true; } // Attempt to detect if there's an autohide taskbar, and if @@ -427,17 +385,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // area. case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWFRAME: { - if (shouldHaveWindowFrame()) { - break; - } else { - *result = 0; - return true; - } + *result = 0; + return true; } case WM_NCPAINT: { // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 - if (!Utilities::isDwmCompositionAvailable() && !shouldHaveWindowFrame()) { + if (!Utilities::isDwmCompositionAvailable()) { // Only block WM_NCPAINT when DWM composition is disabled. If // it's blocked when DWM composition is enabled, the frame // shadow won't be drawn. @@ -448,26 +402,22 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } } case WM_NCACTIVATE: { - if (shouldHaveWindowFrame()) { - break; + if (Utilities::isDwmCompositionAvailable()) { + // DefWindowProc won't repaint the window border if lParam + // (normally a HRGN) is -1. See the following link's "lParam" + // section: + // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate + // Don't use "*result = 0" otherwise the window won't respond + // to the window active state change. + *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); } else { - if (Utilities::isDwmCompositionAvailable()) { - // DefWindowProc won't repaint the window border if lParam - // (normally a HRGN) is -1. See the following link's "lParam" - // section: - // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate - // Don't use "*result = 0" otherwise the window won't respond - // to the window active state change. - *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); + if (static_cast(msg->wParam) == FALSE) { + *result = TRUE; } else { - if (msg->wParam == FALSE) { - *result = TRUE; - } else { - *result = FALSE; - } + *result = FALSE; } - return true; } + return true; } case WM_NCHITTEST: { // 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口 @@ -535,10 +485,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // another branch, if you are interested in it, you can give it a // try. - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)}; if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient")); @@ -557,99 +503,76 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) { isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight) && (localMouse.x() >= 0) && (localMouse.x() <= windowWidth) - && !Utilities::isHitTestVisibleInChrome(window); + && !Utilities::isHitTestVisible(window); } if (window->windowState() == Qt::WindowNoState) { isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight) && (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness)) - && !Utilities::isHitTestVisibleInChrome(window); + && !Utilities::isHitTestVisible(window); } const bool isTop = localMouse.y() <= resizeBorderThickness; - if (shouldHaveWindowFrame()) { - // This will handle the left, right and bottom parts of the frame - // because we didn't change them. - const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam); - if (originalRet != HTCLIENT) { - *result = originalRet; - return true; - } - // At this point, we know that the cursor is inside the client area - // so it has to be either the little border at the top of our custom - // title bar or the drag bar. Apparently, it must be the drag bar or - // the little border at the top which the user can use to move or - // resize the window. - if (!IsMaximized(msg->hwnd) && isTop) { - *result = HTTOP; - return true; - } - if (isTitleBar) { - *result = HTCAPTION; - return true; - } - *result = HTCLIENT; - return true; - } else { - const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{ - if (IsMaximized(msg->hwnd)) { - if (isTitleBar) { - return HTCAPTION; - } - return HTCLIENT; - } - const LONG windowHeight = clientRect.bottom; - const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness)); - // Make the border a little wider to let the user easy to resize on corners. - const qreal factor = (isTop || isBottom) ? 2.0 : 1.0; - const bool isLeft = (localMouse.x() <= qRound(static_cast(resizeBorderThickness) * factor)); - const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast(resizeBorderThickness) * factor))); - const bool fixedSize = Utilities::isWindowFixedSize(window); - const auto getBorderValue = [fixedSize](int value) -> int { - return fixedSize ? HTCLIENT : value; - }; - if (isTop) { - if (isLeft) { - return getBorderValue(HTTOPLEFT); - } - if (isRight) { - return getBorderValue(HTTOPRIGHT); - } - return getBorderValue(HTTOP); - } - if (isBottom) { - if (isLeft) { - return getBorderValue(HTBOTTOMLEFT); - } - if (isRight) { - return getBorderValue(HTBOTTOMRIGHT); - } - return getBorderValue(HTBOTTOM); - } - if (isLeft) { - return getBorderValue(HTLEFT); - } - if (isRight) { - return getBorderValue(HTRIGHT); - } + const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{ + if (IsMaximized(msg->hwnd)) { if (isTitleBar) { return HTCAPTION; } return HTCLIENT; - }(); - *result = hitTestResult; - return true; - } + } + const LONG windowHeight = clientRect.bottom; + const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness)); + // Make the border a little wider to let the user easy to resize on corners. + const qreal factor = (isTop || isBottom) ? 2.0 : 1.0; + const bool isLeft = (localMouse.x() <= qRound(static_cast(resizeBorderThickness) * factor)); + const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast(resizeBorderThickness) * factor))); + const bool fixedSize = Utilities::isWindowFixedSize(window); + const auto getBorderValue = [fixedSize](int value) -> int { + return fixedSize ? HTCLIENT : value; + }; + if (isTop) { + if (isLeft) { + return getBorderValue(HTTOPLEFT); + } + if (isRight) { + return getBorderValue(HTTOPRIGHT); + } + return getBorderValue(HTTOP); + } + if (isBottom) { + if (isLeft) { + return getBorderValue(HTBOTTOMLEFT); + } + if (isRight) { + return getBorderValue(HTBOTTOMRIGHT); + } + return getBorderValue(HTBOTTOM); + } + if (isLeft) { + return getBorderValue(HTLEFT); + } + if (isRight) { + return getBorderValue(HTRIGHT); + } + if (isTitleBar) { + return HTCAPTION; + } + return HTCLIENT; + }(); + *result = hitTestResult; + return true; } case WM_SETICON: case WM_SETTEXT: { - if (Utilities::shouldUseNativeTitleBar()) { - break; - } - // Disable painting while these messages are handled to prevent them // from drawing a window caption over the client area. + SetLastError(ERROR_SUCCESS); const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); + if (oldStyle == 0) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); + break; + } // Prevent Windows from drawing the default title bar by temporarily // toggling the WS_VISIBLE style. + SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast(oldStyle & ~WS_VISIBLE)) == 0) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; @@ -657,6 +580,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const auto winId = reinterpret_cast(msg->hwnd); Utilities::triggerFrameChange(winId); const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); + SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; @@ -665,15 +589,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me *result = ret; return true; } - case WM_SIZE: { - const bool normal = (msg->wParam == SIZE_RESTORED); - const bool max = (msg->wParam == SIZE_MAXIMIZED); - const bool full = (window->windowState() == Qt::WindowFullScreen); - if (normal || max || full) { - Utilities::updateFrameMargins(reinterpret_cast(msg->hwnd), (max || full)); - Utilities::updateQtFrameMargins(const_cast(window), true); - } - } break; default: break; } diff --git a/src/core/framelesshelper_windows.h b/src/core/framelesshelper_windows.h index 990d28c..a5eb4b3 100644 --- a/src/core/framelesshelper_windows.h +++ b/src/core/framelesshelper_windows.h @@ -90,11 +90,11 @@ #endif #ifndef GET_X_LPARAM -#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_X_LPARAM(lp) (static_cast(static_cast(LOWORD(lp)))) #endif #ifndef GET_Y_LPARAM -#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) +#define GET_Y_LPARAM(lp) (static_cast(static_cast(HIWORD(lp)))) #endif #ifndef IsMinimized @@ -105,13 +105,13 @@ #define IsMaximized(window) (IsZoomed(window) != FALSE) #endif -constexpr int kAutoHideTaskbarThickness = 2; // The thickness of an auto-hide taskbar in pixels +constexpr UINT kAutoHideTaskbarThickness = 2; // The thickness of an auto-hide taskbar in pixels constexpr char kDwmRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)"; constexpr char kPersonalizeRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"; -constexpr int kDefaultResizeBorderThicknessClassic = 4; -constexpr int kDefaultResizeBorderThicknessAero = 8; -constexpr int kDefaultCaptionHeight = 23; +constexpr UINT kDefaultResizeBorderThicknessClassic = 4; +constexpr UINT kDefaultResizeBorderThicknessAero = 8; +constexpr UINT kDefaultCaptionHeight = 23; -constexpr WORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37; +constexpr DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37; diff --git a/src/core/framelesswindowsmanager.cpp b/src/core/framelesswindowsmanager.cpp index c2874b2..3b98aec 100644 --- a/src/core/framelesswindowsmanager.cpp +++ b/src/core/framelesswindowsmanager.cpp @@ -38,7 +38,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE #ifdef FRAMELESSHELPER_USE_UNIX_VERSION -//Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix) +Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix) #endif void FramelessWindowsManager::addWindow(QWindow *window) @@ -51,7 +51,7 @@ void FramelessWindowsManager::addWindow(QWindow *window) QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); } #ifdef FRAMELESSHELPER_USE_UNIX_VERSION - //framelessHelperUnix()->removeWindowFrame(window); + framelessHelperUnix()->removeWindowFrame(window); #else FramelessHelperWin::addFramelessWindow(window); // Work-around a Win32 multi-monitor bug. @@ -62,7 +62,7 @@ void FramelessWindowsManager::addWindow(QWindow *window) #endif } -void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject *object, const bool value) +void FramelessWindowsManager::setHitTestVisible(QWindow *window, QObject *object, const bool value) { Q_ASSERT(window); Q_ASSERT(object); @@ -73,7 +73,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject qWarning() << object << "is not a QWidget or QQuickItem."; return; } - auto objList = qvariant_cast(window->property(Constants::kHitTestVisibleInChromeFlag)); + auto objList = qvariant_cast(window->property(Constants::kHitTestVisibleFlag)); if (value) { if (objList.isEmpty() || !objList.contains(object)) { objList.append(object); @@ -83,7 +83,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject objList.removeAll(object); } } - window->setProperty(Constants::kHitTestVisibleInChromeFlag, QVariant::fromValue(objList)); + window->setProperty(Constants::kHitTestVisibleFlag, QVariant::fromValue(objList)); } int FramelessWindowsManager::getResizeBorderThickness(const QWindow *window) @@ -165,7 +165,7 @@ void FramelessWindowsManager::removeWindow(QWindow *window) return; } #ifdef FRAMELESSHELPER_USE_UNIX_VERSION - //framelessHelperUnix()->bringBackWindowFrame(window); + framelessHelperUnix()->bringBackWindowFrame(window); #else FramelessHelperWin::removeFramelessWindow(window); #endif diff --git a/src/core/framelesswindowsmanager.h b/src/core/framelesswindowsmanager.h index 247686c..53b76e9 100644 --- a/src/core/framelesswindowsmanager.h +++ b/src/core/framelesswindowsmanager.h @@ -39,7 +39,7 @@ namespace FramelessWindowsManager FRAMELESSHELPER_API void addWindow(QWindow *window); FRAMELESSHELPER_API void removeWindow(QWindow *window); FRAMELESSHELPER_API bool isWindowFrameless(const QWindow *window); -FRAMELESSHELPER_API void setHitTestVisibleInChrome(QWindow *window, QObject *object, const bool value = true); +FRAMELESSHELPER_API void setHitTestVisible(QWindow *window, QObject *object, const bool value = true); FRAMELESSHELPER_API int getResizeBorderThickness(const QWindow *window); FRAMELESSHELPER_API void setResizeBorderThickness(QWindow *window, const int value); FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window); diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp index d67ed3e..bf9c530 100644 --- a/src/core/utilities.cpp +++ b/src/core/utilities.cpp @@ -49,11 +49,6 @@ QWindow *Utilities::findWindow(const WId winId) return nullptr; } -bool Utilities::shouldUseNativeTitleBar() -{ - return qEnvironmentVariableIsSet(Constants::kUseNativeTitleBarFlag); -} - bool Utilities::isWindowFixedSize(const QWindow *window) { Q_ASSERT(window); @@ -73,13 +68,13 @@ bool Utilities::isWindowFixedSize(const QWindow *window) return false; } -bool Utilities::isHitTestVisibleInChrome(const QWindow *window) +bool Utilities::isHitTestVisible(const QWindow *window) { Q_ASSERT(window); if (!window) { return false; } - const auto objs = qvariant_cast(window->property(Constants::kHitTestVisibleInChromeFlag)); + const auto objs = qvariant_cast(window->property(Constants::kHitTestVisibleFlag)); if (objs.isEmpty()) { return false; } diff --git a/src/core/utilities.h b/src/core/utilities.h index 56d1dd4..9ccb801 100644 --- a/src/core/utilities.h +++ b/src/core/utilities.h @@ -35,9 +35,8 @@ namespace Utilities FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue = false); FRAMELESSHELPER_API QWindow *findWindow(const WId winId); -FRAMELESSHELPER_API bool shouldUseNativeTitleBar(); FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window); -FRAMELESSHELPER_API bool isHitTestVisibleInChrome(const QWindow *window); +FRAMELESSHELPER_API bool isHitTestVisible(const QWindow *window); FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object); FRAMELESSHELPER_API QColor getColorizationColor(); FRAMELESSHELPER_API int getWindowVisibleFrameBorderThickness(const WId winId); @@ -55,7 +54,6 @@ FRAMELESSHELPER_API bool isDwmCompositionAvailable(); FRAMELESSHELPER_API void triggerFrameChange(const WId winId); FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset); FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable); -FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function, const HRESULT hr); FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function); #endif // Q_OS_WINDOWS diff --git a/src/core/utilities_win32.cpp b/src/core/utilities_win32.cpp index 4ccb667..0e898ab 100644 --- a/src/core/utilities_win32.cpp +++ b/src/core/utilities_win32.cpp @@ -69,6 +69,39 @@ static inline bool isWin1019H1OrGreater() return result; } +static inline QString __getSystemErrorMessage(const QString &function, const DWORD code) +{ + Q_ASSERT(!function.isEmpty()); + if (function.isEmpty()) { + return {}; + } + if (code == ERROR_SUCCESS) { + return {}; + } + LPWSTR buf = nullptr; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&buf), 0, nullptr) == 0) { + return {}; + } + const QString message = QStringLiteral("Function %1() failed with error code %2: %3.") + .arg(function, QString::number(code), QString::fromWCharArray(buf)); + LocalFree(buf); + return message; +} + +static inline QString __getSystemErrorMessage(const QString &function, const HRESULT hr) +{ + Q_ASSERT(!function.isEmpty()); + if (function.isEmpty()) { + return {}; + } + if (SUCCEEDED(hr)) { + return {}; + } + const DWORD dwError = HRESULT_CODE(hr); + return __getSystemErrorMessage(function, dwError); +} + bool Utilities::isWin8OrGreater() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) @@ -110,7 +143,7 @@ bool Utilities::isDwmCompositionAvailable() if (SUCCEEDED(hr)) { return (enabled != FALSE); } else { - qWarning() << getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr); + qWarning() << __getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr); const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat); bool ok = false; const DWORD value = registry.value(QStringLiteral("Composition"), 0).toUInt(&ok); @@ -204,7 +237,7 @@ void Utilities::updateFrameMargins(const WId winId, const bool reset) const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1}; const HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins); if (FAILED(hr)) { - qWarning() << getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr); + qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr); } } @@ -240,53 +273,29 @@ void Utilities::updateQtFrameMargins(QWindow *window, const bool enable) #endif } -QString Utilities::getSystemErrorMessage(const QString &function, const HRESULT hr) -{ - Q_ASSERT(!function.isEmpty()); - if (function.isEmpty()) { - return {}; - } - if (SUCCEEDED(hr)) { - return QStringLiteral("Operation succeeded."); - } - const DWORD dwError = HRESULT_CODE(hr); - LPWSTR buf = nullptr; - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 0, nullptr) == 0) { - return QStringLiteral("Failed to retrieve the error message from system."); - } - const QString message = QStringLiteral("%1 failed with error %2: %3.") - .arg(function, QString::number(dwError), QString::fromWCharArray(buf)); - LocalFree(buf); - return message; -} - QString Utilities::getSystemErrorMessage(const QString &function) { Q_ASSERT(!function.isEmpty()); if (function.isEmpty()) { return {}; } - const HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); - if (SUCCEEDED(hr)) { - return QStringLiteral("Operation succeeded."); + const DWORD code = GetLastError(); + if (code == ERROR_SUCCESS) { + return {}; } - return getSystemErrorMessage(function, hr); + return __getSystemErrorMessage(function, code); } QColor Utilities::getColorizationColor() { - COLORREF color = RGB(0, 0, 0); + DWORD color = 0; BOOL opaque = FALSE; const HRESULT hr = DwmGetColorizationColor(&color, &opaque); if (FAILED(hr)) { - qWarning() << getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr); + qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr); const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat); bool ok = false; color = registry.value(QStringLiteral("ColorizationColor"), 0).toUInt(&ok); - if (!ok || (color == 0)) { - color = RGB(128, 128, 128); // Dark gray - } } return QColor::fromRgba(color); } diff --git a/src/framelesshelper_global.h b/src/framelesshelper_global.h index 286c0f7..72b32d9 100644 --- a/src/framelesshelper_global.h +++ b/src/framelesshelper_global.h @@ -86,7 +86,11 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) Q_NAMESPACE_EXPORT(FRAMELESSHELPER_API) +#else +Q_NAMESPACE +#endif namespace Constants { @@ -95,10 +99,7 @@ constexpr char kFramelessModeFlag[] = "_FRAMELESSHELPER_FRAMELESS_MODE"; constexpr char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS"; constexpr char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT"; constexpr char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT"; -constexpr char kHitTestVisibleInChromeFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE_IN_CHROME"; -constexpr char kUseNativeTitleBarFlag[] = "_FRAMELESSHELPER_USE_NATIVE_TITLE_BAR"; -constexpr char kPreserveNativeFrameFlag[] = "_FRAMELESSHELPER_PRESERVE_NATIVE_WINDOW_FRAME"; -constexpr char kForcePreserveNativeFrameFlag[] = "_FRAMELESSHELPER_FORCE_PRESERVE_NATIVE_WINDOW_FRAME"; +constexpr char kHitTestVisibleFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE"; constexpr char kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE"; } diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 9052cee..6c3bafb 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -86,7 +86,7 @@ void FramelessQuickHelper::setHitTestVisibleInChrome(QQuickItem *item, const boo if (!item) { return; } - FramelessWindowsManager::setHitTestVisibleInChrome(window(), item, visible); + FramelessWindowsManager::setHitTestVisible(window(), item, visible); } FRAMELESSHELPER_END_NAMESPACE