From 2f9d0346f00e37725f0f1bce1318501a25c5e438 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Fri, 22 Feb 2013 13:44:06 +0100 Subject: Support activeFocusOnTab in QQuickItem Add activeFocusOnTab as property to QQuickItem. Setting the property allows automatic keyboard navigation between all elements that have it set. This key event handler will only be called after the QML key handlers, such as KeyNavigation and Keys, and the C++ key event handlers, such as keyPressEvent function. Algorithm is most done by Frederik Gladhorn, in cooperation with Gabriel de Dietrich. Done-with: Frederik Gladhorn Done-with: Gabriel de Dietrich Change-Id: I8b58be9c20d113661fe85d27bdb1af84340d9de5 Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Frederik Gladhorn --- .../quick/qquickitem2/data/activeFocusOnTab.qml | 136 +++++++++ .../quick/qquickitem2/data/activeFocusOnTab3.qml | 250 ++++++++++++++++ tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 329 +++++++++++++++++++++ 3 files changed, 715 insertions(+) create mode 100644 tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml create mode 100644 tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml (limited to 'tests/auto/quick') diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml new file mode 100644 index 0000000000..e064b41efe --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml @@ -0,0 +1,136 @@ +import QtQuick 2.0 + +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 + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: parent.top + anchors.topMargin: 100 + } + Item { + id: button12 + objectName: "button12" + activeFocusOnTab: true + 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 + 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 + 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 + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: sub1.right + Item { + id: button21 + objectName: "button21" + width: 100 + height: 50 + activeFocusOnTab: true + 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 + 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 + wrapMode: TextEdit.Wrap + textFormat: TextEdit.RichText + + text: "aaa\n" + +"bbb\n" + +"ccc\n" + +"ddd\n" + } + } +} diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml new file mode 100644 index 0000000000..00fb82d59e --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml @@ -0,0 +1,250 @@ +import QtQuick 2.0 + +Item { + id: main + objectName: "main" + width: 400 + height: 700 + focus: true + Component.onCompleted: button1.focus = true + Item { + id: sub1 + objectName: "sub1" + activeFocusOnTab: false + width: 100 + height: 50 + anchors.top: parent.top + Item { + id: button1 + objectName: "button1" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Row { + id: row_repeater + objectName: "row_repeater" + activeFocusOnTab: false + anchors.top: sub1.bottom + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + Item { + id: sub2 + objectName: "sub2" + activeFocusOnTab: false + anchors.top: row_repeater.bottom + width: 100 + height: 50 + Item { + id: button2 + objectName: "button2" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Row { + id: row + objectName: "row" + activeFocusOnTab: false + anchors.top: sub2.bottom + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "yellow"; width: 50; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "green"; width: 20; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "blue"; width: 50; height: 20 } + } + Item { + id: sub3 + objectName: "sub3" + activeFocusOnTab: false + anchors.top: row.bottom + width: 100 + height: 50 + Item { + id: button3 + objectName: "button3" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Flow { + id: flow + objectName: "flow" + activeFocusOnTab: false + anchors.top: sub3.bottom + width: parent.width + anchors.margins: 4 + spacing: 10 + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "yellow"; width: 50; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "green"; width: 20; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "blue"; width: 50; height: 20 } + } + Item { + id: sub4 + objectName: "sub4" + activeFocusOnTab: false + anchors.top: flow.bottom + width: 100 + height: 50 + Item { + id: button4 + objectName: "button4" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope + objectName: "focusscope" + activeFocusOnTab: false + anchors.top: sub4.bottom + height: 40 + Row { + id: row_focusscope + objectName: "row_focusscope" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub5 + objectName: "sub5" + activeFocusOnTab: false + anchors.top: focusscope.bottom + width: 100 + height: 50 + Item { + id: button5 + objectName: "button5" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope2 + objectName: "focusscope2" + activeFocusOnTab: true + anchors.top: sub5.bottom + height: 40 + Row { + id: row_focusscope2 + objectName: "row_focusscope2" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + focus: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub6 + objectName: "sub6" + activeFocusOnTab: false + anchors.top: focusscope2.bottom + width: 100 + height: 50 + Item { + id: button6 + objectName: "button6" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope3 + objectName: "focusscope3" + activeFocusOnTab: true + anchors.top: sub6.bottom + height: 40 + Row { + id: row_focusscope3 + objectName: "row_focusscope3" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub7 + objectName: "sub7" + activeFocusOnTab: false + anchors.top: focusscope3.bottom + width: 100 + height: 50 + Item { + id: button7 + objectName: "button7" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index c82372c287..668b5e2945 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -64,6 +64,10 @@ private slots: void initTestCase(); void cleanup(); + void activeFocusOnTab(); + void activeFocusOnTab2(); + void activeFocusOnTab3(); + void keys(); void keysProcessingOrder(); void keysim(); @@ -273,6 +277,331 @@ void tst_QQuickItem::cleanup() inputMethodPrivate->testContext = 0; } +void tst_QQuickItem::activeFocusOnTab() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button12 + QQuickItem *item = findItem(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button12->sub2 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "sub2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: sub2->button21 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button21"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button21->button22 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button22"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button22->edit + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: edit->button22 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button22"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button22->button21 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button21"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button21->sub2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "sub2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: sub2->button12 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(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(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(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +void tst_QQuickItem::activeFocusOnTab2() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button12 + QQuickItem *item = findItem(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button12->button11 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(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(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +void tst_QQuickItem::activeFocusOnTab3() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab3.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button1 + QQuickItem *item = findItem(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button1->button2, through a repeater + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button2->button3, through a row + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button3"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button3->button4, through a flow + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button4"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button4->button5, through a focusscope + // parent is activeFocusOnTab:false, one of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button5"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button5->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 5 Tabs: button6->button7, through a focusscope + // parent is activeFocusOnTab:true, none of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 5; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button7"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button7->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button6->button7, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button7"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button7->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button6->button5, through a focusscope(parent is activeFocusOnTab: false) + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button5"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button5->button4, through a focusscope(parent is activeFocusOnTab: false) + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button4"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button4->button3, through a flow + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button3"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button3->button2, through a row + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button2->button1, through a repeater + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(0); -- cgit v1.2.3