blob: 399535cfa69d3465f27ac6843a01a7a1c5df3877 [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 <QtTest/QSignalSpy>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/qquickview.h>
#include <QtGui/private/qinputmethod_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtGui/qstylehints.h>
#include <private/qquickitem_p.h>
#include "../../shared/util.h"
#include "../shared/visualtestutil.h"
#include "../../shared/platforminputcontext.h"
using namespace QQuickVisualTestUtil;
class tst_QQuickItem : public QQmlDataTest
{
Q_OBJECT
public:
tst_QQuickItem();
private slots:
void initTestCase();
void cleanup();
void activeFocusOnTab();
void activeFocusOnTab2();
void activeFocusOnTab3();
void activeFocusOnTab4();
void activeFocusOnTab5();
void activeFocusOnTab6();
void activeFocusOnTab7();
void activeFocusOnTab8();
void activeFocusOnTab9();
void activeFocusOnTab10();
void activeFocusOnTab_infiniteLoop();
void nextItemInFocusChain();
void nextItemInFocusChain2();
void nextItemInFocusChain3();
void tabFence();
void qtbug_50516();
void qtbug_50516_2_data();
void qtbug_50516_2();
void keys();
void standardKeys_data();
void standardKeys();
void keysProcessingOrder();
void keysim();
void keysForward();
void keyNavigation_data();
void keyNavigation();
void keyNavigation_RightToLeft();
void keyNavigation_skipNotVisible();
void keyNavigation_implicitSetting();
void keyNavigation_implicitDestroy();
void keyNavigation_focusReason();
void keyNavigation_loop();
void layoutMirroring();
void layoutMirroringWindow();
void layoutMirroringIllegalParent();
void smooth();
void antialiasing();
void clip();
void mapCoordinates();
void mapCoordinates_data();
void mapCoordinatesRect();
void mapCoordinatesRect_data();
void propertyChanges();
void nonexistentPropertyConnection();
void transforms();
void transforms_data();
void childrenRect();
void childrenRectBug();
void childrenRectBug2();
void childrenRectBug3();
void childrenRectBottomRightCorner();
void childrenProperty();
void resourcesProperty();
void changeListener();
void transformCrash();
void implicitSize();
void qtbug_16871();
void visibleChildren();
void parentLoop();
void contains_data();
void contains();
void childAt();
void isAncestorOf();
void grab();
private:
QQmlEngine engine;
bool qt_tab_all_widgets() {
return QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls;
}
};
class KeysTestObject : public QObject
{
Q_OBJECT
Q_PROPERTY(bool processLast READ processLast NOTIFY processLastChanged)
public:
KeysTestObject() : mKey(0), mModifiers(0), mForwardedKey(0), mLast(false), mNativeScanCode(0) {}
void reset() {
mKey = 0;
mText = QString();
mModifiers = 0;
mForwardedKey = 0;
mNativeScanCode = 0;
}
bool processLast() const { return mLast; }
void setProcessLast(bool b) {
if (b != mLast) {
mLast = b;
emit processLastChanged();
}
}
public slots:
void keyPress(int key, QString text, int modifiers) {
mKey = key;
mText = text;
mModifiers = modifiers;
}
void keyRelease(int key, QString text, int modifiers) {
mKey = key;
mText = text;
mModifiers = modifiers;
}
void forwardedKey(int key) {
mForwardedKey = key;
}
void specialKey(int key, QString text, quint32 nativeScanCode) {
mKey = key;
mText = text;
mNativeScanCode = nativeScanCode;
}
signals:
void processLastChanged();
public:
int mKey;
QString mText;
int mModifiers;
int mForwardedKey;
bool mLast;
quint32 mNativeScanCode;
private:
};
class KeyTestItem : public QQuickItem
{
Q_OBJECT
public:
KeyTestItem(QQuickItem *parent=nullptr) : QQuickItem(parent), mKey(0) {}
protected:
void keyPressEvent(QKeyEvent *e) {
mKey = e->key();
if (e->key() == Qt::Key_A)
e->accept();
else
e->ignore();
}
void keyReleaseEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_B)
e->accept();
else
e->ignore();
}
public:
int mKey;
};
class FocusEventFilter : public QObject
{
protected:
bool eventFilter(QObject *watched, QEvent *event) {
if ((event->type() == QEvent::FocusIn) || (event->type() == QEvent::FocusOut)) {
QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
lastFocusReason = focusEvent->reason();
return false;
} else
return QObject::eventFilter(watched, event);
}
public:
Qt::FocusReason lastFocusReason;
};
QML_DECLARE_TYPE(KeyTestItem);
class HollowTestItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool circle READ isCircle WRITE setCircle)
Q_PROPERTY(qreal holeRadius READ holeRadius WRITE setHoleRadius)
public:
HollowTestItem(QQuickItem *parent = nullptr)
: QQuickItem(parent),
m_isPressed(false),
m_isHovered(false),
m_isCircle(false),
m_holeRadius(50)
{
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
bool isPressed() const { return m_isPressed; }
bool isHovered() const { return m_isHovered; }
bool isCircle() const { return m_isCircle; }
void setCircle(bool circle) { m_isCircle = circle; }
qreal holeRadius() const { return m_holeRadius; }
void setHoleRadius(qreal radius) { m_holeRadius = radius; }
bool contains(const QPointF &point) const {
const qreal w = width();
const qreal h = height();
const qreal r = m_holeRadius;
// check boundaries
if (!QRectF(0, 0, w, h).contains(point))
return false;
// square shape
if (!m_isCircle)
return !QRectF(w / 2 - r, h / 2 - r, r * 2, r * 2).contains(point);
// circle shape
const qreal dx = point.x() - (w / 2);
const qreal dy = point.y() - (h / 2);
const qreal dd = (dx * dx) + (dy * dy);
const qreal outerRadius = qMin<qreal>(w / 2, h / 2);
return dd > (r * r) && dd <= outerRadius * outerRadius;
}
protected:
void hoverEnterEvent(QHoverEvent *) { m_isHovered = true; }
void hoverLeaveEvent(QHoverEvent *) { m_isHovered = false; }
void mousePressEvent(QMouseEvent *) { m_isPressed = true; }
void mouseReleaseEvent(QMouseEvent *) { m_isPressed = false; }
private:
bool m_isPressed;
bool m_isHovered;
bool m_isCircle;
qreal m_holeRadius;
};
QML_DECLARE_TYPE(HollowTestItem);
class TabFenceItem : public QQuickItem
{
Q_OBJECT
public:
TabFenceItem(QQuickItem *parent = nullptr)
: QQuickItem(parent)
{
QQuickItemPrivate *d = QQuickItemPrivate::get(this);
d->isTabFence = true;
}
};
QML_DECLARE_TYPE(TabFenceItem);
class TabFenceItem2 : public QQuickItem
{
Q_OBJECT
public:
TabFenceItem2(QQuickItem *parent = nullptr)
: QQuickItem(parent)
{
QQuickItemPrivate *d = QQuickItemPrivate::get(this);
d->isTabFence = true;
setFlag(ItemIsFocusScope);
}
};
QML_DECLARE_TYPE(TabFenceItem2);
tst_QQuickItem::tst_QQuickItem()
{
}
void tst_QQuickItem::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem");
qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem");
qmlRegisterType<TabFenceItem>("Test", 1, 0, "TabFence");
qmlRegisterType<TabFenceItem2>("Test", 1, 0, "TabFence2");
}
void tst_QQuickItem::cleanup()
{
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
inputMethodPrivate->testContext = nullptr;
}
void tst_QQuickItem::activeFocusOnTab()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button12
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: button12->sub2
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "sub2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: sub2->button21
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button21");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: button21->button22
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button22");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: button22->edit
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: edit->button22
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button22");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button22->button21
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button21");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button21->sub2
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "sub2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: sub2->button12
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button12->button11
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button11->edit
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab2()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button12
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button12->button11
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button11->edit
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab3()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab3.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button1
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button1->button2, through a repeater
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button2->button3, through a row
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button3->button4, through a flow
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button4->button5, through a focusscope
// parent is activeFocusOnTab:false, one of children is focus:true
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button5");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button5->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children is focus:true
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button6");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 5 Tabs: button6->button7, through a focusscope
// parent is activeFocusOnTab:true, none of children is focus:true
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 5; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button7");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button7->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button6");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 Tabs: button6->button7, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);;
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button7");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button7->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button6");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button6->button5, through a focusscope(parent is activeFocusOnTab: false)
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button5");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button5->button4, through a focusscope(parent is activeFocusOnTab: false)
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button4->button3, through a flow
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button3->button2, through a row
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// 4 BackTabs: button2->button1, through a repeater
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
for (int i = 0; i < 4; ++i) {
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
}
item = findItem<QQuickItem>(window->rootObject(), "button1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab4()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab4.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button11
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button11");
item->setActiveFocusOnTab(true);
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: button11->button21
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button21");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab5()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab4.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button11 in sub1
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
QQuickItem *item2 = findItem<QQuickItem>(window->rootObject(), "sub1");
item2->setActiveFocusOnTab(true);
// Tab: button11->button21
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button21");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab6()
{
if (qt_tab_all_widgets())
QSKIP("This function doesn't support iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab6.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// original: button12
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Tab: button12->edit
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: edit->button12
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button12->button11
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// BackTab: button11->edit
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab7()
{
if (qt_tab_all_widgets())
QSKIP("This function doesn't support iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300,300));
window->setSource(testFileUrl("activeFocusOnTab7.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button1");
QVERIFY(item);
item->forceActiveFocus();
QVERIFY(item->hasActiveFocus());
// Tab: button1->button1
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(!key.isAccepted());
QVERIFY(item->hasActiveFocus());
// BackTab: button1->button1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(!key.isAccepted());
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab8()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300,300));
window->setSource(testFileUrl("activeFocusOnTab8.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *content = window->contentItem();
QVERIFY(content);
QVERIFY(content->hasActiveFocus());
QQuickItem *button1 = findItem<QQuickItem>(window->rootObject(), "button1");
QVERIFY(button1);
QVERIFY(!button1->hasActiveFocus());
QQuickItem *button2 = findItem<QQuickItem>(window->rootObject(), "button2");
QVERIFY(button2);
QVERIFY(!button2->hasActiveFocus());
// Tab: contentItem->button1
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(button1->hasActiveFocus());
// Tab: button1->button2
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(button2->hasActiveFocus());
QVERIFY(!button1->hasActiveFocus());
// BackTab: button2->button1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(button1->hasActiveFocus());
QVERIFY(!button2->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab9()
{
if (qt_tab_all_widgets())
QSKIP("This function doesn't support iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300,300));
window->setSource(testFileUrl("activeFocusOnTab9.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *content = window->contentItem();
QVERIFY(content);
QVERIFY(content->hasActiveFocus());
QQuickItem *textinput1 = findItem<QQuickItem>(window->rootObject(), "textinput1");
QVERIFY(textinput1);
QQuickItem *textedit1 = findItem<QQuickItem>(window->rootObject(), "textedit1");
QVERIFY(textedit1);
QVERIFY(!textinput1->hasActiveFocus());
textinput1->forceActiveFocus();
QVERIFY(textinput1->hasActiveFocus());
// Tab: textinput1->textedit1
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textedit1->hasActiveFocus());
// BackTab: textedit1->textinput1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textinput1->hasActiveFocus());
// BackTab: textinput1->textedit1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textedit1->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab10()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300,300));
window->setSource(testFileUrl("activeFocusOnTab9.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *content = window->contentItem();
QVERIFY(content);
QVERIFY(content->hasActiveFocus());
QQuickItem *textinput1 = findItem<QQuickItem>(window->rootObject(), "textinput1");
QVERIFY(textinput1);
QQuickItem *textedit1 = findItem<QQuickItem>(window->rootObject(), "textedit1");
QVERIFY(textedit1);
QQuickItem *textinput2 = findItem<QQuickItem>(window->rootObject(), "textinput2");
QVERIFY(textinput2);
QQuickItem *textedit2 = findItem<QQuickItem>(window->rootObject(), "textedit2");
QVERIFY(textedit2);
QVERIFY(!textinput1->hasActiveFocus());
textinput1->forceActiveFocus();
QVERIFY(textinput1->hasActiveFocus());
// Tab: textinput1->textinput2
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textinput2->hasActiveFocus());
// Tab: textinput2->textedit1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textedit1->hasActiveFocus());
// BackTab: textedit1->textinput2
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textinput2->hasActiveFocus());
// BackTab: textinput2->textinput1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textinput1->hasActiveFocus());
// BackTab: textinput1->textedit2
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textedit2->hasActiveFocus());
// BackTab: textedit2->textedit1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textedit1->hasActiveFocus());
// BackTab: textedit1->textinput2
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(textinput2->hasActiveFocus());
delete window;
}
void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
{
// see QTBUG-68271
// create a window where the currently focused item is not visible
QScopedPointer<QQuickView>window(new QQuickView());
window->setSource(testFileUrl("activeFocusOnTab_infiniteLoop.qml"));
window->show();
auto *hiddenChild = findItem<QQuickItem>(window->rootObject(), "hiddenChild");
QVERIFY(hiddenChild);
// move the focus - this used to result in an infinite loop
auto *item = hiddenChild->nextItemInFocusChain();
// focus is moved to the root object since there is no other candidate
QCOMPARE(item, window->rootObject());
}
void tst_QQuickItem::nextItemInFocusChain()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *button11 = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(button11);
QQuickItem *button12 = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(button12);
QQuickItem *sub2 = findItem<QQuickItem>(window->rootObject(), "sub2");
QVERIFY(sub2);
QQuickItem *button21 = findItem<QQuickItem>(window->rootObject(), "button21");
QVERIFY(button21);
QQuickItem *button22 = findItem<QQuickItem>(window->rootObject(), "button22");
QVERIFY(button22);
QQuickItem *edit = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(edit);
QQuickItem *next, *prev;
next = button11->nextItemInFocusChain(true);
QVERIFY(next);
QCOMPARE(next, button12);
prev = button11->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, edit);
next = button12->nextItemInFocusChain();
QVERIFY(next);
QCOMPARE(next, sub2);
prev = button12->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button11);
next = sub2->nextItemInFocusChain(true);
QVERIFY(next);
QCOMPARE(next, button21);
prev = sub2->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button12);
next = button21->nextItemInFocusChain();
QVERIFY(next);
QCOMPARE(next, button22);
prev = button21->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, sub2);
next = button22->nextItemInFocusChain(true);
QVERIFY(next);
QCOMPARE(next, edit);
prev = button22->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button21);
next = edit->nextItemInFocusChain();
QVERIFY(next);
QCOMPARE(next, button11);
prev = edit->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button22);
delete window;
}
void tst_QQuickItem::nextItemInFocusChain2()
{
if (qt_tab_all_widgets())
QSKIP("This function doesn't support iterating all.");
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("activeFocusOnTab6.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *button11 = findItem<QQuickItem>(window->rootObject(), "button11");
QVERIFY(button11);
QQuickItem *button12 = findItem<QQuickItem>(window->rootObject(), "button12");
QVERIFY(button12);
QQuickItem *edit = findItem<QQuickItem>(window->rootObject(), "edit");
QVERIFY(edit);
QQuickItem *next, *prev;
next = button11->nextItemInFocusChain(true);
QVERIFY(next);
QCOMPARE(next, button12);
prev = button11->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, edit);
next = button12->nextItemInFocusChain();
QVERIFY(next);
QCOMPARE(next, edit);
prev = button12->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button11);
next = edit->nextItemInFocusChain();
QVERIFY(next);
QCOMPARE(next, button11);
prev = edit->nextItemInFocusChain(false);
QVERIFY(prev);
QCOMPARE(prev, button12);
delete window;
}
void tst_QQuickItem::nextItemInFocusChain3()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("nextItemInFocusChain3.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
}
void verifyTabFocusChain(QQuickView *window, const char **focusChain, bool forward)
{
int idx = 0;
for (const char **objectName = focusChain; *objectName; ++objectName, ++idx) {
const QString &descrStr = QString("idx=%1 objectName=\"%2\"").arg(idx).arg(*objectName);
const char *descr = descrStr.toLocal8Bit().data();
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, forward ? Qt::NoModifier : Qt::ShiftModifier);
QGuiApplication::sendEvent(window, &key);
QVERIFY2(key.isAccepted(), descr);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), *objectName);
QVERIFY2(item, descr);
QVERIFY2(item->hasActiveFocus(), descr);
}
}
void tst_QQuickItem::tabFence()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("tabFence.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QVERIFY(QGuiApplication::focusWindow() == window);
QVERIFY(window->rootObject()->hasActiveFocus());
const char *rootTabFocusChain[] = {
"input1", "input2", "input3", "input1", nullptr
};
verifyTabFocusChain(window, rootTabFocusChain, true /* forward */);
const char *rootBacktabFocusChain[] = {
"input3", "input2", "input1", "input3", nullptr
};
verifyTabFocusChain(window, rootBacktabFocusChain, false /* forward */);
// Give focus to input11 in fence1
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "input11");
item->setFocus(true);
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
const char *fence1TabFocusChain[] = {
"input12", "input13", "input11", "input12", nullptr
};
verifyTabFocusChain(window, fence1TabFocusChain, true /* forward */);
const char *fence1BacktabFocusChain[] = {
"input11", "input13", "input12", "input11", nullptr
};
verifyTabFocusChain(window, fence1BacktabFocusChain, false /* forward */);
}
void tst_QQuickItem::qtbug_50516()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl("qtbug_50516.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QVERIFY(QGuiApplication::focusWindow() == window);
QVERIFY(window->rootObject()->hasActiveFocus());
QQuickItem *contentItem = window->rootObject();
QQuickItem *next = contentItem->nextItemInFocusChain(true);
QCOMPARE(next, contentItem);
next = contentItem->nextItemInFocusChain(false);
QCOMPARE(next, contentItem);
delete window;
}
void tst_QQuickItem::qtbug_50516_2_data()
{
QTest::addColumn<QString>("filename");
QTest::addColumn<QString>("item1");
QTest::addColumn<QString>("item2");
QTest::newRow("FocusScope TabFence with one Item(focused)")
<< QStringLiteral("qtbug_50516_2_1.qml") << QStringLiteral("root") << QStringLiteral("root");
QTest::newRow("FocusScope TabFence with one Item(unfocused)")
<< QStringLiteral("qtbug_50516_2_2.qml") << QStringLiteral("root") << QStringLiteral("root");
QTest::newRow("FocusScope TabFence with two Items(focused)")
<< QStringLiteral("qtbug_50516_2_3.qml") << QStringLiteral("root") << QStringLiteral("root");
QTest::newRow("FocusScope TabFence with two Items(unfocused)")
<< QStringLiteral("qtbug_50516_2_4.qml") << QStringLiteral("root") << QStringLiteral("root");
QTest::newRow("FocusScope TabFence with one Item and one TextInput(unfocused)")
<< QStringLiteral("qtbug_50516_2_5.qml") << QStringLiteral("item1") << QStringLiteral("item1");
QTest::newRow("FocusScope TabFence with two TextInputs(unfocused)")
<< QStringLiteral("qtbug_50516_2_6.qml") << QStringLiteral("item1") << QStringLiteral("item2");
}
void tst_QQuickItem::qtbug_50516_2()
{
QFETCH(QString, filename);
QFETCH(QString, item1);
QFETCH(QString, item2);
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(800,600));
window->setSource(testFileUrl(filename));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QVERIFY(QGuiApplication::focusWindow() == window);
QVERIFY(window->rootObject()->hasActiveFocus());
QQuickItem *contentItem = window->rootObject();
QQuickItem *next = contentItem->nextItemInFocusChain(true);
QCOMPARE(next->objectName(), item1);
next = contentItem->nextItemInFocusChain(false);
QCOMPARE(next->objectName(), item2);
delete window;
}
void tst_QQuickItem::keys()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
KeysTestObject *testObject = new KeysTestObject;
window->rootContext()->setContextProperty("keysTestObject", testObject);
window->rootContext()->setContextProperty("enableKeyHanding", QVariant(true));
window->rootContext()->setContextProperty("forwardeeVisible", QVariant(true));
window->setSource(testFileUrl("keystest.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QVERIFY(window->rootObject());
QCOMPARE(window->rootObject()->property("isEnabled").toBool(), true);
QKeyEvent key(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_A));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_A));
QCOMPARE(testObject->mText, QLatin1String("A"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(!key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ShiftModifier, "A", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_A));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_A));
QCOMPARE(testObject->mText, QLatin1String("A"));
QCOMPARE(testObject->mModifiers, int(Qt::ShiftModifier));
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_Return));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Return));
QCOMPARE(testObject->mText, QLatin1String("Return"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "0", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_0));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_0));
QCOMPARE(testObject->mText, QLatin1String("0"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_9, Qt::NoModifier, "9", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_9));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_9));
QCOMPARE(testObject->mText, QLatin1String("9"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(!key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_Tab));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Tab));
QCOMPARE(testObject->mText, QLatin1String("Tab"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_Backtab));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_Backtab));
QCOMPARE(testObject->mText, QLatin1String("Backtab"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_VolumeUp, Qt::NoModifier, 1234, 0, 0);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_VolumeUp));
QCOMPARE(testObject->mForwardedKey, int(Qt::Key_VolumeUp));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QCOMPARE(testObject->mNativeScanCode, quint32(1234));
QVERIFY(key.isAccepted());
testObject->reset();
window->rootContext()->setContextProperty("forwardeeVisible", QVariant(false));
key = QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_A));
QCOMPARE(testObject->mForwardedKey, 0);
QCOMPARE(testObject->mText, QLatin1String("A"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(!key.isAccepted());
testObject->reset();
window->rootContext()->setContextProperty("enableKeyHanding", QVariant(false));
QCOMPARE(window->rootObject()->property("isEnabled").toBool(), false);
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, 0);
QVERIFY(!key.isAccepted());
window->rootContext()->setContextProperty("enableKeyHanding", QVariant(true));
QCOMPARE(window->rootObject()->property("isEnabled").toBool(), true);
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_Return));
QVERIFY(key.isAccepted());
delete window;
delete testObject;
}
Q_DECLARE_METATYPE(QEvent::Type);
Q_DECLARE_METATYPE(QKeySequence::StandardKey);
void tst_QQuickItem::standardKeys_data()
{
QTest::addColumn<QKeySequence::StandardKey>("standardKey");
QTest::addColumn<QKeySequence::StandardKey>("contextProperty");
QTest::addColumn<QEvent::Type>("eventType");
QTest::addColumn<bool>("pressed");
QTest::addColumn<bool>("released");
QTest::newRow("Press: Open") << QKeySequence::Open << QKeySequence::Open << QEvent::KeyPress << true << false;
QTest::newRow("Press: Close") << QKeySequence::Close << QKeySequence::Close << QEvent::KeyPress << true << false;
QTest::newRow("Press: Save") << QKeySequence::Save << QKeySequence::Save << QEvent::KeyPress << true << false;
QTest::newRow("Press: Quit") << QKeySequence::Quit << QKeySequence::Quit << QEvent::KeyPress << true << false;
QTest::newRow("Release: New") << QKeySequence::New << QKeySequence::New << QEvent::KeyRelease << false << true;
QTest::newRow("Release: Delete") << QKeySequence::Delete << QKeySequence::Delete << QEvent::KeyRelease << false << true;
QTest::newRow("Release: Undo") << QKeySequence::Undo << QKeySequence::Undo << QEvent::KeyRelease << false << true;
QTest::newRow("Release: Redo") << QKeySequence::Redo << QKeySequence::Redo << QEvent::KeyRelease << false << true;
QTest::newRow("Mismatch: Cut") << QKeySequence::Cut << QKeySequence::Copy << QEvent::KeyPress << false << false;
QTest::newRow("Mismatch: Copy") << QKeySequence::Copy << QKeySequence::Paste << QEvent::KeyPress << false << false;
QTest::newRow("Mismatch: Paste") << QKeySequence::Paste << QKeySequence::Cut << QEvent::KeyRelease << false << false;
QTest::newRow("Mismatch: Quit") << QKeySequence::Quit << QKeySequence::New << QEvent::KeyRelease << false << false;
}
void tst_QQuickItem::standardKeys()
{
QFETCH(QKeySequence::StandardKey, standardKey);
QFETCH(QKeySequence::StandardKey, contextProperty);
QFETCH(QEvent::Type, eventType);
QFETCH(bool, pressed);
QFETCH(bool, released);
QKeySequence keySequence(standardKey);
if (keySequence.isEmpty())
QSKIP("Undefined key sequence.");
QQuickView view;
view.rootContext()->setContextProperty("standardKey", contextProperty);
view.setSource(testFileUrl("standardkeys.qml"));
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
QQuickItem *item = qobject_cast<QQuickItem*>(view.rootObject());
QVERIFY(item);
const int key = keySequence[0] & Qt::Key_unknown;
const int modifiers = keySequence[0] & Qt::KeyboardModifierMask;
QKeyEvent keyEvent(eventType, key, static_cast<Qt::KeyboardModifiers>(modifiers));
QGuiApplication::sendEvent(&view, &keyEvent);
QCOMPARE(item->property("pressed").toBool(), pressed);
QCOMPARE(item->property("released").toBool(), released);
}
void tst_QQuickItem::keysProcessingOrder()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
KeysTestObject *testObject = new KeysTestObject;
window->rootContext()->setContextProperty("keysTestObject", testObject);
window->setSource(testFileUrl("keyspriority.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
KeyTestItem *testItem = qobject_cast<KeyTestItem*>(window->rootObject());
QVERIFY(testItem);
QCOMPARE(testItem->property("priorityTest").toInt(), 0);
QKeyEvent key(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_A));
QCOMPARE(testObject->mText, QLatin1String("A"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(key.isAccepted());
testObject->reset();
testObject->setProcessLast(true);
QCOMPARE(testItem->property("priorityTest").toInt(), 1);
key = QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier, "A", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, 0);
QVERIFY(key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier, "B", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, int(Qt::Key_B));
QCOMPARE(testObject->mText, QLatin1String("B"));
QCOMPARE(testObject->mModifiers, int(Qt::NoModifier));
QVERIFY(!key.isAccepted());
testObject->reset();
key = QKeyEvent(QEvent::KeyRelease, Qt::Key_B, Qt::NoModifier, "B", false, 1);
QGuiApplication::sendEvent(window, &key);
QCOMPARE(testObject->mKey, 0);
QVERIFY(key.isAccepted());
delete window;
delete testObject;
}
void tst_QQuickItem::keysim()
{
PlatformInputContext platformInputContext;
QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
inputMethodPrivate->testContext = &platformInputContext;
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keysim.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QVERIFY(window->rootObject());
QVERIFY(window->rootObject()->hasFocus() && window->rootObject()->hasActiveFocus());
QQuickTextInput *input = window->rootObject()->findChild<QQuickTextInput*>();
QVERIFY(input);
QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>());
QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev);
QEXPECT_FAIL("", "QTBUG-24280", Continue);
QCOMPARE(input->text(), QLatin1String("Hello world!"));
delete window;
}
void tst_QQuickItem::keysForward()
{
QQuickView window;
window.setBaseSize(QSize(240,320));
window.setSource(testFileUrl("keysforward.qml"));
window.show();
window.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *rootItem = qobject_cast<QQuickItem *>(window.rootObject());
QVERIFY(rootItem);
QQuickItem *sourceItem = rootItem->property("source").value<QQuickItem *>();
QVERIFY(sourceItem);
QQuickItem *primaryTarget = rootItem->property("primaryTarget").value<QQuickItem *>();
QVERIFY(primaryTarget);
QQuickItem *secondaryTarget = rootItem->property("secondaryTarget").value<QQuickItem *>();
QVERIFY(secondaryTarget);
// primary target accepts/consumes Key_P
QKeyEvent pressKeyP(QEvent::KeyPress, Qt::Key_P, Qt::NoModifier, "P");
QCoreApplication::sendEvent(sourceItem, &pressKeyP);
QCOMPARE(rootItem->property("pressedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("pressedKeys").toList(), QVariantList());
QCOMPARE(primaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P);
QCOMPARE(secondaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P);
QVERIFY(pressKeyP.isAccepted());
QKeyEvent releaseKeyP(QEvent::KeyRelease, Qt::Key_P, Qt::NoModifier, "P");
QCoreApplication::sendEvent(sourceItem, &releaseKeyP);
QCOMPARE(rootItem->property("releasedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("releasedKeys").toList(), QVariantList());
QCOMPARE(primaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P);
QCOMPARE(secondaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P);
QVERIFY(releaseKeyP.isAccepted());
// secondary target accepts/consumes Key_S
QKeyEvent pressKeyS(QEvent::KeyPress, Qt::Key_S, Qt::NoModifier, "S");
QCoreApplication::sendEvent(sourceItem, &pressKeyS);
QCOMPARE(rootItem->property("pressedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("pressedKeys").toList(), QVariantList());
QCOMPARE(primaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P);
QCOMPARE(secondaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_S);
QVERIFY(pressKeyS.isAccepted());
QKeyEvent releaseKeyS(QEvent::KeyRelease, Qt::Key_S, Qt::NoModifier, "S");
QCoreApplication::sendEvent(sourceItem, &releaseKeyS);
QCOMPARE(rootItem->property("releasedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("releasedKeys").toList(), QVariantList());
QCOMPARE(primaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P);
QCOMPARE(secondaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_S);
QVERIFY(releaseKeyS.isAccepted());
// neither target accepts/consumes Key_Q
QKeyEvent pressKeyQ(QEvent::KeyPress, Qt::Key_Q, Qt::NoModifier, "Q");
QCoreApplication::sendEvent(sourceItem, &pressKeyQ);
QCOMPARE(rootItem->property("pressedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("pressedKeys").toList(), QVariantList() << Qt::Key_Q);
QCOMPARE(primaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_Q);
QCOMPARE(secondaryTarget->property("pressedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_S << Qt::Key_Q);
QVERIFY(!pressKeyQ.isAccepted());
QKeyEvent releaseKeyQ(QEvent::KeyRelease, Qt::Key_Q, Qt::NoModifier, "Q");
QCoreApplication::sendEvent(sourceItem, &releaseKeyQ);
QCOMPARE(rootItem->property("releasedKeys").toList(), QVariantList());
QCOMPARE(sourceItem->property("releasedKeys").toList(), QVariantList() << Qt::Key_Q);
QCOMPARE(primaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_Q);
QCOMPARE(secondaryTarget->property("releasedKeys").toList(), QVariantList() << Qt::Key_P << Qt::Key_S << Qt::Key_Q);
QVERIFY(!releaseKeyQ.isAccepted());
}
QQuickItemPrivate *childPrivate(QQuickItem *rootItem, const char * itemString)
{
QQuickItem *item = findItem<QQuickItem>(rootItem, QString(QLatin1String(itemString)));
QQuickItemPrivate* itemPrivate = QQuickItemPrivate::get(item);
return itemPrivate;
}
QVariant childProperty(QQuickItem *rootItem, const char * itemString, const char * property)
{
QQuickItem *item = findItem<QQuickItem>(rootItem, QString(QLatin1String(itemString)));
return item->property(property);
}
bool anchorsMirrored(QQuickItem *rootItem, const char * itemString)
{
QQuickItem *item = findItem<QQuickItem>(rootItem, QString(QLatin1String(itemString)));
QQuickItemPrivate* itemPrivate = QQuickItemPrivate::get(item);
return itemPrivate->anchors()->mirrored();
}
void tst_QQuickItem::layoutMirroring()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("layoutmirroring.qml"));
window->show();
QQuickItem *rootItem = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(rootItem);
QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(rootItem);
QVERIFY(rootPrivate);
QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
QCOMPARE(anchorsMirrored(rootItem, "mirrored1"), true);
QCOMPARE(anchorsMirrored(rootItem, "mirrored2"), true);
QCOMPARE(anchorsMirrored(rootItem, "notMirrored1"), false);
QCOMPARE(anchorsMirrored(rootItem, "notMirrored2"), false);
QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror1"), true);
QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror2"), true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->isMirrorImplicit, false);
QCOMPARE(childPrivate(rootItem, "mirrored2")->isMirrorImplicit, false);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->isMirrorImplicit, false);
QCOMPARE(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit, true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent, false);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem, true);
QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem, false);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem, false);
QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem, false);
// load dynamic content using Loader that needs to inherit mirroring
rootItem->setProperty("state", "newContent");
QCOMPARE(childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored3")->isMirrorImplicit, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit, true);
QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent, true);
QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false);
// disable inheritance
rootItem->setProperty("childrenInherit", false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, false);
// re-enable inheritance
rootItem->setProperty("childrenInherit", true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false);
QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true);
QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true);
//
// dynamic parenting
//
QQuickItem *parentItem1 = new QQuickItem();
QQuickItemPrivate::get(parentItem1)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
QQuickItemPrivate::get(parentItem1)->isMirrorImplicit = false;
QQuickItemPrivate::get(parentItem1)->inheritMirrorFromItem = true; // LayoutMirroring.childrenInherit: true
QQuickItemPrivate::get(parentItem1)->resolveLayoutMirror();
// inherit in constructor
QQuickItem *childItem1 = new QQuickItem(parentItem1);
QCOMPARE(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror, true);
QCOMPARE(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent, true);
// inherit through a parent change
QQuickItem *childItem2 = new QQuickItem();
QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
childItem2->setParentItem(parentItem1);
QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, true);
QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, true);
// stop inherting through a parent change
QQuickItem *parentItem2 = new QQuickItem();
QQuickItemPrivate::get(parentItem2)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true
QQuickItemPrivate::get(parentItem2)->resolveLayoutMirror();
childItem2->setParentItem(parentItem2);
QCOMPARE(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror, false);
QCOMPARE(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent, false);
delete parentItem1;
delete parentItem2;
}
void tst_QQuickItem::layoutMirroringWindow()
{
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("layoutmirroring_window.qml"));
QScopedPointer<QObject> object(component.create());
QQuickWindow *window = qobject_cast<QQuickWindow *>(object.data());
QVERIFY(window);
window->show();
QQuickItemPrivate *content = QQuickItemPrivate::get(window->contentItem());
QCOMPARE(content->effectiveLayoutMirror, true);
QCOMPARE(content->inheritedLayoutMirror, true);
QCOMPARE(content->isMirrorImplicit, false);
QCOMPARE(content->inheritMirrorFromParent, true);
QCOMPARE(content->inheritMirrorFromItem, true);
}
void tst_QQuickItem::layoutMirroringIllegalParent()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { LayoutMirroring.enabled: true; LayoutMirroring.childrenInherit: true }", QUrl::fromLocalFile(""));
QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1:21: QML QtObject: LayoutDirection attached property only works with Items and Windows");
QObject *object = component.create();
QVERIFY(object != nullptr);
}
void tst_QQuickItem::keyNavigation_data()
{
QTest::addColumn<QString>("source");
QTest::newRow("KeyNavigation") << QStringLiteral("keynavigationtest.qml");
QTest::newRow("KeyNavigation_FocusScope") << QStringLiteral("keynavigationtest_focusscope.qml");
}
void tst_QQuickItem::keyNavigation()
{
QFETCH(QString, source);
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl(source));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
QVariant result;
QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "verify",
Q_RETURN_ARG(QVariant, result)));
QVERIFY(result.toBool());
// right
QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// down
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// left
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// up
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// tab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// backtab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::keyNavigation_RightToLeft()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keynavigationtest.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *rootItem = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(rootItem);
QQuickItemPrivate* rootItemPrivate = QQuickItemPrivate::get(rootItem);
rootItemPrivate->effectiveLayoutMirror = true; // LayoutMirroring.mirror: true
rootItemPrivate->isMirrorImplicit = false;
rootItemPrivate->inheritMirrorFromItem = true; // LayoutMirroring.inherit: true
rootItemPrivate->resolveLayoutMirror();
QEvent wa(QEvent::WindowActivate);
QGuiApplication::sendEvent(window, &wa);
QFocusEvent fe(QEvent::FocusIn);
QGuiApplication::sendEvent(window, &fe);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
QVariant result;
QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "verify",
Q_RETURN_ARG(QVariant, result)));
QVERIFY(result.toBool());
// right
QKeyEvent key(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// left
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::keyNavigation_skipNotVisible()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keynavigationtest.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// Set item 2 to not visible
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
item->setVisible(false);
QVERIFY(!item->isVisible());
// right
QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// tab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// backtab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
//Set item 3 to not visible
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
item->setVisible(false);
QVERIFY(!item->isVisible());
// tab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// backtab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::keyNavigation_implicitSetting()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keynavigationtest_implicit.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QEvent wa(QEvent::WindowActivate);
QGuiApplication::sendEvent(window, &wa);
QFocusEvent fe(QEvent::FocusIn);
QGuiApplication::sendEvent(window, &fe);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
QVariant result;
QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "verify",
Q_RETURN_ARG(QVariant, result)));
QVERIFY(result.toBool());
// right
QKeyEvent key(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// back to item1
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// down
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// move to item4
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// left
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// back to item4
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// up
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// back to item4
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// tab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// back to item4
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
// backtab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
delete window;
}
// QTBUG-75399
void tst_QQuickItem::keyNavigation_implicitDestroy()
{
QQuickView view;
view.setSource(testFileUrl("keynavigationtest_implicitDestroy.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowActive(&view));
QQuickItem *root = view.rootObject();
QVERIFY(QMetaObject::invokeMethod(root, "createImplicitKeyNavigation"));
// process events is necessary to trigger upcoming memory access violation
QTest::qWait(0);
QVERIFY(root->hasActiveFocus());
QKeyEvent keyPress = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(&view, &keyPress); // <-- access violation happens here
// this should fail the test, even if the access violation does not occur
QVERIFY(!keyPress.isAccepted());
}
void tst_QQuickItem::keyNavigation_focusReason()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
FocusEventFilter focusEventFilter;
window->setSource(testFileUrl("keynavigationtest.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
// install event filter on first item
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
item->installEventFilter(&focusEventFilter);
//install event filter on second item
item = findItem<QQuickItem>(window->rootObject(), "item2");
QVERIFY(item);
item->installEventFilter(&focusEventFilter);
//install event filter on third item
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
item->installEventFilter(&focusEventFilter);
//install event filter on last item
item = findItem<QQuickItem>(window->rootObject(), "item4");
QVERIFY(item);
item->installEventFilter(&focusEventFilter);
// tab
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QCOMPARE(focusEventFilter.lastFocusReason, Qt::TabFocusReason);
// backtab
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QCOMPARE(focusEventFilter.lastFocusReason, Qt::BacktabFocusReason);
// right - it's also one kind of key navigation
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QCOMPARE(focusEventFilter.lastFocusReason, Qt::TabFocusReason);
item->setFocus(true, Qt::OtherFocusReason);
QVERIFY(item->hasActiveFocus());
QCOMPARE(focusEventFilter.lastFocusReason, Qt::OtherFocusReason);
delete window;
}
void tst_QQuickItem::keyNavigation_loop()
{
// QTBUG-47229
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keynavigationtest_loop.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1");
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY(item->hasActiveFocus());
delete window;
}
void tst_QQuickItem::smooth()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; Item { smooth: false; }", QUrl::fromLocalFile(""));
QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
QSignalSpy spy(item, SIGNAL(smoothChanged(bool)));
QVERIFY(item);
QVERIFY(!item->smooth());
item->setSmooth(true);
QVERIFY(item->smooth());
QCOMPARE(spy.count(),1);
QList<QVariant> arguments = spy.first();
QCOMPARE(arguments.count(), 1);
QVERIFY(arguments.at(0).toBool());
item->setSmooth(true);
QCOMPARE(spy.count(),1);
item->setSmooth(false);
QVERIFY(!item->smooth());
QCOMPARE(spy.count(),2);
item->setSmooth(false);
QCOMPARE(spy.count(),2);
delete item;
}
void tst_QQuickItem::antialiasing()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; Item { antialiasing: false; }", QUrl::fromLocalFile(""));
QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
QSignalSpy spy(item, SIGNAL(antialiasingChanged(bool)));
QVERIFY(item);
QVERIFY(!item->antialiasing());
item->setAntialiasing(true);
QVERIFY(item->antialiasing());
QCOMPARE(spy.count(),1);
QList<QVariant> arguments = spy.first();
QCOMPARE(arguments.count(), 1);
QVERIFY(arguments.at(0).toBool());
item->setAntialiasing(true);
QCOMPARE(spy.count(),1);
item->setAntialiasing(false);
QVERIFY(!item->antialiasing());
QCOMPARE(spy.count(),2);
item->setAntialiasing(false);
QCOMPARE(spy.count(),2);
delete item;
}
void tst_QQuickItem::clip()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nItem { clip: false\n }", QUrl::fromLocalFile(""));
QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
QSignalSpy spy(item, SIGNAL(clipChanged(bool)));
QVERIFY(item);
QVERIFY(!item->clip());
item->setClip(true);
QVERIFY(item->clip());
QList<QVariant> arguments = spy.first();
QCOMPARE(arguments.count(), 1);
QVERIFY(arguments.at(0).toBool());
QCOMPARE(spy.count(),1);
item->setClip(true);
QCOMPARE(spy.count(),1);
item->setClip(false);
QVERIFY(!item->clip());
QCOMPARE(spy.count(),2);
item->setClip(false);
QCOMPARE(spy.count(),2);
delete item;
}
void tst_QQuickItem::mapCoordinates()
{
QFETCH(int, x);
QFETCH(int, y);
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300, 300));
window->setSource(testFileUrl("mapCoordinates.qml"));
window->show();
qApp->processEvents();
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root != nullptr);
QQuickItem *a = findItem<QQuickItem>(window->rootObject(), "itemA");
QVERIFY(a != nullptr);
QQuickItem *b = findItem<QQuickItem>(window->rootObject(), "itemB");
QVERIFY(b != nullptr);
QVariant result;
QVERIFY(QMetaObject::invokeMethod(root, "mapAToB",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapToItem(b, QPointF(x, y)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromB",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapFromItem(b, QPointF(x, y)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAToNull",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapToScene(QPointF(x, y)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromNull",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapFromScene(QPointF(x, y)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAToGlobal",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapToGlobal(QPointF(x, y)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromGlobal",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), qobject_cast<QQuickItem*>(a)->mapFromGlobal(QPointF(x, y)));
// for orphans we are primarily testing that we don't crash.
// when orphaned the final position is the original position of the item translated by x,y
QVERIFY(QMetaObject::invokeMethod(root, "mapOrphanToGlobal",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), QPointF(150,150) + QPointF(x, y));
QVERIFY(QMetaObject::invokeMethod(root, "mapOrphanFromGlobal",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QCOMPARE(result.value<QPointF>(), -QPointF(150,150) + QPointF(x, y));
QString warning1 = testFileUrl("mapCoordinates.qml").toString() + ":35:5: QML Item: mapToItem() given argument \"1122\" which is neither null nor an Item";
QString warning2 = testFileUrl("mapCoordinates.qml").toString() + ":35:5: QML Item: mapFromItem() given argument \"1122\" which is neither null nor an Item";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QVERIFY(result.toBool());
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QVERIFY(QMetaObject::invokeMethod(root, "checkMapAFromInvalid",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QVERIFY(result.toBool());
delete window;
}
void tst_QQuickItem::mapCoordinates_data()
{
QTest::addColumn<int>("x");
QTest::addColumn<int>("y");
for (int i=-20; i<=20; i+=10)
QTest::newRow(QTest::toString(i)) << i << i;
}
void tst_QQuickItem::mapCoordinatesRect()
{
QFETCH(int, x);
QFETCH(int, y);
QFETCH(int, width);
QFETCH(int, height);
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300, 300));
window->setSource(testFileUrl("mapCoordinatesRect.qml"));
window->show();
qApp->processEvents();
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root != nullptr);
QQuickItem *a = findItem<QQuickItem>(window->rootObject(), "itemA");
QVERIFY(a != nullptr);
QQuickItem *b = findItem<QQuickItem>(window->rootObject(), "itemB");
QVERIFY(b != nullptr);
QVariant result;
QVERIFY(QMetaObject::invokeMethod(root, "mapAToB",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QCOMPARE(result.value<QRectF>(), qobject_cast<QQuickItem*>(a)->mapRectToItem(b, QRectF(x, y, width, height)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromB",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QCOMPARE(result.value<QRectF>(), qobject_cast<QQuickItem*>(a)->mapRectFromItem(b, QRectF(x, y, width, height)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAToNull",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QCOMPARE(result.value<QRectF>(), qobject_cast<QQuickItem*>(a)->mapRectToScene(QRectF(x, y, width, height)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromNull",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QCOMPARE(result.value<QRectF>(), qobject_cast<QQuickItem*>(a)->mapRectFromScene(QRectF(x, y, width, height)));
QString warning1 = testFileUrl("mapCoordinatesRect.qml").toString() + ":35:5: QML Item: mapToItem() given argument \"1122\" which is neither null nor an Item";
QString warning2 = testFileUrl("mapCoordinatesRect.qml").toString() + ":35:5: QML Item: mapFromItem() given argument \"1122\" which is neither null nor an Item";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QVERIFY(result.toBool());
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QVERIFY(QMetaObject::invokeMethod(root, "checkMapAFromInvalid",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y), Q_ARG(QVariant, width), Q_ARG(QVariant, height)));
QVERIFY(result.toBool());
delete window;
}
void tst_QQuickItem::mapCoordinatesRect_data()
{
QTest::addColumn<int>("x");
QTest::addColumn<int>("y");
QTest::addColumn<int>("width");
QTest::addColumn<int>("height");
for (int i=-20; i<=20; i+=5)
QTest::newRow(QTest::toString(i)) << i << i << i << i;
}
void tst_QQuickItem::transforms_data()
{
QTest::addColumn<QByteArray>("qml");
QTest::addColumn<QTransform>("transform");
QTest::newRow("translate") << QByteArray("Translate { x: 10; y: 20 }")
<< QTransform(1,0,0,0,1,0,10,20,1);
QTest::newRow("matrix4x4") << QByteArray("Matrix4x4 { matrix: Qt.matrix4x4(1,0,0,10, 0,1,0,15, 0,0,1,0, 0,0,0,1) }")
<< QTransform(1,0,0,0,1,0,10,15,1);
QTest::newRow("rotation") << QByteArray("Rotation { angle: 90 }")
<< QTransform(0,1,0,-1,0,0,0,0,1);
QTest::newRow("scale") << QByteArray("Scale { xScale: 1.5; yScale: -2 }")
<< QTransform(1.5,0,0,0,-2,0,0,0,1);
QTest::newRow("sequence") << QByteArray("[ Translate { x: 10; y: 20 }, Scale { xScale: 1.5; yScale: -2 } ]")
<< QTransform(1,0,0,0,1,0,10,20,1) * QTransform(1.5,0,0,0,-2,0,0,0,1);
}
void tst_QQuickItem::transforms()
{
QFETCH(QByteArray, qml);
QFETCH(QTransform, transform);
QQmlComponent component(&engine);
component.setData("import QtQuick 2.3\nItem { transform: "+qml+"}", QUrl::fromLocalFile(""));
QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
QVERIFY(item);
QCOMPARE(item->itemTransform(nullptr,nullptr), transform);
}
void tst_QQuickItem::childrenProperty()
{
QQmlComponent component(&engine, testFileUrl("childrenProperty.qml"));
QObject *o = component.create();
QVERIFY(o != nullptr);
QCOMPARE(o->property("test1").toBool(), true);
QCOMPARE(o->property("test2").toBool(), true);
QCOMPARE(o->property("test3").toBool(), true);
QCOMPARE(o->property("test4").toBool(), true);
QCOMPARE(o->property("test5").toBool(), true);
delete o;
}
void tst_QQuickItem::resourcesProperty()
{
QQmlComponent component(&engine, testFileUrl("resourcesProperty.qml"));
QObject *object = component.create();
QVERIFY(object != nullptr);
QQmlProperty property(object, "resources", component.creationContext());
QVERIFY(property.isValid());
QQmlListReference list = qvariant_cast<QQmlListReference>(property.read());
QVERIFY(list.isValid());
QCOMPARE(list.count(), 4);
QCOMPARE(object->property("test1").toBool(), true);
QCOMPARE(object->property("test2").toBool(), true);
QCOMPARE(object->property("test3").toBool(), true);
QCOMPARE(object->property("test4").toBool(), true);
QCOMPARE(object->property("test5").toBool(), true);
QCOMPARE(object->property("test6").toBool(), true);
QObject *subObject = object->findChild<QObject *>("subObject");
QVERIFY(subObject);
QCOMPARE(object, subObject->parent());
delete subObject;
QCOMPARE(list.count(), 3);
delete object;
}
void tst_QQuickItem::propertyChanges()
{
QQuickView *window = new QQuickView(nullptr);
window->setBaseSize(QSize(300, 300));
window->setSource(testFileUrl("propertychanges.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item");
QQuickItem *parentItem = findItem<QQuickItem>(window->rootObject(), "parentItem");
QVERIFY(item);
QVERIFY(parentItem);
QSignalSpy parentSpy(item, SIGNAL(parentChanged(QQuickItem*)));
QSignalSpy widthSpy(item, SIGNAL(widthChanged()));
QSignalSpy heightSpy(item, SIGNAL(heightChanged()));
QSignalSpy baselineOffsetSpy(item, SIGNAL(baselineOffsetChanged(qreal)));
QSignalSpy childrenRectSpy(parentItem, SIGNAL(childrenRectChanged(QRectF)));
QSignalSpy focusSpy(item, SIGNAL(focusChanged(bool)));
QSignalSpy wantsFocusSpy(parentItem, SIGNAL(activeFocusChanged(bool)));
QSignalSpy childrenChangedSpy(parentItem, SIGNAL(childrenChanged()));
QSignalSpy xSpy(item, SIGNAL(xChanged()));
QSignalSpy ySpy(item, SIGNAL(yChanged()));
item->setParentItem(parentItem);
item->setWidth(100.0);
item->setHeight(200.0);
item->setFocus(true);
item->setBaselineOffset(10.0);
QCOMPARE(item->parentItem(), parentItem);
QCOMPARE(parentSpy.count(),1);
QList<QVariant> parentArguments = parentSpy.first();
QCOMPARE(parentArguments.count(), 1);
QCOMPARE(item->parentItem(), qvariant_cast<QQuickItem *>(parentArguments.at(0)));
QCOMPARE(childrenChangedSpy.count(),1);
item->setParentItem(parentItem);
QCOMPARE(childrenChangedSpy.count(),1);
QCOMPARE(item->width(), 100.0);
QCOMPARE(widthSpy.count(),1);
QCOMPARE(item->height(), 200.0);
QCOMPARE(heightSpy.count(),1);
QCOMPARE(item->baselineOffset(), 10.0);
QCOMPARE(baselineOffsetSpy.count(),1);
QList<QVariant> baselineOffsetArguments = baselineOffsetSpy.first();
QCOMPARE(baselineOffsetArguments.count(), 1);
QCOMPARE(item->baselineOffset(), baselineOffsetArguments.at(0).toReal());
QCOMPARE(parentItem->childrenRect(), QRectF(0.0,0.0,100.0,200.0));
QCOMPARE(childrenRectSpy.count(),1);
QList<QVariant> childrenRectArguments = childrenRectSpy.at(0);
QCOMPARE(childrenRectArguments.count(), 1);
QCOMPARE(parentItem->childrenRect(), childrenRectArguments.at(0).toRectF());
QCOMPARE(item->hasActiveFocus(), true);
QCOMPARE(focusSpy.count(),1);
QList<QVariant> focusArguments = focusSpy.first();
QCOMPARE(focusArguments.count(), 1);
QCOMPARE(focusArguments.at(0).toBool(), true);
QCOMPARE(parentItem->hasActiveFocus(), false);
QCOMPARE(parentItem->hasFocus(), false);
QCOMPARE(wantsFocusSpy.count(),0);
item->setX(10.0);
QCOMPARE(item->x(), 10.0);
QCOMPARE(xSpy.count(), 1);
item->setY(10.0);
QCOMPARE(item->y(), 10.0);
QCOMPARE(ySpy.count(), 1);
delete window;
}
void tst_QQuickItem::nonexistentPropertyConnection()
{
// QTBUG-56551: don't crash
QQmlComponent component(&engine, testFileUrl("nonexistentPropertyConnection.qml"));
QObject *o = component.create();
QVERIFY(o);
delete o;
}
void tst_QQuickItem::childrenRect()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("childrenRect.qml"));
window->setBaseSize(QSize(240,320));
window->show();
QQuickItem *o = window->rootObject();
QQuickItem *item = o->findChild<QQuickItem*>("testItem");
QCOMPARE(item->width(), qreal(0));
QCOMPARE(item->height(), qreal(0));
o->setProperty("childCount", 1);
QCOMPARE(item->width(), qreal(10));
QCOMPARE(item->height(), qreal(20));
o->setProperty("childCount", 5);
QCOMPARE(item->width(), qreal(50));
QCOMPARE(item->height(), qreal(100));
o->setProperty("childCount", 0);
QCOMPARE(item->width(), qreal(0));
QCOMPARE(item->height(), qreal(0));
delete o;
delete window;
}
// QTBUG-11383
void tst_QQuickItem::childrenRectBug()
{
QQuickView *window = new QQuickView(nullptr);
QString warning = testFileUrl("childrenRectBug.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\"";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
window->setSource(testFileUrl("childrenRectBug.qml"));
window->show();
QQuickItem *o = window->rootObject();
QQuickItem *item = o->findChild<QQuickItem*>("theItem");
QCOMPARE(item->width(), qreal(200));
QCOMPARE(item->height(), qreal(100));
QCOMPARE(item->x(), qreal(100));
delete window;
}
// QTBUG-11465
void tst_QQuickItem::childrenRectBug2()
{
QQuickView *window = new QQuickView(nullptr);
QString warning1 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"width\"";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":7:5: QML Item: Binding loop detected for property \"height\"";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
window->setSource(testFileUrl("childrenRectBug2.qml"));
window->show();
QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(window->rootObject());
QVERIFY(rect);
QQuickItem *item = rect->findChild<QQuickItem*>("theItem");
QCOMPARE(item->width(), qreal(100));
QCOMPARE(item->height(), qreal(110));
QCOMPARE(item->x(), qreal(130));
QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(rect);
rectPrivate->setState("row");
QCOMPARE(item->width(), qreal(210));
QCOMPARE(item->height(), qreal(50));
QCOMPARE(item->x(), qreal(75));
delete window;
}
// QTBUG-12722
void tst_QQuickItem::childrenRectBug3()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("childrenRectBug3.qml"));
window->show();
//don't crash on delete
delete window;
}
// QTBUG-38732
void tst_QQuickItem::childrenRectBottomRightCorner()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("childrenRectBottomRightCorner.qml"));
window->show();
QQuickItem *rect = window->rootObject()->findChild<QQuickItem*>("childrenRectProxy");
QCOMPARE(rect->x(), qreal(-100));
QCOMPARE(rect->y(), qreal(-100));
QCOMPARE(rect->width(), qreal(50));
QCOMPARE(rect->height(), qreal(50));
delete window;
}
struct TestListener : public QQuickItemChangeListener
{
TestListener(bool remove = false) : remove(remove) { }
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &oldGeometry) override
{
record(item, QQuickItemPrivate::Geometry, oldGeometry);
}
void itemSiblingOrderChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::SiblingOrder);
}
void itemVisibilityChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::Visibility);
}
void itemOpacityChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::Opacity);
}
void itemRotationChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::Rotation);
}
void itemImplicitWidthChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::ImplicitWidth);
}
void itemImplicitHeightChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::ImplicitHeight);
}
void itemDestroyed(QQuickItem *item) override
{
record(item, QQuickItemPrivate::Destroyed);
}
void itemChildAdded(QQuickItem *item, QQuickItem *child) override
{
record(item, QQuickItemPrivate::Children, QVariant::fromValue(child));
}
void itemChildRemoved(QQuickItem *item, QQuickItem *child) override
{
record(item, QQuickItemPrivate::Children, QVariant::fromValue(child));
}
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override
{
record(item, QQuickItemPrivate::Parent, QVariant::fromValue(parent));
}
QQuickAnchorsPrivate *anchorPrivate() override { return nullptr; }
void record(QQuickItem *item, QQuickItemPrivate::ChangeType change, const QVariant &value = QVariant())
{
changes += change;
values[change] = value;
// QTBUG-54732
if (remove)
QQuickItemPrivate::get(item)->removeItemChangeListener(this, change);
}
int count(QQuickItemPrivate::ChangeType change) const
{
return changes.count(change);
}
QVariant value(QQuickItemPrivate::ChangeType change) const
{
return values.value(change);
}
bool remove;
QList<QQuickItemPrivate::ChangeType> changes;
QHash<QQuickItemPrivate::ChangeType, QVariant> values;
};
void tst_QQuickItem::changeListener()
{
QQuickWindow window;
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QQuickItem *item = new QQuickItem;
TestListener itemListener;
QQuickItemPrivate::get(item)->addItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff));
item->setImplicitWidth(10);
QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitWidth), 1);
QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 1);
QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,0)));
item->setImplicitHeight(20);
QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitHeight), 1);
QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 2);
QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,10,0)));
item->setWidth(item->width() + 30);
QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 3);
QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,10,20)));
item->setHeight(item->height() + 40);
QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 4);
QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,40,20)));
item->setOpacity(0.5);
QCOMPARE(itemListener.count(QQuickItemPrivate::Opacity), 1);
item->setRotation(90);
QCOMPARE(itemListener.count(QQuickItemPrivate::Rotation), 1);
item->setParentItem(window.contentItem());
QCOMPARE(itemListener.count(QQuickItemPrivate::Parent), 1);
item->setVisible(false);
QCOMPARE(itemListener.count(QQuickItemPrivate::Visibility), 1);
QQuickItemPrivate::get(item)->removeItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff));
QQuickItem *parent = new QQuickItem(window.contentItem());
TestListener parentListener;
QQuickItemPrivate::get(parent)->addItemChangeListener(&parentListener, QQuickItemPrivate::Children);
QQuickItem *child1 = new QQuickItem;
QQuickItem *child2 = new QQuickItem;
TestListener child1Listener;
TestListener child2Listener;
QQuickItemPrivate::get(child1)->addItemChangeListener(&child1Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed);
QQuickItemPrivate::get(child2)->addItemChangeListener(&child2Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed);
child1->setParentItem(parent);
QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 1);
QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1));
QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 1);
QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent));
child2->setParentItem(parent);
QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 2);
QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2));
QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 1);
QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent));
child2->stackBefore(child1);
QCOMPARE(child1Listener.count(QQuickItemPrivate::SiblingOrder), 1);
QCOMPARE(child2Listener.count(QQuickItemPrivate::SiblingOrder), 1);
child1->setParentItem(nullptr);
QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 3);
QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1));
QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 2);
QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue<QQuickItem *>(nullptr));
delete child1;
QCOMPARE(child1Listener.count(QQuickItemPrivate::Destroyed), 1);
delete child2;
QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 4);
QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2));
QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 2);
QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue<QQuickItem *>(nullptr));
QCOMPARE(child2Listener.count(QQuickItemPrivate::Destroyed), 1);
QQuickItemPrivate::get(parent)->removeItemChangeListener(&parentListener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// QTBUG-54732: all listeners should get invoked even if they remove themselves while iterating the listeners
QList<TestListener *> listeners;
for (int i = 0; i < 5; ++i)
listeners << new TestListener(true);
// itemVisibilityChanged x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setVisible(false);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemRotationChanged x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setRotation(90);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemOpacityChanged x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setOpacity(0.5);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemChildAdded() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
child1 = new QQuickItem(parent);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Children), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemParentChanged() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(child1)->addItemChangeListener(listener, QQuickItemPrivate::Parent);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), listeners.count());
child1->setParentItem(nullptr);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), 0);
// itemImplicitWidthChanged() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setImplicitWidth(parent->implicitWidth() + 1);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemImplicitHeightChanged() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setImplicitHeight(parent->implicitHeight() + 1);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemGeometryChanged() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
parent->setWidth(parent->width() + 1);
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemChildRemoved() x 5
child1->setParentItem(parent);
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
delete child1;
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Children), 2);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemDestroyed() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Destroyed);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
delete parent;
foreach (TestListener *listener, listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Destroyed), 1);
}
// QTBUG-13893
void tst_QQuickItem::transformCrash()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("transformCrash.qml"));
window->show();
delete window;
}
void tst_QQuickItem::implicitSize()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("implicitsize.qml"));
window->show();
QQuickItem *item = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(item);
QCOMPARE(item->width(), qreal(80));
QCOMPARE(item->height(), qreal(60));
QCOMPARE(item->implicitWidth(), qreal(200));
QCOMPARE(item->implicitHeight(), qreal(100));
QMetaObject::invokeMethod(item, "resetSize");
QCOMPARE(item->width(), qreal(200));
QCOMPARE(item->height(), qreal(100));
QMetaObject::invokeMethod(item, "changeImplicit");
QCOMPARE(item->implicitWidth(), qreal(150));
QCOMPARE(item->implicitHeight(), qreal(80));
QCOMPARE(item->width(), qreal(150));
QCOMPARE(item->height(), qreal(80));
QMetaObject::invokeMethod(item, "assignImplicitBinding");
QCOMPARE(item->implicitWidth(), qreal(150));
QCOMPARE(item->implicitHeight(), qreal(80));
QCOMPARE(item->width(), qreal(150));
QCOMPARE(item->height(), qreal(80));
QMetaObject::invokeMethod(item, "increaseImplicit");
QCOMPARE(item->implicitWidth(), qreal(200));
QCOMPARE(item->implicitHeight(), qreal(100));
QCOMPARE(item->width(), qreal(175));
QCOMPARE(item->height(), qreal(90));
QMetaObject::invokeMethod(item, "changeImplicit");
QCOMPARE(item->implicitWidth(), qreal(150));
QCOMPARE(item->implicitHeight(), qreal(80));
QCOMPARE(item->width(), qreal(150));
QCOMPARE(item->height(), qreal(80));
QMetaObject::invokeMethod(item, "assignUndefinedBinding");
QCOMPARE(item->implicitWidth(), qreal(150));
QCOMPARE(item->implicitHeight(), qreal(80));
QCOMPARE(item->width(), qreal(150));
QCOMPARE(item->height(), qreal(80));
QMetaObject::invokeMethod(item, "increaseImplicit");
QCOMPARE(item->implicitWidth(), qreal(200));
QCOMPARE(item->implicitHeight(), qreal(100));
QCOMPARE(item->width(), qreal(175));
QCOMPARE(item->height(), qreal(90));
QMetaObject::invokeMethod(item, "changeImplicit");
QCOMPARE(item->implicitWidth(), qreal(150));
QCOMPARE(item->implicitHeight(), qreal(80));
QCOMPARE(item->width(), qreal(150));
QCOMPARE(item->height(), qreal(80));
delete window;
}
void tst_QQuickItem::qtbug_16871()
{
QQmlComponent component(&engine, testFileUrl("qtbug_16871.qml"));
QObject *o = component.create();
QVERIFY(o != nullptr);
delete o;
}
void tst_QQuickItem::visibleChildren()
{
QQuickView *window = new QQuickView(nullptr);
window->setSource(testFileUrl("visiblechildren.qml"));
window->show();
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root);
QCOMPARE(root->property("test1_1").toBool(), true);
QCOMPARE(root->property("test1_2").toBool(), true);
QCOMPARE(root->property("test1_3").toBool(), true);
QCOMPARE(root->property("test1_4").toBool(), true);
QMetaObject::invokeMethod(root, "hideFirstAndLastRowChild");
QCOMPARE(root->property("test2_1").toBool(), true);
QCOMPARE(root->property("test2_2").toBool(), true);
QCOMPARE(root->property("test2_3").toBool(), true);
QCOMPARE(root->property("test2_4").toBool(), true);
QMetaObject::invokeMethod(root, "showLastRowChildsLastChild");
QCOMPARE(root->property("test3_1").toBool(), true);
QCOMPARE(root->property("test3_2").toBool(), true);
QCOMPARE(root->property("test3_3").toBool(), true);
QCOMPARE(root->property("test3_4").toBool(), true);
QMetaObject::invokeMethod(root, "showLastRowChild");
QCOMPARE(root->property("test4_1").toBool(), true);
QCOMPARE(root->property("test4_2").toBool(), true);
QCOMPARE(root->property("test4_3").toBool(), true);
QCOMPARE(root->property("test4_4").toBool(), true);
QString warning1 = testFileUrl("visiblechildren.qml").toString() + ":87: TypeError: Cannot read property 'visibleChildren' of null";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QMetaObject::invokeMethod(root, "tryWriteToReadonlyVisibleChildren");
QMetaObject::invokeMethod(root, "reparentVisibleItem3");
QCOMPARE(root->property("test6_1").toBool(), true);
QCOMPARE(root->property("test6_2").toBool(), true);
QCOMPARE(root->property("test6_3").toBool(), true);
QCOMPARE(root->property("test6_4").toBool(), true);
QMetaObject::invokeMethod(root, "reparentImlicitlyInvisibleItem4_1");
QCOMPARE(root->property("test7_1").toBool(), true);
QCOMPARE(root->property("test7_2").toBool(), true);
QCOMPARE(root->property("test7_3").toBool(), true);
QCOMPARE(root->property("test7_4").toBool(), true);
// FINALLY TEST THAT EVERYTHING IS AS EXPECTED
QCOMPARE(root->property("test8_1").toBool(), true);
QCOMPARE(root->property("test8_2").toBool(), true);
QCOMPARE(root->property("test8_3").toBool(), true);
QCOMPARE(root->property("test8_4").toBool(), true);
QCOMPARE(root->property("test8_5").toBool(), true);
delete window;
}
void tst_QQuickItem::parentLoop()
{
QQuickView *window = new QQuickView(nullptr);
#if QT_CONFIG(regularexpression)
QRegularExpression msgRegexp = QRegularExpression("QQuickItem::setParentItem: Parent QQuickItem\\(.*\\) is already part of the subtree of QQuickItem\\(.*\\)");
QTest::ignoreMessage(QtWarningMsg, msgRegexp);
#endif
window->setSource(testFileUrl("parentLoop.qml"));
QQuickItem *root = qobject_cast<QQuickItem*>(window->rootObject());
QVERIFY(root);
QQuickItem *item1 = root->findChild<QQuickItem*>("item1");
QVERIFY(item1);
QCOMPARE(item1->parentItem(), root);
QQuickItem *item2 = root->findChild<QQuickItem*>("item2");
QVERIFY(item2);
QCOMPARE(item2->parentItem(), item1);
delete window;
}
void tst_QQuickItem::contains_data()
{
QTest::addColumn<bool>("circleTest");
QTest::addColumn<bool>("insideTarget");
QTest::addColumn<QList<QPoint> >("points");
QList<QPoint> points;
points << QPoint(176, 176)
<< QPoint(176, 226)
<< QPoint(226, 176)
<< QPoint(226, 226)
<< QPoint(150, 200)
<< QPoint(200, 150)
<< QPoint(200, 250)
<< QPoint(250, 200);
QTest::newRow("hollow square: testing points inside") << false << true << points;
points.clear();
points << QPoint(162, 162)
<< QPoint(162, 242)
<< QPoint(242, 162)
<< QPoint(242, 242)
<< QPoint(200, 200)
<< QPoint(175, 200)
<< QPoint(200, 175)
<< QPoint(200, 228)
<< QPoint(228, 200)
<< QPoint(200, 122)
<< QPoint(122, 200)
<< QPoint(200, 280)
<< QPoint(280, 200);
QTest::newRow("hollow square: testing points outside") << false << false << points;
points.clear();
points << QPoint(174, 174)
<< QPoint(174, 225)
<< QPoint(225, 174)
<< QPoint(225, 225)
<< QPoint(165, 200)
<< QPoint(200, 165)
<< QPoint(200, 235)
<< QPoint(235, 200);
QTest::newRow("hollow circle: testing points inside") << true << true << points;
points.clear();
points << QPoint(160, 160)
<< QPoint(160, 240)
<< QPoint(240, 160)
<< QPoint(240, 240)
<< QPoint(200, 200)
<< QPoint(185, 185)
<< QPoint(185, 216)
<< QPoint(216, 185)
<< QPoint(216, 216)
<< QPoint(145, 200)
<< QPoint(200, 145)
<< QPoint(255, 200)
<< QPoint(200, 255);
QTest::newRow("hollow circle: testing points outside") << true << false << points;
}
void tst_QQuickItem::contains()
{
QFETCH(bool, circleTest);
QFETCH(bool, insideTarget);
QFETCH(QList<QPoint>, points);
QQuickView *window = new QQuickView(nullptr);
window->rootContext()->setContextProperty("circleShapeTest", circleTest);
window->setBaseSize(QSize(400, 400));
window->setSource(testFileUrl("hollowTestItem.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window));
QCOMPARE(QGuiApplication::focusWindow(), window);
QQuickItem *root = qobject_cast<QQuickItem *>(window->rootObject());
QVERIFY(root);
HollowTestItem *hollowItem = root->findChild<HollowTestItem *>("hollowItem");
QVERIFY(hollowItem);
foreach (const QPoint &point, points) {
// check mouse hover
QTest::mouseMove(window, point);
QTest::qWait(10);
QCOMPARE(hollowItem->isHovered(), insideTarget);
// check mouse press
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, point);
QTest::qWait(10);
QCOMPARE(hollowItem->isPressed(), insideTarget);
// check mouse release
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, point);
QTest::qWait(10);
QCOMPARE(hollowItem->isPressed(), false);
}
delete window;
}
void tst_QQuickItem::childAt()
{
QQuickItem parent;
QQuickItem child1;
child1.setX(0);
child1.setY(0);
child1.setWidth(100);
child1.setHeight(100);
child1.setParentItem(&parent);
QQuickItem child2;
child2.setX(50);
child2.setY(50);
child2.setWidth(100);
child2.setHeight(100);
child2.setParentItem(&parent);
QQuickItem child3;
child3.setX(0);
child3.setY(200);
child3.setWidth(50);
child3.setHeight(50);
child3.setParentItem(&parent);
QCOMPARE(parent.childAt(0, 0), &child1);
QCOMPARE(parent.childAt(0, 99), &child1);
QCOMPARE(parent.childAt(25, 25), &child1);
QCOMPARE(parent.childAt(25, 75), &child1);
QCOMPARE(parent.childAt(75, 25), &child1);
QCOMPARE(parent.childAt(75, 75), &child2);
QCOMPARE(parent.childAt(149, 149), &child2);
QCOMPARE(parent.childAt(25, 200), &child3);
QCOMPARE(parent.childAt(0, 150), static_cast<QQuickItem *>(nullptr));
QCOMPARE(parent.childAt(300, 300), static_cast<QQuickItem *>(nullptr));
}
void tst_QQuickItem::grab()
{
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Skipping due to grabToImage not functional on offscreen/minimimal platforms");
QQuickView view;
view.setSource(testFileUrl("grabToImage.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject());
QVERIFY(root);
QQuickItem *item = root->findChild<QQuickItem *>("myItem");
QVERIFY(item);
#if QT_CONFIG(opengl)
{ // Default size (item is 100x100)
QSharedPointer<QQuickItemGrabResult> result = item->grabToImage();
QSignalSpy spy(result.data(), SIGNAL(ready()));
QTRY_VERIFY(spy.size() > 0);
QVERIFY(!result->url().isEmpty());
QImage image = result->image();
QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0));
QCOMPARE(image.pixel(99, 99), qRgb(0, 0, 255));
}
{ // Smaller size
QSharedPointer<QQuickItemGrabResult> result = item->grabToImage(QSize(50, 50));
QVERIFY(!result.isNull());
QSignalSpy spy(result.data(), SIGNAL(ready()));
QTRY_VERIFY(spy.size() > 0);
QVERIFY(!result->url().isEmpty());
QImage image = result->image();
QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0));
QCOMPARE(image.pixel(49, 49), qRgb(0, 0, 255));
}
#endif
}
void tst_QQuickItem::isAncestorOf()
{
QQuickItem parent;
QQuickItem sub1;
sub1.setParentItem(&parent);
QQuickItem child1;
child1.setParentItem(&sub1);
QQuickItem child2;
child2.setParentItem(&sub1);
QQuickItem sub2;
sub2.setParentItem(&parent);
QQuickItem child3;
child3.setParentItem(&sub2);
QQuickItem child4;
child4.setParentItem(&sub2);
QVERIFY(parent.isAncestorOf(&sub1));
QVERIFY(parent.isAncestorOf(&sub2));
QVERIFY(parent.isAncestorOf(&child1));
QVERIFY(parent.isAncestorOf(&child2));
QVERIFY(parent.isAncestorOf(&child3));
QVERIFY(parent.isAncestorOf(&child4));
QVERIFY(sub1.isAncestorOf(&child1));
QVERIFY(sub1.isAncestorOf(&child2));
QVERIFY(!sub1.isAncestorOf(&child3));
QVERIFY(!sub1.isAncestorOf(&child4));
QVERIFY(sub2.isAncestorOf(&child3));
QVERIFY(sub2.isAncestorOf(&child4));
QVERIFY(!sub2.isAncestorOf(&child1));
QVERIFY(!sub2.isAncestorOf(&child2));
QVERIFY(!sub1.isAncestorOf(&sub1));
QVERIFY(!sub2.isAncestorOf(&sub2));
}
QTEST_MAIN(tst_QQuickItem)
#include "tst_qquickitem.moc"