Ensure all platforms send geometry events if request was not fulfilled

The logic of deciding whether or not to send resize and move events
has been centralized in QGuiApplication. This ensures that if a
window with geometry 100,100+200x200 is moved and resized to e.g.
0,0+100x100, but the window manager denies the request (because the
window would e.g. overlap with system UI), and issues a geometry
update with the original geometry, 100,100+200x200, we will still
treat that as warrant of a move/resize event to the application,
so the application knows that its position and size is as before.

[ChangeLog][Qt Gui][QPA] QWindowSystemInterfacePrivate::handleGeometryChange
no longer takes the old geometry as an argument.

Task-number: QTBUG-57608
Change-Id: I1d471cc7a257fef958bdb1e56184fa95489403a3
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-06-28 13:56:47 +02:00
parent 9381ca77b2
commit 3a31c70879
7 changed files with 55 additions and 53 deletions

View File

@ -2213,7 +2213,7 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf
}
// we may have changed scaling, so trigger resize event if needed
if (window->handle()) {
QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window), QRect());
QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window));
processGeometryChangeEvent(&gce);
}
}
@ -2238,35 +2238,46 @@ void QGuiApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePr
if (!window)
return;
QRect newRect = e->newGeometry;
QRect oldRect = e->oldGeometry.isNull() ? window->d_func()->geometry : e->oldGeometry;
const QRect lastReportedGeometry = window->d_func()->geometry;
const QRect requestedGeometry = e->requestedGeometry;
const QRect actualGeometry = e->newGeometry;
bool isResize = oldRect.size() != newRect.size();
bool isMove = oldRect.topLeft() != newRect.topLeft();
// We send size and move events only if the geometry has changed from
// what was last reported, or if the user tried to set a new geometry,
// but the window manager responded by keeping the old geometry. In the
// latter case we send move/resize events with the same geometry as the
// last reported geometry, to indicate that the window wasn't moved or
// resized. Note that this logic does not apply to the property changes
// of the window, as we don't treat them as part of this request/response
// protocol of QWindow/QPA.
const bool isResize = actualGeometry.size() != lastReportedGeometry.size()
|| requestedGeometry.size() != actualGeometry.size();
const bool isMove = actualGeometry.topLeft() != lastReportedGeometry.topLeft()
|| requestedGeometry.topLeft() != actualGeometry.topLeft();
window->d_func()->geometry = newRect;
window->d_func()->geometry = actualGeometry;
if (isResize || window->d_func()->resizeEventPending) {
QResizeEvent e(newRect.size(), oldRect.size());
QResizeEvent e(actualGeometry.size(), lastReportedGeometry.size());
QGuiApplication::sendSpontaneousEvent(window, &e);
window->d_func()->resizeEventPending = false;
if (oldRect.width() != newRect.width())
window->widthChanged(newRect.width());
if (oldRect.height() != newRect.height())
window->heightChanged(newRect.height());
if (actualGeometry.width() != lastReportedGeometry.width())
window->widthChanged(actualGeometry.width());
if (actualGeometry.height() != lastReportedGeometry.height())
window->heightChanged(actualGeometry.height());
}
if (isMove) {
//### frame geometry
QMoveEvent e(newRect.topLeft(), oldRect.topLeft());
QMoveEvent e(actualGeometry.topLeft(), lastReportedGeometry.topLeft());
QGuiApplication::sendSpontaneousEvent(window, &e);
if (oldRect.x() != newRect.x())
window->xChanged(newRect.x());
if (oldRect.y() != newRect.y())
window->yChanged(newRect.y());
if (actualGeometry.x() != lastReportedGeometry.x())
window->xChanged(actualGeometry.x());
if (actualGeometry.y() != lastReportedGeometry.y())
window->yChanged(actualGeometry.y());
}
}

View File

@ -273,13 +273,18 @@ void QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
/*!
If \a oldRect is null, Qt will use the previously reported geometry instead.
*/
QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect, const QRect &oldRect)
QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry)
: WindowSystemEvent(GeometryChange)
, window(window)
, requestedGeometry(window->handle() ? window->handle()->QPlatformWindow::geometry() : QRect())
, newGeometry(newGeometry)
{
}
QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect)
{
Q_ASSERT(window);
QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(window, QHighDpi::fromNativePixels(newRect, window), QHighDpi::fromNativePixels(oldRect, window));
QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(window, QHighDpi::fromNativePixels(newRect, window));
if (window->handle()) {
// Persist the new geometry so that QWindow::geometry() can be queried in the resize event
window->handle()->QPlatformWindow::setGeometry(newRect);

View File

@ -160,7 +160,7 @@ public:
// rect is relative to parent
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleGeometryChange(QWindow *window, const QRect &newRect, const QRect &oldRect = QRect());
static void handleGeometryChange(QWindow *window, const QRect &newRect);
// region is in local coordinates, do not confuse with geometry which is parent-relative
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>

View File

@ -131,12 +131,10 @@ public:
class GeometryChangeEvent : public WindowSystemEvent {
public:
GeometryChangeEvent(QWindow *window, const QRect &newGeometry, const QRect &oldGeometry)
: WindowSystemEvent(GeometryChange), window(window), newGeometry(newGeometry), oldGeometry(oldGeometry)
{ }
GeometryChangeEvent(QWindow *window, const QRect &newGeometry);
QPointer<QWindow> window;
QRect requestedGeometry;
QRect newGeometry;
QRect oldGeometry;
};
class EnterEvent : public WindowSystemEvent {

View File

@ -41,6 +41,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformintegration.h>
#include <private/qguiapplication_p.h>
#include <private/qwindow_p.h>
#ifndef QT_NO_OPENGL
# include <QtGui/private/qopenglcontext_p.h>
# include <QtGui/QOpenGLContext>
@ -234,21 +235,19 @@ void QEglFSWindow::setVisible(bool visible)
void QEglFSWindow::setGeometry(const QRect &r)
{
QRect rect;
bool forceFullscreen = m_flags.testFlag(HasNativeWindow);
if (forceFullscreen)
// Persist the requested rect, like a normal setGeometry call
QPlatformWindow::setGeometry(r);
// Take care of WM behavior, constrain/modify geometry
QRect rect = r;
if (m_flags.testFlag(HasNativeWindow))
rect = screen()->availableGeometry();
else
rect = r;
const bool changed = rect != QPlatformWindow::geometry();
QPlatformWindow::setGeometry(rect);
// React to the setGeometry, as if from a WM callback
QRect lastReportedGeometry = qt_window_private(window())->geometry;
QWindowSystemInterface::handleGeometryChange(window(), rect);
// if we corrected the size, trigger a resize event
if (rect != r)
QWindowSystemInterface::handleGeometryChange(window(), rect, r);
if (changed)
if (rect != lastReportedGeometry)
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}

View File

@ -151,19 +151,12 @@
qWarning() << m_qioswindow->window()
<< "is backed by a UIView that has a transform set. This is not supported.";
// The original geometry requested by setGeometry() might be different
// from what we end up with after applying window constraints.
QRect requestedGeometry = m_qioswindow->geometry();
QRect actualGeometry = QRectF::fromCGRect(self.frame).toRect();
QRect previousGeometry = requestedGeometry != actualGeometry ?
requestedGeometry : qt_window_private(m_qioswindow->window())->geometry;
QWindow *window = m_qioswindow->window();
QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(window, actualGeometry, previousGeometry);
QRect lastReportedGeometry = qt_window_private(window)->geometry;
QRect currentGeometry = QRectF::fromCGRect(self.frame).toRect();
QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(window, currentGeometry);
if (actualGeometry.size() != previousGeometry.size()) {
if (currentGeometry.size() != lastReportedGeometry.size()) {
// Trigger expose event on resize
[self setNeedsDisplay];

View File

@ -2077,10 +2077,6 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
if (!newScreen)
return;
// FIXME: In the case of the requestedGeometry not matching the actualGeometry due
// to e.g. the window manager applying restrictions to the geometry, the application
// will never see a move/resize event if the actualGeometry is the same as the current
// geometry, and may think the requested geometry was fulfilled.
QWindowSystemInterface::handleGeometryChange(window(), actualGeometry);
// QPlatformScreen::screen() is updated asynchronously, so we can't compare it