aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickdeliveryagent.cpp
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2021-02-25 18:20:54 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2021-03-04 10:20:08 +0100
commit518bfcc61d15e3dc7692b360646d5838dcc9ae32 (patch)
tree9ad4739af9501f0df6179d0699b2049817767519 /src/quick/util/qquickdeliveryagent.cpp
parentaa23f786dee088480cd5b25a3e3ff7405d4c4b1c (diff)
Fix click-to-focus in items within subscenes; improve logging
This fixes the ability to focus an item by clicking (such as a TextInput) when it's part of a subscene mapped to a 3D object in Qt Quick 3D, even if there are multiple subscenes with focusable items. QQuickDeliveryAgentPrivate::setFocusInScope(subsceneRoot, textInput) for example did not succeed; for one thing, this check fails: // Does this change the active focus? if (item == rootItem || scopePrivate->activeFocus) { because in a 3D scene, so far the viewport has focus by default, so the given scope (subscene root) does not have active focus. Each window ultimately has one actively focused item, so we need to delegate to the delivery agent belonging to the window's root item, to set its active focus. It's not even clear whether it's really a good idea for each subscene delivery agent to have its own QQuickDeliveryAgentPrivate::activeFocusItem. It might give us flexibility: perhaps each subscene root item should be a focus scope, and each delivery agent should decide which item would be hypothetically focused if the whole subscene got focus. But for now, it seems enough to set the activeFocusItem on the root item of the whole scene. Another problem was that when QQuickItem::forceActiveFocus() goes up the parent hierarchy, it didn't find a focus scope. So when you clicked on a TextInput that already had its focus property set to true, 1) QQuickItem::setFocus() does nothing because d->focus is already set 2) QQuickItem::forceActiveFocus() did not find a parent focus scope Therefore neither of them changed anything, and it wouldn't get active focus. Setting the ItemIsFocusScope flag on each subscene root item fixes (2), and that seems to be enough for now. Another problem was that QQuickWindow::event() was calling QQuickDeliveryAgent::grabberAgent() even when delivering an event that is NOT a press event, in spite of the comment "When delivering _update_ and _release_ events to existing grabbers, use the subscene delivery agent, if any." A hover event often results in a HoverHandler getting a passive grab, but that passive grab is not a reason to deliver a subsequent press event to the same subscene. When the mouse moves, we need to start over with picking in the 3D scene. When the 60fps frame-synchronous hover event occurs, we need to start over with picking, in case 3D objects are being animated under the cursor. When a press occurs, we need to start over with picking in case the press occurs in a different location from the last hover (even though that's unlikely with a mouse, it happens easily with a touchscreen). Another problem was that QQuickItemPrivate::deliveryAgent() was not finding the subscene DA during delivery of key events. A child item's extra is often not allocated, but we still need to keep looking at the parents. The optimization from 68c103225f4e8bd6c1b18ef547108fd60f398c0f was also wrong: after an Item's default initialization, we always need to do the search for the subcene DA. Only if we are sure that the item is NOT in a subscene (as in all normal 2D scenes) we can avoid doing that search next time. Consolidate the number of lines of output in the qt.quick.focus logging category and show the activeFocusItem transition. As with most logging in Qt Quick, it's expected that you set QT_MESSAGE_PATTERN to include %{function} so that you can always see where each line comes from. Therefore the log output itself has only minimal context (as in "q focus item x in scope y") rather than repeating the function name. Change-Id: I1b2a989c02c58c966653f965c0de512aa641bb99 Reviewed-by: Andy Nichols <andy.nichols@qt.io> (cherry picked from commit 543598a6cc07d67e7651c9f65c058465ea6d8425) Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/quick/util/qquickdeliveryagent.cpp')
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp41
1 files changed, 27 insertions, 14 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 91d42412fb..1302f2825c 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -344,15 +344,13 @@ static inline bool singleWindowOnScreen(QQuickWindow *win)
void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item,
Qt::FocusReason reason, FocusOptions options)
{
+ Q_Q(QQuickDeliveryAgent);
Q_ASSERT(item);
Q_ASSERT(scope || item == rootItem);
- qCDebug(lcFocus) << "QQuickDeliveryAgentPrivate::setFocusInScope():";
- qCDebug(lcFocus) << " scope:" << (QObject *)scope;
+ qCDebug(lcFocus) << q << "focus" << item << "in scope" << scope;
if (scope)
- qCDebug(lcFocus) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
- qCDebug(lcFocus) << " item:" << (QObject *)item;
- qCDebug(lcFocus) << " activeFocusItem:" << (QObject *)activeFocusItem;
+ qCDebug(lcFocus) << " scopeSubFocusItem:" << QQuickItemPrivate::get(scope)->subFocusItem;
QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : nullptr;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
@@ -457,17 +455,23 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
if (!changed.isEmpty())
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ if (isSubsceneAgent) {
+ auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
+ qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
+ QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->setFocusInScope(da->rootItem(), item, reason, options);
+ }
+ if (oldActiveFocusItem == activeFocusItem)
+ qCDebug(lcFocus) << " activeFocusItem remains" << activeFocusItem << "in" << q;
+ else
+ qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
}
void QQuickDeliveryAgentPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
{
Q_ASSERT(item);
Q_ASSERT(scope || item == rootItem);
-
- qCDebug(lcFocus) << "QQuickDeliveryAgentPrivate::clearFocusInScope():";
- qCDebug(lcFocus) << " scope:" << (QObject *)scope;
- qCDebug(lcFocus) << " item:" << (QObject *)item;
- qCDebug(lcFocus) << " activeFocusItem:" << (QObject *)activeFocusItem;
+ Q_Q(QQuickDeliveryAgent);
+ qCDebug(lcFocus) << q << "clear focus" << item << "in scope" << scope;
QQuickItemPrivate *scopePrivate = nullptr;
if (scope) {
@@ -547,6 +551,11 @@ void QQuickDeliveryAgentPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem
if (!changed.isEmpty())
notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+
+ if (oldActiveFocusItem == activeFocusItem)
+ qCDebug(lcFocus) << "activeFocusItem remains" << activeFocusItem << "in" << q;
+ else
+ qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
}
void QQuickDeliveryAgentPrivate::clearFocusObject()
@@ -1403,8 +1412,8 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
auto itemPriv = QQuickItemPrivate::get(handler->parentItem());
// An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
// whereas the subscene root item already knows it has its own DA.
- if (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)
- itemPriv->hasSubsceneDeliveryAgent = grabGained;
+ if (grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
+ itemPriv->maybeHasSubsceneDeliveryAgent = true;
subsceneAgent = itemPriv->deliveryAgent();
}
} else {
@@ -1447,8 +1456,8 @@ void QQuickDeliveryAgentPrivate::onGrabChanged(QObject *grabber, QPointingDevice
auto itemPriv = QQuickItemPrivate::get(grabberItem);
// An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
// whereas the subscene root item already knows it has its own DA.
- if (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent)
- itemPriv->hasSubsceneDeliveryAgent = grabGained;
+ if (grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
+ itemPriv->maybeHasSubsceneDeliveryAgent = true;
subsceneAgent = itemPriv->deliveryAgent();
}
}
@@ -1726,6 +1735,10 @@ bool QQuickDeliveryAgentPrivate::deliverPressOrReleaseEvent(QPointerEvent *event
}
for (QQuickItem *item : targetItems) {
+ // failsafe: when items get into a subscene somehow, ensure that QQuickItemPrivate::deliveryAgent() can find it
+ if (isSubsceneAgent)
+ QQuickItemPrivate::get(item)->maybeHasSubsceneDeliveryAgent = true;
+
hasFiltered.clear();
if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
if (event->isAccepted())