Fix forceActiveFocus() with multiple items having focus: true
authorAndrew den Exter <andrew.den-exter@nokia.com>
Wed, 28 Mar 2012 08:11:14 +0000 (18:11 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 30 Mar 2012 05:53:56 +0000 (07:53 +0200)
Ensure focus is cleared from an item if it is parented to an item that
already has a sub focus item.  Checking scopedFocusItem() didn't allow
for the case where an unparented item is the root of the focus tree
but not itself a focus scope, checking the unguarded subFocusItem
member will.

Change-Id: I482779e8077e9f282d22bd0090e669840764e52a
Reviewed-by: Bea Lam <bea.lam@nokia.com>

src/quick/items/qquickitem.cpp
tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml [new file with mode: 0644]
tests/auto/quick/qquickitem/tst_qquickitem.cpp

index 10003d4..117667d 100644 (file)
 
 QT_BEGIN_NAMESPACE
 
+#ifdef FOCUS_DEBUG
+void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth)
+{
+    qWarning()
+            << QByteArray(depth, '\t').constData()
+            << (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ')
+            << item->hasFocus()
+            << item->hasActiveFocus()
+            << item->isFocusScope()
+            << item;
+    foreach (QQuickItem *child, item->childItems()) {
+        printFocusTree(
+                child,
+                item->isFocusScope() || !scope ? item : scope,
+                item->isFocusScope() || !scope ? depth + 1 : depth);
+    }
+}
+#endif
+
 static void QQuickItem_parentNotifier(QObject *o, intptr_t, QQmlNotifier **n)
 {
     QQuickItemPrivate *d = QQuickItemPrivate::get(static_cast<QQuickItem *>(o));
@@ -1990,7 +2009,10 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
             while (!scopeItem->isFocusScope() && scopeItem->parentItem())
                 scopeItem = scopeItem->parentItem();
 
-            if (scopeItem->scopedFocusItem()) {
+            if (QQuickItemPrivate::get(scopeItem)->subFocusItem
+                    || (!scopeItem->isFocusScope() && scopeItem->hasFocus())) {
+                if (scopeFocusedItem != this)
+                    QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, false);
                 QQuickItemPrivate::get(scopeFocusedItem)->focus = false;
                 emit scopeFocusedItem->focusChanged(false);
             } else {
diff --git a/tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml b/tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml
new file mode 100644 (file)
index 0000000..0e50710
--- /dev/null
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+Rectangle {
+    width: 400; height: 400
+
+    FocusScope {
+        width: 400; height: 400
+        focus: true
+        Item {
+            width: 400; height: 400
+            Item {
+                id: dummy
+                objectName: "dummyItem"
+                focus: true
+            }
+            TextInput {
+                id: ti
+                objectName: "textInput"
+                focus: true
+            }
+        }
+    }
+}
index abd0da8..79183f5 100644 (file)
@@ -139,6 +139,8 @@ private slots:
     void addedToCanvas();
     void changeParent();
     void multipleFocusClears();
+    void focusSubItemInNonFocusScope();
+    void parentItemWithFocus();
 
     void constructor();
     void setParentItem();
@@ -720,6 +722,105 @@ void tst_qquickitem::multipleFocusClears()
     QTRY_VERIFY(QGuiApplication::focusWindow() == view);
 }
 
+void tst_qquickitem::focusSubItemInNonFocusScope()
+{
+    QQuickView *view = new QQuickView;
+    view->setSource(testFileUrl("focusSubItemInNonFocusScope.qml"));
+    view->show();
+    qApp->processEvents();
+
+    QQuickItem *dummyItem = view->rootObject()->findChild<QQuickItem *>("dummyItem");
+    QVERIFY(dummyItem);
+
+    QQuickItem *textInput = view->rootObject()->findChild<QQuickItem *>("textInput");
+    QVERIFY(textInput);
+
+    QVERIFY(dummyItem->hasFocus());
+    QVERIFY(!textInput->hasFocus());
+    QVERIFY(dummyItem->hasActiveFocus());
+
+    QVERIFY(QMetaObject::invokeMethod(textInput, "forceActiveFocus"));
+
+    QVERIFY(!dummyItem->hasFocus());
+    QVERIFY(textInput->hasFocus());
+    QVERIFY(textInput->hasActiveFocus());
+
+    delete view;
+}
+
+void tst_qquickitem::parentItemWithFocus()
+{
+    QQuickCanvas canvas;
+    ensureFocus(&canvas);
+    QTRY_VERIFY(QGuiApplication::focusWindow() == &canvas);
+    {
+    QQuickItem parent;
+    QQuickItem child;
+
+    FocusState focusState;
+    focusState << &parent << &child;
+    FVERIFY();
+
+    parent.setFocus(true);
+    child.setFocus(true);
+    focusState[&parent].set(true, false);
+    focusState[&child].set(true, false);
+    FVERIFY();
+
+    child.setParentItem(&parent);
+    focusState[&parent].set(true, false);
+    focusState[&child].set(false, false);
+    FVERIFY();
+
+    parent.setParentItem(canvas.rootItem());
+    focusState[&parent].set(true, true);
+    focusState[&child].set(false, false);
+    focusState.active(&parent);
+    FVERIFY();
+
+    child.forceActiveFocus();
+    focusState[&parent].set(false, false);
+    focusState[&child].set(true, true);
+    focusState.active(&child);
+    FVERIFY();
+    } {
+    QQuickItem parent;
+    QQuickItem child;
+    QQuickItem grandchild(&child);
+
+    FocusState focusState;
+    focusState << &parent << &child << &grandchild;
+    FVERIFY();
+
+    parent.setFocus(true);
+    grandchild.setFocus(true);
+    focusState[&parent].set(true, false);
+    focusState[&child].set(false, false);
+    focusState[&grandchild].set(true, false);
+    FVERIFY();
+
+    child.setParentItem(&parent);
+    focusState[&parent].set(true, false);
+    focusState[&child].set(false, false);
+    focusState[&grandchild].set(false, false);
+    FVERIFY();
+
+    parent.setParentItem(canvas.rootItem());
+    focusState[&parent].set(true, true);
+    focusState[&child].set(false, false);
+    focusState[&grandchild].set(false, false);
+    focusState.active(&parent);
+    FVERIFY();
+
+    grandchild.forceActiveFocus();
+    focusState[&parent].set(false, false);
+    focusState[&child].set(false, false);
+    focusState[&grandchild].set(true, true);
+    focusState.active(&grandchild);
+    FVERIFY();
+    }
+}
+
 void tst_qquickitem::constructor()
 {
     QQuickItem *root = new QQuickItem;