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
31#include <QtQuick/qquickitem.h>
32#include <QtQuick/qquickwindow.h>
33#include <QtQuick/qquickview.h>
34#include "private/qquickfocusscope_p.h"
35#include "private/qquickitem_p.h"
36#include <qpa/qwindowsysteminterface.h>
37#include <QDebug>
38#include <QTimer>
39#include <QQmlEngine>
40#include "../../shared/util.h"
41#include "../shared/viewtestutil.h"
42#include <QSignalSpy>
43#include <QTranslator>
44#include <QtCore/qregularexpression.h>
45
46#ifdef TEST_QTBUG_60123
47#include <QWidget>
48#include <QMainWindow>
49#endif
50
51class TestItem : public QQuickItem
52{
53Q_OBJECT
54public:
55 TestItem(QQuickItem *parent = nullptr)
56 : QQuickItem(parent), focused(false), pressCount(0), releaseCount(0)
57 , wheelCount(0), acceptIncomingTouchEvents(true)
58 , touchEventReached(false), timestamp(0)
59 , lastWheelEventPos(0, 0), lastWheelEventGlobalPos(0, 0)
60 {
61#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
62 setAcceptTouchEvents(true);
63#endif
64 }
65
66 bool focused;
67 int pressCount;
68 int releaseCount;
69 int wheelCount;
70 bool acceptIncomingTouchEvents;
71 bool touchEventReached;
72 ulong timestamp;
73 QPoint lastWheelEventPos;
74 QPoint lastWheelEventGlobalPos;
75 int languageChangeEventCount = 0;
76protected:
77 void focusInEvent(QFocusEvent *) override { Q_ASSERT(!focused); focused = true; }
78 void focusOutEvent(QFocusEvent *) override { Q_ASSERT(focused); focused = false; }
79 void mousePressEvent(QMouseEvent *event) override { event->accept(); ++pressCount; }
80 void mouseReleaseEvent(QMouseEvent *event) override { event->accept(); ++releaseCount; }
81 void touchEvent(QTouchEvent *event) override {
82 touchEventReached = true;
83 event->setAccepted(acceptIncomingTouchEvents);
84 }
85 void wheelEvent(QWheelEvent *event) override {
86 event->accept();
87 ++wheelCount;
88 timestamp = event->timestamp();
89 lastWheelEventPos = event->position().toPoint();
90 lastWheelEventGlobalPos = event->globalPosition().toPoint();
91 }
92 bool event(QEvent *e) override
93 {
94 if (e->type() == QEvent::LanguageChange)
95 languageChangeEventCount++;
96 return QQuickItem::event(e);
97 }
98};
99
100class TestWindow: public QQuickWindow
101{
102public:
103 TestWindow()
104 : QQuickWindow()
105 {}
106
107 virtual bool event(QEvent *event)
108 {
109 return QQuickWindow::event(event);
110 }
111};
112
113class TestPolishItem : public QQuickItem
114{
115Q_OBJECT
116public:
117 TestPolishItem(QQuickItem *parent = nullptr)
118 : QQuickItem(parent), wasPolished(false) {
119
120 }
121
122 bool wasPolished;
123 int repolishLoopCount = 0;
124
125protected:
126 virtual void updatePolish() {
127 wasPolished = true;
128 if (repolishLoopCount > 0) {
129 --repolishLoopCount;
130 polish();
131 }
132 }
133
134public slots:
135 void doPolish() {
136 polish();
137 }
138};
139
140class TestFocusScope : public QQuickFocusScope
141{
142Q_OBJECT
143public:
144 TestFocusScope(QQuickItem *parent = nullptr) : QQuickFocusScope(parent), focused(false) {}
145
146 bool focused;
147protected:
148 virtual void focusInEvent(QFocusEvent *) { Q_ASSERT(!focused); focused = true; }
149 virtual void focusOutEvent(QFocusEvent *) { Q_ASSERT(focused); focused = false; }
150};
151
152class tst_qquickitem : public QQmlDataTest
153{
154 Q_OBJECT
155public:
156
157private slots:
158 void initTestCase();
159
160 void noWindow();
161 void simpleFocus();
162 void scopedFocus();
163 void addedToWindow();
164 void changeParent();
165 void multipleFocusClears();
166 void focusSubItemInNonFocusScope();
167 void parentItemWithFocus();
168 void reparentFocusedItem();
169
170 void constructor();
171 void setParentItem();
172
173 void visible();
174 void enabled();
175 void enabledFocus();
176
177 void mouseGrab();
178 void touchEventAcceptIgnore_data();
179 void touchEventAcceptIgnore();
180 void polishOutsideAnimation();
181 void polishOnCompleted();
182
183 void wheelEvent_data();
184 void wheelEvent();
185 void hoverEvent_data();
186 void hoverEvent();
187 void hoverEventInParent();
188
189 void paintOrder_data();
190 void paintOrder();
191
192 void acceptedMouseButtons();
193
194 void visualParentOwnership();
195 void visualParentOwnershipWindow();
196
197 void testSGInvalidate();
198
199 void objectChildTransform();
200
201 void contains_data();
202 void contains();
203
204 void childAt();
205
206 void ignoreButtonPressNotInAcceptedMouseButtons();
207
208 void shortcutOverride();
209
210#ifdef TEST_QTBUG_60123
211 void qtBug60123();
212#endif
213
214 void setParentCalledInOnWindowChanged();
215 void receivesLanguageChangeEvent();
216 void polishLoopDetection_data();
217 void polishLoopDetection();
218
219private:
220
221 enum PaintOrderOp {
222 NoOp, Append, Remove, StackBefore, StackAfter, SetZ
223 };
224
225 bool ensureFocus(QWindow *w) {
226 if (w->width() <=0 || w->height() <= 0)
227 w->setGeometry(posx: 100, posy: 100, w: 400, h: 300);
228 w->show();
229 w->requestActivate();
230 return QTest::qWaitForWindowActive(window: w);
231 }
232};
233
234void tst_qquickitem::initTestCase()
235{
236 QQmlDataTest::initTestCase();
237 qmlRegisterType<TestPolishItem>(uri: "Qt.test", versionMajor: 1, versionMinor: 0, qmlName: "TestPolishItem");
238}
239
240// Focus still updates when outside a window
241void tst_qquickitem::noWindow()
242{
243 QQuickItem *root = new TestItem;
244 QQuickItem *child = new TestItem(root);
245 QQuickItem *scope = new TestItem(root);
246 QQuickFocusScope *scopedChild = new TestFocusScope(scope);
247 QQuickFocusScope *scopedChild2 = new TestFocusScope(scope);
248
249 QCOMPARE(root->hasFocus(), false);
250 QCOMPARE(child->hasFocus(), false);
251 QCOMPARE(scope->hasFocus(), false);
252 QCOMPARE(scopedChild->hasFocus(), false);
253 QCOMPARE(scopedChild2->hasFocus(), false);
254
255 root->setFocus(true);
256 scope->setFocus(true);
257 scopedChild2->setFocus(true);
258 QCOMPARE(root->hasFocus(), false);
259 QCOMPARE(child->hasFocus(), false);
260 QCOMPARE(scope->hasFocus(), false);
261 QCOMPARE(scopedChild->hasFocus(), false);
262 QCOMPARE(scopedChild2->hasFocus(), true);
263
264 root->setFocus(false);
265 child->setFocus(true);
266 scopedChild->setFocus(true);
267 scope->setFocus(false);
268 QCOMPARE(root->hasFocus(), false);
269 QCOMPARE(child->hasFocus(), false);
270 QCOMPARE(scope->hasFocus(), false);
271 QCOMPARE(scopedChild->hasFocus(), true);
272 QCOMPARE(scopedChild2->hasFocus(), false);
273
274 delete root;
275}
276
277struct FocusData {
278 FocusData() : focus(false), activeFocus(false) {}
279
280 void set(bool f, bool af) { focus = f; activeFocus = af; }
281 bool focus;
282 bool activeFocus;
283};
284struct FocusState : public QHash<QQuickItem *, FocusData>
285{
286 FocusState() : activeFocusItem(nullptr) {}
287 FocusState &operator<<(QQuickItem *item) {
288 insert(key: item, value: FocusData());
289 return *this;
290 }
291
292 void active(QQuickItem *i) {
293 activeFocusItem = i;
294 }
295 QQuickItem *activeFocusItem;
296};
297
298#define FVERIFY() \
299 do { \
300 if (focusState.activeFocusItem) { \
301 QCOMPARE(window.activeFocusItem(), focusState.activeFocusItem); \
302 if (qobject_cast<TestItem *>(window.activeFocusItem())) \
303 QCOMPARE(qobject_cast<TestItem *>(window.activeFocusItem())->focused, true); \
304 else if (qobject_cast<TestFocusScope *>(window.activeFocusItem())) \
305 QCOMPARE(qobject_cast<TestFocusScope *>(window.activeFocusItem())->focused, true); \
306 } else { \
307 QCOMPARE(window.activeFocusItem(), window.contentItem()); \
308 } \
309 for (QHash<QQuickItem *, FocusData>::Iterator iter = focusState.begin(); \
310 iter != focusState.end(); \
311 iter++) { \
312 QCOMPARE(iter.key()->hasFocus(), iter.value().focus); \
313 QCOMPARE(iter.key()->hasActiveFocus(), iter.value().activeFocus); \
314 } \
315 } while (false)
316
317// Tests a simple set of top-level scoped items
318void tst_qquickitem::simpleFocus()
319{
320 QQuickWindow window;
321 QVERIFY(ensureFocus(&window));
322
323 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
324
325 QQuickItem *l1c1 = new TestItem(window.contentItem());
326 QQuickItem *l1c2 = new TestItem(window.contentItem());
327 QQuickItem *l1c3 = new TestItem(window.contentItem());
328
329 QQuickItem *l2c1 = new TestItem(l1c1);
330 QQuickItem *l2c2 = new TestItem(l1c1);
331 QQuickItem *l2c3 = new TestItem(l1c3);
332
333 FocusState focusState;
334 focusState << l1c1 << l1c2 << l1c3
335 << l2c1 << l2c2 << l2c3;
336 FVERIFY();
337
338 l1c1->setFocus(true);
339 focusState[l1c1].set(f: true, af: true);
340 focusState.active(i: l1c1);
341 FVERIFY();
342
343 l2c3->setFocus(true);
344 focusState[l1c1].set(f: false, af: false);
345 focusState[l2c3].set(f: true, af: true);
346 focusState.active(i: l2c3);
347 FVERIFY();
348
349 l1c3->setFocus(true);
350 focusState[l2c3].set(f: false, af: false);
351 focusState[l1c3].set(f: true, af: true);
352 focusState.active(i: l1c3);
353 FVERIFY();
354
355 l1c2->setFocus(false);
356 FVERIFY();
357
358 l1c3->setFocus(false);
359 focusState[l1c3].set(f: false, af: false);
360 focusState.active(i: nullptr);
361 FVERIFY();
362
363 l2c1->setFocus(true);
364 focusState[l2c1].set(f: true, af: true);
365 focusState.active(i: l2c1);
366 FVERIFY();
367}
368
369// Items with a focus scope
370void tst_qquickitem::scopedFocus()
371{
372 QQuickWindow window;
373 QVERIFY(ensureFocus(&window));
374 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
375
376 QQuickItem *l1c1 = new TestItem(window.contentItem());
377 QQuickItem *l1c2 = new TestItem(window.contentItem());
378 QQuickItem *l1c3 = new TestItem(window.contentItem());
379
380 QQuickItem *l2c1 = new TestItem(l1c1);
381 QQuickItem *l2c2 = new TestItem(l1c1);
382 QQuickItem *l2c3 = new TestFocusScope(l1c3);
383
384 QQuickItem *l3c1 = new TestItem(l2c3);
385 QQuickItem *l3c2 = new TestFocusScope(l2c3);
386
387 QQuickItem *l4c1 = new TestItem(l3c2);
388 QQuickItem *l4c2 = new TestItem(l3c2);
389
390 FocusState focusState;
391 focusState << l1c1 << l1c2 << l1c3
392 << l2c1 << l2c2 << l2c3
393 << l3c1 << l3c2
394 << l4c1 << l4c2;
395 FVERIFY();
396
397 l4c2->setFocus(true);
398 focusState[l4c2].set(f: true, af: false);
399 FVERIFY();
400
401 l4c1->setFocus(true);
402 focusState[l4c2].set(f: false, af: false);
403 focusState[l4c1].set(f: true, af: false);
404 FVERIFY();
405
406 l1c1->setFocus(true);
407 focusState[l1c1].set(f: true, af: true);
408 focusState.active(i: l1c1);
409 FVERIFY();
410
411 l3c2->setFocus(true);
412 focusState[l3c2].set(f: true, af: false);
413 FVERIFY();
414
415 l2c3->setFocus(true);
416 focusState[l1c1].set(f: false, af: false);
417 focusState[l2c3].set(f: true, af: true);
418 focusState[l3c2].set(f: true, af: true);
419 focusState[l4c1].set(f: true, af: true);
420 focusState.active(i: l4c1);
421 FVERIFY();
422
423 l3c2->setFocus(false);
424 focusState[l3c2].set(f: false, af: false);
425 focusState[l4c1].set(f: true, af: false);
426 focusState.active(i: l2c3);
427 FVERIFY();
428
429 l3c2->setFocus(true);
430 focusState[l3c2].set(f: true, af: true);
431 focusState[l4c1].set(f: true, af: true);
432 focusState.active(i: l4c1);
433 FVERIFY();
434
435 l4c1->setFocus(false);
436 focusState[l4c1].set(f: false, af: false);
437 focusState.active(i: l3c2);
438 FVERIFY();
439
440 l1c3->setFocus(true);
441 focusState[l1c3].set(f: true, af: true);
442 focusState[l2c3].set(f: false, af: false);
443 focusState[l3c2].set(f: true, af: false);
444 focusState.active(i: l1c3);
445 FVERIFY();
446}
447
448// Tests focus corrects itself when a tree is added to a window for the first time
449void tst_qquickitem::addedToWindow()
450{
451 {
452 QQuickWindow window;
453 QVERIFY(ensureFocus(&window));
454 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
455
456 QQuickItem *item = new TestItem;
457
458 FocusState focusState;
459 focusState << item;
460
461 item->setFocus(true);
462 focusState[item].set(f: true, af: false);
463 FVERIFY();
464
465 item->setParentItem(window.contentItem());
466 focusState[item].set(f: true, af: true);
467 focusState.active(i: item);
468 FVERIFY();
469 }
470
471 {
472 QQuickWindow window;
473 QVERIFY(ensureFocus(&window));
474 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
475
476 QQuickItem *item = new TestItem(window.contentItem());
477
478 QQuickItem *tree = new TestItem;
479 QQuickItem *c1 = new TestItem(tree);
480 QQuickItem *c2 = new TestItem(tree);
481
482 FocusState focusState;
483 focusState << item << tree << c1 << c2;
484
485 item->setFocus(true);
486 c1->setFocus(true);
487 c2->setFocus(true);
488 focusState[item].set(f: true, af: true);
489 focusState[c1].set(f: false, af: false);
490 focusState[c2].set(f: true, af: false);
491 focusState.active(i: item);
492 FVERIFY();
493
494 tree->setParentItem(item);
495 focusState[c1].set(f: false, af: false);
496 focusState[c2].set(f: false, af: false);
497 FVERIFY();
498 }
499
500 {
501 QQuickWindow window;
502 QVERIFY(ensureFocus(&window));
503 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
504
505 QQuickItem *tree = new TestItem;
506 QQuickItem *c1 = new TestItem(tree);
507 QQuickItem *c2 = new TestItem(tree);
508
509 FocusState focusState;
510 focusState << tree << c1 << c2;
511 c1->setFocus(true);
512 c2->setFocus(true);
513 focusState[c1].set(f: false, af: false);
514 focusState[c2].set(f: true, af: false);
515 FVERIFY();
516
517 tree->setParentItem(window.contentItem());
518 focusState[c1].set(f: false, af: false);
519 focusState[c2].set(f: true, af: true);
520 focusState.active(i: c2);
521 FVERIFY();
522 }
523
524 {
525 QQuickWindow window;
526 QVERIFY(ensureFocus(&window));
527 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
528 QQuickItem *tree = new TestFocusScope;
529 QQuickItem *c1 = new TestItem(tree);
530 QQuickItem *c2 = new TestItem(tree);
531
532 FocusState focusState;
533 focusState << tree << c1 << c2;
534 c1->setFocus(true);
535 c2->setFocus(true);
536 focusState[c1].set(f: false, af: false);
537 focusState[c2].set(f: true, af: false);
538 FVERIFY();
539
540 tree->setParentItem(window.contentItem());
541 focusState[c1].set(f: false, af: false);
542 focusState[c2].set(f: true, af: false);
543 FVERIFY();
544
545 tree->setFocus(true);
546 focusState[tree].set(f: true, af: true);
547 focusState[c2].set(f: true, af: true);
548 focusState.active(i: c2);
549 FVERIFY();
550 }
551
552 {
553 QQuickWindow window;
554 QVERIFY(ensureFocus(&window));
555 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
556 QQuickItem *tree = new TestFocusScope;
557 QQuickItem *c1 = new TestItem(tree);
558 QQuickItem *c2 = new TestItem(tree);
559
560 FocusState focusState;
561 focusState << tree << c1 << c2;
562 tree->setFocus(true);
563 c1->setFocus(true);
564 c2->setFocus(true);
565 focusState[tree].set(f: true, af: false);
566 focusState[c1].set(f: false, af: false);
567 focusState[c2].set(f: true, af: false);
568 FVERIFY();
569
570 tree->setParentItem(window.contentItem());
571 focusState[tree].set(f: true, af: true);
572 focusState[c1].set(f: false, af: false);
573 focusState[c2].set(f: true, af: true);
574 focusState.active(i: c2);
575 FVERIFY();
576 }
577
578 {
579 QQuickWindow window;
580 QVERIFY(ensureFocus(&window));
581 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
582 QQuickItem *child = new TestItem(window.contentItem());
583 QQuickItem *tree = new TestFocusScope;
584 QQuickItem *c1 = new TestItem(tree);
585 QQuickItem *c2 = new TestItem(tree);
586
587 FocusState focusState;
588 focusState << child << tree << c1 << c2;
589 child->setFocus(true);
590 tree->setFocus(true);
591 c1->setFocus(true);
592 c2->setFocus(true);
593 focusState[child].set(f: true, af: true);
594 focusState[tree].set(f: true, af: false);
595 focusState[c1].set(f: false, af: false);
596 focusState[c2].set(f: true, af: false);
597 focusState.active(i: child);
598 FVERIFY();
599
600 tree->setParentItem(window.contentItem());
601 focusState[tree].set(f: false, af: false);
602 focusState[c1].set(f: false, af: false);
603 focusState[c2].set(f: true, af: false);
604 FVERIFY();
605
606 tree->setFocus(true);
607 focusState[child].set(f: false, af: false);
608 focusState[tree].set(f: true, af: true);
609 focusState[c2].set(f: true, af: true);
610 focusState.active(i: c2);
611 FVERIFY();
612 }
613}
614
615void tst_qquickitem::changeParent()
616{
617 // Parent to no parent
618 {
619 QQuickWindow window;
620 QVERIFY(ensureFocus(&window));
621 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
622 QQuickItem *child = new TestItem(window.contentItem());
623
624 FocusState focusState;
625 focusState << child;
626 FVERIFY();
627
628 child->setFocus(true);
629 focusState[child].set(f: true, af: true);
630 focusState.active(i: child);
631 FVERIFY();
632
633 child->setParentItem(nullptr);
634 focusState[child].set(f: true, af: false);
635 focusState.active(i: nullptr);
636 FVERIFY();
637 }
638
639 // Different parent, same focus scope
640 {
641 QQuickWindow window;
642 QVERIFY(ensureFocus(&window));
643 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
644 QQuickItem *child = new TestItem(window.contentItem());
645 QQuickItem *child2 = new TestItem(window.contentItem());
646
647 FocusState focusState;
648 focusState << child << child2;
649 FVERIFY();
650
651 child->setFocus(true);
652 focusState[child].set(f: true, af: true);
653 focusState.active(i: child);
654 FVERIFY();
655
656 child->setParentItem(child2);
657 FVERIFY();
658 }
659
660 // Different parent, different focus scope
661 {
662 QQuickWindow window;
663 QVERIFY(ensureFocus(&window));
664 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
665 QQuickItem *child = new TestItem(window.contentItem());
666 QQuickItem *child2 = new TestFocusScope(window.contentItem());
667 QQuickItem *item = new TestItem(child);
668
669 FocusState focusState;
670 focusState << child << child2 << item;
671 FVERIFY();
672
673 item->setFocus(true);
674 focusState[item].set(f: true, af: true);
675 focusState.active(i: item);
676 FVERIFY();
677
678 item->setParentItem(child2);
679 focusState[item].set(f: true, af: false);
680 focusState.active(i: nullptr);
681 FVERIFY();
682 }
683 {
684 QQuickWindow window;
685 QVERIFY(ensureFocus(&window));
686 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
687 QQuickItem *child = new TestItem(window.contentItem());
688 QQuickItem *child2 = new TestFocusScope(window.contentItem());
689 QQuickItem *item = new TestItem(child2);
690
691 FocusState focusState;
692 focusState << child << child2 << item;
693 FVERIFY();
694
695 item->setFocus(true);
696 focusState[item].set(f: true, af: false);
697 focusState.active(i: nullptr);
698 FVERIFY();
699
700 item->setParentItem(child);
701 focusState[item].set(f: true, af: true);
702 focusState.active(i: item);
703 FVERIFY();
704 }
705 {
706 QQuickWindow window;
707 QVERIFY(ensureFocus(&window));
708 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
709 QQuickItem *child = new TestItem(window.contentItem());
710 QQuickItem *child2 = new TestFocusScope(window.contentItem());
711 QQuickItem *item = new TestItem(child2);
712
713 FocusState focusState;
714 focusState << child << child2 << item;
715 FVERIFY();
716
717 child->setFocus(true);
718 item->setFocus(true);
719 focusState[child].set(f: true, af: true);
720 focusState[item].set(f: true, af: false);
721 focusState.active(i: child);
722 FVERIFY();
723
724 item->setParentItem(child);
725 focusState[item].set(f: false, af: false);
726 FVERIFY();
727 }
728
729 // child has active focus, then its fs parent changes parent to 0, then
730 // child is deleted, then its parent changes again to a valid parent
731 {
732 QQuickWindow window;
733 QVERIFY(ensureFocus(&window));
734 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
735 QQuickItem *item = new TestFocusScope(window.contentItem());
736 QQuickItem *child = new TestItem(item);
737 QQuickItem *child2 = new TestItem;
738
739 FocusState focusState;
740 focusState << item << child;
741 FVERIFY();
742
743 item->setFocus(true);
744 child->setFocus(true);
745 focusState[child].set(f: true, af: true);
746 focusState[item].set(f: true, af: true);
747 focusState.active(i: child);
748 FVERIFY();
749
750 item->setParentItem(nullptr);
751 focusState[child].set(f: true, af: false);
752 focusState[item].set(f: true, af: false);
753 focusState.active(i: nullptr);
754 FVERIFY();
755
756 focusState.remove(key: child);
757 delete child;
758 item->setParentItem(window.contentItem());
759 focusState[item].set(f: true, af: true);
760 focusState.active(i: item);
761 FVERIFY();
762 delete child2;
763 }
764}
765
766void tst_qquickitem::multipleFocusClears()
767{
768 //Multiple clears of focus inside a focus scope shouldn't crash. QTBUG-24714
769 QQuickView view;
770 view.setSource(testFileUrl(fileName: "multipleFocusClears.qml"));
771 view.show();
772 QVERIFY(ensureFocus(&view));
773 QTRY_COMPARE(QGuiApplication::focusWindow(), &view);
774}
775
776void tst_qquickitem::focusSubItemInNonFocusScope()
777{
778 QQuickView view;
779 view.setSource(testFileUrl(fileName: "focusSubItemInNonFocusScope.qml"));
780 view.show();
781 QVERIFY(QTest::qWaitForWindowActive(&view));
782
783 QQuickItem *dummyItem = view.rootObject()->findChild<QQuickItem *>(aName: "dummyItem");
784 QVERIFY(dummyItem);
785
786 QQuickItem *textInput = view.rootObject()->findChild<QQuickItem *>(aName: "textInput");
787 QVERIFY(textInput);
788
789 QVERIFY(dummyItem->hasFocus());
790 QVERIFY(!textInput->hasFocus());
791 QVERIFY(dummyItem->hasActiveFocus());
792
793 QVERIFY(QMetaObject::invokeMethod(textInput, "forceActiveFocus"));
794
795 QVERIFY(!dummyItem->hasFocus());
796 QVERIFY(textInput->hasFocus());
797 QVERIFY(textInput->hasActiveFocus());
798}
799
800void tst_qquickitem::parentItemWithFocus()
801{
802 QQuickWindow window;
803 QVERIFY(ensureFocus(&window));
804 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
805 {
806 QQuickItem parent;
807 QQuickItem child;
808
809 FocusState focusState;
810 focusState << &parent << &child;
811 FVERIFY();
812
813 parent.setFocus(true);
814 child.setFocus(true);
815 focusState[&parent].set(f: true, af: false);
816 focusState[&child].set(f: true, af: false);
817 FVERIFY();
818
819 child.setParentItem(&parent);
820 focusState[&parent].set(f: true, af: false);
821 focusState[&child].set(f: false, af: false);
822 FVERIFY();
823
824 parent.setParentItem(window.contentItem());
825 focusState[&parent].set(f: true, af: true);
826 focusState[&child].set(f: false, af: false);
827 focusState.active(i: &parent);
828 FVERIFY();
829
830 child.forceActiveFocus();
831 focusState[&parent].set(f: false, af: false);
832 focusState[&child].set(f: true, af: true);
833 focusState.active(i: &child);
834 FVERIFY();
835 } {
836 QQuickItem parent;
837 QQuickItem child;
838 QQuickItem grandchild(&child);
839
840 FocusState focusState;
841 focusState << &parent << &child << &grandchild;
842 FVERIFY();
843
844 parent.setFocus(true);
845 grandchild.setFocus(true);
846 focusState[&parent].set(f: true, af: false);
847 focusState[&child].set(f: false, af: false);
848 focusState[&grandchild].set(f: true, af: false);
849 FVERIFY();
850
851 child.setParentItem(&parent);
852 focusState[&parent].set(f: true, af: false);
853 focusState[&child].set(f: false, af: false);
854 focusState[&grandchild].set(f: false, af: false);
855 FVERIFY();
856
857 parent.setParentItem(window.contentItem());
858 focusState[&parent].set(f: true, af: true);
859 focusState[&child].set(f: false, af: false);
860 focusState[&grandchild].set(f: false, af: false);
861 focusState.active(i: &parent);
862 FVERIFY();
863
864 grandchild.forceActiveFocus();
865 focusState[&parent].set(f: false, af: false);
866 focusState[&child].set(f: false, af: false);
867 focusState[&grandchild].set(f: true, af: true);
868 focusState.active(i: &grandchild);
869 FVERIFY();
870 }
871
872 {
873 QQuickItem parent;
874 QQuickItem child1;
875 QQuickItem child2;
876
877 FocusState focusState;
878 focusState << &parent << &child1 << &child2;
879 parent.setFocus(true);
880 child1.setParentItem(&parent);
881 child2.setParentItem(&parent);
882 focusState[&parent].set(f: true, af: false);
883 focusState[&child1].set(f: false, af: false);
884 focusState[&child2].set(f: false, af: false);
885 FVERIFY();
886
887 child1.setFocus(true);
888 focusState[&parent].set(f: false, af: false);
889 focusState[&child1].set(f: true, af: false);
890 FVERIFY();
891
892 parent.setFocus(true);
893 focusState[&parent].set(f: true, af: false);
894 focusState[&child1].set(f: false, af: false);
895 FVERIFY();
896 }
897}
898
899void tst_qquickitem::reparentFocusedItem()
900{
901 QQuickWindow window;
902 QVERIFY(ensureFocus(&window));
903 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
904
905 QQuickItem parent(window.contentItem());
906 QQuickItem child(&parent);
907 QQuickItem sibling(&parent);
908 QQuickItem grandchild(&child);
909
910 FocusState focusState;
911 focusState << &parent << &child << &sibling << &grandchild;
912 FVERIFY();
913
914 grandchild.setFocus(true);
915 focusState[&parent].set(f: false, af: false);
916 focusState[&child].set(f: false, af: false);
917 focusState[&sibling].set(f: false, af: false);
918 focusState[&grandchild].set(f: true, af: true);
919 focusState.active(i: &grandchild);
920 FVERIFY();
921
922 // Parenting the item to another item within the same focus scope shouldn't change it's focus.
923 child.setParentItem(&sibling);
924 FVERIFY();
925}
926
927void tst_qquickitem::constructor()
928{
929 QScopedPointer<QQuickItem> root(new QQuickItem);
930 QVERIFY(!root->parent());
931 QVERIFY(!root->parentItem());
932
933 QQuickItem *child1 = new QQuickItem(root.data());
934 QCOMPARE(child1->parent(), root.data());
935 QCOMPARE(child1->parentItem(), root.data());
936 QCOMPARE(root->childItems().count(), 1);
937 QCOMPARE(root->childItems().at(0), child1);
938
939 QQuickItem *child2 = new QQuickItem(root.data());
940 QCOMPARE(child2->parent(), root.data());
941 QCOMPARE(child2->parentItem(), root.data());
942 QCOMPARE(root->childItems().count(), 2);
943 QCOMPARE(root->childItems().at(0), child1);
944 QCOMPARE(root->childItems().at(1), child2);
945}
946
947void tst_qquickitem::setParentItem()
948{
949 QQuickItem *root = new QQuickItem;
950 QVERIFY(!root->parent());
951 QVERIFY(!root->parentItem());
952
953 QQuickItem *child1 = new QQuickItem;
954 QVERIFY(!child1->parent());
955 QVERIFY(!child1->parentItem());
956
957 child1->setParentItem(root);
958 QVERIFY(!child1->parent());
959 QCOMPARE(child1->parentItem(), root);
960 QCOMPARE(root->childItems().count(), 1);
961 QCOMPARE(root->childItems().at(0), child1);
962
963 QQuickItem *child2 = new QQuickItem;
964 QVERIFY(!child2->parent());
965 QVERIFY(!child2->parentItem());
966 child2->setParentItem(root);
967 QVERIFY(!child2->parent());
968 QCOMPARE(child2->parentItem(), root);
969 QCOMPARE(root->childItems().count(), 2);
970 QCOMPARE(root->childItems().at(0), child1);
971 QCOMPARE(root->childItems().at(1), child2);
972
973 child1->setParentItem(nullptr);
974 QVERIFY(!child1->parent());
975 QVERIFY(!child1->parentItem());
976 QCOMPARE(root->childItems().count(), 1);
977 QCOMPARE(root->childItems().at(0), child2);
978
979 delete root;
980
981 QVERIFY(!child1->parent());
982 QVERIFY(!child1->parentItem());
983 QVERIFY(!child2->parent());
984 QVERIFY(!child2->parentItem());
985
986 delete child1;
987 delete child2;
988}
989
990void tst_qquickitem::visible()
991{
992 QQuickItem *root = new QQuickItem;
993
994 QQuickItem *child1 = new QQuickItem;
995 child1->setParentItem(root);
996
997 QQuickItem *child2 = new QQuickItem;
998 child2->setParentItem(root);
999
1000 QVERIFY(child1->isVisible());
1001 QVERIFY(child2->isVisible());
1002
1003 root->setVisible(false);
1004 QVERIFY(!child1->isVisible());
1005 QVERIFY(!child2->isVisible());
1006
1007 root->setVisible(true);
1008 QVERIFY(child1->isVisible());
1009 QVERIFY(child2->isVisible());
1010
1011 child1->setVisible(false);
1012 QVERIFY(!child1->isVisible());
1013 QVERIFY(child2->isVisible());
1014
1015 child2->setParentItem(child1);
1016 QVERIFY(!child1->isVisible());
1017 QVERIFY(!child2->isVisible());
1018
1019 child2->setParentItem(root);
1020 QVERIFY(!child1->isVisible());
1021 QVERIFY(child2->isVisible());
1022
1023 delete root;
1024 delete child1;
1025 delete child2;
1026}
1027
1028void tst_qquickitem::enabled()
1029{
1030 QQuickItem *root = new QQuickItem;
1031
1032 QQuickItem *child1 = new QQuickItem;
1033 child1->setParentItem(root);
1034
1035 QQuickItem *child2 = new QQuickItem;
1036 child2->setParentItem(root);
1037
1038 QVERIFY(child1->isEnabled());
1039 QVERIFY(child2->isEnabled());
1040
1041 root->setEnabled(false);
1042 QVERIFY(!child1->isEnabled());
1043 QVERIFY(!child2->isEnabled());
1044
1045 root->setEnabled(true);
1046 QVERIFY(child1->isEnabled());
1047 QVERIFY(child2->isEnabled());
1048
1049 child1->setEnabled(false);
1050 QVERIFY(!child1->isEnabled());
1051 QVERIFY(child2->isEnabled());
1052
1053 child2->setParentItem(child1);
1054 QVERIFY(!child1->isEnabled());
1055 QVERIFY(!child2->isEnabled());
1056
1057 child2->setParentItem(root);
1058 QVERIFY(!child1->isEnabled());
1059 QVERIFY(child2->isEnabled());
1060
1061 delete root;
1062 delete child1;
1063 delete child2;
1064}
1065
1066void tst_qquickitem::enabledFocus()
1067{
1068 QQuickWindow window;
1069 QVERIFY(ensureFocus(&window));
1070
1071 QQuickFocusScope root;
1072
1073 root.setFocus(true);
1074 root.setEnabled(false);
1075
1076 QCOMPARE(root.isEnabled(), false);
1077 QCOMPARE(root.hasFocus(), true);
1078 QCOMPARE(root.hasActiveFocus(), false);
1079
1080 root.setParentItem(window.contentItem());
1081
1082 QCOMPARE(root.isEnabled(), false);
1083 QCOMPARE(root.hasFocus(), true);
1084 QCOMPARE(root.hasActiveFocus(), false);
1085 QCOMPARE(window.activeFocusItem(), window.contentItem());
1086
1087 root.setEnabled(true);
1088 QCOMPARE(root.isEnabled(), true);
1089 QCOMPARE(root.hasFocus(), true);
1090 QCOMPARE(root.hasActiveFocus(), true);
1091 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1092
1093 QQuickItem child1;
1094 child1.setParentItem(&root);
1095
1096 QCOMPARE(child1.isEnabled(), true);
1097 QCOMPARE(child1.hasFocus(), false);
1098 QCOMPARE(child1.hasActiveFocus(), false);
1099 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1100
1101 QQuickItem child2;
1102 child2.setFocus(true);
1103 child2.setParentItem(&root);
1104
1105 QCOMPARE(root.isEnabled(), true);
1106 QCOMPARE(root.hasFocus(), true);
1107 QCOMPARE(root.hasActiveFocus(), true);
1108 QCOMPARE(child2.isEnabled(), true);
1109 QCOMPARE(child2.hasFocus(), true);
1110 QCOMPARE(child2.hasActiveFocus(), true);
1111 QCOMPARE(window.activeFocusItem(), &child2);
1112
1113 child2.setEnabled(false);
1114
1115 QCOMPARE(root.isEnabled(), true);
1116 QCOMPARE(root.hasFocus(), true);
1117 QCOMPARE(root.hasActiveFocus(), true);
1118 QCOMPARE(child1.isEnabled(), true);
1119 QCOMPARE(child1.hasFocus(), false);
1120 QCOMPARE(child1.hasActiveFocus(), false);
1121 QCOMPARE(child2.isEnabled(), false);
1122 QCOMPARE(child2.hasFocus(), true);
1123 QCOMPARE(child2.hasActiveFocus(), false);
1124 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1125
1126 child1.setEnabled(false);
1127 QCOMPARE(child1.isEnabled(), false);
1128 QCOMPARE(child1.hasFocus(), false);
1129 QCOMPARE(child1.hasActiveFocus(), false);
1130
1131 child1.setFocus(true);
1132 QCOMPARE(child1.isEnabled(), false);
1133 QCOMPARE(child1.hasFocus(), true);
1134 QCOMPARE(child1.hasActiveFocus(), false);
1135 QCOMPARE(child2.isEnabled(), false);
1136 QCOMPARE(child2.hasFocus(), false);
1137 QCOMPARE(child2.hasActiveFocus(), false);
1138 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1139
1140 child1.setEnabled(true);
1141 QCOMPARE(child1.isEnabled(), true);
1142 QCOMPARE(child1.hasFocus(), true);
1143 QCOMPARE(child1.hasActiveFocus(), true);
1144 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&child1));
1145
1146 root.setFocus(false);
1147 QCOMPARE(root.isEnabled(), true);
1148 QCOMPARE(root.hasFocus(), false);
1149 QCOMPARE(root.hasActiveFocus(), false);
1150 QCOMPARE(child1.isEnabled(), true);
1151 QCOMPARE(child1.hasFocus(), true);
1152 QCOMPARE(child1.hasActiveFocus(), false);
1153 QCOMPARE(window.activeFocusItem(), window.contentItem());
1154
1155 child2.forceActiveFocus();
1156 QCOMPARE(root.isEnabled(), true);
1157 QCOMPARE(root.hasFocus(), true);
1158 QCOMPARE(root.hasActiveFocus(), true);
1159 QCOMPARE(child1.isEnabled(), true);
1160 QCOMPARE(child1.hasFocus(), false);
1161 QCOMPARE(child1.hasActiveFocus(), false);
1162 QCOMPARE(child2.isEnabled(), false);
1163 QCOMPARE(child2.hasFocus(), true);
1164 QCOMPARE(child2.hasActiveFocus(), false);
1165 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1166
1167 root.setEnabled(false);
1168 QCOMPARE(root.isEnabled(), false);
1169 QCOMPARE(root.hasFocus(), true);
1170 QCOMPARE(root.hasActiveFocus(), false);
1171 QCOMPARE(child1.isEnabled(), false);
1172 QCOMPARE(child1.hasFocus(), false);
1173 QCOMPARE(child1.hasActiveFocus(), false);
1174 QCOMPARE(child2.isEnabled(), false);
1175 QCOMPARE(child2.hasFocus(), true);
1176 QCOMPARE(child2.hasActiveFocus(), false);
1177 QCOMPARE(window.activeFocusItem(), window.contentItem());
1178
1179 child1.forceActiveFocus();
1180 QCOMPARE(root.isEnabled(), false);
1181 QCOMPARE(root.hasFocus(), true);
1182 QCOMPARE(root.hasActiveFocus(), false);
1183 QCOMPARE(child1.isEnabled(), false);
1184 QCOMPARE(child1.hasFocus(), true);
1185 QCOMPARE(child1.hasActiveFocus(), false);
1186 QCOMPARE(child2.isEnabled(), false);
1187 QCOMPARE(child2.hasFocus(), false);
1188 QCOMPARE(child2.hasActiveFocus(), false);
1189 QCOMPARE(window.activeFocusItem(), window.contentItem());
1190
1191 root.setEnabled(true);
1192 QCOMPARE(root.isEnabled(), true);
1193 QCOMPARE(root.hasFocus(), true);
1194 QCOMPARE(root.hasActiveFocus(), true);
1195 QCOMPARE(child1.isEnabled(), true);
1196 QCOMPARE(child1.hasFocus(), true);
1197 QCOMPARE(child1.hasActiveFocus(), true);
1198 QCOMPARE(child2.isEnabled(), false);
1199 QCOMPARE(child2.hasFocus(), false);
1200 QCOMPARE(child2.hasActiveFocus(), false);
1201 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&child1));
1202
1203 child2.setFocus(true);
1204 QCOMPARE(root.isEnabled(), true);
1205 QCOMPARE(root.hasFocus(), true);
1206 QCOMPARE(root.hasActiveFocus(), true);
1207 QCOMPARE(child1.isEnabled(), true);
1208 QCOMPARE(child1.hasFocus(), false);
1209 QCOMPARE(child1.hasActiveFocus(), false);
1210 QCOMPARE(child2.isEnabled(), false);
1211 QCOMPARE(child2.hasFocus(), true);
1212 QCOMPARE(child2.hasActiveFocus(), false);
1213 QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
1214
1215 root.setEnabled(false);
1216 QCOMPARE(root.isEnabled(), false);
1217 QCOMPARE(root.hasFocus(), true);
1218 QCOMPARE(root.hasActiveFocus(), false);
1219 QCOMPARE(child1.isEnabled(), false);
1220 QCOMPARE(child1.hasFocus(), false);
1221 QCOMPARE(child1.hasActiveFocus(), false);
1222 QCOMPARE(child2.isEnabled(), false);
1223 QCOMPARE(child2.hasFocus(), true);
1224 QCOMPARE(child2.hasActiveFocus(), false);
1225 QCOMPARE(window.activeFocusItem(), window.contentItem());
1226}
1227
1228static inline QByteArray msgItem(const QQuickItem *item)
1229{
1230 QString result;
1231 QDebug(&result) << item;
1232 return result.toLocal8Bit();
1233}
1234
1235void tst_qquickitem::mouseGrab()
1236{
1237#ifdef Q_OS_WIN
1238 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
1239 QSKIP("Fails in the CI for ANGLE builds on Windows, QTBUG-32664");
1240#endif
1241 QQuickWindow window;
1242 window.setFramePosition(QPoint(100, 100));
1243 window.resize(w: 200, h: 200);
1244 window.show();
1245 QVERIFY(QTest::qWaitForWindowExposed(&window));
1246
1247 QScopedPointer<TestItem> child1(new TestItem);
1248 child1->setObjectName(QStringLiteral("child1"));
1249 child1->setAcceptedMouseButtons(Qt::LeftButton);
1250 child1->setSize(QSizeF(200, 100));
1251 child1->setParentItem(window.contentItem());
1252
1253 QScopedPointer<TestItem> child2(new TestItem);
1254 child2->setObjectName(QStringLiteral("child2"));
1255 child2->setAcceptedMouseButtons(Qt::LeftButton);
1256 child2->setY(51);
1257 child2->setSize(QSizeF(200, 100));
1258 child2->setParentItem(window.contentItem());
1259
1260 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1261 QTest::qWait(ms: 100);
1262 QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
1263 QTest::qWait(ms: 100);
1264
1265 QCOMPARE(child1->pressCount, 1);
1266 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1267 QTest::qWait(ms: 50);
1268 QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()).constData());
1269 QCOMPARE(child1->releaseCount, 1);
1270
1271 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1272 QTest::qWait(ms: 50);
1273 QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
1274 QCOMPARE(child1->pressCount, 2);
1275 child1->setEnabled(false);
1276 QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()).constData());
1277 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1278 QTest::qWait(ms: 50);
1279 QCOMPARE(child1->releaseCount, 1);
1280 child1->setEnabled(true);
1281
1282 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1283 QTest::qWait(ms: 50);
1284 QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
1285 QCOMPARE(child1->pressCount, 3);
1286 child1->setVisible(false);
1287 QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()));
1288 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1289 QCOMPARE(child1->releaseCount, 1);
1290 child1->setVisible(true);
1291
1292 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1293 QTest::qWait(ms: 50);
1294 QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
1295 QCOMPARE(child1->pressCount, 4);
1296 child2->grabMouse();
1297 QVERIFY2(window.mouseGrabberItem() == child2.data(), msgItem(window.mouseGrabberItem()).constData());
1298 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1299 QTest::qWait(ms: 50);
1300 QCOMPARE(child1->releaseCount, 1);
1301 QCOMPARE(child2->releaseCount, 1);
1302
1303 child2->grabMouse();
1304 QVERIFY2(window.mouseGrabberItem() == child2.data(), msgItem(window.mouseGrabberItem()).constData());
1305 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1306 QTest::qWait(ms: 50);
1307 QCOMPARE(child1->pressCount, 4);
1308 QCOMPARE(child2->pressCount, 1);
1309 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50,50));
1310 QTest::qWait(ms: 50);
1311 QCOMPARE(child1->releaseCount, 1);
1312 QCOMPARE(child2->releaseCount, 2);
1313}
1314
1315void tst_qquickitem::touchEventAcceptIgnore_data()
1316{
1317 QTest::addColumn<bool>(name: "itemSupportsTouch");
1318
1319 QTest::newRow(dataTag: "with touch") << true;
1320 QTest::newRow(dataTag: "without touch") << false;
1321}
1322
1323void tst_qquickitem::touchEventAcceptIgnore()
1324{
1325 QFETCH(bool, itemSupportsTouch);
1326
1327 TestWindow window;
1328 window.resize(w: 100, h: 100);
1329 window.show();
1330 QVERIFY(QTest::qWaitForWindowExposed(&window));
1331
1332 QScopedPointer<TestItem> item(new TestItem);
1333 item->setSize(QSizeF(100, 100));
1334 item->setParentItem(window.contentItem());
1335 item->acceptIncomingTouchEvents = itemSupportsTouch;
1336
1337 static QTouchDevice* device = nullptr;
1338 if (!device) {
1339 device =new QTouchDevice;
1340 device->setType(QTouchDevice::TouchScreen);
1341 QWindowSystemInterface::registerTouchDevice(device);
1342 }
1343
1344 // Send Begin, Update & End touch sequence
1345 {
1346 QTouchEvent::TouchPoint point;
1347 point.setId(1);
1348 point.setPos(QPointF(50, 50));
1349 point.setScreenPos(point.pos());
1350 point.setState(Qt::TouchPointPressed);
1351
1352 QTouchEvent event(QEvent::TouchBegin, device,
1353 Qt::NoModifier,
1354 Qt::TouchPointPressed,
1355 QList<QTouchEvent::TouchPoint>() << point);
1356 event.setAccepted(true);
1357
1358 item->touchEventReached = false;
1359
1360 bool accepted = window.event(event: &event);
1361 QQuickTouchUtils::flush(window: &window);
1362
1363 QVERIFY(item->touchEventReached);
1364
1365 // always true because QtQuick always eats touch events so as to not
1366 // allow QtGui to synthesise them for us.
1367 QCOMPARE(accepted && event.isAccepted(), true);
1368 }
1369 {
1370 QTouchEvent::TouchPoint point;
1371 point.setId(1);
1372 point.setPos(QPointF(60, 60));
1373 point.setScreenPos(point.pos());
1374 point.setState(Qt::TouchPointMoved);
1375
1376 QTouchEvent event(QEvent::TouchUpdate, device,
1377 Qt::NoModifier,
1378 Qt::TouchPointMoved,
1379 QList<QTouchEvent::TouchPoint>() << point);
1380 event.setAccepted(true);
1381
1382 item->touchEventReached = false;
1383
1384 bool accepted = window.event(event: &event);
1385 QQuickTouchUtils::flush(window: &window);
1386
1387 QCOMPARE(item->touchEventReached, itemSupportsTouch);
1388
1389 // always true because QtQuick always eats touch events so as to not
1390 // allow QtGui to synthesise them for us.
1391 QCOMPARE(accepted && event.isAccepted(), true);
1392 }
1393 {
1394 QTouchEvent::TouchPoint point;
1395 point.setId(1);
1396 point.setPos(QPointF(60, 60));
1397 point.setScreenPos(point.pos());
1398 point.setState(Qt::TouchPointReleased);
1399
1400 QTouchEvent event(QEvent::TouchEnd, device,
1401 Qt::NoModifier,
1402 Qt::TouchPointReleased,
1403 QList<QTouchEvent::TouchPoint>() << point);
1404 event.setAccepted(true);
1405
1406 item->touchEventReached = false;
1407
1408 bool accepted = window.event(event: &event);
1409 QQuickTouchUtils::flush(window: &window);
1410
1411 QCOMPARE(item->touchEventReached, itemSupportsTouch);
1412
1413 // always true because QtQuick always eats touch events so as to not
1414 // allow QtGui to synthesise them for us.
1415 QCOMPARE(accepted && event.isAccepted(), true);
1416 }
1417}
1418
1419void tst_qquickitem::polishOutsideAnimation()
1420{
1421 QQuickWindow *window = new QQuickWindow;
1422 window->resize(w: 200, h: 200);
1423 window->show();
1424
1425 TestPolishItem *item = new TestPolishItem(window->contentItem());
1426 item->setSize(QSizeF(200, 100));
1427 QTest::qWait(ms: 50);
1428
1429 QTimer::singleShot(msec: 10, receiver: item, SLOT(doPolish()));
1430 QTRY_VERIFY(item->wasPolished);
1431
1432 delete item;
1433 delete window;
1434}
1435
1436void tst_qquickitem::polishOnCompleted()
1437{
1438 QQuickView view;
1439 view.setSource(testFileUrl(fileName: "polishOnCompleted.qml"));
1440 view.show();
1441 QVERIFY(QTest::qWaitForWindowExposed(&view));
1442
1443 TestPolishItem *item = qobject_cast<TestPolishItem*>(object: view.rootObject());
1444 QVERIFY(item);
1445
1446 QTRY_VERIFY(item->wasPolished);
1447}
1448
1449struct PolishItemSpan {
1450 int itemCount; // Number of items...
1451 int repolishCount; // ...repolishing 'repolishCount' times
1452};
1453
1454/*
1455 * For instance, two consecutive spans {99,0} and {1,2000} } instructs to
1456 * construct 99 items with no repolish, and 1 item with 2000 repolishes (in that sibling order)
1457 */
1458typedef QVector<PolishItemSpan> PolishItemSpans;
1459
1460Q_DECLARE_METATYPE(PolishItemSpan)
1461Q_DECLARE_METATYPE(PolishItemSpans)
1462
1463void tst_qquickitem::polishLoopDetection_data()
1464{
1465 QTest::addColumn<PolishItemSpans>(name: "listOfItemsToPolish");
1466 QTest::addColumn<int>(name: "expectedNumberOfWarnings");
1467
1468 QTest::newRow(dataTag: "test1.100") << PolishItemSpans({ {.itemCount: 1, .repolishCount: 100} }) << 0;
1469 QTest::newRow(dataTag: "test1.1002") << PolishItemSpans({ {.itemCount: 1, .repolishCount: 1002} }) << 3;
1470 QTest::newRow(dataTag: "test1.2020") << PolishItemSpans({ {.itemCount: 1, .repolishCount: 2020} }) << 10;
1471
1472 QTest::newRow(dataTag: "test5.1") << PolishItemSpans({ {.itemCount: 5, .repolishCount: 1} }) << 0;
1473 QTest::newRow(dataTag: "test5.10") << PolishItemSpans({ {.itemCount: 5, .repolishCount: 10} }) << 0;
1474 QTest::newRow(dataTag: "test5.100") << PolishItemSpans({ {.itemCount: 5, .repolishCount: 100} }) << 0;
1475 QTest::newRow(dataTag: "test5.1000") << PolishItemSpans({ {.itemCount: 5, .repolishCount: 1000} }) << 5;
1476
1477 QTest::newRow(dataTag: "test1000.1") << PolishItemSpans({ {.itemCount: 1000,.repolishCount: 1} }) << 0;
1478 QTest::newRow(dataTag: "test2000.1") << PolishItemSpans({ {.itemCount: 2000,.repolishCount: 1} }) << 0;
1479
1480 QTest::newRow(dataTag: "test99.0-1.1100") << PolishItemSpans({ {.itemCount: 99,.repolishCount: 0},{.itemCount: 1,.repolishCount: 1100} }) << 5;
1481 QTest::newRow(dataTag: "test98.0-2.1100") << PolishItemSpans({ {.itemCount: 98,.repolishCount: 0},{.itemCount: 2,.repolishCount: 1100} }) << 5+5;
1482
1483 // reverse the two above
1484 QTest::newRow(dataTag: "test1.1100-99.0") << PolishItemSpans({ {.itemCount: 1,.repolishCount: 1100},{.itemCount: 99,.repolishCount: 0} }) << 5;
1485 QTest::newRow(dataTag: "test2.1100-98.0") << PolishItemSpans({ {.itemCount: 2,.repolishCount: 1100},{.itemCount: 98,.repolishCount: 0} }) << 5+5;
1486}
1487
1488void tst_qquickitem::polishLoopDetection()
1489{
1490 QFETCH(PolishItemSpans, listOfItemsToPolish);
1491 QFETCH(int, expectedNumberOfWarnings);
1492
1493 QQuickWindow window;
1494 window.resize(w: 200, h: 200);
1495 window.show();
1496
1497 TestPolishItem *item = nullptr;
1498 int count = 0;
1499 for (PolishItemSpan s : listOfItemsToPolish) {
1500 for (int i = 0; i < s.itemCount; ++i) {
1501 item = new TestPolishItem(window.contentItem());
1502 item->setSize(QSizeF(200, 100));
1503 item->repolishLoopCount = s.repolishCount;
1504 item->setObjectName(QString::fromLatin1(str: "obj%1").arg(a: count++));
1505 }
1506 }
1507
1508 for (int i = 0; i < expectedNumberOfWarnings; ++i) {
1509 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*possible QQuickItem..polish.. loop.*"));
1510 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: QRegularExpression(".*TestPolishItem.* called polish.. inside updatePolish.. of TestPolishItem.*"));
1511 }
1512
1513 QList<QQuickItem*> items = window.contentItem()->childItems();
1514 for (int i = 0; i < items.count(); ++i) {
1515 static_cast<TestPolishItem*>(items.at(i))->doPolish();
1516 }
1517 item = static_cast<TestPolishItem*>(items.first());
1518 // item is the last item, so we wait until the last item reached 0
1519 QVERIFY(QTest::qWaitFor([=](){return item->repolishLoopCount == 0 && item->wasPolished;}));
1520}
1521
1522void tst_qquickitem::wheelEvent_data()
1523{
1524 QTest::addColumn<bool>(name: "visible");
1525 QTest::addColumn<bool>(name: "enabled");
1526
1527 QTest::newRow(dataTag: "visible and enabled") << true << true;
1528 QTest::newRow(dataTag: "visible and disabled") << true << false;
1529 QTest::newRow(dataTag: "invisible and enabled") << false << true;
1530 QTest::newRow(dataTag: "invisible and disabled") << false << false;
1531}
1532
1533void tst_qquickitem::wheelEvent()
1534{
1535 QFETCH(bool, visible);
1536 QFETCH(bool, enabled);
1537
1538 const bool shouldReceiveWheelEvents = visible && enabled;
1539
1540 const int width = 200;
1541 const int height = 200;
1542
1543 QQuickWindow window;
1544 window.resize(w: width, h: height);
1545 window.show();
1546 QVERIFY(QTest::qWaitForWindowExposed(&window));
1547
1548 TestItem *item = new TestItem;
1549 item->setSize(QSizeF(width, height));
1550 item->setParentItem(window.contentItem());
1551
1552 item->setEnabled(enabled);
1553 item->setVisible(visible);
1554
1555 QPoint localPoint(width / 2, height / 2);
1556 QPoint globalPoint = window.mapToGlobal(pos: localPoint);
1557 QWheelEvent event(localPoint, globalPoint, QPoint(0, 0), QPoint(0, -120),
1558 Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
1559 event.setTimestamp(123456UL);
1560 event.setAccepted(false);
1561 QGuiApplication::sendEvent(receiver: &window, event: &event);
1562
1563 if (shouldReceiveWheelEvents) {
1564 QVERIFY(event.isAccepted());
1565 QCOMPARE(item->wheelCount, 1);
1566 QCOMPARE(item->timestamp, 123456UL);
1567 QCOMPARE(item->lastWheelEventPos, localPoint);
1568 QCOMPARE(item->lastWheelEventGlobalPos, globalPoint);
1569 } else {
1570 QVERIFY(!event.isAccepted());
1571 QCOMPARE(item->wheelCount, 0);
1572 }
1573}
1574
1575class HoverItem : public QQuickItem
1576{
1577Q_OBJECT
1578public:
1579 HoverItem(QQuickItem *parent = nullptr)
1580 : QQuickItem(parent), hoverEnterCount(0), hoverMoveCount(0), hoverLeaveCount(0)
1581 { }
1582 void resetCounters() {
1583 hoverEnterCount = 0;
1584 hoverMoveCount = 0;
1585 hoverLeaveCount = 0;
1586 }
1587 int hoverEnterCount;
1588 int hoverMoveCount;
1589 int hoverLeaveCount;
1590protected:
1591 virtual void hoverEnterEvent(QHoverEvent *event) {
1592 event->accept();
1593 ++hoverEnterCount;
1594 }
1595 virtual void hoverMoveEvent(QHoverEvent *event) {
1596 event->accept();
1597 ++hoverMoveCount;
1598 }
1599 virtual void hoverLeaveEvent(QHoverEvent *event) {
1600 event->accept();
1601 ++hoverLeaveCount;
1602 }
1603};
1604
1605void tst_qquickitem::hoverEvent_data()
1606{
1607 QTest::addColumn<bool>(name: "visible");
1608 QTest::addColumn<bool>(name: "enabled");
1609 QTest::addColumn<bool>(name: "acceptHoverEvents");
1610
1611 QTest::newRow(dataTag: "visible, enabled, accept hover") << true << true << true;
1612 QTest::newRow(dataTag: "visible, disabled, accept hover") << true << false << true;
1613 QTest::newRow(dataTag: "invisible, enabled, accept hover") << false << true << true;
1614 QTest::newRow(dataTag: "invisible, disabled, accept hover") << false << false << true;
1615
1616 QTest::newRow(dataTag: "visible, enabled, not accept hover") << true << true << false;
1617 QTest::newRow(dataTag: "visible, disabled, not accept hover") << true << false << false;
1618 QTest::newRow(dataTag: "invisible, enabled, not accept hover") << false << true << false;
1619 QTest::newRow(dataTag: "invisible, disabled, not accept hover") << false << false << false;
1620}
1621
1622// ### For some unknown reason QTest::mouseMove() isn't working correctly.
1623static void sendMouseMove(QObject *object, const QPoint &position)
1624{
1625 QMouseEvent moveEvent(QEvent::MouseMove, position, Qt::NoButton, Qt::NoButton, nullptr);
1626 QGuiApplication::sendEvent(receiver: object, event: &moveEvent);
1627}
1628
1629void tst_qquickitem::hoverEvent()
1630{
1631 QFETCH(bool, visible);
1632 QFETCH(bool, enabled);
1633 QFETCH(bool, acceptHoverEvents);
1634
1635 QQuickWindow *window = new QQuickWindow();
1636 window->resize(w: 200, h: 200);
1637 window->show();
1638
1639 HoverItem *item = new HoverItem;
1640 item->setSize(QSizeF(100, 100));
1641 item->setParentItem(window->contentItem());
1642
1643 item->setEnabled(enabled);
1644 item->setVisible(visible);
1645 item->setAcceptHoverEvents(acceptHoverEvents);
1646
1647 const QPoint outside(150, 150);
1648 const QPoint inside(50, 50);
1649 const QPoint anotherInside(51, 51);
1650
1651 sendMouseMove(object: window, position: outside);
1652 item->resetCounters();
1653
1654 // Enter, then move twice inside, then leave.
1655 sendMouseMove(object: window, position: inside);
1656 sendMouseMove(object: window, position: anotherInside);
1657 sendMouseMove(object: window, position: inside);
1658 sendMouseMove(object: window, position: outside);
1659
1660 const bool shouldReceiveHoverEvents = visible && enabled && acceptHoverEvents;
1661 if (shouldReceiveHoverEvents) {
1662 QCOMPARE(item->hoverEnterCount, 1);
1663 QCOMPARE(item->hoverMoveCount, 2);
1664 QCOMPARE(item->hoverLeaveCount, 1);
1665 } else {
1666 QCOMPARE(item->hoverEnterCount, 0);
1667 QCOMPARE(item->hoverMoveCount, 0);
1668 QCOMPARE(item->hoverLeaveCount, 0);
1669 }
1670
1671 delete window;
1672}
1673
1674void tst_qquickitem::hoverEventInParent()
1675{
1676 QQuickWindow window;
1677 window.resize(w: 200, h: 200);
1678 window.show();
1679
1680 HoverItem *parentItem = new HoverItem(window.contentItem());
1681 parentItem->setSize(QSizeF(200, 200));
1682 parentItem->setAcceptHoverEvents(true);
1683
1684 HoverItem *leftItem = new HoverItem(parentItem);
1685 leftItem->setSize(QSizeF(100, 200));
1686 leftItem->setAcceptHoverEvents(true);
1687
1688 HoverItem *rightItem = new HoverItem(parentItem);
1689 rightItem->setSize(QSizeF(100, 200));
1690 rightItem->setPosition(QPointF(100, 0));
1691 rightItem->setAcceptHoverEvents(true);
1692
1693 const QPoint insideLeft(50, 100);
1694 const QPoint insideRight(150, 100);
1695
1696 sendMouseMove(object: &window, position: insideLeft);
1697 parentItem->resetCounters();
1698 leftItem->resetCounters();
1699 rightItem->resetCounters();
1700
1701 sendMouseMove(object: &window, position: insideRight);
1702 QCOMPARE(parentItem->hoverEnterCount, 0);
1703 QCOMPARE(parentItem->hoverLeaveCount, 0);
1704 QCOMPARE(leftItem->hoverEnterCount, 0);
1705 QCOMPARE(leftItem->hoverLeaveCount, 1);
1706 QCOMPARE(rightItem->hoverEnterCount, 1);
1707 QCOMPARE(rightItem->hoverLeaveCount, 0);
1708
1709 sendMouseMove(object: &window, position: insideLeft);
1710 QCOMPARE(parentItem->hoverEnterCount, 0);
1711 QCOMPARE(parentItem->hoverLeaveCount, 0);
1712 QCOMPARE(leftItem->hoverEnterCount, 1);
1713 QCOMPARE(leftItem->hoverLeaveCount, 1);
1714 QCOMPARE(rightItem->hoverEnterCount, 1);
1715 QCOMPARE(rightItem->hoverLeaveCount, 1);
1716}
1717
1718void tst_qquickitem::paintOrder_data()
1719{
1720 const QUrl order1Url = testFileUrl(fileName: "order.1.qml");
1721 const QUrl order2Url = testFileUrl(fileName: "order.2.qml");
1722
1723 QTest::addColumn<QUrl>(name: "source");
1724 QTest::addColumn<int>(name: "op");
1725 QTest::addColumn<QVariant>(name: "param1");
1726 QTest::addColumn<QVariant>(name: "param2");
1727 QTest::addColumn<QStringList>(name: "expected");
1728
1729 QTest::newRow(dataTag: "test 1 noop") << order1Url
1730 << int(NoOp) << QVariant() << QVariant()
1731 << (QStringList() << "1" << "2" << "3");
1732 QTest::newRow(dataTag: "test 1 add") << order1Url
1733 << int(Append) << QVariant("new") << QVariant()
1734 << (QStringList() << "1" << "2" << "3" << "new");
1735 QTest::newRow(dataTag: "test 1 remove") << order1Url
1736 << int(Remove) << QVariant(1) << QVariant()
1737 << (QStringList() << "1" << "3");
1738 QTest::newRow(dataTag: "test 1 stack before") << order1Url
1739 << int(StackBefore) << QVariant(2) << QVariant(1)
1740 << (QStringList() << "1" << "3" << "2");
1741 QTest::newRow(dataTag: "test 1 stack after") << order1Url
1742 << int(StackAfter) << QVariant(0) << QVariant(1)
1743 << (QStringList() << "2" << "1" << "3");
1744 QTest::newRow(dataTag: "test 1 set z") << order1Url
1745 << int(SetZ) << QVariant(1) << QVariant(qreal(1.))
1746 << (QStringList() << "1" << "3" << "2");
1747
1748 QTest::newRow(dataTag: "test 2 noop") << order2Url
1749 << int(NoOp) << QVariant() << QVariant()
1750 << (QStringList() << "1" << "3" << "2");
1751 QTest::newRow(dataTag: "test 2 add") << order2Url
1752 << int(Append) << QVariant("new") << QVariant()
1753 << (QStringList() << "1" << "3" << "new" << "2");
1754 QTest::newRow(dataTag: "test 2 remove 1") << order2Url
1755 << int(Remove) << QVariant(1) << QVariant()
1756 << (QStringList() << "1" << "3");
1757 QTest::newRow(dataTag: "test 2 remove 2") << order2Url
1758 << int(Remove) << QVariant(2) << QVariant()
1759 << (QStringList() << "1" << "2");
1760 QTest::newRow(dataTag: "test 2 stack before 1") << order2Url
1761 << int(StackBefore) << QVariant(1) << QVariant(0)
1762 << (QStringList() << "1" << "3" << "2");
1763 QTest::newRow(dataTag: "test 2 stack before 2") << order2Url
1764 << int(StackBefore) << QVariant(2) << QVariant(0)
1765 << (QStringList() << "3" << "1" << "2");
1766 QTest::newRow(dataTag: "test 2 stack after 1") << order2Url
1767 << int(StackAfter) << QVariant(0) << QVariant(1)
1768 << (QStringList() << "1" << "3" << "2");
1769 QTest::newRow(dataTag: "test 2 stack after 2") << order2Url
1770 << int(StackAfter) << QVariant(0) << QVariant(2)
1771 << (QStringList() << "3" << "1" << "2");
1772 QTest::newRow(dataTag: "test 1 set z") << order1Url
1773 << int(SetZ) << QVariant(2) << QVariant(qreal(2.))
1774 << (QStringList() << "1" << "2" << "3");
1775}
1776
1777void tst_qquickitem::paintOrder()
1778{
1779 QFETCH(QUrl, source);
1780 QFETCH(int, op);
1781 QFETCH(QVariant, param1);
1782 QFETCH(QVariant, param2);
1783 QFETCH(QStringList, expected);
1784
1785 QQuickView view;
1786 view.setSource(source);
1787
1788 QQuickItem *root = qobject_cast<QQuickItem*>(object: view.rootObject());
1789 QVERIFY(root);
1790
1791 switch (op) {
1792 case Append: {
1793 QQuickItem *item = new QQuickItem(root);
1794 item->setObjectName(param1.toString());
1795 }
1796 break;
1797 case Remove: {
1798 QQuickItem *item = root->childItems().at(i: param1.toInt());
1799 delete item;
1800 }
1801 break;
1802 case StackBefore: {
1803 QQuickItem *item1 = root->childItems().at(i: param1.toInt());
1804 QQuickItem *item2 = root->childItems().at(i: param2.toInt());
1805 item1->stackBefore(item2);
1806 }
1807 break;
1808 case StackAfter: {
1809 QQuickItem *item1 = root->childItems().at(i: param1.toInt());
1810 QQuickItem *item2 = root->childItems().at(i: param2.toInt());
1811 item1->stackAfter(item2);
1812 }
1813 break;
1814 case SetZ: {
1815 QQuickItem *item = root->childItems().at(i: param1.toInt());
1816 item->setZ(param2.toReal());
1817 }
1818 break;
1819 default:
1820 break;
1821 }
1822
1823 QList<QQuickItem*> list = QQuickItemPrivate::get(item: root)->paintOrderChildItems();
1824
1825 QStringList items;
1826 for (int i = 0; i < list.count(); ++i)
1827 items << list.at(i)->objectName();
1828
1829 QCOMPARE(items, expected);
1830}
1831
1832void tst_qquickitem::acceptedMouseButtons()
1833{
1834 TestItem item;
1835 QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::NoButton));
1836
1837 QQuickWindow window;
1838 item.setSize(QSizeF(200,100));
1839 item.setParentItem(window.contentItem());
1840
1841 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1842 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1843 QCOMPARE(item.pressCount, 0);
1844 QCOMPARE(item.releaseCount, 0);
1845
1846 QTest::mousePress(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1847 QTest::mouseRelease(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1848 QCOMPARE(item.pressCount, 0);
1849 QCOMPARE(item.releaseCount, 0);
1850
1851 QTest::mousePress(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1852 QTest::mouseRelease(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1853 QCOMPARE(item.pressCount, 0);
1854 QCOMPARE(item.releaseCount, 0);
1855
1856 item.setAcceptedMouseButtons(Qt::LeftButton);
1857 QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::LeftButton));
1858
1859 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1860 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1861 QCOMPARE(item.pressCount, 1);
1862 QCOMPARE(item.releaseCount, 1);
1863
1864 QTest::mousePress(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1865 QTest::mouseRelease(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1866 QCOMPARE(item.pressCount, 1);
1867 QCOMPARE(item.releaseCount, 1);
1868
1869 QTest::mousePress(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1870 QTest::mouseRelease(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1871 QCOMPARE(item.pressCount, 1);
1872 QCOMPARE(item.releaseCount, 1);
1873
1874 item.setAcceptedMouseButtons(Qt::RightButton | Qt::MiddleButton);
1875 QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton));
1876
1877 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1878 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1879 QCOMPARE(item.pressCount, 1);
1880 QCOMPARE(item.releaseCount, 1);
1881
1882 QTest::mousePress(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1883 QTest::mouseRelease(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1884 QCOMPARE(item.pressCount, 2);
1885 QCOMPARE(item.releaseCount, 2);
1886
1887 QTest::mousePress(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1888 QTest::mouseRelease(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
1889 QCOMPARE(item.pressCount, 3);
1890 QCOMPARE(item.releaseCount, 3);
1891}
1892
1893static void gc(QQmlEngine &engine)
1894{
1895 engine.collectGarbage();
1896 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1897 QCoreApplication::processEvents();
1898}
1899
1900void tst_qquickitem::visualParentOwnership()
1901{
1902 QQmlEngine engine;
1903 QQmlComponent component(&engine, testFileUrl(fileName: "visualParentOwnership.qml"));
1904
1905 QScopedPointer<QQuickItem> root(qobject_cast<QQuickItem*>(object: component.create()));
1906 QVERIFY(root);
1907
1908 QVariant newObject;
1909 {
1910 QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1911 QPointer<QQuickItem> newItem = qvariant_cast<QQuickItem*>(v: newObject);
1912 QVERIFY(!newItem.isNull());
1913
1914 QVERIFY(!newItem->parent());
1915 QVERIFY(!newItem->parentItem());
1916
1917 newItem->setParentItem(root.data());
1918
1919 gc(engine);
1920
1921 QVERIFY(!newItem.isNull());
1922 newItem->setParentItem(nullptr);
1923
1924 gc(engine);
1925 QVERIFY(newItem.isNull());
1926 }
1927 {
1928 QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1929 QPointer<QQuickItem> firstItem = qvariant_cast<QQuickItem*>(v: newObject);
1930 QVERIFY(!firstItem.isNull());
1931
1932 firstItem->setParentItem(root.data());
1933
1934 QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1935 QPointer<QQuickItem> secondItem = qvariant_cast<QQuickItem*>(v: newObject);
1936 QVERIFY(!firstItem.isNull());
1937
1938 secondItem->setParentItem(firstItem);
1939
1940 gc(engine);
1941
1942 delete firstItem;
1943
1944 root->setProperty(name: "keepAliveProperty", value: newObject);
1945
1946 gc(engine);
1947 QVERIFY(!secondItem.isNull());
1948
1949 root->setProperty(name: "keepAliveProperty", value: QVariant());
1950
1951 gc(engine);
1952 QVERIFY(secondItem.isNull());
1953 }
1954}
1955
1956void tst_qquickitem::visualParentOwnershipWindow()
1957{
1958 QQmlEngine engine;
1959 QQmlComponent component(&engine, testFileUrl(fileName: "visualParentOwnershipWindow.qml"));
1960
1961 QQuickWindow *window = qobject_cast<QQuickWindow*>(object: component.create());
1962 QVERIFY(window);
1963 QQuickItem *root = window->contentItem();
1964
1965 QVariant newObject;
1966 {
1967 QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1968 QPointer<QQuickItem> newItem = qvariant_cast<QQuickItem*>(v: newObject);
1969 QVERIFY(!newItem.isNull());
1970
1971 QVERIFY(!newItem->parent());
1972 QVERIFY(!newItem->parentItem());
1973
1974 newItem->setParentItem(root);
1975
1976 gc(engine);
1977
1978 QVERIFY(!newItem.isNull());
1979 newItem->setParentItem(nullptr);
1980
1981 gc(engine);
1982 QVERIFY(newItem.isNull());
1983 }
1984 {
1985 QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1986 QPointer<QQuickItem> firstItem = qvariant_cast<QQuickItem*>(v: newObject);
1987 QVERIFY(!firstItem.isNull());
1988
1989 firstItem->setParentItem(root);
1990
1991 QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
1992 QPointer<QQuickItem> secondItem = qvariant_cast<QQuickItem*>(v: newObject);
1993 QVERIFY(!firstItem.isNull());
1994
1995 secondItem->setParentItem(firstItem);
1996
1997 gc(engine);
1998
1999 delete firstItem;
2000
2001 window->setProperty(name: "keepAliveProperty", value: newObject);
2002
2003 gc(engine);
2004 QVERIFY(!secondItem.isNull());
2005
2006 window->setProperty(name: "keepAliveProperty", value: QVariant());
2007
2008 gc(engine);
2009 QVERIFY(secondItem.isNull());
2010 }
2011}
2012
2013class InvalidatedItem : public QQuickItem {
2014 Q_OBJECT
2015signals:
2016 void invalidated();
2017public slots:
2018 void invalidateSceneGraph() { emit invalidated(); }
2019};
2020
2021void tst_qquickitem::testSGInvalidate()
2022{
2023 for (int i=0; i<2; ++i) {
2024 QScopedPointer<QQuickView> view(new QQuickView());
2025
2026 InvalidatedItem *item = new InvalidatedItem();
2027
2028 int expected = 0;
2029 if (i == 0) {
2030 // First iteration, item has contents and should get signals
2031 expected = 1;
2032 item->setFlag(flag: QQuickItem::ItemHasContents, enabled: true);
2033 } else {
2034 // Second iteration, item does not have content and will not get signals
2035 }
2036
2037 QSignalSpy invalidateSpy(item, SIGNAL(invalidated()));
2038 item->setParentItem(view->contentItem());
2039 view->show();
2040
2041 QVERIFY(QTest::qWaitForWindowExposed(view.data()));
2042
2043 delete view.take();
2044 QCOMPARE(invalidateSpy.size(), expected);
2045 }
2046}
2047
2048void tst_qquickitem::objectChildTransform()
2049{
2050 QQuickView view;
2051 view.setSource(testFileUrl(fileName: "objectChildTransform.qml"));
2052
2053 QQuickItem *root = qobject_cast<QQuickItem*>(object: view.rootObject());
2054 QVERIFY(root);
2055
2056 root->setProperty(name: "source", value: QString());
2057 // Shouldn't crash.
2058}
2059
2060void tst_qquickitem::contains_data()
2061{
2062 QTest::addColumn<int>(name: "x");
2063 QTest::addColumn<int>(name: "y");
2064 QTest::addColumn<bool>(name: "contains");
2065
2066 QTest::newRow(dataTag: "(0, 0) = false") << 0 << 0 << false;
2067 QTest::newRow(dataTag: "(50, 0) = false") << 50 << 0 << false;
2068 QTest::newRow(dataTag: "(0, 50) = false") << 0 << 50 << false;
2069 QTest::newRow(dataTag: "(50, 50) = true") << 50 << 50 << true;
2070 QTest::newRow(dataTag: "(100, 100) = true") << 100 << 100 << true;
2071 QTest::newRow(dataTag: "(150, 150) = false") << 150 << 150 << false;
2072}
2073
2074void tst_qquickitem::contains()
2075{
2076 // Tests that contains works, but also checks that mapToItem/mapFromItem
2077 // return the correct type (point or rect, not a JS object with those properties),
2078 // as this is a common combination of calls.
2079
2080 QFETCH(int, x);
2081 QFETCH(int, y);
2082 QFETCH(bool, contains);
2083
2084 QQuickView view;
2085 view.setSource(testFileUrl(fileName: "contains.qml"));
2086
2087 QQuickItem *root = qobject_cast<QQuickItem*>(object: view.rootObject());
2088 QVERIFY(root);
2089
2090 QVariant result = false;
2091 QVERIFY(QMetaObject::invokeMethod(root, "childContainsViaMapToItem",
2092 Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, qreal(x)), Q_ARG(QVariant, qreal(y))));
2093 QCOMPARE(result.toBool(), contains);
2094
2095 result = false;
2096 QVERIFY(QMetaObject::invokeMethod(root, "childContainsViaMapFromItem",
2097 Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, qreal(x)), Q_ARG(QVariant, qreal(y))));
2098 QCOMPARE(result.toBool(), contains);
2099}
2100
2101void tst_qquickitem::childAt()
2102{
2103 QQuickView view;
2104 view.setSource(testFileUrl(fileName: "childAtRectangle.qml"));
2105 QQuickItem *root = qobject_cast<QQuickItem*>(object: view.rootObject());
2106
2107 int found = 0;
2108 for (int i = 0; i < 16; i++)
2109 {
2110 if (root->childAt(x: i, y: 0))
2111 found++;
2112 }
2113 QCOMPARE(found, 16);
2114
2115 found = 0;
2116 for (int i = 0; i < 16; i++)
2117 {
2118 if (root->childAt(x: 0, y: i))
2119 found++;
2120 }
2121 QCOMPARE(found, 16);
2122
2123 found = 0;
2124 for (int i = 0; i < 2; i++)
2125 {
2126 if (root->childAt(x: 18 + i, y: 0))
2127 found++;
2128 }
2129 QCOMPARE(found, 1);
2130
2131 found = 0;
2132 for (int i = 0; i < 16; i++)
2133 {
2134 if (root->childAt(x: 18, y: i))
2135 found++;
2136 }
2137 QCOMPARE(found, 1);
2138
2139 QVERIFY(!root->childAt(19,19));
2140}
2141
2142void tst_qquickitem::ignoreButtonPressNotInAcceptedMouseButtons()
2143{
2144 // Verify the fix for QTBUG-31861
2145 TestItem item;
2146 QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::NoButton));
2147
2148 QQuickWindow window;
2149 item.setSize(QSizeF(200,100));
2150 item.setParentItem(window.contentItem());
2151
2152 item.setAcceptedMouseButtons(Qt::LeftButton);
2153 QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::LeftButton));
2154
2155 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2156 QTest::mousePress(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50)); // ignored because it's not LeftButton
2157 QTest::mouseRelease(window: &window, button: Qt::RightButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50)); // ignored because it didn't grab the RightButton press
2158 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(50, 50));
2159
2160 QCOMPARE(item.pressCount, 1);
2161 QCOMPARE(item.releaseCount, 1);
2162}
2163
2164void tst_qquickitem::shortcutOverride()
2165{
2166 QQuickView view;
2167 view.setSource(testFileUrl(fileName: "shortcutOverride.qml"));
2168 QVERIFY(ensureFocus(&view));
2169
2170 QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 0);
2171 QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
2172
2173 QQuickItem *escapeItem = view.rootObject()->property(name: "escapeItem").value<QQuickItem*>();
2174 QVERIFY(escapeItem);
2175 QVERIFY(escapeItem->hasActiveFocus());
2176
2177 // escapeItem's onEscapePressed handler should accept the first escape press event.
2178 QTest::keyPress(window: &view, key: Qt::Key_Escape);
2179 QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
2180 QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
2181 // Now it shouldn't have focus, so it can't handle the next escape press event.
2182 QVERIFY(!escapeItem->hasActiveFocus());
2183
2184 QTest::keyRelease(window: &view, key: Qt::Key_Escape);
2185 QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
2186 QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
2187
2188 QTest::keyPress(window: &view, key: Qt::Key_Escape);
2189 QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
2190 QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1);
2191
2192 QTest::keyRelease(window: &view, key: Qt::Key_Escape);
2193 QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
2194 QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1);
2195}
2196
2197#ifdef TEST_QTBUG_60123
2198void tst_qquickitem::qtBug60123()
2199{
2200 QMainWindow main;
2201 main.resize(w: 400, h: 200);
2202
2203 QQuickView window;
2204 QQuickView window2;
2205 window.setSource(testFileUrl(fileName: "mainWindowQtBug60123.qml"));
2206 window2.setSource(testFileUrl(fileName: "mainWindowQtBug60123.qml"));
2207
2208 // Create central widget for the main window
2209 QWidget *baseWidget = new QWidget(&main);
2210 baseWidget->resize(w: 400, h: 200);
2211 baseWidget->setMaximumHeight(200);
2212 baseWidget->setMaximumWidth(400);
2213 main.setCentralWidget(baseWidget);
2214
2215 // Create container widgets for both windows
2216 QWidget *containers = QWidget::createWindowContainer(window: &window, parent: baseWidget);
2217 QWidget *containers2 = QWidget::createWindowContainer(window: &window2, parent: baseWidget);
2218 containers->setGeometry(ax: 0, ay: 0, aw: 100, ah: 100);
2219 containers2->setGeometry(ax: 100, ay: 100, aw: 100, ah: 100);
2220
2221 // Show and activate the main window
2222 main.show();
2223 QVERIFY(QTest::qWaitForWindowExposed(&main));
2224
2225 // Activate window, test press and release events
2226 auto activateWindowAndTestPress = [] (QQuickView* testWindow) {
2227 testWindow->requestActivate();
2228 QVERIFY(QTest::qWaitForWindowActive(testWindow));
2229 QTest::mousePress(window: testWindow, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10, 10));
2230 QCOMPARE(testWindow->rootObject()->property("lastEvent").toString(), QString("pressed"));
2231 QTest::mouseRelease(window: testWindow, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(10, 10));
2232 QCOMPARE(testWindow->rootObject()->property("lastEvent").toString(), QString("released"));
2233 };
2234
2235 // First press after switching focus window resulted in cancelled event
2236 activateWindowAndTestPress(&window);
2237 activateWindowAndTestPress(&window2);
2238 activateWindowAndTestPress(&window);
2239}
2240#endif
2241void tst_qquickitem::setParentCalledInOnWindowChanged()
2242{
2243 QQuickView view;
2244 view.setSource(testFileUrl(fileName: "setParentInWindowChange.qml"));
2245 QVERIFY(ensureFocus(&view)); // should not crash
2246}
2247
2248void tst_qquickitem::receivesLanguageChangeEvent()
2249{
2250 QQuickWindow window;
2251 window.setFramePosition(QPoint(100, 100));
2252 window.resize(w: 200, h: 200);
2253 window.show();
2254 QVERIFY(QTest::qWaitForWindowExposed(&window));
2255
2256 QScopedPointer<TestItem> child1(new TestItem);
2257 child1->setObjectName(QStringLiteral("child1"));
2258 child1->setSize(QSizeF(200, 100));
2259 child1->setParentItem(window.contentItem());
2260
2261 QScopedPointer<TestItem> child2(new TestItem);
2262 child2->setObjectName(QStringLiteral("child2"));
2263 child2->setSize(QSizeF(50, 50));
2264 child2->setParentItem(child1.data());
2265
2266 QTranslator t;
2267 QVERIFY(t.load("hellotr_la.qm", dataDirectory()));
2268 QVERIFY(QCoreApplication::installTranslator(&t));
2269
2270 QTRY_COMPARE(child1->languageChangeEventCount, 1);
2271 QCOMPARE(child2->languageChangeEventCount, 1);
2272}
2273
2274QTEST_MAIN(tst_qquickitem)
2275
2276#include "tst_qquickitem.moc"
2277

source code of qtdeclarative/tests/auto/quick/qquickitem/tst_qquickitem.cpp