sync with 1.x changes

This commit is contained in:
Altair Wei 2021-11-14 22:31:02 +08:00
parent 7b82375543
commit 1ebead3148
12 changed files with 217 additions and 383 deletions

View File

@ -167,7 +167,12 @@ void MainWindow::paintEvent(QPaintEvent *event)
{0, h, 0, 0} {0, h, 0, 0}
}; };
painter.save(); 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<qreal>(Utilities::getWindowVisibleFrameBorderThickness(winId()));
painter.setPen({borderColor, qMax(borderThickness, devicePixelRatioF())});
painter.drawLines(lines); painter.drawLines(lines);
painter.restore(); painter.restore();
} }

View File

@ -22,7 +22,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include "../../framelessquickhelper.h" #include "quick/framelessquickhelper.h"
#include <QtGui/qguiapplication.h> #include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h> #include <QtQml/qqmlapplicationengine.h>
#include <QtQuickControls2/qquickstyle.h> #include <QtQuickControls2/qquickstyle.h>

View File

@ -654,28 +654,6 @@ void FramelessHelper::handleResizeHandlerDblClicked()
} }
#ifdef Q_OS_WIN #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. 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 // preserve the four window borders. So we just remove the whole
// window frame, otherwise the code will become much more complex. // window frame, otherwise the code will become much more complex.
if (Utilities::shouldUseNativeTitleBar()) { if (static_cast<BOOL>(msg->wParam) == FALSE) {
break;
}
if (msg->wParam == FALSE) {
*result = 0; *result = 0;
return true; 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; bool nonClientAreaExists = false;
// 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
@ -833,11 +793,9 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
const int resizeBorderThickness = Utilities::getSystemMetric( const int resizeBorderThickness = Utilities::getSystemMetric(
window, SystemMetric::ResizeBorderThickness, true, true); window, SystemMetric::ResizeBorderThickness, true, true);
clientRect->top += resizeBorderThickness; clientRect->top += resizeBorderThickness;
if (!shouldHaveWindowFrame()) {
clientRect->bottom -= resizeBorderThickness; clientRect->bottom -= resizeBorderThickness;
clientRect->left += resizeBorderThickness; clientRect->left += resizeBorderThickness;
clientRect->right -= resizeBorderThickness; clientRect->right -= resizeBorderThickness;
}
nonClientAreaExists = true; nonClientAreaExists = true;
} }
// Attempt to detect if there's an autohide taskbar, and if // Attempt to detect if there's an autohide taskbar, and if
@ -977,17 +935,13 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
// area. // area.
case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWCAPTION:
case WM_NCUAHDRAWFRAME: { case WM_NCUAHDRAWFRAME: {
if (shouldHaveWindowFrame()) {
break;
} else {
*result = 0; *result = 0;
return true; return true;
} }
}
case WM_NCPAINT: { case WM_NCPAINT: {
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
if (!Utilities::isDwmCompositionAvailable() && !shouldHaveWindowFrame()) { if (!Utilities::isDwmCompositionAvailable()) {
// Only block WM_NCPAINT when DWM composition is disabled. If // Only block WM_NCPAINT when DWM composition is disabled. If
// it's blocked when DWM composition is enabled, the frame // it's blocked when DWM composition is enabled, the frame
// shadow won't be drawn. // shadow won't be drawn.
@ -998,9 +952,6 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
} }
} }
case WM_NCACTIVATE: { case WM_NCACTIVATE: {
if (shouldHaveWindowFrame()) {
break;
} else {
if (Utilities::isDwmCompositionAvailable()) { if (Utilities::isDwmCompositionAvailable()) {
// DefWindowProc won't repaint the window border if lParam // DefWindowProc won't repaint the window border if lParam
// (normally a HRGN) is -1. See the following link's "lParam" // (normally a HRGN) is -1. See the following link's "lParam"
@ -1010,7 +961,7 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
// to the window active state change. // to the window active state change.
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
} else { } else {
if (msg->wParam == FALSE) { if (static_cast<BOOL>(msg->wParam) == FALSE) {
*result = TRUE; *result = TRUE;
} else { } else {
*result = FALSE; *result = FALSE;
@ -1018,7 +969,6 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
} }
return true; return true;
} }
}
case WM_NCHITTEST: { case WM_NCHITTEST: {
// 原生Win32窗口只有顶边是在窗口内部resize的其余三边都是在窗口 // 原生Win32窗口只有顶边是在窗口内部resize的其余三边都是在窗口
// 外部进行resize的其原理是WS_THICKFRAME这个窗口样式会在窗 // 外部进行resize的其原理是WS_THICKFRAME这个窗口样式会在窗
@ -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 // another branch, if you are interested in it, you can give it a
// try. // try.
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)}; POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) { if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
@ -1108,44 +1054,7 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
bool isTitleBar = isInTitlebarArea(QPoint(qRound(localMouse.x() / scaleFactor), qRound(localMouse.y() / scaleFactor))); 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; 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 = [ const LRESULT hitTestResult = [
clientRect, msg, isTitleBar, &localMouse, clientRect, msg, isTitleBar, &localMouse,
resizeBorderThickness, windowWidth, isTop, resizeBorderThickness, windowWidth, isTop,
@ -1198,18 +1107,19 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
*result = hitTestResult; *result = hitTestResult;
return true; return true;
} }
}
case WM_SETICON: case WM_SETICON:
case WM_SETTEXT: { case WM_SETTEXT: {
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
// Disable painting while these messages are handled to prevent them // Disable painting while these messages are handled to prevent them
// from drawing a window caption over the client area. // from drawing a window caption over the client area.
SetLastError(ERROR_SUCCESS);
const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); 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 // Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style. // toggling the WS_VISIBLE style.
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) { if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;
@ -1217,6 +1127,7 @@ bool FramelessHelper::handleNativeEvent(QWindow *window, const QByteArray &event
const auto winId = reinterpret_cast<WId>(msg->hwnd); const auto winId = reinterpret_cast<WId>(msg->hwnd);
Utilities::triggerFrameChange(winId); Utilities::triggerFrameChange(winId);
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;

View File

@ -32,28 +32,6 @@
FRAMELESSHELPER_BEGIN_NAMESPACE 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 struct FramelessHelperWinData
{ {
bool create() { 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 // preserve the four window borders. So we just remove the whole
// window frame, otherwise the code will become much more complex. // window frame, otherwise the code will become much more complex.
if (Utilities::shouldUseNativeTitleBar()) { if (static_cast<BOOL>(msg->wParam) == FALSE) {
break;
}
if (msg->wParam == FALSE) {
*result = 0; *result = 0;
return true; return true;
} }
const auto clientRect = &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]); const auto clientRect = &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(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; bool nonClientAreaExists = false;
// 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
@ -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 // 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, false, true); const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true);
clientRect->top += resizeBorderThickness; clientRect->top += resizeBorderThickness;
if (!shouldHaveWindowFrame()) {
clientRect->bottom -= resizeBorderThickness; clientRect->bottom -= resizeBorderThickness;
clientRect->left += resizeBorderThickness; clientRect->left += resizeBorderThickness;
clientRect->right -= resizeBorderThickness; clientRect->right -= resizeBorderThickness;
}
nonClientAreaExists = true; nonClientAreaExists = true;
} }
// Attempt to detect if there's an autohide taskbar, and if // Attempt to detect if there's an autohide taskbar, and if
@ -427,17 +385,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// area. // area.
case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWCAPTION:
case WM_NCUAHDRAWFRAME: { case WM_NCUAHDRAWFRAME: {
if (shouldHaveWindowFrame()) {
break;
} else {
*result = 0; *result = 0;
return true; return true;
} }
}
case WM_NCPAINT: { case WM_NCPAINT: {
// 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失 // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
if (!Utilities::isDwmCompositionAvailable() && !shouldHaveWindowFrame()) { if (!Utilities::isDwmCompositionAvailable()) {
// Only block WM_NCPAINT when DWM composition is disabled. If // Only block WM_NCPAINT when DWM composition is disabled. If
// it's blocked when DWM composition is enabled, the frame // it's blocked when DWM composition is enabled, the frame
// shadow won't be drawn. // shadow won't be drawn.
@ -448,9 +402,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} }
} }
case WM_NCACTIVATE: { case WM_NCACTIVATE: {
if (shouldHaveWindowFrame()) {
break;
} else {
if (Utilities::isDwmCompositionAvailable()) { if (Utilities::isDwmCompositionAvailable()) {
// DefWindowProc won't repaint the window border if lParam // DefWindowProc won't repaint the window border if lParam
// (normally a HRGN) is -1. See the following link's "lParam" // (normally a HRGN) is -1. See the following link's "lParam"
@ -460,7 +411,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// to the window active state change. // to the window active state change.
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1);
} else { } else {
if (msg->wParam == FALSE) { if (static_cast<BOOL>(msg->wParam) == FALSE) {
*result = TRUE; *result = TRUE;
} else { } else {
*result = FALSE; *result = FALSE;
@ -468,7 +419,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} }
return true; return true;
} }
}
case WM_NCHITTEST: { case WM_NCHITTEST: {
// 原生Win32窗口只有顶边是在窗口内部resize的其余三边都是在窗口 // 原生Win32窗口只有顶边是在窗口内部resize的其余三边都是在窗口
// 外部进行resize的其原理是WS_THICKFRAME这个窗口样式会在窗 // 外部进行resize的其原理是WS_THICKFRAME这个窗口样式会在窗
@ -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 // another branch, if you are interested in it, you can give it a
// try. // try.
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)}; POINT winLocalMouse = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)};
if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) { if (ScreenToClient(msg->hwnd, &winLocalMouse) == FALSE) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
@ -557,38 +503,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) { if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) {
isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight) isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight)
&& (localMouse.x() >= 0) && (localMouse.x() <= windowWidth) && (localMouse.x() >= 0) && (localMouse.x() <= windowWidth)
&& !Utilities::isHitTestVisibleInChrome(window); && !Utilities::isHitTestVisible(window);
} }
if (window->windowState() == Qt::WindowNoState) { if (window->windowState() == Qt::WindowNoState) {
isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight) isTitleBar = (localMouse.y() > resizeBorderThickness) && (localMouse.y() <= titleBarHeight)
&& (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness)) && (localMouse.x() > resizeBorderThickness) && (localMouse.x() < (windowWidth - resizeBorderThickness))
&& !Utilities::isHitTestVisibleInChrome(window); && !Utilities::isHitTestVisible(window);
} }
const bool isTop = localMouse.y() <= resizeBorderThickness; 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]{ const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{
if (IsMaximized(msg->hwnd)) { if (IsMaximized(msg->hwnd)) {
if (isTitleBar) { if (isTitleBar) {
@ -638,18 +560,19 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
*result = hitTestResult; *result = hitTestResult;
return true; return true;
} }
}
case WM_SETICON: case WM_SETICON:
case WM_SETTEXT: { case WM_SETTEXT: {
if (Utilities::shouldUseNativeTitleBar()) {
break;
}
// Disable painting while these messages are handled to prevent them // Disable painting while these messages are handled to prevent them
// from drawing a window caption over the client area. // from drawing a window caption over the client area.
SetLastError(ERROR_SUCCESS);
const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); 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 // Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style. // toggling the WS_VISIBLE style.
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) { if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;
@ -657,6 +580,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const auto winId = reinterpret_cast<WId>(msg->hwnd); const auto winId = reinterpret_cast<WId>(msg->hwnd);
Utilities::triggerFrameChange(winId); Utilities::triggerFrameChange(winId);
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) {
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;
@ -665,15 +589,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
*result = ret; *result = ret;
return true; 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<WId>(msg->hwnd), (max || full));
Utilities::updateQtFrameMargins(const_cast<QWindow *>(window), true);
}
} break;
default: default:
break; break;
} }

View File

@ -90,11 +90,11 @@
#endif #endif
#ifndef GET_X_LPARAM #ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) #define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp))))
#endif #endif
#ifndef GET_Y_LPARAM #ifndef GET_Y_LPARAM
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) #define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp))))
#endif #endif
#ifndef IsMinimized #ifndef IsMinimized
@ -105,13 +105,13 @@
#define IsMaximized(window) (IsZoomed(window) != FALSE) #define IsMaximized(window) (IsZoomed(window) != FALSE)
#endif #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 kDwmRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM)";
constexpr char kPersonalizeRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"; constexpr char kPersonalizeRegistryKey[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)";
constexpr int kDefaultResizeBorderThicknessClassic = 4; constexpr UINT kDefaultResizeBorderThicknessClassic = 4;
constexpr int kDefaultResizeBorderThicknessAero = 8; constexpr UINT kDefaultResizeBorderThicknessAero = 8;
constexpr int kDefaultCaptionHeight = 23; constexpr UINT kDefaultCaptionHeight = 23;
constexpr WORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37; constexpr DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;

View File

@ -38,7 +38,7 @@
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION #ifdef FRAMELESSHELPER_USE_UNIX_VERSION
//Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix) Q_GLOBAL_STATIC(FramelessHelper, framelessHelperUnix)
#endif #endif
void FramelessWindowsManager::addWindow(QWindow *window) void FramelessWindowsManager::addWindow(QWindow *window)
@ -51,7 +51,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
} }
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION #ifdef FRAMELESSHELPER_USE_UNIX_VERSION
//framelessHelperUnix()->removeWindowFrame(window); framelessHelperUnix()->removeWindowFrame(window);
#else #else
FramelessHelperWin::addFramelessWindow(window); FramelessHelperWin::addFramelessWindow(window);
// Work-around a Win32 multi-monitor bug. // Work-around a Win32 multi-monitor bug.
@ -62,7 +62,7 @@ void FramelessWindowsManager::addWindow(QWindow *window)
#endif #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(window);
Q_ASSERT(object); Q_ASSERT(object);
@ -73,7 +73,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject
qWarning() << object << "is not a QWidget or QQuickItem."; qWarning() << object << "is not a QWidget or QQuickItem.";
return; return;
} }
auto objList = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleInChromeFlag)); auto objList = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
if (value) { if (value) {
if (objList.isEmpty() || !objList.contains(object)) { if (objList.isEmpty() || !objList.contains(object)) {
objList.append(object); objList.append(object);
@ -83,7 +83,7 @@ void FramelessWindowsManager::setHitTestVisibleInChrome(QWindow *window, QObject
objList.removeAll(object); objList.removeAll(object);
} }
} }
window->setProperty(Constants::kHitTestVisibleInChromeFlag, QVariant::fromValue(objList)); window->setProperty(Constants::kHitTestVisibleFlag, QVariant::fromValue(objList));
} }
int FramelessWindowsManager::getResizeBorderThickness(const QWindow *window) int FramelessWindowsManager::getResizeBorderThickness(const QWindow *window)
@ -165,7 +165,7 @@ void FramelessWindowsManager::removeWindow(QWindow *window)
return; return;
} }
#ifdef FRAMELESSHELPER_USE_UNIX_VERSION #ifdef FRAMELESSHELPER_USE_UNIX_VERSION
//framelessHelperUnix()->bringBackWindowFrame(window); framelessHelperUnix()->bringBackWindowFrame(window);
#else #else
FramelessHelperWin::removeFramelessWindow(window); FramelessHelperWin::removeFramelessWindow(window);
#endif #endif

View File

@ -39,7 +39,7 @@ namespace FramelessWindowsManager
FRAMELESSHELPER_API void addWindow(QWindow *window); FRAMELESSHELPER_API void addWindow(QWindow *window);
FRAMELESSHELPER_API void removeWindow(QWindow *window); FRAMELESSHELPER_API void removeWindow(QWindow *window);
FRAMELESSHELPER_API bool isWindowFrameless(const 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 int getResizeBorderThickness(const QWindow *window);
FRAMELESSHELPER_API void setResizeBorderThickness(QWindow *window, const int value); FRAMELESSHELPER_API void setResizeBorderThickness(QWindow *window, const int value);
FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window); FRAMELESSHELPER_API int getTitleBarHeight(const QWindow *window);

View File

@ -49,11 +49,6 @@ QWindow *Utilities::findWindow(const WId winId)
return nullptr; return nullptr;
} }
bool Utilities::shouldUseNativeTitleBar()
{
return qEnvironmentVariableIsSet(Constants::kUseNativeTitleBarFlag);
}
bool Utilities::isWindowFixedSize(const QWindow *window) bool Utilities::isWindowFixedSize(const QWindow *window)
{ {
Q_ASSERT(window); Q_ASSERT(window);
@ -73,13 +68,13 @@ bool Utilities::isWindowFixedSize(const QWindow *window)
return false; return false;
} }
bool Utilities::isHitTestVisibleInChrome(const QWindow *window) bool Utilities::isHitTestVisible(const QWindow *window)
{ {
Q_ASSERT(window); Q_ASSERT(window);
if (!window) { if (!window) {
return false; return false;
} }
const auto objs = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleInChromeFlag)); const auto objs = qvariant_cast<QObjectList>(window->property(Constants::kHitTestVisibleFlag));
if (objs.isEmpty()) { if (objs.isEmpty()) {
return false; return false;
} }

View File

@ -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 int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiScale, const bool forceSystemValue = false);
FRAMELESSHELPER_API QWindow *findWindow(const WId winId); FRAMELESSHELPER_API QWindow *findWindow(const WId winId);
FRAMELESSHELPER_API bool shouldUseNativeTitleBar();
FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window); 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 QPointF mapOriginPointToWindow(const QObject *object);
FRAMELESSHELPER_API QColor getColorizationColor(); FRAMELESSHELPER_API QColor getColorizationColor();
FRAMELESSHELPER_API int getWindowVisibleFrameBorderThickness(const WId winId); 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 triggerFrameChange(const WId winId);
FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset); FRAMELESSHELPER_API void updateFrameMargins(const WId winId, const bool reset);
FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable); 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); FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function);
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS

View File

@ -69,6 +69,39 @@ static inline bool isWin1019H1OrGreater()
return result; 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<LPWSTR>(&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() bool Utilities::isWin8OrGreater()
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
@ -110,7 +143,7 @@ bool Utilities::isDwmCompositionAvailable()
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
return (enabled != FALSE); return (enabled != FALSE);
} else { } else {
qWarning() << getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr); qWarning() << __getSystemErrorMessage(QStringLiteral("DwmIsCompositionEnabled"), hr);
const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat); const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat);
bool ok = false; bool ok = false;
const DWORD value = registry.value(QStringLiteral("Composition"), 0).toUInt(&ok); 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 MARGINS margins = reset ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
const HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins); const HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins);
if (FAILED(hr)) { 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 #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) QString Utilities::getSystemErrorMessage(const QString &function)
{ {
Q_ASSERT(!function.isEmpty()); Q_ASSERT(!function.isEmpty());
if (function.isEmpty()) { if (function.isEmpty()) {
return {}; return {};
} }
const HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); const DWORD code = GetLastError();
if (SUCCEEDED(hr)) { if (code == ERROR_SUCCESS) {
return QStringLiteral("Operation succeeded."); return {};
} }
return getSystemErrorMessage(function, hr); return __getSystemErrorMessage(function, code);
} }
QColor Utilities::getColorizationColor() QColor Utilities::getColorizationColor()
{ {
COLORREF color = RGB(0, 0, 0); DWORD color = 0;
BOOL opaque = FALSE; BOOL opaque = FALSE;
const HRESULT hr = DwmGetColorizationColor(&color, &opaque); const HRESULT hr = DwmGetColorizationColor(&color, &opaque);
if (FAILED(hr)) { if (FAILED(hr)) {
qWarning() << getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr); qWarning() << __getSystemErrorMessage(QStringLiteral("DwmGetColorizationColor"), hr);
const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat); const QSettings registry(QString::fromUtf8(kDwmRegistryKey), QSettings::NativeFormat);
bool ok = false; bool ok = false;
color = registry.value(QStringLiteral("ColorizationColor"), 0).toUInt(&ok); color = registry.value(QStringLiteral("ColorizationColor"), 0).toUInt(&ok);
if (!ok || (color == 0)) {
color = RGB(128, 128, 128); // Dark gray
}
} }
return QColor::fromRgba(color); return QColor::fromRgba(color);
} }

View File

@ -86,7 +86,11 @@
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
Q_NAMESPACE_EXPORT(FRAMELESSHELPER_API) Q_NAMESPACE_EXPORT(FRAMELESSHELPER_API)
#else
Q_NAMESPACE
#endif
namespace Constants namespace Constants
{ {
@ -95,10 +99,7 @@ constexpr char kFramelessModeFlag[] = "_FRAMELESSHELPER_FRAMELESS_MODE";
constexpr char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS"; constexpr char kResizeBorderThicknessFlag[] = "_FRAMELESSHELPER_RESIZE_BORDER_THICKNESS";
constexpr char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT"; constexpr char kCaptionHeightFlag[] = "_FRAMELESSHELPER_CAPTION_HEIGHT";
constexpr char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT"; constexpr char kTitleBarHeightFlag[] = "_FRAMELESSHELPER_TITLE_BAR_HEIGHT";
constexpr char kHitTestVisibleInChromeFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE_IN_CHROME"; constexpr char kHitTestVisibleFlag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE";
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 kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE"; constexpr char kWindowFixedSizeFlag[] = "_FRAMELESSHELPER_WINDOW_FIXED_SIZE";
} }

View File

@ -86,7 +86,7 @@ void FramelessQuickHelper::setHitTestVisibleInChrome(QQuickItem *item, const boo
if (!item) { if (!item) {
return; return;
} }
FramelessWindowsManager::setHitTestVisibleInChrome(window(), item, visible); FramelessWindowsManager::setHitTestVisible(window(), item, visible);
} }
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE