1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <qtest.h>
30#include <qtesttouch.h>
31#include <QtTest/QSignalSpy>
32#include <QtQml/qqmlcomponent.h>
33#include <QtQml/qqmlcontext.h>
34#include <QtQuick/qquickview.h>
35#include <QtQuick/qquickitem.h>
36#include <QtQuick/private/qquickitem_p.h>
37#include "../../shared/util.h"
38#include <QtGui/QWindow>
39#include <QtGui/QScreen>
40#include <QtGui/QImage>
41#include <QtCore/QDebug>
42#include <QtQml/qqmlengine.h>
43
44#include <QtCore/QLoggingCategory>
45#include <QtGui/qstylehints.h>
46#include <QtWidgets/QBoxLayout>
47#include <QtWidgets/QLabel>
48
49#include <QtQuickWidgets/QQuickWidget>
50
51Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
52
53class MouseRecordingQQWidget : public QQuickWidget
54{
55public:
56 explicit MouseRecordingQQWidget(QWidget *parent = nullptr) : QQuickWidget(parent) {
57 setAttribute(Qt::WA_AcceptTouchEvents);
58 }
59
60protected:
61 void mousePressEvent(QMouseEvent *event) override {
62 qCDebug(lcTests) << event;
63 m_mouseEvents << *event;
64 QQuickWidget::mousePressEvent(event);
65 }
66 void mouseMoveEvent(QMouseEvent *event) override {
67 qCDebug(lcTests) << event;
68 m_mouseEvents << *event;
69 QQuickWidget::mouseMoveEvent(event);
70 }
71 void mouseReleaseEvent(QMouseEvent *event) override {
72 qCDebug(lcTests) << event;
73 m_mouseEvents << *event;
74 QQuickWidget::mouseReleaseEvent(event);
75 }
76
77public:
78 QList<QMouseEvent> m_mouseEvents;
79};
80
81class MouseRecordingItem : public QQuickItem
82{
83public:
84 MouseRecordingItem(bool acceptTouch, QQuickItem *parent = nullptr)
85 : QQuickItem(parent)
86 , m_acceptTouch(acceptTouch)
87 {
88 setSize(QSizeF(300, 300));
89 setAcceptedMouseButtons(Qt::LeftButton);
90 }
91
92protected:
93 void touchEvent(QTouchEvent* event) override {
94 event->setAccepted(m_acceptTouch);
95 m_touchEvents << *event;
96 qCDebug(lcTests) << "accepted?" << event->isAccepted() << event;
97 }
98 void mousePressEvent(QMouseEvent *event) override {
99 qCDebug(lcTests) << event;
100 m_mouseEvents << *event;
101 }
102 void mouseMoveEvent(QMouseEvent *event) override {
103 qCDebug(lcTests) << event;
104 m_mouseEvents << *event;
105 }
106 void mouseReleaseEvent(QMouseEvent *event) override {
107 qCDebug(lcTests) << event;
108 m_mouseEvents << *event;
109 }
110
111public:
112 QList<QMouseEvent> m_mouseEvents;
113 QList<QTouchEvent> m_touchEvents;
114
115private:
116 bool m_acceptTouch;
117};
118
119class tst_qquickwidget : public QQmlDataTest
120{
121 Q_OBJECT
122public:
123 tst_qquickwidget();
124
125private slots:
126 void showHide();
127 void reparentAfterShow();
128 void changeGeometry();
129 void resizemodeitem();
130 void layoutSizeChange();
131 void errors();
132 void engine();
133 void readback();
134 void renderingSignals();
135 void grab();
136 void grabBeforeShow();
137 void reparentToNewWindow();
138 void nullEngine();
139 void keyEvents();
140 void shortcuts();
141 void enterLeave();
142 void mouseEventWindowPos();
143 void synthMouseFromTouch_data();
144 void synthMouseFromTouch();
145 void tabKey();
146 void resizeOverlay();
147
148private:
149 QTouchDevice *device = QTest::createTouchDevice();
150 const QRect m_availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
151};
152
153tst_qquickwidget::tst_qquickwidget()
154{
155}
156
157void tst_qquickwidget::showHide()
158{
159 QWidget window;
160
161 QQuickWidget *childView = new QQuickWidget(&window);
162 childView->setSource(testFileUrl(fileName: "rectangle.qml"));
163
164 window.show();
165 QVERIFY(QTest::qWaitForWindowExposed(&window));
166 QVERIFY(!childView->quickWindow()->isVisible()); // this window is always not visible see QTBUG-65761
167 QVERIFY(childView->quickWindow()->visibility() != QWindow::Hidden);
168
169 window.hide();
170 QVERIFY(!childView->quickWindow()->isVisible());
171 QCOMPARE(childView->quickWindow()->visibility(), QWindow::Hidden);
172}
173
174void tst_qquickwidget::reparentAfterShow()
175{
176 QWidget window;
177
178 QQuickWidget *childView = new QQuickWidget(&window);
179 childView->setSource(testFileUrl(fileName: "rectangle.qml"));
180 window.show();
181 QVERIFY(QTest::qWaitForWindowExposed(&window));
182
183 QScopedPointer<QQuickWidget> toplevelView(new QQuickWidget);
184 toplevelView->setParent(&window);
185 toplevelView->setSource(testFileUrl(fileName: "rectangle.qml"));
186 toplevelView->show();
187 QVERIFY(QTest::qWaitForWindowExposed(&window));
188}
189
190void tst_qquickwidget::changeGeometry()
191{
192 QWidget window;
193
194 QQuickWidget *childView = new QQuickWidget(&window);
195 childView->setSource(testFileUrl(fileName: "rectangle.qml"));
196
197 window.show();
198 QVERIFY(QTest::qWaitForWindowExposed(&window));
199
200 childView->setGeometry(ax: 100,ay: 100,aw: 100,ah: 100);
201}
202
203void tst_qquickwidget::resizemodeitem()
204{
205 QWidget window;
206 window.setGeometry(ax: m_availableGeometry.left(), ay: m_availableGeometry.top(), aw: 400, ah: 400);
207
208 QScopedPointer<QQuickWidget> view(new QQuickWidget);
209 view->setParent(&window);
210 view->setResizeMode(QQuickWidget::SizeRootObjectToView);
211 QCOMPARE(QSize(0,0), view->initialSize());
212 view->setSource(testFileUrl(fileName: "resizemodeitem.qml"));
213 QQuickItem* item = qobject_cast<QQuickItem*>(object: view->rootObject());
214 QVERIFY(item);
215 window.show();
216
217 view->showNormal();
218 // initial size from root object
219 QCOMPARE(item->width(), 200.0);
220 QCOMPARE(item->height(), 200.0);
221 QCOMPARE(view->size(), QSize(200, 200));
222 QCOMPARE(view->size(), view->sizeHint());
223 QCOMPARE(view->size(), view->initialSize());
224
225 // size update from view
226 view->resize(QSize(80,100));
227
228 QTRY_COMPARE(item->width(), 80.0);
229 QCOMPARE(item->height(), 100.0);
230 QCOMPARE(view->size(), QSize(80, 100));
231 QCOMPARE(view->size(), view->sizeHint());
232
233 view->setResizeMode(QQuickWidget::SizeViewToRootObject);
234
235 // size update from view disabled
236 view->resize(QSize(60,80));
237 QCOMPARE(item->width(), 80.0);
238 QCOMPARE(item->height(), 100.0);
239 QTRY_COMPARE(view->size(), QSize(60, 80));
240
241 // size update from root object
242 item->setWidth(250);
243 item->setHeight(350);
244 QCOMPARE(item->width(), 250.0);
245 QCOMPARE(item->height(), 350.0);
246 QTRY_COMPARE(view->size(), QSize(250, 350));
247 QCOMPARE(view->size(), QSize(250, 350));
248 QCOMPARE(view->size(), view->sizeHint());
249
250 // reset window
251 window.hide();
252 view.reset(other: new QQuickWidget(&window));
253 view->setResizeMode(QQuickWidget::SizeViewToRootObject);
254 view->setSource(testFileUrl(fileName: "resizemodeitem.qml"));
255 item = qobject_cast<QQuickItem*>(object: view->rootObject());
256 QVERIFY(item);
257 window.show();
258
259 view->showNormal();
260
261 // initial size for root object
262 QCOMPARE(item->width(), 200.0);
263 QCOMPARE(item->height(), 200.0);
264 QCOMPARE(view->size(), view->sizeHint());
265 QCOMPARE(view->size(), view->initialSize());
266
267 // size update from root object
268 item->setWidth(80);
269 item->setHeight(100);
270 QCOMPARE(item->width(), 80.0);
271 QCOMPARE(item->height(), 100.0);
272 QTRY_COMPARE(view->size(), QSize(80, 100));
273 QCOMPARE(view->size(), view->sizeHint());
274
275 // size update from root object disabled
276 view->setResizeMode(QQuickWidget::SizeRootObjectToView);
277 item->setWidth(60);
278 item->setHeight(80);
279 QCOMPARE(view->width(), 80);
280 QCOMPARE(view->height(), 100);
281 QCOMPARE(QSize(item->width(), item->height()), view->sizeHint());
282
283 // size update from view
284 view->resize(QSize(200,300));
285 QTRY_COMPARE(item->width(), 200.0);
286 QCOMPARE(item->height(), 300.0);
287 QCOMPARE(view->size(), QSize(200, 300));
288 QCOMPARE(view->size(), view->sizeHint());
289
290 window.hide();
291
292 // if we set a specific size for the view then it should keep that size
293 // for SizeRootObjectToView mode.
294 view.reset(other: new QQuickWidget(&window));
295 view->resize(w: 300, h: 300);
296 view->setResizeMode(QQuickWidget::SizeRootObjectToView);
297 QCOMPARE(QSize(0,0), view->initialSize());
298 view->setSource(testFileUrl(fileName: "resizemodeitem.qml"));
299 view->resize(w: 300, h: 300);
300 item = qobject_cast<QQuickItem*>(object: view->rootObject());
301 QVERIFY(item);
302 window.show();
303
304 view->showNormal();
305
306 // initial size from root object
307 QCOMPARE(item->width(), 300.0);
308 QCOMPARE(item->height(), 300.0);
309 QTRY_COMPARE(view->size(), QSize(300, 300));
310 QCOMPARE(view->size(), view->sizeHint());
311 QCOMPARE(view->initialSize(), QSize(200, 200)); // initial object size
312}
313
314void tst_qquickwidget::layoutSizeChange()
315{
316 QWidget window;
317 window.resize(w: 400, h: 400);
318
319 QVBoxLayout *layout = new QVBoxLayout(&window);
320 layout->setContentsMargins(left: 0,top: 0,right: 0,bottom: 0);
321 layout->setSpacing(0);
322 QScopedPointer<QQuickWidget> view(new QQuickWidget);
323 layout->addWidget(view.data());
324 QLabel *label = new QLabel("Label");
325 layout->addWidget(label);
326 layout->addStretch(stretch: 1);
327
328
329 view->resize(w: 300,h: 300);
330 view->setResizeMode(QQuickWidget::SizeViewToRootObject);
331 QCOMPARE(QSize(0,0), view->initialSize());
332 view->setSource(testFileUrl(fileName: "rectangle.qml"));
333 QQuickItem* item = qobject_cast<QQuickItem*>(object: view->rootObject());
334 QVERIFY(item);
335 QCOMPARE(item->height(), 200.0);
336 window.show();
337 QVERIFY(QTest::qWaitForWindowExposed(&window, 5000));
338 QTRY_COMPARE(view->height(), 200);
339 QTRY_COMPARE(label->y(), 200);
340
341 item->setSize(QSizeF(100,100));
342 QCOMPARE(item->height(), 100.0);
343 QTRY_COMPARE(view->height(), 100);
344 QTRY_COMPARE(label->y(), 100);
345}
346
347void tst_qquickwidget::errors()
348{
349 QQuickWidget *view = new QQuickWidget;
350 QScopedPointer<QQuickWidget> cleanupView(view);
351 QVERIFY(view->errors().isEmpty()); // don't crash
352
353 QQmlTestMessageHandler messageHandler;
354 view->setSource(testFileUrl(fileName: "error1.qml"));
355 QCOMPARE(view->status(), QQuickWidget::Error);
356 QCOMPARE(view->errors().count(), 1);
357}
358
359void tst_qquickwidget::engine()
360{
361 QScopedPointer<QQmlEngine> engine(new QQmlEngine);
362 QScopedPointer<QQuickWidget> view(new QQuickWidget(engine.data(), nullptr));
363 QScopedPointer<QQuickWidget> view2(new QQuickWidget(view->engine(), nullptr));
364
365 QVERIFY(view->engine());
366 QVERIFY(view2->engine());
367 QCOMPARE(view->engine(), view2->engine());
368}
369
370void tst_qquickwidget::readback()
371{
372 QWidget window;
373
374 QScopedPointer<QQuickWidget> view(new QQuickWidget);
375 view->setSource(testFileUrl(fileName: "rectangle.qml"));
376
377 view->show();
378 QVERIFY(QTest::qWaitForWindowExposed(view.data()));
379
380 QImage img = view->grabFramebuffer();
381 QVERIFY(!img.isNull());
382 QCOMPARE(img.width(), view->width());
383 QCOMPARE(img.height(), view->height());
384
385 QRgb pix = img.pixel(x: 5, y: 5);
386 QCOMPARE(pix, qRgb(255, 0, 0));
387}
388
389void tst_qquickwidget::renderingSignals()
390{
391 QQuickWidget widget;
392 QQuickWindow *window = widget.quickWindow();
393 QVERIFY(window);
394
395 QSignalSpy beforeRenderingSpy(window, &QQuickWindow::beforeRendering);
396 QSignalSpy beforeSyncSpy(window, &QQuickWindow::beforeSynchronizing);
397 QSignalSpy afterRenderingSpy(window, &QQuickWindow::afterRendering);
398
399 QVERIFY(beforeRenderingSpy.isValid());
400 QVERIFY(beforeSyncSpy.isValid());
401 QVERIFY(afterRenderingSpy.isValid());
402
403 QCOMPARE(beforeRenderingSpy.size(), 0);
404 QCOMPARE(beforeSyncSpy.size(), 0);
405 QCOMPARE(afterRenderingSpy.size(), 0);
406
407 widget.setSource(testFileUrl(fileName: "rectangle.qml"));
408
409 QCOMPARE(beforeRenderingSpy.size(), 0);
410 QCOMPARE(beforeSyncSpy.size(), 0);
411 QCOMPARE(afterRenderingSpy.size(), 0);
412
413 widget.show();
414 QVERIFY(QTest::qWaitForWindowExposed(&widget));
415
416 QTRY_VERIFY(beforeRenderingSpy.size() > 0);
417 QTRY_VERIFY(beforeSyncSpy.size() > 0);
418 QTRY_VERIFY(afterRenderingSpy.size() > 0);
419}
420
421void tst_qquickwidget::grab()
422{
423 QQuickWidget view;
424 view.setSource(testFileUrl(fileName: "rectangle.qml"));
425 QPixmap pixmap = view.grab();
426 QRgb pixel = pixmap.toImage().pixel(x: 5, y: 5);
427 QCOMPARE(pixel, qRgb(255, 0, 0));
428}
429
430// QTBUG-49929, verify that Qt Designer grabbing the contents before drag
431// does not crash due to missing GL contexts or similar.
432void tst_qquickwidget::grabBeforeShow()
433{
434 QQuickWidget widget;
435 QVERIFY(!widget.grab().isNull());
436}
437
438void tst_qquickwidget::reparentToNewWindow()
439{
440 QWidget window1;
441 QWidget window2;
442
443 QQuickWidget *qqw = new QQuickWidget(&window1);
444 qqw->setSource(testFileUrl(fileName: "rectangle.qml"));
445 window1.show();
446 QVERIFY(QTest::qWaitForWindowExposed(&window1));
447 window2.show();
448 QVERIFY(QTest::qWaitForWindowExposed(&window2));
449
450 QSignalSpy afterRenderingSpy(qqw->quickWindow(), &QQuickWindow::afterRendering);
451 qqw->setParent(&window2);
452 qqw->show();
453
454 QTRY_VERIFY(afterRenderingSpy.size() > 0);
455
456 QImage img = qqw->grabFramebuffer();
457 QCOMPARE(img.pixel(5, 5), qRgb(255, 0, 0));
458}
459
460void tst_qquickwidget::nullEngine()
461{
462 QQuickWidget widget;
463 // Default should have no errors, even with a null qml engine
464 QVERIFY(widget.errors().isEmpty());
465 QCOMPARE(widget.status(), QQuickWidget::Null);
466
467 // A QML engine should be created lazily.
468 QVERIFY(widget.rootContext());
469 QVERIFY(widget.engine());
470}
471
472class KeyHandlingWidget : public QQuickWidget
473{
474public:
475 void keyPressEvent(QKeyEvent *e) override {
476 if (e->key() == Qt::Key_A)
477 ok = true;
478 }
479
480 bool ok = false;
481};
482
483void tst_qquickwidget::keyEvents()
484{
485 // A QQuickWidget should behave like a normal widget when it comes to event handling.
486 // Verify that key events actually reach the widget. (QTBUG-45757)
487 KeyHandlingWidget widget;
488 widget.setSource(testFileUrl(fileName: "rectangle.qml"));
489 widget.show();
490 QVERIFY(QTest::qWaitForWindowExposed(widget.window()));
491
492 // Note: send the event to the QWindow, not the QWidget, in order
493 // to simulate the full event processing chain.
494 QTest::keyClick(window: widget.window()->windowHandle(), key: Qt::Key_A);
495
496 QTRY_VERIFY(widget.ok);
497}
498
499class ShortcutEventFilter : public QObject
500{
501public:
502 bool eventFilter(QObject *obj, QEvent *e) override {
503 if (e->type() == QEvent::ShortcutOverride)
504 shortcutOk = true;
505
506 return QObject::eventFilter(watched: obj, event: e);
507 }
508
509 bool shortcutOk = false;
510};
511
512void tst_qquickwidget::shortcuts()
513{
514 // Verify that ShortcutOverride events do not get lost. (QTBUG-60988)
515 KeyHandlingWidget widget;
516 widget.setSource(testFileUrl(fileName: "rectangle.qml"));
517 widget.show();
518 QVERIFY(QTest::qWaitForWindowExposed(widget.window()));
519
520 // Send to the widget, verify that the QQuickWindow sees it.
521
522 ShortcutEventFilter filter;
523 widget.quickWindow()->installEventFilter(filterObj: &filter);
524
525 QKeyEvent e(QEvent::ShortcutOverride, Qt::Key_A, Qt::ControlModifier);
526 QCoreApplication::sendEvent(receiver: &widget, event: &e);
527
528 QTRY_VERIFY(filter.shortcutOk);
529}
530
531void tst_qquickwidget::enterLeave()
532{
533 QQuickWidget view;
534 view.setSource(testFileUrl(fileName: "enterleave.qml"));
535
536 // Ensure the cursor is away from the window first
537 const auto outside = m_availableGeometry.topLeft() + QPoint(50, 50);
538 QCursor::setPos(outside);
539 QTRY_VERIFY(QCursor::pos() == outside);
540
541 view.move(m_availableGeometry.topLeft() + QPoint(100, 100));
542 view.show();
543 QVERIFY(QTest::qWaitForWindowExposed(&view));
544 QQuickItem *rootItem = view.rootObject();
545 QVERIFY(rootItem);
546 const QPoint frameOffset = view.geometry().topLeft() - view.frameGeometry().topLeft();
547
548 QTRY_VERIFY(!rootItem->property("hasMouse").toBool());
549 // Check the enter
550 QCursor::setPos(view.pos() + QPoint(50, 50) + frameOffset);
551 QTRY_VERIFY(rootItem->property("hasMouse").toBool());
552 // Now check the leave
553 QCursor::setPos(outside);
554 QTRY_VERIFY(!rootItem->property("hasMouse").toBool());
555}
556
557void tst_qquickwidget::mouseEventWindowPos()
558{
559 QWidget widget;
560 widget.resize(w: 100, h: 100);
561 QQuickWidget *quick = new QQuickWidget(&widget);
562 quick->setSource(testFileUrl(fileName: "mouse.qml"));
563 quick->move(ax: 50, ay: 50);
564 widget.show();
565 QVERIFY(QTest::qWaitForWindowExposed(&widget));
566 QQuickItem *rootItem = quick->rootObject();
567 QVERIFY(rootItem);
568
569 QVERIFY(!rootItem->property("wasClicked").toBool());
570 QVERIFY(!rootItem->property("wasDoubleClicked").toBool());
571 // Moving an item under the mouse cursor will trigger a mouse move event.
572 // The above quick->move() will trigger a mouse move event on macOS.
573 // Discard that in order to get a clean slate for the actual tests.
574 rootItem->setProperty(name: "wasMoved", value: QVariant(false));
575
576 QWindow *window = widget.windowHandle();
577 QVERIFY(window);
578
579 QTest::mouseMove(window, pos: QPoint(60, 60));
580 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: QPoint(60, 60));
581 QTRY_VERIFY(rootItem->property("wasClicked").toBool());
582 QTest::mouseDClick(window, button: Qt::LeftButton, stateKey: Qt::KeyboardModifiers(), pos: QPoint(60, 60));
583 QTRY_VERIFY(rootItem->property("wasDoubleClicked").toBool());
584 QTest::mouseMove(window, pos: QPoint(70, 70));
585 QTRY_VERIFY(rootItem->property("wasMoved").toBool());
586}
587
588void tst_qquickwidget::synthMouseFromTouch_data()
589{
590 QTest::addColumn<bool>(name: "synthMouse"); // AA_SynthesizeMouseForUnhandledTouchEvents
591 QTest::addColumn<bool>(name: "acceptTouch"); // QQuickItem::touchEvent: setAccepted()
592
593 QTest::newRow(dataTag: "no synth, accept") << false << true; // suitable for touch-capable UIs
594 QTest::newRow(dataTag: "no synth, don't accept") << false << false;
595 QTest::newRow(dataTag: "synth and accept") << true << true;
596 QTest::newRow(dataTag: "synth, don't accept") << true << false; // the default
597}
598
599void tst_qquickwidget::synthMouseFromTouch()
600{
601 QFETCH(bool, synthMouse);
602 QFETCH(bool, acceptTouch);
603
604 QCoreApplication::setAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTouchEvents, on: synthMouse);
605 QWidget window;
606 window.setAttribute(Qt::WA_AcceptTouchEvents);
607 QScopedPointer<MouseRecordingQQWidget> childView(new MouseRecordingQQWidget(&window));
608 MouseRecordingItem *item = new MouseRecordingItem(acceptTouch, nullptr);
609 childView->setContent(url: QUrl(), component: nullptr, item);
610 window.resize(w: 300, h: 300);
611 childView->resize(w: 300, h: 300);
612 window.show();
613 QVERIFY(QTest::qWaitForWindowActive(&window));
614 QVERIFY(!childView->quickWindow()->isVisible()); // this window is always not visible see QTBUG-65761
615 QVERIFY(item->isVisible());
616
617 QPoint p1 = QPoint(20, 20);
618 QPoint p2 = QPoint(30, 30);
619 QTest::touchEvent(widget: &window, device).press(touchId: 0, pt: p1, widget: &window);
620 QTest::touchEvent(widget: &window, device).move(touchId: 0, pt: p2, widget: &window);
621 QTest::touchEvent(widget: &window, device).release(touchId: 0, pt: p2, widget: &window);
622
623 QCOMPARE(item->m_touchEvents.count(), !synthMouse && !acceptTouch ? 1 : 3);
624 QCOMPARE(item->m_mouseEvents.count(), (acceptTouch || !synthMouse) ? 0 : 3);
625 QCOMPARE(childView->m_mouseEvents.count(), 0);
626 for (const QMouseEvent &ev : item->m_mouseEvents)
627 QCOMPARE(ev.source(), Qt::MouseEventSynthesizedByQt);
628}
629
630void tst_qquickwidget::tabKey()
631{
632 if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
633 QSKIP("This function doesn't support NOT iterating all.");
634 QWidget window1;
635 QQuickWidget *qqw = new QQuickWidget(&window1);
636 qqw->setSource(testFileUrl(fileName: "activeFocusOnTab.qml"));
637 QQuickWidget *qqw2 = new QQuickWidget(&window1);
638 qqw2->setSource(testFileUrl(fileName: "noActiveFocusOnTab.qml"));
639 qqw2->move(ax: 100, ay: 0);
640 window1.show();
641 qqw->setFocus();
642 QVERIFY(QTest::qWaitForWindowExposed(&window1, 5000));
643 QVERIFY(qqw->hasFocus());
644 QQuickItem *item = qobject_cast<QQuickItem *>(object: qqw->rootObject());
645 QQuickItem *topItem = item->findChild<QQuickItem *>(aName: "topRect");
646 QQuickItem *middleItem = item->findChild<QQuickItem *>(aName: "middleRect");
647 QQuickItem *bottomItem = item->findChild<QQuickItem *>(aName: "bottomRect");
648 topItem->forceActiveFocus();
649 QVERIFY(topItem->property("activeFocus").toBool());
650 QTest::keyClick(widget: qqw, key: Qt::Key_Tab);
651 QTRY_VERIFY(middleItem->property("activeFocus").toBool());
652 QTest::keyClick(widget: qqw, key: Qt::Key_Tab);
653 QTRY_VERIFY(bottomItem->property("activeFocus").toBool());
654 QTest::keyClick(widget: qqw, key: Qt::Key_Backtab);
655 QTRY_VERIFY(middleItem->property("activeFocus").toBool());
656
657 qqw2->setFocus();
658 QQuickItem *item2 = qobject_cast<QQuickItem *>(object: qqw2->rootObject());
659 QQuickItem *topItem2 = item2->findChild<QQuickItem *>(aName: "topRect2");
660 QTRY_VERIFY(qqw2->hasFocus());
661 QVERIFY(topItem2->property("activeFocus").toBool());
662 QTest::keyClick(widget: qqw2, key: Qt::Key_Tab);
663 QTRY_VERIFY(qqw->hasFocus());
664 QVERIFY(middleItem->property("activeFocus").toBool());
665}
666
667class Overlay : public QQuickItem, public QQuickItemChangeListener
668{
669 Q_OBJECT
670
671public:
672 Overlay() = default;
673
674 ~Overlay()
675 {
676 QQuickItemPrivate::get(item: parentItem())->removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
677 }
678
679 // componentCompleted() is too early to add the listener, as parentItem()
680 // is still null by that stage, so we use this function instead.
681 void startListening()
682 {
683 QQuickItemPrivate::get(item: parentItem())->addItemChangeListener(listener: this, types: QQuickItemPrivate::Geometry);
684 }
685
686private:
687 virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &/*oldGeometry*/) override
688 {
689 auto window = QQuickItemPrivate::get(item: this)->window;
690 if (!window)
691 return;
692
693 setSize(window->size());
694 }
695};
696
697// Test that an item that resizes itself based on the window size can use a
698// Geometry item change listener to respond to changes in size. This is a
699// simplified test to mimic a use case involving Overlay from Qt Quick Controls 2.
700void tst_qquickwidget::resizeOverlay()
701{
702 QWidget widget;
703 auto contentVerticalLayout = new QVBoxLayout(&widget);
704 contentVerticalLayout->setMargin(0);
705
706 qmlRegisterType<Overlay>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "Overlay");
707
708 auto quickWidget = new QQuickWidget(testFileUrl(fileName: "resizeOverlay.qml"), &widget);
709 QCOMPARE(quickWidget->status(), QQuickWidget::Ready);
710 quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
711 contentVerticalLayout->addWidget(quickWidget);
712
713 auto rootItem = qobject_cast<QQuickItem*>(object: quickWidget->rootObject());
714 QVERIFY(rootItem);
715
716 auto overlay = rootItem->property(name: "overlay").value<Overlay*>();
717 QVERIFY(overlay);
718 QVERIFY(overlay->parentItem());
719 overlay->startListening();
720
721 widget.resize(w: 200, h: 200);
722 widget.show();
723 QCOMPARE(rootItem->width(), 200);
724 QCOMPARE(rootItem->height(), 200);
725 QCOMPARE(overlay->width(), rootItem->width());
726 QCOMPARE(overlay->height(), rootItem->height());
727
728 widget.resize(w: 300, h: 300);
729 QCOMPARE(rootItem->width(), 300);
730 QCOMPARE(rootItem->height(), 300);
731 QCOMPARE(overlay->width(), rootItem->width());
732 QCOMPARE(overlay->height(), rootItem->height());
733}
734
735QTEST_MAIN(tst_qquickwidget)
736
737#include "tst_qquickwidget.moc"
738

source code of qtdeclarative/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp