From 6a8439070ed0582311053d130500facfd3e9d89d Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Mon, 23 Jan 2012 13:30:37 +1000 Subject: [PATCH] Update item focus when the enabled property is changed. Remove active focus from an item when it is disabled, and give active focus to an enabled item if it has focus within a scope with active focus. Task-number: QTBUG-22404 Change-Id: Iff2b774a9ff784e6107a4ed6524c93e749ae0182 Reviewed-by: Michael Brasser --- src/quick/items/qquickcanvas.cpp | 11 +- src/quick/items/qquickcanvas_p.h | 1 + src/quick/items/qquickitem.cpp | 27 +++- src/quick/items/qquickitem_p.h | 2 +- .../qtquick2/qquickitem/tst_qquickitem.cpp | 139 ++++++++++++++++++ 5 files changed, 168 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index cf529c0d71..5aa244c4a8 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -379,11 +379,14 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F QVarLengthArray changed; // Does this change the active focus? - if (item == rootItem || scopePrivate->activeFocus) { + if (item == rootItem || scopePrivate->activeFocus && item->isEnabled()) { oldActiveFocusItem = activeFocusItem; newActiveFocusItem = item; - while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem()) + while (newActiveFocusItem->isFocusScope() + && newActiveFocusItem->scopedFocusItem() + && newActiveFocusItem->scopedFocusItem()->isEnabled()) { newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); + } if (oldActiveFocusItem) { #ifndef QT_NO_IM @@ -405,7 +408,7 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F } } - if (item != rootItem) { + if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; // Correct focus chain in scope if (oldSubFocusItem) { @@ -513,7 +516,7 @@ void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, } } - if (item != rootItem) { + if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; // Correct focus chain in scope if (oldSubFocusItem) { diff --git a/src/quick/items/qquickcanvas_p.h b/src/quick/items/qquickcanvas_p.h index 5da75c709a..fe57fd1a47 100644 --- a/src/quick/items/qquickcanvas_p.h +++ b/src/quick/items/qquickcanvas_p.h @@ -135,6 +135,7 @@ class QQuickCanvasPrivate : public QWindowPrivate QList hoverItems; enum FocusOption { DontChangeFocusProperty = 0x01, + DontChangeSubFocusItem = 0x02 }; Q_DECLARE_FLAGS(FocusOptions, FocusOption) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index c9d81c0d11..032427d438 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1913,7 +1913,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) QQuickItemPrivate::get(d->parentItem)->addChild(this); d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); - d->setEffectiveEnableRecur(d->calcEffectiveEnable()); + d->setEffectiveEnableRecur(0, d->calcEffectiveEnable()); if (scopeFocusedItem && d->parentItem && d->canvas) { // We need to test whether this item becomes scope focused @@ -3901,7 +3901,11 @@ void QQuickItem::setEnabled(bool e) d->explicitEnable = e; - d->setEffectiveEnableRecur(d->calcEffectiveEnable()); + QQuickItem *scope = parentItem(); + while (scope && !scope->isFocusScope()) + scope = scope->parentItem(); + + d->setEffectiveEnableRecur(scope, d->calcEffectiveEnable()); } bool QQuickItemPrivate::calcEffectiveVisible() const @@ -3959,12 +3963,10 @@ bool QQuickItemPrivate::calcEffectiveEnable() const return explicitEnable && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveEnable); } -void QQuickItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable) +void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffectiveEnable) { Q_Q(QQuickItem); - // XXX todo - need to fixup focus - if (newEffectiveEnable && !explicitEnable) { // This item locally overrides enable return; @@ -3981,10 +3983,21 @@ void QQuickItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable) QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas); if (canvasPriv->mouseGrabberItem == q) q->ungrabMouse(); + if (scope && !effectiveEnable && activeFocus) { + canvasPriv->clearFocusInScope( + scope, q, QQuickCanvasPrivate::DontChangeFocusProperty | QQuickCanvasPrivate::DontChangeSubFocusItem); + } } - for (int ii = 0; ii < childItems.count(); ++ii) - QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(newEffectiveEnable); + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur( + flags & QQuickItem::ItemIsFocusScope ? q : scope, newEffectiveEnable); + } + + if (canvas && scope && effectiveEnable && focus) { + QQuickCanvasPrivate::get(canvas)->setFocusInScope( + scope, q, QQuickCanvasPrivate::DontChangeFocusProperty | QQuickCanvasPrivate::DontChangeSubFocusItem); + } emit q->enabledChanged(); } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 1d2a97dbb9..08cdd5700f 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -476,7 +476,7 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate bool calcEffectiveVisible() const; void setEffectiveVisibleRecur(bool); bool calcEffectiveEnable() const; - void setEffectiveEnableRecur(bool); + void setEffectiveEnableRecur(QQuickItem *scope, bool); // XXX todo enum DirtyType { diff --git a/tests/auto/qtquick2/qquickitem/tst_qquickitem.cpp b/tests/auto/qtquick2/qquickitem/tst_qquickitem.cpp index 3ff5054bd3..099f29d925 100644 --- a/tests/auto/qtquick2/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/qtquick2/qquickitem/tst_qquickitem.cpp @@ -136,6 +136,7 @@ private slots: void visible(); void enabled(); + void enabledFocus(); void mouseGrab(); void touchEventAccept(); @@ -801,6 +802,144 @@ void tst_qquickitem::enabled() delete child2; } +void tst_qquickitem::enabledFocus() +{ + QQuickCanvas canvas; + ensureFocus(&canvas); + + QQuickFocusScope root; + + root.setFocus(true); + root.setEnabled(false); + + QCOMPARE(root.isEnabled(), false); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), false); + + root.setParentItem(canvas.rootItem()); + + QCOMPARE(root.isEnabled(), false); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), canvas.rootItem()); + + root.setEnabled(true); + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(canvas.activeFocusItem(), static_cast(&root)); + + QQuickItem child1; + child1.setParentItem(&root); + + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), static_cast(&root)); + + QQuickItem child2; + child2.setFocus(true); + child2.setParentItem(&root); + + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(child2.isEnabled(), true); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), true); + QCOMPARE(canvas.activeFocusItem(), &child2); + + child2.setEnabled(false); + + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), static_cast(&root)); + + child1.setEnabled(false); + QCOMPARE(child1.isEnabled(), false); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + + child1.setFocus(true); + QCOMPARE(child1.isEnabled(), false); + QCOMPARE(child1.hasFocus(), true); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), false); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), static_cast(&root)); + + child1.setEnabled(true); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), true); + QCOMPARE(child1.hasActiveFocus(), true); + QCOMPARE(canvas.activeFocusItem(), static_cast(&child1)); + + root.setFocus(false); + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), false); + QCOMPARE(root.hasActiveFocus(), false); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), true); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), canvas.rootItem()); + + child2.forceActiveFocus(); + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), static_cast(&root)); + + root.setEnabled(false); + QCOMPARE(root.isEnabled(), false); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), false); + QCOMPARE(child1.isEnabled(), false); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), canvas.rootItem()); + + child1.forceActiveFocus(); + QCOMPARE(root.isEnabled(), false); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), false); + QCOMPARE(child1.isEnabled(), false); + QCOMPARE(child1.hasFocus(), true); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), false); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), canvas.rootItem()); + + root.setEnabled(true); + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), true); + QCOMPARE(child1.hasActiveFocus(), true); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), false); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(canvas.activeFocusItem(), static_cast(&child1)); +} + void tst_qquickitem::mouseGrab() { QQuickCanvas *canvas = new QQuickCanvas;