Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#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 <QtCore/qscopeguard.h>
#include <QtGui/qscreen.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/private/qinputmethod_p.h>
#include <QtQuick/private/qquickloader_p.h>
#include <QtQuick/private/qquickpalette_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQuick/private/qquickanchors_p.h>
#include <QtQuick/private/qquicktranslate_p.h>
#include <QtGui/qstylehints.h>
#include <private/qquickitem_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <QtQuickTest/QtQuickTest>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/platforminputcontext_p.h>
#include <QtTest/private/qpropertytesthelper_p.h>
#ifdef QT_WIDGETS_LIB
#include <QtWidgets/qwidget.h>
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qlineedit.h>
#endif
using namespace QQuickVisualTestUtils;
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
class tst_QQuickItem : public QQmlDataTest
{
Q_OBJECT
public:
tst_QQuickItem();
private slots:
void initTestCase() override;
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_data();
void activeFocusOnTab_infiniteLoop();
void activeFocusOnTab_infiniteLoopControls();
void nextItemInFocusChain();
void nextItemInFocusChain2();
void nextItemInFocusChain3();
void nextItemInFocusChainWrap_data();
void nextItemInFocusChainWrap();
void tabFence();
void qtbug_50516();
void qtbug_50516_2_data();
void qtbug_50516_2();
void focusableItemReparentedToLoadedComponent();
void keys();
#if QT_CONFIG(shortcut)
void standardKeys_data();
void standardKeys();
#endif
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 keyNavigation_repeater();
void layoutMirroring();
void layoutMirroringWindow();
void layoutMirroringIllegalParent();
void smooth();
void antialiasing();
void clip();
void mapCoordinates();
void mapCoordinates_data();
void mapCoordinatesRect();
void mapCoordinatesRect_data();
void mapCoordinatesWithWindows();
void mapCoordinatesWithFraction();
void propertyChanges();
void nonexistentPropertyConnection();
void transforms();
void transforms_data();
void childrenRect();
void childrenRectBug();
void childrenRectBug2();
void childrenRectBug3();
void childrenRectBottomRightCorner();
void childrenProperty();
void resourcesProperty();
void bindableProperties_data();
void bindableProperties();
void changeListener();
void transformCrash();
void implicitSize();
void qtbug_16871();
void visibleChildren();
void parentLoop();
void contains_data();
void contains();
void childAt();
void isAncestorOf();
void grab();
void grabIsItemSizeInDevicePixels();
void grabFromQmlIsItemSizeInDevicePixels();
void grabMapsItemUnitsToDevicePixels();
void colorGroup();
void paletteAllocated();
void undefinedIsInvalidForWidthAndHeight();
void viewport_data();
void viewport();
void qobject_castOnDestruction();
void signalsOnDestruction_data();
void signalsOnDestruction();
void visibleChanged();
void lastFocusChangeReason();
void focusInScopeChanges();
#ifdef QT_WIDGETS_LIB
void embeddedInWidgetsFocus_data();
void embeddedInWidgetsFocus();
#endif
void gcIntegration();
void focusWorksAfterEnablingRootItem();
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) override {
mKey = e->key();
if (e->key() == Qt::Key_A)
e->accept();
else
e->ignore();
}
void keyReleaseEvent(QKeyEvent *e) override {
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) override {
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 NOTIFY circleChanged)
Q_PROPERTY(qreal holeRadius READ holeRadius WRITE setHoleRadius NOTIFY holeRadiusChanged)
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; emit circleChanged(); }
qreal holeRadius() const { return m_holeRadius; }
void setHoleRadius(qreal radius) { m_holeRadius = radius; emit holeRadiusChanged(); }
bool contains(const QPointF &point) const override {
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;
}
signals:
void circleChanged();
void holeRadiusChanged();
protected:
void hoverEnterEvent(QHoverEvent *) override { m_isHovered = true; }
void hoverLeaveEvent(QHoverEvent *) override { m_isHovered = false; }
void mousePressEvent(QMouseEvent *) override { m_isPressed = true; }
void mouseReleaseEvent(QMouseEvent *) override { m_isPressed = false; }
private:
bool m_isPressed;
bool m_isHovered;
bool m_isCircle;
qreal m_holeRadius;
};
QML_DECLARE_TYPE(HollowTestItem);
class ViewportTestItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QRectF viewport READ viewport NOTIFY viewportChanged)
public:
ViewportTestItem(QQuickItem *parent = nullptr) : QQuickItem(parent) { }
QRectF viewport() const { return clipRect(); }
signals:
void viewportChanged();
};
QML_DECLARE_TYPE(ViewportTestItem);
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()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
}
void tst_QQuickItem::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem");
qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem");
qmlRegisterType<ViewportTestItem>("Test", 1, 0, "ViewportTestItem");
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
}
// Tab: sub2->button21
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// Tab: button21->button22
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// Tab: button22->edit
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: edit->button22
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button22->button21
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button21->sub2
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: sub2->button12
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button12->button11
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button11->edit
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
}
// BackTab: button11->edit
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
}
// 4 Tabs: button2->button3, through a row
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 Tabs: button3->button4, through a flow
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 Tabs: button4->button5, through a focusscope
// parent is activeFocusOnTab:false, one of children is focus:true
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 Tabs: button5->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children is focus:true
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 5 Tabs: button6->button7, through a focusscope
// parent is activeFocusOnTab:true, none of children is focus:true
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button7->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 Tabs: button6->button7, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button7->button6, through a focusscope
// parent is activeFocusOnTab:true, one of children got focus:true in previous code
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button6->button5, through a focusscope(parent is activeFocusOnTab: false)
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button5->button4, through a focusscope(parent is activeFocusOnTab: false)
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button4->button3, through a flow
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button3->button2, through a row
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// 4 BackTabs: button2->button1, through a repeater
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
}
// BackTab: edit->button12
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button12->button11
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// BackTab: button11->edit
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
// Tab: button1->button1
{
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(!key.isAccepted());
QVERIFY_ACTIVE_FOCUS(item);
}
// BackTab: button1->button1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(!key.isAccepted());
QVERIFY_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(content);
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_ACTIVE_FOCUS(button1);
}
// Tab: button1->button2
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(button2);
QVERIFY(!button1->hasActiveFocus());
}
// BackTab: button2->button1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(button1);
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_ACTIVE_FOCUS(content);
QQuickItem *textinput1 = findItem<QQuickItem>(window->rootObject(), "textinput1");
QVERIFY(textinput1);
QQuickItem *textedit1 = findItem<QQuickItem>(window->rootObject(), "textedit1");
QVERIFY(textedit1);
QVERIFY(!textinput1->hasActiveFocus());
textinput1->forceActiveFocus();
QVERIFY_ACTIVE_FOCUS(textinput1);
// Tab: textinput1->textedit1
{
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textedit1);
}
// BackTab: textedit1->textinput1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textinput1);
}
// BackTab: textinput1->textedit1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textedit1);
}
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_ACTIVE_FOCUS(content);
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_ACTIVE_FOCUS(textinput1);
// Tab: textinput1->textinput2
{
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textinput2);
}
// Tab: textinput2->textedit1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textedit1);
}
// BackTab: textedit1->textinput2
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textinput2);
}
// BackTab: textinput2->textinput1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textinput1);
}
// BackTab: textinput1->textedit2
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textedit2);
}
// BackTab: textedit2->textedit1
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textedit1);
}
// BackTab: textedit1->textinput2
{
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(textinput2);
}
delete window;
}
void tst_QQuickItem::activeFocusOnTab_infiniteLoop_data()
{
QTest::addColumn<QUrl>("source");
QTest::newRow("infiniteLoop") << testFileUrl("activeFocusOnTab_infiniteLoop.qml"); // QTBUG-68271
QTest::newRow("infiniteLoop2") << testFileUrl("activeFocusOnTab_infiniteLoop2.qml");// QTBUG-81510
}
void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
{
QFETCH(QUrl, source);
// create a window where the currently focused item is not visible
QScopedPointer<QQuickView>window(new QQuickView());
window->setSource(source);
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());
item = hiddenChild->nextItemInFocusChain(false);
QCOMPARE(item, window->rootObject());
}
void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls()
{
auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml");
QScopedPointer<QQuickView>window(new QQuickView());
window->setSource(source);
window->show();
QVERIFY(window->errors().isEmpty());
QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang
}
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 tst_QQuickItem::nextItemInFocusChainWrap_data()
{
QTest::addColumn<bool>("wrap");
QTest::addColumn<bool>("forward");
QTest::addRow("Wrap: true, Tab") << true << true;
QTest::addRow("Wrap: false, Tab") << false << true;
QTest::addRow("Wrap: true, Backtab") << true << false;
QTest::addRow("Wrap: false, Backtab") << false << false;
}
void tst_QQuickItem::nextItemInFocusChainWrap()
{
if (!qt_tab_all_widgets())
QSKIP("This function doesn't support NOT iterating all.");
QFETCH(bool, wrap);
QFETCH(bool, forward);
QQuickView window;
window.setBaseSize(QSize(300, 300));
window.setSource(testFileUrl("focusChainWrap.qml"));
window.show();
window.requestActivate();
QVERIFY(QTest::qWaitForWindowFocused(&window));
QCOMPARE(QGuiApplication::focusWindow(), &window);
QQuickItem *rect1 = findItem<QQuickItem>(window.rootObject(), "rect1");
QQuickItem *rect11 = findItem<QQuickItem>(window.rootObject(), "rect11");
QQuickItem *rect12 = findItem<QQuickItem>(window.rootObject(), "rect12");
QQuickItem *rect13 = findItem<QQuickItem>(window.rootObject(), "rect13");
QQuickItem *rect2 = findItem<QQuickItem>(window.rootObject(), "rect2");
QQuickItem *rect21 = findItem<QQuickItem>(window.rootObject(), "rect21");
QQuickItem *rect22 = findItem<QQuickItem>(window.rootObject(), "rect22");
QQuickItem *rect23 = findItem<QQuickItem>(window.rootObject(), "rect23");
QList<QQuickItem *> expectedFocusChain = { rect1, rect11, rect12, rect13,
rect2, rect21, rect22, rect23 };
if (!forward)
std::reverse(expectedFocusChain.begin(), expectedFocusChain.end());
expectedFocusChain.at(0)->forceActiveFocus();
QQuickItem *activeFocusItem = window.activeFocusItem();
QTRY_COMPARE(activeFocusItem, expectedFocusChain.at(0));
const Qt::Key tabKey = forward ? Qt::Key_Tab : Qt::Key_Backtab;
for (int i = 1; i < expectedFocusChain.size(); i++) {
QVERIFY(activeFocusItem);
const auto nextPrev = QQuickItemPrivate::nextPrevItemInTabFocusChain(activeFocusItem, forward, wrap);
QCOMPARE(nextPrev, expectedFocusChain.at(i));
QTest::keyClick(&window, tabKey);
QTRY_VERIFY_ACTIVE_FOCUS(nextPrev);
activeFocusItem = window.activeFocusItem();
}
QCOMPARE(activeFocusItem, expectedFocusChain.last());
const auto nextPrev = QQuickItemPrivate::nextPrevItemInTabFocusChain(activeFocusItem, forward, wrap);
QCOMPARE(nextPrev, wrap ? expectedFocusChain.at(0) : nullptr);
}
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);
QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, forward ? Qt::NoModifier : Qt::ShiftModifier);
QGuiApplication::sendEvent(window, &key);
QVERIFY2(key.isAccepted(), qPrintable(descrStr));
QQuickItem *item = findItem<QQuickItem>(window->rootObject(), *objectName);
QVERIFY2(item, qPrintable(descrStr));
QVERIFY2(item->hasActiveFocus(), qPrintable(descrStr));
}
}
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_ACTIVE_FOCUS(window->rootObject());
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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(window->rootObject());
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_ACTIVE_FOCUS(window->rootObject());
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::focusableItemReparentedToLoadedComponent() // QTBUG-89736
{
QQuickView window;
window.setSource(testFileUrl("focusableItemReparentedToLoadedComponent.qml"));
window.show();
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QGuiApplication::focusWindow(), &window);
QQuickLoader *loader = window.rootObject()->findChild<QQuickLoader *>();
QVERIFY(loader);
QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput *>();
QVERIFY(textInput);
// click to focus
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
QTRY_VERIFY_ACTIVE_FOCUS(textInput);
// unload and reload
auto component = loader->sourceComponent();
loader->resetSourceComponent();
QTRY_VERIFY(loader->status() == QQuickLoader::Null);
loader->setSourceComponent(component);
QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
// click to focus again
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, {10, 10});
QTRY_VERIFY_ACTIVE_FOCUS(textInput);
}
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();
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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));
QKeyEvent 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);
QKeyEvent 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);
QKeyEvent 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;
}
#if QT_CONFIG(shortcut)
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].key();
Qt::KeyboardModifiers modifiers = keySequence[0].keyboardModifiers();
QKeyEvent keyEvent(eventType, key, modifiers);
QGuiApplication::sendEvent(&view, &keyEvent);
QCOMPARE(item->property("pressed").toBool(), pressed);
QCOMPARE(item->property("released").toBool(), released);
}
#endif // QT_CONFIG(shortcut)
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);
{
QKeyEvent 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();
{
QKeyEvent 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();
{
QKeyEvent 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());
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);
QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
QVERIFY(anchorsMirrored(rootItem, "mirrored1"));
QVERIFY(anchorsMirrored(rootItem, "mirrored2"));
QVERIFY(!anchorsMirrored(rootItem, "notMirrored1"));
QVERIFY(!anchorsMirrored(rootItem, "notMirrored2"));
QVERIFY(anchorsMirrored(rootItem, "inheritedMirror1"));
QVERIFY(anchorsMirrored(rootItem, "inheritedMirror2"));
QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "mirrored1")->isMirrorImplicit);
QVERIFY(!childPrivate(rootItem, "mirrored2")->isMirrorImplicit);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent);
QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent);
QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent);
QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent);
QVERIFY(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem);
// load dynamic content using Loader that needs to inherit mirroring
rootItem->setProperty("state", "newContent");
QVERIFY(!childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored3")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit);
QVERIFY(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent);
QVERIFY(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent);
QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem);
QVERIFY(!childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem);
// disable inheritance
rootItem->setProperty("childrenInherit", false);
QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
// re-enable inheritance
rootItem->setProperty("childrenInherit", true);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror);
QVERIFY(!childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror);
QVERIFY(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror);
//
// 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);
QVERIFY(QQuickItemPrivate::get(childItem1)->effectiveLayoutMirror);
QVERIFY(QQuickItemPrivate::get(childItem1)->inheritMirrorFromParent);
// inherit through a parent change
QQuickItem *childItem2 = new QQuickItem();
QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
childItem2->setParentItem(parentItem1);
QVERIFY(QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
QVERIFY(QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
// 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);
QVERIFY(!QQuickItemPrivate::get(childItem2)->effectiveLayoutMirror);
QVERIFY(!QQuickItemPrivate::get(childItem2)->inheritMirrorFromParent);
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());
QVERIFY(content->effectiveLayoutMirror);
QVERIFY(content->inheritedLayoutMirror);
QVERIFY(!content->isMirrorImplicit);
QVERIFY(content->inheritMirrorFromParent);
QVERIFY(content->inheritMirrorFromItem);
}
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: LayoutMirroring 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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
}
// down
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// left
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// up
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// tab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// backtab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
}
// left
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
// 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_ACTIVE_FOCUS(item);
}
// tab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// backtab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
//Set item 3 to not visible
item = findItem<QQuickItem>(window->rootObject(), "item3");
QVERIFY(item);
item->setVisible(false);
QVERIFY(!item->isVisible());
// tab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// backtab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
}
// back to item1
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// down
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// move to item4
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// left
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// back to item4
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// up
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// back to item4
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// tab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// back to item4
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
// backtab
{
QKeyEvent 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_ACTIVE_FOCUS(item);
}
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_ACTIVE_FOCUS(root);
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_ACTIVE_FOCUS(item);
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
{
QKeyEvent 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
{
QKeyEvent 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_ACTIVE_FOCUS(item);
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_ACTIVE_FOCUS(item);
QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
QVERIFY_ACTIVE_FOCUS(item);
delete window;
}
void tst_QQuickItem::keyNavigation_repeater()
{
// QTBUG-83356
QScopedPointer<QQuickView> window(new QQuickView());
window->setBaseSize(QSize(240,320));
window->setSource(testFileUrl("keynavigationtest_repeater.qml"));
window->show();
window->requestActivate();
QVariant result;
QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "verify",
Q_RETURN_ARG(QVariant, result)));
QVERIFY(result.toBool());
}
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.size(),1);
QList<QVariant> arguments = spy.first();
QCOMPARE(arguments.size(), 1);
QVERIFY(arguments.at(0).toBool());
item->setSmooth(true);
QCOMPARE(spy.size(),1);
item->setSmooth(false);
QVERIFY(!item->smooth());
QCOMPARE(spy.size(),2);
item->setSmooth(false);
QCOMPARE(spy.size(),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.size(),1);
QList<QVariant> arguments = spy.first();
QCOMPARE(arguments.size(), 1);
QVERIFY(arguments.at(0).toBool());
item->setAntialiasing(true);
QCOMPARE(spy.size(),1);
item->setAntialiasing(false);
QVERIFY(!item->antialiasing());
QCOMPARE(spy.size(),2);
item->setAntialiasing(false);
QCOMPARE(spy.size(),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.size(), 1);
QVERIFY(arguments.at(0).toBool());
QCOMPARE(spy.size(),1);
item->setClip(true);
QCOMPARE(spy.size(),1);
item->setClip(false);
QVERIFY(!item->clip());
QCOMPARE(spy.size(),2);
item->setClip(false);
QCOMPARE(spy.size(),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, "mapAToBPoint",
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, "mapAFromBPoint",
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, "mapAToGlobalPoint",
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)));
QVERIFY(QMetaObject::invokeMethod(root, "mapAFromGlobalPoint",
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));
QRegularExpression warning1 = QRegularExpression(
".*Could not convert argument 0 from 1122 to const QQuickItem\\*.*");
QRegularExpression warning2 = QRegularExpression(".*checkMapA.*Invalid@.*");
QTest::ignoreMessage(QtWarningMsg, warning1);
QTest::ignoreMessage(QtWarningMsg, warning2);
QVERIFY(QMetaObject::invokeMethod(root, "checkMapAToInvalid",
Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, x), Q_ARG(QVariant, y)));
QVERIFY(result.toBool());
QTest::ignoreMessage(QtWarningMsg, warning1);
QTest::ignoreMessage(QtWarningMsg, 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);
std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
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, "mapAToBRect",
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, "mapAFromBRect",
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)));
QRegularExpression warning1 = QRegularExpression(".*Could not convert argument 0 from 1122 to const QQuickItem\\*.*");
QRegularExpression warning2 = QRegularExpression(".*checkMapA.*Invalid@.*");
QTest::ignoreMessage(QtWarningMsg, warning1);
QTest::ignoreMessage(QtWarningMsg, warning2);
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, warning1);
QTest::ignoreMessage(QtWarningMsg, 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());
}
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::mapCoordinatesWithWindows()
{
QQmlComponent component(&engine, testFileUrl("mapCoordinatesWithWindows.qml"));
std::unique_ptr<QObject> root(component.create());
QVERIFY(root);
auto *windowA = root->property("windowA").value<QQuickWindow*>();
QVERIFY(windowA);
// The window container geometry, parenting, etc, is applied
// during polish, so to test these we need to wait for one.
QVERIFY(QQuickTest::qWaitForPolish(windowA));
auto *childItem = windowA->findChild<QQuickItem*>("childItem");
QVERIFY(childItem);
QPoint itemPos = childItem->position().toPoint();
QCOMPARE(childItem->mapToScene({0, 0}), itemPos);
QCOMPARE(childItem->mapToGlobal({0, 0}), windowA->position() + itemPos);
auto *childItemInChildWindow = windowA->findChild<QQuickItem*>("childItemInChildWindow");
QVERIFY(childItemInChildWindow);
QPoint windowItemPos = childItemInChildWindow->position().toPoint();
QCOMPARE(childItemInChildWindow->mapToScene({0, 0}), windowItemPos);
QCOMPARE(childItemInChildWindow->mapToGlobal({0, 0}), windowA->position()
+ childItemInChildWindow->window()->position() + windowItemPos);
QCOMPARE(childItemInChildWindow->mapToItem(nullptr, {0, 0}), windowItemPos);
auto globalItemOffset = [](QQuickItem *a, QQuickItem *b) {
return a->mapToGlobal({0, 0}) - b->mapToGlobal({0, 0});
};
QCOMPARE(childItemInChildWindow->mapToItem(childItem, {0, 0}),
globalItemOffset(childItemInChildWindow, childItem));
QCOMPARE(childItemInChildWindow->mapFromItem(childItem, {0, 0}),
globalItemOffset(childItem, childItemInChildWindow));
QCOMPARE(childItem->mapToItem(childItemInChildWindow, {0, 0}),
globalItemOffset(childItem, childItemInChildWindow));
QCOMPARE(childItem->mapFromItem(childItemInChildWindow, {0, 0}),
globalItemOffset(childItemInChildWindow, childItem));
auto *windowB = root->property("windowB").value<QQuickWindow*>();
QVERIFY(windowA);
auto *childItemInOtherWindow = windowB->findChild<QQuickItem*>("childItem");
QVERIFY(childItemInOtherWindow);
QCOMPARE(childItemInOtherWindow->mapToItem(childItem, {0, 0}),
globalItemOffset(childItemInOtherWindow, childItem));
QCOMPARE(childItemInOtherWindow->mapFromItem(childItem, {0, 0}),
globalItemOffset(childItem, childItemInOtherWindow));
QCOMPARE(childItem->mapToItem(childItemInOtherWindow, {0, 0}),
globalItemOffset(childItem, childItemInOtherWindow));
QCOMPARE(childItem->mapFromItem(childItemInOtherWindow, {0, 0}),
globalItemOffset(childItemInOtherWindow, childItem));
QCOMPARE(childItemInOtherWindow->mapToItem(childItemInChildWindow, {0, 0}),
globalItemOffset(childItemInOtherWindow, childItemInChildWindow));
QCOMPARE(childItemInOtherWindow->mapFromItem(childItemInChildWindow, {0, 0}),
globalItemOffset(childItemInChildWindow, childItemInOtherWindow));
QCOMPARE(childItemInChildWindow->mapToItem(childItemInOtherWindow, {0, 0}),
globalItemOffset(childItemInChildWindow, childItemInOtherWindow));
QCOMPARE(childItemInChildWindow->mapFromItem(childItemInOtherWindow, {0, 0}),
globalItemOffset(childItemInOtherWindow, childItemInChildWindow));
// If one or both of the items are not in a scene (yet), they are assumed
// to eventually be in the same scene.
auto *itemWithoutWindowA = root->property("itemWithoutWindowA").value<QQuickItem*>();
QVERIFY(itemWithoutWindowA);
auto *itemWithoutWindowB = root->property("itemWithoutWindowB").value<QQuickItem*>();
QVERIFY(itemWithoutWindowB);
auto *childItemWithoutWindow = itemWithoutWindowB->findChild<QQuickItem*>("childItemWithoutWindow");
QVERIFY(childItemWithoutWindow);
QPoint itemWithoutWindowAPos = itemWithoutWindowA->position().toPoint();
QPoint itemWithoutWindowBPos = itemWithoutWindowB->position().toPoint();
QCOMPARE(itemWithoutWindowA->mapToItem(childItemWithoutWindow, {0, 0}),
itemWithoutWindowAPos - (itemWithoutWindowBPos + childItemWithoutWindow->position()));
QCOMPARE(itemWithoutWindowA->mapFromItem(childItemWithoutWindow, {0, 0}),
(itemWithoutWindowBPos + childItemWithoutWindow->position()) - itemWithoutWindowAPos);
QCOMPARE(itemWithoutWindowA->mapToItem(childItem, {0, 0}),
itemWithoutWindowAPos - itemPos);
QCOMPARE(itemWithoutWindowA->mapFromItem(childItem, {0, 0}),
itemPos - itemWithoutWindowAPos);
QCOMPARE(childItem->mapToItem(itemWithoutWindowA, {0, 0}),
itemPos - itemWithoutWindowAPos);
QCOMPARE(childItem->mapFromItem(itemWithoutWindowA, {0, 0}),
itemWithoutWindowAPos - itemPos);
}
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::mapCoordinatesWithFraction()
{
QQuickItem parent;
QQuickItem child(&parent);
const QPointF result = child.mapToItem(&parent, 1.5, 1.5);
QCOMPARE(result, QPointF(1.5, 1.5));
}
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::bindableProperties_data()
{
QTest::addColumn<qreal>("initialValue");
QTest::addColumn<qreal>("newValue");
QTest::addColumn<QString>("property");
// can't simply use 3. or 3.0 for the numbers as qreal might
// be float instead of double...
QTest::addRow("x") << qreal(3) << qreal(14) << "x";
QTest::addRow("y") << qreal(10) << qreal(20) << "y";
QTest::addRow("width") << qreal(100) << qreal(200) << "width";
QTest::addRow("height") << qreal(50) << qreal(40) << "height";
}
void tst_QQuickItem::bindableProperties()
{
QQuickItem item;
QFETCH(qreal, initialValue);
QFETCH(qreal, newValue);
QFETCH(QString, property);
QTestPrivate::testReadWritePropertyBasics(item, initialValue, newValue, property.toUtf8().constData());
}
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.size(),1);
QList<QVariant> parentArguments = parentSpy.first();
QCOMPARE(parentArguments.size(), 1);
QCOMPARE(item->parentItem(), qvariant_cast<QQuickItem *>(parentArguments.at(0)));
QCOMPARE(childrenChangedSpy.size(),1);
item->setParentItem(parentItem);
QCOMPARE(childrenChangedSpy.size(),1);
QCOMPARE(item->width(), 100.0);
QCOMPARE(widthSpy.size(),1);
QCOMPARE(item->height(), 200.0);
QCOMPARE(heightSpy.size(),1);
QCOMPARE(item->baselineOffset(), 10.0);
QCOMPARE(baselineOffsetSpy.size(),1);
QList<QVariant> baselineOffsetArguments = baselineOffsetSpy.first();
QCOMPARE(baselineOffsetArguments.size(), 1);
QCOMPARE(item->baselineOffset(), baselineOffsetArguments.at(0).toReal());
QCOMPARE(parentItem->childrenRect(), QRectF(0.0,0.0,100.0,200.0));
QCOMPARE(childrenRectSpy.size(),1);
QList<QVariant> childrenRectArguments = childrenRectSpy.at(0);
QCOMPARE(childrenRectArguments.size(), 1);
QCOMPARE(parentItem->childrenRect(), childrenRectArguments.at(0).toRectF());
QCOMPARE(item->hasActiveFocus(), true);
QCOMPARE(focusSpy.size(),1);
QList<QVariant> focusArguments = focusSpy.first();
QCOMPARE(focusArguments.size(), 1);
QCOMPARE(focusArguments.at(0).toBool(), true);
QCOMPARE(parentItem->hasActiveFocus(), false);
QCOMPARE(parentItem->hasFocus(), false);
QCOMPARE(wantsFocusSpy.size(),0);
item->setX(10.0);
QCOMPARE(item->x(), 10.0);
QCOMPARE(xSpy.size(), 1);
item->setY(10.0);
QCOMPARE(item->y(), 10.0);
QCOMPARE(ySpy.size(), 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() + ":11:9: 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() + ":10:9: QML Item: Binding loop detected for property \"width\"";
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
QString warning2 = testFileUrl("childrenRectBug2.qml").toString() + ":11:9: 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 itemScaleChanged(QQuickItem *item) override
{
record(item, QQuickItemPrivate::Scale);
}
void itemTransformChanged(QQuickItem *item, QQuickItem *transformedItem) override
{
record(item, QQuickItemPrivate::Matrix, QVariant::fromValue(transformedItem));
}
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::Matrix), 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::Matrix), 2);
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::Matrix), 3);
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::Matrix), 4);
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::Matrix), 5);
QCOMPARE(itemListener.count(QQuickItemPrivate::Rotation), 1);
item->setScale(2);
QCOMPARE(itemListener.count(QQuickItemPrivate::Matrix), 6);
QCOMPARE(itemListener.count(QQuickItemPrivate::Scale), 1);
item->setParentItem(window.contentItem());
QCOMPARE(itemListener.count(QQuickItemPrivate::Matrix), 7);
QCOMPARE(itemListener.count(QQuickItemPrivate::Parent), 1);
auto itemTransform = new QQuickTranslate(item);
itemTransform->setX(1);
auto list = item->transform();
list.append(&list, itemTransform);
QCOMPARE(itemListener.count(QQuickItemPrivate::Matrix), 8);
itemTransform->setX(2);
QCOMPARE(itemListener.count(QQuickItemPrivate::Matrix), 9);
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.size(), 0);
// QTBUG-54732: all listeners should get invoked even if they remove themselves while iterating the listeners
std::array<TestListener *, 5> listeners;
auto sweeper = QScopeGuard([&listeners]() { qDeleteAll(listeners); });
for (int i = 0; i < 5; ++i)
listeners[i] = new TestListener(true);
// itemVisibilityChanged x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setVisible(false);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemRotationChanged x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setRotation(90);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemScaleChanged x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Scale);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setScale(0.5);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Scale), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemTransformChanged x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Matrix);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setX(parent->x() + 1);
for (TestListener *listener : listeners) {
QCOMPARE(listener->count(QQuickItemPrivate::Matrix), 1);
QCOMPARE(listener->value(QQuickItemPrivate::Matrix), QVariant::fromValue(parent));
}
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemOpacityChanged x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setOpacity(0.5);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemChildAdded() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
child1 = new QQuickItem(parent);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Children), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemParentChanged() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(child1)->addItemChangeListener(listener, QQuickItemPrivate::Parent);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), listeners.size());
child1->setParentItem(nullptr);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.size(), 0);
// itemImplicitWidthChanged() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setImplicitWidth(parent->implicitWidth() + 1);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemImplicitHeightChanged() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setImplicitHeight(parent->implicitHeight() + 1);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemGeometryChanged() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
parent->setWidth(parent->width() + 1);
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemChildRemoved() x 5
child1->setParentItem(parent);
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
delete child1;
for (TestListener *listener : listeners)
QCOMPARE(listener->count(QQuickItemPrivate::Children), 2);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), 0);
// itemDestroyed() x 5
for (TestListener *listener : listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Destroyed);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.size(), listeners.size());
delete parent;
for (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;
QVERIFY(QQuickTest::showView(window, testFileUrl("hollowTestItem.qml")));
QQuickItem *root = qobject_cast<QQuickItem *>(window.rootObject());
QVERIFY(root);
// Ensure that we don't get extra hover events delivered on the side.
QQuickWindowPrivate::get(&window)->deliveryAgentPrivate()->frameSynchronousHoverInterval = -1;
// Flush out any mouse events that might be queued up
qGuiApp->processEvents();
HollowTestItem *hollowItem = root->findChild<HollowTestItem *>("hollowItem");
QVERIFY(hollowItem);
hollowItem->setCircle(circleTest);
for (const QPoint &point : points) {
qCDebug(lcTests) << "hover and click @" << point;
// check mouse hover
QTest::mouseMove(&window, point);
QTRY_COMPARE(hollowItem->isHovered(), insideTarget);
// check mouse press
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, point);
QTRY_COMPARE(hollowItem->isPressed(), insideTarget);
// check mouse release
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, point);
QTRY_COMPARE(hollowItem->isPressed(), false);
}
}
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("minimal"))
QSKIP("Skipping due to grabToImage not functional on minimal 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);
// The actual grabbed item size can be different, depending on DPR.
const auto dpr = view.screen()->devicePixelRatio();
#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));
// Take DPR into account
const QPoint bottomRight = QPoint(99, 99) * dpr;
QCOMPARE(image.pixel(bottomRight), 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));
// Take DPR into account
const QPoint bottomRight = QPoint(49, 49) * dpr;
QCOMPARE(image.pixel(bottomRight), qRgb(0, 0, 255));
}
#endif
{
// Make sure that we do not deadlock when calling getters in the
// slot connected to the ready() signal.
QSharedPointer<QQuickItemGrabResult> result = item->grabToImage();
QImage capturedImage;
QUrl capturedUrl;
connect(result.data(), &QQuickItemGrabResult::ready, result.data(),
[result, &capturedImage, &capturedUrl]() {
capturedImage = result->image();
capturedUrl = result->url();
});
QTRY_VERIFY(!capturedImage.isNull());
QVERIFY(!capturedUrl.isEmpty());
}
}
void tst_QQuickItem::grabIsItemSizeInDevicePixels()
{
#if !QT_CONFIG(highdpiscaling)
QSKIP("This test requires high-DPI scaling support");
#endif
if (QGuiApplication::platformName() == QLatin1String("minimal")
|| QGuiApplication::platformName() == QLatin1String("offscreen")) {
QSKIP("Skipping due to grabToImage not functional on minimal/offscreen platforms");
}
constexpr qreal dpr = 1.5;
constexpr int pixelWidth = 595;
constexpr int pixelHeight = 511;
QScreen *screen = QGuiApplication::primaryScreen();
QHighDpiScaling::setGlobalFactor(dpr / screen->devicePixelRatio());
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
const auto restoreScaling = qScopeGuard([]{
QHighDpiScaling::setGlobalFactor(1);
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
});
QCOMPARE(screen->devicePixelRatio(), dpr);
QQuickView view;
view.setInitialProperties({{ "dpr", dpr },
{ "pixelWidth", pixelWidth },
{ "pixelHeight", pixelHeight }});
view.setSource(testFileUrl("devicePixelStripes.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCOMPARE(view.effectiveDevicePixelRatio(), dpr);
QQuickItem *target = view.rootObject()->findChild<QQuickItem *>("target");
QVERIFY(target);
QSharedPointer<QQuickItemGrabResult> result = target->grabToImage();
QVERIFY(!result.isNull());
QSignalSpy spy(result.data(), SIGNAL(ready()));
QTRY_VERIFY(spy.size() > 0);
QCOMPARE(result->image().size(), QSize(pixelWidth, pixelHeight));
}
void tst_QQuickItem::grabFromQmlIsItemSizeInDevicePixels()
{
#if !QT_CONFIG(highdpiscaling)
QSKIP("This test requires high-DPI scaling support");
#endif
if (QGuiApplication::platformName() == QLatin1String("minimal")
|| QGuiApplication::platformName() == QLatin1String("offscreen")) {
QSKIP("Skipping due to grabToImage not functional on minimal/offscreen platforms");
}
constexpr qreal dpr = 1.5;
constexpr int pixelWidth = 595;
constexpr int pixelHeight = 511;
QScreen *screen = QGuiApplication::primaryScreen();
QHighDpiScaling::setGlobalFactor(dpr / screen->devicePixelRatio());
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
const auto restoreScaling = qScopeGuard([]{
QHighDpiScaling::setGlobalFactor(1);
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
});
QCOMPARE(screen->devicePixelRatio(), dpr);
QQuickView view;
view.setInitialProperties({{ "dpr", dpr },
{ "pixelWidth", pixelWidth },
{ "pixelHeight", pixelHeight }});
view.setSource(testFileUrl("devicePixelStripes.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCOMPARE(view.effectiveDevicePixelRatio(), dpr);
QVERIFY(QMetaObject::invokeMethod(view.rootObject(), "grabTargetFromQml"));
QTRY_VERIFY(view.rootObject()->property("grabResult").value<QObject *>());
auto *grabbed = qobject_cast<QQuickItemGrabResult *>(
view.rootObject()->property("grabResult").value<QObject *>());
QVERIFY(grabbed);
QCOMPARE(grabbed->image().size(), QSize(pixelWidth, pixelHeight));
}
void tst_QQuickItem::grabMapsItemUnitsToDevicePixels()
{
#if !QT_CONFIG(highdpiscaling)
QSKIP("This test requires high-DPI scaling support");
#endif
if (QGuiApplication::platformName() == QLatin1String("minimal")
|| QGuiApplication::platformName() == QLatin1String("offscreen")) {
QSKIP("Skipping due to grabToImage not functional on minimal/offscreen platforms");
}
constexpr qreal dpr = 1.5;
constexpr int pixelWidth = 595;
constexpr int pixelHeight = 511;
QScreen *screen = QGuiApplication::primaryScreen();
QHighDpiScaling::setGlobalFactor(dpr / screen->devicePixelRatio());
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
const auto restoreScaling = qScopeGuard([]{
QHighDpiScaling::setGlobalFactor(1);
QGuiApplicationPrivate::resetCachedDevicePixelRatio();
});
QCOMPARE(screen->devicePixelRatio(), dpr);
QQuickView view;
view.setInitialProperties({{ "dpr", dpr },
{ "pixelWidth", pixelWidth },
{ "pixelHeight", pixelHeight }});
view.setSource(testFileUrl("devicePixelStripes.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
if (view.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
QSKIP("The software backend rasterises with its own rounding");
QQuickItem *target = view.rootObject()->findChild<QQuickItem *>("target");
QVERIFY(target);
QSharedPointer<QQuickItemGrabResult> result = target->grabToImage();
QVERIFY(!result.isNull());
QSignalSpy spy(result.data(), SIGNAL(ready()));
QTRY_VERIFY(spy.size() > 0);
const QImage image = result->image();
QCOMPARE(image.size(), QSize(pixelWidth, pixelHeight));
const int row = pixelHeight / 2;
int transitions = 0;
for (int x = 1; x < pixelWidth; ++x) {
if (image.pixel(x, row) != image.pixel(x - 1, row))
++transitions;
}
QCOMPARE(transitions, pixelWidth - 1);
}
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));
}
/*
Verify that a nested item's palette responds to changes of the enabled state
and of the window's activation state by switching the current color group.
*/
void tst_QQuickItem::colorGroup()
{
QQuickView view;
view.setSource(testFileUrl("colorgroup.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject());
QQuickItem *background = root->findChild<QQuickItem *>("background");
QVERIFY(background);
QQuickItem *foreground = root->findChild<QQuickItem *>("foreground");
QVERIFY(foreground);
QQuickPalette *palette = foreground->property("palette").value<QQuickPalette*>();
QVERIFY(palette);
view.show();
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(palette->currentColorGroup(), QPalette::Active);
QCOMPARE(foreground->property("color").value<QColor>(), palette->active()->base());
background->setEnabled(false);
QCOMPARE(palette->currentColorGroup(), QPalette::Disabled);
QCOMPARE(foreground->property("color").value<QColor>(), palette->disabled()->base());
QWindow activationThief;
activationThief.show();
activationThief.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&activationThief));
QCOMPARE(palette->currentColorGroup(), QPalette::Disabled);
QCOMPARE(foreground->property("color").value<QColor>(), palette->disabled()->base());
background->setEnabled(true);
QCOMPARE(palette->currentColorGroup(), QPalette::Inactive);
QCOMPARE(foreground->property("color").value<QColor>(), palette->inactive()->base());
activationThief.hide();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
QCOMPARE(palette->currentColorGroup(), QPalette::Active);
QCOMPARE(foreground->property("color").value<QColor>(), palette->active()->base());
activationThief.show();
activationThief.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&activationThief));
QCOMPARE(palette->currentColorGroup(), QPalette::Inactive);
QCOMPARE(foreground->property("color").value<QColor>(), palette->inactive()->base());
}
/*!
Verify that items don't allocate their own QQuickPalette instance
unnecessarily.
*/
void tst_QQuickItem::paletteAllocated()
{
QQuickView view;
view.setSource(testFileUrl("paletteAllocate.qml"));
QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject());
QQuickItem *background = root->findChild<QQuickItem *>("background");
QVERIFY(background);
QQuickItem *foreground = root->findChild<QQuickItem *>("foreground");
QVERIFY(foreground);
bool backgroundHasPalette = false;
bool foregroundHasPalette = false;
QObject::connect(background, &QQuickItem::paletteCreated, this, [&]{ backgroundHasPalette = true; });
QObject::connect(foreground, &QQuickItem::paletteCreated, this, [&]{ foregroundHasPalette = true; });
view.show();
view.requestActivate();
QVERIFY(QTest::qWaitForWindowActive(&view));
QVERIFY(!backgroundHasPalette);
QVERIFY(!foregroundHasPalette);
view.close();
QVERIFY(!backgroundHasPalette);
QVERIFY(!foregroundHasPalette);
auto quickpalette = foreground->property("palette").value<QQuickPalette*>();
QVERIFY(!backgroundHasPalette);
QVERIFY(quickpalette);
QVERIFY(foregroundHasPalette);
}
void tst_QQuickItem::undefinedIsInvalidForWidthAndHeight()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("undefinedInvalid.qml"));
QScopedPointer<QObject> root(component.create());
QVERIFY(root);
auto item = qobject_cast<QQuickItem *>(root.get());
auto priv = QQuickItemPrivate::get(item);
QVERIFY(item);
QCOMPARE(item->height(), 300);
QCOMPARE(item->width(), 200);
QVERIFY(!priv->widthValid());
QVERIFY(!priv->heightValid());
}
void tst_QQuickItem::viewport_data()
{
QTest::addColumn<bool>("contentObservesViewport");
QTest::addColumn<bool>("innerClip");
QTest::addColumn<bool>("innerViewport");
QTest::addColumn<bool>("innerObservesViewport");
QTest::addColumn<bool>("outerClip");
QTest::addColumn<bool>("outerViewport");
QTest::addColumn<bool>("outerObservesViewport");
QTest::addColumn<QPoint>("innerViewportOffset");
QTest::addColumn<QPoint>("outerViewportOffset");
QTest::addColumn<QRect>("expectedViewportTestRect");
QTest::addColumn<QRect>("expectedContentClipRect");
QTest::newRow("default") << false
<< false << false << false
<< false << false << false
<< QPoint() << QPoint() << QRect(0, 0, 290, 290) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp, clipping and observing") << true
<< true << true << true
<< true << true << true
<< QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp, clipping and observing; content not observing") << false
<< true << true << true
<< true << true << true
<< QPoint() << QPoint() << QRect(0, 0, 290, 290) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing") << true
<< false << true << true
<< false << true << true
<< QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, inner pos offset") << true
<< false << true << true
<< false << true << true
<< QPoint(120, 120) << QPoint() << QRect(55, 55, 80, 80) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, inner neg offset") << true
<< false << true << true
<< false << true << true
<< QPoint(-70, -50) << QPoint() << QRect(105, 85, 170, 190) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, outer pos offset") << true
<< false << true << true
<< false << true << true
<< QPoint() << QPoint(220, 220) << QRect(55, 55, 20, 20) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, outer neg offset") << true
<< false << true << true
<< false << true << true
<< QPoint() << QPoint(-70, -50) << QRect(65, 55, 190, 200) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, pos and neg offset") << true
<< false << true << true
<< false << true << true
<< QPoint(150, 150) << QPoint(-170, -150) << QRect(55, 55, 50, 50) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp and observing, neg and pos offset") << true
<< false << true << true
<< false << true << true
<< QPoint(-180, -210) << QPoint(100, 115) << QRect(215, 245, 60, 30) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp not observing") << true
<< false << true << false
<< false << true << false
<< QPoint() << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp not observing, inner pos offset") << true
<< false << true << false
<< false << true << false
<< QPoint(120, 120) << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp not observing, inner neg offset") << true
<< false << true << false
<< false << true << false
<< QPoint(-70, -50) << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp not observing, outer pos offset") << true
<< false << true << false
<< false << true << false
<< QPoint() << QPoint(220, 220) << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner and outer: vp not observing, outer neg offset") << true
<< false << true << false
<< false << true << false
<< QPoint() << QPoint(-70, -50) << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner clipping and observing") << true
<< true << true << true
<< false << false << false
<< QPoint() << QPoint() << QRect(55, 55, 220, 220) << QRect(0, 0, 290, 290);
QTest::newRow("inner clipping and observing only outer") << true
<< true << true << true
<< false << true << false
<< QPoint() << QPoint() << QRect(55, 55, 200, 200) << QRect(0, 0, 290, 290);
}
void tst_QQuickItem::viewport()
{
QFETCH(bool, contentObservesViewport);
QFETCH(bool, innerClip);
QFETCH(bool, innerViewport);
QFETCH(bool, innerObservesViewport);
QFETCH(bool, outerClip);
QFETCH(bool, outerViewport);
QFETCH(bool, outerObservesViewport);
QFETCH(QPoint, innerViewportOffset);
QFETCH(QPoint, outerViewportOffset);
QFETCH(QRect, expectedViewportTestRect);
QFETCH(QRect, expectedContentClipRect);
QQuickView window;
QVERIFY(QQuickTest::showView(window, testFileUrl("viewports.qml")));
QQuickItem *root = qobject_cast<QQuickItem *>(window.rootObject());
QQuickItem *outer = root->findChild<QQuickItem *>("outerViewport");
QVERIFY(outer);
QQuickItem *inner = root->findChild<QQuickItem *>("innerViewport");
QVERIFY(inner);
QQuickItem *contentItem = root->findChild<QQuickItem *>("innerRect");
QVERIFY(contentItem);
ViewportTestItem *viewportTestItem = root->findChild<ViewportTestItem *>();
QVERIFY(viewportTestItem);
inner->setPosition(inner->position() + innerViewportOffset);
outer->setPosition(outer->position() + outerViewportOffset);
outer->setClip(outerClip);
QCOMPARE(outer->flags().testFlag(QQuickItem::ItemIsViewport), outerClip);
outer->setFlag(QQuickItem::ItemIsViewport, outerViewport);
outer->setFlag(QQuickItem::ItemObservesViewport, outerObservesViewport);
inner->setClip(innerClip);
QCOMPARE(inner->flags().testFlag(QQuickItem::ItemIsViewport), innerClip);
inner->setFlag(QQuickItem::ItemIsViewport, innerViewport);
inner->setFlag(QQuickItem::ItemObservesViewport, innerObservesViewport);
viewportTestItem->setFlag(QQuickItem::ItemObservesViewport, contentObservesViewport);
emit viewportTestItem->viewportChanged();
if (lcTests().isDebugEnabled())
QTest::qWait(1000);
if (contentObservesViewport) {
if (innerViewport)
QCOMPARE(viewportTestItem->viewportItem(), inner);
else if (outerViewport)
QCOMPARE(viewportTestItem->viewportItem(), outer);
else
QCOMPARE(viewportTestItem->viewportItem(), root->parentItem()); // QQuickRootItem
} else {
QCOMPARE(viewportTestItem->viewportItem(), root->parentItem()); // QQuickRootItem
}
QCOMPARE(contentItem->clipRect().toRect(), expectedContentClipRect);
QCOMPARE(viewportTestItem->clipRect().toRect(), expectedViewportTestRect);
}
// Test that in a slot connected to destroyed() the emitter is
// is no longer a QQuickItem.
void tst_QQuickItem::qobject_castOnDestruction()
{
QQuickItem item;
QObject::connect(&item, &QObject::destroyed, this, [](QObject *object)
{
QVERIFY(!qobject_cast<QQuickItem *>(object));
QVERIFY(!dynamic_cast<QQuickItem *>(object));
QVERIFY(!object->isQuickItemType());
});
}
/*
Items that are getting destroyed should not emit property change notifications.
*/
void tst_QQuickItem::signalsOnDestruction_data()
{
QTest::addColumn<bool>("childVisible");
QTest::addColumn<bool>("grandChildVisible");
QTest::addRow("Both visible") << true << true;
QTest::addRow("Child visible") << true << false;
QTest::addRow("Grand child visible") << false << true;
QTest::addRow("Both hidden") << false << false;
}
void tst_QQuickItem::signalsOnDestruction()
{
QFETCH(bool, childVisible);
QFETCH(bool, grandChildVisible);
QQuickWindow window;
window.show();
int expectedChildVisibleCount = 0;
int expectedGrandChildVisibleCount = 0;
// Visual children, but not QObject children.
// Note: QQuickItem's visible property defaults to true after creation, as visual
// items are always expected to be added to a visual hierarchy. So for the sake
// of this test we first add, and then remove the item from a parent. This explicitly
// sets the effective visibility to false.
std::unique_ptr<QQuickItem> parent(new QQuickItem(window.contentItem()));
QVERIFY(parent->isVisible());
std::unique_ptr<QQuickItem> child(new QQuickItem);
child->setVisible(childVisible);
child->setParentItem(parent.get());
child->setParentItem(nullptr);
QVERIFY(!child->isVisible());
std::unique_ptr<QQuickItem> grandChild(new QQuickItem);
grandChild->setVisible(grandChildVisible);
grandChild->setParentItem(child.get());
grandChild->setParentItem(nullptr);
QVERIFY(!grandChild->isVisible());
QSignalSpy childrenSpy(parent.get(), &QQuickItem::childrenChanged);
QSignalSpy visibleChildrenSpy(parent.get(), &QQuickItem::visibleChildrenChanged);
QSignalSpy childParentSpy(child.get(), &QQuickItem::parentChanged);
QSignalSpy childVisibleSpy(child.get(), &QQuickItem::visibleChanged);
QSignalSpy childChildrenSpy(child.get(), &QQuickItem::childrenChanged);
QSignalSpy childVisibleChildrenSpy(child.get(), &QQuickItem::visibleChildrenChanged);
QSignalSpy grandChildParentSpy(grandChild.get(), &QQuickItem::parentChanged);
QSignalSpy grandChildVisibleSpy(grandChild.get(), &QQuickItem::visibleChanged);
child->setParentItem(parent.get());
QCOMPARE(childrenSpy.count(), 1);
if (childVisible)
++expectedChildVisibleCount;
QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount);
QCOMPARE(childParentSpy.count(), 1);
QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount);
QCOMPARE(childChildrenSpy.count(), 0);
QCOMPARE(childVisibleChildrenSpy.count(), 0);
grandChild->setParentItem(child.get());
QCOMPARE(childrenSpy.count(), 1);
QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount);
QCOMPARE(childParentSpy.count(), 1);
QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount);
QCOMPARE(childChildrenSpy.count(), 1);
if (grandChildVisible && childVisible)
++expectedGrandChildVisibleCount;
QCOMPARE(childVisibleChildrenSpy.count(), expectedGrandChildVisibleCount);
QCOMPARE(grandChildParentSpy.count(), 1);
QCOMPARE(grandChildVisibleSpy.count(), expectedGrandChildVisibleCount);
parent.reset();
QVERIFY(!child->parentItem());
QVERIFY(grandChild->parentItem());
QCOMPARE(childrenSpy.count(), 1);
QCOMPARE(visibleChildrenSpy.count(), expectedChildVisibleCount);
QCOMPARE(childParentSpy.count(), 2);
QCOMPARE(childChildrenSpy.count(), 1);
if (childVisible)
++expectedChildVisibleCount;
QCOMPARE(childVisibleSpy.count(), expectedChildVisibleCount);
if (childVisible && grandChildVisible)
++expectedGrandChildVisibleCount;
QCOMPARE(childVisibleChildrenSpy.count(), expectedGrandChildVisibleCount);
QCOMPARE(grandChildParentSpy.count(), 1);
QCOMPARE(grandChildVisibleSpy.count(), expectedGrandChildVisibleCount);
}
void tst_QQuickItem::visibleChanged()
{
QQuickView window;
window.setSource(testFileUrl("visiblechanged.qml"));
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QQuickItem *root = qobject_cast<QQuickItem*>(window.rootObject());
QVERIFY(root);
QPointer<QQuickItem> parentItem = root->findChild<QQuickItem *>("parentItem");
QPointer<QQuickItem> childItem = root->findChild<QQuickItem *>("childItem");
QPointer<QQuickItem> loader = root->findChild<QQuickItem *>("loader");
QPointer<QQuickItem> loaderChild = root->findChild<QQuickItem *>("loaderChild");
QVERIFY(parentItem);
QVERIFY(childItem);
QVERIFY(loader);
QVERIFY(loaderChild);
QSignalSpy parentItemSpy(parentItem.data(), &QQuickItem::visibleChanged);
QSignalSpy childItemSpy(childItem.data(), &QQuickItem::visibleChanged);
QSignalSpy loaderChildSpy(loaderChild.data(), &QQuickItem::visibleChanged);
loader->setProperty("active", false);
QCOMPARE(parentItemSpy.count(), 0);
QCOMPARE(childItemSpy.count(), 0);
QVERIFY(!loaderChild->parentItem());
QCOMPARE(loaderChildSpy.count(), 1);
QCOMPARE(loaderChild->isVisible(), false);
delete parentItem.data();
QVERIFY(!parentItem);
QVERIFY(childItem);
QVERIFY(!childItem->parentItem());
QCOMPARE(parentItemSpy.count(), 0);
QCOMPARE(childItemSpy.count(), 1);
}
void tst_QQuickItem::lastFocusChangeReason()
{
std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("focusReason.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowActive(window.get()));
QQuickItem *item = window->findChild<QQuickItem *>("item");
QQuickItem *customText = window->findChild<QQuickItem *>("customText");
QQuickItem *customItem = window->findChild<QQuickItem *>("customItem");
QQuickItem *hyperlink = window->findChild<QQuickItem *>("hyperlink");
QQuickItem *textInputChild = window->findChild<QQuickItem *>("textInputChild");
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
QQuickItemPrivate *customTextPrivate = QQuickItemPrivate::get(customText);
QQuickItemPrivate *customItemPrivate = QQuickItemPrivate::get(customItem);
QQuickItemPrivate *hyperlinkPrivate = QQuickItemPrivate::get(hyperlink);
QQuickItemPrivate *textInputChildPrivate = QQuickItemPrivate::get(textInputChild);
QVERIFY(item);
QVERIFY(customText);
QVERIFY(customItem);
QVERIFY(hyperlink);
QVERIFY(textInputChild);
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
auto resetTabFocusBehavior = qScopeGuard([]{
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1));
});
// helper for clicking into an item
const auto itemCenter = [](const QQuickItem *item) -> QPoint {
return item->mapToScene(item->clipRect().center()).toPoint();
};
// setting focusPolicy to Strong/WheelFocus doesn't implicitly turn on event delivery
customText->setAcceptedMouseButtons(Qt::LeftButton);
customItem->setAcceptedMouseButtons(Qt::LeftButton);
customItem->setAcceptTouchEvents(true);
customText->setAcceptTouchEvents(true);
hyperlink->setAcceptTouchEvents(true);
// window activation -> ActiveWindowFocusReason
QVERIFY(item->hasFocus());
QVERIFY_ACTIVE_FOCUS(item);
if (itemPrivate->lastFocusChangeReason() != Qt::ActiveWindowFocusReason
&& QStringList{"windows", "offscreen"}.contains(QGuiApplication::platformName())) {
QEXPECT_FAIL("", "On Windows and offscreen platforms, window activation does not set focus reason", Continue);
}
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ActiveWindowFocusReason);
// test setter/getter
item->setFocus(false, Qt::MouseFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
item->setFocus(true, Qt::TabFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
item->setFocus(false, Qt::BacktabFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
item->forceActiveFocus(Qt::ShortcutFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::ShortcutFocusReason);
item->setFocus(false, Qt::NoFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::NoFocusReason);
QVERIFY(!item->hasFocus());
// programmatic focus changes
item->setFocus(true, Qt::OtherFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason);
QVERIFY(item->hasFocus());
QVERIFY_ACTIVE_FOCUS(item);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::OtherFocusReason);
// tab focus -> TabFocusReason
QTest::keyClick(window.get(), Qt::Key_Tab);
QVERIFY(customText->hasFocus());
QVERIFY_ACTIVE_FOCUS(customText);
QCOMPARE(qApp->focusObject(), customText);
QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QTest::keyClick(window.get(), Qt::Key_Tab);
QVERIFY(customItem->hasFocus());
QVERIFY_ACTIVE_FOCUS(customItem);
QCOMPARE(qApp->focusObject(), customItem);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QTest::keyClick(window.get(), Qt::Key_Tab);
QVERIFY(hyperlink->hasFocus());
QVERIFY_ACTIVE_FOCUS(hyperlink);
QCOMPARE(qApp->focusObject(), hyperlink);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QTest::keyClick(window.get(), Qt::Key_Tab);
QVERIFY(item->hasFocus());
QVERIFY_ACTIVE_FOCUS(item);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::TabFocusReason);
// backtab -> BacktabFocusReason
QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(hyperlink->hasFocus());
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
QCOMPARE(itemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(customItem->hasFocus());
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
QTest::keyClick(window.get(), Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(customText->hasFocus());
QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::BacktabFocusReason);
// click focus -> MouseFocusReason
QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customItem));
QVERIFY(customItem->hasFocus());
QVERIFY_ACTIVE_FOCUS(customItem);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QCOMPARE(customTextPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(hyperlink));
QVERIFY(hyperlink->hasFocus());
QVERIFY_ACTIVE_FOCUS(hyperlink);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QTest::mouseClick(window.get(), Qt::LeftButton, {}, itemCenter(customText));
QCOMPARE(textInputChild, textInputChild);
QVERIFY(textInputChild->hasFocus());
QVERIFY_ACTIVE_FOCUS(textInputChild);
QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
// touch focus -> MouseFocusReason
std::unique_ptr<QPointingDevice> touchDevice(QTest::createTouchDevice());
QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem));
QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(customItem));
QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(customItem));
QVERIFY(customItem->hasFocus());
QVERIFY_ACTIVE_FOCUS(customItem);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QCOMPARE(textInputChildPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QTest::touchEvent(window.get(), touchDevice.get()).press(0, itemCenter(hyperlink));
QTest::touchEvent(window.get(), touchDevice.get()).release(0, itemCenter(hyperlink));
QVERIFY(hyperlink->hasFocus());
QVERIFY_ACTIVE_FOCUS(hyperlink);
QCOMPARE(hyperlinkPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
// Wheel focus -> MouseFocusReason
QWheelEvent wheelEvent(QPointF(customItem->width() / 2, customItem->height() / 2), QPointF(),
QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier,
Qt::NoScrollPhase, false);
QGuiApplication::sendEvent(customItem, &wheelEvent);
QVERIFY_ACTIVE_FOCUS(customItem);
QCOMPARE(customItemPrivate->lastFocusChangeReason(), Qt::MouseFocusReason);
}
void tst_QQuickItem::focusInScopeChanges()
{
std::unique_ptr<QQuickView> window = std::make_unique<QQuickView>();
window->setSource(testFileUrl("focusInScopeChanges.qml"));
window->show();
window->requestActivate();
QVERIFY(QTest::qWaitForWindowFocused(window.get()));
QQuickItem *main = window->rootObject();
QVERIFY(main);
QQuickItem *focusScope = main->findChild<QQuickItem *>("focusScope");
QQuickItem *rect = main->findChild<QQuickItem *>("rect");
QQuickItem *textInput = main->findChild<QQuickItem *>("textInput");
QVERIFY(focusScope);
QVERIFY(rect);
QVERIFY(textInput);
QVERIFY(window->contentItem());
QSignalSpy fsActiveFocusSpy(focusScope, SIGNAL(activeFocusChanged(bool)));
QSignalSpy rectActiveFocusSpy(rect, SIGNAL(activeFocusChanged(bool)));
QSignalSpy textInputActiveFocusSpy(textInput, SIGNAL(activeFocusChanged(bool)));
// The window's content item will have activeFocus if window is focused
QTRY_VERIFY_ACTIVE_FOCUS(window->contentItem());
QVERIFY(!focusScope->hasActiveFocus());
QVERIFY(!rect->hasActiveFocus());
QVERIFY(!textInput->hasActiveFocus());
QCOMPARE(fsActiveFocusSpy.size(), 0);
QCOMPARE(rectActiveFocusSpy.size(), 0);
QCOMPARE(textInputActiveFocusSpy.size(), 0);
// setting focus to rect shouldn't affect activeFocus as long as its
// parent focus scope doesn't have the activeFocus
rect->setFocus(true);
QCOMPARE(fsActiveFocusSpy.size(), 0);
QCOMPARE(rectActiveFocusSpy.size(), 0);
QCOMPARE(textInputActiveFocusSpy.size(), 0);
// focusScope is the only child with focus in the parent
// scope, so it will gain activeFocus
focusScope->setFocus(true);
QCOMPARE(fsActiveFocusSpy.size(), 1);
QVERIFY(fsActiveFocusSpy.first().at(0).toBool());
// rect loses activeFocus because textInput gains it (as a result of code in signal handler)
QCOMPARE(rectActiveFocusSpy.size(), 2);
QVERIFY(!rect->hasActiveFocus());
QCOMPARE(textInputActiveFocusSpy.size(), 1);
QVERIFY_ACTIVE_FOCUS(textInput);
}
#ifdef QT_WIDGETS_LIB
void tst_QQuickItem::embeddedInWidgetsFocus_data()
{
QTest::addColumn<QUrl>("source");
QTest::newRow("Embedded") << testFileUrl("embedded.qml");
QTest::newRow("Embedded Focus Scope") << testFileUrl("embedded_FocusScope.qml");
}
void tst_QQuickItem::embeddedInWidgetsFocus()
{
if (!qt_tab_all_widgets())
QSKIP("Test requires Qt::TabFocusAllControls tab focus behavior");
QFETCH(QUrl, source);
QWidget root;
QVBoxLayout *layout = new QVBoxLayout(&root);
QLineEdit *lineEdit1 = new QLineEdit(&root);
lineEdit1->setFocusPolicy(Qt::FocusPolicy::TabFocus);
QQuickView *quickView = new QQuickView;
quickView->setSource(source);
QWidget *container = QWidget::createWindowContainer(quickView, &root);
container->setMinimumSize(quickView->size());
container->setFocusPolicy(Qt::TabFocus);
QLineEdit *lineEdit2 = new QLineEdit(&root);
lineEdit2->setFocusPolicy(Qt::FocusPolicy::TabFocus);
layout->addWidget(lineEdit1);
layout->addWidget(container);
layout->addWidget(lineEdit2);
QQuickItem *rect1 = findItem<QQuickItem>(quickView->rootObject(), "rect1");
QQuickItem *rect2 = findItem<QQuickItem>(quickView->rootObject(), "rect2");
QVERIFY(rect1);
QVERIFY(rect2);
root.show();
QTRY_VERIFY(root.isVisible());
QVERIFY(QTest::qWaitForWindowExposed(&root));
QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
lineEdit1->setFocus();
QTRY_VERIFY(lineEdit1->hasFocus());
// Tab forward
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
QTRY_VERIFY(container->hasFocus());
QVERIFY(QTest::qWaitForWindowFocused(quickView));
QVERIFY_ACTIVE_FOCUS(rect1);
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
QTRY_VERIFY_ACTIVE_FOCUS(rect2);
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab);
QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
QVERIFY(lineEdit2->hasFocus());
QVERIFY(!rect2->hasActiveFocus());
// Tab backwards
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
QTRY_VERIFY(container->hasFocus());
QVERIFY(QTest::qWaitForWindowFocused(quickView));
QVERIFY_ACTIVE_FOCUS(rect2);
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY_ACTIVE_FOCUS(rect1);
QTest::keyClick(QGuiApplication::focusWindow(), Qt::Key_Tab, Qt::ShiftModifier);
QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle()));
QVERIFY(lineEdit1->hasFocus());
}
#endif
void tst_QQuickItem::gcIntegration()
{
QQmlEngine e;
QQmlComponent comp(&e);
QQuickItem *parentItem, *child;
comp.loadFromModule("QtQuick", "Item");
parentItem = qobject_cast<QQuickItem *>(comp.create());
child = qobject_cast<QQuickItem *>(comp.create());
QVERIFY(parentItem);
QVERIFY(child);
QV4::ExecutionEngine *v4 = e.handle();
QV4::QObjectWrapper::ensureWrapper(v4, child);
QV4::QObjectWrapper::ensureWrapper(v4, parentItem);
// line blow is a hack; loadFromModule ought to set that up...
QQmlData::get(child)->context = QQmlContextData::get(e.rootContext()).data();
QPointer<QObject> observer(child);
QJSEngine::setObjectOwnership(parentItem, QJSEngine::JavaScriptOwnership);
QJSEngine::setObjectOwnership(child, QJSEngine::JavaScriptOwnership);
QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(child));
QVERIFY(!QQmlData::keepAliveDuringGarbageCollection(parentItem));
QV4::MemoryManager *mm = v4->memoryManager;
QV4::GCStateMachine *sm = mm->gcStateMachine.get();
sm->reset();
while (sm->state != QV4::GCState::MarkGlobalObject) {
QV4::GCStateInfo& stateInfo = sm->stateInfoMap[int(sm->state)];
sm->state = stateInfo.execute(sm, sm->stateData);
}
// simulate someone owning the parentItem
QV4::QObjectWrapper::markWrapper(parentItem, mm->markStack());
mm->markStack()->drain();
child->setParentItem(parentItem);
child->setParent(nullptr);
gc(*v4);
QVERIFY(observer);
child->setParentItem(nullptr);
QVERIFY(!child->parent());
gc(*v4);
QVERIFY(!observer);
}
void tst_QQuickItem::focusWorksAfterEnablingRootItem() // QTBUG-135624
{
QQmlEngine e;
QQmlComponent comp(&e);
comp.loadUrl(testFileUrl("DisabledRootItemWithTextInput.qml"));
QQuickWindow *window = qobject_cast<QQuickWindow *>(comp.create());
window->requestActivate();
QVERIFY(QTest::qWaitForWindowFocused(window));
QList<QQuickItem *> childItems = window->contentItem()->childItems();
QVERIFY(!childItems.isEmpty());
QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(childItems.first());
QVERIFY(textInput);
// Should be set to false in the QML file
QCOMPARE(window->contentItem()->isEnabled(), false);
QCOMPARE(textInput->hasFocus(), false);
// The click won't do anything when the item is effectively disabled
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, textInput->mapToScene(textInput->boundingRect().center()).toPoint());
QCOMPARE(textInput->hasFocus(), false);
QCOMPARE(window->activeFocusItem(), nullptr);
// Enable the root item and verify that focus works normally
window->contentItem()->setEnabled(true);
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, textInput->mapToScene(textInput->boundingRect().center()).toPoint());
QTRY_COMPARE(textInput->hasFocus(), true);
QCOMPARE(window->activeFocusItem(), textInput);
textInput->clear();
QTest::keyClick(window, Qt::Key_A);
QTRY_COMPARE(textInput->text(), "a");
}
QTEST_MAIN(tst_QQuickItem)
#include "tst_qquickitem.moc"