diff options
Diffstat (limited to 'src/quick/items/qquickitem.cpp')
-rw-r--r-- | src/quick/items/qquickitem.cpp | 299 |
1 files changed, 216 insertions, 83 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 423fb0f40c..2a744c0559 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1621,6 +1621,36 @@ void QQuickItemPrivate::setAccessibleFlagAndListener() } } +void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) +{ + Q_Q(QQuickItem); + Q_ASSERT(scope); + + QQuickItemPrivate *scopePrivate = QQuickItemPrivate::get(scope); + + QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi && sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + + if (focus) { + scopePrivate->subFocusItem = q; + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi && sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = q; + sfi = sfi->parentItem(); + } + } else { + scopePrivate->subFocusItem = 0; + } +} + + /*! \class QQuickItem \brief The QQuickItem class provides the most basic of all visual items in QML. @@ -1794,10 +1824,13 @@ QQuickItem::~QQuickItem() Q_D(QQuickItem); + if (d->canvasRefCount > 1) + d->canvasRefCount = 1; // Make sure canvas is set to null in next call to derefCanvas(). if (d->parentItem) setParentItem(0); - else if (d->canvas && d->itemNodeInstance) - QQuickCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root + else if (d->canvas) + d->derefCanvas(); + // XXX todo - optimize while (!d->childItems.isEmpty()) d->childItems.first()->setParentItem(0); @@ -1894,19 +1927,22 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) QQuickItem *scopeItem = 0; - if (d->canvas && hasFocus()) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + if (hasFocus()) scopeFocusedItem = this; - } else if (d->canvas && !isFocusScope() && d->subFocusItem) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + else if (!isFocusScope() && d->subFocusItem) scopeFocusedItem = d->subFocusItem; - } - if (scopeFocusedItem) - QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, + if (scopeFocusedItem) { + scopeItem = oldParentItem; + while (!scopeItem->isFocusScope() && scopeItem->parentItem()) + scopeItem = scopeItem->parentItem(); + if (d->canvas) { + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, QQuickCanvasPrivate::DontChangeFocusProperty); + } else { + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, false); + } + } const bool wasVisible = isVisible(); op->removeChild(this); @@ -1917,35 +1953,54 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) QQuickCanvasPrivate::get(d->canvas)->parentlessItems.remove(this); } - d->parentItem = parentItem; - - QQuickCanvas *parentCanvas = parentItem?QQuickItemPrivate::get(parentItem)->canvas:0; - if (d->canvas != parentCanvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, parentCanvas); + QQuickCanvas *oldParentCanvas = oldParentItem ? QQuickItemPrivate::get(oldParentItem)->canvas : 0; + QQuickCanvas *parentCanvas = parentItem ? QQuickItemPrivate::get(parentItem)->canvas : 0; + if (oldParentCanvas == parentCanvas) { + // Avoid freeing and reallocating resources if the canvas stays the same. + d->parentItem = parentItem; + } else { + if (oldParentCanvas) + d->derefCanvas(); + d->parentItem = parentItem; + if (parentCanvas) + d->refCanvas(parentCanvas); } d->dirty(QQuickItemPrivate::ParentChanged); if (d->parentItem) QQuickItemPrivate::get(d->parentItem)->addChild(this); + else if (d->canvas) + QQuickCanvasPrivate::get(d->canvas)->parentlessItems.insert(this); d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); d->setEffectiveEnableRecur(0, d->calcEffectiveEnable()); - if (scopeFocusedItem && d->parentItem && d->canvas) { - // We need to test whether this item becomes scope focused - QQuickItem *scopeItem = 0; - scopeItem = d->parentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + if (d->parentItem) { + if (!scopeFocusedItem) { + if (hasFocus()) + scopeFocusedItem = this; + else if (!isFocusScope() && d->subFocusItem) + scopeFocusedItem = d->subFocusItem; + } - if (scopeItem->scopedFocusItem()) { - QQuickItemPrivate::get(scopeFocusedItem)->focus = false; - emit scopeFocusedItem->focusChanged(false); - } else { - QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, - QQuickCanvasPrivate::DontChangeFocusProperty); + if (scopeFocusedItem) { + // We need to test whether this item becomes scope focused + QQuickItem *scopeItem = d->parentItem; + while (!scopeItem->isFocusScope() && scopeItem->parentItem()) + scopeItem = scopeItem->parentItem(); + + if (scopeItem->scopedFocusItem()) { + QQuickItemPrivate::get(scopeFocusedItem)->focus = false; + emit scopeFocusedItem->focusChanged(false); + } else { + if (d->canvas) { + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, + QQuickCanvasPrivate::DontChangeFocusProperty); + } else { + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true); + } + } } } @@ -2129,30 +2184,82 @@ QQuickItem *QQuickItemPrivate::InitializationState::getFocusScope(QQuickItem *it return focusScope; } -void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c) +void QQuickItemPrivate::refCanvas(InitializationState *state, QQuickCanvas *c) { - Q_Q(QQuickItem); + // An item needs a canvas if it is referenced by another item which has a canvas. + // Typically the item is referenced by a parent, but can also be referenced by a + // ShaderEffect or ShaderEffectSource. 'canvasRefCount' counts how many items with + // a canvas is referencing this item. When the reference count goes from zero to one, + // or one to zero, the canvas of this item is updated and propagated to the children. + // As long as the reference count stays above zero, the canvas is unchanged. + // refCanvas() increments the reference count. + // derefCanvas() decrements the reference count. - if (canvas) { - removeFromDirtyList(); - QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas); - if (polishScheduled) - c->itemsToPolish.remove(q); - if (c->mouseGrabberItem == q) - c->mouseGrabberItem = 0; - if ( hoverEnabled ) - c->hoverItems.removeAll(q); - if (itemNodeInstance) - c->cleanup(itemNodeInstance); - if (!parentItem) - c->parentlessItems.remove(q); + Q_Q(QQuickItem); + Q_ASSERT((canvas != 0) == (canvasRefCount > 0)); + Q_ASSERT(c); + if (++canvasRefCount > 1) { + if (c != canvas) + qWarning("QQuickItem: Cannot use same item on different canvases at the same time."); + return; // Canvas already set. } + Q_ASSERT(canvas == 0); canvas = c; - if (canvas && polishScheduled) + if (polishScheduled) QQuickCanvasPrivate::get(canvas)->itemsToPolish.insert(q); + InitializationState _dummy; + InitializationState *childState = state; + + if (q->isFocusScope()) { + _dummy.clear(q); + childState = &_dummy; + } + + if (!parentItem) + QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q); + + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); + QQuickItemPrivate::get(child)->refCanvas(childState, c); + } + + dirty(Canvas); + + if (extra.isAllocated() && extra->screenAttached) + extra->screenAttached->canvasChanged(c); + itemChange(QQuickItem::ItemSceneChange, c); +} + +void QQuickItemPrivate::derefCanvas() +{ + Q_Q(QQuickItem); + Q_ASSERT((canvas != 0) == (canvasRefCount > 0)); + + if (!canvas) + return; // This can happen when destroying recursive shader effect sources. + + if (--canvasRefCount > 0) + return; // There are still other references, so don't set canvas to null yet. + + q->releaseResources(); + removeFromDirtyList(); + QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas); + if (polishScheduled) + c->itemsToPolish.remove(q); + if (c->mouseGrabberItem == q) + c->mouseGrabberItem = 0; + if ( hoverEnabled ) + c->hoverItems.removeAll(q); + if (itemNodeInstance) + c->cleanup(itemNodeInstance); + if (!parentItem) + c->parentlessItems.remove(q); + + canvas = 0; + itemNodeInstance = 0; if (extra.isAllocated()) { @@ -2165,39 +2272,19 @@ void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c) groupNode = 0; paintNode = 0; - InitializationState _dummy; - InitializationState *childState = state; - - if (c && q->isFocusScope()) { - _dummy.clear(q); - childState = &_dummy; - } - - if (!parentItem && canvas) - QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q); - for (int ii = 0; ii < childItems.count(); ++ii) { QQuickItem *child = childItems.at(ii); - QQuickItemPrivate::get(child)->initCanvas(childState, c); - } - - if (c && focus) { - // Fixup - if (state->getFocusScope(q)->scopedFocusItem()) { - focus = false; - emit q->focusChanged(false); - } else { - QQuickCanvasPrivate::get(canvas)->setFocusInScope(state->getFocusScope(q), q); - } + QQuickItemPrivate::get(child)->derefCanvas(); } dirty(Canvas); if (extra.isAllocated() && extra->screenAttached) - extra->screenAttached->canvasChanged(c); - itemChange(QQuickItem::ItemSceneChange, c); + extra->screenAttached->canvasChanged(0); + itemChange(QQuickItem::ItemSceneChange, (QQuickCanvas *)0); } + /*! Returns a transform that maps points from canvas space into item space. */ @@ -2300,7 +2387,7 @@ bool QQuickItem::isComponentComplete() const QQuickItemPrivate::QQuickItemPrivate() : _anchors(0), _stateGroup(0), flags(0), widthValid(false), heightValid(false), baselineOffsetValid(false), componentComplete(true), - keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), + keepMouse(false), keepTouch(false), hoverEnabled(false), smooth(true), focus(false), activeFocus(false), notifiedFocus(false), notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), @@ -2310,7 +2397,7 @@ QQuickItemPrivate::QQuickItemPrivate() dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0), - canvas(0), parentItem(0), sortedChildItems(&childItems), + canvas(0), canvasRefCount(0), parentItem(0), sortedChildItems(&childItems), subFocusItem(0), @@ -2954,6 +3041,23 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) return 0; } +/*! + This function is called when the item's scene graph resources are no longer needed. + It allows items to free its resources, for instance textures, that are not owned by scene graph + nodes. Note that scene graph nodes are managed by QQuickCanvas and should not be deleted by + this function. Scene graph resources are no longer needed when the parent is set to null and + the item is not used by any \l ShaderEffect or \l ShaderEffectSource. + + This function is called from the main thread. Therefore, resources used by the scene graph + should not be deleted directly, but by calling \l QObject::deleteLater(). + + \note The item destructor still needs to free its scene graph resources if not already done. + */ + +void QQuickItem::releaseResources() +{ +} + QSGTransformNode *QQuickItemPrivate::createTransformNode() { return new QSGTransformNode; @@ -3018,6 +3122,10 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event) void QQuickItem::focusInEvent(QFocusEvent *) { +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(this, QAccessible::Focus); + QAccessible::updateAccessibility(&ev); +#endif } void QQuickItem::focusOutEvent(QFocusEvent *) @@ -3988,7 +4096,12 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible); - +#ifndef QT_NO_ACCESSIBILITY + if (isAccessible) { + QAccessibleEvent ev(q, effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide); + QAccessible::updateAccessibility(&ev); + } +#endif emit q->visibleChanged(); if (childVisibilityChanged) emit q->visibleChildrenChanged(); @@ -4217,13 +4330,15 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt /*! \property QQuickItem::smooth - \brief whether the item is smoothly transformed. + \brief whether the item is smoothed or not. - This property is provided purely for the purpose of optimization. Turning - smooth transforms off is faster, but looks worse; turning smooth - transformations on is slower, but looks better. + Primarily used in image based elements to decide if the item should use smooth + sampling or not. Smooth sampling is performed using linear interpolation, while + non-smooth is performed using nearest neighbor. - By default smooth transformations are off. + In Qt Quick 2.0, this property has minimal impact on performance. + + By default is true. */ /*! @@ -4650,15 +4765,33 @@ void QQuickItem::setFocus(bool focus) if (d->focus == focus) return; - if (d->canvas) { + if (d->canvas || d->parentItem) { // Need to find our nearest focus scope QQuickItem *scope = parentItem(); - while (scope && !scope->isFocusScope()) + while (scope && !scope->isFocusScope() && scope->parentItem()) scope = scope->parentItem(); - if (focus) - QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); - else - QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); + if (d->canvas) { + if (focus) + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); + else + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); + } else { + // do the focus changes from setFocusInScope/clearFocusInScope that are + // unrelated to a canvas + QVarLengthArray<QQuickItem *, 20> changed; + QQuickItem *oldSubFocusItem = QQuickItemPrivate::get(scope)->subFocusItem; + if (oldSubFocusItem) { + QQuickItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + d->updateSubFocusItem(scope, focus); + + d->focus = focus; + changed << this; + emit focusChanged(focus); + + QQuickCanvasPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1); + } } else { d->focus = focus; emit focusChanged(focus); |