1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include <QtTest/qtest.h>
38#include <QtTest/qsignalspy.h>
39#include "../shared/util.h"
40#include "../shared/visualtestutil.h"
41#include "../shared/qtest_quickcontrols.h"
42
43#include <QtGui/qstylehints.h>
44#include <QtGui/qtouchdevice.h>
45#include <QtGui/qguiapplication.h>
46#include <QtGui/qpa/qwindowsysteminterface.h>
47#include <QtQuick/private/qquickwindow_p.h>
48#include <QtQuick/private/qquickflickable_p.h>
49#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
50#include <QtQuickTemplates2/private/qquickoverlay_p.h>
51#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
52#include <QtQuickTemplates2/private/qquickdrawer_p.h>
53#include <QtQuickTemplates2/private/qquickbutton_p.h>
54#include <QtQuickTemplates2/private/qquickslider_p.h>
55
56using namespace QQuickVisualTestUtil;
57
58class tst_QQuickDrawer : public QQmlDataTest
59{
60 Q_OBJECT
61
62private slots:
63 void initTestCase();
64
65 void defaults();
66 void invalidEdge();
67
68 void visible_data();
69 void visible();
70
71 void state();
72
73 void position_data();
74 void position();
75
76 void dragMargin_data();
77 void dragMargin();
78
79 void reposition();
80 void header();
81
82 void hover_data();
83 void hover();
84
85 void wheel_data();
86 void wheel();
87
88 void multiple();
89
90 void touch_data();
91 void touch();
92
93 void multiTouch();
94
95 void grabber();
96
97 void interactive_data();
98 void interactive();
99
100 void flickable_data();
101 void flickable();
102
103 void dragOverModalShadow_data();
104 void dragOverModalShadow();
105
106 void nonModal_data();
107 void nonModal();
108
109 void slider_data();
110 void slider();
111
112 void topEdgeScreenEdge();
113
114private:
115 struct TouchDeviceDeleter
116 {
117 static inline void cleanup(QTouchDevice *device)
118 {
119 QWindowSystemInterface::unregisterTouchDevice(device);
120 delete device;
121 }
122 };
123
124 QScopedPointer<QTouchDevice, TouchDeviceDeleter> touchDevice;
125};
126
127
128void tst_QQuickDrawer::initTestCase()
129{
130 QQmlDataTest::initTestCase();
131 qputenv(varName: "QML_NO_TOUCH_COMPRESSION", value: "1");
132
133 touchDevice.reset(other: new QTouchDevice);
134 touchDevice->setType(QTouchDevice::TouchScreen);
135 QWindowSystemInterface::registerTouchDevice(device: touchDevice.data());
136}
137
138void tst_QQuickDrawer::defaults()
139{
140 QQmlEngine engine;
141 QQmlComponent component(&engine);
142 component.loadUrl(url: testFileUrl(fileName: "window.qml"));
143
144 QScopedPointer<QObject> root(component.create());
145 QVERIFY2(!root.isNull(), qPrintable(component.errorString()));
146
147 QQuickDrawer *drawer = root->property(name: "drawer").value<QQuickDrawer *>();
148 QVERIFY(drawer);
149 QCOMPARE(drawer->edge(), Qt::LeftEdge);
150 QCOMPARE(drawer->position(), 0.0);
151 QCOMPARE(drawer->dragMargin(), qGuiApp->styleHints()->startDragDistance());
152}
153
154void tst_QQuickDrawer::invalidEdge()
155{
156 QQmlEngine engine;
157 QQmlComponent component(&engine);
158 component.loadUrl(url: testFileUrl(fileName: "window.qml"));
159
160 QScopedPointer<QObject> root(component.create());
161 QVERIFY2(!root.isNull(), qPrintable(component.errorString()));
162
163 QQuickDrawer *drawer = root->property(name: "drawer").value<QQuickDrawer *>();
164 QVERIFY(drawer);
165
166 // Test an invalid value - it should warn and ignore it.
167 QTest::ignoreMessage(type: QtWarningMsg, qUtf8Printable(testFileUrl("window.qml").toString() + ":61:5: QML Drawer: invalid edge value - valid values are: Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge"));
168 drawer->setEdge(static_cast<Qt::Edge>(QQuickDrawer::Right));
169 QCOMPARE(drawer->edge(), Qt::LeftEdge);
170}
171
172void tst_QQuickDrawer::visible_data()
173{
174 QTest::addColumn<QString>(name: "source");
175 QTest::newRow(dataTag: "Window") << "window.qml";
176 QTest::newRow(dataTag: "ApplicationWindow") << "applicationwindow.qml";
177}
178
179void tst_QQuickDrawer::visible()
180{
181 QFETCH(QString, source);
182 QQuickApplicationHelper helper(this, source);
183 QVERIFY2(helper.ready, helper.failureMessage());
184
185 QQuickWindow *window = helper.window;
186 window->show();
187 QVERIFY(QTest::qWaitForWindowExposed(window));
188
189 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
190 QVERIFY(drawer);
191 QQuickItem *popupItem = drawer->popupItem();
192
193 QCOMPARE(drawer->isVisible(), false);
194 QCOMPARE(drawer->position(), qreal(0.0));
195
196 QQuickOverlay *overlay = QQuickOverlay::overlay(window);
197 QVERIFY(overlay);
198 QVERIFY(!overlay->childItems().contains(popupItem));
199
200 drawer->open();
201 QVERIFY(drawer->isVisible());
202 QVERIFY(overlay->childItems().contains(popupItem));
203 QTRY_COMPARE(drawer->position(), qreal(1.0));
204
205 drawer->close();
206 QTRY_VERIFY(!drawer->isVisible());
207 QTRY_COMPARE(drawer->position(), qreal(0.0));
208 QVERIFY(!overlay->childItems().contains(popupItem));
209
210 drawer->setVisible(true);
211 QVERIFY(drawer->isVisible());
212 QVERIFY(overlay->childItems().contains(popupItem));
213 QTRY_COMPARE(drawer->position(), qreal(1.0));
214
215 drawer->setVisible(false);
216 QTRY_VERIFY(!drawer->isVisible());
217 QTRY_COMPARE(drawer->position(), qreal(0.0));
218 QTRY_VERIFY(!overlay->childItems().contains(popupItem));
219}
220
221void tst_QQuickDrawer::state()
222{
223 QQuickApplicationHelper helper(this, "applicationwindow.qml");
224 QVERIFY2(helper.ready, helper.failureMessage());
225
226 QQuickWindow *window = helper.window;
227 window->show();
228 QVERIFY(QTest::qWaitForWindowExposed(window));
229
230 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
231 QVERIFY(drawer);
232
233 QCOMPARE(drawer->isVisible(), false);
234
235 QSignalSpy visibleChangedSpy(drawer, SIGNAL(visibleChanged()));
236 QSignalSpy aboutToShowSpy(drawer, SIGNAL(aboutToShow()));
237 QSignalSpy aboutToHideSpy(drawer, SIGNAL(aboutToHide()));
238 QSignalSpy openedSpy(drawer, SIGNAL(opened()));
239 QSignalSpy closedSpy(drawer, SIGNAL(closed()));
240
241 QVERIFY(visibleChangedSpy.isValid());
242 QVERIFY(aboutToShowSpy.isValid());
243 QVERIFY(aboutToHideSpy.isValid());
244 QVERIFY(openedSpy.isValid());
245 QVERIFY(closedSpy.isValid());
246
247 int visibleChangedCount = 0;
248 int aboutToShowCount = 0;
249 int aboutToHideCount = 0;
250 int openedCount = 0;
251 int closedCount = 0;
252
253 // open programmatically...
254 drawer->open();
255 QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount);
256 QCOMPARE(aboutToShowSpy.count(), ++aboutToShowCount);
257 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
258 QCOMPARE(openedSpy.count(), openedCount);
259 QCOMPARE(closedSpy.count(), closedCount);
260
261 // ...and wait until fully open
262 QVERIFY(openedSpy.wait());
263 QCOMPARE(visibleChangedSpy.count(), visibleChangedCount);
264 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
265 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
266 QCOMPARE(openedSpy.count(), ++openedCount);
267 QCOMPARE(closedSpy.count(), closedCount);
268
269 // close programmatically...
270 drawer->close();
271 QCOMPARE(visibleChangedSpy.count(), visibleChangedCount);
272 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
273 QCOMPARE(aboutToHideSpy.count(), ++aboutToHideCount);
274 QCOMPARE(openedSpy.count(), openedCount);
275 QCOMPARE(closedSpy.count(), closedCount);
276
277 // ...and wait until fully closed
278 QVERIFY(closedSpy.wait());
279 QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount);
280 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
281 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
282 QCOMPARE(openedSpy.count(), openedCount);
283 QCOMPARE(closedSpy.count(), ++closedCount);
284
285 // open interactively...
286 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, drawer->height() / 2));
287 QTest::mouseMove(window, pos: QPoint(drawer->width() * 0.2, drawer->height() / 2), delay: 16);
288 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(drawer->width() * 0.8, drawer->height() / 2), delay: 16);
289 QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount);
290 QCOMPARE(aboutToShowSpy.count(), ++aboutToShowCount);
291 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
292 QCOMPARE(openedSpy.count(), openedCount);
293 QCOMPARE(closedSpy.count(), closedCount);
294
295 // ...and wait until fully open
296 QVERIFY(openedSpy.wait());
297 QCOMPARE(visibleChangedSpy.count(), visibleChangedCount);
298 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
299 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
300 QCOMPARE(openedSpy.count(), ++openedCount);
301 QCOMPARE(closedSpy.count(), closedCount);
302
303 // close interactively...
304 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(drawer->width(), drawer->height() / 2));
305 QTest::mouseMove(window, pos: QPoint(drawer->width() * 0.8, drawer->height() / 2), delay: 16);
306 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(drawer->width() * 0.2, drawer->height() / 2), delay: 16);
307 QCOMPARE(visibleChangedSpy.count(), visibleChangedCount);
308 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
309 QCOMPARE(aboutToHideSpy.count(), ++aboutToHideCount);
310 QCOMPARE(openedSpy.count(), openedCount);
311 QCOMPARE(closedSpy.count(), closedCount);
312
313 // ...and wait until fully closed
314 QVERIFY(closedSpy.wait());
315 QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount);
316 QCOMPARE(aboutToShowSpy.count(), aboutToShowCount);
317 QCOMPARE(aboutToHideSpy.count(), aboutToHideCount);
318 QCOMPARE(openedSpy.count(), openedCount);
319 QCOMPARE(closedSpy.count(), ++closedCount);
320}
321
322void tst_QQuickDrawer::position_data()
323{
324 QTest::addColumn<Qt::Edge>(name: "edge");
325 QTest::addColumn<QPoint>(name: "press");
326 QTest::addColumn<QPoint>(name: "from");
327 QTest::addColumn<QPoint>(name: "to");
328 QTest::addColumn<qreal>(name: "position");
329
330 QTest::newRow(dataTag: "top") << Qt::TopEdge << QPoint(100, 0) << QPoint(100, 50) << QPoint(100, 150) << qreal(0.5);
331 QTest::newRow(dataTag: "left") << Qt::LeftEdge << QPoint(0, 100) << QPoint(50, 100) << QPoint(150, 100) << qreal(0.5);
332 QTest::newRow(dataTag: "right") << Qt::RightEdge << QPoint(399, 100) << QPoint(350, 100) << QPoint(250, 100) << qreal(0.5);
333 QTest::newRow(dataTag: "bottom") << Qt::BottomEdge << QPoint(100, 399) << QPoint(100, 350) << QPoint(150, 250) << qreal(0.5);
334}
335
336void tst_QQuickDrawer::position()
337{
338 QFETCH(Qt::Edge, edge);
339 QFETCH(QPoint, press);
340 QFETCH(QPoint, from);
341 QFETCH(QPoint, to);
342 QFETCH(qreal, position);
343
344 QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml"));
345 QVERIFY2(helper.ready, helper.failureMessage());
346
347 QQuickApplicationWindow *window = helper.appWindow;
348 window->show();
349 window->requestActivate();
350 QVERIFY(QTest::qWaitForWindowActive(window));
351
352 QQuickDrawer *drawer = helper.appWindow->property(name: "drawer").value<QQuickDrawer*>();
353 QVERIFY(drawer);
354 drawer->setEdge(edge);
355
356 // Give it some time (50 ms) before the press to avoid flakiness on OpenSUSE: QTBUG-77946
357 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: press, delay: 50);
358 QTest::mouseMove(window, pos: from);
359 QTest::mouseMove(window, pos: to);
360 QCOMPARE(drawer->position(), position);
361
362 // moved half-way open at almost infinite speed => snap to open
363 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
364 QTRY_COMPARE(drawer->position(), 1.0);
365}
366
367void tst_QQuickDrawer::dragMargin_data()
368{
369 QTest::addColumn<Qt::Edge>(name: "edge");
370 QTest::addColumn<qreal>(name: "dragMargin");
371 QTest::addColumn<qreal>(name: "dragFromLeft");
372 QTest::addColumn<qreal>(name: "dragFromRight");
373
374 QTest::newRow(dataTag: "left:0") << Qt::LeftEdge << qreal(0) << qreal(0) << qreal(0);
375 QTest::newRow(dataTag: "left:-1") << Qt::LeftEdge << qreal(-1) << qreal(0) << qreal(0);
376 QTest::newRow(dataTag: "left:startDragDistance") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance()) << qreal(0.45) << qreal(0);
377 QTest::newRow(dataTag: "left:startDragDistance*2") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance() * 2) << qreal(0.45) << qreal(0);
378
379 QTest::newRow(dataTag: "right:0") << Qt::RightEdge << qreal(0) << qreal(0) << qreal(0);
380 QTest::newRow(dataTag: "right:-1") << Qt::RightEdge << qreal(-1) << qreal(0) << qreal(0);
381 QTest::newRow(dataTag: "right:startDragDistance") << Qt::RightEdge << qreal(QGuiApplication::styleHints()->startDragDistance()) << qreal(0) << qreal(0.75);
382 QTest::newRow(dataTag: "right:startDragDistance*2") << Qt::RightEdge << qreal(QGuiApplication::styleHints()->startDragDistance() * 2) << qreal(0) << qreal(0.75);
383}
384
385void tst_QQuickDrawer::dragMargin()
386{
387 QFETCH(Qt::Edge, edge);
388 QFETCH(qreal, dragMargin);
389 QFETCH(qreal, dragFromLeft);
390 QFETCH(qreal, dragFromRight);
391
392 QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml"));
393 QVERIFY2(helper.ready, helper.failureMessage());
394
395 QQuickApplicationWindow *window = helper.appWindow;
396 window->show();
397 window->requestActivate();
398 QVERIFY(QTest::qWaitForWindowActive(window));
399
400 QQuickDrawer *drawer = helper.appWindow->property(name: "drawer").value<QQuickDrawer*>();
401 QVERIFY(drawer);
402 drawer->setEdge(edge);
403 drawer->setDragMargin(dragMargin);
404
405 const int startDragDistance = qMax(a: 20, b: QGuiApplication::styleHints()->startDragDistance() + 5) + 1;
406
407 // drag from the left
408 int leftX = qMax<int>(a: 0, b: dragMargin);
409 int leftDistance = startDragDistance + drawer->width() * 0.45;
410 QVERIFY(leftDistance > QGuiApplication::styleHints()->startDragDistance());
411 // Give it some time (50 ms) before the press to avoid flakiness on OpenSUSE: QTBUG-77946
412 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(leftX, drawer->height() / 2), delay: 50);
413 QTest::mouseMove(window, pos: QPoint(leftX + startDragDistance, drawer->height() / 2));
414 QTest::mouseMove(window, pos: QPoint(leftX + leftDistance, drawer->height() / 2));
415 QCOMPARE(drawer->position(), dragFromLeft);
416 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(leftX + leftDistance, drawer->height() / 2));
417
418 drawer->close();
419 QTRY_COMPARE(drawer->position(), qreal(0.0));
420
421 // drag from the right
422 int rightX = qMin<int>(a: window->width() - 1, b: window->width() - dragMargin);
423 int rightDistance = startDragDistance + drawer->width() * 0.75;
424 QVERIFY(rightDistance > QGuiApplication::styleHints()->startDragDistance());
425 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(rightX, drawer->height() / 2));
426 QTest::mouseMove(window, pos: QPoint(rightX - startDragDistance, drawer->height() / 2));
427 QTest::mouseMove(window, pos: QPoint(rightX - rightDistance, drawer->height() / 2));
428 QCOMPARE(drawer->position(), dragFromRight);
429 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(rightX - rightDistance, drawer->height() / 2));
430}
431
432static QRectF geometry(const QQuickItem *item)
433{
434 return QRectF(item->x(), item->y(), item->width(), item->height());
435}
436
437void tst_QQuickDrawer::reposition()
438{
439 QQuickApplicationHelper helper(this, QStringLiteral("reposition.qml"));
440 QVERIFY2(helper.ready, helper.failureMessage());
441
442 QQuickApplicationWindow *window = helper.appWindow;
443 window->show();
444 QVERIFY(QTest::qWaitForWindowExposed(window));
445
446 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
447 QVERIFY(drawer);
448 QQuickItem *popupItem = drawer->popupItem();
449 QVERIFY(popupItem);
450
451 drawer->open();
452 QQuickItem *dimmer = QQuickPopupPrivate::get(popup: drawer)->dimmer;
453 QVERIFY(dimmer);
454
455 QCOMPARE(geometry(dimmer), QRectF(0, 0, window->width(), window->height()));
456 QTRY_COMPARE(geometry(popupItem), QRectF(0, 0, window->width() / 2, window->height()));
457
458 drawer->setY(100);
459 QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100));
460 QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100));
461
462 drawer->setHeight(window->height());
463 QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height()));
464 QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height()));
465
466 drawer->resetHeight();
467 QCOMPARE(geometry(dimmer), QRectF(0, 100, window->width(), window->height() - 100));
468 QCOMPARE(geometry(popupItem), QRectF(0, 100, window->width() / 2, window->height() - 100));
469
470 drawer->setParentItem(window->contentItem());
471 QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150));
472 QCOMPARE(geometry(popupItem), QRectF(0, 150, window->width() / 2, window->height() - 150));
473
474 drawer->setEdge(Qt::RightEdge);
475 QCOMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150));
476 QTRY_COMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150));
477
478 window->setWidth(window->width() + 100);
479 QTRY_COMPARE(geometry(dimmer), QRectF(0, 150, window->width(), window->height() - 150));
480 QTRY_COMPARE(geometry(popupItem), QRectF(window->width() - drawer->width(), 150, window->width() / 2, window->height() - 150));
481
482 drawer->close();
483 QTRY_COMPARE(geometry(popupItem), QRectF(window->width(), 150, window->width() / 2, window->height() - 150));
484
485 QQuickDrawer *drawer2 = window->property(name: "drawer2").value<QQuickDrawer *>();
486 QVERIFY(drawer2);
487 QQuickItem *popupItem2 = drawer2->popupItem();
488 QVERIFY(popupItem2);
489
490 drawer2->open();
491 QVERIFY(popupItem2->isVisible());
492 QCOMPARE(popupItem2->x(), -drawer2->width());
493 QTRY_COMPARE(popupItem2->x(), 0.0);
494}
495
496void tst_QQuickDrawer::header()
497{
498 QQuickApplicationHelper helper(this, QStringLiteral("header.qml"));
499 QVERIFY2(helper.ready, helper.failureMessage());
500
501 QQuickApplicationWindow *window = helper.appWindow;
502 window->show();
503 QVERIFY(QTest::qWaitForWindowExposed(window));
504
505 QQuickItem *content = window->contentItem();
506 QVERIFY(content);
507
508 QQuickOverlay *overlay = QQuickOverlay::overlay(window);
509 QVERIFY(overlay);
510
511 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
512 QVERIFY(drawer);
513 QQuickItem *popupItem = drawer->popupItem();
514
515 QQuickButton *button = window->property(name: "button").value<QQuickButton*>();
516 QVERIFY(button);
517
518 drawer->open();
519 QVERIFY(drawer->isVisible());
520
521 QCOMPARE(drawer->parentItem(), overlay);
522 QCOMPARE(drawer->height(), overlay->height());
523 QCOMPARE(popupItem->height(), overlay->height());
524
525 drawer->setParentItem(content);
526 QCOMPARE(drawer->parentItem(), content);
527 QCOMPARE(drawer->height(), content->height());
528 QCOMPARE(popupItem->height(), content->height());
529
530 // must be possible to interact with the header when the drawer is below the header
531 QSignalSpy clickSpy(button, SIGNAL(clicked()));
532 QVERIFY(clickSpy.isValid());
533 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(button->x() + button->width() / 2, button->y() + button->height() / 2));
534 QCOMPARE(clickSpy.count(), 1);
535}
536
537void tst_QQuickDrawer::hover_data()
538{
539 QTest::addColumn<QString>(name: "source");
540 QTest::addColumn<bool>(name: "modal");
541
542 QTest::newRow(dataTag: "Window:modal") << "window-hover.qml" << true;
543 QTest::newRow(dataTag: "Window:modeless") << "window-hover.qml" << false;
544 QTest::newRow(dataTag: "ApplicationWindow:modal") << "applicationwindow-hover.qml" << true;
545 QTest::newRow(dataTag: "ApplicationWindow:modeless") << "applicationwindow-hover.qml" << false;
546}
547
548void tst_QQuickDrawer::hover()
549{
550 QFETCH(QString, source);
551 QFETCH(bool, modal);
552
553 QQuickApplicationHelper helper(this, source);
554 QVERIFY2(helper.ready, helper.failureMessage());
555 QQuickWindow *window = helper.window;
556 window->show();
557 window->requestActivate();
558 QVERIFY(QTest::qWaitForWindowActive(window));
559
560 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
561 QVERIFY(drawer);
562 drawer->setModal(modal);
563
564 QQuickControl *drawerItem = qobject_cast<QQuickControl *>(object: drawer->popupItem());
565 QVERIFY(drawerItem);
566 QVERIFY(drawerItem->isHoverEnabled());
567
568 QQuickButton *backgroundButton = window->property(name: "backgroundButton").value<QQuickButton*>();
569 QVERIFY(backgroundButton);
570 backgroundButton->setHoverEnabled(true);
571
572 QQuickButton *drawerButton = window->property(name: "drawerButton").value<QQuickButton*>();
573 QVERIFY(drawerButton);
574 drawerButton->setHoverEnabled(true);
575
576 QSignalSpy openedSpy(drawer, SIGNAL(opened()));
577 QVERIFY(openedSpy.isValid());
578 drawer->open();
579 QVERIFY(openedSpy.count() == 1 || openedSpy.wait());
580
581 // hover the background button outside the drawer
582 QTest::mouseMove(window, pos: QPoint(window->width() - 1, window->height() - 1));
583 QCOMPARE(backgroundButton->isHovered(), !modal);
584 QVERIFY(!drawerButton->isHovered());
585 QVERIFY(!drawerItem->isHovered());
586
587 // hover the drawer background
588 QTest::mouseMove(window, pos: QPoint(1, 1));
589 QVERIFY(!backgroundButton->isHovered());
590 QVERIFY(!drawerButton->isHovered());
591 QVERIFY(drawerItem->isHovered());
592
593 // hover the button in a drawer
594 QTest::mouseMove(window, pos: QPoint(2, 2));
595 QVERIFY(!backgroundButton->isHovered());
596 QVERIFY(drawerButton->isHovered());
597 QVERIFY(drawerItem->isHovered());
598
599 QSignalSpy closedSpy(drawer, SIGNAL(closed()));
600 QVERIFY(closedSpy.isValid());
601 drawer->close();
602 QVERIFY(closedSpy.count() == 1 || closedSpy.wait());
603
604 // hover the background button after closing the drawer
605 QTest::mouseMove(window, pos: QPoint(window->width() / 2, window->height() / 2));
606 QVERIFY(backgroundButton->isHovered());
607 QVERIFY(!drawerButton->isHovered());
608 QVERIFY(!drawerItem->isHovered());
609}
610
611void tst_QQuickDrawer::wheel_data()
612{
613 QTest::addColumn<QString>(name: "source");
614 QTest::addColumn<bool>(name: "modal");
615
616 QTest::newRow(dataTag: "Window:modal") << "window-wheel.qml" << true;
617 QTest::newRow(dataTag: "Window:modeless") << "window-wheel.qml" << false;
618 QTest::newRow(dataTag: "ApplicationWindow:modal") << "applicationwindow-wheel.qml" << true;
619 QTest::newRow(dataTag: "ApplicationWindow:modeless") << "applicationwindow-wheel.qml" << false;
620}
621
622static bool sendWheelEvent(QQuickItem *item, const QPoint &localPos, int degrees)
623{
624 QQuickWindow *window = item->window();
625 QWheelEvent wheelEvent(localPos, item->window()->mapToGlobal(pos: localPos), QPoint(0, 0), QPoint(0, 8 * degrees), 0, Qt::Vertical, Qt::NoButton, 0);
626 QSpontaneKeyEvent::setSpontaneous(&wheelEvent);
627 return qGuiApp->notify(window, &wheelEvent);
628}
629
630void tst_QQuickDrawer::wheel()
631{
632 QFETCH(QString, source);
633 QFETCH(bool, modal);
634
635 QQuickApplicationHelper helper(this, source);
636 QVERIFY2(helper.ready, helper.failureMessage());
637 QQuickWindow *window = helper.window;
638 window->show();
639 QVERIFY(QTest::qWaitForWindowExposed(window));
640
641 QQuickSlider *contentSlider = window->property(name: "contentSlider").value<QQuickSlider*>();
642 QVERIFY(contentSlider);
643
644 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
645 QVERIFY(drawer && drawer->contentItem());
646 drawer->setModal(modal);
647
648 QQuickSlider *drawerSlider = window->property(name: "drawerSlider").value<QQuickSlider*>();
649 QVERIFY(drawerSlider);
650
651 {
652 // wheel over the content
653 qreal oldContentValue = contentSlider->value();
654 qreal oldDrawerValue = drawerSlider->value();
655
656 QVERIFY(sendWheelEvent(contentSlider, QPoint(contentSlider->width() / 2, contentSlider->height() / 2), 15));
657
658 QVERIFY(!qFuzzyCompare(contentSlider->value(), oldContentValue)); // must have moved
659 QVERIFY(qFuzzyCompare(drawerSlider->value(), oldDrawerValue)); // must not have moved
660 }
661
662 QSignalSpy openedSpy(drawer, SIGNAL(opened()));
663 QVERIFY(openedSpy.isValid());
664 drawer->open();
665 QVERIFY(openedSpy.count() == 1 || openedSpy.wait());
666
667 {
668 // wheel over the drawer content
669 qreal oldContentValue = contentSlider->value();
670 qreal oldDrawerValue = drawerSlider->value();
671
672 QVERIFY(sendWheelEvent(drawerSlider, QPoint(drawerSlider->width() / 2, drawerSlider->height() / 2), 15));
673
674 QVERIFY(qFuzzyCompare(contentSlider->value(), oldContentValue)); // must not have moved
675 QVERIFY(!qFuzzyCompare(drawerSlider->value(), oldDrawerValue)); // must have moved
676 }
677
678 {
679 // wheel over the overlay
680 qreal oldContentValue = contentSlider->value();
681 qreal oldDrawerValue = drawerSlider->value();
682
683 QVERIFY(sendWheelEvent(QQuickOverlay::overlay(window), QPoint(0, 0), 15));
684
685 if (modal) {
686 // the content below a modal overlay must not move
687 QVERIFY(qFuzzyCompare(contentSlider->value(), oldContentValue));
688 } else {
689 // the content below a modeless overlay must move
690 QVERIFY(!qFuzzyCompare(contentSlider->value(), oldContentValue));
691 }
692 QVERIFY(qFuzzyCompare(drawerSlider->value(), oldDrawerValue)); // must not have moved
693 }
694}
695
696void tst_QQuickDrawer::multiple()
697{
698 QQuickApplicationHelper helper(this, QStringLiteral("multiple.qml"));
699 QVERIFY2(helper.ready, helper.failureMessage());
700 QQuickWindow *window = helper.window;
701 window->show();
702 QVERIFY(QTest::qWaitForWindowExposed(window));
703
704 QQuickDrawer *leftDrawer = window->property(name: "leftDrawer").value<QQuickDrawer*>();
705 QVERIFY(leftDrawer);
706 QQuickButton *leftButton = window->property(name: "leftButton").value<QQuickButton*>();
707 QVERIFY(leftButton);
708 QSignalSpy leftClickSpy(leftButton, SIGNAL(clicked()));
709 QVERIFY(leftClickSpy.isValid());
710
711 QQuickDrawer *rightDrawer = window->property(name: "rightDrawer").value<QQuickDrawer*>();
712 QVERIFY(rightDrawer);
713 QQuickButton *rightButton = window->property(name: "rightButton").value<QQuickButton*>();
714 QVERIFY(rightButton);
715 QSignalSpy rightClickSpy(rightButton, SIGNAL(clicked()));
716 QVERIFY(rightClickSpy.isValid());
717
718 QQuickButton *contentButton = window->property(name: "contentButton").value<QQuickButton*>();
719 QVERIFY(contentButton);
720 QSignalSpy contentClickSpy(contentButton, SIGNAL(clicked()));
721 QVERIFY(contentClickSpy.isValid());
722
723 // no drawers open, click the content
724 QTest::mouseClick(window, button: Qt::LeftButton);
725 QCOMPARE(contentClickSpy.count(), 1);
726 QCOMPARE(leftClickSpy.count(), 0);
727 QCOMPARE(rightClickSpy.count(), 0);
728
729 // drag the left drawer open
730 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, window->height() / 2));
731 QTest::mouseMove(window, pos: QPoint(leftDrawer->width() / 4, window->height() / 2));
732 QTest::mouseMove(window, pos: QPoint(leftDrawer->width() / 4 * 3, window->height() / 2));
733 QCOMPARE(leftDrawer->position(), 0.5);
734 QCOMPARE(rightDrawer->position(), 0.0);
735 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(leftDrawer->width() / 2, window->height() / 2));
736 QTRY_COMPARE(leftDrawer->position(), 1.0);
737 QCOMPARE(rightDrawer->position(), 0.0);
738
739 // cannot drag the right drawer while the left drawer is open
740 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - 1, window->height() / 2));
741 QTest::mouseMove(window, pos: QPoint(window->width() - leftDrawer->width() / 2, window->height() / 2));
742 QCOMPARE(leftDrawer->position(), 1.0);
743 QCOMPARE(rightDrawer->position(), 0.0);
744 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - leftDrawer->width() / 2, window->height() / 2));
745 QCOMPARE(rightDrawer->position(), 0.0);
746 QCOMPARE(leftDrawer->position(), 1.0);
747
748 // open the right drawer below the left drawer
749 rightDrawer->open();
750 QTRY_COMPARE(rightDrawer->position(), 1.0);
751
752 // click the left drawer's button
753 QTest::mouseClick(window, button: Qt::LeftButton);
754 QCOMPARE(contentClickSpy.count(), 1);
755 QCOMPARE(leftClickSpy.count(), 1);
756 QCOMPARE(rightClickSpy.count(), 0);
757
758 // click the left drawer's background (button disabled, don't leak through to the right drawer below)
759 leftButton->setEnabled(false);
760 QTest::mouseClick(window, button: Qt::LeftButton);
761 QCOMPARE(contentClickSpy.count(), 1);
762 QCOMPARE(leftClickSpy.count(), 1);
763 QCOMPARE(rightClickSpy.count(), 0);
764 leftButton->setEnabled(true);
765
766 // click the overlay of the left drawer (don't leak through to right drawer below)
767 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - (window->width() - leftDrawer->width()) / 2, window->height() / 2));
768 QCOMPARE(contentClickSpy.count(), 1);
769 QCOMPARE(leftClickSpy.count(), 1);
770 QCOMPARE(rightClickSpy.count(), 0);
771 QTRY_VERIFY(!leftDrawer->isVisible());
772
773 // click the right drawer's button
774 QTest::mouseClick(window, button: Qt::LeftButton);
775 QCOMPARE(contentClickSpy.count(), 1);
776 QCOMPARE(leftClickSpy.count(), 1);
777 QCOMPARE(rightClickSpy.count(), 1);
778
779 // cannot drag the left drawer while the right drawer is open
780 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, window->height() / 2));
781 QTest::mouseMove(window, pos: QPoint(leftDrawer->width() / 2, window->height() / 2));
782 QCOMPARE(leftDrawer->position(), 0.0);
783 QCOMPARE(rightDrawer->position(), 1.0);
784 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(leftDrawer->width() / 2, window->height() / 2));
785 QCOMPARE(leftDrawer->position(), 0.0);
786 QCOMPARE(rightDrawer->position(), 1.0);
787
788 // click the right drawer's background (button disabled, don't leak through to the content below)
789 rightButton->setEnabled(false);
790 QTest::mouseClick(window, button: Qt::LeftButton);
791 QCOMPARE(contentClickSpy.count(), 1);
792 QCOMPARE(leftClickSpy.count(), 1);
793 QCOMPARE(rightClickSpy.count(), 1);
794 rightButton->setEnabled(true);
795
796 // click the overlay of the right drawer (don't leak through to the content below)
797 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint((window->width() - rightDrawer->width()) / 2, window->height() / 2));
798 QCOMPARE(contentClickSpy.count(), 1);
799 QCOMPARE(leftClickSpy.count(), 1);
800 QCOMPARE(rightClickSpy.count(), 1);
801 QTRY_VERIFY(!rightDrawer->isVisible());
802
803 // no drawers open, click the content
804 QTest::mouseClick(window, button: Qt::LeftButton);
805 QCOMPARE(contentClickSpy.count(), 2);
806 QCOMPARE(leftClickSpy.count(), 1);
807 QCOMPARE(rightClickSpy.count(), 1);
808
809 // drag the right drawer open
810 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - 1, window->height() / 2));
811 QTest::mouseMove(window, pos: QPoint(window->width() - rightDrawer->width() / 4, window->height() / 2));
812 QTest::mouseMove(window, pos: QPoint(window->width() - rightDrawer->width() / 4 * 3, window->height() / 2));
813 QCOMPARE(rightDrawer->position(), 0.5);
814 QCOMPARE(leftDrawer->position(), 0.0);
815 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2));
816 QTRY_COMPARE(rightDrawer->position(), 1.0);
817 QCOMPARE(leftDrawer->position(), 0.0);
818}
819
820void tst_QQuickDrawer::touch_data()
821{
822 QTest::addColumn<QString>(name: "source");
823 QTest::addColumn<QPoint>(name: "from");
824 QTest::addColumn<QPoint>(name: "to");
825
826 QTest::newRow(dataTag: "Window:inside") << "window.qml" << QPoint(150, 100) << QPoint(50, 100);
827 QTest::newRow(dataTag: "Window:outside") << "window.qml" << QPoint(300, 100) << QPoint(100, 100);
828 QTest::newRow(dataTag: "ApplicationWindow:inside") << "applicationwindow.qml" << QPoint(150, 100) << QPoint(50, 100);
829 QTest::newRow(dataTag: "ApplicationWindow:outside") << "applicationwindow.qml" << QPoint(300, 100) << QPoint(100, 100);
830
831 QTest::newRow(dataTag: "Window+Button:inside") << "window-button.qml" << QPoint(150, 100) << QPoint(50, 100);
832 QTest::newRow(dataTag: "Window+Button:outside") << "window-button.qml" << QPoint(300, 100) << QPoint(100, 100);
833 QTest::newRow(dataTag: "ApplicationWindow+Button:inside") << "applicationwindow-button.qml" << QPoint(150, 100) << QPoint(50, 100);
834 QTest::newRow(dataTag: "ApplicationWindow+Button:outside") << "applicationwindow-button.qml" << QPoint(300, 100) << QPoint(100, 100);
835}
836
837void tst_QQuickDrawer::touch()
838{
839 QFETCH(QString, source);
840 QFETCH(QPoint, from);
841 QFETCH(QPoint, to);
842
843 QQuickApplicationHelper helper(this, source);
844 QVERIFY2(helper.ready, helper.failureMessage());
845
846 QQuickWindow *window = helper.window;
847 window->show();
848 QVERIFY(QTest::qWaitForWindowExposed(window));
849
850 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
851 QVERIFY(drawer);
852
853 QSignalSpy drawerOpenedSpy(drawer, SIGNAL(opened()));
854 QSignalSpy drawerClosedSpy(drawer, SIGNAL(closed()));
855 QVERIFY(drawerOpenedSpy.isValid());
856 QVERIFY(drawerClosedSpy.isValid());
857
858 // drag to open
859 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: QPoint(0, 100));
860 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(50, 100));
861 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(150, 100));
862 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: QPoint(150, 100));
863 QVERIFY(drawerOpenedSpy.wait());
864 QCOMPARE(drawer->position(), 1.0);
865
866 // drag to close
867 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: from);
868 for (int x = from.x(); x > to.x(); x -= 10)
869 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, to.y()));
870 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: to);
871 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: to);
872 QVERIFY(drawerClosedSpy.wait());
873 QCOMPARE(drawer->position(), 0.0);
874}
875
876void tst_QQuickDrawer::multiTouch()
877{
878 QQuickApplicationHelper helper(this, QStringLiteral("multiTouch.qml"));
879 QVERIFY2(helper.ready, helper.failureMessage());
880 QQuickWindow *window = helper.window;
881 window->show();
882 QVERIFY(QTest::qWaitForWindowActive(window));
883
884 QQuickOverlay *overlay = QQuickOverlay::overlay(window);
885 QVERIFY(overlay);
886
887 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
888 QVERIFY(drawer);
889
890 QQuickPopup *popup = window->property(name: "popup").value<QQuickPopup *>();
891 QVERIFY(popup);
892
893 QQuickButton *button = window->property(name: "button").value<QQuickButton *>();
894 QVERIFY(button);
895
896 QSignalSpy overlayPressedSpy(overlay, SIGNAL(pressed()));
897 QSignalSpy overlayReleasedSpy(overlay, SIGNAL(released()));
898 QVERIFY(overlayPressedSpy.isValid());
899 QVERIFY(overlayReleasedSpy.isValid());
900
901 QSignalSpy drawerOpenedSpy(drawer, SIGNAL(opened()));
902 QVERIFY(drawerOpenedSpy.isValid());
903
904 QSignalSpy buttonPressedSpy(button, SIGNAL(pressed()));
905 QSignalSpy buttonReleasedSpy(button, SIGNAL(released()));
906 QVERIFY(buttonPressedSpy.isValid());
907 QVERIFY(buttonReleasedSpy.isValid());
908
909 popup->open();
910 QVERIFY(popup->isVisible());
911
912 drawer->open();
913 QVERIFY(drawer->isVisible());
914 QVERIFY(drawerOpenedSpy.wait());
915
916 // 1st press
917 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: QPoint(300, 100));
918 QVERIFY(popup->isVisible());
919 QVERIFY(drawer->isVisible());
920 QCOMPARE(buttonPressedSpy.count(), 0);
921 QCOMPARE(overlayPressedSpy.count(), 1);
922
923 // 2nd press (blocked & ignored)
924 QTest::touchEvent(window, device: touchDevice.data()).stationary(touchId: 0).press(touchId: 1, pt: QPoint(300, 200));
925 QVERIFY(popup->isVisible());
926 QVERIFY(drawer->isVisible());
927 QCOMPARE(buttonPressedSpy.count(), 0);
928 QCOMPARE(overlayPressedSpy.count(), 2);
929
930 // 2nd release (blocked & ignored)
931 QTest::touchEvent(window, device: touchDevice.data()).stationary(touchId: 0).release(touchId: 1, pt: QPoint(300, 200));
932 QVERIFY(popup->isVisible());
933 QVERIFY(drawer->isVisible());
934 QCOMPARE(buttonPressedSpy.count(), 0);
935 QCOMPARE(buttonReleasedSpy.count(), 0);
936 QCOMPARE(overlayPressedSpy.count(), 2);
937 QCOMPARE(overlayReleasedSpy.count(), 1);
938
939 // 1st release
940 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: QPoint(300, 100));
941 QVERIFY(popup->isVisible());
942 QTRY_VERIFY(!drawer->isVisible());
943 QCOMPARE(buttonPressedSpy.count(), 0);
944 QCOMPARE(buttonReleasedSpy.count(), 0);
945 QCOMPARE(overlayPressedSpy.count(), 2);
946 QCOMPARE(overlayReleasedSpy.count(), 2);
947
948 drawer->open();
949 QVERIFY(drawer->isVisible());
950 QVERIFY(drawerOpenedSpy.wait());
951
952 // 1st drag
953 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: QPoint(300, 100));
954 QCOMPARE(buttonPressedSpy.count(), 0);
955 QCOMPARE(overlayPressedSpy.count(), 3);
956 for (int x = 300; x >= 100; x -= 10) {
957 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, 100));
958 QVERIFY(popup->isVisible());
959 QVERIFY(drawer->isVisible());
960 }
961 QCOMPARE(drawer->position(), 0.5);
962
963 // 2nd drag (blocked & ignored)
964 QTest::touchEvent(window, device: touchDevice.data()).stationary(touchId: 0).press(touchId: 1, pt: QPoint(300, 200));
965 QCOMPARE(buttonPressedSpy.count(), 0);
966 QCOMPARE(overlayPressedSpy.count(), 4);
967 for (int x = 300; x >= 0; x -= 10) {
968 QTest::touchEvent(window, device: touchDevice.data()).stationary(touchId: 0).move(touchId: 1, pt: QPoint(x, 200));
969 QVERIFY(popup->isVisible());
970 QVERIFY(drawer->isVisible());
971 }
972 QCOMPARE(drawer->position(), 0.5);
973
974 // 2nd release (blocked & ignored)
975 QTest::touchEvent(window, device: touchDevice.data()).stationary(touchId: 0).release(touchId: 1, pt: QPoint(300, 0));
976 QVERIFY(popup->isVisible());
977 QVERIFY(drawer->isVisible());
978 QCOMPARE(drawer->position(), 0.5);
979 QCOMPARE(buttonReleasedSpy.count(), 0);
980 QCOMPARE(overlayReleasedSpy.count(), 3);
981
982 // 1st release
983 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: QPoint(300, 100));
984 QVERIFY(popup->isVisible());
985 QTRY_VERIFY(!drawer->isVisible());
986 QCOMPARE(buttonReleasedSpy.count(), 0);
987 QCOMPARE(overlayReleasedSpy.count(), 4);
988}
989
990void tst_QQuickDrawer::grabber()
991{
992 QQuickApplicationHelper helper(this, QStringLiteral("grabber.qml"));
993 QVERIFY2(helper.ready, helper.failureMessage());
994 QQuickWindow *window = helper.window;
995 window->show();
996 QVERIFY(QTest::qWaitForWindowExposed(window));
997
998 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
999 QVERIFY(drawer);
1000
1001 QSignalSpy drawerOpenedSpy(drawer, SIGNAL(opened()));
1002 QSignalSpy drawerClosedSpy(drawer, SIGNAL(closed()));
1003 QVERIFY(drawerOpenedSpy.isValid());
1004 QVERIFY(drawerClosedSpy.isValid());
1005
1006 drawer->open();
1007 QVERIFY(drawerOpenedSpy.wait());
1008
1009 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(300, 100));
1010 QVERIFY(drawerClosedSpy.wait());
1011
1012 QQuickPopup *popup = window->property(name: "popup").value<QQuickPopup *>();
1013 QVERIFY(popup);
1014
1015 QSignalSpy popupOpenedSpy(popup, SIGNAL(opened()));
1016 QSignalSpy popupClosedSpy(popup, SIGNAL(closed()));
1017 QVERIFY(popupOpenedSpy.isValid());
1018 QVERIFY(popupClosedSpy.isValid());
1019
1020 popup->open();
1021 QTRY_COMPARE(popupOpenedSpy.count(), 1);
1022
1023 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(100, 300));
1024 QTRY_COMPARE(popupClosedSpy.count(), 1);
1025}
1026
1027void tst_QQuickDrawer::interactive_data()
1028{
1029 QTest::addColumn<QString>(name: "source");
1030 QTest::newRow(dataTag: "Window") << "window.qml";
1031 QTest::newRow(dataTag: "ApplicationWindow") << "applicationwindow.qml";
1032}
1033
1034void tst_QQuickDrawer::interactive()
1035{
1036 QFETCH(QString, source);
1037 QQuickApplicationHelper helper(this, source);
1038 QVERIFY2(helper.ready, helper.failureMessage());
1039
1040 QQuickWindow *window = helper.window;
1041 window->show();
1042 QVERIFY(QTest::qWaitForWindowActive(window));
1043
1044 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer*>();
1045 QVERIFY(drawer);
1046
1047 drawer->setInteractive(false);
1048
1049 QSignalSpy openedSpy(drawer, SIGNAL(opened()));
1050 QSignalSpy aboutToHideSpy(drawer, SIGNAL(aboutToHide()));
1051 QVERIFY(openedSpy.isValid());
1052 QVERIFY(aboutToHideSpy.isValid());
1053
1054 drawer->open();
1055 QVERIFY(openedSpy.wait());
1056
1057 // click outside
1058 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(300, 100));
1059 QCOMPARE(aboutToHideSpy.count(), 0);
1060
1061 // drag inside
1062 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(drawer->width(), 0));
1063 QTest::mouseMove(window, pos: QPoint(0, 0));
1064 QCOMPARE(drawer->position(), 1.0);
1065 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, 0));
1066 QCOMPARE(drawer->position(), 1.0);
1067 QCOMPARE(aboutToHideSpy.count(), 0);
1068
1069 // drag outside
1070 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(window->width() - 1, 0));
1071 QTest::mouseMove(window, pos: QPoint(0, 0));
1072 QCOMPARE(drawer->position(), 1.0);
1073 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(0, 0));
1074 QCOMPARE(drawer->position(), 1.0);
1075 QCOMPARE(aboutToHideSpy.count(), 0);
1076
1077 // close on escape
1078 QTest::keyClick(window, key: Qt::Key_Escape);
1079 QCOMPARE(aboutToHideSpy.count(), 0);
1080}
1081
1082void tst_QQuickDrawer::flickable_data()
1083{
1084 QTest::addColumn<bool>(name: "mouse");
1085 QTest::addColumn<QPoint>(name: "from");
1086 QTest::addColumn<QPoint>(name: "to");
1087
1088 QTest::newRow(dataTag: "mouse,straight") << true << QPoint(200, 200) << QPoint(200, 100);
1089 QTest::newRow(dataTag: "mouse,diagonal") << true << QPoint(200, 200) << QPoint(250, 100);
1090 QTest::newRow(dataTag: "touch,straight") << false << QPoint(200, 200) << QPoint(200, 100);
1091 QTest::newRow(dataTag: "touch,diagonal") << false << QPoint(200, 200) << QPoint(250, 100);
1092}
1093
1094void tst_QQuickDrawer::flickable()
1095{
1096 QFETCH(bool, mouse);
1097 QFETCH(QPoint, from);
1098 QFETCH(QPoint, to);
1099
1100 QQuickApplicationHelper helper(this, QStringLiteral("flickable.qml"));
1101 QVERIFY2(helper.ready, helper.failureMessage());
1102 QQuickWindow *window = helper.window;
1103 window->show();
1104 QVERIFY(QTest::qWaitForWindowExposed(window));
1105
1106 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
1107 QVERIFY(drawer);
1108
1109 QQuickFlickable *flickable = window->property(name: "flickable").value<QQuickFlickable *>();
1110 QVERIFY(flickable);
1111
1112 QSignalSpy drawerOpenedSpy(drawer, SIGNAL(opened()));
1113 QVERIFY(drawerOpenedSpy.isValid());
1114
1115 drawer->open();
1116 QVERIFY(drawerOpenedSpy.wait());
1117
1118 if (mouse)
1119 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
1120 else
1121 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: from);
1122
1123 static const int steps = 10;
1124 for (int i = 0; i < steps; ++i) {
1125 int x = i * qAbs(t: from.x() - to.x()) / steps;
1126 int y = i * qAbs(t: from.y() - to.y()) / steps;
1127
1128 if (mouse)
1129 QTest::mouseMove(window, pos: QPoint(x, y));
1130 else
1131 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, y));
1132 QTest::qWait(ms: 1); // avoid infinite velocity
1133 }
1134
1135 QVERIFY(flickable->isDragging());
1136
1137 if (mouse)
1138 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
1139 else
1140 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: to);
1141
1142 QVERIFY(!flickable->isDragging());
1143}
1144
1145void tst_QQuickDrawer::dragOverModalShadow_data()
1146{
1147 QTest::addColumn<bool>(name: "mouse");
1148 QTest::newRow(dataTag: "mouse") << true;
1149 QTest::newRow(dataTag: "touch") << false;
1150}
1151
1152// QTBUG-60602
1153void tst_QQuickDrawer::dragOverModalShadow()
1154{
1155 QFETCH(bool, mouse);
1156
1157 QQuickApplicationHelper helper(this, QStringLiteral("dragOverModalShadow.qml"));
1158 QVERIFY2(helper.ready, helper.failureMessage());
1159 QQuickWindow *window = helper.window;
1160 window->show();
1161 QVERIFY(QTest::qWaitForWindowActive(window));
1162
1163 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
1164 QVERIFY(drawer);
1165
1166 QQuickPopup *popup = window->property(name: "popup").value<QQuickPopup *>();
1167 QVERIFY(popup);
1168
1169 popup->open();
1170 QVERIFY(popup->isVisible());
1171 QVERIFY(!drawer->isVisible());
1172
1173 const QPoint from(popup->x(), popup->y() + popup->height() + 5);
1174 const QPoint to(popup->x() + popup->width(), popup->y() + popup->height() + 5);
1175
1176 if (mouse)
1177 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
1178 else
1179 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: from);
1180 QVERIFY(!drawer->isVisible());
1181
1182 static const int steps = 10;
1183 for (int i = 0; i < steps; ++i) {
1184 int x = from.x() + i * qAbs(t: from.x() - to.x()) / steps;
1185 int y = from.y() + i * qAbs(t: from.y() - to.y()) / steps;
1186
1187 if (mouse)
1188 QTest::mouseMove(window, pos: QPoint(x, y));
1189 else
1190 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, y));
1191 QTest::qWait(ms: 1); // avoid infinite velocity
1192 QVERIFY(!drawer->isVisible());
1193 }
1194
1195 if (mouse)
1196 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
1197 else
1198 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: to);
1199 QVERIFY(!drawer->isVisible());
1200}
1201
1202void tst_QQuickDrawer::nonModal_data()
1203{
1204 QTest::addColumn<bool>(name: "mouse");
1205 QTest::newRow(dataTag: "mouse") << true;
1206 QTest::newRow(dataTag: "touch") << false;
1207}
1208
1209// QTBUG-59652
1210void tst_QQuickDrawer::nonModal()
1211{
1212 QFETCH(bool, mouse);
1213
1214 QQuickApplicationHelper helper(this, QStringLiteral("window.qml"));
1215 QVERIFY2(helper.ready, helper.failureMessage());
1216 QQuickWindow *window = helper.window;
1217 window->show();
1218 QVERIFY(QTest::qWaitForWindowActive(window));
1219
1220 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
1221 QVERIFY(drawer);
1222 drawer->setModal(false);
1223
1224 const QPoint from(1, 1);
1225 const QPoint to(150, 1);
1226
1227 // drag to open
1228 QSignalSpy openedSpy(drawer, SIGNAL(opened()));
1229 QVERIFY(openedSpy.isValid());
1230
1231 if (mouse)
1232 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
1233 else
1234 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: from);
1235
1236 static const int steps = 10;
1237 for (int i = 0; i < steps; ++i) {
1238 int x = i * qAbs(t: from.x() - to.x()) / steps;
1239 int y = i * qAbs(t: from.y() - to.y()) / steps;
1240
1241 if (mouse)
1242 QTest::mouseMove(window, pos: QPoint(x, y));
1243 else
1244 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, y));
1245 QTest::qWait(ms: 1); // avoid infinite velocity
1246 }
1247 QVERIFY(drawer->isVisible());
1248
1249 if (mouse)
1250 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
1251 else
1252 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: to);
1253 QVERIFY(openedSpy.wait());
1254
1255 // drag to close
1256 QSignalSpy closedSpy(drawer, SIGNAL(closed()));
1257 QVERIFY(closedSpy.isValid());
1258
1259 if (mouse)
1260 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
1261 else
1262 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: to);
1263
1264 for (int i = steps - 1; i >= 0; --i) {
1265 int x = i * qAbs(t: from.x() - to.x()) / steps;
1266 int y = i * qAbs(t: from.y() - to.y()) / steps;
1267
1268 if (mouse)
1269 QTest::mouseMove(window, pos: QPoint(x, y));
1270 else
1271 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: QPoint(x, y));
1272 QTest::qWait(ms: 1); // avoid infinite velocity
1273 }
1274 QVERIFY(drawer->isVisible());
1275
1276 if (mouse)
1277 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
1278 else
1279 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: from);
1280 QVERIFY(closedSpy.wait());
1281}
1282
1283void tst_QQuickDrawer::slider_data()
1284{
1285 QTest::addColumn<bool>(name: "mouse");
1286 QTest::addColumn<int>(name: "delta");
1287
1288 QTest::newRow(dataTag: "mouse") << true << 2;
1289 QTest::newRow(dataTag: "touch") << false << 2;
1290 QTest::newRow(dataTag: "mouse,delta") << true << 296 / 8;
1291}
1292
1293void tst_QQuickDrawer::slider()
1294{
1295 QFETCH(bool, mouse);
1296 QFETCH(int, delta);
1297
1298 QQuickApplicationHelper helper(this, QStringLiteral("slider.qml"));
1299 QVERIFY2(helper.ready, helper.failureMessage());
1300 QQuickWindow *window = helper.window;
1301 window->show();
1302 QVERIFY(QTest::qWaitForWindowActive(window));
1303
1304 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
1305 QVERIFY(drawer);
1306
1307 QQuickSlider *slider = window->property(name: "slider").value<QQuickSlider *>();
1308 QVERIFY(slider);
1309
1310 QCOMPARE(slider->value(), 1.0);
1311 QCOMPARE(drawer->position(), 1.0);
1312
1313 const qreal y = slider->height() / 2;
1314 const QPoint from(slider->width() - 1, y);
1315 const QPoint to(1, y);
1316
1317 if (mouse)
1318 QTest::mousePress(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
1319 else
1320 QTest::touchEvent(window, device: touchDevice.data()).press(touchId: 0, pt: from);
1321
1322 int distance = qAbs(t: from.x() - to.x());
1323 for (int dx = delta; dx <= distance; dx += delta) {
1324 if (mouse)
1325 QTest::mouseMove(window, pos: from - QPoint(dx, 0));
1326 else
1327 QTest::touchEvent(window, device: touchDevice.data()).move(touchId: 0, pt: from - QPoint(dx, 0));
1328 QTest::qWait(ms: 1); // avoid infinite velocity
1329 }
1330
1331 QCOMPARE(slider->value(), 0.0);
1332 QCOMPARE(drawer->position(), 1.0);
1333
1334 if (mouse)
1335 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
1336 else
1337 QTest::touchEvent(window, device: touchDevice.data()).release(touchId: 0, pt: to);
1338}
1339
1340void tst_QQuickDrawer::topEdgeScreenEdge()
1341{
1342 QQuickApplicationHelper helper(this, QStringLiteral("topEdgeScreenEdge.qml"));
1343 QVERIFY2(helper.ready, helper.failureMessage());
1344 QQuickWindow *window = helper.window;
1345 window->show();
1346 QVERIFY(QTest::qWaitForWindowActive(window));
1347
1348 QQuickDrawer *drawer = window->property(name: "drawer").value<QQuickDrawer *>();
1349 QVERIFY(drawer);
1350
1351 QVERIFY(QMetaObject::invokeMethod(drawer, "open"));
1352 QTRY_COMPARE(drawer->position(), 1.0);
1353}
1354
1355QTEST_QUICKCONTROLS_MAIN(tst_QQuickDrawer)
1356
1357#include "tst_qquickdrawer.moc"
1358

source code of qtquickcontrols2/tests/auto/qquickdrawer/tst_qquickdrawer.cpp