aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@digia.com>2013-02-22 13:44:06 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-12 02:27:29 +0100
commit2f9d0346f00e37725f0f1bce1318501a25c5e438 (patch)
tree24060fe2777ada4a054cb0c82e5bf89c28ffa70a
parentb4673e69a0522fca9c592f1dab1734738b1443a8 (diff)
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 <frederik.gladhorn@digia.com> Done-with: Gabriel de Dietrich <gabriel.dedietrich@digia.com> Change-Id: I8b58be9c20d113661fe85d27bdb1af84340d9de5 Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
-rw-r--r--examples/quick/keyinteraction/focus/Core/GridMenu.qml3
-rw-r--r--examples/quick/keyinteraction/focus/Core/TabMenu.qml104
-rw-r--r--examples/quick/keyinteraction/focus/focus.qml46
-rw-r--r--examples/quick/keyinteraction/keyinteraction.qrc1
-rw-r--r--src/quick/items/qquickitem.cpp157
-rw-r--r--src/quick/items/qquickitem.h5
-rw-r--r--src/quick/items/qquickitem_p.h6
-rw-r--r--tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml136
-rw-r--r--tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml250
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp329
10 files changed, 1023 insertions, 14 deletions
diff --git a/examples/quick/keyinteraction/focus/Core/GridMenu.qml b/examples/quick/keyinteraction/focus/Core/GridMenu.qml
index 3dd0637344..e18ec1f0d3 100644
--- a/examples/quick/keyinteraction/focus/Core/GridMenu.qml
+++ b/examples/quick/keyinteraction/focus/Core/GridMenu.qml
@@ -45,7 +45,7 @@ FocusScope {
onActiveFocusChanged: {
if (activeFocus)
- mainView.state = ""
+ mainView.state = "showGridViews"
}
Rectangle {
@@ -63,6 +63,7 @@ FocusScope {
focus: true
model: 12
+ KeyNavigation.up: tabMenu
KeyNavigation.down: listMenu
KeyNavigation.left: contextMenu
diff --git a/examples/quick/keyinteraction/focus/Core/TabMenu.qml b/examples/quick/keyinteraction/focus/Core/TabMenu.qml
new file mode 100644
index 0000000000..5eea611b44
--- /dev/null
+++ b/examples/quick/keyinteraction/focus/Core/TabMenu.qml
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+FocusScope {
+ onActiveFocusChanged: {
+ if (activeFocus)
+ mainView.state = "showTabViews"
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ clip: true
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#193441" }
+ GradientStop { position: 1.0; color: Qt.darker("#193441") }
+ }
+
+ Row {
+ id: tabView
+ anchors.fill: parent; anchors.leftMargin: 20; anchors.rightMargin: 20
+ Repeater {
+ activeFocusOnTab: false
+ model: 5
+ Item {
+ id: container
+ width: 152; height: 152
+ activeFocusOnTab: true
+ focus: true
+
+ KeyNavigation.up: listMenu
+ KeyNavigation.down: gridMenu
+
+ Rectangle {
+ id: content
+ color: "transparent"
+ antialiasing: true
+ anchors.fill: parent; anchors.margins: 20; radius: 10
+
+ Rectangle { color: "#91AA9D"; anchors.fill: parent; anchors.margins: 3; radius: 8; antialiasing: true }
+ Image { source: "images/qt-logo.png"; anchors.centerIn: parent }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onClicked: {
+ container.forceActiveFocus()
+ }
+ }
+
+ states: State {
+ name: "active"; when: container.activeFocus
+ PropertyChanges { target: content; color: "#FCFFF5"; scale: 1.1 }
+ }
+
+ transitions: Transition {
+ NumberAnimation { properties: "scale"; duration: 100 }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/keyinteraction/focus/focus.qml b/examples/quick/keyinteraction/focus/focus.qml
index 193b3d7b3a..91fb146e40 100644
--- a/examples/quick/keyinteraction/focus/focus.qml
+++ b/examples/quick/keyinteraction/focus/focus.qml
@@ -44,7 +44,7 @@ import "Core"
Rectangle {
id: window
- width: 800; height: 480
+ width: 800; height: 640
color: "#3E606F"
FocusScope {
@@ -53,17 +53,24 @@ Rectangle {
width: parent.width; height: parent.height
focus: true
- GridMenu {
- id: gridMenu
- width: parent.width; height: 320
+ TabMenu {
+ id: tabMenu
+ y: 160; width: parent.width; height: 160
focus: true
- interactive: parent.activeFocus
+ activeFocusOnTab: true
+ }
+
+ GridMenu {
+ id: gridMenu
+ y: 320; width: parent.width; height: 320
+ activeFocusOnTab: true
}
ListMenu {
id: listMenu
- y: 320; width: parent.width; height: 320
+ y: 640; width: parent.width; height: 320
+ activeFocusOnTab: true
}
Rectangle {
@@ -73,11 +80,28 @@ Rectangle {
opacity: 0
}
- states: State {
- name: "showListViews"
- PropertyChanges { target: gridMenu; y: -160 }
- PropertyChanges { target: listMenu; y: 160 }
- }
+ states: [
+ State {
+ name: "showTabViews"
+ PropertyChanges { target: tabMenu; y: 160 }
+ PropertyChanges { target: gridMenu; y: 320 }
+ PropertyChanges { target: listMenu; y: 640 }
+ },
+
+ State {
+ name: "showGridViews"
+ PropertyChanges { target: tabMenu; y: 0 }
+ PropertyChanges { target: gridMenu; y: 160 }
+ PropertyChanges { target: listMenu; y: 480 }
+ },
+
+ State {
+ name: "showListViews"
+ PropertyChanges { target: tabMenu; y: -160 }
+ PropertyChanges { target: gridMenu; y: 0 }
+ PropertyChanges { target: listMenu; y: 320 }
+ }
+ ]
transitions: Transition {
NumberAnimation { properties: "y"; duration: 600; easing.type: Easing.OutQuint }
diff --git a/examples/quick/keyinteraction/keyinteraction.qrc b/examples/quick/keyinteraction/keyinteraction.qrc
index 21bde4472c..c719654835 100644
--- a/examples/quick/keyinteraction/keyinteraction.qrc
+++ b/examples/quick/keyinteraction/keyinteraction.qrc
@@ -8,5 +8,6 @@
<file>focus/Core/GridMenu.qml</file>
<file>focus/Core/ListMenu.qml</file>
<file>focus/Core/ListViewDelegate.qml</file>
+ <file>focus/Core/TabMenu.qml</file>
</qresource>
</RCC>
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 53773553ee..67c4fd2140 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -1863,6 +1863,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
*/
/*!
+ \fn void QQuickItem::activeFocusOnTabChanged(bool)
+ \internal
+*/
+
+/*!
\fn void QQuickItem::childrenChanged()
\internal
*/
@@ -2023,6 +2028,93 @@ QQuickItem::~QQuickItem()
}
/*!
+ \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
+ \return Whether the next item in the focus chain is found or not
+
+ If \a next is true, the next item visited will be in depth-first order relative to \a item.
+ If \a next is false, the next item visited will be in reverse depth-first order relative to \a item.
+*/
+bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
+{
+ Q_ASSERT(item);
+ Q_ASSERT(item->activeFocusOnTab());
+
+ QQuickItem *from = 0;
+ if (forward) {
+ from = item->parentItem();
+ } else {
+ if (!item->childItems().isEmpty())
+ from = item->childItems().first();
+ else
+ from = item->parentItem();
+ }
+ bool skip = false;
+ QQuickItem *current = item;
+ do {
+ skip = false;
+ QQuickItem *last = current;
+
+ bool hasChildren = !current->childItems().isEmpty() && current->isEnabled() && current->isVisible();
+
+ // coming from parent: check children
+ if (hasChildren && from == current->parentItem()) {
+ if (forward) {
+ current = current->childItems().first();
+ } else {
+ current = current->childItems().last();
+ if (!current->childItems().isEmpty())
+ skip = true;
+ }
+ } else if (hasChildren && forward && from != current->childItems().last()) {
+ // not last child going forwards
+ int nextChild = current->childItems().indexOf(from) + 1;
+ current = current->childItems().at(nextChild);
+ } else if (hasChildren && !forward && from != current->childItems().first()) {
+ // not first child going backwards
+ int prevChild = current->childItems().indexOf(from) - 1;
+ current = current->childItems().at(prevChild);
+ if (!current->childItems().isEmpty())
+ skip = true;
+ // back to the parent
+ } else if (current->parentItem()) {
+ current = current->parentItem();
+ // we would evaluate the parent twice, thus we skip
+ if (forward) {
+ skip = true;
+ } else if (!forward && !current->childItems().isEmpty()) {
+ if (last != current->childItems().first()) {
+ skip = true;
+ } else if (last == current->childItems().first()) {
+ if (current->isFocusScope() && current->activeFocusOnTab() && current->hasActiveFocus())
+ skip = true;
+ }
+ }
+ } else if (hasChildren) {
+ // Wrap around after checking all items forward
+ if (forward) {
+ current = current->childItems().first();
+ } else {
+ current = current->childItems().last();
+ if (!current->childItems().isEmpty())
+ skip = true;
+ }
+ }
+
+ from = last;
+ } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible());
+
+ if (current == item)
+ return false;
+
+ current->forceActiveFocus();
+
+ return true;
+}
+
+/*!
\qmlproperty Item QtQuick2::Item::parent
This property holds the visual parent of the item.
@@ -2522,6 +2614,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, isAccessible(false)
, culled(false)
, hasCursor(false)
+ , activeFocusOnTab(false)
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
@@ -4170,6 +4263,23 @@ void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
else
extra->keyHandler->keyReleased(e, true);
}
+
+ if (e->isAccepted())
+ return;
+
+ //only care about KeyPress now
+ if (q->activeFocusOnTab() && e->type() == QEvent::KeyPress) {
+ bool res = false;
+ if (!(e->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
+ if (e->key() == Qt::Key_Backtab
+ || (e->key() == Qt::Key_Tab && (e->modifiers() & Qt::ShiftModifier)))
+ res = QQuickItemPrivate::focusNextPrev(q, false);
+ else if (e->key() == Qt::Key_Tab)
+ res = QQuickItemPrivate::focusNextPrev(q, true);
+ if (res)
+ e->setAccepted(true);
+ }
+ }
}
#ifndef QT_NO_IM
@@ -5332,6 +5442,53 @@ void QQuickItem::setSmooth(bool smooth)
}
/*!
+ \qmlproperty bool QtQuick2::Item::activeFocusOnTab
+
+ This property holds whether the item wants to be in tab focus
+ chain. By default this is set to false.
+
+ The tab focus chain traverses elements by visiting first the
+ parent, and then its children in the order they occur in the
+ children property. Pressing the tab key on an item in the tab
+ focus chain will move keyboard focus to the next item in the
+ chain. Pressing BackTab (normally Shift+Tab) will move focus
+ to the previous item.
+
+ To set up a manual tab focus chain, see \l KeyNavigation. Tab
+ key events used by Keys or KeyNavigation have precedence over
+ focus chain behavior, ignore the events in other key handlers
+ to allow it to propagate.
+*/
+/*!
+ \property QQuickItem::activeFocusOnTab
+
+ This property holds whether the item wants to be in tab focus
+ chain. By default this is set to false.
+*/
+bool QQuickItem::activeFocusOnTab() const
+{
+ Q_D(const QQuickItem);
+ return d->activeFocusOnTab;
+}
+void QQuickItem::setActiveFocusOnTab(bool activeFocusOnTab)
+{
+ Q_D(QQuickItem);
+ if (d->activeFocusOnTab == activeFocusOnTab)
+ return;
+
+ if (window()) {
+ if ((this == window()->activeFocusItem()) && !activeFocusOnTab) {
+ qWarning("QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.");
+ return;
+ }
+ }
+
+ d->activeFocusOnTab = activeFocusOnTab;
+
+ emit activeFocusOnTabChanged(activeFocusOnTab);
+}
+
+/*!
\qmlproperty bool QtQuick2::Item::antialiasing
Primarily used in Rectangle and image based elements to decide if the item should
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 71681698b9..c37bc10bdd 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -132,6 +132,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL)
Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL)
+ Q_PROPERTY(bool activeFocusOnTab READ activeFocusOnTab WRITE setActiveFocusOnTab NOTIFY activeFocusOnTabChanged FINAL)
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
@@ -261,6 +262,9 @@ public:
bool smooth() const;
void setSmooth(bool);
+ bool activeFocusOnTab() const;
+ void setActiveFocusOnTab(bool);
+
bool antialiasing() const;
void setAntialiasing(bool);
@@ -345,6 +349,7 @@ Q_SIGNALS:
void stateChanged(const QString &);
void focusChanged(bool);
void activeFocusChanged(bool);
+ void activeFocusOnTabChanged(bool);
void parentChanged(QQuickItem *);
void transformOriginChanged(TransformOrigin);
void smoothChanged(bool);
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 8f6865b40b..4bd9d82c20 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -396,8 +396,8 @@ public:
bool antialiasing:1;
bool focus:1;
bool activeFocus:1;
- bool notifiedFocus:1;
// Bit 16
+ bool notifiedFocus:1;
bool notifiedActiveFocus:1;
bool filtersChildMouseEvents:1;
bool explicitVisible:1;
@@ -413,8 +413,8 @@ public:
bool isAccessible:1;
bool culled:1;
bool hasCursor:1;
- // bool dummy:1
// Bit 32
+ bool activeFocusOnTab:1;
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -484,6 +484,8 @@ public:
QTransform itemToWindowTransform() const;
void itemToParentTransform(QTransform &) const;
+ static bool focusNextPrev(QQuickItem *item, bool forward);
+
qreal x;
qreal y;
qreal width;
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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<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::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<QQuickItem>(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<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::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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(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<QQuickItem>(window->rootObject(), "button1");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ delete window;
+}
+
void tst_QQuickItem::keys()
{
QQuickView *window = new QQuickView(0);