2022-05-10 10:06:48 +00:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2024-02-02 13:36:10 +00:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
2011-08-29 06:26:37 +00:00
|
|
|
|
2014-08-28 10:59:14 +00:00
|
|
|
#include <qrasterwindow.h>
|
2012-07-26 11:23:28 +00:00
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
2013-03-11 13:41:52 +00:00
|
|
|
#include <qpa/qplatformintegration.h>
|
2018-10-08 08:10:07 +00:00
|
|
|
#include <qpa/qplatformwindow.h>
|
2013-03-11 13:41:52 +00:00
|
|
|
#include <private/qguiapplication_p.h>
|
2015-06-18 15:01:01 +00:00
|
|
|
#include <private/qhighdpiscaling_p.h>
|
Add dedicated child and parent events for QWindow reparenting
When a QWindow is parented into another window, either via its constructor,
or via setParent(), we also update the QObject parent of the window. This
in turn sends ChildAdded and ChildRemoved events to the new and old parent
of the window, as well as ParentAboutToChange and ParentChange to the window
itself.
But at the point when these events are sent, the QWindow parent relationship
has not been fully established. We have not updated d->parentWindow, nor
have we propagated the parent relationship to the platform window.
This is problematic because clients can not use these events to react to
window parent changes synchronously. For example. trying to raise a child
window when added to a parent is not going to work, because at that point
the child window isn't a native child of the parent.
Instead of re-using the QObject events for QWindow, like QWidget does,
by delaying the events or sending them manually at a point when the
window parent relationship has been fully formed, we opt to add new
dedicated events.
Change-Id: I019c14eba444861f537e4f68ab3a82297f843cf7
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-11-04 10:17:30 +00:00
|
|
|
#include <private/qwindow_p.h>
|
2014-08-28 10:59:14 +00:00
|
|
|
#include <QtGui/QPainter>
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2020-11-26 16:31:50 +00:00
|
|
|
#include <QTest>
|
2025-01-21 14:29:36 +00:00
|
|
|
#include <QtTest/private/qtesthelpers_p.h>
|
2020-11-26 16:31:50 +00:00
|
|
|
#include <QSignalSpy>
|
2011-12-12 15:24:33 +00:00
|
|
|
#include <QEvent>
|
2012-03-24 08:48:12 +00:00
|
|
|
#include <QStyleHints>
|
2011-12-12 15:24:33 +00:00
|
|
|
|
2014-03-18 16:18:04 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
#include <QOpenGLContext>
|
2020-06-05 07:24:37 +00:00
|
|
|
#elif defined(Q_OS_WIN)
|
2014-08-28 10:59:14 +00:00
|
|
|
# include <QtCore/qt_windows.h>
|
2014-03-18 16:18:04 +00:00
|
|
|
#endif
|
|
|
|
|
2020-09-08 06:45:24 +00:00
|
|
|
Q_LOGGING_CATEGORY(lcTests, "qt.gui.tests")
|
|
|
|
|
2023-10-19 09:35:21 +00:00
|
|
|
static bool isPlatformEglFS()
|
|
|
|
{
|
|
|
|
static const bool isEglFS = !QGuiApplication::platformName().compare(QLatin1String("eglfs"), Qt::CaseInsensitive);
|
|
|
|
return isEglFS;
|
|
|
|
}
|
|
|
|
|
2011-08-29 06:26:37 +00:00
|
|
|
class tst_QWindow: public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
private slots:
|
2015-10-16 13:04:49 +00:00
|
|
|
void create();
|
2015-10-16 13:28:31 +00:00
|
|
|
void setParent();
|
2015-10-16 14:41:34 +00:00
|
|
|
void setVisible();
|
2023-10-18 17:02:08 +00:00
|
|
|
void setVisibleThenCreate();
|
2017-02-10 14:57:11 +00:00
|
|
|
void setVisibleFalseDoesNotCreateWindow();
|
2012-03-14 16:55:43 +00:00
|
|
|
void eventOrderOnShow();
|
2020-08-24 16:59:54 +00:00
|
|
|
void paintEvent();
|
2013-07-26 15:51:42 +00:00
|
|
|
void resizeEventAfterResize();
|
2016-06-11 16:39:23 +00:00
|
|
|
void exposeEventOnShrink_QTBUG54040();
|
2011-08-29 06:26:37 +00:00
|
|
|
void mapGlobal();
|
2011-09-22 12:37:58 +00:00
|
|
|
void positioning();
|
2024-07-03 14:47:49 +00:00
|
|
|
void framePositioning();
|
|
|
|
void framePositioning_data();
|
2024-08-27 09:20:59 +00:00
|
|
|
void framePositioningStableAfterDestroy();
|
2025-04-09 09:37:13 +00:00
|
|
|
void geometryAfterWmUpdateAndDestroyCreate();
|
2014-06-19 11:45:06 +00:00
|
|
|
void positioningDuringMinimized();
|
2015-10-16 15:36:20 +00:00
|
|
|
void childWindowPositioning_data();
|
|
|
|
void childWindowPositioning();
|
2016-10-24 16:35:32 +00:00
|
|
|
void childWindowLevel();
|
2014-11-15 11:40:51 +00:00
|
|
|
void platformSurface();
|
2012-03-14 16:55:43 +00:00
|
|
|
void isExposed();
|
2011-09-29 16:02:54 +00:00
|
|
|
void isActive();
|
2011-12-12 15:24:33 +00:00
|
|
|
void testInputEvents();
|
2012-01-12 07:53:13 +00:00
|
|
|
void touchToMouseTranslation();
|
2014-12-09 15:52:24 +00:00
|
|
|
void touchToMouseTranslationForDevices();
|
2012-01-12 07:53:13 +00:00
|
|
|
void mouseToTouchTranslation();
|
|
|
|
void mouseToTouchLoop();
|
2012-02-09 12:39:40 +00:00
|
|
|
void touchCancel();
|
|
|
|
void touchCancelWithTouchToMouse();
|
2014-03-11 21:10:05 +00:00
|
|
|
void touchInterruptedByPopup();
|
2012-01-13 09:31:11 +00:00
|
|
|
void orientation();
|
2012-11-01 16:03:45 +00:00
|
|
|
void sizes();
|
2012-01-20 12:08:42 +00:00
|
|
|
void close();
|
2012-02-24 17:05:06 +00:00
|
|
|
void activateAndClose();
|
2012-03-24 08:48:12 +00:00
|
|
|
void mouseEventSequence();
|
2012-04-04 10:11:40 +00:00
|
|
|
void windowModality();
|
2012-04-23 13:18:01 +00:00
|
|
|
void inputReentrancy();
|
2023-02-22 11:03:16 +00:00
|
|
|
void tabletEvents_data();
|
2012-05-23 12:07:39 +00:00
|
|
|
void tabletEvents();
|
2012-09-21 10:12:01 +00:00
|
|
|
void windowModality_QTBUG27039();
|
2013-02-08 09:49:52 +00:00
|
|
|
void visibility();
|
2013-02-25 07:54:30 +00:00
|
|
|
void mask();
|
2014-01-09 11:45:14 +00:00
|
|
|
void initialSize();
|
2014-04-03 11:11:59 +00:00
|
|
|
void modalDialog();
|
|
|
|
void modalDialogClosingOneOfTwoModal();
|
|
|
|
void modalWithChildWindow();
|
|
|
|
void modalWindowModallity();
|
2014-05-19 17:58:35 +00:00
|
|
|
void modalWindowPosition();
|
2022-10-10 12:25:45 +00:00
|
|
|
void modalCloseWhileBlocked();
|
2016-01-19 21:32:52 +00:00
|
|
|
#ifndef QT_NO_CURSOR
|
|
|
|
void modalWindowEnterEventOnHide_QTBUG35109();
|
2017-12-20 13:07:17 +00:00
|
|
|
void spuriousMouseMove();
|
2016-01-19 21:32:52 +00:00
|
|
|
#endif
|
2014-08-28 10:59:14 +00:00
|
|
|
void windowsTransientChildren();
|
2015-02-12 14:28:12 +00:00
|
|
|
void requestUpdate();
|
2014-07-31 11:48:15 +00:00
|
|
|
void initTestCase();
|
2015-12-09 17:06:20 +00:00
|
|
|
void stateChange_data();
|
|
|
|
void stateChange();
|
2016-11-02 20:20:26 +00:00
|
|
|
void flags();
|
2014-07-24 13:11:27 +00:00
|
|
|
void cleanup();
|
2017-08-15 08:29:16 +00:00
|
|
|
void testBlockingWindowShownAfterModalDialog();
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
void generatedMouseMove();
|
2018-10-08 08:10:07 +00:00
|
|
|
void keepPendingUpdateRequests();
|
2021-06-11 12:17:35 +00:00
|
|
|
void activateDeactivateEvent();
|
2021-12-25 16:23:40 +00:00
|
|
|
void qobject_castOnDestruction();
|
2022-05-24 11:00:07 +00:00
|
|
|
void touchToMouseTranslationByPopup();
|
2022-10-28 13:23:41 +00:00
|
|
|
void stateChangeSignal();
|
2021-09-22 09:43:57 +00:00
|
|
|
#ifndef QT_NO_CURSOR
|
|
|
|
void enterLeaveOnWindowShowHide_data();
|
|
|
|
void enterLeaveOnWindowShowHide();
|
|
|
|
#endif
|
2023-10-27 12:59:36 +00:00
|
|
|
void windowExposedAfterReparent();
|
Add dedicated child and parent events for QWindow reparenting
When a QWindow is parented into another window, either via its constructor,
or via setParent(), we also update the QObject parent of the window. This
in turn sends ChildAdded and ChildRemoved events to the new and old parent
of the window, as well as ParentAboutToChange and ParentChange to the window
itself.
But at the point when these events are sent, the QWindow parent relationship
has not been fully established. We have not updated d->parentWindow, nor
have we propagated the parent relationship to the platform window.
This is problematic because clients can not use these events to react to
window parent changes synchronously. For example. trying to raise a child
window when added to a parent is not going to work, because at that point
the child window isn't a native child of the parent.
Instead of re-using the QObject events for QWindow, like QWidget does,
by delaying the events or sending them manually at a point when the
window parent relationship has been fully formed, we opt to add new
dedicated events.
Change-Id: I019c14eba444861f537e4f68ab3a82297f843cf7
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-11-04 10:17:30 +00:00
|
|
|
void childEvents();
|
|
|
|
void parentEvents();
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
private:
|
2014-07-31 11:48:15 +00:00
|
|
|
QPoint m_availableTopLeft;
|
|
|
|
QSize m_testWindowSize;
|
Introduce QInputDevice hierarchy; replace QTouchDevice
We have seen during the Qt 5 series that QMouseEvent::source() does
not provide enough information: if it is synthesized, it could have
come from any device for which mouse events are synthesized, not only
from a touchscreen. By providing in every QInputEvent as complete
information about the actual source device as possible, we will enable
very fine-tuned behavior in the object that handles each event.
Further, we would like to support multiple keyboards, pointing devices,
and named groups of devices that are known as "seats" in Wayland.
In Qt 5, QPA plugins registered each touchscreen as it was discovered.
Now we extend this pattern to all input devices. This new requirement
can be implemented gradually; for now, if a QTWSI input event is
received wtihout a device pointer, a default "core" device will be
created on-the-fly, and a warning emitted.
In Qt 5, QTouchEvent::TouchPoint::id() was forced to be unique even when
multiple devices were in use simultaneously. Now that each event
identifies the device it came from, this hack is no longer needed.
A stub of the new QPointerEvent is added; it will be developed further
in subsequent patches.
[ChangeLog][QtGui][QInputEvent] Every QInputEvent now carries a pointer
to an instance of QInputDevice, or the subclass QPointingDevice in case
of mouse, touch and tablet events. Each platform plugin is expected to
create the device instances, register them, and provide valid pointers
with all input events. If this is not done, warnings are emitted and
default devices are created as necessary. When the device has accurate
information, it provides the opportunity to fine-tune behavior depending
on device type and capabilities: for example if a QMouseEvent is
synthesized from a touchscreen, the recipient can see which touchscreen
it came from. Each device also has a seatName to distinguish users on
multi-user windowing systems. Touchpoint IDs are no longer unique on
their own, but the combination of ID and device is.
Fixes: QTBUG-46412
Fixes: QTBUG-72167
Task-number: QTBUG-69433
Task-number: QTBUG-52430
Change-Id: I933fb2b86182efa722037b7a33e404c5daf5292a
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-05-31 06:38:16 +00:00
|
|
|
QPointingDevice *touchDevice = QTest::createTouchDevice();
|
|
|
|
QPointingDevice *touchDeviceWithMouseEmulation =
|
|
|
|
QTest::createTouchDevice(QInputDevice::DeviceType::TouchScreen,
|
|
|
|
QInputDevice::Capability::Position | QInputDevice::Capability::MouseEmulation);
|
2011-08-29 06:26:37 +00:00
|
|
|
};
|
|
|
|
|
2022-06-28 07:29:13 +00:00
|
|
|
static bool isPlatformWayland()
|
|
|
|
{
|
|
|
|
return QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
|
|
|
|
}
|
|
|
|
|
2014-07-31 11:48:15 +00:00
|
|
|
void tst_QWindow::initTestCase()
|
|
|
|
{
|
2023-07-24 10:16:58 +00:00
|
|
|
#ifdef Q_OS_ANDROID
|
|
|
|
if (QNativeInterface::QAndroidApplication::sdkVersion() == 33)
|
|
|
|
QSKIP("Is flaky on Android 13 / RHEL 8.6 and 8.8 (QTQAINFRA-5606)");
|
|
|
|
#endif
|
2014-07-31 11:48:15 +00:00
|
|
|
// Size of reference window, 200 for < 2000, scale up for larger screens
|
|
|
|
// to avoid Windows warnings about minimum size for decorated windows.
|
|
|
|
int width = 200;
|
|
|
|
const QScreen *screen = QGuiApplication::primaryScreen();
|
|
|
|
m_availableTopLeft = screen->availableGeometry().topLeft();
|
|
|
|
const int screenWidth = screen->geometry().width();
|
|
|
|
if (screenWidth > 2000)
|
|
|
|
width = 100 * ((screenWidth + 500) / 1000);
|
|
|
|
m_testWindowSize = QSize(width, width);
|
2022-06-28 07:29:13 +00:00
|
|
|
|
|
|
|
// Make sure test runs consistently on all compositors by force-disabling window decorations
|
|
|
|
if (isPlatformWayland())
|
|
|
|
qputenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1");
|
2014-07-31 11:48:15 +00:00
|
|
|
}
|
|
|
|
|
2014-07-24 13:11:27 +00:00
|
|
|
void tst_QWindow::cleanup()
|
|
|
|
{
|
|
|
|
QVERIFY(QGuiApplication::allWindows().isEmpty());
|
|
|
|
}
|
|
|
|
|
2015-10-16 13:04:49 +00:00
|
|
|
void tst_QWindow::create()
|
|
|
|
{
|
|
|
|
QWindow a;
|
|
|
|
QVERIFY2(!a.handle(), "QWindow should lazy init the platform window");
|
|
|
|
|
|
|
|
a.create();
|
|
|
|
QVERIFY2(a.handle(), "Explicitly creating a platform window should never fail");
|
|
|
|
|
|
|
|
QWindow b;
|
|
|
|
QWindow c(&b);
|
|
|
|
b.create();
|
|
|
|
QVERIFY(b.handle());
|
|
|
|
QVERIFY2(!c.handle(), "Creating a parent window should not automatically create children");
|
|
|
|
|
|
|
|
QWindow d;
|
|
|
|
QWindow e(&d);
|
|
|
|
e.create();
|
|
|
|
QVERIFY(e.handle());
|
|
|
|
QVERIFY2(d.handle(), "Creating a child window should automatically create parents");
|
|
|
|
|
|
|
|
QWindow f;
|
|
|
|
QWindow g(&f);
|
|
|
|
f.create();
|
|
|
|
QVERIFY(f.handle());
|
|
|
|
QPlatformWindow *platformWindow = f.handle();
|
|
|
|
g.create();
|
|
|
|
QVERIFY(g.handle());
|
|
|
|
QVERIFY2(f.handle() == platformWindow, "Creating a child window should not affect parent if already created");
|
|
|
|
}
|
|
|
|
|
2015-10-16 13:28:31 +00:00
|
|
|
void tst_QWindow::setParent()
|
|
|
|
{
|
|
|
|
QWindow a;
|
|
|
|
QWindow b(&a);
|
|
|
|
QVERIFY2(b.parent() == &a, "Setting parent at construction time should work");
|
|
|
|
QVERIFY2(a.children().contains(&b), "Parent should have child in list of children");
|
|
|
|
|
|
|
|
QWindow c;
|
|
|
|
QWindow d;
|
|
|
|
d.setParent(&c);
|
|
|
|
QVERIFY2(d.parent() == &c, "Setting parent after construction should work");
|
|
|
|
QVERIFY2(c.children().contains(&d), "Parent should have child in list of children");
|
|
|
|
|
|
|
|
a.create();
|
2019-05-10 07:10:27 +00:00
|
|
|
b.setParent(nullptr);
|
2015-10-16 13:28:31 +00:00
|
|
|
QVERIFY2(!b.handle(), "Making window top level shouild not automatically create it");
|
|
|
|
|
|
|
|
QWindow e;
|
|
|
|
c.create();
|
|
|
|
e.setParent(&c);
|
|
|
|
QVERIFY2(!e.handle(), "Making window a child of a created window should not automatically create it");
|
|
|
|
|
|
|
|
QWindow f;
|
|
|
|
QWindow g;
|
|
|
|
g.create();
|
|
|
|
QVERIFY(g.handle());
|
|
|
|
g.setParent(&f);
|
|
|
|
QVERIFY2(f.handle(), "Making a created window a child of a non-created window should automatically create it");
|
|
|
|
}
|
|
|
|
|
2015-10-16 14:41:34 +00:00
|
|
|
void tst_QWindow::setVisible()
|
|
|
|
{
|
|
|
|
QWindow a;
|
|
|
|
QWindow b(&a);
|
|
|
|
a.setVisible(true);
|
|
|
|
QVERIFY2(!b.handle(), "Making a top level window visible doesn't create its children");
|
|
|
|
QVERIFY2(!b.isVisible(), "Making a top level window visible doesn't make its children visible");
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&a));
|
|
|
|
|
|
|
|
QWindow c;
|
|
|
|
QWindow d(&c);
|
|
|
|
d.setVisible(true);
|
|
|
|
QVERIFY2(!c.handle(), "Making a child window visible doesn't create parent window if parent is hidden");
|
|
|
|
QVERIFY2(!c.isVisible(), "Making a child window visible doesn't make its parent visible");
|
|
|
|
|
|
|
|
QVERIFY2(!d.handle(), "Making a child window visible doesn't create platform window if parent is hidden");
|
|
|
|
|
|
|
|
c.create();
|
|
|
|
QVERIFY(c.handle());
|
|
|
|
QVERIFY2(d.handle(), "Creating a parent window should automatically create children if they are visible");
|
|
|
|
QVERIFY2(!c.isVisible(), "Creating a parent window should not make it visible just because it has visible children");
|
|
|
|
|
|
|
|
QWindow e;
|
|
|
|
QWindow f(&e);
|
|
|
|
f.setVisible(true);
|
|
|
|
QVERIFY(!f.handle());
|
|
|
|
QVERIFY(!e.handle());
|
2019-05-10 07:10:27 +00:00
|
|
|
f.setParent(nullptr);
|
2015-10-16 14:41:34 +00:00
|
|
|
QVERIFY2(f.handle(), "Making a visible but not created child window top level should create it");
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&f));
|
|
|
|
|
|
|
|
QWindow g;
|
|
|
|
QWindow h;
|
|
|
|
QWindow i(&g);
|
|
|
|
i.setVisible(true);
|
|
|
|
h.setVisible(true);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&h));
|
|
|
|
QVERIFY(!i.handle());
|
|
|
|
QVERIFY(!g.handle());
|
|
|
|
QVERIFY(h.handle());
|
|
|
|
i.setParent(&h);
|
|
|
|
QVERIFY2(i.handle(), "Making a visible but not created child window child of a created window should create it");
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&i));
|
|
|
|
}
|
|
|
|
|
2023-10-18 17:02:08 +00:00
|
|
|
class SurfaceCreatedWindow : public QWindow
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
using QWindow::QWindow;
|
|
|
|
|
|
|
|
bool eventFilter(QObject *o, QEvent *e) override
|
|
|
|
{
|
|
|
|
if (e->type() == QEvent::PlatformSurface) {
|
|
|
|
auto type = static_cast<QPlatformSurfaceEvent*>(e)->surfaceEventType();
|
|
|
|
if (type == QPlatformSurfaceEvent::SurfaceCreated)
|
|
|
|
++surfaceCreatedEvents;
|
|
|
|
}
|
2023-11-04 11:36:29 +00:00
|
|
|
return QWindow::eventFilter(o, e);
|
2023-10-18 17:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int surfaceCreatedEvents = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QWindow::setVisibleThenCreate()
|
|
|
|
{
|
|
|
|
QWindow parent;
|
|
|
|
parent.setObjectName("Parent");
|
|
|
|
SurfaceCreatedWindow child(&parent);
|
|
|
|
child.installEventFilter(&child);
|
|
|
|
child.setObjectName("Child");
|
|
|
|
child.setVisible(true);
|
|
|
|
child.create();
|
|
|
|
QCOMPARE(child.surfaceCreatedEvents, 1);
|
|
|
|
parent.setVisible(true);
|
|
|
|
QCOMPARE(child.surfaceCreatedEvents, 1);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
|
|
|
}
|
|
|
|
|
2017-02-10 14:57:11 +00:00
|
|
|
void tst_QWindow::setVisibleFalseDoesNotCreateWindow()
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
w.setVisible(false);
|
|
|
|
QVERIFY2(!w.handle(), "Hiding a non-created window doesn't create it");
|
|
|
|
w.setVisible(true);
|
|
|
|
QVERIFY2(w.handle(), "Showing a non-created window creates it");
|
|
|
|
}
|
|
|
|
|
2011-08-29 06:26:37 +00:00
|
|
|
void tst_QWindow::mapGlobal()
|
|
|
|
{
|
|
|
|
QWindow a;
|
|
|
|
QWindow b(&a);
|
|
|
|
QWindow c(&b);
|
|
|
|
|
|
|
|
a.setGeometry(10, 10, 300, 300);
|
|
|
|
b.setGeometry(20, 20, 200, 200);
|
|
|
|
c.setGeometry(40, 40, 100, 100);
|
|
|
|
|
|
|
|
QCOMPARE(a.mapToGlobal(QPoint(100, 100)), QPoint(110, 110));
|
2020-06-11 07:24:12 +00:00
|
|
|
auto delta = a.mapToGlobal(QPointF(100.5, 100.5)) - QPointF(110.5, 110.5);
|
|
|
|
QVERIFY(qFuzzyIsNull(delta.manhattanLength()));
|
|
|
|
|
2011-08-29 06:26:37 +00:00
|
|
|
QCOMPARE(b.mapToGlobal(QPoint(100, 100)), QPoint(130, 130));
|
|
|
|
QCOMPARE(c.mapToGlobal(QPoint(100, 100)), QPoint(170, 170));
|
|
|
|
|
|
|
|
QCOMPARE(a.mapFromGlobal(QPoint(100, 100)), QPoint(90, 90));
|
2020-06-11 07:24:12 +00:00
|
|
|
delta = a.mapFromGlobal(QPointF(100.5, 100.5)) - QPointF(90.5, 90.5);
|
|
|
|
QVERIFY(qFuzzyIsNull(delta.manhattanLength()));
|
|
|
|
|
2011-08-29 06:26:37 +00:00
|
|
|
QCOMPARE(b.mapFromGlobal(QPoint(100, 100)), QPoint(70, 70));
|
|
|
|
QCOMPARE(c.mapFromGlobal(QPoint(100, 100)), QPoint(30, 30));
|
|
|
|
}
|
|
|
|
|
2011-09-22 12:37:58 +00:00
|
|
|
class Window : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
2014-01-03 13:11:27 +00:00
|
|
|
Window(const Qt::WindowFlags flags = Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint)
|
2017-07-18 12:28:58 +00:00
|
|
|
: QWindow(), lastReceivedWindowState(windowState())
|
2011-09-22 12:37:58 +00:00
|
|
|
{
|
2011-09-29 16:02:54 +00:00
|
|
|
reset();
|
2014-01-03 13:11:27 +00:00
|
|
|
setFlags(flags);
|
2014-03-18 16:18:04 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
setSurfaceType(QSurface::OpenGLSurface);
|
|
|
|
#endif
|
2017-07-18 12:28:58 +00:00
|
|
|
|
|
|
|
#if !defined(Q_OS_MACOS)
|
|
|
|
// FIXME: All platforms should send window-state change events, regardless
|
|
|
|
// of the sync/async nature of the the underlying platform, but they don't.
|
2021-06-30 08:57:17 +00:00
|
|
|
connect(this, &QWindow::windowStateChanged, [this]() {
|
2017-07-18 12:28:58 +00:00
|
|
|
lastReceivedWindowState = windowState();
|
|
|
|
});
|
|
|
|
#endif
|
2011-09-22 12:37:58 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
m_received.clear();
|
2014-12-17 12:47:19 +00:00
|
|
|
m_framePositionsOnMove.clear();
|
2011-09-29 16:02:54 +00:00
|
|
|
}
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
bool event(QEvent *event) override
|
2011-09-22 12:37:58 +00:00
|
|
|
{
|
2011-09-29 16:02:54 +00:00
|
|
|
m_received[event->type()]++;
|
2012-03-14 16:55:43 +00:00
|
|
|
m_order << event->type();
|
2014-11-15 11:40:51 +00:00
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::PlatformSurface:
|
|
|
|
m_surfaceventType = static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType();
|
|
|
|
break;
|
|
|
|
|
2014-12-18 11:12:58 +00:00
|
|
|
case QEvent::Move:
|
2014-12-17 12:47:19 +00:00
|
|
|
m_framePositionsOnMove << framePosition();
|
2014-12-18 11:12:58 +00:00
|
|
|
break;
|
2017-07-18 12:28:58 +00:00
|
|
|
|
|
|
|
case QEvent::WindowStateChange:
|
|
|
|
lastReceivedWindowState = windowState();
|
2019-05-10 07:10:27 +00:00
|
|
|
break;
|
2017-07-18 12:28:58 +00:00
|
|
|
|
2014-11-15 11:40:51 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2011-09-22 12:37:58 +00:00
|
|
|
|
|
|
|
return QWindow::event(event);
|
|
|
|
}
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
int received(QEvent::Type type)
|
|
|
|
{
|
|
|
|
return m_received.value(type, 0);
|
|
|
|
}
|
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
int eventIndex(QEvent::Type type)
|
|
|
|
{
|
|
|
|
return m_order.indexOf(type);
|
|
|
|
}
|
|
|
|
|
2014-11-15 11:40:51 +00:00
|
|
|
QPlatformSurfaceEvent::SurfaceEventType surfaceEventType() const
|
|
|
|
{
|
|
|
|
return m_surfaceventType;
|
|
|
|
}
|
|
|
|
|
2020-06-23 08:04:16 +00:00
|
|
|
QList<QPoint> m_framePositionsOnMove;
|
2017-07-18 12:28:58 +00:00
|
|
|
Qt::WindowStates lastReceivedWindowState;
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
private:
|
|
|
|
QHash<QEvent::Type, int> m_received;
|
2020-06-23 08:04:16 +00:00
|
|
|
QList<QEvent::Type> m_order;
|
2014-11-15 11:40:51 +00:00
|
|
|
QPlatformSurfaceEvent::SurfaceEventType m_surfaceventType;
|
2011-09-22 12:37:58 +00:00
|
|
|
};
|
|
|
|
|
2015-10-16 15:36:20 +00:00
|
|
|
class ColoredWindow : public QRasterWindow {
|
|
|
|
public:
|
2019-05-10 07:10:27 +00:00
|
|
|
explicit ColoredWindow(const QColor &color, QWindow *parent = nullptr) : QRasterWindow(parent), m_color(color) {}
|
2017-09-18 08:36:49 +00:00
|
|
|
void paintEvent(QPaintEvent *) override
|
2015-10-16 15:36:20 +00:00
|
|
|
{
|
|
|
|
QPainter p(this);
|
|
|
|
p.fillRect(QRect(QPoint(0, 0), size()), m_color);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const QColor m_color;
|
|
|
|
};
|
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
void tst_QWindow::eventOrderOnShow()
|
|
|
|
{
|
2012-04-27 13:45:24 +00:00
|
|
|
// Some platforms enforce minimum widths for windows, which can cause extra resize
|
|
|
|
// events, so set the width to suitably large value to avoid those.
|
2014-07-31 11:48:15 +00:00
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
2012-03-14 16:55:43 +00:00
|
|
|
|
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-03-14 16:55:43 +00:00
|
|
|
window.setGeometry(geometry);
|
|
|
|
window.show();
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2012-03-14 16:55:43 +00:00
|
|
|
|
|
|
|
QTRY_COMPARE(window.received(QEvent::Show), 1);
|
|
|
|
QTRY_COMPARE(window.received(QEvent::Resize), 1);
|
|
|
|
QTRY_VERIFY(window.isExposed());
|
|
|
|
|
|
|
|
QVERIFY(window.eventIndex(QEvent::Show) < window.eventIndex(QEvent::Resize));
|
|
|
|
QVERIFY(window.eventIndex(QEvent::Resize) < window.eventIndex(QEvent::Expose));
|
|
|
|
}
|
|
|
|
|
2020-08-24 16:59:54 +00:00
|
|
|
class PaintWindow : public Window
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using Window::Window;
|
|
|
|
|
|
|
|
protected:
|
2020-09-10 18:39:49 +00:00
|
|
|
void paintEvent(QPaintEvent *) override
|
|
|
|
{
|
2020-08-24 16:59:54 +00:00
|
|
|
// Handled, not calling base class
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QWindow::paintEvent()
|
|
|
|
{
|
|
|
|
PaintWindow window;
|
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
|
|
|
window.setGeometry(QRect(m_availableTopLeft, m_testWindowSize));
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose));
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Paint));
|
|
|
|
QVERIFY(window.isExposed());
|
|
|
|
|
|
|
|
// There is no defined order between paint and expose, so we don't test that
|
|
|
|
|
|
|
|
window.reset();
|
|
|
|
window.resize(m_testWindowSize * 2);
|
|
|
|
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Paint));
|
|
|
|
QVERIFY(!window.received(QEvent::Expose));
|
|
|
|
|
|
|
|
window.reset();
|
|
|
|
window.hide();
|
|
|
|
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose));
|
|
|
|
QVERIFY(!window.received(QEvent::Paint));
|
|
|
|
QVERIFY(!window.isExposed());
|
|
|
|
}
|
|
|
|
|
2013-07-26 15:51:42 +00:00
|
|
|
void tst_QWindow::resizeEventAfterResize()
|
|
|
|
{
|
|
|
|
// Some platforms enforce minimum widths for windows, which can cause extra resize
|
|
|
|
// events, so set the width to suitably large value to avoid those.
|
2014-07-31 11:48:15 +00:00
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize * 2);
|
2013-07-26 15:51:42 +00:00
|
|
|
|
|
|
|
Window window;
|
|
|
|
window.setGeometry(geometry);
|
2013-11-15 16:55:25 +00:00
|
|
|
window.showNormal();
|
2013-07-26 15:51:42 +00:00
|
|
|
|
|
|
|
QTRY_COMPARE(window.received(QEvent::Resize), 1);
|
|
|
|
|
|
|
|
// QTBUG-32706
|
|
|
|
// Make sure we get a resizeEvent after calling resize
|
2014-07-31 11:48:15 +00:00
|
|
|
window.resize(m_testWindowSize);
|
2013-07-26 15:51:42 +00:00
|
|
|
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
|
|
|
|
|
2013-07-26 15:51:42 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::Resize), 2);
|
|
|
|
}
|
|
|
|
|
2016-06-11 16:39:23 +00:00
|
|
|
void tst_QWindow::exposeEventOnShrink_QTBUG54040()
|
|
|
|
{
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QSKIP("", "eglfs windows are fullscreen by default.", Continue);
|
2016-06-11 16:39:23 +00:00
|
|
|
Window window;
|
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
|
|
|
window.setTitle(QTest::currentTestFunction());
|
|
|
|
window.showNormal();
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
2017-09-26 10:20:30 +00:00
|
|
|
int exposeCount = window.received(QEvent::Expose);
|
2016-06-11 16:39:23 +00:00
|
|
|
window.resize(window.width(), window.height() - 5);
|
2017-09-26 10:20:30 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
|
|
|
|
|
|
|
|
exposeCount = window.received(QEvent::Expose);
|
2016-06-11 16:39:23 +00:00
|
|
|
window.resize(window.width() - 5, window.height());
|
2017-09-26 10:20:30 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
|
|
|
|
|
|
|
|
exposeCount = window.received(QEvent::Expose);
|
2016-06-11 16:39:23 +00:00
|
|
|
window.resize(window.width() - 5, window.height() - 5);
|
2017-09-26 10:20:30 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose) > exposeCount);
|
2016-06-11 16:39:23 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 15:01:01 +00:00
|
|
|
// Compare a window position that may go through scaling in the platform plugin with fuzz.
|
|
|
|
static inline bool qFuzzyCompareWindowPosition(const QPoint &p1, const QPoint p2, int fuzz)
|
|
|
|
{
|
|
|
|
return (p1 - p2).manhattanLength() <= fuzz;
|
|
|
|
}
|
|
|
|
|
2015-12-09 17:06:20 +00:00
|
|
|
static inline bool qFuzzyCompareWindowSize(const QSize &s1, const QSize &s2, int fuzz)
|
|
|
|
{
|
|
|
|
const int manhattanLength = qAbs(s1.width() - s2.width()) + qAbs(s1.height() - s2.height());
|
|
|
|
return manhattanLength <= fuzz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool qFuzzyCompareWindowGeometry(const QRect &r1, const QRect &r2, int fuzz)
|
|
|
|
{
|
|
|
|
return qFuzzyCompareWindowPosition(r1.topLeft(), r2.topLeft(), fuzz)
|
|
|
|
&& qFuzzyCompareWindowSize(r1.size(), r2.size(), fuzz);
|
|
|
|
}
|
|
|
|
|
2015-06-18 15:01:01 +00:00
|
|
|
static QString msgPointMismatch(const QPoint &p1, const QPoint p2)
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
QDebug(&result) << p1 << "!=" << p2 << ", manhattanLength=" << (p1 - p2).manhattanLength();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-12-09 17:06:20 +00:00
|
|
|
static QString msgRectMismatch(const QRect &r1, const QRect &r2)
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
QDebug(&result) << r1 << "!=" << r2;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-09-22 12:37:58 +00:00
|
|
|
void tst_QWindow::positioning()
|
|
|
|
{
|
2022-07-26 11:10:53 +00:00
|
|
|
#ifdef Q_OS_ANDROID
|
|
|
|
QSKIP("Fails on Android. QTBUG-105201");
|
|
|
|
#endif
|
2013-05-31 18:27:50 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
|
|
|
|
QPlatformIntegration::NonFullScreenWindows)) {
|
|
|
|
QSKIP("This platform does not support non-fullscreen windows");
|
|
|
|
}
|
|
|
|
|
2018-03-02 14:28:24 +00:00
|
|
|
if (isPlatformWayland())
|
2018-06-05 10:16:12 +00:00
|
|
|
QSKIP("Wayland: This fails. See QTBUG-68660.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2012-04-27 13:45:24 +00:00
|
|
|
// Some platforms enforce minimum widths for windows, which can cause extra resize
|
|
|
|
// events, so set the width to suitably large value to avoid those.
|
2014-07-31 11:48:15 +00:00
|
|
|
const QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2024-07-03 14:36:08 +00:00
|
|
|
Window window;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(20, 20), m_testWindowSize));
|
|
|
|
window.setFramePosition(m_availableTopLeft + QPoint(40, 40)); // Move window around before show, size must not change.
|
|
|
|
QCOMPARE(window.geometry().size(), m_testWindowSize);
|
2011-09-22 12:37:58 +00:00
|
|
|
window.setGeometry(geometry);
|
|
|
|
QCOMPARE(window.geometry(), geometry);
|
2013-05-31 18:27:50 +00:00
|
|
|
// explicitly use non-fullscreen show. show() can be fullscreen on some platforms
|
|
|
|
window.showNormal();
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2022-07-25 16:53:19 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
2011-09-22 12:37:58 +00:00
|
|
|
|
|
|
|
QMargins originalMargins = window.frameMargins();
|
|
|
|
|
2012-11-27 13:57:19 +00:00
|
|
|
QCOMPARE(window.position(), window.framePosition() + QPoint(originalMargins.left(), originalMargins.top()));
|
2011-09-22 12:37:58 +00:00
|
|
|
QVERIFY(window.frameGeometry().contains(window.geometry()));
|
|
|
|
|
2012-10-22 15:06:23 +00:00
|
|
|
QPoint originalPos = window.position();
|
2012-11-27 13:57:19 +00:00
|
|
|
QPoint originalFramePos = window.framePosition();
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2014-12-03 11:09:50 +00:00
|
|
|
window.reset();
|
2011-09-22 12:37:58 +00:00
|
|
|
window.setWindowState(Qt::WindowFullScreen);
|
2017-07-18 12:28:58 +00:00
|
|
|
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowFullScreen);
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2015-10-15 13:24:54 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Resize) > 0);
|
2014-01-03 13:11:27 +00:00
|
|
|
|
2014-12-03 11:09:50 +00:00
|
|
|
window.reset();
|
2011-09-22 12:37:58 +00:00
|
|
|
window.setWindowState(Qt::WindowNoState);
|
2017-07-18 12:28:58 +00:00
|
|
|
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowNoState);
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2015-10-15 13:24:54 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Resize) > 0);
|
2014-12-18 14:44:38 +00:00
|
|
|
|
2012-10-22 15:06:23 +00:00
|
|
|
QTRY_COMPARE(originalPos, window.position());
|
2012-11-27 13:57:19 +00:00
|
|
|
QTRY_COMPARE(originalFramePos, window.framePosition());
|
2011-09-22 12:37:58 +00:00
|
|
|
QTRY_COMPARE(originalMargins, window.frameMargins());
|
2024-07-03 14:47:49 +00:00
|
|
|
}
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2024-07-03 14:47:49 +00:00
|
|
|
void tst_QWindow::framePositioning_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("showBeforePositioning");
|
|
|
|
|
|
|
|
QTest::newRow("before show") << false;
|
|
|
|
QTest::newRow("after show") << true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::framePositioning()
|
|
|
|
{
|
2024-08-05 13:53:25 +00:00
|
|
|
#ifdef Q_OS_ANDROID
|
|
|
|
QSKIP("Fails on Android. QTBUG-105201");
|
|
|
|
#endif
|
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(
|
|
|
|
QPlatformIntegration::NonFullScreenWindows)) {
|
|
|
|
QSKIP("This platform does not support non-fullscreen windows");
|
|
|
|
}
|
2024-07-23 11:08:15 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("Wayland: This fails. See QTBUG-68660.");
|
|
|
|
|
2024-07-03 14:47:49 +00:00
|
|
|
QFETCH(bool, showBeforePositioning);
|
|
|
|
|
|
|
|
Window window;
|
|
|
|
const QScreen *screen = window.screen();
|
|
|
|
const QRect availableGeometry = screen->availableGeometry();
|
|
|
|
const QPoint screenCenter = availableGeometry.center();
|
|
|
|
|
|
|
|
const QPoint oldFramePos = window.framePosition();
|
|
|
|
QMargins originalMargins;
|
|
|
|
|
|
|
|
if (showBeforePositioning) {
|
|
|
|
window.showNormal();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2024-11-21 09:39:06 +00:00
|
|
|
// Needed on OpenSuse. The window manager (KWin) sets the frame margins after exposure. See
|
|
|
|
// QTBUG-131368.
|
|
|
|
QVERIFY(QTest::qWaitFor([&window]{ return !window.frameMargins().isNull(); }));
|
2024-07-03 14:47:49 +00:00
|
|
|
originalMargins = window.frameMargins();
|
|
|
|
window.setFramePosition(screenCenter);
|
|
|
|
} else {
|
|
|
|
window.setFramePosition(screenCenter);
|
|
|
|
window.showNormal();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
}
|
|
|
|
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Move));
|
|
|
|
const int fuzz = int(QHighDpiScaling::factor(&window));
|
|
|
|
if (!qFuzzyCompareWindowPosition(window.framePosition(), screenCenter, fuzz)) {
|
|
|
|
qDebug() << "About to fail auto-test. Here is some additional information:";
|
|
|
|
qDebug() << "window.framePosition() == " << window.framePosition();
|
|
|
|
qDebug() << "old frame position == " << oldFramePos;
|
|
|
|
qDebug() << "We received " << window.received(QEvent::Move) << " move events";
|
|
|
|
qDebug() << "frame positions after each move event:" << window.m_framePositionsOnMove;
|
|
|
|
}
|
|
|
|
QTRY_VERIFY2(qFuzzyCompareWindowPosition(window.framePosition(), screenCenter, fuzz),
|
|
|
|
qPrintable(msgPointMismatch(window.framePosition(), screenCenter)));
|
|
|
|
|
|
|
|
if (showBeforePositioning) {
|
|
|
|
// Repositioning should not affect existing margins
|
2011-09-22 12:37:58 +00:00
|
|
|
QTRY_COMPARE(originalMargins, window.frameMargins());
|
2012-11-27 13:57:19 +00:00
|
|
|
QCOMPARE(window.position(), window.framePosition() + QPoint(originalMargins.left(), originalMargins.top()));
|
2024-07-03 14:47:49 +00:00
|
|
|
}
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2024-07-03 14:47:49 +00:00
|
|
|
// Check that regular positioning still works
|
2011-09-22 12:37:58 +00:00
|
|
|
|
2024-07-03 14:47:49 +00:00
|
|
|
const QPoint screenCenterAdjusted = screenCenter + QPoint(50, 50);
|
|
|
|
window.reset();
|
|
|
|
window.setPosition(screenCenterAdjusted);
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Move));
|
|
|
|
QTRY_COMPARE(screenCenterAdjusted, window.position());
|
2011-09-22 12:37:58 +00:00
|
|
|
}
|
|
|
|
|
2024-08-27 09:20:59 +00:00
|
|
|
void tst_QWindow::framePositioningStableAfterDestroy()
|
|
|
|
{
|
|
|
|
QWindow window;
|
|
|
|
window.setFramePosition(QPoint(100, 100));
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
2025-01-02 09:03:39 +00:00
|
|
|
const bool frameOk = QTest::qWaitFor([&]{return window.geometry() != window.frameGeometry(); });
|
|
|
|
if (!frameOk)
|
|
|
|
qCritical() << "Frame geometry failed to update";
|
|
|
|
|
2024-08-27 09:20:59 +00:00
|
|
|
const QPoint stablePosition = window.position();
|
|
|
|
const QPoint stableFramePosition = window.framePosition();
|
|
|
|
|
|
|
|
window.destroy();
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2025-01-02 09:03:39 +00:00
|
|
|
QTRY_COMPARE(window.position(), stablePosition);
|
|
|
|
QTRY_COMPARE(window.framePosition(), stableFramePosition);
|
2024-08-27 09:20:59 +00:00
|
|
|
}
|
|
|
|
|
2025-04-09 09:37:13 +00:00
|
|
|
void tst_QWindow::geometryAfterWmUpdateAndDestroyCreate()
|
|
|
|
{
|
2025-06-11 10:52:18 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("A window can't be moved programmatically on Wayland");
|
|
|
|
|
2025-04-09 09:37:13 +00:00
|
|
|
QWindow window;
|
|
|
|
window.setFlag(Qt::FramelessWindowHint);
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
|
|
|
if (window.windowState() != Qt::WindowNoState)
|
|
|
|
QSKIP("Default window state is not Qt::WindowNoState");
|
|
|
|
|
|
|
|
const QRect geometryAfterShow = window.geometry();
|
|
|
|
|
|
|
|
// Check that the geometry is retained for create/destroy/create,
|
|
|
|
// if the user has moved and resized the window via the window-manager
|
|
|
|
// (i.e. no explicit setGeometry calls from user code).
|
|
|
|
QRect modifiedGeometry = geometryAfterShow.translated(42, 42);
|
|
|
|
modifiedGeometry.setSize(modifiedGeometry.size() + QSize(42, 42));
|
|
|
|
QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(
|
|
|
|
&window, modifiedGeometry);
|
|
|
|
|
|
|
|
window.destroy();
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
|
|
|
QTRY_COMPARE(window.geometry(), modifiedGeometry);
|
|
|
|
}
|
|
|
|
|
2014-06-19 11:45:06 +00:00
|
|
|
void tst_QWindow::positioningDuringMinimized()
|
|
|
|
{
|
|
|
|
// QTBUG-39544, setting a geometry in minimized state should work as well.
|
2015-07-07 11:40:42 +00:00
|
|
|
if (QGuiApplication::platformName().compare("windows", Qt::CaseInsensitive) != 0
|
|
|
|
&& QGuiApplication::platformName().compare("cocoa", Qt::CaseInsensitive) != 0)
|
2014-06-19 11:45:06 +00:00
|
|
|
QSKIP("Not supported on this platform");
|
|
|
|
Window window;
|
|
|
|
window.setTitle(QStringLiteral("positioningDuringMinimized"));
|
2014-07-31 11:48:15 +00:00
|
|
|
const QRect initialGeometry(m_availableTopLeft + QPoint(100, 100), m_testWindowSize);
|
2014-06-19 11:45:06 +00:00
|
|
|
window.setGeometry(initialGeometry);
|
|
|
|
window.showNormal();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
QCOMPARE(window.geometry(), initialGeometry);
|
|
|
|
window.setWindowState(Qt::WindowMinimized);
|
|
|
|
QCOMPARE(window.geometry(), initialGeometry);
|
|
|
|
const QRect newGeometry(initialGeometry.topLeft() + QPoint(50, 50), initialGeometry.size() + QSize(50, 50));
|
|
|
|
window.setGeometry(newGeometry);
|
|
|
|
QTRY_COMPARE(window.geometry(), newGeometry);
|
|
|
|
window.setWindowState(Qt::WindowNoState);
|
|
|
|
QTRY_COMPARE(window.geometry(), newGeometry);
|
|
|
|
}
|
|
|
|
|
2015-10-16 15:36:20 +00:00
|
|
|
void tst_QWindow::childWindowPositioning_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("showInsteadOfCreate");
|
|
|
|
|
|
|
|
QTest::newRow("create") << false;
|
|
|
|
QTest::newRow("show") << true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::childWindowPositioning()
|
|
|
|
{
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QSKIP("eglfs does not support child windows.");
|
2015-10-16 15:36:20 +00:00
|
|
|
const QPoint topLeftOrigin(0, 0);
|
|
|
|
|
|
|
|
ColoredWindow topLevelWindowFirst(Qt::green);
|
|
|
|
topLevelWindowFirst.setObjectName("topLevelWindowFirst");
|
|
|
|
ColoredWindow childWindowAfter(Qt::yellow, &topLevelWindowFirst);
|
|
|
|
childWindowAfter.setObjectName("childWindowAfter");
|
|
|
|
|
|
|
|
topLevelWindowFirst.setFramePosition(m_availableTopLeft);
|
|
|
|
childWindowAfter.setFramePosition(topLeftOrigin);
|
|
|
|
|
|
|
|
ColoredWindow topLevelWindowAfter(Qt::green);
|
|
|
|
topLevelWindowAfter.setObjectName("topLevelWindowAfter");
|
|
|
|
ColoredWindow childWindowFirst(Qt::yellow, &topLevelWindowAfter);
|
|
|
|
childWindowFirst.setObjectName("childWindowFirst");
|
|
|
|
|
|
|
|
topLevelWindowAfter.setFramePosition(m_availableTopLeft);
|
|
|
|
childWindowFirst.setFramePosition(topLeftOrigin);
|
|
|
|
|
|
|
|
QFETCH(bool, showInsteadOfCreate);
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QWindow *windows[] = {&topLevelWindowFirst, &childWindowAfter, &childWindowFirst, &topLevelWindowAfter};
|
|
|
|
for (QWindow *window : windows) {
|
|
|
|
if (showInsteadOfCreate)
|
2015-10-16 15:36:20 +00:00
|
|
|
window->showNormal();
|
2019-05-10 07:10:27 +00:00
|
|
|
else
|
2015-10-16 15:36:20 +00:00
|
|
|
window->create();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (showInsteadOfCreate) {
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&topLevelWindowFirst));
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&topLevelWindowAfter));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creation order shouldn't affect the geometry
|
2015-11-11 14:24:37 +00:00
|
|
|
// Use try compare since on X11 the window manager may still re-position the window after expose
|
|
|
|
QTRY_COMPARE(topLevelWindowFirst.geometry(), topLevelWindowAfter.geometry());
|
|
|
|
QTRY_COMPARE(childWindowAfter.geometry(), childWindowFirst.geometry());
|
2015-10-16 15:36:20 +00:00
|
|
|
|
|
|
|
// Creation order shouldn't affect the child ending up at 0,0
|
|
|
|
QCOMPARE(childWindowFirst.framePosition(), topLeftOrigin);
|
|
|
|
QCOMPARE(childWindowAfter.framePosition(), topLeftOrigin);
|
|
|
|
}
|
|
|
|
|
2016-10-24 16:35:32 +00:00
|
|
|
void tst_QWindow::childWindowLevel()
|
|
|
|
{
|
|
|
|
ColoredWindow topLevel(Qt::green);
|
|
|
|
topLevel.setObjectName("topLevel");
|
|
|
|
ColoredWindow yellowChild(Qt::yellow, &topLevel);
|
|
|
|
yellowChild.setObjectName("yellowChild");
|
|
|
|
ColoredWindow redChild(Qt::red, &topLevel);
|
|
|
|
redChild.setObjectName("redChild");
|
|
|
|
ColoredWindow blueChild(Qt::blue, &topLevel);
|
|
|
|
blueChild.setObjectName("blueChild");
|
|
|
|
|
|
|
|
const QObjectList &siblings = topLevel.children();
|
|
|
|
|
|
|
|
QCOMPARE(siblings.constFirst(), &yellowChild);
|
|
|
|
QCOMPARE(siblings.constLast(), &blueChild);
|
|
|
|
|
|
|
|
yellowChild.raise();
|
|
|
|
QCOMPARE(siblings.constLast(), &yellowChild);
|
|
|
|
|
|
|
|
blueChild.lower();
|
|
|
|
QCOMPARE(siblings.constFirst(), &blueChild);
|
|
|
|
}
|
|
|
|
|
2015-12-09 17:06:20 +00:00
|
|
|
// QTBUG-49709: Verify that the normal geometry is correctly restored
|
|
|
|
// when executing a sequence of window state changes. So far, Windows
|
|
|
|
// only where state changes have immediate effect.
|
|
|
|
|
|
|
|
typedef QList<Qt::WindowState> WindowStateList;
|
|
|
|
|
|
|
|
Q_DECLARE_METATYPE(WindowStateList)
|
|
|
|
|
|
|
|
void tst_QWindow::stateChange_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<WindowStateList>("stateSequence");
|
|
|
|
|
|
|
|
QTest::newRow("normal->min->normal") <<
|
|
|
|
(WindowStateList() << Qt::WindowMinimized << Qt::WindowNoState);
|
|
|
|
QTest::newRow("normal->maximized->normal") <<
|
|
|
|
(WindowStateList() << Qt::WindowMaximized << Qt::WindowNoState);
|
|
|
|
QTest::newRow("normal->fullscreen->normal") <<
|
|
|
|
(WindowStateList() << Qt::WindowFullScreen << Qt::WindowNoState);
|
|
|
|
QTest::newRow("normal->maximized->fullscreen->normal") <<
|
|
|
|
(WindowStateList() << Qt::WindowMaximized << Qt::WindowFullScreen << Qt::WindowNoState);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::stateChange()
|
|
|
|
{
|
|
|
|
QFETCH(WindowStateList, stateSequence);
|
|
|
|
|
|
|
|
if (QGuiApplication::platformName().compare(QLatin1String("windows"), Qt::CaseInsensitive))
|
|
|
|
QSKIP("Windows-only test");
|
|
|
|
|
|
|
|
Window window;
|
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag()));
|
|
|
|
const QRect normalGeometry(m_availableTopLeft + QPoint(40, 40), m_testWindowSize);
|
|
|
|
window.setGeometry(normalGeometry);
|
|
|
|
// explicitly use non-fullscreen show. show() can be fullscreen on some platforms
|
|
|
|
window.showNormal();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2022-10-06 09:08:21 +00:00
|
|
|
for (Qt::WindowState state : std::as_const(stateSequence)) {
|
2015-12-09 17:06:20 +00:00
|
|
|
window.setWindowState(state);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
}
|
|
|
|
const QRect geometry = window.geometry();
|
|
|
|
const int fuzz = int(QHighDpiScaling::factor(&window));
|
|
|
|
QVERIFY2(qFuzzyCompareWindowGeometry(geometry, normalGeometry, fuzz),
|
|
|
|
qPrintable(msgRectMismatch(geometry, normalGeometry)));
|
|
|
|
}
|
|
|
|
|
2014-11-15 11:40:51 +00:00
|
|
|
class PlatformWindowFilter : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2019-05-10 07:10:27 +00:00
|
|
|
explicit PlatformWindowFilter(Window *window) : m_window(window) {}
|
2014-11-15 11:40:51 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
bool eventFilter(QObject *o, QEvent *e) override
|
2014-11-15 11:40:51 +00:00
|
|
|
{
|
|
|
|
// Check that the platform surface events are delivered synchronously.
|
|
|
|
// If they are, the native platform surface should always exist when we
|
|
|
|
// receive a QPlatformSurfaceEvent
|
|
|
|
if (e->type() == QEvent::PlatformSurface && o == m_window) {
|
2017-09-18 09:49:52 +00:00
|
|
|
m_alwaysExisted &= (m_window->handle() != nullptr);
|
2014-11-15 11:40:51 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool surfaceExisted() const { return m_alwaysExisted; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
Window *m_window;
|
2019-05-10 07:10:27 +00:00
|
|
|
bool m_alwaysExisted = true;
|
2014-11-15 11:40:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QWindow::platformSurface()
|
|
|
|
{
|
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
|
|
|
|
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
PlatformWindowFilter filter(&window);
|
2014-11-15 11:40:51 +00:00
|
|
|
window.installEventFilter(&filter);
|
|
|
|
|
|
|
|
window.setGeometry(geometry);
|
|
|
|
QCOMPARE(window.geometry(), geometry);
|
|
|
|
window.create();
|
|
|
|
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::PlatformSurface), 1);
|
|
|
|
QTRY_COMPARE(window.surfaceEventType(), QPlatformSurfaceEvent::SurfaceCreated);
|
2017-09-18 09:49:52 +00:00
|
|
|
QTRY_VERIFY(window.handle() != nullptr);
|
2014-11-15 11:40:51 +00:00
|
|
|
|
|
|
|
window.destroy();
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::PlatformSurface), 2);
|
|
|
|
QTRY_COMPARE(window.surfaceEventType(), QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
|
|
|
|
QTRY_VERIFY(!window.handle());
|
2014-11-15 11:40:51 +00:00
|
|
|
|
|
|
|
// Check for synchronous delivery of platform surface events and that the platform
|
|
|
|
// surface always existed upon event delivery
|
|
|
|
QTRY_VERIFY(filter.surfaceExisted());
|
|
|
|
}
|
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
void tst_QWindow::isExposed()
|
|
|
|
{
|
2014-07-31 11:48:15 +00:00
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
2012-03-14 16:55:43 +00:00
|
|
|
|
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-03-14 16:55:43 +00:00
|
|
|
window.setGeometry(geometry);
|
|
|
|
QCOMPARE(window.geometry(), geometry);
|
|
|
|
window.show();
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2012-03-14 16:55:43 +00:00
|
|
|
|
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose) > 0);
|
|
|
|
QTRY_VERIFY(window.isExposed());
|
|
|
|
|
|
|
|
window.hide();
|
|
|
|
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2012-03-14 16:55:43 +00:00
|
|
|
QTRY_VERIFY(window.received(QEvent::Expose) > 1);
|
|
|
|
QTRY_VERIFY(!window.isExposed());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
void tst_QWindow::isActive()
|
|
|
|
{
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-04-27 13:45:24 +00:00
|
|
|
// Some platforms enforce minimum widths for windows, which can cause extra resize
|
|
|
|
// events, so set the width to suitably large value to avoid those.
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2011-09-29 16:02:54 +00:00
|
|
|
window.show();
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2011-09-29 16:02:54 +00:00
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
QTRY_VERIFY(window.isExposed());
|
2014-03-18 16:18:04 +00:00
|
|
|
#if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store
|
|
|
|
// and then post the window in order for screen to show the window
|
|
|
|
QOpenGLContext context;
|
|
|
|
context.create();
|
|
|
|
context.makeCurrent(&window);
|
|
|
|
context.swapBuffers(&window);
|
|
|
|
#endif
|
2011-09-29 16:02:54 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::Resize), 1);
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
|
2011-09-29 16:02:54 +00:00
|
|
|
QVERIFY(window.isActive());
|
|
|
|
|
2025-06-11 10:47:26 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("A nested window or a subsurface in wayland terms can't get focus.");
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
Window child;
|
|
|
|
child.setParent(&window);
|
|
|
|
child.setGeometry(10, 10, 20, 20);
|
|
|
|
child.show();
|
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
QTRY_VERIFY(child.isExposed());
|
2011-09-29 16:02:54 +00:00
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
child.requestActivate();
|
2011-09-29 16:02:54 +00:00
|
|
|
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &child);
|
2011-09-29 16:02:54 +00:00
|
|
|
QVERIFY(child.isActive());
|
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
// parent shouldn't receive new resize events from child being shown
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2011-09-29 16:02:54 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::Resize), 1);
|
|
|
|
QTRY_COMPARE(window.received(QEvent::FocusIn), 1);
|
|
|
|
QTRY_COMPARE(window.received(QEvent::FocusOut), 1);
|
|
|
|
QTRY_COMPARE(child.received(QEvent::FocusIn), 1);
|
|
|
|
|
|
|
|
// child has focus
|
|
|
|
QVERIFY(window.isActive());
|
|
|
|
|
2016-08-30 13:52:17 +00:00
|
|
|
// test focus back to parent and then back to child (QTBUG-39362)
|
|
|
|
// also verify the cumulative FocusOut and FocusIn counts
|
|
|
|
// activate parent
|
|
|
|
window.requestActivate();
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
|
|
|
|
QVERIFY(window.isActive());
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(child.received(QEvent::FocusOut), 1);
|
|
|
|
QTRY_COMPARE(window.received(QEvent::FocusIn), 2);
|
|
|
|
|
|
|
|
// activate child again
|
|
|
|
child.requestActivate();
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &child);
|
|
|
|
QVERIFY(child.isActive());
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.received(QEvent::FocusOut), 2);
|
|
|
|
QTRY_COMPARE(child.received(QEvent::FocusIn), 2);
|
|
|
|
|
2011-09-29 16:02:54 +00:00
|
|
|
Window dialog;
|
|
|
|
dialog.setTransientParent(&window);
|
2014-07-31 11:48:15 +00:00
|
|
|
dialog.setGeometry(QRect(m_availableTopLeft + QPoint(110, 100), m_testWindowSize));
|
2011-09-29 16:02:54 +00:00
|
|
|
dialog.show();
|
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
dialog.requestActivate();
|
2011-09-29 16:02:54 +00:00
|
|
|
|
2012-03-14 16:55:43 +00:00
|
|
|
QTRY_VERIFY(dialog.isExposed());
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2011-09-29 16:02:54 +00:00
|
|
|
QTRY_COMPARE(dialog.received(QEvent::Resize), 1);
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &dialog);
|
2011-09-29 16:02:54 +00:00
|
|
|
QVERIFY(dialog.isActive());
|
|
|
|
|
|
|
|
// transient child has focus
|
|
|
|
QVERIFY(window.isActive());
|
|
|
|
|
|
|
|
// parent is active
|
|
|
|
QVERIFY(child.isActive());
|
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.requestActivate();
|
2011-09-29 16:02:54 +00:00
|
|
|
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
|
2012-04-27 13:45:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
2011-09-29 16:02:54 +00:00
|
|
|
QTRY_COMPARE(dialog.received(QEvent::FocusOut), 1);
|
2019-10-01 09:32:55 +00:00
|
|
|
// We should be checking for exactly three, but since this is a try-compare _loop_, we might
|
|
|
|
// loose and regain focus multiple times in the event of a system popup. This has been observed
|
|
|
|
// to fail on Windows, see QTBUG-77769.
|
|
|
|
QTRY_VERIFY2(window.received(QEvent::FocusIn) >= 3,
|
|
|
|
qPrintable(
|
|
|
|
QStringLiteral("Expected more than three focus in events, received: %1")
|
|
|
|
.arg(window.received(QEvent::FocusIn))));
|
2011-09-29 16:02:54 +00:00
|
|
|
|
|
|
|
QVERIFY(window.isActive());
|
|
|
|
|
|
|
|
// transient parent has focus
|
|
|
|
QVERIFY(dialog.isActive());
|
|
|
|
|
|
|
|
// parent has focus
|
|
|
|
QVERIFY(child.isActive());
|
|
|
|
}
|
|
|
|
|
2017-12-20 13:07:17 +00:00
|
|
|
class InputTestWindow : public ColoredWindow
|
2011-12-12 15:24:33 +00:00
|
|
|
{
|
|
|
|
public:
|
2019-05-10 07:10:27 +00:00
|
|
|
void keyPressEvent(QKeyEvent *event) override
|
|
|
|
{
|
2011-12-12 15:24:33 +00:00
|
|
|
keyPressCode = event->key();
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void keyReleaseEvent(QKeyEvent *event) override
|
|
|
|
{
|
2011-12-12 15:24:33 +00:00
|
|
|
keyReleaseCode = event->key();
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void mousePressEvent(QMouseEvent *event) override
|
|
|
|
{
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << event;
|
|
|
|
mouseDevice = event->pointingDevice();
|
2012-01-23 08:28:58 +00:00
|
|
|
if (ignoreMouse) {
|
2012-01-12 07:53:13 +00:00
|
|
|
event->ignore();
|
2012-01-23 08:28:58 +00:00
|
|
|
} else {
|
2020-09-30 20:37:09 +00:00
|
|
|
QCOMPARE(event->isBeginEvent(), true);
|
|
|
|
QCOMPARE(event->isUpdateEvent(), false);
|
|
|
|
QCOMPARE(event->isEndEvent(), false);
|
|
|
|
QCOMPARE(event->points().first().state(), QEventPoint::State::Pressed);
|
2012-03-24 08:48:12 +00:00
|
|
|
++mousePressedCount;
|
|
|
|
mouseSequenceSignature += 'p';
|
2012-01-12 07:53:13 +00:00
|
|
|
mousePressButton = event->button();
|
2020-06-04 16:07:06 +00:00
|
|
|
mousePressScreenPos = event->globalPosition();
|
|
|
|
mousePressLocalPos = event->position();
|
2012-04-23 13:18:01 +00:00
|
|
|
if (spinLoopWhenPressed)
|
|
|
|
QCoreApplication::processEvents();
|
2012-01-23 08:28:58 +00:00
|
|
|
}
|
2022-05-24 11:00:07 +00:00
|
|
|
if (closeOnTap)
|
|
|
|
this->close();
|
|
|
|
|
2011-12-12 15:24:33 +00:00
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void mouseReleaseEvent(QMouseEvent *event) override
|
|
|
|
{
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << event;
|
2012-03-24 08:48:12 +00:00
|
|
|
if (ignoreMouse) {
|
2012-01-12 07:53:13 +00:00
|
|
|
event->ignore();
|
2012-03-24 08:48:12 +00:00
|
|
|
} else {
|
2020-09-30 20:37:09 +00:00
|
|
|
QCOMPARE(event->isBeginEvent(), false);
|
|
|
|
QCOMPARE(event->isUpdateEvent(), false);
|
|
|
|
QCOMPARE(event->isEndEvent(), true);
|
|
|
|
QCOMPARE(event->points().first().state(), QEventPoint::State::Released);
|
2012-03-24 08:48:12 +00:00
|
|
|
++mouseReleasedCount;
|
|
|
|
mouseSequenceSignature += 'r';
|
2012-01-12 07:53:13 +00:00
|
|
|
mouseReleaseButton = event->button();
|
2012-03-24 08:48:12 +00:00
|
|
|
}
|
2011-12-12 15:24:33 +00:00
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void mouseMoveEvent(QMouseEvent *event) override
|
|
|
|
{
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << event;
|
2023-02-22 11:03:16 +00:00
|
|
|
mouseMoveDevice = event->pointingDevice();
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
buttonStateInGeneratedMove = event->buttons();
|
2012-01-23 08:28:58 +00:00
|
|
|
if (ignoreMouse) {
|
|
|
|
event->ignore();
|
|
|
|
} else {
|
2020-09-30 20:37:09 +00:00
|
|
|
QCOMPARE(event->isBeginEvent(), false);
|
|
|
|
QCOMPARE(event->isUpdateEvent(), true);
|
|
|
|
QCOMPARE(event->isEndEvent(), false);
|
|
|
|
QCOMPARE(event->points().first().state(), QEventPoint::State::Updated);
|
2012-04-23 13:18:01 +00:00
|
|
|
++mouseMovedCount;
|
2012-01-23 08:28:58 +00:00
|
|
|
mouseMoveButton = event->button();
|
2020-06-04 16:07:06 +00:00
|
|
|
mouseMoveScreenPos = event->globalPosition();
|
2012-01-23 08:28:58 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void mouseDoubleClickEvent(QMouseEvent *event) override
|
|
|
|
{
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << event;
|
2012-03-24 08:48:12 +00:00
|
|
|
if (ignoreMouse) {
|
|
|
|
event->ignore();
|
|
|
|
} else {
|
2020-09-30 20:37:09 +00:00
|
|
|
QCOMPARE(event->isBeginEvent(), false);
|
|
|
|
QCOMPARE(event->isUpdateEvent(), true);
|
|
|
|
QCOMPARE(event->isEndEvent(), false);
|
|
|
|
QCOMPARE(event->points().first().state(), QEventPoint::State::Stationary);
|
2012-03-24 08:48:12 +00:00
|
|
|
++mouseDoubleClickedCount;
|
|
|
|
mouseSequenceSignature += 'd';
|
|
|
|
}
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void touchEvent(QTouchEvent *event) override
|
|
|
|
{
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << event;
|
|
|
|
touchDevice = event->pointingDevice();
|
2012-01-12 07:53:13 +00:00
|
|
|
if (ignoreTouch) {
|
|
|
|
event->ignore();
|
|
|
|
return;
|
|
|
|
}
|
2012-02-09 12:39:40 +00:00
|
|
|
touchEventType = event->type();
|
2020-09-08 06:45:24 +00:00
|
|
|
QList<QTouchEvent::TouchPoint> points = event->points();
|
Port from container.count()/length() to size()
This is semantic patch using ClangTidyTransformator:
auto QtContainerClass = expr(hasType(namedDecl(hasAnyName(<classes>)))).bind(o)
makeRule(cxxMemberCallExpr(on(QtContainerClass),
callee(cxxMethodDecl(hasAnyName({"count", "length"),
parameterCountIs(0))))),
changeTo(cat(access(o, cat("size"), "()"))),
cat("use 'size()' instead of 'count()/length()'"))
a.k.a qt-port-to-std-compatible-api with config Scope: 'Container'.
<classes> are:
// sequential:
"QByteArray",
"QList",
"QQueue",
"QStack",
"QString",
"QVarLengthArray",
"QVector",
// associative:
"QHash",
"QMultiHash",
"QMap",
"QMultiMap",
"QSet",
// Qt has no QMultiSet
Change-Id: Ibe8837be96e8d30d1846881ecd65180c1bc459af
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
2022-09-30 12:09:04 +00:00
|
|
|
for (int i = 0; i < points.size(); ++i) {
|
2021-01-12 19:17:15 +00:00
|
|
|
const auto &point = points.at(i);
|
|
|
|
switch (point.state()) {
|
2020-03-27 16:06:11 +00:00
|
|
|
case QEventPoint::State::Pressed:
|
2011-12-12 15:24:33 +00:00
|
|
|
++touchPressedCount;
|
2012-04-23 13:18:01 +00:00
|
|
|
if (spinLoopWhenPressed)
|
|
|
|
QCoreApplication::processEvents();
|
2021-01-12 19:17:15 +00:00
|
|
|
if (i == 0) {
|
|
|
|
touchPressLocalPos = point.position();
|
|
|
|
touchPressGlobalPos = point.globalPosition();
|
|
|
|
}
|
2022-05-24 11:00:07 +00:00
|
|
|
if (closeOnTap)
|
|
|
|
this->close();
|
2011-12-12 15:24:33 +00:00
|
|
|
break;
|
2020-03-27 16:06:11 +00:00
|
|
|
case QEventPoint::State::Released:
|
2011-12-12 15:24:33 +00:00
|
|
|
++touchReleasedCount;
|
|
|
|
break;
|
2020-03-27 16:06:11 +00:00
|
|
|
case QEventPoint::State::Updated:
|
2012-02-09 12:39:40 +00:00
|
|
|
++touchMovedCount;
|
|
|
|
break;
|
2012-01-12 07:53:13 +00:00
|
|
|
default:
|
|
|
|
break;
|
2011-12-12 15:24:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
bool event(QEvent *e) override
|
|
|
|
{
|
2016-01-19 21:32:52 +00:00
|
|
|
switch (e->type()) {
|
|
|
|
case QEvent::Enter:
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << e;
|
2016-01-19 21:32:52 +00:00
|
|
|
++enterEventCount;
|
|
|
|
break;
|
|
|
|
case QEvent::Leave:
|
2020-09-08 06:45:24 +00:00
|
|
|
qCDebug(lcTests) << e;
|
2016-01-19 21:32:52 +00:00
|
|
|
++leaveEventCount;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
return ColoredWindow::event(e);
|
2016-01-19 21:32:52 +00:00
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
void resetCounters()
|
|
|
|
{
|
2012-04-23 13:18:01 +00:00
|
|
|
mousePressedCount = mouseReleasedCount = mouseMovedCount = mouseDoubleClickedCount = 0;
|
2019-05-10 07:10:27 +00:00
|
|
|
mouseSequenceSignature.clear();
|
2012-03-24 08:48:12 +00:00
|
|
|
touchPressedCount = touchReleasedCount = touchMovedCount = 0;
|
2016-01-19 21:32:52 +00:00
|
|
|
enterEventCount = leaveEventCount = 0;
|
2012-03-24 08:48:12 +00:00
|
|
|
}
|
2011-12-12 15:24:33 +00:00
|
|
|
|
2017-12-20 13:07:17 +00:00
|
|
|
explicit InputTestWindow(const QColor &color = Qt::white, QWindow *parent = nullptr)
|
2019-05-10 07:10:27 +00:00
|
|
|
: ColoredWindow(color, parent) {}
|
2011-12-12 15:24:33 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
int keyPressCode = 0, keyReleaseCode = 0;
|
|
|
|
int mousePressButton = 0, mouseReleaseButton = 0, mouseMoveButton = 0;
|
|
|
|
int mousePressedCount = 0, mouseReleasedCount = 0, mouseMovedCount = 0, mouseDoubleClickedCount = 0;
|
2012-03-24 08:48:12 +00:00
|
|
|
QString mouseSequenceSignature;
|
2012-05-23 12:38:22 +00:00
|
|
|
QPointF mousePressScreenPos, mouseMoveScreenPos, mousePressLocalPos;
|
2021-01-12 19:17:15 +00:00
|
|
|
QPointF touchPressGlobalPos, touchPressLocalPos;
|
2019-05-10 07:10:27 +00:00
|
|
|
int touchPressedCount = 0, touchReleasedCount = 0, touchMovedCount = 0;
|
|
|
|
QEvent::Type touchEventType = QEvent::None;
|
|
|
|
int enterEventCount = 0, leaveEventCount = 0;
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
bool ignoreMouse = false, ignoreTouch = false;
|
2012-04-23 13:18:01 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
bool spinLoopWhenPressed = false;
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
Qt::MouseButtons buttonStateInGeneratedMove;
|
2020-09-08 06:45:24 +00:00
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
const QPointingDevice *mouseMoveDevice = nullptr;
|
2020-09-08 06:45:24 +00:00
|
|
|
const QPointingDevice *mouseDevice = nullptr;
|
|
|
|
const QPointingDevice *touchDevice = nullptr;
|
2022-05-24 11:00:07 +00:00
|
|
|
|
|
|
|
bool closeOnTap = false;
|
2011-12-12 15:24:33 +00:00
|
|
|
};
|
|
|
|
|
2019-09-04 08:23:47 +00:00
|
|
|
static void simulateMouseClick(QWindow *target, const QPointF &local, const QPointF &global)
|
|
|
|
{
|
|
|
|
QWindowSystemInterface::handleMouseEvent(target, local, global,
|
2020-09-30 20:37:09 +00:00
|
|
|
Qt::LeftButton, Qt::LeftButton, QEvent::MouseButtonPress);
|
2019-09-04 08:23:47 +00:00
|
|
|
QWindowSystemInterface::handleMouseEvent(target, local, global,
|
2020-09-30 20:37:09 +00:00
|
|
|
{}, Qt::LeftButton, QEvent::MouseButtonRelease);
|
2019-09-04 08:23:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void simulateMouseClick(QWindow *target, ulong &timeStamp,
|
|
|
|
const QPointF &local, const QPointF &global)
|
|
|
|
{
|
|
|
|
QWindowSystemInterface::handleMouseEvent(target, timeStamp++, local, global,
|
2020-09-30 20:37:09 +00:00
|
|
|
Qt::LeftButton, Qt::LeftButton, QEvent::MouseButtonPress);
|
2019-09-04 08:23:47 +00:00
|
|
|
QWindowSystemInterface::handleMouseEvent(target, timeStamp++, local, global,
|
2020-09-30 20:37:09 +00:00
|
|
|
{}, Qt::LeftButton, QEvent::MouseButtonRelease);
|
2019-09-04 08:23:47 +00:00
|
|
|
}
|
|
|
|
|
2011-12-12 15:24:33 +00:00
|
|
|
void tst_QWindow::testInputEvents()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2013-05-31 18:27:50 +00:00
|
|
|
window.showNormal();
|
2025-05-22 10:40:18 +00:00
|
|
|
QTRY_VERIFY(window.isActive());
|
2025-01-21 14:29:36 +00:00
|
|
|
QVERIFY(QTestPrivate::ensurePositionTopLeft(&window));
|
2011-12-12 15:24:33 +00:00
|
|
|
|
2015-06-18 15:01:01 +00:00
|
|
|
QTest::keyClick(&window, Qt::Key_A, Qt::NoModifier);
|
2011-12-12 15:24:33 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.keyPressCode, int(Qt::Key_A));
|
|
|
|
QCOMPARE(window.keyReleaseCode, int(Qt::Key_A));
|
|
|
|
|
|
|
|
QPointF local(12, 34);
|
2015-06-18 15:01:01 +00:00
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, local.toPoint());
|
2011-12-12 15:24:33 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
|
|
|
|
QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
2012-05-23 12:38:22 +00:00
|
|
|
QCOMPARE(window.mousePressLocalPos, local);
|
2011-12-12 15:24:33 +00:00
|
|
|
|
2012-01-12 07:53:13 +00:00
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
|
|
|
QWindowSystemInterface::TouchPoint tp1, tp2;
|
|
|
|
tp1.id = 1;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2012-01-12 07:53:13 +00:00
|
|
|
tp1.area = QRect(10, 10, 4, 4);
|
|
|
|
tp2.id = 2;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp2.state = QEventPoint::State::Pressed;
|
2012-01-12 07:53:13 +00:00
|
|
|
tp2.area = QRect(20, 20, 4, 4);
|
|
|
|
points << tp1 << tp2;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
points[1].state = QEventPoint::State::Released;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 2);
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 2);
|
2012-05-23 12:38:22 +00:00
|
|
|
|
|
|
|
// Now with null pointer as window. local param should not be utilized:
|
|
|
|
// handleMouseEvent() with tlw == 0 means the event is in global coords only.
|
|
|
|
window.mousePressButton = window.mouseReleaseButton = 0;
|
2015-06-18 15:01:01 +00:00
|
|
|
const QPointF nonWindowGlobal(window.geometry().topRight() + QPoint(200, 50)); // not inside the window
|
|
|
|
const QPointF deviceNonWindowGlobal = QHighDpi::toNativePixels(nonWindowGlobal, window.screen());
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(nullptr, deviceNonWindowGlobal, deviceNonWindowGlobal);
|
2012-05-23 12:38:22 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressButton, 0);
|
|
|
|
QCOMPARE(window.mouseReleaseButton, 0);
|
2015-06-18 15:01:01 +00:00
|
|
|
const QPointF windowGlobal = window.mapToGlobal(local.toPoint());
|
|
|
|
const QPointF deviceWindowGlobal = QHighDpi::toNativePixels(windowGlobal, window.screen());
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(nullptr, deviceWindowGlobal, deviceWindowGlobal);
|
2012-05-23 12:38:22 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
|
|
|
|
QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
|
|
|
QCOMPARE(window.mousePressScreenPos, windowGlobal);
|
|
|
|
QCOMPARE(window.mousePressLocalPos, local); // the local we passed was bogus, verify that qGuiApp calculated the proper one
|
2012-01-12 07:53:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::touchToMouseTranslation()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-01-12 07:53:13 +00:00
|
|
|
window.ignoreTouch = true;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-01-12 07:53:13 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2011-12-12 15:24:33 +00:00
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
2022-05-24 11:00:07 +00:00
|
|
|
QWindowSystemInterface::TouchPoint tp1, tp2;
|
2012-01-23 08:28:58 +00:00
|
|
|
const QRectF pressArea(101, 102, 4, 4);
|
|
|
|
const QRectF moveArea(105, 108, 4, 4);
|
2011-12-12 15:24:33 +00:00
|
|
|
tp1.id = 1;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2015-06-18 15:01:01 +00:00
|
|
|
tp1.area = QHighDpi::toNativePixels(pressArea, &window);
|
2011-12-12 15:24:33 +00:00
|
|
|
tp2.id = 2;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp2.state = QEventPoint::State::Pressed;
|
2011-12-12 15:24:33 +00:00
|
|
|
points << tp1 << tp2;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2012-01-23 08:28:58 +00:00
|
|
|
// Now an update but with changed list order. The mouse event should still
|
|
|
|
// be generated from the point with id 1.
|
|
|
|
tp1.id = 2;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Stationary;
|
2012-01-23 08:28:58 +00:00
|
|
|
tp2.id = 1;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp2.state = QEventPoint::State::Updated;
|
2015-06-18 15:01:01 +00:00
|
|
|
tp2.area = QHighDpi::toNativePixels(moveArea, &window);
|
2012-01-23 08:28:58 +00:00
|
|
|
points.clear();
|
|
|
|
points << tp1 << tp2;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
points[1].state = QEventPoint::State::Released;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
2012-01-23 08:28:58 +00:00
|
|
|
QTRY_COMPARE(window.mousePressScreenPos, pressArea.center());
|
|
|
|
QTRY_COMPARE(window.mouseMoveScreenPos, moveArea.center());
|
2020-09-08 06:45:24 +00:00
|
|
|
QCOMPARE(window.mouseDevice, window.touchDevice);
|
|
|
|
QCOMPARE(window.mouseDevice->type(), QInputDevice::DeviceType::TouchScreen);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
window.mousePressButton = 0;
|
|
|
|
window.mouseReleaseButton = 0;
|
|
|
|
|
|
|
|
window.ignoreTouch = false;
|
|
|
|
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Pressed;
|
|
|
|
points[1].state = QEventPoint::State::Pressed;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
points[1].state = QEventPoint::State::Released;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
// no new mouse events should be generated since the input window handles the touch events
|
|
|
|
QTRY_COMPARE(window.mousePressButton, 0);
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, 0);
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
window.ignoreTouch = true;
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Pressed;
|
|
|
|
points[1].state = QEventPoint::State::Pressed;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
points[1].state = QEventPoint::State::Released;
|
2012-01-12 07:53:13 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2011-12-12 15:24:33 +00:00
|
|
|
QCoreApplication::processEvents();
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
// mouse event synthesis was disabled when the events were sent above
|
2012-01-12 07:53:13 +00:00
|
|
|
QTRY_COMPARE(window.mousePressButton, 0);
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, 0);
|
2020-09-29 13:33:11 +00:00
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
// but now, mouse event synthesis is enabled
|
2020-09-29 13:33:11 +00:00
|
|
|
points.clear();
|
|
|
|
points.append(tp2);
|
|
|
|
points[0].state = QEventPoint::State::Pressed;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
points.clear();
|
|
|
|
points.append(tp1);
|
|
|
|
points[0].state = QEventPoint::State::Pressed;
|
2023-02-22 11:03:16 +00:00
|
|
|
const int mouseMoveCountWas = window.mouseMovedCount;
|
2020-09-29 13:33:11 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.mousePressButton, 1);
|
2023-02-22 11:03:16 +00:00
|
|
|
QCOMPARE(window.mouseDevice, touchDevice);
|
|
|
|
// tp1's position is different than tp2's, so
|
|
|
|
// QGuiApplicationPrivate::processMouseEvent() sent a synth-mouse move
|
|
|
|
QCOMPARE(window.mouseMoveDevice, touchDevice);
|
|
|
|
QCOMPARE(window.mouseMovedCount, mouseMoveCountWas + 1);
|
2020-09-29 13:33:11 +00:00
|
|
|
|
|
|
|
points.clear();
|
|
|
|
points.append(tp2);
|
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
points.clear();
|
|
|
|
points.append(tp1);
|
|
|
|
points[0].state = QEventPoint::State::Released;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, 1);
|
2012-01-12 07:53:13 +00:00
|
|
|
}
|
|
|
|
|
2014-12-09 15:52:24 +00:00
|
|
|
void tst_QWindow::touchToMouseTranslationForDevices()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-12-09 15:52:24 +00:00
|
|
|
window.ignoreTouch = true;
|
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
|
|
|
QPoint touchPoint(10, 10);
|
|
|
|
|
|
|
|
QTest::touchEvent(&window, touchDevice).press(0, touchPoint, &window);
|
|
|
|
QTest::touchEvent(&window, touchDevice).release(0, touchPoint, &window);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
QCOMPARE(window.mousePressedCount, 1);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 1);
|
|
|
|
|
|
|
|
window.resetCounters();
|
|
|
|
|
Introduce QInputDevice hierarchy; replace QTouchDevice
We have seen during the Qt 5 series that QMouseEvent::source() does
not provide enough information: if it is synthesized, it could have
come from any device for which mouse events are synthesized, not only
from a touchscreen. By providing in every QInputEvent as complete
information about the actual source device as possible, we will enable
very fine-tuned behavior in the object that handles each event.
Further, we would like to support multiple keyboards, pointing devices,
and named groups of devices that are known as "seats" in Wayland.
In Qt 5, QPA plugins registered each touchscreen as it was discovered.
Now we extend this pattern to all input devices. This new requirement
can be implemented gradually; for now, if a QTWSI input event is
received wtihout a device pointer, a default "core" device will be
created on-the-fly, and a warning emitted.
In Qt 5, QTouchEvent::TouchPoint::id() was forced to be unique even when
multiple devices were in use simultaneously. Now that each event
identifies the device it came from, this hack is no longer needed.
A stub of the new QPointerEvent is added; it will be developed further
in subsequent patches.
[ChangeLog][QtGui][QInputEvent] Every QInputEvent now carries a pointer
to an instance of QInputDevice, or the subclass QPointingDevice in case
of mouse, touch and tablet events. Each platform plugin is expected to
create the device instances, register them, and provide valid pointers
with all input events. If this is not done, warnings are emitted and
default devices are created as necessary. When the device has accurate
information, it provides the opportunity to fine-tune behavior depending
on device type and capabilities: for example if a QMouseEvent is
synthesized from a touchscreen, the recipient can see which touchscreen
it came from. Each device also has a seatName to distinguish users on
multi-user windowing systems. Touchpoint IDs are no longer unique on
their own, but the combination of ID and device is.
Fixes: QTBUG-46412
Fixes: QTBUG-72167
Task-number: QTBUG-69433
Task-number: QTBUG-52430
Change-Id: I933fb2b86182efa722037b7a33e404c5daf5292a
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-05-31 06:38:16 +00:00
|
|
|
QTest::touchEvent(&window, touchDeviceWithMouseEmulation).press(0, touchPoint, &window);
|
|
|
|
QTest::touchEvent(&window, touchDeviceWithMouseEmulation).release(0, touchPoint, &window);
|
2014-12-09 15:52:24 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
QCOMPARE(window.mousePressedCount, 0);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 0);
|
|
|
|
}
|
|
|
|
|
2012-01-12 07:53:13 +00:00
|
|
|
void tst_QWindow::mouseToTouchTranslation()
|
|
|
|
{
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-01-12 07:53:13 +00:00
|
|
|
window.ignoreMouse = true;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-01-12 07:53:13 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2021-01-12 19:17:15 +00:00
|
|
|
const QPoint localPos(10, 10);
|
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, {}, localPos);
|
2012-01-12 07:53:13 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 1);
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 1);
|
2020-09-08 06:45:24 +00:00
|
|
|
QCOMPARE(window.mouseDevice, window.touchDevice);
|
2022-02-16 10:58:23 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QEXPECT_FAIL("", "Wayland: This fails. See QTBUG-100887.", Abort);
|
2020-09-08 06:45:24 +00:00
|
|
|
QCOMPARE(window.touchDevice->type(), QInputDevice::DeviceType::Mouse);
|
2021-01-12 19:17:15 +00:00
|
|
|
QCOMPARE(window.touchPressLocalPos.toPoint(), localPos);
|
|
|
|
QCOMPARE(window.touchPressGlobalPos.toPoint(), window.mapToGlobal(localPos));
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
window.ignoreMouse = false;
|
|
|
|
|
2021-01-12 19:17:15 +00:00
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, {}, localPos);
|
2012-01-12 07:53:13 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
// no new touch events should be generated since the input window handles the mouse events
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 1);
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 1);
|
|
|
|
|
|
|
|
window.ignoreMouse = true;
|
|
|
|
|
2021-01-12 19:17:15 +00:00
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, {}, localPos);
|
2012-01-12 07:53:13 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
// touch event synthesis disabled
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 1);
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::mouseToTouchLoop()
|
|
|
|
{
|
|
|
|
// make sure there's no infinite loop when synthesizing both ways
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
|
2012-01-12 07:53:13 +00:00
|
|
|
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
|
|
|
|
2012-01-12 07:53:13 +00:00
|
|
|
window.ignoreMouse = true;
|
|
|
|
window.ignoreTouch = true;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-01-12 07:53:13 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-01-12 07:53:13 +00:00
|
|
|
|
2019-09-04 08:23:47 +00:00
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, {}, QPoint(10, 10));
|
2012-01-12 07:53:13 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false);
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
|
2012-02-09 12:39:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::touchCancel()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-02-09 12:39:40 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-02-09 12:39:40 +00:00
|
|
|
|
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
|
|
|
QWindowSystemInterface::TouchPoint tp1;
|
|
|
|
tp1.id = 1;
|
|
|
|
|
|
|
|
// Start a touch.
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2012-02-09 12:39:40 +00:00
|
|
|
tp1.area = QRect(10, 10, 4, 4);
|
|
|
|
points << tp1;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 1);
|
|
|
|
|
|
|
|
// Cancel the active touch sequence.
|
|
|
|
QWindowSystemInterface::handleTouchCancelEvent(&window, touchDevice);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
|
|
|
|
|
|
|
|
// Send a move -> will not be delivered due to the cancellation.
|
|
|
|
QTRY_COMPARE(window.touchMovedCount, 0);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Updated;
|
2012-02-09 12:39:40 +00:00
|
|
|
tp1.area.adjust(2, 2, 2, 2);
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchMovedCount, 0);
|
|
|
|
|
|
|
|
// Likewise. The only allowed transition is TouchCancel -> TouchBegin.
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 0);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
2012-02-09 12:39:40 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 0);
|
|
|
|
|
|
|
|
// Start a new sequence -> from this point on everything should go through normally.
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Pressed;
|
2012-02-09 12:39:40 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 2);
|
|
|
|
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Updated;
|
2012-02-09 12:39:40 +00:00
|
|
|
tp1.area.adjust(2, 2, 2, 2);
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchMovedCount, 1);
|
|
|
|
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
2012-02-09 12:39:40 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::touchCancelWithTouchToMouse()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-02-09 12:39:40 +00:00
|
|
|
window.ignoreTouch = true;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-02-09 12:39:40 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-02-09 12:39:40 +00:00
|
|
|
|
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
|
|
|
QWindowSystemInterface::TouchPoint tp1;
|
|
|
|
tp1.id = 1;
|
|
|
|
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2015-06-18 15:01:01 +00:00
|
|
|
const QRect area(100, 100, 4, 4);
|
|
|
|
tp1.area = QHighDpi::toNativePixels(area, &window);
|
2012-02-09 12:39:40 +00:00
|
|
|
points << tp1;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
|
2015-06-18 15:01:01 +00:00
|
|
|
const int fuzz = 2 * int(QHighDpiScaling::factor(&window));
|
|
|
|
QTRY_VERIFY2(qFuzzyCompareWindowPosition(window.mousePressScreenPos.toPoint(), area.center(), fuzz),
|
|
|
|
qPrintable(msgPointMismatch(window.mousePressScreenPos.toPoint(), area.center())));
|
2012-02-09 12:39:40 +00:00
|
|
|
|
|
|
|
// Cancel the touch. Should result in a mouse release for windows that have
|
|
|
|
// have an active touch-to-mouse sequence.
|
2019-05-10 07:10:27 +00:00
|
|
|
QWindowSystemInterface::handleTouchCancelEvent(nullptr, touchDevice);
|
2012-02-09 12:39:40 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
|
|
|
|
|
|
|
// Now change the window to accept touches.
|
|
|
|
window.mousePressButton = window.mouseReleaseButton = 0;
|
|
|
|
window.ignoreTouch = false;
|
|
|
|
|
|
|
|
// Send a touch, there will be no mouse event generated.
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.mousePressButton, 0);
|
|
|
|
|
|
|
|
// Cancel the touch. It should not result in a mouse release with this window.
|
2019-05-10 07:10:27 +00:00
|
|
|
QWindowSystemInterface::handleTouchCancelEvent(nullptr, touchDevice);
|
2012-02-09 12:39:40 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, 0);
|
2011-12-12 15:24:33 +00:00
|
|
|
}
|
|
|
|
|
2014-03-11 21:10:05 +00:00
|
|
|
void tst_QWindow::touchInterruptedByPopup()
|
|
|
|
{
|
2025-06-23 09:21:04 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("Wayland: need real user action like a button press, key press, or touch down event.");
|
|
|
|
|
2014-03-11 21:10:05 +00:00
|
|
|
InputTestWindow window;
|
Move popup management from QApplication to QGuiApplication
We need to be able to have true popup windows in Qt Quick and Controls,
including handling the press-drag-release sequence to select one entry
from a menu or combobox. After the mouse press, a new window is created.
On some platforms (such as xcb), the new window gets window system grabs
of both keyboard and mouse (QApplicationPrivate::openPopup() calls
grabForPopup() and it actually works); while on others, the pre-existing
window continues to get the whole sequence of mouse events until the
release. In the latter case, Qt needs to forward events from the
original window to the popup. Until now, the list of popups was
QApplicationPrivate::popupWidgets.
Now we track the open popups as a list of QWindows rather than QWidgets,
in QGuiApplicationPrivate::popup_list, and add a set of static functions
to manage that list. Functions such as QApplication::activePopupWidget()
QApplicationPrivate::openPopup() and closePopup() are rewritten to make
requests to QGuiApplicationPrivate.
276943c8b791ba5897dcdb1ecfda780ac33a090b made
QGuiApplicationPrivate::closeAllPopups() virtual. That is now reverted,
because we're putting QGuiApplication in charge of popup management
and trying to minimize widget-specific behavior. So far,
QApplicationPrivate::closePopup() is still overridden to take care
of focus changes.
So far, QtGui does not take care of closing popups when the user
clicks outside: the active popup window gets those events, and needs
to close itself if the click occurs outside. An attempt to move this
logic raised some issues with legacy widget test cases.
Using a touchscreen to press on QMenuBar and open a QMenu, drag to
a menu item and release, is temporarily broken for now. The plan is
to fix it in a subsequent patch.
Task-number: QTBUG-68080
Task-number: QTBUG-69777
Change-Id: I02b5034987b5ee8909917d305f414c8b0db9c7f5
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2023-03-22 09:11:44 +00:00
|
|
|
window.setObjectName("main");
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2014-03-11 21:10:05 +00:00
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
|
|
|
QWindowSystemInterface::TouchPoint tp1;
|
|
|
|
tp1.id = 1;
|
|
|
|
|
|
|
|
// Start a touch.
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2014-03-11 21:10:05 +00:00
|
|
|
tp1.area = QRect(10, 10, 4, 4);
|
|
|
|
points << tp1;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchEventType, QEvent::TouchBegin);
|
|
|
|
QTRY_COMPARE(window.touchPressedCount, 1);
|
|
|
|
|
|
|
|
// Launch a popup window
|
|
|
|
InputTestWindow popup;
|
2025-06-23 09:26:46 +00:00
|
|
|
popup.setObjectName("popup");
|
2014-03-11 21:10:05 +00:00
|
|
|
popup.setFlags(Qt::Popup);
|
|
|
|
popup.setModality(Qt::WindowModal);
|
2014-07-31 11:48:15 +00:00
|
|
|
popup.resize(m_testWindowSize / 2);
|
2014-03-11 21:10:05 +00:00
|
|
|
popup.setTransientParent(&window);
|
|
|
|
popup.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&popup));
|
|
|
|
|
|
|
|
// Send a move -> will not be delivered to the original window
|
|
|
|
// (TODO verify where it is forwarded, after we've defined that)
|
|
|
|
QTRY_COMPARE(window.touchMovedCount, 0);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Updated;
|
2014-03-11 21:10:05 +00:00
|
|
|
tp1.area.adjust(2, 2, 2, 2);
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchMovedCount, 0);
|
|
|
|
|
|
|
|
// Send a touch end -> will not be delivered to the original window
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 0);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
2014-03-11 21:10:05 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(window.touchReleasedCount, 0);
|
|
|
|
}
|
|
|
|
|
2012-01-13 09:31:11 +00:00
|
|
|
void tst_QWindow::orientation()
|
|
|
|
{
|
2012-02-21 02:00:14 +00:00
|
|
|
qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
|
|
|
|
|
2012-01-13 09:31:11 +00:00
|
|
|
QWindow window;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-01-13 09:31:11 +00:00
|
|
|
window.create();
|
|
|
|
|
|
|
|
window.reportContentOrientationChange(Qt::PortraitOrientation);
|
|
|
|
QCOMPARE(window.contentOrientation(), Qt::PortraitOrientation);
|
|
|
|
|
|
|
|
window.reportContentOrientationChange(Qt::PrimaryOrientation);
|
2012-01-25 12:41:43 +00:00
|
|
|
QCOMPARE(window.contentOrientation(), Qt::PrimaryOrientation);
|
2012-01-13 09:31:11 +00:00
|
|
|
|
2012-01-25 12:41:43 +00:00
|
|
|
QSignalSpy spy(&window, SIGNAL(contentOrientationChanged(Qt::ScreenOrientation)));
|
|
|
|
window.reportContentOrientationChange(Qt::LandscapeOrientation);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2012-01-13 09:31:11 +00:00
|
|
|
}
|
|
|
|
|
2012-11-01 16:03:45 +00:00
|
|
|
void tst_QWindow::sizes()
|
|
|
|
{
|
|
|
|
QWindow window;
|
|
|
|
|
|
|
|
QSignalSpy minimumWidthSpy(&window, SIGNAL(minimumWidthChanged(int)));
|
|
|
|
QSignalSpy minimumHeightSpy(&window, SIGNAL(minimumHeightChanged(int)));
|
|
|
|
QSignalSpy maximumWidthSpy(&window, SIGNAL(maximumWidthChanged(int)));
|
|
|
|
QSignalSpy maximumHeightSpy(&window, SIGNAL(maximumHeightChanged(int)));
|
|
|
|
|
|
|
|
QSize oldMaximum = window.maximumSize();
|
|
|
|
|
|
|
|
window.setMinimumWidth(10);
|
|
|
|
QCOMPARE(window.minimumWidth(), 10);
|
|
|
|
QCOMPARE(window.minimumHeight(), 0);
|
|
|
|
QCOMPARE(window.minimumSize(), QSize(10, 0));
|
|
|
|
QCOMPARE(window.maximumSize(), oldMaximum);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(minimumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(minimumHeightSpy.size(), 0);
|
|
|
|
QCOMPARE(maximumWidthSpy.size(), 0);
|
|
|
|
QCOMPARE(maximumHeightSpy.size(), 0);
|
2012-11-01 16:03:45 +00:00
|
|
|
|
|
|
|
window.setMinimumHeight(10);
|
|
|
|
QCOMPARE(window.minimumWidth(), 10);
|
|
|
|
QCOMPARE(window.minimumHeight(), 10);
|
|
|
|
QCOMPARE(window.minimumSize(), QSize(10, 10));
|
|
|
|
QCOMPARE(window.maximumSize(), oldMaximum);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(minimumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(minimumHeightSpy.size(), 1);
|
|
|
|
QCOMPARE(maximumWidthSpy.size(), 0);
|
|
|
|
QCOMPARE(maximumHeightSpy.size(), 0);
|
2012-11-01 16:03:45 +00:00
|
|
|
|
|
|
|
window.setMaximumWidth(100);
|
|
|
|
QCOMPARE(window.maximumWidth(), 100);
|
|
|
|
QCOMPARE(window.maximumHeight(), oldMaximum.height());
|
|
|
|
QCOMPARE(window.minimumSize(), QSize(10, 10));
|
|
|
|
QCOMPARE(window.maximumSize(), QSize(100, oldMaximum.height()));
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(minimumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(minimumHeightSpy.size(), 1);
|
|
|
|
QCOMPARE(maximumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(maximumHeightSpy.size(), 0);
|
2012-11-01 16:03:45 +00:00
|
|
|
|
|
|
|
window.setMaximumHeight(100);
|
|
|
|
QCOMPARE(window.maximumWidth(), 100);
|
|
|
|
QCOMPARE(window.maximumHeight(), 100);
|
|
|
|
QCOMPARE(window.minimumSize(), QSize(10, 10));
|
|
|
|
QCOMPARE(window.maximumSize(), QSize(100, 100));
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(minimumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(minimumHeightSpy.size(), 1);
|
|
|
|
QCOMPARE(maximumWidthSpy.size(), 1);
|
|
|
|
QCOMPARE(maximumHeightSpy.size(), 1);
|
2022-05-13 09:01:43 +00:00
|
|
|
|
|
|
|
// test if min and max limits will change the size
|
|
|
|
QVERIFY(window.minimumWidth() < 50 && window.maximumWidth() > 80);
|
|
|
|
QVERIFY(window.minimumHeight() < 50 && window.maximumHeight() > 80);
|
|
|
|
window.resize(50, 50);
|
|
|
|
QCOMPARE(window.size(), QSize(50, 50));
|
|
|
|
window.setMinimumSize(QSize(60, 60));
|
|
|
|
QCOMPARE(window.size(), QSize(60, 60));
|
|
|
|
window.resize(80, 80);
|
|
|
|
window.setMaximumSize(QSize(70, 70));
|
|
|
|
QCOMPARE(window.size(), QSize(70, 70));
|
2023-05-09 10:27:01 +00:00
|
|
|
|
|
|
|
// QTBUG-113233
|
|
|
|
// test for an invalid min/max pair
|
|
|
|
window.setMinimumSize(QSize(80, 80)); // current maximumSize = QSize(70, 70)
|
|
|
|
QCOMPARE(window.size(), QSize(70, 70));
|
|
|
|
window.setMaximumSize(QSize(90, 90));
|
|
|
|
QCOMPARE(window.size(), QSize(80, 80));
|
2012-11-01 16:03:45 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 13:36:08 +00:00
|
|
|
class CloseOnCloseEventWindow : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline static int closeEvents;
|
|
|
|
CloseOnCloseEventWindow() { closeEvents = 0; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void closeEvent(QCloseEvent *e) override
|
|
|
|
{
|
|
|
|
if (++closeEvents > 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
close();
|
|
|
|
e->accept();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-01-20 12:08:42 +00:00
|
|
|
void tst_QWindow::close()
|
|
|
|
{
|
2020-08-10 17:00:56 +00:00
|
|
|
{
|
|
|
|
QWindow a;
|
|
|
|
QWindow b;
|
|
|
|
QWindow c(&a);
|
|
|
|
|
|
|
|
a.show();
|
|
|
|
b.show();
|
|
|
|
|
|
|
|
// we can not close a non top level window
|
|
|
|
QVERIFY(!c.close());
|
|
|
|
QVERIFY(a.close());
|
|
|
|
QVERIFY(b.close());
|
|
|
|
}
|
2012-01-20 12:08:42 +00:00
|
|
|
|
2020-08-10 17:00:56 +00:00
|
|
|
// Verify that closing a QWindow deletes its platform window,
|
|
|
|
// independent of API used to close the window.
|
|
|
|
{
|
|
|
|
// Close with QWindow::close
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
w.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
w.close();
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
}
|
2012-01-20 12:08:42 +00:00
|
|
|
|
2020-08-10 17:00:56 +00:00
|
|
|
// Close with QWindowSystemInterface::handleCloseEvent();
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
w.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
QWindowSystemInterface::handleCloseEvent(&w);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that closing a QWindow deletes the platform window for
|
|
|
|
// child windows
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
QWindow c(&w);
|
|
|
|
w.create();
|
|
|
|
c.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
QVERIFY(c.handle());
|
|
|
|
w.close();
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
QVERIFY(!c.handle());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that re-creating closed windows is possble.
|
|
|
|
{
|
|
|
|
// Re-create top-level window
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
w.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
w.close();
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
w.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-create top-level window with child window
|
|
|
|
{
|
|
|
|
QWindow w;
|
|
|
|
QWindow c(&w);
|
|
|
|
c.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
QVERIFY(c.handle());
|
|
|
|
w.close();
|
|
|
|
QVERIFY(!w.handle());
|
|
|
|
QVERIFY(!c.handle());
|
|
|
|
c.create();
|
|
|
|
QVERIFY(w.handle());
|
|
|
|
QVERIFY(c.handle());
|
|
|
|
}
|
|
|
|
}
|
2021-10-13 13:36:08 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// A QWidget will call close() from the destructor, and
|
|
|
|
// we allow widgets deleting itself in the closeEvent,
|
|
|
|
// so we need to guard against close being called recursively.
|
|
|
|
CloseOnCloseEventWindow w;
|
|
|
|
w.create();
|
|
|
|
w.close();
|
|
|
|
QCOMPARE(CloseOnCloseEventWindow::closeEvents, 1);
|
|
|
|
}
|
2012-01-20 12:08:42 +00:00
|
|
|
}
|
|
|
|
|
2012-02-24 17:05:06 +00:00
|
|
|
void tst_QWindow::activateAndClose()
|
|
|
|
{
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2019-05-10 07:10:27 +00:00
|
|
|
for (int i = 0; i < 10; ++i) {
|
2012-02-24 17:05:06 +00:00
|
|
|
QWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()) + QString::number(i));
|
2014-03-18 16:18:04 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
window.setSurfaceType(QSurface::OpenGLSurface);
|
|
|
|
#endif
|
2013-11-15 18:25:54 +00:00
|
|
|
// qWaitForWindowActive will block for the duration of
|
|
|
|
// of the timeout if the window is at 0,0
|
|
|
|
window.setGeometry(QGuiApplication::primaryScreen()->availableGeometry().adjusted(1, 1, -1, -1));
|
|
|
|
window.showNormal();
|
2014-03-18 16:18:04 +00:00
|
|
|
#if defined(Q_OS_QNX) // We either need to create a eglSurface or a create a backing store
|
|
|
|
// and then post the window in order for screen to show the window
|
2017-09-08 16:42:25 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2014-03-18 16:18:04 +00:00
|
|
|
QOpenGLContext context;
|
|
|
|
context.create();
|
|
|
|
context.makeCurrent(&window);
|
|
|
|
context.swapBuffers(&window);
|
|
|
|
#endif
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.requestActivate();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
2019-05-10 07:10:27 +00:00
|
|
|
QCOMPARE(QGuiApplication::focusWindow(), &window);
|
2012-02-24 17:05:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 08:48:12 +00:00
|
|
|
void tst_QWindow::mouseEventSequence()
|
|
|
|
{
|
2019-05-10 07:10:27 +00:00
|
|
|
const auto doubleClickInterval = ulong(QGuiApplication::styleHints()->mouseDoubleClickInterval());
|
2012-03-24 08:48:12 +00:00
|
|
|
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-03-24 08:48:12 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-03-24 08:48:12 +00:00
|
|
|
|
|
|
|
ulong timestamp = 0;
|
|
|
|
QPointF local(12, 34);
|
2015-06-18 15:01:01 +00:00
|
|
|
const QPointF deviceLocal = QHighDpi::toNativePixels(local, &window);
|
2019-09-04 08:23:47 +00:00
|
|
|
|
|
|
|
simulateMouseClick(&window, timestamp, deviceLocal, deviceLocal);
|
2012-03-24 08:48:12 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressedCount, 1);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 1);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 0);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("pr"));
|
|
|
|
|
|
|
|
window.resetCounters();
|
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
|
|
|
|
// A double click must result in press, release, press, doubleclick, release.
|
|
|
|
// Check that no unexpected event suppression occurs and that the order is correct.
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressedCount, 2);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 2);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 1);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdr"));
|
|
|
|
|
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
window.resetCounters();
|
|
|
|
|
|
|
|
// Triple click = double + single click
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressedCount, 3);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 3);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 1);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrpr"));
|
|
|
|
|
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
window.resetCounters();
|
|
|
|
|
|
|
|
// Two double clicks.
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressedCount, 4);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 4);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 2);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("prpdrprpdr"));
|
|
|
|
|
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
window.resetCounters();
|
|
|
|
|
|
|
|
// Four clicks, none of which qualifies as a double click.
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
timestamp += doubleClickInterval;
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
timestamp += doubleClickInterval;
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
timestamp += doubleClickInterval;
|
2019-09-04 08:23:47 +00:00
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
2012-03-24 08:48:12 +00:00
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressedCount, 4);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 4);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 0);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("prprprpr"));
|
2025-03-14 09:01:33 +00:00
|
|
|
|
|
|
|
// Test double click across windows
|
|
|
|
InputTestWindow windowNew;
|
|
|
|
windowNew.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
|
|
|
windowNew.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&windowNew));
|
|
|
|
|
|
|
|
timestamp += doubleClickInterval;
|
|
|
|
windowNew.resetCounters();
|
|
|
|
window.resetCounters();
|
|
|
|
|
|
|
|
simulateMouseClick(&windowNew, timestamp, local, local);
|
|
|
|
simulateMouseClick(&window, timestamp, local, local);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(windowNew.mousePressedCount, 1);
|
|
|
|
QCOMPARE(windowNew.mouseReleasedCount, 1);
|
|
|
|
QCOMPARE(windowNew.mouseDoubleClickedCount, 0);
|
|
|
|
QCOMPARE(windowNew.mouseSequenceSignature, QLatin1String("pr"));
|
|
|
|
QCOMPARE(window.mousePressedCount, 1);
|
|
|
|
QCOMPARE(window.mouseReleasedCount, 1);
|
|
|
|
QCOMPARE(window.mouseDoubleClickedCount, 0);
|
|
|
|
QCOMPARE(window.mouseSequenceSignature, QLatin1String("pr"));
|
2012-03-24 08:48:12 +00:00
|
|
|
}
|
|
|
|
|
2012-04-04 10:11:40 +00:00
|
|
|
void tst_QWindow::windowModality()
|
|
|
|
{
|
|
|
|
qRegisterMetaType<Qt::WindowModality>("Qt::WindowModality");
|
|
|
|
|
|
|
|
QWindow window;
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
QSignalSpy spy(&window, SIGNAL(modalityChanged(Qt::WindowModality)));
|
2012-04-04 10:11:40 +00:00
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
QCOMPARE(window.modality(), Qt::NonModal);
|
|
|
|
window.setModality(Qt::NonModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::NonModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 0);
|
2012-04-04 10:11:40 +00:00
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.setModality(Qt::WindowModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::WindowModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.setModality(Qt::WindowModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::WindowModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2012-04-04 10:11:40 +00:00
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.setModality(Qt::ApplicationModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::ApplicationModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 2);
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.setModality(Qt::ApplicationModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::ApplicationModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 2);
|
2012-04-04 10:11:40 +00:00
|
|
|
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
window.setModality(Qt::NonModal);
|
|
|
|
QCOMPARE(window.modality(), Qt::NonModal);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 3);
|
2012-04-04 10:11:40 +00:00
|
|
|
}
|
|
|
|
|
2012-04-23 13:18:01 +00:00
|
|
|
void tst_QWindow::inputReentrancy()
|
|
|
|
{
|
|
|
|
InputTestWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2012-04-23 13:18:01 +00:00
|
|
|
window.spinLoopWhenPressed = true;
|
|
|
|
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
|
2012-04-23 13:18:01 +00:00
|
|
|
window.show();
|
2012-07-20 10:49:12 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-04-23 13:18:01 +00:00
|
|
|
|
|
|
|
// Queue three events.
|
|
|
|
QPointF local(12, 34);
|
2020-09-30 20:37:09 +00:00
|
|
|
QWindowSystemInterface::handleMouseEvent(&window, local, local, Qt::LeftButton,
|
2019-09-04 08:23:47 +00:00
|
|
|
Qt::LeftButton, QEvent::MouseButtonPress);
|
2012-04-23 13:18:01 +00:00
|
|
|
local += QPointF(2, 2);
|
2019-09-04 08:23:47 +00:00
|
|
|
QWindowSystemInterface::handleMouseEvent(&window, local, local,
|
2020-09-30 20:37:09 +00:00
|
|
|
Qt::LeftButton, {}, QEvent::MouseMove);
|
|
|
|
QWindowSystemInterface::handleMouseEvent(&window, local, local, {},
|
2019-09-04 08:23:47 +00:00
|
|
|
Qt::LeftButton, QEvent::MouseButtonRelease);
|
2012-04-23 13:18:01 +00:00
|
|
|
// Process them. However, the event handler for the press will also call
|
|
|
|
// processEvents() so the move and release will be delivered before returning
|
|
|
|
// from mousePressEvent(). The point is that no events should get lost.
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.mousePressButton, int(Qt::LeftButton));
|
|
|
|
QCOMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
|
|
|
QCOMPARE(window.mousePressedCount, 1);
|
2020-09-30 20:37:09 +00:00
|
|
|
// The mouse press may have generated a synthetic move in QGuiApplicationPrivate::processMouseEvent()
|
|
|
|
QVERIFY(window.mouseMovedCount == 1 || window.mouseMovedCount == 2);
|
2012-04-23 13:18:01 +00:00
|
|
|
QCOMPARE(window.mouseReleasedCount, 1);
|
|
|
|
|
|
|
|
// Now the same for touch.
|
|
|
|
QList<QWindowSystemInterface::TouchPoint> points;
|
|
|
|
QWindowSystemInterface::TouchPoint tp1;
|
|
|
|
tp1.id = 1;
|
2020-03-27 16:06:11 +00:00
|
|
|
tp1.state = QEventPoint::State::Pressed;
|
2012-04-23 13:18:01 +00:00
|
|
|
tp1.area = QRectF(10, 10, 4, 4);
|
|
|
|
points << tp1;
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Updated;
|
2012-04-23 13:18:01 +00:00
|
|
|
points[0].area = QRectF(20, 20, 8, 8);
|
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
2020-03-27 16:06:11 +00:00
|
|
|
points[0].state = QEventPoint::State::Released;
|
2012-04-23 13:18:01 +00:00
|
|
|
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QCOMPARE(window.touchPressedCount, 1);
|
|
|
|
QCOMPARE(window.touchMovedCount, 1);
|
|
|
|
QCOMPARE(window.touchReleasedCount, 1);
|
|
|
|
}
|
|
|
|
|
2017-05-30 21:04:21 +00:00
|
|
|
#if QT_CONFIG(tabletevent)
|
2023-02-22 11:03:16 +00:00
|
|
|
struct PointerEvent {
|
|
|
|
QEvent::Type type;
|
|
|
|
Qt::MouseButton button;
|
|
|
|
const QPointingDevice *device;
|
|
|
|
QPointF local;
|
|
|
|
QPointF global;
|
|
|
|
|
|
|
|
bool operator==(const PointerEvent &other) const
|
|
|
|
{
|
|
|
|
return type == other.type && button == other.button && device == other.device &&
|
|
|
|
global == other.global && local == other.local;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
QDebug operator<< (QDebug d, const PointerEvent& pe)
|
|
|
|
{
|
|
|
|
QDebugStateSaver saver(d);
|
|
|
|
d.space() << "PtrEvt {" << pe.type << pe.button << pe.local << pe.global << pe.device << '}';
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2012-05-23 12:07:39 +00:00
|
|
|
class TabletTestWindow : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
2019-05-10 07:10:27 +00:00
|
|
|
void tabletEvent(QTabletEvent *ev) override
|
|
|
|
{
|
2023-02-22 11:03:16 +00:00
|
|
|
ev->setAccepted(acceptTabletEvent);
|
2012-05-23 12:07:39 +00:00
|
|
|
}
|
2019-05-10 07:10:27 +00:00
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
bool event(QEvent *ev) override
|
|
|
|
{
|
|
|
|
if (ev->type() == QEvent::MouseButtonDblClick) {
|
|
|
|
++doubleClickCount;
|
|
|
|
} else if (ev->isSinglePointEvent()) {
|
|
|
|
auto *spe = static_cast<QSinglePointEvent *>(ev);
|
|
|
|
singlePointEvents << PointerEvent {
|
|
|
|
spe->type(), spe->button(), spe->pointingDevice(),
|
|
|
|
spe->position(), spe->globalPosition() };
|
|
|
|
}
|
|
|
|
return QWindow::event(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<PointerEvent> singlePointEvents;
|
|
|
|
int doubleClickCount = 0;
|
|
|
|
bool acceptTabletEvent = false;
|
2019-05-10 07:10:27 +00:00
|
|
|
|
|
|
|
bool eventFilter(QObject *obj, QEvent *ev) override
|
|
|
|
{
|
2012-05-23 12:07:39 +00:00
|
|
|
if (ev->type() == QEvent::TabletEnterProximity
|
|
|
|
|| ev->type() == QEvent::TabletLeaveProximity) {
|
|
|
|
QTabletEvent *te = static_cast<QTabletEvent *>(ev);
|
2023-02-22 11:03:16 +00:00
|
|
|
singlePointEvents << PointerEvent {
|
|
|
|
te->type(), te->button(), te->pointingDevice(),
|
|
|
|
te->position(), te->globalPosition() };
|
2012-05-23 12:07:39 +00:00
|
|
|
}
|
|
|
|
return QWindow::eventFilter(obj, ev);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
void tst_QWindow::tabletEvents_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("guiSynthMouse");
|
|
|
|
QTest::addColumn<bool>("acceptTabletEvent");
|
|
|
|
|
|
|
|
QTest::newRow("nosynth-accept") << false << true;
|
|
|
|
QTest::newRow("synth-accept") << true << true;
|
|
|
|
QTest::newRow("nosynth-ignore") << false << false;
|
|
|
|
QTest::newRow("synth-ignore") << true << false;;
|
|
|
|
}
|
|
|
|
|
2012-05-23 12:07:39 +00:00
|
|
|
void tst_QWindow::tabletEvents()
|
|
|
|
{
|
2017-05-30 21:04:21 +00:00
|
|
|
#if QT_CONFIG(tabletevent)
|
2023-02-22 11:03:16 +00:00
|
|
|
QFETCH(bool, guiSynthMouse);
|
|
|
|
QFETCH(bool, acceptTabletEvent);
|
|
|
|
qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents, guiSynthMouse);
|
|
|
|
|
2020-06-10 13:32:26 +00:00
|
|
|
// the fake USB tablet device is "plugged in"
|
|
|
|
QPointingDevice tabletDevice("macow", 0xbeef, QInputDevice::DeviceType::Unknown, QPointingDevice::PointerType::Generic,
|
|
|
|
QInputDevice::Capability::Position, 1, 0);
|
|
|
|
QWindowSystemInterface::registerInputDevice(&tabletDevice);
|
|
|
|
|
2012-05-23 12:07:39 +00:00
|
|
|
TabletTestWindow window;
|
2023-02-22 11:03:16 +00:00
|
|
|
window.acceptTabletEvent = acceptTabletEvent;
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(10, 10), m_testWindowSize));
|
2012-05-23 12:07:39 +00:00
|
|
|
qGuiApp->installEventFilter(&window);
|
2023-02-22 11:03:16 +00:00
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2012-05-23 12:07:39 +00:00
|
|
|
|
2015-06-18 15:01:01 +00:00
|
|
|
const QPoint local(10, 10);
|
|
|
|
const QPoint global = window.mapToGlobal(local);
|
|
|
|
const QPoint deviceLocal = QHighDpi::toNativeLocalPosition(local, &window);
|
|
|
|
const QPoint deviceGlobal = QHighDpi::toNativePixels(global, window.screen());
|
2020-06-10 13:32:26 +00:00
|
|
|
ulong timestamp = 1234;
|
|
|
|
|
2023-02-22 11:03:16 +00:00
|
|
|
// The stylus is just now seen for the first time, as it comes into proximity.
|
|
|
|
// Its QObject-parent will be the tablet device.
|
2020-06-10 13:32:26 +00:00
|
|
|
QPointingDevice tabletStylus("macow stylus eraser", 0xe6a5e6, QInputDevice::DeviceType::Stylus, QPointingDevice::PointerType::Eraser,
|
|
|
|
QInputDevice::Capability::Position | QInputDevice::Capability::Pressure, 1, 3, QString(),
|
|
|
|
QPointingDeviceUniqueId::fromNumericId(42), &tabletDevice);
|
|
|
|
QWindowSystemInterface::registerInputDevice(&tabletStylus);
|
2023-02-22 11:03:16 +00:00
|
|
|
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(&window, timestamp++, &tabletStylus, true, local, global);
|
2020-06-10 13:32:26 +00:00
|
|
|
QCoreApplication::processEvents();
|
2023-02-22 11:03:16 +00:00
|
|
|
qCDebug(lcTests) << "expect TabletEnterProximity:" << window.singlePointEvents;
|
|
|
|
QTRY_COMPARE_GE(window.singlePointEvents.size(), 1);
|
|
|
|
// the positions given to handleTabletEnterLeaveProximityEvent are not currently delivered (QTBUG-111400)
|
|
|
|
QCOMPARE(window.singlePointEvents.last(), PointerEvent({ QEvent::TabletEnterProximity, Qt::NoButton, &tabletStylus, {}, {} }));
|
|
|
|
window.singlePointEvents.clear();
|
2020-06-10 13:32:26 +00:00
|
|
|
|
|
|
|
// the eraser is pressed into contact with the tablet surface
|
|
|
|
QWindowSystemInterface::handleTabletEvent(&window, timestamp++, &tabletStylus, deviceLocal, deviceGlobal,
|
|
|
|
Qt::LeftButton, 0.5, 1, 2, 0.1, 0, 0, {});
|
2012-05-23 12:07:39 +00:00
|
|
|
QCoreApplication::processEvents();
|
2023-02-22 11:03:16 +00:00
|
|
|
qCDebug(lcTests) << "eraser pressed:" << window.singlePointEvents;
|
|
|
|
if (guiSynthMouse && !acceptTabletEvent) {
|
|
|
|
// expect synth-mouse events after the ignored tablet event,
|
|
|
|
// all from the same stylus device at the same position
|
|
|
|
// TODO why does this not work on QNX and some ARM Linux platforms?
|
|
|
|
if (!QTest::qWaitFor([&window]{ return window.singlePointEvents.size() == 3; }))
|
|
|
|
QSKIP("Failed to receive synth-mouse after tablet event on this platform (QTBUG-129998)");
|
|
|
|
QCOMPARE(window.singlePointEvents.size(), 3);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(0).type, QEvent::TabletPress);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(1).type, QEvent::MouseMove);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(2).type, QEvent::MouseButtonPress);
|
|
|
|
for (int i = 0; i < window.singlePointEvents.size(); ++i) {
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).device, &tabletStylus);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).local.toPoint(), local);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).global.toPoint(), global);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QTRY_COMPARE(window.singlePointEvents.size(), 1);
|
|
|
|
QCOMPARE(window.singlePointEvents.first().type, QEvent::TabletPress);
|
|
|
|
QCOMPARE(window.singlePointEvents.first().local.toPoint(), local);
|
|
|
|
QCOMPARE(window.singlePointEvents.first().global.toPoint(), global);
|
|
|
|
}
|
2012-05-23 12:07:39 +00:00
|
|
|
|
2020-06-10 13:32:26 +00:00
|
|
|
// now it's lifted
|
|
|
|
QWindowSystemInterface::handleTabletEvent(&window, timestamp++, &tabletStylus, deviceLocal, deviceGlobal,
|
|
|
|
Qt::NoButton, 0, 3, 4, 0.11, 2, 1, {});
|
2012-05-23 12:07:39 +00:00
|
|
|
QCoreApplication::processEvents();
|
2023-02-22 11:03:16 +00:00
|
|
|
qCDebug(lcTests) << "eraser lifted:" << window.singlePointEvents;
|
|
|
|
if (guiSynthMouse && !acceptTabletEvent) {
|
|
|
|
// expect synth-mouse events after the ignored tablet event,
|
|
|
|
// all from the same stylus device at the same position
|
|
|
|
#ifndef Q_OS_QNX
|
|
|
|
QTRY_COMPARE(window.singlePointEvents.size(), 5);
|
|
|
|
for (int i = 2; i < window.singlePointEvents.size(); ++i) {
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).device, &tabletStylus);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).local.toPoint(), local);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(i).global.toPoint(), global);
|
|
|
|
}
|
|
|
|
QCOMPARE(window.singlePointEvents.at(window.singlePointEvents.size() - 2).type, QEvent::TabletRelease);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(window.singlePointEvents.size() - 1).type, QEvent::MouseButtonRelease);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
QTRY_COMPARE(window.singlePointEvents.size(), 2);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(1).type, QEvent::TabletRelease);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(1).local.toPoint(), local);
|
|
|
|
QCOMPARE(window.singlePointEvents.at(1).global.toPoint(), global);
|
|
|
|
}
|
|
|
|
QCOMPARE(window.doubleClickCount, 0);
|
2012-05-23 12:07:39 +00:00
|
|
|
|
2020-06-10 13:32:26 +00:00
|
|
|
// and is taken away (goes out of proxmity)
|
2023-02-22 11:03:16 +00:00
|
|
|
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(&window, timestamp, &tabletStylus, false, local, global);
|
2012-05-23 12:07:39 +00:00
|
|
|
QCoreApplication::processEvents();
|
2023-02-22 11:03:16 +00:00
|
|
|
// the positions given to handleTabletEnterLeaveProximityEvent are not currently delivered (QTBUG-111400)
|
|
|
|
QTRY_COMPARE(window.singlePointEvents.last(), PointerEvent({ QEvent::TabletLeaveProximity, Qt::NoButton, &tabletStylus, {}, {} }));
|
|
|
|
QCOMPARE(window.doubleClickCount, 0);
|
2012-05-23 12:07:39 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-09-21 10:12:01 +00:00
|
|
|
void tst_QWindow::windowModality_QTBUG27039()
|
|
|
|
{
|
|
|
|
QWindow parent;
|
2019-05-10 07:10:27 +00:00
|
|
|
parent.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
parent.setGeometry(QRect(m_availableTopLeft + QPoint(10, 10), m_testWindowSize));
|
2012-09-21 10:12:01 +00:00
|
|
|
parent.show();
|
|
|
|
|
|
|
|
InputTestWindow modalA;
|
|
|
|
modalA.setTransientParent(&parent);
|
2014-07-31 11:48:15 +00:00
|
|
|
modalA.setGeometry(QRect(m_availableTopLeft + QPoint(20, 10), m_testWindowSize));
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
modalA.setModality(Qt::ApplicationModal);
|
2012-09-21 10:12:01 +00:00
|
|
|
modalA.show();
|
|
|
|
|
|
|
|
InputTestWindow modalB;
|
|
|
|
modalB.setTransientParent(&parent);
|
2014-07-31 11:48:15 +00:00
|
|
|
modalA.setGeometry(QRect(m_availableTopLeft + QPoint(30, 10), m_testWindowSize));
|
Rename all QWindow properties that have "window" in them
windowTitle, windowModality, windowIcon and so on are named that way
to be similar to the ones in QWidget. However QQuickWindow inherits
all of the declared properties, and we would like to have shorter
property names in QML. If you are working with a Window then it's
obvious the title property is the window title. Unfortunately,
there must be patches in many other modules which depend on this one.
In order to avoid the need to merge them all at the same time,
there is also patch https://codereview.qt-project.org/#change,39001
which temporarily adds backwards-compatible accessors, which can be
removed after the other modules are able to build without them.
We should not rename windowState to state, because in QML, state
usually drives the state machine for animation transitions etc.
(although QWindow is not an Item, a user might get confused about it).
Related patches are
https://codereview.qt-project.org/#change,39001
https://codereview.qt-project.org/#change,37764
https://codereview.qt-project.org/#change,37765
https://codereview.qt-project.org/#change,37766
https://codereview.qt-project.org/#change,37762
Change-Id: Ie4424ec15fbdef6b29b137f90a2ae33f173edd21
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
2012-10-22 10:47:34 +00:00
|
|
|
modalB.setModality(Qt::ApplicationModal);
|
2012-09-21 10:12:01 +00:00
|
|
|
modalB.show();
|
|
|
|
|
2019-09-04 08:23:47 +00:00
|
|
|
QPoint local(5, 5);
|
|
|
|
QTest::mouseClick(&modalA, Qt::LeftButton, {}, local);
|
|
|
|
QTest::mouseClick(&modalB, Qt::LeftButton, {}, local);
|
2012-09-21 10:12:01 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
// modal A should be blocked since it was shown first, but modal B should not be blocked
|
|
|
|
QCOMPARE(modalB.mousePressedCount, 1);
|
|
|
|
QCOMPARE(modalA.mousePressedCount, 0);
|
|
|
|
|
|
|
|
modalB.hide();
|
2019-09-04 08:23:47 +00:00
|
|
|
QTest::mouseClick(&modalA, Qt::LeftButton, {}, local);
|
2012-09-21 10:12:01 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
// modal B has been hidden, modal A should be unblocked again
|
|
|
|
QCOMPARE(modalA.mousePressedCount, 1);
|
|
|
|
}
|
|
|
|
|
2013-02-08 09:49:52 +00:00
|
|
|
void tst_QWindow::visibility()
|
|
|
|
{
|
|
|
|
qRegisterMetaType<Qt::WindowModality>("QWindow::Visibility");
|
|
|
|
|
2017-07-18 12:28:58 +00:00
|
|
|
Window window;
|
2013-02-08 09:49:52 +00:00
|
|
|
QSignalSpy spy(&window, SIGNAL(visibilityChanged(QWindow::Visibility)));
|
|
|
|
|
|
|
|
window.setVisibility(QWindow::AutomaticVisibility);
|
|
|
|
QVERIFY(window.isVisible());
|
|
|
|
QVERIFY(window.visibility() != QWindow::Hidden);
|
|
|
|
QVERIFY(window.visibility() != QWindow::AutomaticVisibility);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2013-02-08 09:49:52 +00:00
|
|
|
spy.clear();
|
|
|
|
|
|
|
|
window.setVisibility(QWindow::Hidden);
|
|
|
|
QVERIFY(!window.isVisible());
|
|
|
|
QCOMPARE(window.visibility(), QWindow::Hidden);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2013-02-08 09:49:52 +00:00
|
|
|
spy.clear();
|
|
|
|
|
|
|
|
window.setVisibility(QWindow::FullScreen);
|
|
|
|
QVERIFY(window.isVisible());
|
|
|
|
QCOMPARE(window.windowState(), Qt::WindowFullScreen);
|
|
|
|
QCOMPARE(window.visibility(), QWindow::FullScreen);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2017-07-18 12:28:58 +00:00
|
|
|
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowFullScreen);
|
2013-02-08 09:49:52 +00:00
|
|
|
spy.clear();
|
|
|
|
|
|
|
|
window.setWindowState(Qt::WindowNoState);
|
|
|
|
QCOMPARE(window.visibility(), QWindow::Windowed);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2017-07-18 12:28:58 +00:00
|
|
|
QTRY_COMPARE(window.lastReceivedWindowState, Qt::WindowNoState);
|
2013-02-08 09:49:52 +00:00
|
|
|
spy.clear();
|
|
|
|
|
|
|
|
window.setVisible(false);
|
|
|
|
QCOMPARE(window.visibility(), QWindow::Hidden);
|
Port from container::count() and length() to size() - V5
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that the on() matcher has been replaced by one that doesn't
ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Added two NOLINTNEXTLINEs in tst_qbitarray and tst_qcontiguouscache,
to avoid porting calls that explicitly test count().
Change-Id: Icfb8808c2ff4a30187e9935a51cad26987451c22
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2022-09-30 12:09:04 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
2013-02-08 09:49:52 +00:00
|
|
|
spy.clear();
|
|
|
|
}
|
|
|
|
|
2013-02-25 07:54:30 +00:00
|
|
|
void tst_QWindow::mask()
|
|
|
|
{
|
|
|
|
QRegion mask = QRect(10, 10, 800 - 20, 600 - 20);
|
|
|
|
|
2017-06-14 09:52:09 +00:00
|
|
|
{
|
|
|
|
QWindow window;
|
|
|
|
window.resize(800, 600);
|
|
|
|
QCOMPARE(window.mask(), QRegion());
|
2013-02-25 07:54:30 +00:00
|
|
|
|
2017-06-14 09:52:09 +00:00
|
|
|
window.create();
|
|
|
|
window.setMask(mask);
|
|
|
|
QCOMPARE(window.mask(), mask);
|
|
|
|
}
|
2013-02-25 07:54:30 +00:00
|
|
|
|
2017-06-14 09:52:09 +00:00
|
|
|
{
|
|
|
|
QWindow window;
|
|
|
|
window.resize(800, 600);
|
|
|
|
QCOMPARE(window.mask(), QRegion());
|
|
|
|
|
|
|
|
window.setMask(mask);
|
|
|
|
QCOMPARE(window.mask(), mask);
|
|
|
|
window.create();
|
|
|
|
QCOMPARE(window.mask(), mask);
|
|
|
|
}
|
2013-02-25 07:54:30 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-09 11:45:14 +00:00
|
|
|
void tst_QWindow::initialSize()
|
|
|
|
{
|
|
|
|
QSize defaultSize(0,0);
|
|
|
|
{
|
|
|
|
Window w;
|
2019-05-10 07:10:27 +00:00
|
|
|
w.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2015-03-10 13:41:30 +00:00
|
|
|
w.showNormal();
|
2014-01-09 11:45:14 +00:00
|
|
|
QTRY_VERIFY(w.width() > 0);
|
|
|
|
QTRY_VERIFY(w.height() > 0);
|
|
|
|
defaultSize = QSize(w.width(), w.height());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
Window w;
|
2019-05-10 07:10:27 +00:00
|
|
|
w.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
w.setWidth(m_testWindowSize.width());
|
2015-03-10 13:41:30 +00:00
|
|
|
w.showNormal();
|
2023-10-19 09:35:21 +00:00
|
|
|
|
|
|
|
if (isPlatformEglFS())
|
|
|
|
QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
|
|
|
|
|
2014-07-31 11:48:15 +00:00
|
|
|
QTRY_COMPARE(w.width(), m_testWindowSize.width());
|
2014-01-09 11:45:14 +00:00
|
|
|
QTRY_VERIFY(w.height() > 0);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
Window w;
|
2019-05-10 07:10:27 +00:00
|
|
|
w.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
const QSize testSize(m_testWindowSize.width(), 42);
|
|
|
|
w.resize(testSize);
|
2015-03-10 13:41:30 +00:00
|
|
|
w.showNormal();
|
2014-07-31 11:48:15 +00:00
|
|
|
|
|
|
|
const QSize expectedSize = testSize;
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
|
2014-07-31 11:48:15 +00:00
|
|
|
QTRY_COMPARE(w.size(), expectedSize);
|
2014-01-09 11:45:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-20 11:28:42 +00:00
|
|
|
static bool isPlatformOffscreenOrMinimal()
|
|
|
|
{
|
|
|
|
return ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|
|
|
|
|| (QGuiApplication::platformName() == QLatin1String("minimal")));
|
|
|
|
}
|
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
void tst_QWindow::modalDialog()
|
|
|
|
{
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2017-07-14 12:30:01 +00:00
|
|
|
if (QGuiApplication::platformName() == QLatin1String("cocoa"))
|
|
|
|
QSKIP("Test fails due to QTBUG-61965, and is slow due to QTBUG-61964");
|
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QWindow normalWindow;
|
2019-05-10 07:10:27 +00:00
|
|
|
normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normalWindow.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
normalWindow.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
|
|
|
|
|
|
|
|
QWindow dialog;
|
2014-07-31 11:48:15 +00:00
|
|
|
dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
|
|
|
|
dialog.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
dialog.setModality(Qt::ApplicationModal);
|
|
|
|
dialog.setFlags(Qt::Dialog);
|
|
|
|
dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
|
|
|
|
|
|
|
normalWindow.requestActivate();
|
|
|
|
|
|
|
|
QGuiApplication::sync();
|
|
|
|
QGuiApplication::processEvents();
|
2017-03-29 11:19:57 +00:00
|
|
|
|
2017-01-20 11:28:42 +00:00
|
|
|
if (isPlatformOffscreenOrMinimal()) {
|
2021-07-31 16:25:42 +00:00
|
|
|
qWarning("Focus stays in normalWindow on offscreen/minimal platforms");
|
2017-03-29 11:19:57 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &normalWindow);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &dialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::modalDialogClosingOneOfTwoModal()
|
|
|
|
{
|
2018-06-05 10:25:04 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QWindow normalWindow;
|
2019-05-10 07:10:27 +00:00
|
|
|
normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normalWindow.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
normalWindow.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
|
|
|
|
|
|
|
|
QWindow first_dialog;
|
2014-07-31 11:48:15 +00:00
|
|
|
first_dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
|
|
|
|
first_dialog.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
first_dialog.setModality(Qt::ApplicationModal);
|
|
|
|
first_dialog.setFlags(Qt::Dialog);
|
|
|
|
first_dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&first_dialog));
|
|
|
|
|
|
|
|
{
|
|
|
|
QWindow second_dialog;
|
2014-07-31 11:48:15 +00:00
|
|
|
second_dialog.setFramePosition(m_availableTopLeft + QPoint(300, 300));
|
|
|
|
second_dialog.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
second_dialog.setModality(Qt::ApplicationModal);
|
|
|
|
second_dialog.setFlags(Qt::Dialog);
|
|
|
|
second_dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&second_dialog));
|
|
|
|
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &second_dialog);
|
|
|
|
|
|
|
|
second_dialog.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
QGuiApplication::sync();
|
|
|
|
QGuiApplication::processEvents();
|
2017-03-29 11:19:57 +00:00
|
|
|
|
2017-01-20 11:28:42 +00:00
|
|
|
if (isPlatformOffscreenOrMinimal()) {
|
2021-07-31 16:25:42 +00:00
|
|
|
qWarning("Focus is lost when closing modal dialog on offscreen/minimal platforms");
|
2017-03-29 11:19:57 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), nullptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &first_dialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::modalWithChildWindow()
|
|
|
|
{
|
2025-06-19 12:18:03 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("A nested window or a subsurface in wayland terms can't get focus.");
|
|
|
|
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QWindow normalWindow;
|
2019-05-10 07:10:27 +00:00
|
|
|
normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normalWindow.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
normalWindow.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
|
|
|
|
|
|
|
|
QWindow tlw_dialog;
|
2014-07-31 11:48:15 +00:00
|
|
|
tlw_dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
|
|
|
|
tlw_dialog.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
tlw_dialog.setModality(Qt::ApplicationModal);
|
|
|
|
tlw_dialog.setFlags(Qt::Dialog);
|
|
|
|
tlw_dialog.create();
|
|
|
|
|
|
|
|
QWindow sub_window(&tlw_dialog);
|
|
|
|
sub_window.resize(200,300);
|
|
|
|
sub_window.show();
|
|
|
|
|
|
|
|
tlw_dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&tlw_dialog));
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&sub_window));
|
|
|
|
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &tlw_dialog);
|
|
|
|
|
|
|
|
sub_window.requestActivate();
|
|
|
|
QGuiApplication::sync();
|
|
|
|
QGuiApplication::processEvents();
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &sub_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::modalWindowModallity()
|
|
|
|
{
|
2025-06-23 09:32:48 +00:00
|
|
|
if (isPlatformWayland() && qgetenv("XDG_CURRENT_DESKTOP").toLower().contains("ubuntu:gnome"))
|
|
|
|
QSKIP("Wayland: This will trigger a 'X is ready' system notification in GNOME.");
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2014-08-21 11:23:25 +00:00
|
|
|
|
2014-04-03 11:11:59 +00:00
|
|
|
QWindow normal_window;
|
2019-05-10 07:10:27 +00:00
|
|
|
normal_window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
normal_window.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normal_window.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
normal_window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normal_window));
|
|
|
|
|
|
|
|
QWindow parent_to_modal;
|
2014-07-31 11:48:15 +00:00
|
|
|
parent_to_modal.setFramePosition(normal_window.geometry().topRight() + QPoint(100, 0));
|
|
|
|
parent_to_modal.resize(m_testWindowSize);
|
2014-04-03 11:11:59 +00:00
|
|
|
parent_to_modal.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&parent_to_modal));
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &parent_to_modal);
|
|
|
|
|
|
|
|
QWindow modal_dialog;
|
2014-07-31 11:48:15 +00:00
|
|
|
modal_dialog.resize(m_testWindowSize);
|
|
|
|
modal_dialog.setFramePosition(normal_window.geometry().bottomLeft() + QPoint(0, 100));
|
2014-04-03 11:11:59 +00:00
|
|
|
modal_dialog.setModality(Qt::WindowModal);
|
|
|
|
modal_dialog.setFlags(Qt::Dialog);
|
|
|
|
modal_dialog.setTransientParent(&parent_to_modal);
|
|
|
|
modal_dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&modal_dialog));
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &modal_dialog);
|
|
|
|
|
|
|
|
normal_window.requestActivate();
|
|
|
|
QTRY_COMPARE(QGuiApplication::focusWindow(), &normal_window);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-05-19 17:58:35 +00:00
|
|
|
void tst_QWindow::modalWindowPosition()
|
|
|
|
{
|
2022-06-28 07:44:47 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("Window position not queryable on Wayland");
|
|
|
|
|
2014-05-19 17:58:35 +00:00
|
|
|
QWindow window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2014-07-31 11:48:15 +00:00
|
|
|
window.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
|
2014-05-19 17:58:35 +00:00
|
|
|
// Allow for any potential resizing due to constraints
|
|
|
|
QRect origGeo = window.geometry();
|
|
|
|
window.setModality(Qt::WindowModal);
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
2023-10-19 09:35:21 +00:00
|
|
|
|
|
|
|
if (isPlatformEglFS())
|
|
|
|
QEXPECT_FAIL("", "eglfs windows are fullscreen by default.", Continue);
|
|
|
|
|
2014-05-19 17:58:35 +00:00
|
|
|
QCOMPARE(window.geometry(), origGeo);
|
|
|
|
}
|
|
|
|
|
2022-10-10 12:25:45 +00:00
|
|
|
void tst_QWindow::modalCloseWhileBlocked()
|
|
|
|
{
|
|
|
|
QWindow first;
|
|
|
|
first.setModality(Qt::ApplicationModal);
|
|
|
|
first.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&first));
|
|
|
|
|
|
|
|
QWindow second;
|
|
|
|
second.setModality(Qt::ApplicationModal);
|
|
|
|
second.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&first));
|
|
|
|
|
|
|
|
first.close();
|
|
|
|
QTRY_VERIFY(!first.isVisible());
|
|
|
|
}
|
|
|
|
|
2016-01-19 21:32:52 +00:00
|
|
|
#ifndef QT_NO_CURSOR
|
|
|
|
void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
|
|
|
|
{
|
2018-03-02 14:24:32 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
2018-03-02 14:23:40 +00:00
|
|
|
|
2017-01-20 11:28:42 +00:00
|
|
|
if (isPlatformOffscreenOrMinimal())
|
|
|
|
QSKIP("Can't test window focusing on offscreen/minimal");
|
|
|
|
|
2025-06-23 10:45:22 +00:00
|
|
|
if (isPlatformEglFS() || isPlatformWayland())
|
2023-10-19 09:35:21 +00:00
|
|
|
QSKIP("QCursor::setPos() is not supported on this platform");
|
|
|
|
|
2016-01-19 21:32:52 +00:00
|
|
|
const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center();
|
|
|
|
|
|
|
|
const int childOffset = 16;
|
|
|
|
const QPoint rootPos = center - QPoint(m_testWindowSize.width(),
|
|
|
|
m_testWindowSize.height())/2;
|
|
|
|
const QPoint modalPos = rootPos + QPoint(childOffset * 5,
|
|
|
|
childOffset * 5);
|
|
|
|
const QPoint cursorPos = rootPos - QPoint(80, 80);
|
|
|
|
|
|
|
|
// Test whether tlw can receive the enter event
|
|
|
|
{
|
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
InputTestWindow root;
|
|
|
|
root.setTitle(__FUNCTION__);
|
|
|
|
root.setGeometry(QRect(rootPos, m_testWindowSize));
|
|
|
|
root.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
|
|
|
root.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&root));
|
|
|
|
|
|
|
|
// Move the mouse over the root window, but not over the modal window.
|
|
|
|
QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
|
|
|
childOffset * 5 / 2));
|
|
|
|
|
|
|
|
// Wait for the enter event. It must be delivered here, otherwise second
|
|
|
|
// compare can PASS because of this event even after "resetCounters()".
|
|
|
|
QTRY_COMPARE(root.enterEventCount, 1);
|
|
|
|
QTRY_COMPARE(root.leaveEventCount, 0);
|
|
|
|
|
|
|
|
QWindow modal;
|
|
|
|
modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
|
|
|
modal.setTransientParent(&root);
|
|
|
|
modal.resize(m_testWindowSize/2);
|
|
|
|
modal.setFramePosition(modalPos);
|
|
|
|
modal.setModality(Qt::ApplicationModal);
|
|
|
|
modal.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&modal));
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(root.leaveEventCount, 1);
|
|
|
|
|
|
|
|
root.resetCounters();
|
|
|
|
modal.close();
|
|
|
|
|
|
|
|
// Check for the enter event
|
|
|
|
QTRY_COMPARE(root.enterEventCount, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test whether child window can receive the enter event
|
|
|
|
{
|
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
QWindow root;
|
|
|
|
root.setTitle(__FUNCTION__);
|
|
|
|
root.setGeometry(QRect(rootPos, m_testWindowSize));
|
|
|
|
|
|
|
|
QWindow childLvl1;
|
|
|
|
childLvl1.setParent(&root);
|
|
|
|
childLvl1.setGeometry(childOffset,
|
|
|
|
childOffset,
|
|
|
|
m_testWindowSize.width() - childOffset,
|
|
|
|
m_testWindowSize.height() - childOffset);
|
|
|
|
|
|
|
|
InputTestWindow childLvl2;
|
|
|
|
childLvl2.setParent(&childLvl1);
|
|
|
|
childLvl2.setGeometry(childOffset,
|
|
|
|
childOffset,
|
|
|
|
childLvl1.width() - childOffset,
|
|
|
|
childLvl1.height() - childOffset);
|
|
|
|
|
|
|
|
root.show();
|
|
|
|
childLvl1.show();
|
|
|
|
childLvl2.show();
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
|
|
|
root.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&root));
|
|
|
|
QVERIFY(childLvl1.isVisible());
|
|
|
|
QVERIFY(childLvl2.isVisible());
|
|
|
|
|
|
|
|
// Move the mouse over the child window, but not over the modal window.
|
|
|
|
// Be sure that the value is almost left-top of second child window for
|
|
|
|
// checking proper position mapping.
|
|
|
|
QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
|
|
|
childOffset * 5 / 2));
|
|
|
|
|
|
|
|
// Wait for the enter event. It must be delivered here, otherwise second
|
|
|
|
// compare can PASS because of this event even after "resetCounters()".
|
|
|
|
QTRY_COMPARE(childLvl2.enterEventCount, 1);
|
|
|
|
QTRY_COMPARE(childLvl2.leaveEventCount, 0);
|
|
|
|
|
|
|
|
QWindow modal;
|
|
|
|
modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
|
|
|
modal.setTransientParent(&root);
|
|
|
|
modal.resize(m_testWindowSize/2);
|
|
|
|
modal.setFramePosition(modalPos);
|
|
|
|
modal.setModality(Qt::ApplicationModal);
|
|
|
|
modal.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&modal));
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(childLvl2.leaveEventCount, 1);
|
|
|
|
|
|
|
|
childLvl2.resetCounters();
|
|
|
|
modal.close();
|
|
|
|
|
|
|
|
// Check for the enter event
|
|
|
|
QTRY_COMPARE(childLvl2.enterEventCount, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test whether tlw can receive the enter event if mouse is over the invisible child windnow
|
|
|
|
{
|
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
|
|
|
|
InputTestWindow root;
|
|
|
|
root.setTitle(__FUNCTION__);
|
|
|
|
root.setGeometry(QRect(rootPos, m_testWindowSize));
|
|
|
|
|
|
|
|
QWindow child;
|
|
|
|
child.setParent(&root);
|
|
|
|
child.setGeometry(QRect(QPoint(), m_testWindowSize));
|
|
|
|
|
|
|
|
root.show();
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&root));
|
|
|
|
QVERIFY(!child.isVisible());
|
|
|
|
|
|
|
|
// Move the mouse over the child window, but not over the modal window.
|
|
|
|
QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
|
|
|
childOffset * 5 / 2));
|
|
|
|
|
|
|
|
// Wait for the enter event. It must be delivered here, otherwise second
|
|
|
|
// compare can PASS because of this event even after "resetCounters()".
|
|
|
|
QTRY_COMPARE(root.enterEventCount, 1);
|
|
|
|
QTRY_COMPARE(root.leaveEventCount, 0);
|
|
|
|
|
|
|
|
QWindow modal;
|
|
|
|
modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
|
|
|
modal.setTransientParent(&root);
|
|
|
|
modal.resize(m_testWindowSize/2);
|
|
|
|
modal.setFramePosition(modalPos);
|
|
|
|
modal.setModality(Qt::ApplicationModal);
|
|
|
|
modal.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&modal));
|
|
|
|
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_COMPARE(root.leaveEventCount, 1);
|
|
|
|
|
|
|
|
root.resetCounters();
|
|
|
|
modal.close();
|
|
|
|
|
|
|
|
// Check for the enter event
|
|
|
|
QTRY_COMPARE(root.enterEventCount, 1);
|
|
|
|
}
|
|
|
|
}
|
2017-12-20 13:07:17 +00:00
|
|
|
|
|
|
|
// Verify that no spurious mouse move events are received. On Windows, there is
|
|
|
|
// no enter event, the OS sends mouse move events instead. Test that the QPA
|
|
|
|
// plugin properly suppresses those since they can interfere with tests.
|
|
|
|
// Simulate a main window setup with a modal dialog on top, keep the cursor
|
|
|
|
// in the center and check that no mouse events are recorded.
|
|
|
|
void tst_QWindow::spuriousMouseMove()
|
|
|
|
{
|
|
|
|
const QString &platformName = QGuiApplication::platformName();
|
|
|
|
if (platformName == QLatin1String("offscreen") || platformName == QLatin1String("cocoa"))
|
|
|
|
QSKIP("No enter events sent");
|
2021-02-26 08:00:46 +00:00
|
|
|
if (platformName == QLatin1String("wayland"))
|
|
|
|
QSKIP("Setting mouse cursor position is not possible on Wayland");
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QSKIP("QCursor::setPos() is not supported on this platform");
|
2017-12-20 13:07:17 +00:00
|
|
|
const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
|
|
|
|
const QPoint center = screenGeometry.center();
|
|
|
|
QCursor::setPos(center);
|
|
|
|
QRect windowGeometry(QPoint(), 2 * m_testWindowSize);
|
|
|
|
windowGeometry.moveCenter(center);
|
|
|
|
QTRY_COMPARE(QCursor::pos(), center);
|
|
|
|
InputTestWindow topLevel;
|
|
|
|
topLevel.setTitle(QTest::currentTestFunction());
|
|
|
|
topLevel.setGeometry(windowGeometry);
|
|
|
|
topLevel.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
|
|
|
|
QTRY_VERIFY(topLevel.enterEventCount > 0);
|
|
|
|
InputTestWindow dialog(Qt::yellow);
|
|
|
|
dialog.setTransientParent(&topLevel);
|
|
|
|
dialog.setTitle("Dialog " + topLevel.title());
|
|
|
|
dialog.setModality(Qt::ApplicationModal);
|
|
|
|
windowGeometry.setSize(m_testWindowSize);
|
|
|
|
windowGeometry.moveCenter(center);
|
|
|
|
dialog.setGeometry(windowGeometry);
|
|
|
|
dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
|
|
|
QTRY_VERIFY(dialog.enterEventCount > 0);
|
|
|
|
dialog.setVisible(false);
|
|
|
|
QCOMPARE(dialog.mousePressedCount, 0);
|
|
|
|
QCOMPARE(dialog.mouseReleasedCount, 0);
|
|
|
|
QCOMPARE(dialog.mouseMovedCount, 0);
|
|
|
|
QCOMPARE(dialog.mouseDoubleClickedCount, 0);
|
|
|
|
topLevel.setVisible(false);
|
|
|
|
QCOMPARE(topLevel.mousePressedCount, 0);
|
|
|
|
QCOMPARE(topLevel.mouseReleasedCount, 0);
|
|
|
|
QCOMPARE(topLevel.mouseMovedCount, 0);
|
|
|
|
QCOMPARE(topLevel.mouseDoubleClickedCount, 0);
|
|
|
|
}
|
|
|
|
#endif // !QT_NO_CURSOR
|
2016-01-19 21:32:52 +00:00
|
|
|
|
2014-08-28 10:59:14 +00:00
|
|
|
static bool isNativeWindowVisible(const QWindow *window)
|
|
|
|
{
|
2020-06-05 07:24:37 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
2014-08-28 10:59:14 +00:00
|
|
|
return IsWindowVisible(reinterpret_cast<HWND>(window->winId()));
|
|
|
|
#else
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
return window->isVisible();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QWindow::windowsTransientChildren()
|
|
|
|
{
|
|
|
|
if (QGuiApplication::platformName().compare(QStringLiteral("windows"), Qt::CaseInsensitive))
|
|
|
|
QSKIP("Windows only test");
|
|
|
|
|
|
|
|
ColoredWindow mainWindow(Qt::yellow);
|
|
|
|
mainWindow.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
|
|
|
|
mainWindow.setTitle(QStringLiteral("Main"));
|
|
|
|
ColoredWindow child(Qt::blue, &mainWindow);
|
|
|
|
child.setGeometry(QRect(QPoint(0, 0), m_testWindowSize / 2));
|
|
|
|
|
|
|
|
ColoredWindow dialog(Qt::red);
|
|
|
|
dialog.setGeometry(QRect(m_availableTopLeft + QPoint(200, 200), m_testWindowSize));
|
|
|
|
dialog.setTitle(QStringLiteral("Dialog"));
|
|
|
|
dialog.setTransientParent(&mainWindow);
|
|
|
|
|
|
|
|
mainWindow.show();
|
|
|
|
child.show();
|
|
|
|
dialog.show();
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
|
|
|
mainWindow.setWindowState(Qt::WindowMinimized);
|
|
|
|
QVERIFY(!isNativeWindowVisible(&dialog));
|
|
|
|
dialog.hide();
|
|
|
|
mainWindow.setWindowState(Qt::WindowNoState);
|
|
|
|
// QTBUG-40696, transient children hidden by Qt should not be re-shown by Windows.
|
|
|
|
QVERIFY(!isNativeWindowVisible(&dialog));
|
|
|
|
QVERIFY(isNativeWindowVisible(&child)); // Real children should be visible.
|
|
|
|
}
|
|
|
|
|
2015-02-12 14:28:12 +00:00
|
|
|
void tst_QWindow::requestUpdate()
|
|
|
|
{
|
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
|
|
|
|
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2015-02-12 14:28:12 +00:00
|
|
|
window.setGeometry(geometry);
|
|
|
|
window.show();
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_VERIFY(window.isExposed());
|
|
|
|
|
2015-07-30 13:15:12 +00:00
|
|
|
QCOMPARE(window.received(QEvent::UpdateRequest), 0);
|
2015-02-12 14:28:12 +00:00
|
|
|
|
|
|
|
window.requestUpdate();
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::UpdateRequest), 1);
|
2015-02-12 14:28:12 +00:00
|
|
|
|
|
|
|
window.requestUpdate();
|
2015-07-30 13:15:12 +00:00
|
|
|
QTRY_COMPARE(window.received(QEvent::UpdateRequest), 2);
|
2015-02-12 14:28:12 +00:00
|
|
|
}
|
|
|
|
|
2016-11-02 20:20:26 +00:00
|
|
|
void tst_QWindow::flags()
|
|
|
|
{
|
|
|
|
Window window;
|
2025-07-17 12:01:58 +00:00
|
|
|
QSignalSpy spy(&window, SIGNAL(flagsChanged(Qt::WindowFlags)));
|
|
|
|
|
2016-11-02 20:20:26 +00:00
|
|
|
const auto baseFlags = window.flags();
|
|
|
|
window.setFlags(window.flags() | Qt::FramelessWindowHint);
|
|
|
|
QCOMPARE(window.flags(), baseFlags | Qt::FramelessWindowHint);
|
2025-07-17 12:01:58 +00:00
|
|
|
QCOMPARE(spy.size(), 1);
|
|
|
|
window.setFlags(window.flags());
|
|
|
|
QCOMPARE(spy.size(), 1);
|
|
|
|
|
2016-11-02 20:20:26 +00:00
|
|
|
window.setFlag(Qt::WindowStaysOnTopHint, true);
|
|
|
|
QCOMPARE(window.flags(), baseFlags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
2025-07-17 12:01:58 +00:00
|
|
|
QCOMPARE(spy.size(), 2);
|
|
|
|
window.setFlags(window.flags());
|
|
|
|
QCOMPARE(spy.size(), 2);
|
|
|
|
|
2016-11-02 20:20:26 +00:00
|
|
|
window.setFlag(Qt::FramelessWindowHint, false);
|
|
|
|
QCOMPARE(window.flags(), baseFlags | Qt::WindowStaysOnTopHint);
|
2025-07-17 12:01:58 +00:00
|
|
|
QCOMPARE(spy.size(), 3);
|
|
|
|
window.setFlags(window.flags());
|
|
|
|
QCOMPARE(spy.size(), 3);
|
2016-11-02 20:20:26 +00:00
|
|
|
}
|
|
|
|
|
2017-08-15 08:29:16 +00:00
|
|
|
class EventWindow : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
2019-05-10 07:10:27 +00:00
|
|
|
bool gotBlocked = false;
|
|
|
|
|
2017-08-15 08:29:16 +00:00
|
|
|
protected:
|
2019-05-10 07:10:27 +00:00
|
|
|
bool event(QEvent *e) override
|
2017-08-15 08:29:16 +00:00
|
|
|
{
|
|
|
|
if (e->type() == QEvent::WindowBlocked)
|
|
|
|
gotBlocked = true;
|
|
|
|
return QWindow::event(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QWindow::testBlockingWindowShownAfterModalDialog()
|
|
|
|
{
|
|
|
|
EventWindow normalWindow;
|
2019-05-10 07:10:27 +00:00
|
|
|
normalWindow.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2017-08-15 08:29:16 +00:00
|
|
|
normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normalWindow.resize(m_testWindowSize);
|
|
|
|
normalWindow.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normalWindow));
|
|
|
|
QVERIFY(!normalWindow.gotBlocked);
|
|
|
|
|
|
|
|
QWindow dialog;
|
|
|
|
dialog.setFramePosition(m_availableTopLeft + QPoint(200, 200));
|
|
|
|
dialog.resize(m_testWindowSize);
|
|
|
|
dialog.setModality(Qt::ApplicationModal);
|
|
|
|
dialog.setFlags(Qt::Dialog);
|
|
|
|
dialog.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
|
|
|
QVERIFY(normalWindow.gotBlocked);
|
|
|
|
|
|
|
|
EventWindow normalWindowAfter;
|
|
|
|
normalWindowAfter.setFramePosition(m_availableTopLeft + QPoint(80, 80));
|
|
|
|
normalWindowAfter.resize(m_testWindowSize);
|
|
|
|
QVERIFY(!normalWindowAfter.gotBlocked);
|
|
|
|
normalWindowAfter.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&normalWindowAfter));
|
|
|
|
QVERIFY(normalWindowAfter.gotBlocked);
|
|
|
|
}
|
|
|
|
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
void tst_QWindow::generatedMouseMove()
|
|
|
|
{
|
|
|
|
InputTestWindow w;
|
2019-05-10 07:10:27 +00:00
|
|
|
w.setTitle(QLatin1String(QTest::currentTestFunction()));
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
w.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize));
|
2018-02-28 10:39:21 +00:00
|
|
|
w.setFlags(w.flags() | Qt::FramelessWindowHint); // ### FIXME: QTBUG-63542
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
w.show();
|
2018-03-02 14:24:09 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QPoint point(10, 10);
|
|
|
|
QPoint step(2, 2);
|
|
|
|
|
|
|
|
QVERIFY(w.mouseMovedCount == 0);
|
2018-02-28 10:39:21 +00:00
|
|
|
QTest::mouseMove(&w, point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 1);
|
2018-02-28 10:39:21 +00:00
|
|
|
// A press event that does not change position should not generate mouse move
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mousePress(&w, Qt::LeftButton, Qt::KeyboardModifiers(), point);
|
|
|
|
QTest::mousePress(&w, Qt::RightButton, Qt::KeyboardModifiers(), point);
|
2018-02-28 10:39:21 +00:00
|
|
|
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 1);
|
|
|
|
|
2018-02-28 10:39:21 +00:00
|
|
|
// Verify that a move event is generated for a mouse release event that changes position
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
point += step;
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mouseRelease(&w, Qt::LeftButton,Qt::KeyboardModifiers(), point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 2);
|
|
|
|
QVERIFY(w.buttonStateInGeneratedMove == (Qt::LeftButton | Qt::RightButton));
|
|
|
|
point += step;
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mouseRelease(&w, Qt::RightButton, Qt::KeyboardModifiers(), point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 3);
|
|
|
|
QVERIFY(w.buttonStateInGeneratedMove == Qt::RightButton);
|
|
|
|
|
2018-02-28 10:39:21 +00:00
|
|
|
// Verify that a move event is generated for a mouse press event that changes position
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
point += step;
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mousePress(&w, Qt::LeftButton, Qt::KeyboardModifiers(), point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 4);
|
|
|
|
QVERIFY(w.buttonStateInGeneratedMove == Qt::NoButton);
|
|
|
|
point += step;
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mousePress(&w, Qt::RightButton, Qt::KeyboardModifiers(), point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 5);
|
|
|
|
QVERIFY(w.buttonStateInGeneratedMove == Qt::LeftButton);
|
|
|
|
|
2018-02-28 10:39:21 +00:00
|
|
|
// A release event that does not change position should not generate mouse move
|
2019-05-10 07:10:27 +00:00
|
|
|
QTest::mouseRelease(&w, Qt::RightButton, Qt::KeyboardModifiers(), point);
|
|
|
|
QTest::mouseRelease(&w, Qt::LeftButton, Qt::KeyboardModifiers(), point);
|
mouse handling: fix issue when mixing QTest::mouse* APIs
... that become apparent after switching qtestlib to use enhanced mouse
event (a37785ec7638e7485112b87dd7e767881fecc114). With the old code path,
where QGuiApplication was deducing event type it would deduce mouse release
event even when there wasn't one. The new code path doesn't do that, which
revealed an obscure problem when mixing QTest::mouse* APIs (where QWindow
overload goes through QWindowSystemInterface API and QWidget overload goes
through QApplication::notify() and sets mouse_buttons from there). What
happened in this specific test case "./tst_qtreeview selection statusTip" was:
// tst_QTreeView::selection sets mouse_buttons = Qt::LeftButton from QApplication::notify
QTest::mousePress(widget, Qt::LeftButton, ..)
// tst_QTreeView::statusTip
QTest::mouseMove(window, )
The old code path sees that position and state has changed, creates a fake
mouse event, which gets deduced as mouse release even if there wasn't one.
And by luck this happened to set mouse_buttons=Qt::NoButton. So when we use
mouse_buttons later to create QMouseEvent everything works as expected. With
the enhanced mouse we don't clear the pressed button from mouse_buttons (set
in tst_QTreeView::selection) as this is done only from press/release events,
then pass it to QMouseEvent and later because of that QApplicationPrivate::
pickMouseReceiver() returns nullptr.
The fix here is to use e->buttons when constructing QMouseEvent, instead of
relying on mouse_buttons which gets changed from various places and has other
issues that can not be solved without invalidating the current documentation
of QGuiApplication::mouseButtons() (e.g QTBUG-33161). Tests and any Qt code
in general should avoid using the fragile QGuiApplication::mouseButtons() API.
This patch does not affect the old code path (it continues working as before)
and fixes the issue described above for the enhanced mouse API. The enhanced
mouse API actually is better in a way that it does not get affected by button
state from test functions that run earlier, as opposed to the old code path
where every subsequent test function uses mouse_buttons in whatever state it
was left by the test functions that run earlier.
Not relying on mouse_buttons when creating QMouseEvent helped also to discover
other logic error. This caused an in incorrect button state for a mouse move
event that is generated for a release event that simultaneously changes a mouse
position.
Task-number: QTBUG-64043
Change-Id: I6ad8e49d8437ab0858180c2d0d45694f3b3c2d60
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
2017-10-30 09:15:43 +00:00
|
|
|
QVERIFY(w.mouseMovedCount == 5);
|
|
|
|
}
|
|
|
|
|
2018-10-08 08:10:07 +00:00
|
|
|
void tst_QWindow::keepPendingUpdateRequests()
|
|
|
|
{
|
|
|
|
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
|
|
|
|
|
|
|
|
Window window;
|
2019-05-10 07:10:27 +00:00
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
2018-10-08 08:10:07 +00:00
|
|
|
window.setGeometry(geometry);
|
|
|
|
window.show();
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
QTRY_VERIFY(window.isExposed());
|
|
|
|
|
|
|
|
window.requestUpdate();
|
|
|
|
window.close();
|
|
|
|
window.setVisible(true);
|
|
|
|
|
|
|
|
QPlatformWindow *platformWindow = window.handle();
|
|
|
|
QVERIFY(platformWindow);
|
|
|
|
|
|
|
|
QVERIFY(platformWindow->hasPendingUpdateRequest());
|
|
|
|
QTRY_VERIFY(!platformWindow->hasPendingUpdateRequest());
|
|
|
|
}
|
|
|
|
|
2021-06-11 12:17:35 +00:00
|
|
|
void tst_QWindow::activateDeactivateEvent()
|
|
|
|
{
|
2021-12-08 13:12:11 +00:00
|
|
|
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
|
|
|
|
QSKIP("QWindow::requestActivate() is not supported.");
|
|
|
|
|
2021-06-11 12:17:35 +00:00
|
|
|
class Window : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using QWindow::QWindow;
|
|
|
|
|
|
|
|
int activateCount = 0;
|
|
|
|
int deactivateCount = 0;
|
|
|
|
protected:
|
2021-06-30 11:57:55 +00:00
|
|
|
bool event(QEvent *e) override
|
2021-06-11 12:17:35 +00:00
|
|
|
{
|
|
|
|
switch (e->type()) {
|
|
|
|
case QEvent::WindowActivate:
|
|
|
|
++activateCount;
|
|
|
|
break;
|
|
|
|
case QEvent::WindowDeactivate:
|
|
|
|
++deactivateCount;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return QWindow::event(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Window w1;
|
|
|
|
Window w2;
|
|
|
|
|
|
|
|
w1.show();
|
|
|
|
w1.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&w1));
|
|
|
|
QCOMPARE(w1.activateCount, 1);
|
|
|
|
QCOMPARE(w1.deactivateCount, 0);
|
|
|
|
|
|
|
|
w2.show();
|
|
|
|
w2.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&w2));
|
|
|
|
QCOMPARE(w1.deactivateCount, 1);
|
|
|
|
QCOMPARE(w2.activateCount, 1);
|
|
|
|
}
|
|
|
|
|
2021-12-25 16:23:40 +00:00
|
|
|
// Test that in a slot connected to destroyed() the emitter is
|
|
|
|
// is no longer a QWindow.
|
|
|
|
void tst_QWindow::qobject_castOnDestruction()
|
|
|
|
{
|
|
|
|
QWindow window;
|
|
|
|
QObject::connect(&window, &QObject::destroyed, [](QObject *object)
|
|
|
|
{
|
|
|
|
QVERIFY(!qobject_cast<QWindow *>(object));
|
|
|
|
QVERIFY(!dynamic_cast<QWindow *>(object));
|
|
|
|
QVERIFY(!object->isWindowType());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-24 11:00:07 +00:00
|
|
|
void tst_QWindow::touchToMouseTranslationByPopup()
|
|
|
|
{
|
2025-06-23 09:21:04 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("Wayland: need real user action like a button press, key press, or touch down event.");
|
|
|
|
|
2022-05-24 11:00:07 +00:00
|
|
|
InputTestWindow window;
|
|
|
|
window.setTitle(QLatin1String(QTest::currentTestFunction()));
|
|
|
|
window.ignoreTouch = true;
|
|
|
|
window.setGeometry(QRect(m_availableTopLeft, m_testWindowSize));
|
|
|
|
window.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
|
|
|
|
|
|
|
InputTestWindow popupWindow;
|
|
|
|
popupWindow.setGeometry(QRect(m_availableTopLeft + QPoint(20, 20),
|
|
|
|
QSize(m_testWindowSize.width(), m_testWindowSize.height() / 2)));
|
|
|
|
popupWindow.setFlag(Qt::Popup);
|
|
|
|
popupWindow.setTransientParent(&window);
|
|
|
|
popupWindow.ignoreTouch = true;
|
|
|
|
popupWindow.closeOnTap = true;
|
|
|
|
popupWindow.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&popupWindow));
|
|
|
|
|
|
|
|
QTest::touchEvent(&popupWindow, touchDevice).press(0, {1, 1}, &window);
|
|
|
|
QVERIFY(!popupWindow.isVisible());
|
|
|
|
|
|
|
|
// Omit touchpoint 0: because the popup was closed, touchpoint0.release is not sent.
|
|
|
|
const QPoint tp1(50, 1);
|
|
|
|
QTest::touchEvent(&window, touchDevice).press(1, tp1, &window);
|
|
|
|
QTRY_COMPARE(window.mousePressButton, int(Qt::LeftButton));
|
|
|
|
QTest::touchEvent(&window, touchDevice).release(1, tp1, &window);
|
|
|
|
QTRY_COMPARE(window.mouseReleaseButton, int(Qt::LeftButton));
|
|
|
|
}
|
|
|
|
|
2022-10-28 13:23:41 +00:00
|
|
|
// Test that windowStateChanged is not emitted on noop change (QTBUG-102478)
|
|
|
|
void tst_QWindow::stateChangeSignal()
|
|
|
|
{
|
|
|
|
// Test only for Windows, Linux and macOS
|
|
|
|
#if !defined(Q_OS_LINUX) && !defined(Q_OS_WINDOWS) && !defined(Q_OS_DARWIN)
|
|
|
|
QSKIP("Singular windowStateChanged signal emission is guaranteed for Linux, Windows and macOS only.\n"
|
|
|
|
"On other operating systems, the signal may be emitted twice.");
|
|
|
|
#endif
|
|
|
|
QWindow w;
|
2023-08-11 04:38:23 +00:00
|
|
|
Q_ASSERT(connect (&w, &QWindow::windowStateChanged, [](Qt::WindowState s){qCDebug(lcTests) << "State change to" << s;}));
|
2022-10-28 13:23:41 +00:00
|
|
|
QSignalSpy spy(&w, SIGNAL(windowStateChanged(Qt::WindowState)));
|
|
|
|
unsigned short signalCount = 0;
|
|
|
|
QList<Qt::WindowState> effectiveStates;
|
2023-08-11 04:38:23 +00:00
|
|
|
Q_ASSERT(connect(&w, &QWindow::windowStateChanged, [&effectiveStates](Qt::WindowState state)
|
2022-10-28 13:23:41 +00:00
|
|
|
{ effectiveStates.append(state); }));
|
|
|
|
// Part 1:
|
|
|
|
// => test signal emission on programmatic state changes
|
|
|
|
QCOMPARE(w.windowState(), Qt::WindowNoState);
|
|
|
|
// - wait for target state to be set
|
|
|
|
// - wait for signal spy to have reached target count
|
|
|
|
// - extract state from signal and compare to target
|
|
|
|
#define CHECK_STATE(State)\
|
|
|
|
QTRY_VERIFY(QTest::qWaitFor([&w](){return (w.windowState() == State); }));\
|
|
|
|
CHECK_SIGNAL(State)
|
|
|
|
#define CHECK_SIGNAL(State)\
|
|
|
|
QTRY_COMPARE(spy.count(), signalCount);\
|
|
|
|
if (signalCount > 0) {\
|
|
|
|
QVariantList list = spy.at(signalCount - 1).toList();\
|
|
|
|
QCOMPARE(list.count(), 1);\
|
|
|
|
bool ok;\
|
|
|
|
const int stateInt = list.at(0).toInt(&ok);\
|
|
|
|
QVERIFY(ok);\
|
|
|
|
const Qt::WindowState newState = static_cast<Qt::WindowState>(stateInt);\
|
|
|
|
QCOMPARE(newState, State);\
|
|
|
|
}
|
|
|
|
// Check initialization
|
|
|
|
CHECK_STATE(Qt::WindowNoState);
|
|
|
|
// showMaximized after init
|
|
|
|
// expected behavior: signal emitted once with state == WindowMaximized
|
|
|
|
++signalCount;
|
|
|
|
w.showMaximized();
|
|
|
|
CHECK_STATE(Qt::WindowMaximized);
|
|
|
|
// setWindowState to normal
|
|
|
|
// expected behavior: signal emitted once with state == WindowNoState
|
|
|
|
++signalCount;
|
|
|
|
w.setWindowState(Qt::WindowNoState);
|
|
|
|
CHECK_STATE(Qt::WindowNoState);
|
|
|
|
// redundant setWindowState to normal - except windows, where the no-op is counted
|
|
|
|
// expected behavior: No emits.
|
|
|
|
// On Windows, a no-op state change causes a no-op resize and repaint, leading to a
|
|
|
|
// no-op state change and singal emission.
|
|
|
|
#ifdef Q_OS_WINDOWS
|
|
|
|
++signalCount;
|
|
|
|
++signalCount;
|
|
|
|
#endif
|
|
|
|
w.setWindowState(Qt::WindowNoState);
|
|
|
|
CHECK_STATE(Qt::WindowNoState);
|
|
|
|
// setWindowState to minimized
|
|
|
|
// expected behavior: signal emitted once with state == WindowMinimized
|
|
|
|
++signalCount;
|
|
|
|
w.showMinimized();
|
|
|
|
CHECK_STATE(Qt::WindowMinimized);
|
|
|
|
// setWindowState to Normal
|
|
|
|
// expected behavior: signal emitted once with state == WindowNoState
|
|
|
|
++signalCount;
|
|
|
|
w.showNormal();
|
|
|
|
CHECK_STATE(Qt::WindowNoState);
|
|
|
|
/*
|
|
|
|
- Testcase showFullScreen is omitted: Depending on window manager,
|
|
|
|
WindowFullScreen can be mapped to WindowMaximized
|
|
|
|
- Transition from WindowMinimized to WindowMaximized is omitted:
|
|
|
|
WindowNoState to WindowMaximized
|
|
|
|
*/
|
|
|
|
// Part 2:
|
|
|
|
// => test signal emission on simulated user interaction
|
|
|
|
// To test the code path, inject state change events into the QPA event queue.
|
|
|
|
// Test the signal emission only, not the window's actual visible state.
|
|
|
|
|
|
|
|
// Flush pending events and clear
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
spy.clear();
|
|
|
|
effectiveStates.clear();
|
|
|
|
signalCount = 0;
|
|
|
|
// Maximize window
|
|
|
|
QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowMaximized, w.windowState());
|
|
|
|
++signalCount;
|
|
|
|
CHECK_SIGNAL(Qt::WindowMaximized);
|
|
|
|
// Normalize window
|
|
|
|
QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowNoState, w.windowState());
|
|
|
|
++signalCount;
|
|
|
|
CHECK_SIGNAL(Qt::WindowNoState);
|
|
|
|
// Minimize window
|
|
|
|
QWindowSystemInterface::handleWindowStateChanged(&w, Qt::WindowMinimized, w.windowState());
|
|
|
|
++signalCount;
|
|
|
|
CHECK_SIGNAL(Qt::WindowMinimized);
|
|
|
|
}
|
|
|
|
|
2021-09-22 09:43:57 +00:00
|
|
|
#ifndef QT_NO_CURSOR
|
|
|
|
void tst_QWindow::enterLeaveOnWindowShowHide_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<Qt::WindowType>("windowType");
|
|
|
|
QTest::addRow("dialog") << Qt::Dialog;
|
|
|
|
QTest::addRow("popup") << Qt::Popup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Verify that we get enter and leave events if the window under the mouse
|
|
|
|
opens and closes a modal dialog or popup. QWindow might get multiple
|
|
|
|
events in a row, as the various QPA plugins need to use different techniques
|
|
|
|
to synthesize events if the native platform doesn't provide them for us.
|
|
|
|
*/
|
|
|
|
void tst_QWindow::enterLeaveOnWindowShowHide()
|
|
|
|
{
|
2023-06-27 13:47:25 +00:00
|
|
|
if (isPlatformWayland())
|
|
|
|
QSKIP("Can't set cursor position and qWaitForWindowActive on Wayland");
|
|
|
|
|
2023-10-19 09:35:21 +00:00
|
|
|
if (isPlatformEglFS())
|
|
|
|
QSKIP("QCursor::setPos() is not supported on this platform");
|
|
|
|
|
2021-09-22 09:43:57 +00:00
|
|
|
QFETCH(Qt::WindowType, windowType);
|
|
|
|
|
|
|
|
class Window : public QWindow
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
int numEnterEvents = 0;
|
|
|
|
int numLeaveEvents = 0;
|
|
|
|
QPoint enterPosition;
|
|
|
|
protected:
|
|
|
|
bool event(QEvent *e) override
|
|
|
|
{
|
|
|
|
switch (e->type()) {
|
|
|
|
case QEvent::Enter:
|
|
|
|
++numEnterEvents;
|
|
|
|
enterPosition = static_cast<QEnterEvent*>(e)->position().toPoint();
|
|
|
|
break;
|
|
|
|
case QEvent::Leave:
|
|
|
|
++numLeaveEvents;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return QWindow::event(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
int expectedEnter = 0;
|
|
|
|
int expectedLeave = 0;
|
|
|
|
|
|
|
|
Window window;
|
|
|
|
const QRect screenGeometry = window.screen()->availableGeometry();
|
|
|
|
const QPoint cursorPos = screenGeometry.topLeft() + QPoint(50, 50);
|
|
|
|
window.setGeometry(QRect(cursorPos - QPoint(50, 50), screenGeometry.size() / 4));
|
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
|
|
|
|
if (!QTest::qWaitFor([&]{ return window.geometry().contains(QCursor::pos()); }))
|
|
|
|
QSKIP("We can't move the cursor");
|
|
|
|
|
|
|
|
window.show();
|
|
|
|
window.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
|
|
|
|
|
|
|
++expectedEnter;
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(window.numEnterEvents, expectedEnter, 250);
|
|
|
|
QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos()));
|
|
|
|
|
|
|
|
QWindow secondary;
|
|
|
|
secondary.setFlag(windowType);
|
|
|
|
secondary.setModality(Qt::WindowModal);
|
|
|
|
secondary.setTransientParent(&window);
|
|
|
|
secondary.setPosition(cursorPos + QPoint(50, 50));
|
|
|
|
secondary.show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&secondary));
|
|
|
|
++expectedLeave;
|
|
|
|
QTRY_VERIFY(window.numLeaveEvents >= expectedLeave);
|
|
|
|
secondary.close();
|
|
|
|
++expectedEnter;
|
|
|
|
QTRY_VERIFY(window.numEnterEvents >= expectedEnter);
|
|
|
|
QCOMPARE(window.enterPosition, window.mapFromGlobal(QCursor::pos()));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-10-27 12:59:36 +00:00
|
|
|
void tst_QWindow::windowExposedAfterReparent()
|
|
|
|
{
|
|
|
|
QWindow parent;
|
|
|
|
QWindow child(&parent);
|
|
|
|
child.show();
|
|
|
|
parent.show();
|
|
|
|
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&parent));
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
|
|
|
|
2025-06-30 14:22:49 +00:00
|
|
|
// Close the child before reparenting it to ensure it is correctly converted
|
|
|
|
// to a toplevel window by the window manager.
|
|
|
|
child.close();
|
2023-10-27 12:59:36 +00:00
|
|
|
child.setParent(nullptr);
|
2025-06-30 14:22:49 +00:00
|
|
|
child.show();
|
2023-10-27 12:59:36 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
|
|
|
|
|
|
|
child.setParent(&parent);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
|
|
|
}
|
|
|
|
|
Add dedicated child and parent events for QWindow reparenting
When a QWindow is parented into another window, either via its constructor,
or via setParent(), we also update the QObject parent of the window. This
in turn sends ChildAdded and ChildRemoved events to the new and old parent
of the window, as well as ParentAboutToChange and ParentChange to the window
itself.
But at the point when these events are sent, the QWindow parent relationship
has not been fully established. We have not updated d->parentWindow, nor
have we propagated the parent relationship to the platform window.
This is problematic because clients can not use these events to react to
window parent changes synchronously. For example. trying to raise a child
window when added to a parent is not going to work, because at that point
the child window isn't a native child of the parent.
Instead of re-using the QObject events for QWindow, like QWidget does,
by delaying the events or sending them manually at a point when the
window parent relationship has been fully formed, we opt to add new
dedicated events.
Change-Id: I019c14eba444861f537e4f68ab3a82297f843cf7
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
2023-11-04 10:17:30 +00:00
|
|
|
struct ParentWindow : public QWindow
|
|
|
|
{
|
|
|
|
bool event(QEvent *event) override
|
|
|
|
{
|
|
|
|
[&]() -> void {
|
|
|
|
if (event->type() == QEvent::ChildWindowAdded
|
|
|
|
|| event->type() == QEvent::ChildWindowRemoved) {
|
|
|
|
// We should not receive child events after the window has been destructed
|
|
|
|
QVERIFY(this->isWindowType());
|
|
|
|
|
|
|
|
auto *parentWindow = this;
|
|
|
|
auto *childEvent = static_cast<QChildWindowEvent*>(event);
|
|
|
|
auto *childWindow = childEvent->child();
|
|
|
|
|
|
|
|
if (event->type() == QEvent::ChildWindowAdded) {
|
|
|
|
QVERIFY(childWindow->parent());
|
|
|
|
QVERIFY(parentWindow->isAncestorOf(childWindow));
|
|
|
|
if (childWindow->handle())
|
|
|
|
QVERIFY(childWindow->handle()->parent() == parentWindow->handle());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
QVERIFY(!childWindow->parent());
|
|
|
|
QVERIFY(!parentWindow->isAncestorOf(childWindow));
|
|
|
|
if (childWindow->handle())
|
|
|
|
QVERIFY(childWindow->handle()->parent() != parentWindow->handle());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return QWindow::event(event);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QWindow::childEvents()
|
|
|
|
{
|
|
|
|
ParentWindow parent;
|
|
|
|
|
|
|
|
{
|
|
|
|
// ChildAdded via constructor
|
|
|
|
QWindow constructorChild(&parent);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
// ChildRemoved via destructor
|
|
|
|
}
|
|
|
|
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
// ChildAdded and ChildRemoved via setParent
|
|
|
|
QWindow child;
|
|
|
|
child.setParent(&parent);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
child.setParent(nullptr);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
parent.create();
|
|
|
|
child.create();
|
|
|
|
|
|
|
|
// ChildAdded and ChildRemoved after creation
|
|
|
|
child.setParent(&parent);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
child.setParent(nullptr);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ChildWindowPrivate;
|
|
|
|
struct ChildWindow : public QWindow
|
|
|
|
{
|
|
|
|
ChildWindow(QWindow *parent = nullptr);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ChildWindowPrivate : public QWindowPrivate
|
|
|
|
{
|
|
|
|
ChildWindowPrivate() : QWindowPrivate()
|
|
|
|
{
|
|
|
|
receiveParentEvents = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ChildWindow::ChildWindow(QWindow *parent)
|
|
|
|
: QWindow(*new ChildWindowPrivate, parent)
|
|
|
|
{}
|
|
|
|
|
|
|
|
struct ParentEventTester : public QObject
|
|
|
|
{
|
|
|
|
bool eventFilter(QObject *object, QEvent *event) override
|
|
|
|
{
|
|
|
|
[&]() -> void {
|
|
|
|
if (event->type() == QEvent::ParentWindowAboutToChange
|
|
|
|
|| event->type() == QEvent::ParentWindowChange) {
|
|
|
|
// We should not receive parent events after the window has been destructed
|
|
|
|
QVERIFY(object->isWindowType());
|
|
|
|
auto *window = static_cast<QWindow*>(object);
|
|
|
|
|
|
|
|
if (event->type() == QEvent::ParentWindowAboutToChange) {
|
|
|
|
QVERIFY(window->parent() != nextExpectedParent);
|
|
|
|
if (window->handle()) {
|
|
|
|
QVERIFY(window->handle()->parent() !=
|
|
|
|
(nextExpectedParent ? nextExpectedParent->handle() : nullptr));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QVERIFY(window->parent() == nextExpectedParent);
|
|
|
|
if (window->handle()) {
|
|
|
|
QVERIFY(window->handle()->parent() ==
|
|
|
|
(nextExpectedParent ? nextExpectedParent->handle() : nullptr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return QObject::eventFilter(object, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
QWindow *nextExpectedParent = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void tst_QWindow::parentEvents()
|
|
|
|
{
|
|
|
|
QWindow parent;
|
|
|
|
|
|
|
|
{
|
|
|
|
ParentEventTester tester;
|
|
|
|
|
|
|
|
{
|
|
|
|
// We can't hook in early enough to get the parent change during
|
|
|
|
// QObject construction.
|
|
|
|
ChildWindow child(&parent);
|
|
|
|
|
|
|
|
// But we can observe the one during destruction
|
|
|
|
child.installEventFilter(&tester);
|
|
|
|
tester.nextExpectedParent = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
ParentEventTester tester;
|
|
|
|
ChildWindow child;
|
|
|
|
child.installEventFilter(&tester);
|
|
|
|
|
|
|
|
tester.nextExpectedParent = &parent;
|
|
|
|
child.setParent(&parent);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
tester.nextExpectedParent = nullptr;
|
|
|
|
child.setParent(nullptr);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
parent.create();
|
|
|
|
child.create();
|
|
|
|
|
|
|
|
tester.nextExpectedParent = &parent;
|
|
|
|
child.setParent(&parent);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
|
|
|
|
tester.nextExpectedParent = nullptr;
|
|
|
|
child.setParent(nullptr);
|
|
|
|
if (QTest::currentTestFailed()) return;
|
|
|
|
}
|
|
|
|
|
2011-08-29 06:26:37 +00:00
|
|
|
#include <tst_qwindow.moc>
|
2012-03-24 08:48:12 +00:00
|
|
|
QTEST_MAIN(tst_QWindow)
|
2012-09-21 10:12:01 +00:00
|
|
|
|