blob: e44302bd7ee41871284c5d75441c87f16cc03bfb [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/qquickview.h>
#include "private/qquickfocusscope_p.h"
#include "private/qquickitem_p.h"
#include <qpa/qwindowsysteminterface.h>
#include <QDebug>
#include <QTimer>
#include <QQmlEngine>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include <QSignalSpy>
#include <QTranslator>
#include <QtCore/qregularexpression.h>
#ifdef TEST_QTBUG_60123
#include <QWidget>
#include <QMainWindow>
#endif
class TestItem : public QQuickItem
{
Q_OBJECT
public:
TestItem(QQuickItem *parent = nullptr)
: QQuickItem(parent), focused(false), pressCount(0), releaseCount(0)
, wheelCount(0), acceptIncomingTouchEvents(true)
, touchEventReached(false), timestamp(0)
, lastWheelEventPos(0, 0), lastWheelEventGlobalPos(0, 0)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
setAcceptTouchEvents(true);
#endif
}
bool focused;
int pressCount;
int releaseCount;
int wheelCount;
bool acceptIncomingTouchEvents;
bool touchEventReached;
ulong timestamp;
QPoint lastWheelEventPos;
QPoint lastWheelEventGlobalPos;
int languageChangeEventCount = 0;
protected:
virtual void focusInEvent(QFocusEvent *) { Q_ASSERT(!focused); focused = true; }
virtual void focusOutEvent(QFocusEvent *) { Q_ASSERT(focused); focused = false; }
virtual void mousePressEvent(QMouseEvent *event) { event->accept(); ++pressCount; }
virtual void mouseReleaseEvent(QMouseEvent *event) { event->accept(); ++releaseCount; }
virtual void touchEvent(QTouchEvent *event) {
touchEventReached = true;
event->setAccepted(acceptIncomingTouchEvents);
}
virtual void wheelEvent(QWheelEvent *event) {
event->accept();
++wheelCount;
timestamp = event->timestamp();
lastWheelEventPos = event->position().toPoint();
lastWheelEventGlobalPos = event->globalPosition().toPoint();
}
bool event(QEvent *e) override
{
if (e->type() == QEvent::LanguageChange)
languageChangeEventCount++;
return QQuickItem::event(e);
}
};
class TestWindow: public QQuickWindow
{
public:
TestWindow()
: QQuickWindow()
{}
virtual bool event(QEvent *event)
{
return QQuickWindow::event(event);
}
};
class TestPolishItem : public QQuickItem
{
Q_OBJECT
public:
TestPolishItem(QQuickItem *parent = nullptr)
: QQuickItem(parent), wasPolished(false) {
}
bool wasPolished;
int repolishLoopCount = 0;
protected:
virtual void updatePolish() {
wasPolished = true;
if (repolishLoopCount > 0) {
--repolishLoopCount;
polish();
}
}
public slots:
void doPolish() {
polish();
}
};
class TestFocusScope : public QQuickFocusScope
{
Q_OBJECT
public:
TestFocusScope(QQuickItem *parent = nullptr) : QQuickFocusScope(parent), focused(false) {}
bool focused;
protected:
virtual void focusInEvent(QFocusEvent *) { Q_ASSERT(!focused); focused = true; }
virtual void focusOutEvent(QFocusEvent *) { Q_ASSERT(focused); focused = false; }
};
class tst_qquickitem : public QQmlDataTest
{
Q_OBJECT
public:
private slots:
void initTestCase();
void noWindow();
void simpleFocus();
void scopedFocus();
void addedToWindow();
void changeParent();
void multipleFocusClears();
void focusSubItemInNonFocusScope();
void parentItemWithFocus();
void reparentFocusedItem();
void constructor();
void setParentItem();
void visible();
void enabled();
void enabledFocus();
void mouseGrab();
void touchEventAcceptIgnore_data();
void touchEventAcceptIgnore();
void polishOutsideAnimation();
void polishOnCompleted();
void wheelEvent_data();
void wheelEvent();
void hoverEvent_data();
void hoverEvent();
void hoverEventInParent();
void paintOrder_data();
void paintOrder();
void acceptedMouseButtons();
void visualParentOwnership();
void visualParentOwnershipWindow();
void testSGInvalidate();
void objectChildTransform();
void contains_data();
void contains();
void childAt();
void ignoreButtonPressNotInAcceptedMouseButtons();
void shortcutOverride();
#ifdef TEST_QTBUG_60123
void qtBug60123();
#endif
void setParentCalledInOnWindowChanged();
void receivesLanguageChangeEvent();
void polishLoopDetection_data();
void polishLoopDetection();
private:
enum PaintOrderOp {
NoOp, Append, Remove, StackBefore, StackAfter, SetZ
};
bool ensureFocus(QWindow *w) {
if (w->width() <=0 || w->height() <= 0)
w->setGeometry(100, 100, 400, 300);
w->show();
w->requestActivate();
return QTest::qWaitForWindowActive(w);
}
};
void tst_qquickitem::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<TestPolishItem>("Qt.test", 1, 0, "TestPolishItem");
}
// Focus still updates when outside a window
void tst_qquickitem::noWindow()
{
QQuickItem *root = new TestItem;
QQuickItem *child = new TestItem(root);
QQuickItem *scope = new TestItem(root);
QQuickFocusScope *scopedChild = new TestFocusScope(scope);
QQuickFocusScope *scopedChild2 = new TestFocusScope(scope);
QCOMPARE(root->hasFocus(), false);
QCOMPARE(child->hasFocus(), false);
QCOMPARE(scope->hasFocus(), false);
QCOMPARE(scopedChild->hasFocus(), false);
QCOMPARE(scopedChild2->hasFocus(), false);
root->setFocus(true);
scope->setFocus(true);
scopedChild2->setFocus(true);
QCOMPARE(root->hasFocus(), false);
QCOMPARE(child->hasFocus(), false);
QCOMPARE(scope->hasFocus(), false);
QCOMPARE(scopedChild->hasFocus(), false);
QCOMPARE(scopedChild2->hasFocus(), true);
root->setFocus(false);
child->setFocus(true);
scopedChild->setFocus(true);
scope->setFocus(false);
QCOMPARE(root->hasFocus(), false);
QCOMPARE(child->hasFocus(), false);
QCOMPARE(scope->hasFocus(), false);
QCOMPARE(scopedChild->hasFocus(), true);
QCOMPARE(scopedChild2->hasFocus(), false);
delete root;
}
struct FocusData {
FocusData() : focus(false), activeFocus(false) {}
void set(bool f, bool af) { focus = f; activeFocus = af; }
bool focus;
bool activeFocus;
};
struct FocusState : public QHash<QQuickItem *, FocusData>
{
FocusState() : activeFocusItem(nullptr) {}
FocusState &operator<<(QQuickItem *item) {
insert(item, FocusData());
return *this;
}
void active(QQuickItem *i) {
activeFocusItem = i;
}
QQuickItem *activeFocusItem;
};
#define FVERIFY() \
do { \
if (focusState.activeFocusItem) { \
QCOMPARE(window.activeFocusItem(), focusState.activeFocusItem); \
if (qobject_cast<TestItem *>(window.activeFocusItem())) \
QCOMPARE(qobject_cast<TestItem *>(window.activeFocusItem())->focused, true); \
else if (qobject_cast<TestFocusScope *>(window.activeFocusItem())) \
QCOMPARE(qobject_cast<TestFocusScope *>(window.activeFocusItem())->focused, true); \
} else { \
QCOMPARE(window.activeFocusItem(), window.contentItem()); \
} \
for (QHash<QQuickItem *, FocusData>::Iterator iter = focusState.begin(); \
iter != focusState.end(); \
iter++) { \
QCOMPARE(iter.key()->hasFocus(), iter.value().focus); \
QCOMPARE(iter.key()->hasActiveFocus(), iter.value().activeFocus); \
} \
} while (false)
// Tests a simple set of top-level scoped items
void tst_qquickitem::simpleFocus()
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *l1c1 = new TestItem(window.contentItem());
QQuickItem *l1c2 = new TestItem(window.contentItem());
QQuickItem *l1c3 = new TestItem(window.contentItem());
QQuickItem *l2c1 = new TestItem(l1c1);
QQuickItem *l2c2 = new TestItem(l1c1);
QQuickItem *l2c3 = new TestItem(l1c3);
FocusState focusState;
focusState << l1c1 << l1c2 << l1c3
<< l2c1 << l2c2 << l2c3;
FVERIFY();
l1c1->setFocus(true);
focusState[l1c1].set(true, true);
focusState.active(l1c1);
FVERIFY();
l2c3->setFocus(true);
focusState[l1c1].set(false, false);
focusState[l2c3].set(true, true);
focusState.active(l2c3);
FVERIFY();
l1c3->setFocus(true);
focusState[l2c3].set(false, false);
focusState[l1c3].set(true, true);
focusState.active(l1c3);
FVERIFY();
l1c2->setFocus(false);
FVERIFY();
l1c3->setFocus(false);
focusState[l1c3].set(false, false);
focusState.active(nullptr);
FVERIFY();
l2c1->setFocus(true);
focusState[l2c1].set(true, true);
focusState.active(l2c1);
FVERIFY();
}
// Items with a focus scope
void tst_qquickitem::scopedFocus()
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *l1c1 = new TestItem(window.contentItem());
QQuickItem *l1c2 = new TestItem(window.contentItem());
QQuickItem *l1c3 = new TestItem(window.contentItem());
QQuickItem *l2c1 = new TestItem(l1c1);
QQuickItem *l2c2 = new TestItem(l1c1);
QQuickItem *l2c3 = new TestFocusScope(l1c3);
QQuickItem *l3c1 = new TestItem(l2c3);
QQuickItem *l3c2 = new TestFocusScope(l2c3);
QQuickItem *l4c1 = new TestItem(l3c2);
QQuickItem *l4c2 = new TestItem(l3c2);
FocusState focusState;
focusState << l1c1 << l1c2 << l1c3
<< l2c1 << l2c2 << l2c3
<< l3c1 << l3c2
<< l4c1 << l4c2;
FVERIFY();
l4c2->setFocus(true);
focusState[l4c2].set(true, false);
FVERIFY();
l4c1->setFocus(true);
focusState[l4c2].set(false, false);
focusState[l4c1].set(true, false);
FVERIFY();
l1c1->setFocus(true);
focusState[l1c1].set(true, true);
focusState.active(l1c1);
FVERIFY();
l3c2->setFocus(true);
focusState[l3c2].set(true, false);
FVERIFY();
l2c3->setFocus(true);
focusState[l1c1].set(false, false);
focusState[l2c3].set(true, true);
focusState[l3c2].set(true, true);
focusState[l4c1].set(true, true);
focusState.active(l4c1);
FVERIFY();
l3c2->setFocus(false);
focusState[l3c2].set(false, false);
focusState[l4c1].set(true, false);
focusState.active(l2c3);
FVERIFY();
l3c2->setFocus(true);
focusState[l3c2].set(true, true);
focusState[l4c1].set(true, true);
focusState.active(l4c1);
FVERIFY();
l4c1->setFocus(false);
focusState[l4c1].set(false, false);
focusState.active(l3c2);
FVERIFY();
l1c3->setFocus(true);
focusState[l1c3].set(true, true);
focusState[l2c3].set(false, false);
focusState[l3c2].set(true, false);
focusState.active(l1c3);
FVERIFY();
}
// Tests focus corrects itself when a tree is added to a window for the first time
void tst_qquickitem::addedToWindow()
{
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *item = new TestItem;
FocusState focusState;
focusState << item;
item->setFocus(true);
focusState[item].set(true, false);
FVERIFY();
item->setParentItem(window.contentItem());
focusState[item].set(true, true);
focusState.active(item);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *item = new TestItem(window.contentItem());
QQuickItem *tree = new TestItem;
QQuickItem *c1 = new TestItem(tree);
QQuickItem *c2 = new TestItem(tree);
FocusState focusState;
focusState << item << tree << c1 << c2;
item->setFocus(true);
c1->setFocus(true);
c2->setFocus(true);
focusState[item].set(true, true);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
focusState.active(item);
FVERIFY();
tree->setParentItem(item);
focusState[c1].set(false, false);
focusState[c2].set(false, false);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *tree = new TestItem;
QQuickItem *c1 = new TestItem(tree);
QQuickItem *c2 = new TestItem(tree);
FocusState focusState;
focusState << tree << c1 << c2;
c1->setFocus(true);
c2->setFocus(true);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
FVERIFY();
tree->setParentItem(window.contentItem());
focusState[c1].set(false, false);
focusState[c2].set(true, true);
focusState.active(c2);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *tree = new TestFocusScope;
QQuickItem *c1 = new TestItem(tree);
QQuickItem *c2 = new TestItem(tree);
FocusState focusState;
focusState << tree << c1 << c2;
c1->setFocus(true);
c2->setFocus(true);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
FVERIFY();
tree->setParentItem(window.contentItem());
focusState[c1].set(false, false);
focusState[c2].set(true, false);
FVERIFY();
tree->setFocus(true);
focusState[tree].set(true, true);
focusState[c2].set(true, true);
focusState.active(c2);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *tree = new TestFocusScope;
QQuickItem *c1 = new TestItem(tree);
QQuickItem *c2 = new TestItem(tree);
FocusState focusState;
focusState << tree << c1 << c2;
tree->setFocus(true);
c1->setFocus(true);
c2->setFocus(true);
focusState[tree].set(true, false);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
FVERIFY();
tree->setParentItem(window.contentItem());
focusState[tree].set(true, true);
focusState[c1].set(false, false);
focusState[c2].set(true, true);
focusState.active(c2);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
QQuickItem *tree = new TestFocusScope;
QQuickItem *c1 = new TestItem(tree);
QQuickItem *c2 = new TestItem(tree);
FocusState focusState;
focusState << child << tree << c1 << c2;
child->setFocus(true);
tree->setFocus(true);
c1->setFocus(true);
c2->setFocus(true);
focusState[child].set(true, true);
focusState[tree].set(true, false);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
focusState.active(child);
FVERIFY();
tree->setParentItem(window.contentItem());
focusState[tree].set(false, false);
focusState[c1].set(false, false);
focusState[c2].set(true, false);
FVERIFY();
tree->setFocus(true);
focusState[child].set(false, false);
focusState[tree].set(true, true);
focusState[c2].set(true, true);
focusState.active(c2);
FVERIFY();
}
}
void tst_qquickitem::changeParent()
{
// Parent to no parent
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
FocusState focusState;
focusState << child;
FVERIFY();
child->setFocus(true);
focusState[child].set(true, true);
focusState.active(child);
FVERIFY();
child->setParentItem(nullptr);
focusState[child].set(true, false);
focusState.active(nullptr);
FVERIFY();
}
// Different parent, same focus scope
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
QQuickItem *child2 = new TestItem(window.contentItem());
FocusState focusState;
focusState << child << child2;
FVERIFY();
child->setFocus(true);
focusState[child].set(true, true);
focusState.active(child);
FVERIFY();
child->setParentItem(child2);
FVERIFY();
}
// Different parent, different focus scope
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
QQuickItem *child2 = new TestFocusScope(window.contentItem());
QQuickItem *item = new TestItem(child);
FocusState focusState;
focusState << child << child2 << item;
FVERIFY();
item->setFocus(true);
focusState[item].set(true, true);
focusState.active(item);
FVERIFY();
item->setParentItem(child2);
focusState[item].set(true, false);
focusState.active(nullptr);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
QQuickItem *child2 = new TestFocusScope(window.contentItem());
QQuickItem *item = new TestItem(child2);
FocusState focusState;
focusState << child << child2 << item;
FVERIFY();
item->setFocus(true);
focusState[item].set(true, false);
focusState.active(nullptr);
FVERIFY();
item->setParentItem(child);
focusState[item].set(true, true);
focusState.active(item);
FVERIFY();
}
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *child = new TestItem(window.contentItem());
QQuickItem *child2 = new TestFocusScope(window.contentItem());
QQuickItem *item = new TestItem(child2);
FocusState focusState;
focusState << child << child2 << item;
FVERIFY();
child->setFocus(true);
item->setFocus(true);
focusState[child].set(true, true);
focusState[item].set(true, false);
focusState.active(child);
FVERIFY();
item->setParentItem(child);
focusState[item].set(false, false);
FVERIFY();
}
// child has active focus, then its fs parent changes parent to 0, then
// child is deleted, then its parent changes again to a valid parent
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *item = new TestFocusScope(window.contentItem());
QQuickItem *child = new TestItem(item);
QQuickItem *child2 = new TestItem;
FocusState focusState;
focusState << item << child;
FVERIFY();
item->setFocus(true);
child->setFocus(true);
focusState[child].set(true, true);
focusState[item].set(true, true);
focusState.active(child);
FVERIFY();
item->setParentItem(nullptr);
focusState[child].set(true, false);
focusState[item].set(true, false);
focusState.active(nullptr);
FVERIFY();
focusState.remove(child);
delete child;
item->setParentItem(window.contentItem());
focusState[item].set(true, true);
focusState.active(item);
FVERIFY();
delete child2;
}
}
void tst_qquickitem::multipleFocusClears()
{
//Multiple clears of focus inside a focus scope shouldn't crash. QTBUG-24714
QQuickView view;
view.setSource(testFileUrl("multipleFocusClears.qml"));
view.show();
QVERIFY(ensureFocus(&view));
QTRY_COMPARE(QGuiApplication::focusWindow(), &view);
}
void tst_qquickitem::focusSubItemInNonFocusScope()
{
QQuickView view;
view.setSource(testFileUrl("focusSubItemInNonFocusScope.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowActive(&view));
QQuickItem *dummyItem = view.rootObject()->findChild<QQuickItem *>("dummyItem");
QVERIFY(dummyItem);
QQuickItem *textInput = view.rootObject()->findChild<QQuickItem *>("textInput");
QVERIFY(textInput);
QVERIFY(dummyItem->hasFocus());
QVERIFY(!textInput->hasFocus());
QVERIFY(dummyItem->hasActiveFocus());
QVERIFY(QMetaObject::invokeMethod(textInput, "forceActiveFocus"));
QVERIFY(!dummyItem->hasFocus());
QVERIFY(textInput->hasFocus());
QVERIFY(textInput->hasActiveFocus());
}
void tst_qquickitem::parentItemWithFocus()
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
{
QQuickItem parent;
QQuickItem child;
FocusState focusState;
focusState << &parent << &child;
FVERIFY();
parent.setFocus(true);
child.setFocus(true);
focusState[&parent].set(true, false);
focusState[&child].set(true, false);
FVERIFY();
child.setParentItem(&parent);
focusState[&parent].set(true, false);
focusState[&child].set(false, false);
FVERIFY();
parent.setParentItem(window.contentItem());
focusState[&parent].set(true, true);
focusState[&child].set(false, false);
focusState.active(&parent);
FVERIFY();
child.forceActiveFocus();
focusState[&parent].set(false, false);
focusState[&child].set(true, true);
focusState.active(&child);
FVERIFY();
} {
QQuickItem parent;
QQuickItem child;
QQuickItem grandchild(&child);
FocusState focusState;
focusState << &parent << &child << &grandchild;
FVERIFY();
parent.setFocus(true);
grandchild.setFocus(true);
focusState[&parent].set(true, false);
focusState[&child].set(false, false);
focusState[&grandchild].set(true, false);
FVERIFY();
child.setParentItem(&parent);
focusState[&parent].set(true, false);
focusState[&child].set(false, false);
focusState[&grandchild].set(false, false);
FVERIFY();
parent.setParentItem(window.contentItem());
focusState[&parent].set(true, true);
focusState[&child].set(false, false);
focusState[&grandchild].set(false, false);
focusState.active(&parent);
FVERIFY();
grandchild.forceActiveFocus();
focusState[&parent].set(false, false);
focusState[&child].set(false, false);
focusState[&grandchild].set(true, true);
focusState.active(&grandchild);
FVERIFY();
}
{
QQuickItem parent;
QQuickItem child1;
QQuickItem child2;
FocusState focusState;
focusState << &parent << &child1 << &child2;
parent.setFocus(true);
child1.setParentItem(&parent);
child2.setParentItem(&parent);
focusState[&parent].set(true, false);
focusState[&child1].set(false, false);
focusState[&child2].set(false, false);
FVERIFY();
child1.setFocus(true);
focusState[&parent].set(false, false);
focusState[&child1].set(true, false);
FVERIFY();
parent.setFocus(true);
focusState[&parent].set(true, false);
focusState[&child1].set(false, false);
FVERIFY();
}
}
void tst_qquickitem::reparentFocusedItem()
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem parent(window.contentItem());
QQuickItem child(&parent);
QQuickItem sibling(&parent);
QQuickItem grandchild(&child);
FocusState focusState;
focusState << &parent << &child << &sibling << &grandchild;
FVERIFY();
grandchild.setFocus(true);
focusState[&parent].set(false, false);
focusState[&child].set(false, false);
focusState[&sibling].set(false, false);
focusState[&grandchild].set(true, true);
focusState.active(&grandchild);
FVERIFY();
// Parenting the item to another item within the same focus scope shouldn't change it's focus.
child.setParentItem(&sibling);
FVERIFY();
}
void tst_qquickitem::constructor()
{
QScopedPointer<QQuickItem> root(new QQuickItem);
QVERIFY(!root->parent());
QVERIFY(!root->parentItem());
QQuickItem *child1 = new QQuickItem(root.data());
QCOMPARE(child1->parent(), root.data());
QCOMPARE(child1->parentItem(), root.data());
QCOMPARE(root->childItems().count(), 1);
QCOMPARE(root->childItems().at(0), child1);
QQuickItem *child2 = new QQuickItem(root.data());
QCOMPARE(child2->parent(), root.data());
QCOMPARE(child2->parentItem(), root.data());
QCOMPARE(root->childItems().count(), 2);
QCOMPARE(root->childItems().at(0), child1);
QCOMPARE(root->childItems().at(1), child2);
}
void tst_qquickitem::setParentItem()
{
QQuickItem *root = new QQuickItem;
QVERIFY(!root->parent());
QVERIFY(!root->parentItem());
QQuickItem *child1 = new QQuickItem;
QVERIFY(!child1->parent());
QVERIFY(!child1->parentItem());
child1->setParentItem(root);
QVERIFY(!child1->parent());
QCOMPARE(child1->parentItem(), root);
QCOMPARE(root->childItems().count(), 1);
QCOMPARE(root->childItems().at(0), child1);
QQuickItem *child2 = new QQuickItem;
QVERIFY(!child2->parent());
QVERIFY(!child2->parentItem());
child2->setParentItem(root);
QVERIFY(!child2->parent());
QCOMPARE(child2->parentItem(), root);
QCOMPARE(root->childItems().count(), 2);
QCOMPARE(root->childItems().at(0), child1);
QCOMPARE(root->childItems().at(1), child2);
child1->setParentItem(nullptr);
QVERIFY(!child1->parent());
QVERIFY(!child1->parentItem());
QCOMPARE(root->childItems().count(), 1);
QCOMPARE(root->childItems().at(0), child2);
delete root;
QVERIFY(!child1->parent());
QVERIFY(!child1->parentItem());
QVERIFY(!child2->parent());
QVERIFY(!child2->parentItem());
delete child1;
delete child2;
}
void tst_qquickitem::visible()
{
QQuickItem *root = new QQuickItem;
QQuickItem *child1 = new QQuickItem;
child1->setParentItem(root);
QQuickItem *child2 = new QQuickItem;
child2->setParentItem(root);
QVERIFY(child1->isVisible());
QVERIFY(child2->isVisible());
root->setVisible(false);
QVERIFY(!child1->isVisible());
QVERIFY(!child2->isVisible());
root->setVisible(true);
QVERIFY(child1->isVisible());
QVERIFY(child2->isVisible());
child1->setVisible(false);
QVERIFY(!child1->isVisible());
QVERIFY(child2->isVisible());
child2->setParentItem(child1);
QVERIFY(!child1->isVisible());
QVERIFY(!child2->isVisible());
child2->setParentItem(root);
QVERIFY(!child1->isVisible());
QVERIFY(child2->isVisible());
delete root;
delete child1;
delete child2;
}
void tst_qquickitem::enabled()
{
QQuickItem *root = new QQuickItem;
QQuickItem *child1 = new QQuickItem;
child1->setParentItem(root);
QQuickItem *child2 = new QQuickItem;
child2->setParentItem(root);
QVERIFY(child1->isEnabled());
QVERIFY(child2->isEnabled());
root->setEnabled(false);
QVERIFY(!child1->isEnabled());
QVERIFY(!child2->isEnabled());
root->setEnabled(true);
QVERIFY(child1->isEnabled());
QVERIFY(child2->isEnabled());
child1->setEnabled(false);
QVERIFY(!child1->isEnabled());
QVERIFY(child2->isEnabled());
child2->setParentItem(child1);
QVERIFY(!child1->isEnabled());
QVERIFY(!child2->isEnabled());
child2->setParentItem(root);
QVERIFY(!child1->isEnabled());
QVERIFY(child2->isEnabled());
delete root;
delete child1;
delete child2;
}
void tst_qquickitem::enabledFocus()
{
QQuickWindow window;
QVERIFY(ensureFocus(&window));
QQuickFocusScope root;
root.setFocus(true);
root.setEnabled(false);
QCOMPARE(root.isEnabled(), false);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), false);
root.setParentItem(window.contentItem());
QCOMPARE(root.isEnabled(), false);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), window.contentItem());
root.setEnabled(true);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
QQuickItem child1;
child1.setParentItem(&root);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
QQuickItem child2;
child2.setFocus(true);
child2.setParentItem(&root);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(child2.isEnabled(), true);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), true);
QCOMPARE(window.activeFocusItem(), &child2);
child2.setEnabled(false);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
child1.setEnabled(false);
QCOMPARE(child1.isEnabled(), false);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
child1.setFocus(true);
QCOMPARE(child1.isEnabled(), false);
QCOMPARE(child1.hasFocus(), true);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), false);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
child1.setEnabled(true);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), true);
QCOMPARE(child1.hasActiveFocus(), true);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&child1));
root.setFocus(false);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), false);
QCOMPARE(root.hasActiveFocus(), false);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), true);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), window.contentItem());
child2.forceActiveFocus();
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
root.setEnabled(false);
QCOMPARE(root.isEnabled(), false);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), false);
QCOMPARE(child1.isEnabled(), false);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), window.contentItem());
child1.forceActiveFocus();
QCOMPARE(root.isEnabled(), false);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), false);
QCOMPARE(child1.isEnabled(), false);
QCOMPARE(child1.hasFocus(), true);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), false);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), window.contentItem());
root.setEnabled(true);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), true);
QCOMPARE(child1.hasActiveFocus(), true);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), false);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&child1));
child2.setFocus(true);
QCOMPARE(root.isEnabled(), true);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), true);
QCOMPARE(child1.isEnabled(), true);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root));
root.setEnabled(false);
QCOMPARE(root.isEnabled(), false);
QCOMPARE(root.hasFocus(), true);
QCOMPARE(root.hasActiveFocus(), false);
QCOMPARE(child1.isEnabled(), false);
QCOMPARE(child1.hasFocus(), false);
QCOMPARE(child1.hasActiveFocus(), false);
QCOMPARE(child2.isEnabled(), false);
QCOMPARE(child2.hasFocus(), true);
QCOMPARE(child2.hasActiveFocus(), false);
QCOMPARE(window.activeFocusItem(), window.contentItem());
}
static inline QByteArray msgItem(const QQuickItem *item)
{
QString result;
QDebug(&result) << item;
return result.toLocal8Bit();
}
void tst_qquickitem::mouseGrab()
{
#ifdef Q_OS_WIN
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
QSKIP("Fails in the CI for ANGLE builds on Windows, QTBUG-32664");
#endif
QQuickWindow window;
window.setFramePosition(QPoint(100, 100));
window.resize(200, 200);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QScopedPointer<TestItem> child1(new TestItem);
child1->setObjectName(QStringLiteral("child1"));
child1->setAcceptedMouseButtons(Qt::LeftButton);
child1->setSize(QSizeF(200, 100));
child1->setParentItem(window.contentItem());
QScopedPointer<TestItem> child2(new TestItem);
child2->setObjectName(QStringLiteral("child2"));
child2->setAcceptedMouseButtons(Qt::LeftButton);
child2->setY(51);
child2->setSize(QSizeF(200, 100));
child2->setParentItem(window.contentItem());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(100);
QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
QTest::qWait(100);
QCOMPARE(child1->pressCount, 1);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()).constData());
QCOMPARE(child1->releaseCount, 1);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
QCOMPARE(child1->pressCount, 2);
child1->setEnabled(false);
QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()).constData());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QCOMPARE(child1->releaseCount, 1);
child1->setEnabled(true);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
QCOMPARE(child1->pressCount, 3);
child1->setVisible(false);
QVERIFY2(window.mouseGrabberItem() == nullptr, msgItem(window.mouseGrabberItem()));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QCOMPARE(child1->releaseCount, 1);
child1->setVisible(true);
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QVERIFY2(window.mouseGrabberItem() == child1.data(), msgItem(window.mouseGrabberItem()).constData());
QCOMPARE(child1->pressCount, 4);
child2->grabMouse();
QVERIFY2(window.mouseGrabberItem() == child2.data(), msgItem(window.mouseGrabberItem()).constData());
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QCOMPARE(child1->releaseCount, 1);
QCOMPARE(child2->releaseCount, 1);
child2->grabMouse();
QVERIFY2(window.mouseGrabberItem() == child2.data(), msgItem(window.mouseGrabberItem()).constData());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QCOMPARE(child1->pressCount, 4);
QCOMPARE(child2->pressCount, 1);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50,50));
QTest::qWait(50);
QCOMPARE(child1->releaseCount, 1);
QCOMPARE(child2->releaseCount, 2);
}
void tst_qquickitem::touchEventAcceptIgnore_data()
{
QTest::addColumn<bool>("itemSupportsTouch");
QTest::newRow("with touch") << true;
QTest::newRow("without touch") << false;
}
void tst_qquickitem::touchEventAcceptIgnore()
{
QFETCH(bool, itemSupportsTouch);
TestWindow window;
window.resize(100, 100);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QScopedPointer<TestItem> item(new TestItem);
item->setSize(QSizeF(100, 100));
item->setParentItem(window.contentItem());
item->acceptIncomingTouchEvents = itemSupportsTouch;
static QTouchDevice* device = nullptr;
if (!device) {
device =new QTouchDevice;
device->setType(QTouchDevice::TouchScreen);
QWindowSystemInterface::registerTouchDevice(device);
}
// Send Begin, Update & End touch sequence
{
QTouchEvent::TouchPoint point;
point.setId(1);
point.setPos(QPointF(50, 50));
point.setScreenPos(point.pos());
point.setState(Qt::TouchPointPressed);
QTouchEvent event(QEvent::TouchBegin, device,
Qt::NoModifier,
Qt::TouchPointPressed,
QList<QTouchEvent::TouchPoint>() << point);
event.setAccepted(true);
item->touchEventReached = false;
bool accepted = window.event(&event);
QQuickTouchUtils::flush(&window);
QVERIFY(item->touchEventReached);
// always true because QtQuick always eats touch events so as to not
// allow QtGui to synthesise them for us.
QCOMPARE(accepted && event.isAccepted(), true);
}
{
QTouchEvent::TouchPoint point;
point.setId(1);
point.setPos(QPointF(60, 60));
point.setScreenPos(point.pos());
point.setState(Qt::TouchPointMoved);
QTouchEvent event(QEvent::TouchUpdate, device,
Qt::NoModifier,
Qt::TouchPointMoved,
QList<QTouchEvent::TouchPoint>() << point);
event.setAccepted(true);
item->touchEventReached = false;
bool accepted = window.event(&event);
QQuickTouchUtils::flush(&window);
QCOMPARE(item->touchEventReached, itemSupportsTouch);
// always true because QtQuick always eats touch events so as to not
// allow QtGui to synthesise them for us.
QCOMPARE(accepted && event.isAccepted(), true);
}
{
QTouchEvent::TouchPoint point;
point.setId(1);
point.setPos(QPointF(60, 60));
point.setScreenPos(point.pos());
point.setState(Qt::TouchPointReleased);
QTouchEvent event(QEvent::TouchEnd, device,
Qt::NoModifier,
Qt::TouchPointReleased,
QList<QTouchEvent::TouchPoint>() << point);
event.setAccepted(true);
item->touchEventReached = false;
bool accepted = window.event(&event);
QQuickTouchUtils::flush(&window);
QCOMPARE(item->touchEventReached, itemSupportsTouch);
// always true because QtQuick always eats touch events so as to not
// allow QtGui to synthesise them for us.
QCOMPARE(accepted && event.isAccepted(), true);
}
}
void tst_qquickitem::polishOutsideAnimation()
{
QQuickWindow *window = new QQuickWindow;
window->resize(200, 200);
window->show();
TestPolishItem *item = new TestPolishItem(window->contentItem());
item->setSize(QSizeF(200, 100));
QTest::qWait(50);
QTimer::singleShot(10, item, SLOT(doPolish()));
QTRY_VERIFY(item->wasPolished);
delete item;
delete window;
}
void tst_qquickitem::polishOnCompleted()
{
QQuickView view;
view.setSource(testFileUrl("polishOnCompleted.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
TestPolishItem *item = qobject_cast<TestPolishItem*>(view.rootObject());
QVERIFY(item);
QTRY_VERIFY(item->wasPolished);
}
struct PolishItemSpan {
int itemCount; // Number of items...
int repolishCount; // ...repolishing 'repolishCount' times
};
/*
* For instance, two consecutive spans {99,0} and {1,2000} } instructs to
* construct 99 items with no repolish, and 1 item with 2000 repolishes (in that sibling order)
*/
typedef QVector<PolishItemSpan> PolishItemSpans;
Q_DECLARE_METATYPE(PolishItemSpan)
Q_DECLARE_METATYPE(PolishItemSpans)
void tst_qquickitem::polishLoopDetection_data()
{
QTest::addColumn<PolishItemSpans>("listOfItemsToPolish");
QTest::addColumn<int>("expectedNumberOfWarnings");
QTest::newRow("test1.100") << PolishItemSpans({ {1, 100} }) << 0;
QTest::newRow("test1.1002") << PolishItemSpans({ {1, 1002} }) << 3;
QTest::newRow("test1.2020") << PolishItemSpans({ {1, 2020} }) << 10;
QTest::newRow("test5.1") << PolishItemSpans({ {5, 1} }) << 0;
QTest::newRow("test5.10") << PolishItemSpans({ {5, 10} }) << 0;
QTest::newRow("test5.100") << PolishItemSpans({ {5, 100} }) << 0;
QTest::newRow("test5.1000") << PolishItemSpans({ {5, 1000} }) << 5;
QTest::newRow("test1000.1") << PolishItemSpans({ {1000,1} }) << 0;
QTest::newRow("test2000.1") << PolishItemSpans({ {2000,1} }) << 0;
QTest::newRow("test99.0-1.1100") << PolishItemSpans({ {99,0},{1,1100} }) << 5;
QTest::newRow("test98.0-2.1100") << PolishItemSpans({ {98,0},{2,1100} }) << 5+5;
// reverse the two above
QTest::newRow("test1.1100-99.0") << PolishItemSpans({ {1,1100},{99,0} }) << 5;
QTest::newRow("test2.1100-98.0") << PolishItemSpans({ {2,1100},{98,0} }) << 5+5;
}
void tst_qquickitem::polishLoopDetection()
{
QFETCH(PolishItemSpans, listOfItemsToPolish);
QFETCH(int, expectedNumberOfWarnings);
QQuickWindow window;
window.resize(200, 200);
window.show();
TestPolishItem *item = nullptr;
int count = 0;
for (PolishItemSpan s : listOfItemsToPolish) {
for (int i = 0; i < s.itemCount; ++i) {
item = new TestPolishItem(window.contentItem());
item->setSize(QSizeF(200, 100));
item->repolishLoopCount = s.repolishCount;
item->setObjectName(QString::fromLatin1("obj%1").arg(count++));
}
}
for (int i = 0; i < expectedNumberOfWarnings; ++i) {
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*possible QQuickItem..polish.. loop.*"));
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*TestPolishItem.* called polish.. inside updatePolish.. of TestPolishItem.*"));
}
QList<QQuickItem*> items = window.contentItem()->childItems();
for (int i = 0; i < items.count(); ++i) {
static_cast<TestPolishItem*>(items.at(i))->doPolish();
}
item = static_cast<TestPolishItem*>(items.first());
// item is the last item, so we wait until the last item reached 0
QVERIFY(QTest::qWaitFor([=](){return item->repolishLoopCount == 0 && item->wasPolished;}));
}
void tst_qquickitem::wheelEvent_data()
{
QTest::addColumn<bool>("visible");
QTest::addColumn<bool>("enabled");
QTest::newRow("visible and enabled") << true << true;
QTest::newRow("visible and disabled") << true << false;
QTest::newRow("invisible and enabled") << false << true;
QTest::newRow("invisible and disabled") << false << false;
}
void tst_qquickitem::wheelEvent()
{
QFETCH(bool, visible);
QFETCH(bool, enabled);
const bool shouldReceiveWheelEvents = visible && enabled;
const int width = 200;
const int height = 200;
QQuickWindow window;
window.resize(width, height);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
TestItem *item = new TestItem;
item->setSize(QSizeF(width, height));
item->setParentItem(window.contentItem());
item->setEnabled(enabled);
item->setVisible(visible);
QPoint localPoint(width / 2, height / 2);
QPoint globalPoint = window.mapToGlobal(localPoint);
QWheelEvent event(localPoint, globalPoint, QPoint(0, 0), QPoint(0, -120),
Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
event.setTimestamp(123456UL);
event.setAccepted(false);
QGuiApplication::sendEvent(&window, &event);
if (shouldReceiveWheelEvents) {
QVERIFY(event.isAccepted());
QCOMPARE(item->wheelCount, 1);
QCOMPARE(item->timestamp, 123456UL);
QCOMPARE(item->lastWheelEventPos, localPoint);
QCOMPARE(item->lastWheelEventGlobalPos, globalPoint);
} else {
QVERIFY(!event.isAccepted());
QCOMPARE(item->wheelCount, 0);
}
}
class HoverItem : public QQuickItem
{
Q_OBJECT
public:
HoverItem(QQuickItem *parent = nullptr)
: QQuickItem(parent), hoverEnterCount(0), hoverMoveCount(0), hoverLeaveCount(0)
{ }
void resetCounters() {
hoverEnterCount = 0;
hoverMoveCount = 0;
hoverLeaveCount = 0;
}
int hoverEnterCount;
int hoverMoveCount;
int hoverLeaveCount;
protected:
virtual void hoverEnterEvent(QHoverEvent *event) {
event->accept();
++hoverEnterCount;
}
virtual void hoverMoveEvent(QHoverEvent *event) {
event->accept();
++hoverMoveCount;
}
virtual void hoverLeaveEvent(QHoverEvent *event) {
event->accept();
++hoverLeaveCount;
}
};
void tst_qquickitem::hoverEvent_data()
{
QTest::addColumn<bool>("visible");
QTest::addColumn<bool>("enabled");
QTest::addColumn<bool>("acceptHoverEvents");
QTest::newRow("visible, enabled, accept hover") << true << true << true;
QTest::newRow("visible, disabled, accept hover") << true << false << true;
QTest::newRow("invisible, enabled, accept hover") << false << true << true;
QTest::newRow("invisible, disabled, accept hover") << false << false << true;
QTest::newRow("visible, enabled, not accept hover") << true << true << false;
QTest::newRow("visible, disabled, not accept hover") << true << false << false;
QTest::newRow("invisible, enabled, not accept hover") << false << true << false;
QTest::newRow("invisible, disabled, not accept hover") << false << false << false;
}
// ### For some unknown reason QTest::mouseMove() isn't working correctly.
static void sendMouseMove(QObject *object, const QPoint &position)
{
QMouseEvent moveEvent(QEvent::MouseMove, position, Qt::NoButton, Qt::NoButton, nullptr);
QGuiApplication::sendEvent(object, &moveEvent);
}
void tst_qquickitem::hoverEvent()
{
QFETCH(bool, visible);
QFETCH(bool, enabled);
QFETCH(bool, acceptHoverEvents);
QQuickWindow *window = new QQuickWindow();
window->resize(200, 200);
window->show();
HoverItem *item = new HoverItem;
item->setSize(QSizeF(100, 100));
item->setParentItem(window->contentItem());
item->setEnabled(enabled);
item->setVisible(visible);
item->setAcceptHoverEvents(acceptHoverEvents);
const QPoint outside(150, 150);
const QPoint inside(50, 50);
const QPoint anotherInside(51, 51);
sendMouseMove(window, outside);
item->resetCounters();
// Enter, then move twice inside, then leave.
sendMouseMove(window, inside);
sendMouseMove(window, anotherInside);
sendMouseMove(window, inside);
sendMouseMove(window, outside);
const bool shouldReceiveHoverEvents = visible && enabled && acceptHoverEvents;
if (shouldReceiveHoverEvents) {
QCOMPARE(item->hoverEnterCount, 1);
QCOMPARE(item->hoverMoveCount, 2);
QCOMPARE(item->hoverLeaveCount, 1);
} else {
QCOMPARE(item->hoverEnterCount, 0);
QCOMPARE(item->hoverMoveCount, 0);
QCOMPARE(item->hoverLeaveCount, 0);
}
delete window;
}
void tst_qquickitem::hoverEventInParent()
{
QQuickWindow window;
window.resize(200, 200);
window.show();
HoverItem *parentItem = new HoverItem(window.contentItem());
parentItem->setSize(QSizeF(200, 200));
parentItem->setAcceptHoverEvents(true);
HoverItem *leftItem = new HoverItem(parentItem);
leftItem->setSize(QSizeF(100, 200));
leftItem->setAcceptHoverEvents(true);
HoverItem *rightItem = new HoverItem(parentItem);
rightItem->setSize(QSizeF(100, 200));
rightItem->setPosition(QPointF(100, 0));
rightItem->setAcceptHoverEvents(true);
const QPoint insideLeft(50, 100);
const QPoint insideRight(150, 100);
sendMouseMove(&window, insideLeft);
parentItem->resetCounters();
leftItem->resetCounters();
rightItem->resetCounters();
sendMouseMove(&window, insideRight);
QCOMPARE(parentItem->hoverEnterCount, 0);
QCOMPARE(parentItem->hoverLeaveCount, 0);
QCOMPARE(leftItem->hoverEnterCount, 0);
QCOMPARE(leftItem->hoverLeaveCount, 1);
QCOMPARE(rightItem->hoverEnterCount, 1);
QCOMPARE(rightItem->hoverLeaveCount, 0);
sendMouseMove(&window, insideLeft);
QCOMPARE(parentItem->hoverEnterCount, 0);
QCOMPARE(parentItem->hoverLeaveCount, 0);
QCOMPARE(leftItem->hoverEnterCount, 1);
QCOMPARE(leftItem->hoverLeaveCount, 1);
QCOMPARE(rightItem->hoverEnterCount, 1);
QCOMPARE(rightItem->hoverLeaveCount, 1);
}
void tst_qquickitem::paintOrder_data()
{
const QUrl order1Url = testFileUrl("order.1.qml");
const QUrl order2Url = testFileUrl("order.2.qml");
QTest::addColumn<QUrl>("source");
QTest::addColumn<int>("op");
QTest::addColumn<QVariant>("param1");
QTest::addColumn<QVariant>("param2");
QTest::addColumn<QStringList>("expected");
QTest::newRow("test 1 noop") << order1Url
<< int(NoOp) << QVariant() << QVariant()
<< (QStringList() << "1" << "2" << "3");
QTest::newRow("test 1 add") << order1Url
<< int(Append) << QVariant("new") << QVariant()
<< (QStringList() << "1" << "2" << "3" << "new");
QTest::newRow("test 1 remove") << order1Url
<< int(Remove) << QVariant(1) << QVariant()
<< (QStringList() << "1" << "3");
QTest::newRow("test 1 stack before") << order1Url
<< int(StackBefore) << QVariant(2) << QVariant(1)
<< (QStringList() << "1" << "3" << "2");
QTest::newRow("test 1 stack after") << order1Url
<< int(StackAfter) << QVariant(0) << QVariant(1)
<< (QStringList() << "2" << "1" << "3");
QTest::newRow("test 1 set z") << order1Url
<< int(SetZ) << QVariant(1) << QVariant(qreal(1.))
<< (QStringList() << "1" << "3" << "2");
QTest::newRow("test 2 noop") << order2Url
<< int(NoOp) << QVariant() << QVariant()
<< (QStringList() << "1" << "3" << "2");
QTest::newRow("test 2 add") << order2Url
<< int(Append) << QVariant("new") << QVariant()
<< (QStringList() << "1" << "3" << "new" << "2");
QTest::newRow("test 2 remove 1") << order2Url
<< int(Remove) << QVariant(1) << QVariant()
<< (QStringList() << "1" << "3");
QTest::newRow("test 2 remove 2") << order2Url
<< int(Remove) << QVariant(2) << QVariant()
<< (QStringList() << "1" << "2");
QTest::newRow("test 2 stack before 1") << order2Url
<< int(StackBefore) << QVariant(1) << QVariant(0)
<< (QStringList() << "1" << "3" << "2");
QTest::newRow("test 2 stack before 2") << order2Url
<< int(StackBefore) << QVariant(2) << QVariant(0)
<< (QStringList() << "3" << "1" << "2");
QTest::newRow("test 2 stack after 1") << order2Url
<< int(StackAfter) << QVariant(0) << QVariant(1)
<< (QStringList() << "1" << "3" << "2");
QTest::newRow("test 2 stack after 2") << order2Url
<< int(StackAfter) << QVariant(0) << QVariant(2)
<< (QStringList() << "3" << "1" << "2");
QTest::newRow("test 1 set z") << order1Url
<< int(SetZ) << QVariant(2) << QVariant(qreal(2.))
<< (QStringList() << "1" << "2" << "3");
}
void tst_qquickitem::paintOrder()
{
QFETCH(QUrl, source);
QFETCH(int, op);
QFETCH(QVariant, param1);
QFETCH(QVariant, param2);
QFETCH(QStringList, expected);
QQuickView view;
view.setSource(source);
QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject());
QVERIFY(root);
switch (op) {
case Append: {
QQuickItem *item = new QQuickItem(root);
item->setObjectName(param1.toString());
}
break;
case Remove: {
QQuickItem *item = root->childItems().at(param1.toInt());
delete item;
}
break;
case StackBefore: {
QQuickItem *item1 = root->childItems().at(param1.toInt());
QQuickItem *item2 = root->childItems().at(param2.toInt());
item1->stackBefore(item2);
}
break;
case StackAfter: {
QQuickItem *item1 = root->childItems().at(param1.toInt());
QQuickItem *item2 = root->childItems().at(param2.toInt());
item1->stackAfter(item2);
}
break;
case SetZ: {
QQuickItem *item = root->childItems().at(param1.toInt());
item->setZ(param2.toReal());
}
break;
default:
break;
}
QList<QQuickItem*> list = QQuickItemPrivate::get(root)->paintOrderChildItems();
QStringList items;
for (int i = 0; i < list.count(); ++i)
items << list.at(i)->objectName();
QCOMPARE(items, expected);
}
void tst_qquickitem::acceptedMouseButtons()
{
TestItem item;
QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::NoButton));
QQuickWindow window;
item.setSize(QSizeF(200,100));
item.setParentItem(window.contentItem());
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 0);
QCOMPARE(item.releaseCount, 0);
QTest::mousePress(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 0);
QCOMPARE(item.releaseCount, 0);
QTest::mousePress(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 0);
QCOMPARE(item.releaseCount, 0);
item.setAcceptedMouseButtons(Qt::LeftButton);
QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::LeftButton));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 1);
QCOMPARE(item.releaseCount, 1);
QTest::mousePress(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 1);
QCOMPARE(item.releaseCount, 1);
QTest::mousePress(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 1);
QCOMPARE(item.releaseCount, 1);
item.setAcceptedMouseButtons(Qt::RightButton | Qt::MiddleButton);
QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 1);
QCOMPARE(item.releaseCount, 1);
QTest::mousePress(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 2);
QCOMPARE(item.releaseCount, 2);
QTest::mousePress(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QTest::mouseRelease(&window, Qt::MiddleButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 3);
QCOMPARE(item.releaseCount, 3);
}
static void gc(QQmlEngine &engine)
{
engine.collectGarbage();
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QCoreApplication::processEvents();
}
void tst_qquickitem::visualParentOwnership()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("visualParentOwnership.qml"));
QScopedPointer<QQuickItem> root(qobject_cast<QQuickItem*>(component.create()));
QVERIFY(root);
QVariant newObject;
{
QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> newItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!newItem.isNull());
QVERIFY(!newItem->parent());
QVERIFY(!newItem->parentItem());
newItem->setParentItem(root.data());
gc(engine);
QVERIFY(!newItem.isNull());
newItem->setParentItem(nullptr);
gc(engine);
QVERIFY(newItem.isNull());
}
{
QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> firstItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!firstItem.isNull());
firstItem->setParentItem(root.data());
QVERIFY(QMetaObject::invokeMethod(root.data(), "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> secondItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!firstItem.isNull());
secondItem->setParentItem(firstItem);
gc(engine);
delete firstItem;
root->setProperty("keepAliveProperty", newObject);
gc(engine);
QVERIFY(!secondItem.isNull());
root->setProperty("keepAliveProperty", QVariant());
gc(engine);
QVERIFY(secondItem.isNull());
}
}
void tst_qquickitem::visualParentOwnershipWindow()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("visualParentOwnershipWindow.qml"));
QQuickWindow *window = qobject_cast<QQuickWindow*>(component.create());
QVERIFY(window);
QQuickItem *root = window->contentItem();
QVariant newObject;
{
QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> newItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!newItem.isNull());
QVERIFY(!newItem->parent());
QVERIFY(!newItem->parentItem());
newItem->setParentItem(root);
gc(engine);
QVERIFY(!newItem.isNull());
newItem->setParentItem(nullptr);
gc(engine);
QVERIFY(newItem.isNull());
}
{
QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> firstItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!firstItem.isNull());
firstItem->setParentItem(root);
QVERIFY(QMetaObject::invokeMethod(window, "createItemWithoutParent", Q_RETURN_ARG(QVariant, newObject)));
QPointer<QQuickItem> secondItem = qvariant_cast<QQuickItem*>(newObject);
QVERIFY(!firstItem.isNull());
secondItem->setParentItem(firstItem);
gc(engine);
delete firstItem;
window->setProperty("keepAliveProperty", newObject);
gc(engine);
QVERIFY(!secondItem.isNull());
window->setProperty("keepAliveProperty", QVariant());
gc(engine);
QVERIFY(secondItem.isNull());
}
}
class InvalidatedItem : public QQuickItem {
Q_OBJECT
signals:
void invalidated();
public slots:
void invalidateSceneGraph() { emit invalidated(); }
};
void tst_qquickitem::testSGInvalidate()
{
for (int i=0; i<2; ++i) {
QScopedPointer<QQuickView> view(new QQuickView());
InvalidatedItem *item = new InvalidatedItem();
int expected = 0;
if (i == 0) {
// First iteration, item has contents and should get signals
expected = 1;
item->setFlag(QQuickItem::ItemHasContents, true);
} else {
// Second iteration, item does not have content and will not get signals
}
QSignalSpy invalidateSpy(item, SIGNAL(invalidated()));
item->setParentItem(view->contentItem());
view->show();
QVERIFY(QTest::qWaitForWindowExposed(view.data()));
delete view.take();
QCOMPARE(invalidateSpy.size(), expected);
}
}
void tst_qquickitem::objectChildTransform()
{
QQuickView view;
view.setSource(testFileUrl("objectChildTransform.qml"));
QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject());
QVERIFY(root);
root->setProperty("source", QString());
// Shouldn't crash.
}
void tst_qquickitem::contains_data()
{
QTest::addColumn<int>("x");
QTest::addColumn<int>("y");
QTest::addColumn<bool>("contains");
QTest::newRow("(0, 0) = false") << 0 << 0 << false;
QTest::newRow("(50, 0) = false") << 50 << 0 << false;
QTest::newRow("(0, 50) = false") << 0 << 50 << false;
QTest::newRow("(50, 50) = true") << 50 << 50 << true;
QTest::newRow("(100, 100) = true") << 100 << 100 << true;
QTest::newRow("(150, 150) = false") << 150 << 150 << false;
}
void tst_qquickitem::contains()
{
// Tests that contains works, but also checks that mapToItem/mapFromItem
// return the correct type (point or rect, not a JS object with those properties),
// as this is a common combination of calls.
QFETCH(int, x);
QFETCH(int, y);
QFETCH(bool, contains);
QQuickView view;
view.setSource(testFileUrl("contains.qml"));
QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject());
QVERIFY(root);
QVariant result = false;
QVERIFY(QMetaObject::invokeMethod(root, "childContainsViaMapToItem",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, qreal(x)), Q_ARG(QVariant, qreal(y))));
QCOMPARE(result.toBool(), contains);
result = false;
QVERIFY(QMetaObject::invokeMethod(root, "childContainsViaMapFromItem",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, qreal(x)), Q_ARG(QVariant, qreal(y))));
QCOMPARE(result.toBool(), contains);
}
void tst_qquickitem::childAt()
{
QQuickView view;
view.setSource(testFileUrl("childAtRectangle.qml"));
QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject());
int found = 0;
for (int i = 0; i < 16; i++)
{
if (root->childAt(i, 0))
found++;
}
QCOMPARE(found, 16);
found = 0;
for (int i = 0; i < 16; i++)
{
if (root->childAt(0, i))
found++;
}
QCOMPARE(found, 16);
found = 0;
for (int i = 0; i < 2; i++)
{
if (root->childAt(18 + i, 0))
found++;
}
QCOMPARE(found, 1);
found = 0;
for (int i = 0; i < 16; i++)
{
if (root->childAt(18, i))
found++;
}
QCOMPARE(found, 1);
QVERIFY(!root->childAt(19,19));
}
void tst_qquickitem::ignoreButtonPressNotInAcceptedMouseButtons()
{
// Verify the fix for QTBUG-31861
TestItem item;
QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::NoButton));
QQuickWindow window;
item.setSize(QSizeF(200,100));
item.setParentItem(window.contentItem());
item.setAcceptedMouseButtons(Qt::LeftButton);
QCOMPARE(item.acceptedMouseButtons(), Qt::MouseButtons(Qt::LeftButton));
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QTest::mousePress(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50)); // ignored because it's not LeftButton
QTest::mouseRelease(&window, Qt::RightButton, Qt::NoModifier, QPoint(50, 50)); // ignored because it didn't grab the RightButton press
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, QPoint(50, 50));
QCOMPARE(item.pressCount, 1);
QCOMPARE(item.releaseCount, 1);
}
void tst_qquickitem::shortcutOverride()
{
QQuickView view;
view.setSource(testFileUrl("shortcutOverride.qml"));
QVERIFY(ensureFocus(&view));
QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 0);
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
QQuickItem *escapeItem = view.rootObject()->property("escapeItem").value<QQuickItem*>();
QVERIFY(escapeItem);
QVERIFY(escapeItem->hasActiveFocus());
// escapeItem's onEscapePressed handler should accept the first escape press event.
QTest::keyPress(&view, Qt::Key_Escape);
QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
// Now it shouldn't have focus, so it can't handle the next escape press event.
QVERIFY(!escapeItem->hasActiveFocus());
QTest::keyRelease(&view, Qt::Key_Escape);
QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 0);
QTest::keyPress(&view, Qt::Key_Escape);
QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1);
QTest::keyRelease(&view, Qt::Key_Escape);
QCOMPARE(view.rootObject()->property("escapeHandlerActivationCount").toInt(), 1);
QCOMPARE(view.rootObject()->property("shortcutActivationCount").toInt(), 1);
}
#ifdef TEST_QTBUG_60123
void tst_qquickitem::qtBug60123()
{
QMainWindow main;
main.resize(400, 200);
QQuickView window;
QQuickView window2;
window.setSource(testFileUrl("mainWindowQtBug60123.qml"));
window2.setSource(testFileUrl("mainWindowQtBug60123.qml"));
// Create central widget for the main window
QWidget *baseWidget = new QWidget(&main);
baseWidget->resize(400, 200);
baseWidget->setMaximumHeight(200);
baseWidget->setMaximumWidth(400);
main.setCentralWidget(baseWidget);
// Create container widgets for both windows
QWidget *containers = QWidget::createWindowContainer(&window, baseWidget);
QWidget *containers2 = QWidget::createWindowContainer(&window2, baseWidget);
containers->setGeometry(0, 0, 100, 100);
containers2->setGeometry(100, 100, 100, 100);
// Show and activate the main window
main.show();
QVERIFY(QTest::qWaitForWindowExposed(&main));
// Activate window, test press and release events
auto activateWindowAndTestPress = [] (QQuickView* testWindow) {
testWindow->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(testWindow));
QTest::mousePress(testWindow, Qt::LeftButton, Qt::NoModifier, QPoint(10, 10));
QCOMPARE(testWindow->rootObject()->property("lastEvent").toString(), QString("pressed"));
QTest::mouseRelease(testWindow, Qt::LeftButton, Qt::NoModifier, QPoint(10, 10));
QCOMPARE(testWindow->rootObject()->property("lastEvent").toString(), QString("released"));
};
// First press after switching focus window resulted in cancelled event
activateWindowAndTestPress(&window);
activateWindowAndTestPress(&window2);
activateWindowAndTestPress(&window);
}
#endif
void tst_qquickitem::setParentCalledInOnWindowChanged()
{
QQuickView view;
view.setSource(testFileUrl("setParentInWindowChange.qml"));
QVERIFY(ensureFocus(&view)); // should not crash
}
void tst_qquickitem::receivesLanguageChangeEvent()
{
QQuickWindow window;
window.setFramePosition(QPoint(100, 100));
window.resize(200, 200);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QScopedPointer<TestItem> child1(new TestItem);
child1->setObjectName(QStringLiteral("child1"));
child1->setSize(QSizeF(200, 100));
child1->setParentItem(window.contentItem());
QScopedPointer<TestItem> child2(new TestItem);
child2->setObjectName(QStringLiteral("child2"));
child2->setSize(QSizeF(50, 50));
child2->setParentItem(child1.data());
QTranslator t;
QVERIFY(t.load("hellotr_la.qm", dataDirectory()));
QVERIFY(QCoreApplication::installTranslator(&t));
QTRY_COMPARE(child1->languageChangeEventCount, 1);
QCOMPARE(child2->languageChangeEventCount, 1);
}
QTEST_MAIN(tst_qquickitem)
#include "tst_qquickitem.moc"