win: fix the multi monitor window expand issue

#141
#184

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-11-22 15:03:11 +08:00
parent 3961ecb505
commit ebd3d6573f
6 changed files with 114 additions and 61 deletions

View File

@ -415,10 +415,10 @@ Q_ENUM_NS(WindowCornerStyle)
struct VersionNumber
{
int major = 0;
int minor = 0;
int patch = 0;
int tweak = 0;
const int major = 0;
const int minor = 0;
const int patch = 0;
const int tweak = 0;
[[nodiscard]] friend constexpr bool operator==(const VersionNumber &lhs, const VersionNumber &rhs) noexcept
{
@ -571,6 +571,23 @@ struct SystemParameters
}
};
struct VersionInfo
{
const int version = 0;
const char *version_str = nullptr;
const char *commit = nullptr;
const char *compileDateTime = nullptr;
const char *compiler = nullptr;
const bool isDebug = false;
const bool isStatic = false;
};
struct Dpi
{
const quint32 x = 0;
const quint32 y = 0;
};
#ifdef Q_OS_WINDOWS
[[maybe_unused]] inline constexpr const VersionNumber WindowsVersions[] =
{
@ -604,17 +621,6 @@ struct SystemParameters
};
#endif // Q_OS_WINDOWS
struct VersionInfo
{
const int version = 0;
const char *version_str = nullptr;
const char *commit = nullptr;
const char *compileDateTime = nullptr;
const char *compiler = nullptr;
const bool isDebug = false;
const bool isStatic = false;
};
} // namespace Global
namespace FramelessHelper::Core
@ -633,3 +639,10 @@ FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::SystemParameters)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi);
#ifndef QT_NO_DEBUG_STREAM
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &);
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &);
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &);
#endif // QT_NO_DEBUG_STREAM

View File

@ -92,7 +92,7 @@ FRAMELESSHELPER_CORE_API void showSystemMenu(
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId,
const bool horizontal,
const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId,
const bool scaled);

View File

@ -531,8 +531,8 @@ void FramelessHelperWin::addWindow(const SystemParameters &params)
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data());
}
g_win32Helper()->mutex.unlock();
DEBUG.nospace().noquote() << "The DPI of window " << hwnd2str(windowId) << " is: QDpi("
<< Utils::getWindowDpi(windowId, true) << ", " << Utils::getWindowDpi(windowId, false) << ").";
const Dpi dpi = {Utils::getWindowDpi(windowId, true), Utils::getWindowDpi(windowId, false)};
DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << dpi;
// Some Qt internals have to be corrected.
Utils::maybeFixupQtInternals(windowId);
// Qt maintains a frame margin internally, we need to update it accordingly
@ -1122,22 +1122,30 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} break;
#endif
case WM_DPICHANGED: {
const UINT dpiX = LOWORD(wParam);
const UINT dpiY = HIWORD(wParam);
DEBUG.nospace().noquote() << "New DPI for window "
<< hwnd2str(hWnd) << ": QDpi(" << dpiX << ", " << dpiY << ").";
const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))};
DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi;
// Sync the internal window frame margins with the latest DPI.
Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true);
// Here we need a little delay because event filters are processed before
// Qt's own window message handlers.
QTimer::singleShot(50, [data](){ // Copy "data" intentionally, otherwise it'll go out of scope when Qt finally use it.
QTimer::singleShot(0, qApp, [data, windowId](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use it.
// For some unknown reason, Qt sometimes won't re-paint the window contents after
// the DPI changes, and in my experiments the controls should be moved to our
// desired geometry already, the only issue is we don't get the updated appearance
// of our window. And we can workaround this issue by simply triggering a resize
// event manually. There's no need to increase/decrease the window size and then
// change it back, just give Qt our current window size is sufficient enough.
data.params.setWindowSize(data.params.getWindowSize());
// event manually.
// For some reason the window will always expand for some pixels after the DPI
// changes, and normal Qt windows don't have this issue. It's probably caused
// by the custom margins we set, QPA may calculate a wrong frame size. It will
// always add a title bar height to the window, while it should not due to we
// have hide the title bar. For now we can simply workaround it by restoring
// to the original window size after the DPI change, but it's not ideal. Maybe
// we should reset the custom margins first and then restore it instead, but
// sadly this solution doesn't work as expected. Still need investigating.
const int titleBarHeight = Utils::getTitleBarHeight(windowId, false);
const QSize expandedSize = data.params.getWindowSize();
const QSize correctedSize = {expandedSize.width(), expandedSize.height() - titleBarHeight};
data.params.setWindowSize(correctedSize);
});
} break;
case WM_DWMCOMPOSITIONCHANGED: {

View File

@ -46,7 +46,7 @@
#endif
#include <QtCore/qmutex.h>
#include <QtCore/qiodevice.h>
#include <QtGui/qguiapplication.h>
#include <QtCore/qcoreapplication.h>
#ifndef COMPILER_STRING
# ifdef Q_CC_CLANG // Must be before GNU, because Clang claims to be GNU too.
@ -72,6 +72,46 @@
# endif
#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &ver)
{
const QDebugStateSaver saver(d);
d.nospace().noquote() << "VersionNumber("
<< ver.major << ", "
<< ver.minor << ", "
<< ver.patch << ", "
<< ver.tweak << ')';
return d;
}
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &ver)
{
const QDebugStateSaver saver(d);
int major = 0, minor = 0, patch = 0, tweak = 0;
FRAMELESSHELPER_EXTRACT_VERSION(ver.version, major, minor, patch, tweak)
const auto ver_num = FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber{major, minor, patch, tweak};
d.nospace().noquote() << "VersionInfo("
<< "version number: " << ver_num << ", "
<< "version string: " << ver.version_str << ", "
<< "commit hash: " << ver.commit << ", "
<< "compiler: " << ver.compiler << ", "
<< "debug build: " << ver.isDebug << ", "
<< "static build: " << ver.isStatic << ')';
return d;
}
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi)
{
const QDebugStateSaver saver(d);
const qreal scaleFactor = (qreal(dpi.x) / qreal(96));
d.nospace().noquote() << "Dpi("
<< "x: " << dpi.x << ", "
<< "y: " << dpi.y << ", "
<< "scale factor: " << scaleFactor << ')';
return d;
}
#endif // QT_NO_DEBUG_STREAM
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global")
@ -172,15 +212,6 @@ void initialize()
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Non-integer scale factors will cause Qt have some painting defects
// for both Qt Widgets and Qt Quick applications, and it's still not
// totally fixed till now (Qt 6.5), so we round the scale factors to
// get a better looking. Non-integer scale factors will also cause
// flicker and jitter during window resizing.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif
qRegisterMetaType<Option>();
qRegisterMetaType<SystemTheme>();
qRegisterMetaType<SystemButtonType>();
@ -206,6 +237,7 @@ void initialize()
qRegisterMetaType<VersionNumber>();
qRegisterMetaType<SystemParameters>();
qRegisterMetaType<VersionInfo>();
qRegisterMetaType<Dpi>();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qRegisterMetaType<FramelessManager>();
# ifdef Q_OS_WINDOWS

View File

@ -220,7 +220,7 @@ void FramelessManagerPrivate::addWindow(const SystemParameters &params)
Q_UNUSED(screen);
// Add a little delay here, make sure it happens after Qt has processed the window
// messages.
QTimer::singleShot(50, window, [windowId, window](){
QTimer::singleShot(0, window, [windowId, window](){
// 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.
Utils::triggerFrameChange(windowId);

View File

@ -1735,7 +1735,7 @@ quint32 Utils::getResizeBorderThickness(const WId windowId, const bool horizonta
}
}
quint32 Utils::getCaptionHeight(const WId windowId, const bool scaled)
quint32 Utils::getCaptionBarHeight(const WId windowId, const bool scaled)
{
Q_ASSERT(windowId);
if (!windowId) {
@ -1750,7 +1750,7 @@ quint32 Utils::getTitleBarHeight(const WId windowId, const bool scaled)
if (!windowId) {
return 0;
}
return (getCaptionHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled));
return (getCaptionBarHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled));
}
quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled)