Mac: respect the system settings in Full Keyboard Access
authorLiang Qi <liang.qi@digia.com>
Fri, 10 May 2013 09:54:37 +0000 (11:54 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 14 May 2013 15:27:14 +0000 (17:27 +0200)
Iterate all or not in nextPrevItemInTabFocusChain function.

Change-Id: I95289b042f3d9924c28ffb9c8c7124c767addf2e
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
src/quick/items/qquickitem.cpp
src/quick/items/qquickitem_p.h
tests/auto/quick/qquickitem2/data/activeFocusOnTab6.qml [new file with mode: 0644]
tests/auto/quick/qquickitem2/tst_qquickitem.cpp

index 5ad53e9..1667736 100644 (file)
@@ -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>
@@ -2034,6 +2035,41 @@ QQuickItem::~QQuickItem()
 
 /*!
     \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
     \param item The item that currently has the focus
     \param forward The direction
@@ -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;
 }
index 12a5165..c71da3c 100644 (file)
@@ -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 (file)
index 0000000..22b4d24
--- /dev/null
@@ -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"
+        }
+    }
+}
index 6b73f52..9a6bed6 100644 (file)
@@ -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);