diff options
-rw-r--r-- | src/plugins/platforms/windows/qwindowscontext.cpp | 66 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowscontext.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.cpp | 30 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.h | 2 |
4 files changed, 74 insertions, 28 deletions
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 40d4cb1497..c8c09bebff 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -380,6 +380,11 @@ void QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreat d->m_creationContext = ctx; } +QSharedPointer<QWindowCreationContext> QWindowsContext::windowCreationContext() const +{ + return d->m_creationContext; +} + int QWindowsContext::defaultDPI() const { return d->m_defaultDPI; @@ -808,7 +813,9 @@ static inline QWindowsInputContext *windowsInputContext() bool QWindowsContext::windowsProc(HWND hwnd, UINT message, QtWindows::WindowsEventType et, - WPARAM wParam, LPARAM lParam, LRESULT *result) + WPARAM wParam, LPARAM lParam, + LRESULT *result, + QWindowsWindow **platformWindowPtr) { *result = 0; @@ -839,6 +846,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, } QWindowsWindow *platformWindow = findPlatformWindow(hwnd); + *platformWindowPtr = platformWindow; if (platformWindow) { filterResult = 0; if (QWindowSystemInterface::handleNativeEvent(platformWindow->window(), d->m_eventType, &msg, &filterResult)) { @@ -1020,9 +1028,6 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; case QtWindows::ThemeChanged: { // Switch from Aero to Classic changes margins. - const Qt::WindowFlags flags = platformWindow->window()->flags(); - if ((flags & Qt::WindowType_Mask) != Qt::Desktop && !(flags & Qt::FramelessWindowHint)) - platformWindow->setFlag(QWindowsWindow::FrameDirty); if (QWindowsTheme *theme = QWindowsTheme::instance()) theme->windowsThemeChanged(platformWindow->window()); return true; @@ -1195,6 +1200,37 @@ QTouchDevice *QWindowsContext::touchDevice() const return d->m_mouseHandler.touchDevice(); } +static inline bool isEmptyRect(const RECT &rect) +{ + return rect.right - rect.left == 0 && rect.bottom - rect.top == 0; +} + +static inline QMargins marginsFromRects(const RECT &frame, const RECT &client) +{ + return QMargins(client.left - frame.left, client.top - frame.top, + frame.right - client.right, frame.bottom - client.bottom); +} + +static RECT rectFromNcCalcSize(UINT message, WPARAM wParam, LPARAM lParam, int n) +{ + RECT result = {0, 0, 0, 0}; + if (message == WM_NCCALCSIZE && wParam) + result = reinterpret_cast<const NCCALCSIZE_PARAMS *>(lParam)->rgrc[n]; + return result; +} + +static inline bool isMinimized(HWND hwnd) +{ + WINDOWPLACEMENT windowPlacement; + windowPlacement.length = sizeof(WINDOWPLACEMENT); + return GetWindowPlacement(hwnd, &windowPlacement) && windowPlacement.showCmd == SW_SHOWMINIMIZED; +} + +static inline bool isTopLevel(HWND hwnd) +{ + return (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) == 0; +} + /*! \brief Windows functions for actual windows. @@ -1208,7 +1244,9 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR { LRESULT result; const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam); - const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result); + QWindowsWindow *platformWindow = nullptr; + const RECT ncCalcSizeFrame = rectFromNcCalcSize(message, wParam, lParam, 0); + const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result, &platformWindow); if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) { if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) { qCDebug(lcQpaEvents) << "EVENT: hwd=" << hwnd << eventName << hex << "msg=0x" << message @@ -1218,6 +1256,24 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR } if (!handled) result = DefWindowProc(hwnd, message, wParam, lParam); + + // Capture WM_NCCALCSIZE on top level windows and obtain the window margins by + // subtracting the rectangles before and after processing. This will correctly + // capture client code overriding the message and allow for per-monitor margins + // for High DPI (QTBUG-53255, QTBUG-40578). + if (message == WM_NCCALCSIZE && !isEmptyRect(ncCalcSizeFrame) && isTopLevel(hwnd) && !isMinimized(hwnd)) { + const QMargins margins = + marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); + if (margins.left() >= 0) { + if (platformWindow) { + platformWindow->setFrameMargins(margins); + } else { + const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext(); + if (!ctx.isNull()) + ctx->margins = margins; + } + } + } return result; } diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index 9dfde67797..41405f8efc 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -181,12 +181,14 @@ public: inline bool windowsProc(HWND hwnd, UINT message, QtWindows::WindowsEventType et, - WPARAM wParam, LPARAM lParam, LRESULT *result); + WPARAM wParam, LPARAM lParam, LRESULT *result, + QWindowsWindow **platformWindowPtr); QWindow *keyGrabber() const; void setKeyGrabber(QWindow *hwnd); void setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx); + QSharedPointer<QWindowCreationContext> windowCreationContext() const; void setTabletAbsoluteRange(int a); void setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 2d9a683da6..2f7abc6de8 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1681,7 +1681,6 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt, QWindowsWindowData result = m_data; result.flags = creationData.flags; result.embedded = creationData.embedded; - setFlag(FrameDirty); return result; } @@ -1689,7 +1688,6 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowState state) { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << "\n from " << m_windowState << " to " << state; - setFlag(FrameDirty); m_windowState = state; QWindowSystemInterface::handleWindowStateChanged(window(), state); switch (state) { @@ -1766,8 +1764,6 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) const bool visible = isVisible(); - setFlag(FrameDirty); - if ((oldState == Qt::WindowFullScreen) != (newState == Qt::WindowFullScreen)) { if (newState == Qt::WindowFullScreen) { #ifndef Q_FLATTEN_EXPOSE @@ -1861,7 +1857,6 @@ void QWindowsWindow::setStyle(unsigned s) const { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinStyle(s); setFlag(WithinSetStyle); - setFlag(FrameDirty); SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s); clearFlag(WithinSetStyle); } @@ -1870,7 +1865,6 @@ void QWindowsWindow::setExStyle(unsigned s) const { qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << this << ' ' << window() << " 0x" << QByteArray::number(s, 16); - setFlag(FrameDirty); SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s); } @@ -1926,22 +1920,17 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins); } -QMargins QWindowsWindow::frameMargins() const +void QWindowsWindow::setFrameMargins(const QMargins &newMargins) { - // Frames are invalidated by style changes (window state, flags). - // As they are also required for geometry calculations in resize - // event sequences, introduce a dirty flag mechanism to be able - // to cache results. - if (testFlag(FrameDirty)) { - // Always skip calculating style-dependent margins for windows claimed to be frameless. - // This allows users to remove the margins by handling WM_NCCALCSIZE with WS_THICKFRAME set - // to ensure Areo snap still works (QTBUG-40578). - m_data.frame = m_data.flags & Qt::FramelessWindowHint - ? QMargins(0, 0, 0, 0) - : QWindowsGeometryHint::frame(style(), exStyle()); - clearFlag(FrameDirty); + if (m_data.frame != newMargins) { + qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.frame << "->" << newMargins; + m_data.frame = newMargins; } - return m_data.frame + m_data.customMargins; +} + +QMargins QWindowsWindow::frameMargins() const +{ + return m_data.frame; } void QWindowsWindow::setOpacity(qreal level) @@ -2357,7 +2346,6 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) const QPoint topLeft = currentFrameGeometry.topLeft(); QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; newFrame.moveTo(topLeft); - setFlag(FrameDirty); qCDebug(lcQpaWindows) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins << currentFrameGeometry << "->" << newFrame; SetWindowPos(m_data.hwnd, 0, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 90b0940813..dd433a3ca8 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -189,7 +189,6 @@ public: { AutoMouseCapture = 0x1, //! Automatic mouse capture on button press. WithinSetParent = 0x2, - FrameDirty = 0x4, //! Frame outdated by setStyle, recalculate in next query. OpenGLSurface = 0x10, OpenGL_ES2 = 0x20, OpenGLDoubleBuffered = 0x40, @@ -244,6 +243,7 @@ public: static bool handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp); bool handleGeometryChanging(MSG *message) const; QMargins frameMargins() const Q_DECL_OVERRIDE; + void setFrameMargins(const QMargins &newMargins); void setOpacity(qreal level) Q_DECL_OVERRIDE; void setMask(const QRegion ®ion) Q_DECL_OVERRIDE; |