diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 5ad53e99b6c6aea85ad2d203527ad64d23a8b0d9..1667736d4652256f2f432aa7d159700b846409d0 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -58,6 +58,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qcoreevent.h> #include <QtCore/qnumeric.h> +#include <QtGui/qpa/qplatformtheme.h> #include <private/qqmlglobal_p.h> #include <private/qqmlengine_p.h> @@ -2032,6 +2033,41 @@ QQuickItem::~QQuickItem() delete d->_stateGroup; d->_stateGroup = 0; } +/*! + \internal +*/ +bool QQuickItemPrivate::qt_tab_all_widgets() +{ + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + return theme->themeHint(QPlatformTheme::TabAllWidgets).toBool(); + return true; +} + +/*! + \internal +*/ +bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item) +{ + bool result = true; + + if (item->window() && item == item->window()->contentItem()) + return true; + +#ifndef QT_NO_ACCESSIBILITY + result = false; + if (QObject *acc = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(item, false)) { + int role = acc->property("role").toInt(); + if (role == QAccessible::EditableText + || role == QAccessible::Table + || role == QAccessible::List + || role == QAccessible::SpinBox) + result = true; + } +#endif + + return result; +} + /*! \internal \brief QQuickItemPrivate::focusNextPrev focuses the next/prev item in the tab-focus-chain @@ -2059,6 +2095,8 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo Q_ASSERT(item); Q_ASSERT(item->activeFocusOnTab()); + bool all = QQuickItemPrivate::qt_tab_all_widgets(); + QQuickItem *from = 0; if (forward) { from = item->parentItem(); @@ -2121,7 +2159,8 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo } from = last; - } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible()); + } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible() + || !(all || QQuickItemPrivate::canAcceptTabFocus(current))); return current; } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 12a5165d9bdbf4d2ecc273415e89ade4f0c2a0d7..c71da3c3301fbe9a4a89a5009fa645e3125927f1 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -491,6 +491,9 @@ public: static bool focusNextPrev(QQuickItem *item, bool forward); static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward); + static bool qt_tab_all_widgets(); //todo: move to QGuiApplication? + static bool canAcceptTabFocus(QQuickItem *item); + qreal x; qreal y; qreal width; diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab6.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab6.qml new file mode 100644 index 0000000000000000000000000000000000000000..22b4d249f02df1b594bc0d96d03b7722627a2fb4 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab6.qml @@ -0,0 +1,144 @@ +import QtQuick 2.1 + +Item { + id: main + objectName: "main" + width: 800 + height: 600 + focus: true + Component.onCompleted: button12.focus = true + Item { + id: sub1 + objectName: "sub1" + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: parent.left + Item { + id: button11 + objectName: "button11" + width: 100 + height: 50 + activeFocusOnTab: true + Accessible.role: Accessible.Table + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: parent.top + anchors.topMargin: 100 + } + Item { + id: button12 + objectName: "button12" + activeFocusOnTab: true + Accessible.role: Accessible.List + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button11.bottom + anchors.bottomMargin: 100 + } + Item { + id: button13 + objectName: "button13" + enabled: false + activeFocusOnTab: true + Accessible.role: Accessible.Table + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button12.bottom + anchors.bottomMargin: 100 + } + Item { + id: button14 + objectName: "button14" + visible: false + activeFocusOnTab: true + Accessible.role: Accessible.List + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button12.bottom + anchors.bottomMargin: 100 + } + } + Item { + id: sub2 + objectName: "sub2" + activeFocusOnTab: true + Accessible.role: Accessible.Row + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: sub1.right + Item { + id: button21 + objectName: "button21" + width: 100 + height: 50 + activeFocusOnTab: true + Accessible.role: Accessible.PushButton + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: parent.top + anchors.topMargin: 100 + } + Item { + id: button22 + objectName: "button22" + width: 100 + height: 50 + activeFocusOnTab: true + Accessible.role: Accessible.RadioButton + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: button21.bottom + anchors.bottomMargin: 100 + } + } + Item { + id: sub3 + objectName: "sub3" + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: sub2.right + TextEdit { + id: edit + objectName: "edit" + width: 230 + height: 400 + readOnly: false + activeFocusOnTab: true + Accessible.role: Accessible.EditableText + wrapMode: TextEdit.Wrap + textFormat: TextEdit.RichText + + text: "aaa\n" + +"bbb\n" + +"ccc\n" + +"ddd\n" + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 6b73f52593b4f6a03a9e8bbf3da6c4e84f68478b..9a6bed6dbee5c51285d09bb3c0777424d0f1de7b 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -47,6 +47,7 @@ #include <QtGui/private/qinputmethod_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquicktextinput_p.h> +#include <QtGui/qpa/qplatformtheme.h> #include <private/qquickitem_p.h> #include "../../shared/util.h" #include "../shared/visualtestutil.h" @@ -69,8 +70,10 @@ private slots: void activeFocusOnTab3(); void activeFocusOnTab4(); void activeFocusOnTab5(); + void activeFocusOnTab6(); void nextItemInFocusChain(); + void nextItemInFocusChain2(); void keys(); void keysProcessingOrder(); @@ -110,6 +113,11 @@ private slots: private: QQmlEngine engine; + bool qt_tab_all_widgets() { + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + return theme->themeHint(QPlatformTheme::TabAllWidgets).toBool(); + return true; + } }; class KeysTestObject : public QObject @@ -283,6 +291,9 @@ void tst_QQuickItem::cleanup() void tst_QQuickItem::activeFocusOnTab() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -392,6 +403,9 @@ void tst_QQuickItem::activeFocusOnTab() void tst_QQuickItem::activeFocusOnTab2() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -429,6 +443,9 @@ void tst_QQuickItem::activeFocusOnTab2() void tst_QQuickItem::activeFocusOnTab3() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -608,6 +625,9 @@ void tst_QQuickItem::activeFocusOnTab3() void tst_QQuickItem::activeFocusOnTab4() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -637,6 +657,9 @@ void tst_QQuickItem::activeFocusOnTab4() void tst_QQuickItem::activeFocusOnTab5() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -666,8 +689,69 @@ void tst_QQuickItem::activeFocusOnTab5() delete window; } +void tst_QQuickItem::activeFocusOnTab6() +{ + if (qt_tab_all_widgets()) + QSKIP("This function doesn't support iterating all."); + + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab6.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button12 + QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button12->edit + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: edit->button12 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button12->button11 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "button11"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button11->edit + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + void tst_QQuickItem::nextItemInFocusChain() { + if (!qt_tab_all_widgets()) + QSKIP("This function doesn't support NOT iterating all."); + QQuickView *window = new QQuickView(0); window->setBaseSize(QSize(800,600)); @@ -739,6 +823,54 @@ void tst_QQuickItem::nextItemInFocusChain() delete window; } +void tst_QQuickItem::nextItemInFocusChain2() +{ + if (qt_tab_all_widgets()) + QSKIP("This function doesn't support iterating all."); + + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab6.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(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::keys() { QQuickView *window = new QQuickView(0);