diff options
Diffstat (limited to 'src/quick/items')
34 files changed, 759 insertions, 394 deletions
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index f8e5e3c57f..a605b9ce6d 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -63,6 +63,10 @@ #include <private/qv8domerrors_p.h> #include <QtCore/qnumeric.h> +#ifdef Q_OS_QNX +#include <ctype.h> +#endif + QT_BEGIN_NAMESPACE /*! \qmlclass Context2D QQuickContext2D diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 46f85417d2..27efebca0c 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -144,6 +144,10 @@ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) // Enable accessibility for items with accessible content. This also // enables accessibility for the ancestors of souch items. item->d_func()->setAccessibleFlagAndListener(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(item, QAccessible::ObjectCreated); + QAccessible::updateAccessibility(&ev); +#endif } QQuickAccessibleAttached::~QQuickAccessibleAttached() diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 129c3ef240..419f21a4b3 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -85,6 +85,10 @@ public: if (name != m_name) { m_name = name; emit nameChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(parent(), QAccessible::NameChanged); + QAccessible::updateAccessibility(&ev); +#endif } } @@ -94,6 +98,10 @@ public: if (m_description != description) { m_description = description; emit descriptionChanged(); +#ifndef QT_NO_ACCESSIBILITY + QAccessibleEvent ev(parent(), QAccessible::DescriptionChanged); + QAccessible::updateAccessibility(&ev); +#endif } } diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index cd3bbbfa2a..a11f68f709 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -306,6 +306,8 @@ QQuickCanvasPrivate::QQuickCanvasPrivate() , windowManager(0) , clearColor(Qt::white) , clearBeforeRendering(true) + , persistentGLContext(false) + , persistentSceneGraph(false) , renderTarget(0) , renderTargetId(0) , incubationController(0) @@ -325,6 +327,7 @@ void QQuickCanvasPrivate::init(QQuickCanvas *c) rootItem = new QQuickRootItem; QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem); rootItemPrivate->canvas = q; + rootItemPrivate->canvasRefCount = 1; rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope; // In the absence of a focus in event on some platforms assume the window will @@ -536,7 +539,7 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { + while (afi && afi != scope) { if (QQuickItemPrivate::get(afi)->activeFocus) { QQuickItemPrivate::get(afi)->activeFocus = false; changed << afi; @@ -548,27 +551,12 @@ void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - { - scopePrivate->subFocusItem = item; - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = item; - sfi = sfi->parentItem(); - } - } - if (oldSubFocusItem) { QQuickItemPrivate::get(oldSubFocusItem)->focus = false; changed << oldSubFocusItem; } + + QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true); } if (!(options & DontChangeFocusProperty)) { @@ -647,7 +635,7 @@ void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { + while (afi && afi != scope) { if (QQuickItemPrivate::get(afi)->activeFocus) { QQuickItemPrivate::get(afi)->activeFocus = false; changed << afi; @@ -658,20 +646,13 @@ void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, if (item != rootItem && !(options & DontChangeSubFocusItem)) { QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - scopePrivate->subFocusItem = 0; - if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { QQuickItemPrivate::get(oldSubFocusItem)->focus = false; changed << oldSubFocusItem; } + + QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false); + } else if (!(options & DontChangeFocusProperty)) { QQuickItemPrivate::get(item)->focus = false; changed << item; @@ -750,6 +731,55 @@ void QQuickCanvasPrivate::cleanup(QSGNode *n) reparent the items to the root item or to an existing item in the scene. For easily displaying a scene from a QML file, see \l{QQuickView}. + + + \section1 Scene Graph and Rendering + + The QQuickCanvas uses a scene graph on top of OpenGL to render. This scene graph is disconnected + from the QML scene and potentially lives in another thread, depending on the platform + implementation. Since the rendering scene graph lives independently from the QML scene, it can + also be completely released without affecting the state of the QML scene. + + The sceneGraphInitialized() signal is emitted on the rendering thread before the QML scene is + rendered to the screen for the first time. If the rendering scene graph has been released + the signal will be emitted again before the next frame is rendered. + + Rendering is done by first copying the QML scene's state into the rendering scene graph. This is + done by calling QQuickItem::updatePaintNode() functions on all items that have changed. This phase + is run on the rendering thread with the GUI thread blocked, when a separate rendering thread + is being used. The scene can then be rendered. + + Before the scene graph is rendered, the beforeRendering() signal is emitted. The OpenGL context + is bound at this point and the application is free to do its own rendering. Also + make sure to disable the clearing of the color buffer, using setClearBeforeRendering(). The + default clear color is white and can be changed with setClearColor(). After the scene has + been rendered, the afterRendering() signal is emitted. The application can use this to render + OpenGL on top of a QML application. Once the frame is fully done and has been swapped, + the frameSwapped() signal is emitted. + + While the scene graph is being rendered on the rendering thread, the GUI will process animations + for the next frame. This means that as long as users are not using scene graph API + directly, the added complexity of a rendering thread can be completely ignored. + + When a QQuickCanvas is programatically hidden with hide() or setVisible(false), it will + stop rendering and its scene graph and OpenGL context might be released. The + sceneGraphInvalidated() signal will be emitted when this happens. + + \warning It is crucial that OpenGL operations and interaction with the scene graph happens + exclusively on the rendering thread, primarily during the updatePaintNode() phase. + + \warning As signals related to rendering might be emitted from the rendering thread, + connections should be made using Qt::DirectConnection + + + \section1 Resource Management + + QML will typically try to cache images, scene graph nodes, etc to improve performance, but in + some low-memory scenarios it might be required to aggressively release these resources. The + releaseResources() can be used to force clean up of certain resources. Calling releaseResources() + may result in the entire scene graph and its OpenGL context being deleted. The + sceneGraphInvalidated() signal will be emitted when this happens. + */ QQuickCanvas::QQuickCanvas(QWindow *parent) : QWindow(*(new QQuickCanvasPrivate), parent) @@ -771,11 +801,6 @@ QQuickCanvas::~QQuickCanvas() d->windowManager->canvasDestroyed(this); - // ### should we change ~QQuickItem to handle this better? - // manually cleanup for the root item (item destructor only handles these when an item is parented) - QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem); - rootItemPrivate->removeFromDirtyList(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); delete d->incubationController; d->incubationController = 0; @@ -786,6 +811,15 @@ QQuickCanvas::~QQuickCanvas() /*! This function tries to release redundant resources currently held by the QML scene. + + Calling this function might result in the scene graph and the OpenGL context used + for rendering being released to release graphics memory. If this happens, the + sceneGraphInvalidated() signal will be called, allowing users to clean up their + own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph() + functions can be used to prevent this from happening, if handling the cleanup is + not feasible in the application, at the cost of higher memory usage. + + \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph(). */ void QQuickCanvas::releaseResources() @@ -798,6 +832,69 @@ void QQuickCanvas::releaseResources() /*! + Controls whether the OpenGL context can be released as a part of a call to + releaseResources(). + + The OpenGL context might still be released when the user makes an explicit + call to hide(). + + \sa setPersistentSceneGraph() + */ + +void QQuickCanvas::setPersistentOpenGLContext(bool persistent) +{ + Q_D(QQuickCanvas); + d->persistentGLContext = persistent; +} + + +/*! + Returns whether the OpenGL context can be released as a part of a call to + releaseResources(). + */ + +bool QQuickCanvas::isPersistentOpenGLContext() const +{ + Q_D(const QQuickCanvas); + return d->persistentGLContext; +} + + + +/*! + Controls whether the scene graph nodes and resources can be released as a + part of a call to releaseResources(). + + The scene graph nodes and resources might still be released when the user + makes an explicit call to hide(). + + \sa setPersistentOpenGLContext() + */ + +void QQuickCanvas::setPersistentSceneGraph(bool persistent) +{ + Q_D(QQuickCanvas); + d->persistentSceneGraph = persistent; +} + + + +/*! + Returns whether the scene graph nodes and resources can be released as a part + of a call to releaseResources(). + */ + +bool QQuickCanvas::isPersistentSceneGraph() const +{ + Q_D(const QQuickCanvas); + return d->persistentSceneGraph; +} + + + + + +/*! Returns the invisible root item of the scene. A QQuickCanvas always has a single invisible root item. To add items to this canvas, @@ -1618,6 +1715,7 @@ void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item) if (p->extra.isAllocated()) { p->extra->opacityNode = 0; p->extra->clipNode = 0; + p->extra->rootNode = 0; } p->groupNode = 0; diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h index 4ac9509896..787bb7e3c7 100644 --- a/src/quick/items/qquickcanvas.h +++ b/src/quick/items/qquickcanvas.h @@ -114,6 +114,12 @@ public: void setClearColor(const QColor &color); QColor clearColor() const; + void setPersistentOpenGLContext(bool persistent); + bool isPersistentOpenGLContext() const; + + void setPersistentSceneGraph(bool persistent); + bool isPersistentSceneGraph() const; + QOpenGLContext *openglContext() const; Q_SIGNALS: diff --git a/src/quick/items/qquickcanvas_p.h b/src/quick/items/qquickcanvas_p.h index 147526466e..643d694400 100644 --- a/src/quick/items/qquickcanvas_p.h +++ b/src/quick/items/qquickcanvas_p.h @@ -144,7 +144,7 @@ public: void setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); void clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); - void notifyFocusChangesRecur(QQuickItem **item, int remaining); + static void notifyFocusChangesRecur(QQuickItem **item, int remaining); void updateFocusItemTransform(); @@ -184,6 +184,11 @@ public: uint clearBeforeRendering : 1; + // Currently unused in the default implementation, as we're not stopping + // rendering when obscured as we should... + uint persistentGLContext : 1; + uint persistentSceneGraph : 1; + QOpenGLFramebufferObject *renderTarget; uint renderTargetId; QSize renderTargetSize; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index b6d3ebd7f2..62d0e4aa41 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -887,11 +887,11 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) bool stealY = stealMouse; bool stealX = stealMouse; - qint64 elapsed = computeCurrentTime(event) - lastPressTime; + qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime; if (q->yflick()) { qreal dy = event->localPos().y() - pressPos.y(); - if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsed > 200) { + if (qAbs(dy) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) { if (!vMoved) vData.dragStartOffset = dy; qreal newY = dy + vData.pressPos - vData.dragStartOffset; @@ -924,7 +924,7 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) if (q->xflick()) { qreal dx = event->localPos().x() - pressPos.x(); - if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsed > 200) { + if (qAbs(dx) > qApp->styleHints()->startDragDistance() || elapsedSincePress > 200) { if (!hMoved) hData.dragStartOffset = dx; qreal newX = dx + hData.pressPos - hData.dragStartOffset; @@ -974,28 +974,26 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) q->movementStarting(); } - if (!lastPos.isNull()) { - qint64 currentTimestamp = computeCurrentTime(event); - qreal elapsed = qreal(currentTimestamp - lastPosTime) / 1000.; - if (elapsed <= 0) - return; - lastPosTime = currentTimestamp; - QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event); - if (q->yflick() && !rejectY) { - if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { - vData.addVelocitySample(extended->velocity().y(), maxVelocity); - } else { - qreal dy = event->localPos().y()-lastPos.y(); - vData.addVelocitySample(dy/elapsed, maxVelocity); - } + qint64 currentTimestamp = computeCurrentTime(event); + qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.; + if (elapsed <= 0) + return; + lastPosTime = currentTimestamp; + QQuickMouseEventEx *extended = QQuickMouseEventEx::extended(event); + if (q->yflick() && !rejectY) { + if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { + vData.addVelocitySample(extended->velocity().y(), maxVelocity); + } else { + qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y()); + vData.addVelocitySample(dy/elapsed, maxVelocity); } - if (q->xflick() && !rejectX) { - if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { - hData.addVelocitySample(extended->velocity().x(), maxVelocity); - } else { - qreal dx = event->localPos().x()-lastPos.x(); - hData.addVelocitySample(dx/elapsed, maxVelocity); - } + } + if (q->xflick() && !rejectX) { + if (extended && extended->capabilities().testFlag(QTouchDevice::Velocity)) { + hData.addVelocitySample(extended->velocity().x(), maxVelocity); + } else { + qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x()); + hData.addVelocitySample(dx/elapsed, maxVelocity); } } @@ -1763,6 +1761,10 @@ void QQuickFlickable::mouseUngrabEvent() d->draggingEnding(); d->stealMouse = false; setKeepMouseGrab(false); + d->fixupX(); + d->fixupY(); + if (!d->timeline.isActive()) + movementEnding(); } } 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); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index f14d60b028..70a8ebc932 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -396,6 +396,7 @@ protected: const QRectF &oldGeometry); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void releaseResources(); virtual void updatePolish(); protected Q_SLOTS: diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 03fc66eadb..01e8b4d335 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -450,6 +450,7 @@ public: QQuickItem**prevDirtyItem; QQuickCanvas *canvas; + int canvasRefCount; inline QSGContext *sceneGraphContext() const; QQuickItem *parentItem; @@ -472,9 +473,13 @@ public: private: QQuickItem *focusScope; }; - void initCanvas(InitializationState *, QQuickCanvas *); + + void refCanvas(QQuickCanvas *); + void refCanvas(InitializationState *, QQuickCanvas *); + void derefCanvas(); QQuickItem *subFocusItem; + void updateSubFocusItem(QQuickItem *scope, bool focus); QTransform canvasToItemTransform() const; QTransform itemToCanvasTransform() const; @@ -856,6 +861,13 @@ QQuickItem::TransformOrigin QQuickItemPrivate::origin() const return extra.isAllocated()?extra->origin:QQuickItem::Center; } +inline void QQuickItemPrivate::refCanvas(QQuickCanvas *c) +{ + QQuickItemPrivate::InitializationState initState; + initState.clear(); + refCanvas(&initState, c); +} + QSGTransformNode *QQuickItemPrivate::itemNode() { if (!itemNodeInstance) { diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index a1c398eeb6..3e84eb6115 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -927,6 +927,8 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio void QQuickPathAnimationUpdater::setValue(qreal v) { + v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);; + if (interruptStart.isValid()) { if (reverse) v = 1 - v; diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 50a3216bf0..0d95500860 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1101,9 +1101,9 @@ void QQuickItemView::trackedPositionChanged() if (d->layoutOrientation() == Qt::Vertical) endOffset += d->vData.endMargin; else if (d->isContentFlowReversed()) - endOffset += d->hData.endMargin; - else endOffset += d->hData.startMargin; + else + endOffset += d->hData.endMargin; trackedPos += endOffset; trackedEndPos += endOffset; toItemPos += endOffset; @@ -1204,12 +1204,11 @@ qreal QQuickItemView::minXExtent() const return QQuickFlickable::minXExtent(); if (d->hData.minExtentDirty) { - d->minExtent = -d->startPosition(); + d->minExtent = -d->startPosition() + d->hData.startMargin; qreal highlightStart; qreal highlightEnd; qreal endPositionFirstItem = 0; if (d->isContentFlowReversed()) { - d->minExtent += d->hData.endMargin; if (d->model && d->model->count()) endPositionFirstItem = d->positionAt(d->model->count()-1); else if (d->header) @@ -1222,7 +1221,6 @@ qreal QQuickItemView::minXExtent() const if (d->minExtent < maxX) d->minExtent = maxX; } else { - d->minExtent += d->hData.startMargin; endPositionFirstItem = d->endPositionAt(0); highlightStart = d->highlightRangeStart; highlightEnd = d->highlightRangeEnd; @@ -1279,7 +1277,7 @@ qreal QQuickItemView::maxXExtent() const if (d->isContentFlowReversed()) { if (d->header) d->maxExtent -= d->headerSize(); - d->maxExtent -= d->hData.startMargin; + d->maxExtent -= d->hData.endMargin; } else { if (d->footer) d->maxExtent -= d->footerSize(); @@ -1314,7 +1312,7 @@ qreal QQuickItemView::xOrigin() const { Q_D(const QQuickItemView); if (d->isContentFlowReversed()) - return -maxXExtent() + d->size() - d->hData.startMargin; + return -maxXExtent() + d->size() - d->hData.endMargin; else return -minXExtent() + d->hData.startMargin; } diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 59cb37c15d..f41ba44943 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -61,6 +61,8 @@ QQuickLoaderPrivate::QQuickLoaderPrivate() QQuickLoaderPrivate::~QQuickLoaderPrivate() { + delete itemContext; + itemContext = 0; delete incubator; disposeInitialPropertyValues(); } @@ -79,12 +81,21 @@ void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRec void QQuickLoaderPrivate::clear() { + Q_Q(QQuickLoader); disposeInitialPropertyValues(); if (incubator) incubator->clear(); + delete itemContext; + itemContext = 0; + if (loadingFromSource && component) { + // disconnect since we deleteLater + QObject::disconnect(component, SIGNAL(statusChanged(QQmlComponent::Status)), + q, SLOT(_q_sourceLoaded())); + QObject::disconnect(component, SIGNAL(progressChanged(qreal)), + q, SIGNAL(progressChanged())); component->deleteLater(); component = 0; } @@ -545,6 +556,7 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj) QQml_setParent_noEvent(itemContext, obj); QQml_setParent_noEvent(item, q); item->setParentItem(q); + itemContext = 0; } if (initialPropertyValues.IsEmpty()) diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1fa0a90b28..b85a449c51 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1422,6 +1422,9 @@ void QQuickPathView::mouseUngrabEvent() d->stealMouse = false; setKeepMouseGrab(false); d->lastPosTime.invalidate(); + d->fixOffset(); + if (!d->tl.isActive()) + movementEnding(); } } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index d7eedd42b6..e232746417 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -398,12 +398,27 @@ void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status) emit statusChanged(); } +void QQuickShaderEffect::sourceDestroyed(QObject *object) +{ + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + if (object == source.sourceObject) + source.sourceObject = 0; + } +} + void QQuickShaderEffect::setSource(const QVariant &var, int index) { Q_ASSERT(index >= 0 && index < m_sources.size()); SourceData &source = m_sources[index]; + if (source.sourceObject) { + if (canvas()) + QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); + disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); + } + source.sourceObject = 0; if (var.isNull()) { return; @@ -425,16 +440,13 @@ void QQuickShaderEffect::setSource(const QVariant &var, int index) source.sourceObject = item; if (item) { - QQuickItemPrivate *d = QQuickItemPrivate::get(item); // 'item' needs a canvas to get a scene graph node. It usually gets one through its // parent, but if the source item is "inline" rather than a reference -- i.e. // "property variant source: Image { }" instead of "property variant source: foo" -- it // will not get a parent. In those cases, 'item' should get the canvas from 'this'. - if (!d->parentItem && canvas() && !d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, canvas()); - } + if (canvas()) + QQuickItemPrivate::get(item)->refCanvas(canvas()); + connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); } } @@ -512,6 +524,11 @@ void QQuickShaderEffect::reset() for (int i = 0; i < m_sources.size(); ++i) { const SourceData &source = m_sources.at(i); delete source.mapper; + if (source.sourceObject) { + if (canvas()) + QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); + disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); + } } m_sources.clear(); m_log.clear(); @@ -817,15 +834,22 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa } for (int i = 0; i < oldTextures.size(); ++i) { QSGTextureProvider *t = oldTextures.at(i).second; - if (t) + if (t) { disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } } for (int i = 0; i < m_sources.size(); ++i) { const SourceData &source = m_sources.at(i); QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0; textures.append(qMakePair(source.name, t)); - if (t) - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); + if (t) { + Q_ASSERT_X(t->thread() == QThread::currentThread(), + "QQuickShaderEffect::updatePaintNode", + "Texture provider must belong to the rendering thread"); + connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + connect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } } material->setUniforms(values); material->setTextureProviders(textures); @@ -840,12 +864,15 @@ void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &val { if (change == QQuickItem::ItemSceneChange) { // See comment in QQuickShaderEffect::setSource(). - for (int i = 0; i < m_sources.size(); ++i) { - QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject); - if (!d->parentItem && value.canvas != d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, value.canvas); + if (value.canvas) { + for (int i = 0; i < m_sources.size(); ++i) { + if (m_sources.at(i).sourceObject) + QQuickItemPrivate::get(m_sources.at(i).sourceObject)->refCanvas(value.canvas); + } + } else { + for (int i = 0; i < m_sources.size(); ++i) { + if (m_sources.at(i).sourceObject) + QQuickItemPrivate::get(m_sources.at(i).sourceObject)->derefCanvas(); } } } diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 4475c22b28..db1e4e78c1 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -133,6 +133,7 @@ private Q_SLOTS: void updateData(); void updateGeometry(); void updateLogAndStatus(const QString &log, int status); + void sourceDestroyed(QObject *object); private: friend class QQuickCustomMaterialShader; @@ -156,7 +157,7 @@ private: struct SourceData { QSignalMapper *mapper; - QPointer<QQuickItem> sourceObject; + QQuickItem *sourceObject; QByteArray name; }; QVector<SourceData> m_sources; diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index ae61ad940d..c4b91844e0 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -376,6 +376,14 @@ void QQuickShaderEffectMaterial::updateTextures() const } } +void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) +{ + for (int i = 0; i < m_textures.size(); ++i) { + if (provider == m_textures.at(i).second) + m_textures[i].second = 0; + } +} + QQuickShaderEffectNode::QQuickShaderEffectNode() : m_material(this) @@ -397,6 +405,12 @@ void QQuickShaderEffectNode::markDirtyTexture() markDirty(DirtyMaterial); } +void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object) +{ + Q_ASSERT(qobject_cast<QSGTextureProvider *>(object)); + m_material.invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); +} + void QQuickShaderEffectNode::preprocess() { Q_ASSERT(material()); diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index fc47f626e1..e22d2de9e2 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -102,6 +102,7 @@ public: void setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures); const QVector<QPair<QByteArray, QSGTextureProvider *> > &textureProviders() const; void updateTextures() const; + void invalidateTextureProvider(QSGTextureProvider *provider); protected: friend class QQuickCustomMaterialShader; @@ -143,6 +144,7 @@ Q_SIGNALS: private Q_SLOTS: void markDirtyTexture(); + void textureProviderDestroyed(QObject *object); private: QQuickShaderEffectMaterial m_material; diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index 33776be712..c55b1ca7f5 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -54,6 +54,39 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) +namespace +{ + class BindableFbo : public QSGBindable + { + public: + BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil); + virtual ~BindableFbo(); + virtual void bind() const; + private: + QOpenGLFramebufferObject *m_fbo; + QSGDepthStencilBuffer *m_depthStencil; + }; + + BindableFbo::BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil) + : m_fbo(fbo) + , m_depthStencil(depthStencil) + { + } + + BindableFbo::~BindableFbo() + { + if (m_depthStencil) + m_depthStencil->detach(); + } + + void BindableFbo::bind() const + { + m_fbo->bind(); + if (m_depthStencil) + m_depthStencil->attach(); + } +} + class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider { Q_OBJECT @@ -239,6 +272,7 @@ void QQuickShaderEffectTexture::grab() delete m_fbo; delete m_secondaryFbo; m_fbo = m_secondaryFbo = 0; + m_depthStencilBuffer.clear(); m_dirtyTexture = false; if (m_grab) emit scheduledUpdateCompleted(); @@ -272,13 +306,12 @@ void QQuickShaderEffectTexture::grab() delete m_secondaryFbo; QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); format.setInternalTextureFormat(m_format); format.setSamples(8); m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); format.setInternalTextureFormat(m_format); format.setMipmap(m_mipmap); if (m_recursive) { @@ -287,6 +320,7 @@ void QQuickShaderEffectTexture::grab() m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); updateBindOptions(true); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { delete m_fbo; delete m_secondaryFbo; @@ -294,6 +328,7 @@ void QQuickShaderEffectTexture::grab() m_secondaryFbo = 0; glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); updateBindOptions(true); + m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo); } } } @@ -336,7 +371,7 @@ void QQuickShaderEffectTexture::grab() m_renderer->setClearColor(Qt::transparent); if (m_multisampling) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data())); if (deleteFboLater) { delete m_fbo; @@ -354,7 +389,7 @@ void QQuickShaderEffectTexture::grab() QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r); } else { if (m_recursive) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + m_renderer->renderScene(BindableFbo(m_secondaryFbo, m_depthStencilBuffer.data())); if (deleteFboLater) { delete m_fbo; @@ -368,7 +403,7 @@ void QQuickShaderEffectTexture::grab() } qSwap(m_fbo, m_secondaryFbo); } else { - m_renderer->renderScene(QSGBindableFbo(m_fbo)); + m_renderer->renderScene(BindableFbo(m_fbo, m_depthStencilBuffer.data())); } } @@ -504,6 +539,8 @@ QQuickShaderEffectSource::~QQuickShaderEffectSource() QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem); sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry); sd->derefFromEffectItem(m_hideSource); + if (canvas()) + sd->derefCanvas(); } } @@ -599,6 +636,9 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem); d->derefFromEffectItem(m_hideSource); d->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*))); + if (canvas()) + d->derefCanvas(); } m_sourceItem = item; @@ -608,18 +648,25 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) // parent, but if the source item is "inline" rather than a reference -- i.e. // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent. // In those cases, 'item' should get the canvas from 'this'. - if (!d->parentItem && canvas() && !d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, canvas()); - } + if (canvas()) + d->refCanvas(canvas()); d->refFromEffectItem(m_hideSource); d->addItemChangeListener(this, QQuickItemPrivate::Geometry); + connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*))); } update(); emit sourceItemChanged(); } +void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item) +{ + Q_ASSERT(item == m_sourceItem); + m_sourceItem = 0; + update(); + emit sourceItemChanged(); +} + + /*! \qmlproperty rect ShaderEffectSource::sourceRect @@ -841,22 +888,35 @@ static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::W } +void QQuickShaderEffectSource::releaseResources() +{ + if (m_texture) { + m_texture->deleteLater(); + m_texture = 0; + } + if (m_provider) { + m_provider->deleteLater(); + m_provider = 0; + } +} + QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) { + if (m_texture) + m_texture->setItem(0); delete oldNode; return 0; } ensureTexture(); - QQuickShaderEffectTexture *tex = qobject_cast<QQuickShaderEffectTexture *>(m_texture); - tex->setLive(m_live); - tex->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode()); + m_texture->setLive(m_live); + m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode()); QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height()) : m_sourceRect; - tex->setRect(sourceRect); + m_texture->setRect(sourceRect); QSize textureSize = m_textureSize.isEmpty() ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height()))) : m_textureSize; @@ -869,13 +929,13 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint while (textureSize.height() < minTextureSize.height()) textureSize.rheight() *= 2; - tex->setSize(textureSize); - tex->setRecursive(m_recursive); - tex->setFormat(GLenum(m_format)); - tex->setHasMipmaps(m_mipmap); + m_texture->setSize(textureSize); + m_texture->setRecursive(m_recursive); + m_texture->setFormat(GLenum(m_format)); + m_texture->setHasMipmaps(m_mipmap); if (m_grab) - tex->scheduleUpdate(); + m_texture->scheduleUpdate(); m_grab = false; QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth @@ -924,12 +984,10 @@ void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeDat { if (change == QQuickItem::ItemSceneChange && m_sourceItem) { // See comment in QQuickShaderEffectSource::setSourceItem(). - QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem); - if (!d->parentItem && value.canvas != d->canvas) { - QQuickItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, value.canvas); - } + if (value.canvas) + QQuickItemPrivate::get(m_sourceItem)->refCanvas(value.canvas); + else + QQuickItemPrivate::get(m_sourceItem)->derefCanvas(); } QQuickItem::itemChange(change, value); } diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 793e89cd69..0853394339 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -136,6 +136,7 @@ private: QSGRenderer *m_renderer; QOpenGLFramebufferObject *m_fbo; QOpenGLFramebufferObject *m_secondaryFbo; + QSharedPointer<QSGDepthStencilBuffer> m_depthStencilBuffer; #ifdef QSG_DEBUG_FBO_OVERLAY QSGRectangleNode *m_debugOverlay; @@ -228,7 +229,11 @@ Q_SIGNALS: void scheduledUpdateCompleted(); +private Q_SLOTS: + void sourceItemDestroyed(QObject *item); + protected: + virtual void releaseResources(); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); virtual void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect); @@ -240,7 +245,7 @@ private: QQuickShaderEffectSourceTextureProvider *m_provider; QQuickShaderEffectTexture *m_texture; WrapMode m_wrapMode; - QPointer<QQuickItem> m_sourceItem; + QQuickItem *m_sourceItem; QRectF m_sourceRect; QSize m_textureSize; Format m_format; diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp index e0535ed77e..724bf8fef1 100644 --- a/src/quick/items/qquicksprite.cpp +++ b/src/quick/items/qquicksprite.cpp @@ -248,12 +248,14 @@ int QQuickSprite::variedDuration() const //Deals with precedence when multiple d + (m_frameDurationVariation * ((qreal)qrand()/RAND_MAX) * 2) - m_frameDurationVariation; return qMax(0, m_frames * mspf); - } - qWarning() << "Sprite::duration is changing meaning to the full animation duration."; - qWarning() << "Use Sprite::frameDuration for the old meaning, of per frame duration."; - qWarning() << "As an interim measure, duration/durationVariation means the same as frameDuration/frameDurationVariation, and you'll get this warning spewed out everywhere to movtivate you."; + } else if (duration() >= 0) { + qWarning() << "Sprite::duration is changing meaning to the full animation duration."; + qWarning() << "Use Sprite::frameDuration for the old meaning, of per frame duration."; + qWarning() << "As an interim measure, duration/durationVariation means the same as frameDuration/frameDurationVariation, and you'll get this warning spewed out everywhere to motivate you."; //Note that the spammyness is due to this being the best location to detect, but also called once each animation loop - return QQuickStochasticState::variedDuration() * m_frames; + return QQuickStochasticState::variedDuration() * m_frames; + } + return 1000; //When nothing set } void QQuickSprite::startImageLoading() diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 1c35688c29..d4ddbc400d 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -411,9 +411,9 @@ QImage QQuickSpriteEngine::assembledImage() int frameWidth = state->m_frameWidth; int frameHeight = state->m_frameHeight; if (img.height() == frameHeight && img.width() < maxSize){//Simple case - p.drawImage(0,y,img); + p.drawImage(0,y,img.copy(state->m_frameX,0,state->m_frames * frameWidth, frameHeight)); + state->m_rowStartX = 0; state->m_rowY = y; - state->m_rowStartX = state->m_frameX;//In case it was offset, but we took the simple route of not chopping out the other bits y += frameHeight; }else{//Chopping up image case state->m_framesPerRow = image.width()/frameWidth; diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 3763509462..00cefbfbc0 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -70,7 +70,7 @@ class Q_AUTOTEST_EXPORT QQuickStochasticState : public QObject //Currently for i public: QQuickStochasticState(QObject* parent = 0) : QObject(parent) - , m_duration(1000) + , m_duration(-1) , m_durationVariation(0) , m_randomStart(false) { diff --git a/src/quick/items/qquickspriteimage.cpp b/src/quick/items/qquickspriteimage.cpp index 6edb3ad8c3..2a151d02a8 100644 --- a/src/quick/items/qquickspriteimage.cpp +++ b/src/quick/items/qquickspriteimage.cpp @@ -261,7 +261,7 @@ struct SpriteVertices { The sprite or sprites to draw. Sprites will be scaled to the size of this element. */ -//TODO: Implicitly size element to size of first sprite? +//TODO: Implicitly size element to size of first sprite? or currentSprite? QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) : QQuickItem(parent) , m_node(0) diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 9f22dfdd08..d7303352c5 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -421,6 +421,10 @@ void QQuickTextPrivate::updateSize() //setup instance of QTextLayout for all cases other than richtext if (!richText) { QRectF textRect = setupTextLayout(&naturalWidth); + + if (internalWidthUpdate) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect if it is. + layedOutTextRect = textRect; size = textRect.size(); dy -= size.height(); @@ -443,7 +447,13 @@ void QQuickTextPrivate::updateSize() if (requireImplicitWidth && q->widthValid()) { extra->doc->setTextWidth(-1); naturalWidth = extra->doc->idealWidth(); + const bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(naturalWidth); + internalWidthUpdate = wasInLayout; } + if (internalWidthUpdate) + return; if (wrapMode != QQuickText::NoWrap && q->widthValid()) extra->doc->setTextWidth(q->width()); else @@ -468,8 +478,6 @@ void QQuickTextPrivate::updateSize() qreal iWidth = -1; if (!q->widthValid()) iWidth = size.width(); - else if (requireImplicitWidth) - iWidth = naturalWidth; if (iWidth > -1) q->setImplicitSize(iWidth, size.height()); internalWidthUpdate = false; @@ -697,6 +705,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) layout.endLayout(); *naturalWidth = layout.maximumWidth(); layout.clearLayout(); + + bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(*naturalWidth); + internalWidthUpdate = wasInLayout; } QFontMetrics fm(font); @@ -704,7 +717,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) return QRect(0, 0, 0, height); } - const qreal lineWidth = q->widthValid() ? q->width() : FLT_MAX; + qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX; const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX; const bool customLayout = isLineLaidOutConnected(); @@ -735,6 +748,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) QTextLine line; int visibleCount = 0; bool elide; + bool widthChanged; qreal height = 0; QString elideText; bool once = true; @@ -755,13 +769,14 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) scaledFont.setPointSize(scaledFontSize); layout.setFont(scaledFont); } - layout.beginLayout(); + layout.beginLayout(); bool wrapped = false; bool truncateHeight = false; truncated = false; elide = false; + widthChanged = false; int characterCount = 0; int unwrappedLineCount = 1; int maxLineCount = maximumLineCount(); @@ -864,7 +879,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) br = br.united(line.naturalTextRect()); line = nextLine; } - layout.endLayout(); br.moveTop(0); @@ -886,8 +900,21 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) if (!line.isValid()) break; } + *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth()); } + + bool wasInLayout = internalWidthUpdate; + internalWidthUpdate = true; + q->setImplicitWidth(*naturalWidth); + internalWidthUpdate = wasInLayout; + + const qreal oldWidth = lineWidth; + lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX; + if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) { + widthChanged = true; + continue; + } } // If the next needs to be elided and there's an abbreviated string available @@ -911,32 +938,36 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) if (horizontalFit) { if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) { largeFont = scaledFontSize - 1; - scaledFontSize = (smallFont + largeFont) / 2; if (smallFont > largeFont) break; + scaledFontSize = (smallFont + largeFont) / 2; + if (pixelSize) + scaledFont.setPixelSize(scaledFontSize); + else + scaledFont.setPointSize(scaledFontSize); continue; } else if (!verticalFit) { smallFont = scaledFontSize; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont == largeFont) break; + scaledFontSize = (smallFont + largeFont + 1) / 2; } } if (verticalFit) { if (truncateHeight || unelidedRect.height() > maxHeight) { largeFont = scaledFontSize - 1; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont > largeFont) break; + scaledFontSize = (smallFont + largeFont) / 2; + } else { smallFont = scaledFontSize; - scaledFontSize = (smallFont + largeFont + 1) / 2; if (smallFont == largeFont) break; + scaledFontSize = (smallFont + largeFont + 1) / 2; } } - } if (eos != multilengthEos) diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 9a61312910..1846d03b9b 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -531,29 +531,12 @@ void QQuickTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, if (!wordSelectionEnabled && (mouseXPosition < wordStartX || mouseXPosition > wordEndX)) return; - if (wordSelectionEnabled) { - if (suggestedNewPosition < selectedWordOnDoubleClick.position()) { - cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); - setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); - } else { - cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); - setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); - } + if (suggestedNewPosition < selectedWordOnDoubleClick.position()) { + cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); + setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); } else { - // keep the already selected word even when moving to the left - // (#39164) - if (suggestedNewPosition < selectedWordOnDoubleClick.position()) - cursor.setPosition(selectedWordOnDoubleClick.selectionEnd()); - else - cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); - - const qreal differenceToStart = mouseXPosition - wordStartX; - const qreal differenceToEnd = wordEndX - mouseXPosition; - - if (differenceToStart < differenceToEnd) - setCursorPosition(wordStartPos, QTextCursor::KeepAnchor); - else - setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); + cursor.setPosition(selectedWordOnDoubleClick.selectionStart()); + setCursorPosition(wordEndPos, QTextCursor::KeepAnchor); } if (interactionFlags & Qt::TextSelectableByMouse) { @@ -594,13 +577,6 @@ void QQuickTextControlPrivate::extendBlockwiseSelection(int suggestedNewPosition } } -void QQuickTextControlPrivate::_q_deleteSelected() -{ - if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection()) - return; - cursor.removeSelectedText(); -} - void QQuickTextControl::undo() { Q_D(QQuickTextControl); @@ -690,14 +666,6 @@ void QQuickTextControl::paste(QClipboard::Mode mode) } #endif -void QQuickTextControl::clear() -{ - Q_D(QQuickTextControl); - // clears and sets empty content - d->setContent(); -} - - void QQuickTextControl::selectAll() { Q_D(QQuickTextControl); @@ -1527,13 +1495,6 @@ QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property) cons } } -void QQuickTextControl::setFocus(bool focus, Qt::FocusReason reason) -{ - QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut, - reason); - processEvent(&ev); -} - void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) { Q_Q(QQuickTextControl); @@ -1555,31 +1516,6 @@ void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) } } -QString QQuickTextControlPrivate::anchorForCursor(const QTextCursor &anchorCursor) const -{ - if (anchorCursor.hasSelection()) { - QTextCursor cursor = anchorCursor; - if (cursor.selectionStart() != cursor.position()) - cursor.setPosition(cursor.selectionStart()); - cursor.movePosition(QTextCursor::NextCharacter); - QTextCharFormat fmt = cursor.charFormat(); - if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) - return fmt.stringProperty(QTextFormat::AnchorHref); - } - return QString(); -} - -QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const -{ - Q_D(const QQuickTextControl); - int cursorPos = hitTest(pos, Qt::FuzzyHit); - if (cursorPos == -1) - cursorPos = 0; - QTextCursor c(d->doc); - c.setPosition(cursorPos); - return c; -} - QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const { Q_D(const QQuickTextControl); @@ -1609,23 +1545,6 @@ QString QQuickTextControl::anchorAt(const QPointF &pos) const return d->doc->documentLayout()->anchorAt(pos); } -QString QQuickTextControl::anchorAtCursor() const -{ - Q_D(const QQuickTextControl); - - return d->anchorForCursor(d->cursor); -} - -int QQuickTextControl::cursorWidth() const -{ -#ifndef QT_NO_PROPERTIES - Q_D(const QQuickTextControl); - return d->doc->documentLayout()->property("cursorWidth").toInt(); -#else - return 1; -#endif -} - void QQuickTextControl::setCursorWidth(int width) { Q_D(QQuickTextControl); @@ -1639,36 +1558,12 @@ void QQuickTextControl::setCursorWidth(int width) d->repaintCursor(); } -bool QQuickTextControl::acceptRichText() const -{ - Q_D(const QQuickTextControl); - return d->acceptRichText; -} - void QQuickTextControl::setAcceptRichText(bool accept) { Q_D(QQuickTextControl); d->acceptRichText = accept; } -void QQuickTextControl::setTextWidth(qreal width) -{ - Q_D(QQuickTextControl); - d->doc->setTextWidth(width); -} - -qreal QQuickTextControl::textWidth() const -{ - Q_D(const QQuickTextControl); - return d->doc->textWidth(); -} - -QSizeF QQuickTextControl::size() const -{ - Q_D(const QQuickTextControl); - return d->doc->size(); -} - void QQuickTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode) { Q_D(QQuickTextControl); @@ -1700,24 +1595,12 @@ void QQuickTextControl::setCursorIsFocusIndicator(bool b) d->repaintCursor(); } -bool QQuickTextControl::cursorIsFocusIndicator() const -{ - Q_D(const QQuickTextControl); - return d->cursorIsFocusIndicator; -} - void QQuickTextControl::setWordSelectionEnabled(bool enabled) { Q_D(QQuickTextControl); d->wordSelectionEnabled = enabled; } -bool QQuickTextControl::isWordSelectionEnabled() const -{ - Q_D(const QQuickTextControl); - return d->wordSelectionEnabled; -} - QMimeData *QQuickTextControl::createMimeDataFromSelection() const { Q_D(const QQuickTextControl); diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 97ecdc4c6e..9e3fc90eb1 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -80,12 +80,6 @@ class Q_AUTOTEST_EXPORT QQuickTextControl : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QQuickTextControl) -#ifndef QT_NO_TEXTHTMLPARSER - Q_PROPERTY(QString html READ toHtml WRITE setHtml NOTIFY textChanged USER true) -#endif - Q_PROPERTY(bool acceptRichText READ acceptRichText WRITE setAcceptRichText) - Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth) - Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags WRITE setTextInteractionFlags) public: explicit QQuickTextControl(QTextDocument *doc, QObject *parent = 0); virtual ~QQuickTextControl(); @@ -104,7 +98,6 @@ public: QString toHtml() const; #endif - QTextCursor cursorForPosition(const QPointF &pos) const; QRectF cursorRect(const QTextCursor &cursor) const; QRectF cursorRect() const; QRectF selectionRect(const QTextCursor &cursor) const; @@ -112,26 +105,15 @@ public: QString anchorAt(const QPointF &pos) const; - QString anchorAtCursor() const; - - int cursorWidth() const; void setCursorWidth(int width); - bool acceptRichText() const; void setAcceptRichText(bool accept); - void setTextWidth(qreal width); - qreal textWidth() const; - QSizeF size() const; - void moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor); bool canPaste() const; void setCursorIsFocusIndicator(bool b); - bool cursorIsFocusIndicator() const; - - bool isWordSelectionEnabled() const; void setWordSelectionEnabled(bool enabled); virtual int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const; @@ -151,7 +133,6 @@ public Q_SLOTS: void undo(); void redo(); - void clear(); void selectAll(); Q_SIGNALS: @@ -175,9 +156,6 @@ public: virtual void processEvent(QEvent *e, const QMatrix &matrix); void processEvent(QEvent *e, const QPointF &coordinateOffset = QPointF()); - // control methods - void setFocus(bool focus, Qt::FocusReason = Qt::OtherFocusReason); - virtual QVariant inputMethodQuery(Qt::InputMethodQuery property) const; virtual QMimeData *createMimeDataFromSelection() const; @@ -195,7 +173,6 @@ private: Q_DISABLE_COPY(QQuickTextControl) Q_PRIVATE_SLOT(d_func(), void _q_updateCurrentCharFormatAndSelection()) Q_PRIVATE_SLOT(d_func(), void _q_emitCursorPosChanged(const QTextCursor &)) - Q_PRIVATE_SLOT(d_func(), void _q_deleteSelected()) Q_PRIVATE_SLOT(d_func(), void _q_updateBlock(const QTextBlock &)) Q_PRIVATE_SLOT(d_func(), void _q_documentLayoutChanged()) }; diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h index 44bc00221b..9d776ce46b 100644 --- a/src/quick/items/qquicktextcontrol_p_p.h +++ b/src/quick/items/qquicktextcontrol_p_p.h @@ -106,8 +106,6 @@ public: void extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition); void extendBlockwiseSelection(int suggestedNewPosition); - void _q_deleteSelected(); - void _q_setCursorAfterUndoRedo(int undoPosition, int charsAdded, int charsRemoved); QRectF cursorRectPlusUnicodeDirectionMarkers(const QTextCursor &cursor) const; @@ -116,8 +114,6 @@ public: inline QRectF selectionRect() const { return selectionRect(this->cursor); } - QString anchorForCursor(const QTextCursor &anchor) const; - void keyPressEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *event, const QPointF &pos); void mouseMoveEvent(QMouseEvent *event, const QPointF &pos); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 5456d3523a..4fa5233b9a 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1143,7 +1143,8 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints) void QQuickTextEdit::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (newGeometry.width() != oldGeometry.width()) + Q_D(QQuickTextEdit); + if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap && !d->inLayout) updateSize(); QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } @@ -1477,6 +1478,7 @@ Handles the given mouse \a event. void QQuickTextEdit::mousePressEvent(QMouseEvent *event) { Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); if (d->focusOnPress){ bool hadActiveFocus = hasActiveFocus(); forceActiveFocus(); @@ -1484,7 +1486,6 @@ void QQuickTextEdit::mousePressEvent(QMouseEvent *event) if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) openSoftwareInputPanel(); } - d->control->processEvent(event, QPointF(0, -d->yoff)); if (!event->isAccepted()) QQuickImplicitSizeItem::mousePressEvent(event); } @@ -1857,6 +1858,13 @@ void QQuickTextEdit::updateSize() if (d->requireImplicitWidth) { d->document->setTextWidth(-1); naturalWidth = d->document->idealWidth(); + + const bool wasInLayout = d->inLayout; + d->inLayout = true; + setImplicitWidth(naturalWidth); + d->inLayout = wasInLayout; + if (d->inLayout) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect. } if (d->document->textWidth() != width()) d->document->setTextWidth(width()); @@ -1888,11 +1896,11 @@ void QQuickTextEdit::updateSize() d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. qreal iWidth = -1; - if (!widthValid()) + if (!widthValid() && !d->requireImplicitWidth) iWidth = newWidth; - else if (d->requireImplicitWidth) - iWidth = naturalWidth; + qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height(); + if (iWidth > -1) setImplicitSize(iWidth, newHeight); else diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 055b5c7929..f0a35d5266 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -78,7 +78,7 @@ public: , documentDirty(true), dirty(false), richText(false), cursorVisible(false) , focusOnPress(true), persistentSelection(false), requireImplicitWidth(false) , selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true) - , rightToLeftText(false), textCached(false) + , rightToLeftText(false), textCached(false), inLayout(false) { } @@ -144,6 +144,7 @@ public: bool hAlignImplicit:1; bool rightToLeftText:1; bool textCached:1; + bool inLayout:1; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 0b21d6b169..94856b63ab 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1444,13 +1444,9 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) d->pressPos = event->localPos(); - if (d->focusOnPress) { - bool hadActiveFocus = hasActiveFocus(); - forceActiveFocus(); - // re-open input panel on press if already focused - if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) - openSoftwareInputPanel(); - } + if (d->sendMouseEventToInputContext(event)) + return; + if (d->selectByMouse) { setKeepMouseGrab(false); d->selectPressed = true; @@ -1463,12 +1459,18 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) } } - if (d->sendMouseEventToInputContext(event)) - return; - bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; int cursor = d->positionAt(event->localPos()); d->moveCursor(cursor, mark); + + if (d->focusOnPress) { + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + // re-open input panel on press if already focused + if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) + openSoftwareInputPanel(); + } + event->setAccepted(true); } @@ -1602,9 +1604,11 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickTextInput); - if (newGeometry.width() != oldGeometry.width()) - d->updateLayout(); - updateCursorRectangle(); + if (!d->inLayout) { + if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap) + d->updateLayout(); + updateCursorRectangle(); + } QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } @@ -1614,14 +1618,19 @@ void QQuickTextInputPrivate::updateHorizontalScroll() QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor); const int preeditLength = m_textLayout.preeditAreaText().length(); const qreal width = qMax<qreal>(0, q->width()); - qreal widthUsed = currentLine.isValid() ? currentLine.naturalTextWidth() : 0; + qreal cix = 0; + qreal widthUsed = 0; + if (currentLine.isValid()) { + cix = currentLine.cursorToX(m_cursor + preeditLength); + const qreal cursorWidth = cix >= 0 ? cix : width - cix; + widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth); + } int previousScroll = hscroll; if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) { hscroll = 0; } else { Q_ASSERT(currentLine.isValid()); - qreal cix = currentLine.cursorToX(m_cursor + preeditLength); if (cix - hscroll >= width) { // text doesn't fit, cursor is to the right of br (scroll right) hscroll = cix - width; @@ -1632,6 +1641,10 @@ void QQuickTextInputPrivate::updateHorizontalScroll() // text doesn't fit, text document is to the left of br; align // right hscroll = widthUsed - width; + } else if (width - hscroll > widthUsed) { + // text doesn't fit, text document is to the right of br; align + // left + hscroll = width - widthUsed; } if (preeditLength > 0) { // check to ensure long pre-edit text doesn't push the cursor @@ -2688,6 +2701,38 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate) } } +qreal QQuickTextInputPrivate::getImplicitWidth() const +{ + Q_Q(const QQuickTextInput); + if (!requireImplicitWidth) { + QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this); + d->requireImplicitWidth = true; + + if (q->isComponentComplete()) { + // One time cost, only incurred if implicitWidth is first requested after + // componentComplete. + QTextLayout layout(m_text); + + QTextOption option = m_textLayout.textOption(); + option.setTextDirection(m_layoutDirection); + option.setFlags(QTextOption::IncludeTrailingSpaces); + option.setWrapMode(QTextOption::WrapMode(wrapMode)); + option.setAlignment(Qt::Alignment(q->effectiveHAlign())); + layout.setTextOption(option); + layout.setFont(font); + layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText()); + layout.beginLayout(); + + QTextLine line = layout.createLine(); + line.setLineWidth(INT_MAX); + d->implicitWidth = qCeil(line.naturalTextWidth()); + + layout.endLayout(); + } + } + return implicitWidth; +} + void QQuickTextInputPrivate::updateLayout() { Q_Q(QQuickTextInput); @@ -2699,7 +2744,6 @@ void QQuickTextInputPrivate::updateLayout() QTextOption option = m_textLayout.textOption(); option.setTextDirection(layoutDirection()); - option.setFlags(QTextOption::IncludeTrailingSpaces); option.setWrapMode(QTextOption::WrapMode(wrapMode)); option.setAlignment(Qt::Alignment(q->effectiveHAlign())); m_textLayout.setTextOption(option); @@ -2708,9 +2752,17 @@ void QQuickTextInputPrivate::updateLayout() boundingRect = QRectF(); m_textLayout.beginLayout(); QTextLine line = m_textLayout.createLine(); + if (requireImplicitWidth) { + line.setLineWidth(INT_MAX); + const bool wasInLayout = inLayout; + inLayout = true; + q->setImplicitWidth(qCeil(line.naturalTextWidth())); + inLayout = wasInLayout; + if (inLayout) // probably the result of a binding loop, but by letting it + return; // get this far we'll get a warning to that effect. + } qreal lineWidth = q->widthValid() ? q->width() : INT_MAX; qreal height = 0; - QTextLine firstLine = line; do { line.setLineWidth(lineWidth); line.setPosition(QPointF(line.position().x(), height)); @@ -2728,7 +2780,11 @@ void QQuickTextInputPrivate::updateLayout() updateType = UpdatePaintNode; q->update(); - q->setImplicitSize(boundingRect.width(), boundingRect.height()); + + if (!requireImplicitWidth && !q->widthValid()) + q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height())); + else + q->setImplicitHeight(qCeil(boundingRect.height())); if (previousRect != boundingRect) emit q->contentSizeChanged(); @@ -3261,7 +3317,14 @@ void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool e m_textDirty = (oldText != m_text); bool changed = finishChange(-1, true, edited); +#ifdef QT_NO_ACCESSIBILITY Q_UNUSED(changed) +#else + if (changed) { + QAccessibleTextUpdateEvent ev(q, 0, oldText, m_text); + QAccessible::updateAccessibility(&ev); + } +#endif } @@ -3885,6 +3948,11 @@ bool QQuickTextInputPrivate::emitCursorPositionChanged() } } +#ifndef QT_NO_ACCESSIBILITY + QAccessibleTextCursorEvent ev(q, m_cursor); + QAccessible::updateAccessibility(&ev); +#endif + return true; } return false; @@ -3964,12 +4032,10 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) } #ifndef QT_NO_SHORTCUT else if (event == QKeySequence::Undo) { - if (!m_readOnly) - q->undo(); + q->undo(); } else if (event == QKeySequence::Redo) { - if (!m_readOnly) - q->redo(); + q->redo(); } else if (event == QKeySequence::SelectAll) { selectAll(); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index bb00600661..165155acd0 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -125,6 +125,8 @@ public: , m_acceptableInput(1) , m_blinkStatus(0) , m_passwordEchoEditing(false) + , inLayout(false) + , requireImplicitWidth(false) { } @@ -256,7 +258,8 @@ public: bool m_acceptableInput : 1; bool m_blinkStatus : 1; bool m_passwordEchoEditing : 1; - + bool inLayout:1; + bool requireImplicitWidth:1; static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { return t->d_func(); @@ -404,6 +407,8 @@ public: void updateLayout(); + qreal getImplicitWidth() const; + private: void removeSelectedText(); void internalSetText(const QString &txt, int pos = -1, bool edited = true); diff --git a/src/quick/items/qquickvisualadaptormodel.cpp b/src/quick/items/qquickvisualadaptormodel.cpp index 622adf4ce9..cd9db7235b 100644 --- a/src/quick/items/qquickvisualadaptormodel.cpp +++ b/src/quick/items/qquickvisualadaptormodel.cpp @@ -65,7 +65,8 @@ public: } VDMDelegateDataType(const VDMDelegateDataType &type) - : metaObject(0) + : QQmlRefCount() + , metaObject(0) , propertyCache(0) , propertyOffset(type.propertyOffset) , signalOffset(type.signalOffset) diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp index 64eb2bf53b..61c2ef24b4 100644 --- a/src/quick/items/qquickwindowmanager.cpp +++ b/src/quick/items/qquickwindowmanager.cpp @@ -228,7 +228,7 @@ public slots: private: void handleAddedWindows(); void handleAddedWindow(QQuickCanvas *canvas); - void handleRemovedWindows(); + void handleRemovedWindows(bool clearGLContext = true); QSGContext *sg; QOpenGLContext *gl; @@ -475,7 +475,7 @@ void QQuickRenderThreadSingleContextWindowManager::hide(QQuickCanvas *canvas) /*! Called on Render Thread */ -void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows() +void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext) { #ifdef THREAD_DEBUG printf(" RenderThread: about to remove %d\n", m_removed_windows.size()); @@ -496,7 +496,7 @@ void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows() // If a window is removed because it has been hidden it will take with it // the gl context (at least on Mac) if bound, so disconnect the gl context // from anything - if (removedAnything) + if (removedAnything && clearGLContext) gl->doneCurrent(); } @@ -755,7 +755,7 @@ void QQuickRenderThreadSingleContextWindowManager::run() #endif m_removed_windows << m_rendered_windows.keys(); - handleRemovedWindows(); + handleRemovedWindows(false); sg->invalidate(); |