Windows: Add support for invisible window margins
Windows 10 windows contain an invisible area within the NC window frame on which the mouse cursor is enabled to perform resizing. This change captures the geometry of the invisible margins and considers it when moving a window, so that, for instance, a move(0,0) does not generate gap between the window and the beginning of screen. [ChangeLog][Windows] The dimensions of invisible margins inside the frames of Windows 10 windows will now be disregarded in the positioning of Qt windows to avoid a misplaced look (offset by a few pixels from the expected position). Task-number: QTBUG-55762 Change-Id: I1f537756eb1a093f78b919de9d44992528199700 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>bb10
parent
7c3ecf85a7
commit
ec97be5585
|
|
@ -1401,7 +1401,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR
|
|||
marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0));
|
||||
if (margins.left() >= 0) {
|
||||
if (platformWindow) {
|
||||
platformWindow->setFrameMargins(margins);
|
||||
platformWindow->setFullFrameMargins(margins);
|
||||
} else {
|
||||
const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext();
|
||||
if (!ctx.isNull())
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
|
|||
<< "\n Requested: " << requested.geometry << " frame incl.="
|
||||
<< QWindowsGeometryHint::positionIncludesFrame(window)
|
||||
<< ' ' << requested.flags
|
||||
<< "\n Obtained : " << obtained.geometry << " margins=" << obtained.frame
|
||||
<< "\n Obtained : " << obtained.geometry << " margins=" << obtained.fullFrameMargins
|
||||
<< " handle=" << obtained.hwnd << ' ' << obtained.flags << '\n';
|
||||
|
||||
if (Q_UNLIKELY(!obtained.hwnd))
|
||||
|
|
|
|||
|
|
@ -421,6 +421,31 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo
|
|||
setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity);
|
||||
}
|
||||
|
||||
/*!
|
||||
Calculates the dimensions of the invisible borders within the
|
||||
window frames in Windows 10, using an empirical expression that
|
||||
reproduces the measured values for standard DPI settings.
|
||||
*/
|
||||
|
||||
static QMargins invisibleMargins(QPoint screenPoint)
|
||||
{
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
|
||||
POINT pt = {screenPoint.x(), screenPoint.y()};
|
||||
if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
|
||||
if (QWindowsContext::shcoredll.isValid()) {
|
||||
UINT dpiX;
|
||||
UINT dpiY;
|
||||
if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) {
|
||||
const qreal sc = (dpiX - 96) / 96.0;
|
||||
const int gap = 7 + qRound(5*sc) - int(sc);
|
||||
return QMargins(gap, 0, gap, gap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return QMargins();
|
||||
}
|
||||
|
||||
/*!
|
||||
\class WindowCreationData
|
||||
\brief Window creation code.
|
||||
|
|
@ -651,16 +676,20 @@ QWindowsWindowData
|
|||
const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle));
|
||||
QWindowsContext::instance()->setWindowCreationContext(context);
|
||||
|
||||
QMargins invMargins = topLevel && !(result.flags & Qt::FramelessWindowHint) && QWindowsGeometryHint::positionIncludesFrame(w)
|
||||
? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins();
|
||||
|
||||
qCDebug(lcQpaWindows).nospace()
|
||||
<< "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title
|
||||
<< '\n' << *this << "\nrequested: " << rect << ": "
|
||||
<< context->frameWidth << 'x' << context->frameHeight
|
||||
<< '+' << context->frameX << '+' << context->frameY
|
||||
<< " custom margins: " << context->customMargins;
|
||||
<< " custom margins: " << context->customMargins
|
||||
<< " invisible margins: " << invMargins;
|
||||
|
||||
result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
|
||||
style,
|
||||
context->frameX, context->frameY,
|
||||
context->frameX - invMargins.left(), context->frameY - invMargins.top(),
|
||||
context->frameWidth, context->frameHeight,
|
||||
parentHandle, NULL, appinst, NULL);
|
||||
qCDebug(lcQpaWindows).nospace()
|
||||
|
|
@ -673,7 +702,7 @@ QWindowsWindowData
|
|||
}
|
||||
|
||||
result.geometry = context->obtainedGeometry;
|
||||
result.frame = context->margins;
|
||||
result.fullFrameMargins = context->margins;
|
||||
result.embedded = embedded;
|
||||
result.customMargins = context->customMargins;
|
||||
|
||||
|
|
@ -887,7 +916,7 @@ QRect QWindowsBaseWindow::frameGeometry_sys() const
|
|||
|
||||
QRect QWindowsBaseWindow::geometry_sys() const
|
||||
{
|
||||
return frameGeometry_sys().marginsRemoved(frameMargins());
|
||||
return frameGeometry_sys().marginsRemoved(fullFrameMargins());
|
||||
}
|
||||
|
||||
QMargins QWindowsBaseWindow::frameMargins_sys() const
|
||||
|
|
@ -1560,7 +1589,7 @@ QRect QWindowsWindow::normalGeometry() const
|
|||
const bool fakeFullScreen =
|
||||
m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
|
||||
const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
|
||||
const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMargins();
|
||||
const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : fullFrameMargins();
|
||||
return frame.isValid() ? frame.marginsRemoved(margins) : frame;
|
||||
}
|
||||
|
||||
|
|
@ -1592,8 +1621,8 @@ void QWindowsWindow::setGeometry(const QRect &rectIn)
|
|||
window()->metaObject()->className(), qPrintable(window()->objectName()),
|
||||
m_data.geometry.width(), m_data.geometry.height(),
|
||||
m_data.geometry.x(), m_data.geometry.y(),
|
||||
m_data.frame.left(), m_data.frame.top(),
|
||||
m_data.frame.right(), m_data.frame.bottom(),
|
||||
m_data.fullFrameMargins.left(), m_data.fullFrameMargins.top(),
|
||||
m_data.fullFrameMargins.right(), m_data.fullFrameMargins.bottom(),
|
||||
m_data.customMargins.left(), m_data.customMargins.top(),
|
||||
m_data.customMargins.right(), m_data.customMargins.bottom(),
|
||||
window()->minimumWidth(), window()->minimumHeight(),
|
||||
|
|
@ -1685,7 +1714,7 @@ void QWindowsWindow::handleGeometryChange()
|
|||
|
||||
void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
|
||||
{
|
||||
const QMargins margins = frameMargins();
|
||||
const QMargins margins = fullFrameMargins();
|
||||
const QRect frameGeometry = rect + margins;
|
||||
|
||||
qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window()
|
||||
|
|
@ -2106,21 +2135,29 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *
|
|||
|
||||
bool QWindowsWindow::handleGeometryChanging(MSG *message) const
|
||||
{
|
||||
const QMargins margins = window()->isTopLevel() ? frameMargins() : QMargins();
|
||||
const QMargins margins = window()->isTopLevel() ? fullFrameMargins() : QMargins();
|
||||
return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins);
|
||||
}
|
||||
|
||||
void QWindowsWindow::setFrameMargins(const QMargins &newMargins)
|
||||
void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
|
||||
{
|
||||
if (m_data.frame != newMargins) {
|
||||
qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.frame << "->" << newMargins;
|
||||
m_data.frame = newMargins;
|
||||
if (m_data.fullFrameMargins != newMargins) {
|
||||
qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins;
|
||||
m_data.fullFrameMargins = newMargins;
|
||||
}
|
||||
}
|
||||
|
||||
QMargins QWindowsWindow::frameMargins() const
|
||||
{
|
||||
return m_data.frame;
|
||||
QMargins result = fullFrameMargins();
|
||||
if (isTopLevel() && !(m_data.flags & Qt::FramelessWindowHint))
|
||||
result -= invisibleMargins(geometry().topLeft());
|
||||
return result;
|
||||
}
|
||||
|
||||
QMargins QWindowsWindow::fullFrameMargins() const
|
||||
{
|
||||
return m_data.fullFrameMargins;
|
||||
}
|
||||
|
||||
void QWindowsWindow::setOpacity(qreal level)
|
||||
|
|
@ -2174,7 +2211,7 @@ void QWindowsWindow::setMask(const QRegion ®ion)
|
|||
|
||||
// Mask is in client area coordinates, so offset it in case we have a frame
|
||||
if (window()->isTopLevel()) {
|
||||
const QMargins margins = frameMargins();
|
||||
const QMargins margins = fullFrameMargins();
|
||||
OffsetRgn(winRegion, margins.left(), margins.top());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ struct QWindowsWindowData
|
|||
{
|
||||
Qt::WindowFlags flags;
|
||||
QRect geometry;
|
||||
QMargins frame; // Do not use directly for windows, see FrameDirty.
|
||||
QMargins customMargins; // User-defined, additional frame for NCCALCSIZE
|
||||
QMargins fullFrameMargins; // Do not use directly for windows, see FrameDirty.
|
||||
QMargins customMargins; // User-defined, additional frame for NCCALCSIZE
|
||||
HWND hwnd = 0;
|
||||
bool embedded = false;
|
||||
|
||||
|
|
@ -125,9 +125,10 @@ public:
|
|||
|
||||
WId winId() const override { return WId(handle()); }
|
||||
QRect geometry() const override { return geometry_sys(); }
|
||||
QMargins frameMargins() const override { return frameMargins_sys(); }
|
||||
QMargins frameMargins() const override { return fullFrameMargins(); }
|
||||
QPoint mapToGlobal(const QPoint &pos) const override;
|
||||
QPoint mapFromGlobal(const QPoint &pos) const override;
|
||||
virtual QMargins fullFrameMargins() const { return frameMargins_sys(); }
|
||||
|
||||
using QPlatformWindow::screenForGeometry;
|
||||
|
||||
|
|
@ -258,7 +259,8 @@ public:
|
|||
static bool handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp);
|
||||
bool handleGeometryChanging(MSG *message) const;
|
||||
QMargins frameMargins() const override;
|
||||
void setFrameMargins(const QMargins &newMargins);
|
||||
QMargins fullFrameMargins() const override;
|
||||
void setFullFrameMargins(const QMargins &newMargins);
|
||||
|
||||
void setOpacity(qreal level) override;
|
||||
void setMask(const QRegion ®ion) override;
|
||||
|
|
|
|||
|
|
@ -3798,9 +3798,10 @@ void tst_QWidget::optimizedResize_topLevel()
|
|||
// a native function call works (it basically has to go through
|
||||
// WM_RESIZE in QApplication). This is a corner case, though.
|
||||
// See task 243708
|
||||
const QRect frame = topLevel.frameGeometry();
|
||||
MoveWindow(winHandleOf(&topLevel), frame.x(), frame.y(),
|
||||
frame.width() + 10, frame.height() + 10,
|
||||
RECT rect;
|
||||
GetWindowRect(winHandleOf(&topLevel), &rect);
|
||||
MoveWindow(winHandleOf(&topLevel), rect.left, rect.top,
|
||||
rect.right - rect.left + 10, rect.bottom - rect.top + 10,
|
||||
true);
|
||||
QTest::qWait(100);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue