Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-03-11 17:23:31 +08:00
parent 0e4f95fe2c
commit ae65733b2d
7 changed files with 275 additions and 156 deletions

View File

@ -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}>"

View File

@ -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)

View File

@ -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;
} }

View File

@ -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;

View File

@ -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());

View File

@ -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

View File

@ -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