forked from github_mirror/framelesshelper
parent
0e4f95fe2c
commit
ae65733b2d
|
@ -20,8 +20,8 @@ set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED)
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED)
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
|
#find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
|
#find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
framelesshelper_global.h
|
framelesshelper_global.h
|
||||||
|
@ -34,12 +34,12 @@ set(SOURCES
|
||||||
utilities.cpp
|
utilities.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
framelessquickhelper.h
|
framelessquickhelper.h
|
||||||
framelessquickhelper.cpp
|
framelessquickhelper.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()]]
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
|
@ -85,11 +85,11 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
Qt${QT_VERSION_MAJOR}::GuiPrivate
|
Qt${QT_VERSION_MAJOR}::GuiPrivate
|
||||||
)
|
)
|
||||||
|
|
||||||
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
Qt${QT_VERSION_MAJOR}::Quick
|
Qt${QT_VERSION_MAJOR}::Quick
|
||||||
)
|
)
|
||||||
endif()
|
endif()]]
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||||
|
|
|
@ -151,10 +151,7 @@ enum class Property : int
|
||||||
FrameBorderColor_Inactive = 15,
|
FrameBorderColor_Inactive = 15,
|
||||||
SystemAccentColor = 16,
|
SystemAccentColor = 16,
|
||||||
SystemColorizationArea = 17,
|
SystemColorizationArea = 17,
|
||||||
SystemTheme = 18,
|
SystemTheme = 18
|
||||||
WallpaperBackgroundColor = 19,
|
|
||||||
WallpaperAspectStyle = 20,
|
|
||||||
WallpaperFilePath = 21
|
|
||||||
};
|
};
|
||||||
Q_ENUM_NS(Property)
|
Q_ENUM_NS(Property)
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "framelesshelper_win32.h"
|
#include "framelesshelper_win32.h"
|
||||||
#include <QtCore/qmutex.h>
|
#include <QtCore/qmutex.h>
|
||||||
|
#include <QtCore/qvariant.h>
|
||||||
#include <QtCore/qcoreapplication.h>
|
#include <QtCore/qcoreapplication.h>
|
||||||
#include <QtGui/qwindow.h>
|
#include <QtGui/qwindow.h>
|
||||||
#include "framelesswindowsmanager_p.h"
|
#include "framelesswindowsmanager_p.h"
|
||||||
|
@ -64,8 +65,10 @@ void FramelessHelperWin::addWindow(QWindow *window)
|
||||||
g_helper()->instance.reset(new FramelessHelperWin);
|
g_helper()->instance.reset(new FramelessHelperWin);
|
||||||
qApp->installNativeEventFilter(g_helper()->instance.data());
|
qApp->installNativeEventFilter(g_helper()->instance.data());
|
||||||
}
|
}
|
||||||
|
const WId winId = window->winId();
|
||||||
|
Utilities::fixupQtInternals(winId);
|
||||||
Utilities::updateInternalWindowFrameMargins(window, true);
|
Utilities::updateInternalWindowFrameMargins(window, true);
|
||||||
Utilities::updateWindowFrameMargins(window->winId(), false);
|
Utilities::updateWindowFrameMargins(winId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessHelperWin::removeWindow(QWindow *window)
|
void FramelessHelperWin::removeWindow(QWindow *window)
|
||||||
|
@ -98,13 +101,34 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
// Anyway, we should skip the entire function in this case.
|
// Anyway, we should skip the entire function in this case.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const WId winId = reinterpret_cast<WId>(msg->hwnd);
|
||||||
const auto manager = Private::FramelessManager::instance();
|
const auto manager = Private::FramelessManager::instance();
|
||||||
|
Q_CHECK_PTR(manager);
|
||||||
|
if (!manager) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
manager->mutex.lock();
|
manager->mutex.lock();
|
||||||
if (!manager->winId.contains(reinterpret_cast<WId>(msg->hwnd))) {
|
if (!manager->winId.contains(winId)) {
|
||||||
manager->mutex.unlock();
|
manager->mutex.unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const QUuid uuid = manager->winId.value(winId);
|
||||||
|
Q_ASSERT(manager->data.contains(uuid));
|
||||||
|
if (!manager->data.contains(uuid)) {
|
||||||
|
manager->mutex.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const QVariantHash data = manager->data.value(uuid);
|
||||||
manager->mutex.unlock();
|
manager->mutex.unlock();
|
||||||
|
Q_ASSERT(data.contains(kWindow));
|
||||||
|
if (!data.contains(kWindow)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto window = qvariant_cast<QWindow *>(data.value(kWindow));
|
||||||
|
Q_ASSERT(window);
|
||||||
|
if (!window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
switch (msg->message) {
|
switch (msg->message) {
|
||||||
case WM_NCCALCSIZE: {
|
case WM_NCCALCSIZE: {
|
||||||
// Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
|
// Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
|
||||||
|
@ -197,8 +221,20 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
const auto clientRect = ((static_cast<BOOL>(msg->wParam) == FALSE)
|
const auto clientRect = ((static_cast<BOOL>(msg->wParam) == FALSE)
|
||||||
? reinterpret_cast<LPRECT>(msg->lParam)
|
? reinterpret_cast<LPRECT>(msg->lParam)
|
||||||
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam))->rgrc[0]);
|
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam))->rgrc[0]);
|
||||||
|
if (Utilities::isWin10OrGreater()) {
|
||||||
|
// 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, msg->wParam, 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;
|
||||||
|
}
|
||||||
const bool max = IsMaximized(msg->hwnd);
|
const bool max = IsMaximized(msg->hwnd);
|
||||||
const bool full = Utilities::isFullScreen(reinterpret_cast<WId>(msg->hwnd));
|
const bool full = Utilities::isFullScreen(winId);
|
||||||
// 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
|
||||||
// borders, and the default frame will be fine.
|
// borders, and the default frame will be fine.
|
||||||
|
@ -209,11 +245,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
// then the window is clipped to the monitor so that the resize handle
|
// 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
|
// do not appear because you don't need them (because you can't resize
|
||||||
// a window when it's maximized unless you restore it).
|
// a window when it's maximized unless you restore it).
|
||||||
const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true, true);
|
const int frameSizeY = Utilities::getResizeBorderThickness(winId, false, true);
|
||||||
clientRect->top += resizeBorderThickness;
|
clientRect->top += frameSizeY;
|
||||||
clientRect->bottom -= resizeBorderThickness;
|
if (!Utilities::isWin10OrGreater()) {
|
||||||
clientRect->left += resizeBorderThickness;
|
clientRect->bottom -= frameSizeY;
|
||||||
clientRect->right -= resizeBorderThickness;
|
const int frameSizeX = Utilities::getResizeBorderThickness(winId, true, true);
|
||||||
|
clientRect->left += frameSizeX;
|
||||||
|
clientRect->right -= frameSizeX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Attempt to detect if there's an autohide taskbar, and if
|
// Attempt to detect if there's an autohide taskbar, and if
|
||||||
// there is, reduce our size a bit on the side with the taskbar,
|
// there is, reduce our size a bit on the side with the taskbar,
|
||||||
|
@ -341,44 +380,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
*result = ((static_cast<BOOL>(msg->wParam) == FALSE) ? 0 : WVR_REDRAW);
|
*result = ((static_cast<BOOL>(msg->wParam) == FALSE) ? 0 : WVR_REDRAW);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case WM_NCUAHDRAWCAPTION:
|
|
||||||
case WM_NCUAHDRAWFRAME: {
|
|
||||||
// These undocumented messages are sent to draw themed window
|
|
||||||
// borders. Block them to prevent drawing borders over the client
|
|
||||||
// area.
|
|
||||||
*result = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case WM_NCPAINT: {
|
|
||||||
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
|
|
||||||
|
|
||||||
if (!Utilities::isDwmCompositionEnabled()) {
|
|
||||||
// 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.
|
|
||||||
*result = 0;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case WM_NCACTIVATE: {
|
|
||||||
if (Utilities::isDwmCompositionEnabled()) {
|
|
||||||
// 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" here, otherwise the window won't respond to the
|
|
||||||
// window activation state change.
|
|
||||||
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
|
|
||||||
} else {
|
|
||||||
if (static_cast<BOOL>(msg->wParam) == FALSE) {
|
|
||||||
*result = TRUE;
|
|
||||||
} else {
|
|
||||||
*result = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case WM_NCHITTEST: {
|
case WM_NCHITTEST: {
|
||||||
// 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口
|
// 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口
|
||||||
// 外部进行resize的,其原理是,WS_THICKFRAME这个窗口样式会在窗
|
// 外部进行resize的,其原理是,WS_THICKFRAME这个窗口样式会在窗
|
||||||
|
@ -445,99 +446,111 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
// another branch, if you are interested in it, you can give it a
|
// another branch, if you are interested in it, you can give it a
|
||||||
// try.
|
// try.
|
||||||
|
|
||||||
POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
|
const POINT globalPos = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
|
||||||
if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) {
|
POINT localPos = globalPos;
|
||||||
|
if (ScreenToClient(msg->hwnd, &localPos) == FALSE) {
|
||||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const QPointF localMouse = {static_cast<qreal>(winLocalMouse.x), static_cast<qreal>(winLocalMouse.y)};
|
|
||||||
RECT clientRect = {0, 0, 0, 0};
|
|
||||||
if (GetClientRect(msg->hwnd, &clientRect) == FALSE) {
|
|
||||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetClientRect"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const LONG windowWidth = clientRect.right;
|
|
||||||
const bool max = IsMaximized(msg->hwnd);
|
const bool max = IsMaximized(msg->hwnd);
|
||||||
const bool isTop = localMouse.y() <= resizeBorderThickness;
|
const bool full = Utilities::isFullScreen(winId);
|
||||||
*result = [clientRect, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window, max](){
|
const int frameSizeY = Utilities::getResizeBorderThickness(winId, false, true);
|
||||||
if (max) {
|
const bool isTop = (localPos.y < frameSizeY);
|
||||||
if (isTitleBar && mousePressed) {
|
const bool isTitleBar = false; // ### TODO
|
||||||
return HTCAPTION;
|
if (Utilities::isWin10OrGreater()) {
|
||||||
}
|
// This will handle the left, right and bottom parts of the frame
|
||||||
return HTCLIENT;
|
// because we didn't change them.
|
||||||
|
const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam);
|
||||||
|
if (originalRet != HTCLIENT) {
|
||||||
|
*result = originalRet;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
const LONG windowHeight = clientRect.bottom;
|
// At this point, we know that the cursor is inside the client area
|
||||||
const bool isBottom = (localMouse.y() >= (windowHeight - resizeBorderThickness));
|
// 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 (full) {
|
||||||
|
*result = HTCLIENT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (max) {
|
||||||
|
*result = (isTitleBar ? HTCAPTION : HTCLIENT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTop) {
|
||||||
|
*result = HTTOP;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isTitleBar) {
|
||||||
|
*result = HTCAPTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*result = HTCLIENT;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (full) {
|
||||||
|
*result = HTCLIENT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (max) {
|
||||||
|
*result = (isTitleBar ? HTCAPTION : HTCLIENT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
RECT clientRect = {0, 0, 0, 0};
|
||||||
|
if (GetClientRect(msg->hwnd, &clientRect) == FALSE) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("GetClientRect"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const LONG width = clientRect.right;
|
||||||
|
const LONG height = clientRect.bottom;
|
||||||
|
const bool isBottom = (localPos.y >= (height - frameSizeY));
|
||||||
// Make the border a little wider to let the user easy to resize on corners.
|
// 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 qreal scaleFactor = ((isTop || isBottom) ? 2.0 : 1.0);
|
||||||
const bool isLeft = (localMouse.x() <= qRound(static_cast<qreal>(resizeBorderThickness) * factor));
|
const int frameSizeX = Utilities::getResizeBorderThickness(winId, true, true);
|
||||||
const bool isRight = (localMouse.x() >= (windowWidth - qRound(static_cast<qreal>(resizeBorderThickness) * factor)));
|
const auto scaledFrameSizeX = static_cast<int>(qRound(qreal(frameSizeX) * scaleFactor));
|
||||||
const bool fixedSize = Utilities::isWindowFixedSize(window);
|
const bool isLeft = (localPos.x < scaledFrameSizeX);
|
||||||
const auto getBorderValue = [fixedSize](int value) -> int {
|
const bool isRight = (localPos.x >= (width - scaledFrameSizeX));
|
||||||
return fixedSize ? HTCLIENT : value;
|
|
||||||
};
|
|
||||||
if (isTop) {
|
if (isTop) {
|
||||||
if (isLeft) {
|
if (isLeft) {
|
||||||
return getBorderValue(HTTOPLEFT);
|
*result = HTTOPLEFT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isRight) {
|
if (isRight) {
|
||||||
return getBorderValue(HTTOPRIGHT);
|
*result = HTTOPRIGHT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return getBorderValue(HTTOP);
|
*result = HTTOP;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
if (isLeft) {
|
if (isLeft) {
|
||||||
return getBorderValue(HTBOTTOMLEFT);
|
*result = HTBOTTOMLEFT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isRight) {
|
if (isRight) {
|
||||||
return getBorderValue(HTBOTTOMRIGHT);
|
*result = HTBOTTOMRIGHT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return getBorderValue(HTBOTTOM);
|
*result = HTBOTTOM;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isLeft) {
|
if (isLeft) {
|
||||||
return getBorderValue(HTLEFT);
|
*result = HTLEFT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isRight) {
|
if (isRight) {
|
||||||
return getBorderValue(HTRIGHT);
|
*result = HTRIGHT;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (isTitleBar && mousePressed) {
|
if (isTitleBar) {
|
||||||
return HTCAPTION;
|
*result = HTCAPTION;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return HTCLIENT;
|
*result = HTCLIENT;
|
||||||
}();
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
#if 0 // This block of code is causing some problems in my own Qt Quick applications. Needs some more investigation.
|
|
||||||
case WM_SETICON:
|
|
||||||
case WM_SETTEXT: {
|
|
||||||
// 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<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) {
|
|
||||||
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto winId = reinterpret_cast<WId>(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;
|
|
||||||
}
|
|
||||||
Utilities::triggerFrameChange(winId);
|
|
||||||
*result = ret;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 2))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 2))
|
||||||
case WM_WINDOWPOSCHANGING: {
|
case WM_WINDOWPOSCHANGING: {
|
||||||
// Tell Windows to discard the entire contents of the client area, as re-using
|
// Tell Windows to discard the entire contents of the client area, as re-using
|
||||||
|
@ -552,11 +565,93 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
} break;
|
} break;
|
||||||
case WM_DWMCOMPOSITIONCHANGED: {
|
case WM_DWMCOMPOSITIONCHANGED: {
|
||||||
// Re-apply the custom window frame if recovered from the basic theme.
|
// Re-apply the custom window frame if recovered from the basic theme.
|
||||||
Utilities::updateWindowFrameMargins(reinterpret_cast<WId>(msg->hwnd), false);
|
Utilities::updateWindowFrameMargins(winId, false);
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (Utilities::isWin101809OrGreater()) {
|
||||||
|
if (msg->message == WM_SETTINGCHANGE) {
|
||||||
|
if ((msg->wParam == 0) && (QString::compare(QString::fromWCharArray(reinterpret_cast<LPCWSTR>(msg->lParam)), kThemeChangeEventName, Qt::CaseInsensitive) == 0)) {
|
||||||
|
const bool dark = Utilities::shouldAppsUseDarkMode();
|
||||||
|
Utilities::updateWindowFrameBorderColor(winId, dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Utilities::isWin10OrGreater()) {
|
||||||
|
switch (msg->message) {
|
||||||
|
case WM_NCUAHDRAWCAPTION:
|
||||||
|
case WM_NCUAHDRAWFRAME: {
|
||||||
|
// These undocumented messages are sent to draw themed window
|
||||||
|
// borders. Block them to prevent drawing borders over the client
|
||||||
|
// area.
|
||||||
|
*result = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case WM_NCPAINT: {
|
||||||
|
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
|
||||||
|
|
||||||
|
if (!Utilities::isDwmCompositionEnabled()) {
|
||||||
|
// 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.
|
||||||
|
*result = 0;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case WM_NCACTIVATE: {
|
||||||
|
if (Utilities::isDwmCompositionEnabled()) {
|
||||||
|
// 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" here, otherwise the window won't respond to the
|
||||||
|
// window activation state change.
|
||||||
|
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
|
||||||
|
} else {
|
||||||
|
if (static_cast<BOOL>(msg->wParam) == FALSE) {
|
||||||
|
*result = TRUE;
|
||||||
|
} else {
|
||||||
|
*result = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#if 0 // This block of code is causing some problems in my own Qt Quick applications. Needs some more investigation.
|
||||||
|
case WM_SETICON:
|
||||||
|
case WM_SETTEXT: {
|
||||||
|
// 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<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
Utilities::triggerFrameChange(winId);
|
||||||
|
*result = ret;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,7 @@
|
||||||
|
|
||||||
[[maybe_unused]] static const QString kDwmRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\DWM)");
|
[[maybe_unused]] static const QString kDwmRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\DWM)");
|
||||||
[[maybe_unused]] static const QString kPersonalizeRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
|
[[maybe_unused]] static const QString kPersonalizeRegistryKey = QStringLiteral(R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
|
||||||
|
[[maybe_unused]] static const QString kThemeChangeEventName = QStringLiteral("ImmersiveColorSet");
|
||||||
|
|
||||||
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
||||||
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "framelesswindowsmanager.h"
|
#include "framelesswindowsmanager.h"
|
||||||
#include "framelesswindowsmanager_p.h"
|
#include "framelesswindowsmanager_p.h"
|
||||||
#include <QtCore/qvariant.h>
|
#include <QtCore/qvariant.h>
|
||||||
|
#include <QtGui/qscreen.h>
|
||||||
#include "framelesshelper.h"
|
#include "framelesshelper.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
|
@ -77,7 +78,8 @@ void FramelessWindowsManager::addWindow(QWindow *window)
|
||||||
FramelessHelperWin::addWindow(window);
|
FramelessHelperWin::addWindow(window);
|
||||||
}
|
}
|
||||||
// Work-around Win32 multi-monitor artifacts.
|
// Work-around Win32 multi-monitor artifacts.
|
||||||
QObject::connect(window, &QWindow::screenChanged, window, [window](){
|
QObject::connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){
|
||||||
|
Q_UNUSED(screen);
|
||||||
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
|
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
|
||||||
// this is only necessary when the window is being moved cross monitors.
|
// this is only necessary when the window is being moved cross monitors.
|
||||||
Utilities::triggerFrameChange(window->winId());
|
Utilities::triggerFrameChange(window->winId());
|
||||||
|
|
|
@ -51,9 +51,6 @@ FRAMELESSHELPER_API void showSystemMenu(const WId winId, const QPointF &pos);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
|
[[nodiscard]] FRAMELESSHELPER_API bool shouldAppsUseDarkMode();
|
||||||
[[nodiscard]] FRAMELESSHELPER_API DwmColorizationArea getDwmColorizationArea();
|
[[nodiscard]] FRAMELESSHELPER_API DwmColorizationArea getDwmColorizationArea();
|
||||||
[[nodiscard]] FRAMELESSHELPER_API bool isHighContrastModeEnabled();
|
[[nodiscard]] FRAMELESSHELPER_API bool isHighContrastModeEnabled();
|
||||||
[[nodiscard]] FRAMELESSHELPER_API QColor getWallpaperBackgroundColor();
|
|
||||||
[[nodiscard]] FRAMELESSHELPER_API int getWallpaperAspectStyle();
|
|
||||||
[[nodiscard]] FRAMELESSHELPER_API QString getWallpaperFilePath();
|
|
||||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getPrimaryScreenDpi(const bool horizontal);
|
[[nodiscard]] FRAMELESSHELPER_API quint32 getPrimaryScreenDpi(const bool horizontal);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getWindowDpi(const WId winId, const bool horizontal);
|
[[nodiscard]] FRAMELESSHELPER_API quint32 getWindowDpi(const WId winId, const bool horizontal);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getResizeBorderThickness(const WId winId, const bool horizontal, const bool scaled);
|
[[nodiscard]] FRAMELESSHELPER_API quint32 getResizeBorderThickness(const WId winId, const bool horizontal, const bool scaled);
|
||||||
|
@ -61,7 +58,8 @@ FRAMELESSHELPER_API void showSystemMenu(const WId winId, const QPointF &pos);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getTitleBarHeight(const WId winId, const bool scaled);
|
[[nodiscard]] FRAMELESSHELPER_API quint32 getTitleBarHeight(const WId winId, const bool scaled);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API quint32 getFrameBorderThickness(const WId winId, const bool scaled);
|
[[nodiscard]] FRAMELESSHELPER_API quint32 getFrameBorderThickness(const WId winId, const bool scaled);
|
||||||
[[nodiscard]] FRAMELESSHELPER_API QColor getFrameBorderColor(const bool active);
|
[[nodiscard]] FRAMELESSHELPER_API QColor getFrameBorderColor(const bool active);
|
||||||
FRAMELESSHELPER_API void updateWindowFrameColor(const WId winId, const bool dark);
|
FRAMELESSHELPER_API void updateWindowFrameBorderColor(const WId winId, const bool dark);
|
||||||
|
FRAMELESSHELPER_API void fixupQtInternals(const WId winId);
|
||||||
#endif // Q_OS_WINDOWS
|
#endif // Q_OS_WINDOWS
|
||||||
|
|
||||||
} // namespace Utilities
|
} // namespace Utilities
|
||||||
|
|
|
@ -198,23 +198,32 @@ void Utilities::triggerFrameChange(const WId winId)
|
||||||
|
|
||||||
void Utilities::updateWindowFrameMargins(const WId winId, const bool reset)
|
void Utilities::updateWindowFrameMargins(const WId winId, const bool reset)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// DwmExtendFrameIntoClientArea() will always fail if DWM composition is disabled.
|
// DwmExtendFrameIntoClientArea() will always fail if DWM composition is disabled.
|
||||||
// No need to try in this case.
|
// No need to try in this case.
|
||||||
if (!isDwmCompositionEnabled()) {
|
if (!isDwmCompositionEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Q_ASSERT(winId);
|
|
||||||
if (!winId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
static const auto pDwmExtendFrameIntoClientArea =
|
static const auto pDwmExtendFrameIntoClientArea =
|
||||||
reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>(
|
reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>(
|
||||||
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmExtendFrameIntoClientArea"));
|
QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmExtendFrameIntoClientArea"));
|
||||||
if (!pDwmExtendFrameIntoClientArea) {
|
if (!pDwmExtendFrameIntoClientArea) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const MARGINS margins = [reset, winId]() -> MARGINS {
|
||||||
|
if (reset) {
|
||||||
|
return {0, 0, 0, 0};
|
||||||
|
}
|
||||||
|
if (isWin10OrGreater()) {
|
||||||
|
return {0, static_cast<int>(getTitleBarHeight(winId, true)), 0, 0};
|
||||||
|
} else {
|
||||||
|
return {1, 1, 1, 1};
|
||||||
|
}
|
||||||
|
}();
|
||||||
const auto hwnd = reinterpret_cast<HWND>(winId);
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
const MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
|
|
||||||
const HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins);
|
const HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr);
|
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr);
|
||||||
|
@ -230,10 +239,16 @@ void Utilities::updateInternalWindowFrameMargins(QWindow *window, const bool ena
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const WId winId = window->winId();
|
const WId winId = window->winId();
|
||||||
const int resizeBorderThicknessH = enable ? getResizeBorderThickness(winId, true, true) : 0;
|
const QMargins margins = [winId]() -> QMargins {
|
||||||
const int resizeBorderThicknessV = enable ? getResizeBorderThickness(winId, false, true) : 0;
|
const int titleBarHeight = getTitleBarHeight(winId, true);
|
||||||
const int titleBarHeight = enable ? getTitleBarHeight(winId, true) : 0;
|
if (isWin10OrGreater()) {
|
||||||
const QMargins margins = {-resizeBorderThicknessH, -titleBarHeight, -resizeBorderThicknessH, -resizeBorderThicknessV};
|
return {0, -titleBarHeight, 0, 0};
|
||||||
|
} else {
|
||||||
|
const int frameSizeX = getResizeBorderThickness(winId, true, true);
|
||||||
|
const int frameSizeY = getResizeBorderThickness(winId, false, true);
|
||||||
|
return {-frameSizeX, -titleBarHeight, -frameSizeX, -frameSizeY};
|
||||||
|
}
|
||||||
|
}();
|
||||||
const QVariant marginsVar = QVariant::fromValue(margins);
|
const QVariant marginsVar = QVariant::fromValue(margins);
|
||||||
window->setProperty("_q_windowsCustomMargins", marginsVar);
|
window->setProperty("_q_windowsCustomMargins", marginsVar);
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
@ -541,21 +556,6 @@ bool Utilities::isHighContrastModeEnabled()
|
||||||
return (hc.dwFlags & HCF_HIGHCONTRASTON);
|
return (hc.dwFlags & HCF_HIGHCONTRASTON);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor Utilities::getWallpaperBackgroundColor()
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int Utilities::getWallpaperAspectStyle()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utilities::getWallpaperFilePath()
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 Utilities::getPrimaryScreenDpi(const bool horizontal)
|
quint32 Utilities::getPrimaryScreenDpi(const bool horizontal)
|
||||||
{
|
{
|
||||||
static const auto pGetDpiForMonitor =
|
static const auto pGetDpiForMonitor =
|
||||||
|
@ -705,7 +705,7 @@ QColor Utilities::getFrameBorderColor(const bool active)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utilities::updateWindowFrameColor(const WId winId, const bool dark)
|
void Utilities::updateWindowFrameBorderColor(const WId winId, const bool dark)
|
||||||
{
|
{
|
||||||
Q_ASSERT(winId);
|
Q_ASSERT(winId);
|
||||||
if (!winId) {
|
if (!winId) {
|
||||||
|
@ -742,4 +742,30 @@ void Utilities::updateWindowFrameColor(const WId winId, const bool dark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Utilities::fixupQtInternals(const WId winId)
|
||||||
|
{
|
||||||
|
Q_ASSERT(winId);
|
||||||
|
if (!winId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto hwnd = reinterpret_cast<HWND>(winId);
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
const auto oldClassStyle = static_cast<DWORD>(GetClassLongPtrW(hwnd, GCL_STYLE));
|
||||||
|
if (oldClassStyle == 0) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("GetClassLongPtrW"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const DWORD newClassStyle = (oldClassStyle | CS_HREDRAW | CS_VREDRAW);
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetClassLongPtrW(hwnd, GCL_STYLE, static_cast<LONG_PTR>(newClassStyle)) == 0) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetClassLongPtrW"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const DWORD newWindowStyle = (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
|
||||||
|
SetLastError(ERROR_SUCCESS);
|
||||||
|
if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast<LONG_PTR>(newWindowStyle)) == 0) {
|
||||||
|
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
Loading…
Reference in New Issue