diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index d6a663ab77681b6ef5c77d39fd0424376a362356..58e1612e1993c96f58f0b31ec61badfe2dc95426 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2050,7 +2050,10 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) { bool result = true; - if (item->window() && item == item->window()->contentItem()) + if (!item->window()) + return false; + + if (item == item->window()->contentItem()) return true; #ifndef QT_NO_ACCESSIBILITY @@ -2093,7 +2096,6 @@ bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward) QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward) { Q_ASSERT(item); - Q_ASSERT(item->activeFocusOnTab()); bool all = QQuickItemPrivate::qt_tab_all_widgets(); @@ -2107,6 +2109,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo from = item->parentItem(); } bool skip = false; + const QQuickItem * const contentItem = item->window()->contentItem(); + const QQuickItem * const originalItem = item; + QQuickItem * startItem = item; + QQuickItem * firstFromItem = from; QQuickItem *current = item; do { skip = false; @@ -2157,8 +2163,25 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo skip = true; } } - from = last; + if (current == startItem && from == firstFromItem) { + // wrapped around, avoid endless loops + if (originalItem == contentItem) { +#ifdef FOCUS_DEBUG + qDebug() << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; +#endif + return item->window()->contentItem(); + } else { +#ifdef FOCUS_DEBUG + qDebug() << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return " << startItem; +#endif + return startItem; + } + } + if (!firstFromItem) { //start from root + startItem = current; + firstFromItem = from; + } } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible() || !(all || QQuickItemPrivate::canAcceptTabFocus(current))); @@ -4375,7 +4398,8 @@ void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e) return; //only care about KeyPress now - if (q->activeFocusOnTab() && e->type() == QEvent::KeyPress) { + if ((q == q->window()->contentItem() || q->activeFocusOnTab()) + && e->type() == QEvent::KeyPress) { bool res = false; if (!(e->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? if (e->key() == Qt::Key_Backtab diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab7.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab7.qml new file mode 100644 index 0000000000000000000000000000000000000000..e81d9be9509f4e3005ac6da1c2d56795d660aad4 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab7.qml @@ -0,0 +1,36 @@ +import QtQuick 2.1 + +Item { + id: main + objectName: "main" + width: 300 + height: 300 + Item { + id: button1 + objectName: "button1" + width: 300 + height: 150 + activeFocusOnTab: true + Accessible.role: Accessible.Button + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + anchors.top: parent.top + anchors.left: parent.left + } + Item { + id: button2 + objectName: "button2" + width: 300 + height: 150 + activeFocusOnTab: true + Accessible.role: Accessible.Button + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + anchors.bottom: parent.bottom + anchors.left: parent.left + } +} diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab8.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab8.qml new file mode 100644 index 0000000000000000000000000000000000000000..641a39c1fa951b53ef3a31dc94b6562070d8724d --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab8.qml @@ -0,0 +1,36 @@ +import QtQuick 2.1 + +Item { + id: main + objectName: "main" + width: 300 + height: 300 + Item { + id: button1 + objectName: "button1" + width: 300 + height: 150 + activeFocusOnTab: true + Accessible.role: Accessible.Table + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + anchors.top: parent.top + anchors.left: parent.left + } + Item { + id: button2 + objectName: "button2" + width: 300 + height: 150 + activeFocusOnTab: true + Accessible.role: Accessible.Table + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + anchors.bottom: parent.bottom + anchors.left: parent.left + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 9a6bed6dbee5c51285d09bb3c0777424d0f1de7b..992e81aa64311438b078b97a33ad011484db7a83 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -71,6 +71,8 @@ private slots: void activeFocusOnTab4(); void activeFocusOnTab5(); void activeFocusOnTab6(); + void activeFocusOnTab7(); + void activeFocusOnTab8(); void nextItemInFocusChain(); void nextItemInFocusChain2(); @@ -747,6 +749,91 @@ void tst_QQuickItem::activeFocusOnTab6() delete window; } +void tst_QQuickItem::activeFocusOnTab7() +{ + if (qt_tab_all_widgets()) + QSKIP("This function doesn't support iterating all."); + + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(300,300)); + + window->setSource(testFileUrl("activeFocusOnTab7.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button1"); + QVERIFY(item); + item->forceActiveFocus(); + QVERIFY(item->hasActiveFocus()); + + // Tab: button1->button1 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(!key.isAccepted()); + + QVERIFY(item->hasActiveFocus()); + + // BackTab: button1->button1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(!key.isAccepted()); + + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +void tst_QQuickItem::activeFocusOnTab8() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(300,300)); + + window->setSource(testFileUrl("activeFocusOnTab8.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + QQuickItem *content = window->contentItem(); + QVERIFY(content); + QVERIFY(content->hasActiveFocus()); + + QQuickItem *button1 = findItem<QQuickItem>(window->rootObject(), "button1"); + QVERIFY(button1); + QVERIFY(!button1->hasActiveFocus()); + + QQuickItem *button2 = findItem<QQuickItem>(window->rootObject(), "button2"); + QVERIFY(button2); + QVERIFY(!button2->hasActiveFocus()); + + // Tab: contentItem->button1 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + QVERIFY(button1->hasActiveFocus()); + + // Tab: button1->button2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + QVERIFY(button2->hasActiveFocus()); + QVERIFY(!button1->hasActiveFocus()); + + // BackTab: button2->button1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + QVERIFY(button1->hasActiveFocus()); + QVERIFY(!button2->hasActiveFocus()); + + delete window; +} + void tst_QQuickItem::nextItemInFocusChain() { if (!qt_tab_all_widgets())