Fix QWindow::mapToGlobal()/mapFromGlobal() for multi-screen windows

Make these functions handle the case where a window spans multiple
screens, and high-DPI scaling is enabled, and the local position (in the
window) is not on the window primary screen (as returned by
QWindow::screen()).

This is done by detecting the case, and then calculating
the correct position using the native coordinate system.

[ChangeLog][QtGui] QWindow::mapToGlobal()/mapFromGlobal() now handle
windows spanning screens correctly.

Done-with: Friedemann Kleint<Friedemann.Kleint@qt.io>
Task-number: QTBUG-73231
Change-Id: I3c31b741344d9e85e4f5d9e60bae75acce2db741
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Morten Johan Sørvig 2019-01-30 13:55:15 +01:00
parent 3da9d8a4fe
commit 3af7b27917
4 changed files with 56 additions and 4 deletions

View File

@ -41,7 +41,9 @@
#include "qguiapplication.h"
#include "qscreen.h"
#include "qplatformintegration.h"
#include "qplatformwindow.h"
#include "private/qscreen_p.h"
#include <private/qguiapplication_p.h>
#include <QtCore/qdebug.h>
@ -376,6 +378,46 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform
return (pos - topLeft) / scaleFactor + topLeft;
}
QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
{
QPoint globalPosCandidate = pos + windowGlobalPosition;
if (QGuiApplicationPrivate::screen_list.size() <= 1)
return globalPosCandidate;
// The global position may be outside device independent screen geometry
// in cases where a window spans screens. Detect this case and map via
// native coordinates to the correct screen.
auto currentScreen = window->screen();
if (currentScreen && !currentScreen->geometry().contains(globalPosCandidate)) {
auto nativeGlobalPos = QHighDpi::toNativePixels(globalPosCandidate, currentScreen);
if (auto actualPlatformScreen = currentScreen->handle()->screenForPosition(nativeGlobalPos))
return QHighDpi::fromNativePixels(nativeGlobalPos, actualPlatformScreen->screen());
}
return globalPosCandidate;
}
QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
{
QPoint windowPosCandidate = pos - windowGlobalPosition;
if (QGuiApplicationPrivate::screen_list.size() <= 1)
return windowPosCandidate;
// Device independent global (screen) space may discontiguous when high-dpi scaling
// is active. This means that the normal subtracting of the window global position from the
// position-to-be-mapped may not work in cases where a window spans multiple screens.
// Map both positions to native global space (using the correct screens), subtract there,
// and then map the difference back using the scale factor for the window.
QScreen *posScreen = QGuiApplication::screenAt(pos);
if (posScreen && posScreen != window->screen()) {
QPoint nativePos = QHighDpi::toNativePixels(pos, posScreen);
QPoint windowNativePos = window->handle()->geometry().topLeft();
return QHighDpi::fromNativeLocalPosition(nativePos - windowNativePos, window);
}
return windowPosCandidate;
}
qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
{
qreal factor = qreal(1.0);

View File

@ -83,8 +83,10 @@ public:
static qreal factor(const QPlatformScreen *platformScreen);
static QPoint origin(const QScreen *screen);
static QPoint origin(const QPlatformScreen *platformScreen);
static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
static QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
static QDpi logicalDpi();
private:

View File

@ -1670,9 +1670,9 @@ void QWindow::setGeometry(const QRect &rect)
chicken and egg problem here: we cannot convert to native coordinates
before we know which screen we are on.
*/
QScreen *QWindowPrivate::screenForGeometry(const QRect &newGeometry)
QScreen *QWindowPrivate::screenForGeometry(const QRect &newGeometry) const
{
Q_Q(QWindow);
Q_Q(const QWindow);
QScreen *currentScreen = q->screen();
QScreen *fallback = currentScreen;
QPoint center = newGeometry.center();
@ -2542,6 +2542,10 @@ QPoint QWindow::mapToGlobal(const QPoint &pos) const
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapToGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this);
}
if (QHighDpiScaling::isActive())
return QHighDpiScaling::mapPositionToGlobal(pos, d->globalPosition(), this);
return pos + d->globalPosition();
}
@ -2562,6 +2566,10 @@ QPoint QWindow::mapFromGlobal(const QPoint &pos) const
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this);
}
if (QHighDpiScaling::isActive())
return QHighDpiScaling::mapPositionFromGlobal(pos, d->globalPosition(), this);
return pos - d->globalPosition();
}

View File

@ -148,7 +148,7 @@ public:
void connectToScreen(QScreen *topLevelScreen);
void disconnectFromScreen();
void emitScreenChangedRecursion(QScreen *newScreen);
QScreen *screenForGeometry(const QRect &rect);
QScreen *screenForGeometry(const QRect &rect) const;
virtual void clearFocusObject();
virtual QRectF closestAcceptableGeometry(const QRectF &rect) const;