diff options
Diffstat (limited to 'src/quick')
52 files changed, 532 insertions, 344 deletions
diff --git a/src/quick/accessible/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp index 222690e4f2..b3d1b6fc0f 100644 --- a/src/quick/accessible/qaccessiblequickview.cpp +++ b/src/quick/accessible/qaccessiblequickview.cpp @@ -81,6 +81,14 @@ QAccessibleInterface *QAccessibleQuickWindow::child(int index) const return 0; } +QAccessibleInterface *QAccessibleQuickWindow::focusChild() const +{ + QObject *focusObject = window()->focusObject(); + if (focusObject) + return QAccessible::queryAccessibleInterface(focusObject); + return nullptr; +} + QAccessible::Role QAccessibleQuickWindow::role() const { return QAccessible::Window; // FIXME diff --git a/src/quick/accessible/qaccessiblequickview_p.h b/src/quick/accessible/qaccessiblequickview_p.h index 7c103380cb..39ffcaf39c 100644 --- a/src/quick/accessible/qaccessiblequickview_p.h +++ b/src/quick/accessible/qaccessiblequickview_p.h @@ -65,6 +65,7 @@ public: QAccessibleInterface *parent() const override; QAccessibleInterface *child(int index) const override; + QAccessibleInterface *focusChild() const override; QAccessible::Role role() const override; QAccessible::State state() const override; diff --git a/src/quick/configure.json b/src/quick/configure.json index 4ed11e8318..047fa8c948 100644 --- a/src/quick/configure.json +++ b/src/quick/configure.json @@ -51,6 +51,7 @@ "quick-canvas": { "label": "Canvas item", "purpose": "Provides the Qt Quick Canvas Item", + "condition": "features.quick-path", "output": [ "privateFeature" ] @@ -97,6 +98,14 @@ "privateFeature" ] }, + "quick-particles": { + "label": "Particle support", + "purpose": "Provides a particle system for Qt Quick", + "condition": "features.quick-shadereffect && features.quick-sprite && features.opengl", + "output": [ + "privateFeature" + ] + }, "quick-path": { "label": "Path support", "purpose": "Provides Path elements in Qt Quick", diff --git a/src/quick/doc/snippets/qml/localstorage/hello.qml b/src/quick/doc/snippets/qml/localstorage/hello.qml new file mode 100644 index 0000000000..959895c82a --- /dev/null +++ b/src/quick/doc/snippets/qml/localstorage/hello.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 + +Rectangle { + color: "white" + width: 200 + height: 100 + + Text { + text: "?" + anchors.horizontalCenter: parent.horizontalCenter + function findGreetings() { + var db = openDatabaseSync("QDeclarativeExampleDB", "1.0", "The Example QML SQL!", 1000000); + + db.transaction( + function(tx) { + // Create the database if it doesn't already exist + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + + // Add (another) greeting row + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + + // Show all added greetings + var rs = tx.executeSql('SELECT * FROM Greeting'); + + var r = "" + for (var i = 0; i < rs.rows.length; i++) { + r += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + "\n" + } + text = r + } + ) + } + Component.onCompleted: findGreetings() + } +} +//![0] diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc index 20a6d131f5..e5834eb5c8 100644 --- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc @@ -64,7 +64,7 @@ In addition to the above features, GridLayout adds these features: \list \li \l{Layout::row}{Grid coordinates} can be specified with the \l{Layout::row}{Layout.row} and - \l{Layout::column}{Layout.column}. + \l{Layout::column}{Layout.column} properties. \li \l{GridLayout::flow}{Automatic grid coordinates} used together with the \l{GridLayout::flow}{flow}, \l{GridLayout::rows}{rows}, and \l{GridLayout::columns}{columns} properties. diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index a764402c2f..12a107491a 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -36,10 +36,10 @@ Models can be defined in C++ and then made available to QML. This is useful for exposing existing C++ data models or otherwise complex datasets to QML. -A C++ model class can be defined as a \l QStringList, a QObjectList or a -\l QAbstractItemModel. The first two are useful for exposing simpler datasets, -while QAbstractItemModel provides a more flexible solution for more complex -models. +A C++ model class can be defined as a \l QStringList, a \l QVariantList, a +QObjectList or a \l QAbstractItemModel. The first three are useful for exposing +simpler datasets, while QAbstractItemModel provides a more flexible solution for +more complex models. \section2 QStringList-based Model @@ -60,10 +60,20 @@ The complete source code for this example is available in \l {models/stringlistmodel}{examples/quick/models/stringlistmodel} within the Qt install directory. -\b{Note:} There is no way for the view to know that the contents of a QStringList -have changed. If the QStringList changes, it will be necessary to reset +\note There is no way for the view to know that the contents of a QStringList +have changed. If the QStringList changes, it will be necessary to reset the model by calling QQmlContext::setContextProperty() again. +\section2 QVariantList-based Model + +A model may be a single \l QVariantList, which provides the contents of the list +via the \e modelData role. + +The API works just like with \l QStringList, as shown in the previous section. + +\note There is no way for the view to know that the contents of a QVariantList +have changed. If the QVariantList changes, it will be necessary to reset +the model. \section2 QObjectList-based model diff --git a/src/quick/doc/src/concepts/positioning/topic.qdoc b/src/quick/doc/src/concepts/positioning/topic.qdoc index 92113ece54..b28acd1f89 100644 --- a/src/quick/doc/src/concepts/positioning/topic.qdoc +++ b/src/quick/doc/src/concepts/positioning/topic.qdoc @@ -112,7 +112,7 @@ them, and where possible, pristine Anchor layouts should be preferred. \section1 Anchors -Anchors allows an item to be placed either adjacent to or inside of another, +Anchors allow an item to be placed either adjacent to or inside of another, by attaching one or more of the item's anchor-points (boundaries) to an anchor-point of the other. These anchors will remain even if the dimensions or location of one of the items changes, allowing for highly dynamic diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index 3fd92177f9..d80c72e0f1 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -31,18 +31,14 @@ \section1 Scene Graph Adaptations in Qt Quick -Originally Qt Quick only had one available renderer for parsing the scene graph -and rendering the results to a render target. This renderer is now the default -OpenGL Renderer which supports rendering either using the OpenGL ES 2.0 or -OpenGL 2.0 (with framebuffer object extensions) APIs. The Qt Quick APIs have -originally been designed with the assumption that OpenGL is always available. -However, it is now possible to use other graphics API's to render Qt Quick -scenes using the scene graph APIs. +Originally Qt Quick always relied on OpenGL (OpenGL ES 2.0 or OpenGL 2.0) for parsing +the scene graph and rendering the results to a render target. From Qt 5.8 onwards +Qt Quick also supports rendering in software and with Direct3D 12. \section1 Switching between the adaptation used by the application -The default of the OpenGL, or - in Qt builds with disabled OpenGL support - the -software adaptation, can be overridden either by using an environment variable +The default rendering backend is still OpenGL, or - in Qt builds with disabled OpenGL support - +the software renderer. This can be overridden either by using an environment variable or a C++ API. The former consists of setting the \c{QT_QUICK_BACKEND} or the legacy \c{QMLSCENE_DEVICE} environment variable before launching applications. The latter is done by calling QQuickWindow::setSceneGraphBackend() early in the diff --git a/src/quick/doc/src/concepts/visualcanvas/visualparent.qdoc b/src/quick/doc/src/concepts/visualcanvas/visualparent.qdoc index fd5bf51307..f971043b58 100644 --- a/src/quick/doc/src/concepts/visualcanvas/visualparent.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/visualparent.qdoc @@ -125,7 +125,7 @@ the blue rectangle and beneath any of the blue rectangle's children. Stacking order can be influenced with the \l Item::z property. Z values below 0 will stack below the parent, and if z values are assigned then siblings will stack in z-order (with creation order used to break ties). Z values only affect -stacking compared to siblings and the parent item. If you have an item who is obscured by a subtree rooted above its +stacking compared to siblings and the parent item. If you have an item which is obscured by a subtree rooted above its parent item, no z value on that item will increase its stacking order to stack above that subtree. To stack that item above the other subtree you'll have to alter z values farther up in the hierarchy, or re-arrange the visual item hierarchy. diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 1167f408f5..da9379e7af 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -44,8 +44,10 @@ #include <private/qquickcontext2d_p.h> #include <private/qquickcontext2dtexture_p.h> #include <private/qsgadaptationlayer_p.h> +#include <qsgtextureprovider.h> #include <QtQuick/private/qquickpixmapcache_p.h> #include <QtGui/QGuiApplication> +#include <qsgtextureprovider.h> #include <qqmlinfo.h> #include <private/qqmlengine_p.h> diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b9b701313e..db9b1acbdf 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -42,13 +42,16 @@ #include "qquickcanvasitem_p.h" #include <private/qquickcontext2dtexture_p.h> #include <private/qquickitem_p.h> +#if QT_CONFIG(quick_shadereffect) #include <QtQuick/private/qquickshadereffectsource_p.h> +#endif #include <qsgrendererinterface.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qquicksvgparser_p.h> +#if QT_CONFIG(quick_path) #include <private/qquickpath_p.h> - +#endif #include <private/qquickimage_p_p.h> #include <qqmlinfo.h> @@ -126,8 +129,6 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); -#define DEGREES(t) ((t) * 180.0 / M_PI) - #define CHECK_CONTEXT(r) if (!r || !r->d()->context || !r->d()->context->bufferValid()) \ THROW_GENERIC_ERROR("Not a Context2D object"); @@ -567,9 +568,10 @@ struct QQuickJSContext2D : public QV4::Object static void method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); // should these two be on the proto? +#if QT_CONFIG(quick_path) static void method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_set_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - +#endif static void method_get_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_set_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_get_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); @@ -1639,7 +1641,7 @@ void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::Builtin if (callData->argc >= 3) { qreal x = callData->args[0].toNumber(); qreal y = callData->args[1].toNumber(); - qreal angle = DEGREES(callData->args[2].toNumber()); + qreal angle = qRadiansToDegrees(callData->args[2].toNumber()); if (!qt_is_finite(x) || !qt_is_finite(y)) { THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments"); } @@ -2032,6 +2034,7 @@ void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, Q RETURN_UNDEFINED(); } +#if QT_CONFIG(quick_path) void QQuickJSContext2D::method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); @@ -2058,6 +2061,7 @@ void QQuickJSContext2D::method_set_path(const QV4::BuiltinFunction *, QV4::Scope r->d()->context->m_v4path.set(scope.engine, value); RETURN_UNDEFINED(); } +#endif // QT_CONFIG(quick_path) //rects /*! @@ -3364,7 +3368,7 @@ void QQuickContext2D::rotate(qreal angle) return; QTransform newTransform =state.matrix; - newTransform.rotate(DEGREES(angle)); + newTransform.rotate(qRadiansToDegrees(angle)); if (!newTransform.isInvertible()) { state.invertibleCTM = false; @@ -3373,7 +3377,7 @@ void QQuickContext2D::rotate(qreal angle) state.matrix = newTransform; buffer()->updateMatrix(state.matrix); - m_path = QTransform().rotate(-DEGREES(angle)).map(m_path); + m_path = QTransform().rotate(-qRadiansToDegrees(angle)).map(m_path); } void QQuickContext2D::shear(qreal h, qreal v) @@ -3770,8 +3774,8 @@ void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear antiClockWise = !antiClockWise; //end hack - float sa = DEGREES(sar); - float ea = DEGREES(ear); + float sa = qRadiansToDegrees(sar); + float ea = qRadiansToDegrees(ear); double span = 0; @@ -4208,7 +4212,9 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4) proto->defineAccessorProperty(QStringLiteral("fillStyle"), QQuickJSContext2D::method_get_fillStyle, QQuickJSContext2D::method_set_fillStyle); proto->defineAccessorProperty(QStringLiteral("shadowColor"), QQuickJSContext2D::method_get_shadowColor, QQuickJSContext2D::method_set_shadowColor); proto->defineAccessorProperty(QStringLiteral("textBaseline"), QQuickJSContext2D::method_get_textBaseline, QQuickJSContext2D::method_set_textBaseline); +#if QT_CONFIG(quick_path) proto->defineAccessorProperty(QStringLiteral("path"), QQuickJSContext2D::method_get_path, QQuickJSContext2D::method_set_path); +#endif proto->defineAccessorProperty(QStringLiteral("lineJoin"), QQuickJSContext2D::method_get_lineJoin, QQuickJSContext2D::method_set_lineJoin); proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth); proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign); diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 38b5f054bf..d90f527486 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -48,6 +48,7 @@ #include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObjectFormat> #include <QOpenGLFunctions> +#include <QtGui/private/qopenglextensions_p.h> #endif #include <QtCore/QThread> #include <QtGui/QGuiApplication> @@ -499,9 +500,9 @@ bool QQuickContext2DFBOTexture::doMultisampling() const static bool multisamplingSupported = false; if (!extensionsChecked) { - const QSet<QByteArray> extensions = QOpenGLContext::currentContext()->extensions(); - multisamplingSupported = extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_multisample")) - && extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_blit")); + QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()); + multisamplingSupported = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) + && e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); extensionsChecked = true; } diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index b6c45c40a8..f1f82f9e0e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -193,8 +193,8 @@ Item { */ /*! - \qmlproperty int QtQuick::MouseEvent::x - \qmlproperty int QtQuick::MouseEvent::y + \qmlproperty real QtQuick::MouseEvent::x + \qmlproperty real QtQuick::MouseEvent::y These properties hold the coordinates of the position supplied by the mouse event. */ @@ -340,8 +340,8 @@ Item { */ /*! - \qmlproperty int QtQuick::WheelEvent::x - \qmlproperty int QtQuick::WheelEvent::y + \qmlproperty real QtQuick::WheelEvent::x + \qmlproperty real QtQuick::WheelEvent::y These properties hold the coordinates of the position supplied by the wheel event. */ diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 4ab604b30f..27d7072f03 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -642,8 +642,10 @@ is finished. \section1 Limitations - \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by - \c id. Use \c parent instead. + \note Due to an implementation detail, items placed inside a Flickable + cannot anchor to the Flickable. Instead, use \l {Item::}{parent}, which + refers to the Flickable's \l contentItem. The size of the content item is + determined by \l contentWidth and \l contentHeight. */ /*! @@ -2299,7 +2301,8 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) bool receiverDisabled = receiver && !receiver->isEnabled(); bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || contains(localPos)) && (!receiver || !receiver->keepMouseGrab() || receiverDisabled)) { + bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab()); + if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) { QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos)); mouseEvent->setAccepted(false); @@ -2319,7 +2322,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) default: break; } - if ((receiver && stealThisEvent && !receiver->keepMouseGrab() && receiver != this) || receiverDisabled) { + if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver != this) || receiverDisabled) { d->clearDelayedPress(); grabMouse(); } else if (d->delayedPressEvent) { @@ -2335,7 +2338,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) d->lastPosTime = -1; returnToBounds(); } - if (event->type() == QEvent::MouseButtonRelease || (receiver && receiver->keepMouseGrab() && !receiverDisabled)) { + if (event->type() == QEvent::MouseButtonRelease || (receiverKeepsGrab && !receiverDisabled)) { // mouse released, or another item has claimed the grab d->lastPosTime = -1; d->clearDelayedPress(); @@ -2349,8 +2352,11 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) { Q_D(QQuickFlickable); - if (!isVisible() || !isEnabled() || !isInteractive()) + if (!isVisible() || !isEnabled() || !isInteractive()) { + d->cancelInteraction(); return QQuickItem::childMouseEventFilter(i, e); + } + switch (e->type()) { case QEvent::MouseButtonPress: case QEvent::MouseMove: diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 22ee3f39e4..539a374dd9 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2911,14 +2911,15 @@ void QQuickItemPrivate::addChild(QQuickItem *child) childItems.append(child); -#if QT_CONFIG(cursor) QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); +#if QT_CONFIG(cursor) // if the added child has a cursor and we do not currently have any children // with cursors, bubble the notification up if (childPrivate->subtreeCursorEnabled && !subtreeCursorEnabled) setHasCursorInChild(true); #endif + if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled) setHasHoverInChild(true); @@ -2939,13 +2940,14 @@ void QQuickItemPrivate::removeChild(QQuickItem *child) childItems.removeOne(child); Q_ASSERT(!childItems.contains(child)); -#if QT_CONFIG(cursor) QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); +#if QT_CONFIG(cursor) // turn it off, if nothing else is using it if (childPrivate->subtreeCursorEnabled && subtreeCursorEnabled) setHasCursorInChild(false); #endif + if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled) setHasHoverInChild(false); @@ -3807,10 +3809,10 @@ QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() /*! This function is called when an item should release graphics - resources which are not already managed by the nodes returend from + resources which are not already managed by the nodes returned from QQuickItem::updatePaintNode(). - This happens when the item is about to be removed from window it + This happens when the item is about to be removed from the window it was previously rendering to. The item is guaranteed to have a \l {QQuickItem::window()}{window} when the function is called. @@ -6744,8 +6746,27 @@ bool QQuickItem::heightValid() const } /*! - \internal - */ + \since 5.10 + + Returns the size of the item. + + \sa setSize, width, height + */ + +QSizeF QQuickItem::size() const +{ + Q_D(const QQuickItem); + return QSizeF(d->width, d->height); +} + + +/*! + \since 5.10 + + Sets the size of the item to \a size. + + \sa size, setWidth, setHeight + */ void QQuickItem::setSize(const QSizeF &size) { Q_D(QQuickItem); diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index c9494d91bd..f58946d01d 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -237,6 +237,7 @@ public: void setImplicitHeight(qreal); qreal implicitHeight() const; + QSizeF size() const; void setSize(const QSizeF &size); TransformOrigin transformOrigin() const; diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 9873622f41..874130b137 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -327,7 +327,7 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act } if (scale != 0) - rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale)); else { qmlWarning(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0"); ok = false; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 43f4b88833..6c0539bcc1 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -274,7 +274,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickAnchorSet>(); qmlRegisterType<QQuickAnchorAnimation>(uri, major, minor,"AnchorAnimation"); qmlRegisterType<QQuickParentAnimation>(uri, major, minor,"ParentAnimation"); -#if QT_CONFIG(quick_canvas) +#if QT_CONFIG(quick_path) qmlRegisterType<QQuickPathAnimation>("QtQuick",2,0,"PathAnimation"); qmlRegisterType<QQuickPathInterpolator>("QtQuick",2,0,"PathInterpolator"); #endif diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index bb23b1fd54..c662efe40e 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -689,9 +689,10 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event) #endif setHovered(true); d->startScene = event->windowPos(); - d->pressAndHoldTimer.start(pressAndHoldInterval(), this); setKeepMouseGrab(d->stealMouse); event->setAccepted(setPressed(event->button(), true, event->source())); + if (event->isAccepted()) + d->pressAndHoldTimer.start(pressAndHoldInterval(), this); } } @@ -738,23 +739,34 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) bool dragY = drag()->axis() & QQuickDrag::YAxis; QPointF dragPos = d->drag->target()->position(); + QPointF boundedDragPos = dragPos; if (dragX) { - dragPos.setX(qBound( + dragPos.setX(startPos.x() + curLocalPos.x() - startLocalPos.x()); + boundedDragPos.setX(qBound( d->drag->xmin(), - startPos.x() + curLocalPos.x() - startLocalPos.x(), + dragPos.x(), d->drag->xmax())); } if (dragY) { - dragPos.setY(qBound( + dragPos.setY(startPos.y() + curLocalPos.y() - startLocalPos.y()); + boundedDragPos.setY(qBound( d->drag->ymin(), - startPos.y() + curLocalPos.y() - startLocalPos.y(), + dragPos.y(), d->drag->ymax())); } + + QPointF targetPos = d->drag->target()->position(); + if (d->drag->active()) - d->drag->target()->setPosition(dragPos); + d->drag->target()->setPosition(boundedDragPos); + + bool dragOverThresholdX = QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), + Qt::XAxis, event, d->drag->threshold()); + bool dragOverThresholdY = QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), + Qt::YAxis, event, d->drag->threshold()); - if (!d->overThreshold && (QQuickWindowPrivate::dragOverThreshold(dragPos.x() - startPos.x(), Qt::XAxis, event, d->drag->threshold()) - || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event, d->drag->threshold()))) + if (!d->overThreshold && (((targetPos.x() != boundedDragPos.x()) && dragOverThresholdX) || + ((targetPos.y() != boundedDragPos.y()) && dragOverThresholdY))) { d->overThreshold = true; if (d->drag->smoothed()) diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 42fcee3c0d..4f4c403483 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -256,10 +256,14 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData()); } else { auto *mapper = signalMappers[shaderType].at(i); - mapper->setSignalIndex(pd->notifyIndex()); + mapper->setSignalIndex(itemMetaObject->property(d.propertyIndex).notifySignal().methodIndex()); Q_ASSERT(item->metaObject() == itemMetaObject); - QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper, - Qt::AutoConnection, nullptr, itemMetaObject); + bool ok = QObjectPrivate::connectImpl(item, pd->notifyIndex(), item, nullptr, mapper, + Qt::AutoConnection, nullptr, itemMetaObject); + if (!ok) + qWarning() << "Failed to connect to property" << itemMetaObject->property(d.propertyIndex).name() + << "(" << d.propertyIndex << ", signal index" << pd->notifyIndex() + << ") of item" << item; } } else { // If the source is set via a dynamic property, like the layer is, then we need this diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h index 742e786335..3e160ed55a 100644 --- a/src/quick/items/qquickpainteditem_p.h +++ b/src/quick/items/qquickpainteditem_p.h @@ -51,7 +51,9 @@ // We mean it. // +#include "qquickpainteditem.h" #include "qquickitem_p.h" +#include "qquickpainteditem.h" #include <QtGui/qcolor.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h index 8dc0d90a2f..cfe163b4c1 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE class QQuickBasePositionerPrivate; -class QQuickPositionerAttached : public QObject +class Q_QUICK_PRIVATE_EXPORT QQuickPositionerAttached : public QObject { Q_OBJECT diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 7926607e33..d317c1d19b 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -873,4 +873,11 @@ void QQuickShaderEffectPrivate::updatePolish() q->m_impl->maybeUpdateShaders(); } +#if QT_CONFIG(opengl) +bool QQuickShaderEffect::isOpenGLShaderEffect() const +{ + return m_glImpl != Q_NULLPTR; +} +#endif + QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 7885daffbb..30bd018098 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -117,6 +117,10 @@ public: bool isComponentComplete() const; QString parseLog(); +#if QT_CONFIG(opengl) + bool isOpenGLShaderEffect() const; +#endif + Q_SIGNALS: void fragmentShaderChanged(); void vertexShaderChanged(); diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index b40a9e2843..a4ce13a199 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -101,7 +101,7 @@ void QQuickParentChangePrivate::doChange(QQuickItem *targetParent, QQuickItem *s } if (scale != 0) - rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale)); else { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under scale of 0"); ok = false; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 57810e4ec9..bd68f39e48 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -635,8 +635,8 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. Q_ASSERT(pointerEvent->asPointerTouchEvent()); - QTouchEvent *event = pointerEvent->asPointerTouchEvent()->touchEventForItem(item); - if (!event) + QScopedPointer<QTouchEvent> event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item)); + if (event.isNull()) return false; // For each point, check if it is accepted, if not, try the next point. @@ -653,7 +653,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve break; qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; - QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); + QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false)); // Send a single press and see if that's accepted QCoreApplication::sendEvent(item, mousePress.data()); @@ -667,7 +667,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve pointerEventPoint->setGrabber(item); if (checkIfDoubleClicked(event->timestamp())) { - QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); + QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false)); QCoreApplication::sendEvent(item, mouseDoubleClick.data()); event->setAccepted(mouseDoubleClick->isAccepted()); if (!mouseDoubleClick->isAccepted()) { @@ -684,7 +684,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve } else if (touchMouseDevice == device && p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event.data(), mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { @@ -695,7 +695,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve // no grabber, check if we care about mouse hover // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now. // hover for touch??? - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, item, false)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event.data(), item, false)); if (lastMousePosition.isNull()) lastMousePosition = me->windowPos(); QPointF last = lastMousePosition; @@ -713,7 +713,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve } else if (p.state() & Qt::TouchPointReleased) { // currently handled point was released if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { - QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false)); + QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event.data(), mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); if (item->acceptHoverEvents() && p.screenPos() != QGuiApplicationPrivate::lastCursorPosition) { @@ -746,6 +746,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; QQuickItem *oldGrabber = q->mouseGrabberItem(); + bool fromTouch = false; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber @@ -753,6 +754,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) point->setGrabber(grabber); + fromTouch = true; } else { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); @@ -762,8 +764,11 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (oldGrabber) { QEvent e(QEvent::UngrabMouse); QSet<QQuickItem *> hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { oldGrabber->mouseUngrabEvent(); + if (fromTouch) + oldGrabber->touchUngrabEvent(); + } } } @@ -811,6 +816,10 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); + if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { + qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; + setMouseGrabber(nullptr); + } if (Q_LIKELY(touch)) { const auto touchDevices = QQuickPointerDevice::touchDevices(); for (auto device : touchDevices) { @@ -824,10 +833,6 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to } } } - if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) { - qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null"; - setMouseGrabber(nullptr); - } } /*! @@ -2397,7 +2402,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); if (deliverDragEvent(grabber, **grabItem, moveEvent)) { - moveEvent->setAccepted(true); for (++grabItem; grabItem != grabber->end();) { QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); if ((**grabItem)->contains(p)) { @@ -2472,7 +2476,10 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); + translatedEvent.setAccepted(event->isAccepted()); QCoreApplication::sendEvent(item, &translatedEvent); + event->setAccepted(translatedEvent.isAccepted()); + event->setDropAction(translatedEvent.dropAction()); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); @@ -2583,23 +2590,39 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem break; } + bool touchMouseUnset = (touchMouseId == -1); // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if (touchMouseId == -1 || touchMouseId == tp.id()) { + if (touchMouseUnset || touchMouseId == tp.id()) { // targetEvent is already transformed wrt local position, velocity, etc. // FIXME: remove asTouchEvent!!! QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); + // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should + // report the touchpoint's grabber. Whenever we send a synthetic mouse event, + // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. + touchMouseId = tp.id(); + touchMouseDevice = event->device(); if (target->childMouseEventFilter(item, mouseEvent.data())) { qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; if (t != QEvent::MouseButtonRelease) { qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - touchMouseId = tp.id(); - touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); + if (touchMouseUnset) { + // the point was grabbed as a pure touch point before, now it will be treated as mouse + // but the old receiver still needs to be informed + if (auto oldGrabber = touchMouseDevice->pointerEvent()->pointById(tp.id())->grabber()) + oldGrabber->touchUngrabEvent(); + } + touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set target->grabMouse(); } filtered = true; } + if (touchMouseUnset) { + // Now that we're done sending a synth mouse event, and it wasn't grabbed, + // the touchpoint is no longer acting as a synthetic mouse. Restore previous state. + touchMouseId = -1; + touchMouseDevice = nullptr; + } // Only one event can be filtered as a mouse event. break; } @@ -3392,6 +3415,11 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo) The specified FBO must be created in the context of the window or one that shares with it. + \note \a fboId can also be set to 0. In this case rendering will target the + default framebuffer of whichever surface is current when the scenegraph + renders. \a size must still be valid, specifying the dimensions of the + surface. + \note This function only has an effect when using the default OpenGL scene graph adaptation. @@ -4495,8 +4523,8 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const \note The call to the function must happen before constructing the first QQuickWindow in the application. It cannot be changed afterwards. - If \a backend is invalid or an error occurs, the default backend (OpenGL or - software, depending on the Qt configuration) is used. + If the selected backend is invalid or an error occurs, the default backend + (OpenGL or software, depending on the Qt configuration) is used. \since 5.8 */ diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 6856d34616..b3b8274a73 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -101,7 +101,7 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) } } -void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) +void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); if (!m_windows.contains(window)) @@ -174,7 +174,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) if (alsoSwap && window->isVisible()) { //Flush backingstore to window - m_backingStores[window]->flush(softwareRenderer->flushRegion()); + if (!isNewExpose) + m_backingStores[window]->flush(softwareRenderer->flushRegion()); + else + m_backingStores[window]->flush(QRegion(QRect(QPoint(0,0), window->size()))); cd->fireFrameSwapped(); } @@ -206,7 +209,7 @@ void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window) { if (window->isExposed()) { m_windows[window].updatePending = true; - renderWindow(window); + renderWindow(window, true); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h index 02dcf4eefa..c724d18298 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h @@ -69,7 +69,7 @@ public: void windowDestroyed(QQuickWindow *window) override; - void renderWindow(QQuickWindow *window); + void renderWindow(QQuickWindow *window, bool isNewExpose = false); void exposureChanged(QQuickWindow *window) override; QImage grab(QQuickWindow *window) override; diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 9207fdbc55..bb2671f6c3 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -134,6 +134,7 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) , m_bindable(0) , m_changed_emitted(false) , m_is_rendering(false) + , m_is_preprocessing(false) { } @@ -287,6 +288,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) void QSGRenderer::preprocess() { + m_is_preprocessing = true; + QSGRootNode *root = rootNode(); Q_ASSERT(root); @@ -298,6 +301,11 @@ void QSGRenderer::preprocess() for (QSet<QSGNode *>::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { QSGNode *n = *it; + + // If we are currently preprocessing, check this node hasn't been + // deleted or something. we don't want a use-after-free! + if (m_nodes_dont_preprocess.contains(n)) // skip + continue; if (!nodeUpdater()->isNodeBlocked(n, root)) { n->preprocess(); } @@ -315,8 +323,13 @@ void QSGRenderer::preprocess() updatePassTime = frameTimer.nsecsElapsed(); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererUpdate); + + m_is_preprocessing = false; + m_nodes_dont_preprocess.clear(); } + + void QSGRenderer::addNodesToPreprocess(QSGNode *node) { for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) @@ -329,8 +342,13 @@ void QSGRenderer::removeNodesToPreprocess(QSGNode *node) { for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) removeNodesToPreprocess(c); - if (node->flags() & QSGNode::UsePreprocess) + if (node->flags() & QSGNode::UsePreprocess) { m_nodes_to_preprocess.remove(node); + + // If preprocessing *now*, mark the node as gone. + if (m_is_preprocessing) + m_nodes_dont_preprocess.insert(node); + } } diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 4589685765..1ea2775e6f 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -118,11 +118,13 @@ private: QSGNodeUpdater *m_node_updater; QSet<QSGNode *> m_nodes_to_preprocess; + QSet<QSGNode *> m_nodes_dont_preprocess; const QSGBindable *m_bindable; uint m_changed_emitted : 1; uint m_is_rendering : 1; + uint m_is_preprocessing : 1; }; class Q_QUICK_PRIVATE_EXPORT QSGBindable diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 412023564f..f90706affe 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -40,7 +40,6 @@ #include "qsgadaptationlayer_p.h" #include <qmath.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qrawfont_p.h> @@ -57,9 +56,8 @@ static QElapsedTimer qsg_render_timer; QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; -QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) - : m_manager(man) - , m_pendingGlyphs(64) +QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) + : m_pendingGlyphs(64) { Q_ASSERT(font.isValid()); diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index a8e35b1ac1..ba146b884f 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -73,7 +73,6 @@ QT_BEGIN_NAMESPACE class QSGNode; class QImage; class TextureReference; -class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldGlyphNode; class QOpenGLContext; class QSGInternalImageNode; @@ -409,7 +408,7 @@ public: class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache { public: - QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); virtual ~QSGDistanceFieldGlyphCache(); struct Metrics { @@ -443,8 +442,6 @@ public: bool operator == (const Texture &other) const { return textureId == other.textureId; } }; - const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; } - const QRawFont &referenceFont() const { return m_referenceFont; } qreal fontScale(qreal pixelSize) const @@ -514,8 +511,6 @@ protected: inline bool isCoreProfile() const { return m_coreProfile; } private: - QSGDistanceFieldGlyphCacheManager *m_manager; - QRawFont m_referenceFont; int m_glyphCount; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index d52f69c7a3..ff2379ecb5 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -333,7 +333,6 @@ QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderCont QSGRenderContext::QSGRenderContext(QSGContext *context) : m_sg(context) - , m_distanceFieldCacheManager(0) { } diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 2f5d5790ee..bd10453131 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -78,7 +78,6 @@ class QSGMaterial; class QSGRenderLoop; class QSGLayer; class QQuickTextureFactory; -class QSGDistanceFieldGlyphCacheManager; class QSGContext; class QQuickPaintedItem; class QSGRendererInterface; @@ -194,7 +193,7 @@ protected: QMutex m_mutex; QHash<QQuickTextureFactory *, QSGTexture *> m_textures; QSet<QSGTexture *> m_texturesToDelete; - QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager; + QHash<QRawFont, QSGDistanceFieldGlyphCache*> m_glyphCaches; QSet<QFontEngine *> m_fontEnginesToClean; }; diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index d31a7025e5..be5fec9dab 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -39,7 +39,6 @@ #include "qsgdefaultcontext_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> #include <QtQuick/private/qsgdefaultinternalimagenode_p.h> #include <QtQuick/private/qsgdefaultpainternode_p.h> diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index f0a336e229..ba25172d2f 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -42,7 +42,6 @@ #include <QtGui/private/qdistancefield_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtQml/private/qqmlglobal_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <qopenglfunctions.h> #include <qopenglframebufferobject.h> #include <qmath.h> @@ -60,8 +59,8 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI # define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2 #endif -QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) - : QSGDistanceFieldGlyphCache(man, c, font) +QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) + : QSGDistanceFieldGlyphCache(c, font) , m_maxTextureSize(0) , m_maxTextureCount(3) , m_blitProgram(0) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index 57dc4a5d07..fe365495c2 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -69,7 +69,7 @@ class QOpenGLFunctions_3_2_Core; class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache { public: - QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); virtual ~QSGDefaultDistanceFieldGlyphCache(); void requestGlyphs(const QSet<glyph_t> &glyphs) override; diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index b856d99bc1..0d42102f36 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -42,11 +42,33 @@ QT_BEGIN_NAMESPACE +QSGDefaultGlyphNode::QSGDefaultGlyphNode() + : m_glyphNodeType(RootGlyphNode) + , m_dirtyGeometry(false) +{ + setFlag(UsePreprocess); +} + +QSGDefaultGlyphNode::~QSGDefaultGlyphNode() +{ + if (m_glyphNodeType == SubGlyphNode) + return; + + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); +} + void QSGDefaultGlyphNode::setMaterialColor(const QColor &color) { static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color); } +void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + QSGBasicGlyphNode::setGlyphs(position, glyphs); + m_dirtyGeometry = true; +} + void QSGDefaultGlyphNode::update() { QRawFont font = m_glyphs.rawFont(); @@ -84,4 +106,110 @@ void QSGDefaultGlyphNode::update() markDirty(DirtyGeometry); } +void QSGDefaultGlyphNode::preprocess() +{ + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); + + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGDefaultGlyphNode::updateGeometry() +{ + // Remove previously created sub glyph nodes + // We assume all the children are sub glyph nodes + QSGNode *subnode = firstChild(); + while (subnode) { + // We can't delete the node now as it might be in the preprocess list + // It will be deleted in the next preprocess + m_nodesToDelete.append(subnode); + subnode = subnode->nextSibling(); + } + removeAllChildNodes(); + + GlyphInfo glyphInfo; + + const QVector<quint32> indexes = m_glyphs.glyphIndexes(); + const QVector<QPointF> positions = m_glyphs.positions(); + + const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384 + const int maxVertices = maxGlyphs * 4; // 65536 + const int maxIndexes = maxGlyphs * 6; // 98304 + + for (int i = 0; i < indexes.size(); ++i) { + const int glyphIndex = indexes.at(i); + const QPointF position = positions.at(i); + + // As we use UNSIGNED_SHORT indexing in the geometry, we overload the + // "glyphsInOtherNodes" concept as overflow for if there are more than + // 65536 (16384 * 4) vertices to render which would otherwise exceed + // the maximum index size. This will cause sub-nodes to be recursively + // created to handle any number of glyphs. + if (i >= maxGlyphs) { + glyphInfo.indexes.append(glyphIndex); + glyphInfo.positions.append(position); + continue; + } + } + + if (!glyphInfo.indexes.isEmpty()) { + QGlyphRun subNodeGlyphRun(m_glyphs); + subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes); + subNodeGlyphRun.setPositions(glyphInfo.positions); + + QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(); + subNode->setGlyphNodeType(SubGlyphNode); + subNode->setColor(m_color); + subNode->setStyle(m_style); + subNode->setStyleColor(m_styleColor); + subNode->setGlyphs(m_position, subNodeGlyphRun); + subNode->update(); + subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered + appendChildNode(subNode); + + QSGGeometry *g = geometry(); + + QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D(); + quint16 *indexData = g->indexDataAsUShort(); + + QVector<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices); + QVector<quint16> tempIndexData(maxIndexes); + + for (int i = 0; i < maxGlyphs; i++) { + tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0]; + tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1]; + tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2]; + tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3]; + + tempIndexData[i * 6 + 0] = indexData[i * 6 + 0]; + tempIndexData[i * 6 + 1] = indexData[i * 6 + 1]; + tempIndexData[i * 6 + 2] = indexData[i * 6 + 2]; + tempIndexData[i * 6 + 3] = indexData[i * 6 + 3]; + tempIndexData[i * 6 + 4] = indexData[i * 6 + 4]; + tempIndexData[i * 6 + 5] = indexData[i * 6 + 5]; + } + + g->allocate(maxVertices, maxIndexes); + vertexData = g->vertexDataAsTexturedPoint2D(); + indexData = g->indexDataAsUShort(); + + for (int i = 0; i < maxGlyphs; i++) { + vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0]; + vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1]; + vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2]; + vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3]; + + indexData[i * 6 + 0] = tempIndexData[i * 6 + 0]; + indexData[i * 6 + 1] = tempIndexData[i * 6 + 1]; + indexData[i * 6 + 2] = tempIndexData[i * 6 + 2]; + indexData[i * 6 + 3] = tempIndexData[i * 6 + 3]; + indexData[i * 6 + 4] = tempIndexData[i * 6 + 4]; + indexData[i * 6 + 5] = tempIndexData[i * 6 + 5]; + } + } + + m_dirtyGeometry = false; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index 0eb7a4e4bd..37a89c70b9 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -59,8 +59,31 @@ QT_BEGIN_NAMESPACE class QSGDefaultGlyphNode : public QSGBasicGlyphNode { public: + QSGDefaultGlyphNode(); + ~QSGDefaultGlyphNode(); void setMaterialColor(const QColor &color) override; + void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override; void update() override; + void preprocess() override; + void updateGeometry(); + +private: + enum DefaultGlyphNodeType { + RootGlyphNode, + SubGlyphNode + }; + + void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; } + + DefaultGlyphNodeType m_glyphNodeType; + QLinkedList<QSGNode *> m_nodesToDelete; + + struct GlyphInfo { + QVector<quint32> indexes; + QVector<QPointF> positions; + }; + + uint m_dirtyGeometry: 1; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index fc8040e67b..6fa9dd6359 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -44,6 +44,7 @@ #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QOpenGLFunctions> +#include <QtGui/private/qopenglextensions_p.h> #include <QtQuick/private/qsgdepthstencilbuffer_p.h> @@ -330,9 +331,9 @@ void QSGDefaultLayer::grab() if (effectiveSamples <= 1) { m_multisampling = false; } else { - const QSet<QByteArray> extensions = m_context->openglContext()->extensions(); - m_multisampling = extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_multisample")) - && extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_blit")); + QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(funcs); + m_multisampling = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) + && e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); } m_multisamplingChecked = true; } diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index aaf4635c0c..db0eea15df 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -45,7 +45,6 @@ #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgatlastexture_p.h> #include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> QT_BEGIN_NAMESPACE @@ -159,8 +158,8 @@ void QSGDefaultRenderContext::invalidate() delete m_depthStencilManager; m_depthStencilManager = 0; - delete m_distanceFieldCacheManager; - m_distanceFieldCacheManager = 0; + qDeleteAll(m_glyphCaches); + m_glyphCaches.clear(); if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); @@ -294,13 +293,10 @@ QT_END_NAMESPACE QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) { - if (!m_distanceFieldCacheManager) - m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager; - - QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font); + QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(font, 0); if (!cache) { - cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); - m_distanceFieldCacheManager->insertCache(font, cache); + cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font); + m_glyphCaches.insert(font, cache); } return cache; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index 456a197ba1..32eda2d142 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -39,7 +39,6 @@ #include "qsgdistancefieldglyphnode_p.h" #include "qsgdistancefieldglyphnode_p_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgcontext_p.h> QT_BEGIN_NAMESPACE @@ -76,9 +75,6 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode() m_glyph_cache->unregisterGlyphNode(this); m_glyph_cache->unregisterOwnerElement(ownerElement()); } - - while (m_nodesToDelete.count()) - delete m_nodesToDelete.takeLast(); } void QSGDistanceFieldGlyphNode::setColor(const QColor &color) @@ -158,9 +154,6 @@ void QSGDistanceFieldGlyphNode::preprocess() { Q_ASSERT(m_glyph_cache); - while (m_nodesToDelete.count()) - delete m_nodesToDelete.takeLast(); - m_glyph_cache->processPendingGlyphs(); m_glyph_cache->update(); @@ -188,13 +181,12 @@ void QSGDistanceFieldGlyphNode::updateGeometry() // Remove previously created sub glyph nodes // We assume all the children are sub glyph nodes QSGNode *subnode = firstChild(); + QSGNode *nextNode = 0; while (subnode) { - // We can't delete the node now as it might be in the preprocess list - // It will be deleted in the next preprocess - m_nodesToDelete.append(subnode); - subnode = subnode->nextSibling(); + nextNode = subnode->nextSibling(); + delete subnode; + subnode = nextNode; } - removeAllChildNodes(); QSGGeometry *g = geometry(); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index fc66f2f2cc..a67c659c99 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qsgdistancefieldglyphnode_p_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgtexture_p.h> #include <QtGui/qopenglfunctions.h> #include <QtGui/qsurface.h> @@ -58,7 +57,7 @@ public: protected: void initialize() override; - void updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc); + void updateAlphaRange(); void updateColor(const QVector4D &c); void updateTextureScale(const QVector2D &ts); @@ -98,7 +97,31 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader() setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag")); } -void QSGDistanceFieldTextMaterialShader::updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc) +static float qt_sg_envFloat(const char *name, float defaultValue) +{ + if (Q_LIKELY(!qEnvironmentVariableIsSet(name))) + return defaultValue; + bool ok = false; + const float value = qgetenv(name).toFloat(&ok); + return ok ? value : defaultValue; +} + +static float thresholdFunc(float glyphScale) +{ + static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f); + static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f); + static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f); + static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f); + return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev); +} + +static float spreadFunc(float glyphScale) +{ + static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f); + return range / glyphScale; +} + +void QSGDistanceFieldTextMaterialShader::updateAlphaRange() { float combinedScale = m_fontScale * m_matrixScale; float base = thresholdFunc(combinedScale); @@ -169,8 +192,7 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q updateRange = true; } if (updateRange) { - updateAlphaRange(material->glyphCache()->manager()->thresholdFunc(), - material->glyphCache()->manager()->antialiasingSpreadFunc()); + updateAlphaRange(); } Q_ASSERT(material->glyphCache()); @@ -334,7 +356,7 @@ public: protected: void initialize() override; - void updateOutlineAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc, int dfRadius); + void updateOutlineAlphaRange(int dfRadius); int m_outlineAlphaMax0_id; int m_outlineAlphaMax1_id; @@ -355,9 +377,7 @@ void DistanceFieldOutlineTextMaterialShader::initialize() m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1"); } -void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(ThresholdFunc thresholdFunc, - AntialiasingSpreadFunc spreadFunc, - int dfRadius) +void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius) { float combinedScale = m_fontScale * m_matrixScale; float base = thresholdFunc(combinedScale); @@ -381,9 +401,7 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat if (oldMaterial == 0 || material->fontScale() != oldMaterial->fontScale() || state.isMatrixDirty()) - updateOutlineAlphaRange(material->glyphCache()->manager()->thresholdFunc(), - material->glyphCache()->manager()->antialiasingSpreadFunc(), - material->glyphCache()->distanceFieldRadius()); + updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius()); } diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index c0c6bda718..7008f20925 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE class QSGRenderContext; -class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldTextMaterial; class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer { @@ -107,7 +106,6 @@ private: AntialiasingMode m_antialiasingMode; QRectF m_boundingRect; const QSGDistanceFieldGlyphCache::Texture *m_texture; - QLinkedList<QSGNode *> m_nodesToDelete; struct GlyphInfo { QVector<quint32> indexes; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 38c3b8dd85..c6db3df158 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -45,7 +45,6 @@ HEADERS += \ $$PWD/util/qsgtexture.h \ $$PWD/util/qsgtexture_p.h \ $$PWD/util/qsgtextureprovider.h \ - $$PWD/util/qsgdistancefieldutil_p.h \ $$PWD/util/qsgflatcolormaterial.h \ $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgtexturematerial.h \ @@ -62,7 +61,6 @@ SOURCES += \ $$PWD/util/qsgsimpletexturenode.cpp \ $$PWD/util/qsgtexture.cpp \ $$PWD/util/qsgtextureprovider.cpp \ - $$PWD/util/qsgdistancefieldutil.cpp \ $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplematerial.cpp \ $$PWD/util/qsgtexturematerial.cpp \ diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp index 389b9e0b4e..9ffd1b4b08 100644 --- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp @@ -238,9 +238,9 @@ void QSGDefaultPainterNode::updateGeometry() void QSGDefaultPainterNode::updateRenderTarget() { if (!m_extensionsChecked) { - const QSet<QByteArray> extensions = m_context->openglContext()->extensions(); - m_multisamplingSupported = extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_multisample")) - && extensions.contains(QByteArrayLiteral("GL_EXT_framebuffer_blit")); + QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions()); + m_multisamplingSupported = e->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample) + && e->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit); m_extensionsChecked = true; } diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp deleted file mode 100644 index 9ca9cdb107..0000000000 --- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgdistancefieldutil_p.h" - -#include <private/qsgadaptationlayer_p.h> -#if QT_CONFIG(opengl) -# include <QtGui/private/qopenglengineshadersource_p.h> -#endif -#include <QtQuick/private/qsgcontext_p.h> - -QT_BEGIN_NAMESPACE - -static float qt_sg_envFloat(const char *name, float defaultValue) -{ - if (Q_LIKELY(!qEnvironmentVariableIsSet(name))) - return defaultValue; - bool ok = false; - const float value = qgetenv(name).toFloat(&ok); - return ok ? value : defaultValue; -} - -static float defaultThresholdFunc(float glyphScale) -{ - static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f); - static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f); - static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f); - static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f); - return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev); -} - -static float defaultAntialiasingSpreadFunc(float glyphScale) -{ - static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f); - return range / glyphScale; -} - -QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager() - : m_threshold_func(defaultThresholdFunc) - , m_antialiasingSpread_func(defaultAntialiasingSpreadFunc) -{ -} - -QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() -{ - qDeleteAll(m_caches); -} - -QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font) -{ - return m_caches.value(font, 0); -} - -void QSGDistanceFieldGlyphCacheManager::insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache) -{ - m_caches.insert(font, cache); -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h deleted file mode 100644 index ad366cb4d4..0000000000 --- a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGDISTANCEFIELDUTIL_H -#define QSGDISTANCEFIELDUTIL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qrawfont.h> -#include <private/qfontengine_p.h> -#include <private/qsgadaptationlayer_p.h> - -QT_BEGIN_NAMESPACE - -typedef float (*ThresholdFunc)(float glyphScale); -typedef float (*AntialiasingSpreadFunc)(float glyphScale); - -class QOpenGLShaderProgram; -class QSGDistanceFieldGlyphCache; -class QSGContext; - -class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCacheManager -{ -public: - QSGDistanceFieldGlyphCacheManager(); - ~QSGDistanceFieldGlyphCacheManager(); - - QSGDistanceFieldGlyphCache *cache(const QRawFont &font); - void insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache); - - ThresholdFunc thresholdFunc() const { return m_threshold_func; } - void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; } - - AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; } - void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; } - -private: - QHash<QRawFont, QSGDistanceFieldGlyphCache *> m_caches; - - ThresholdFunc m_threshold_func; - AntialiasingSpreadFunc m_antialiasingSpread_func; -}; - -QT_END_NAMESPACE - -#endif // QSGDISTANCEFIELDUTIL_H diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp index 7214a255df..f29c58ad9e 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.cpp +++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp @@ -194,19 +194,15 @@ /*! \fn const char *QSGSimpleMaterialShader::uniformMatrixName() const - Reimplement this function to give a different name to the uniform for - item transformation. The default value is \c qt_Matrix. - + Returns the name for the transform matrix uniform of this item. + The default value is \c qt_Matrix. */ /*! \fn const char *QSGSimpleMaterialShader::uniformOpacityName() const - Reimplement this function to give a different name to the uniform for - item opacity. The default value is \c qt_Opacity. - - If the shader program does not implement the item opacity, the - implemented function should return a null pointer. + Returns the name for the opacity uniform of this item. + The default value is \c qt_Opacity. */ /*! diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index 0e7219d7bd..b5b8815b4a 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -71,6 +71,7 @@ public: resolveUniforms(); } + // ### Qt 6: make both virtual and fix docs const char *uniformMatrixName() const { return "qt_Matrix"; } const char *uniformOpacityName() const { return "qt_Opacity"; } diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 5dd16407b8..89007cff1f 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -47,6 +47,7 @@ #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) # include <private/qquickopenglshadereffectnode_p.h> # include <private/qquickopenglshadereffect_p.h> +# include <private/qquickshadereffect_p.h> #endif #include <private/qanimationgroupjob_p.h> @@ -344,12 +345,13 @@ void QQuickTransformAnimatorJob::postSync() } QQuickItemPrivate *d = QQuickItemPrivate::get(m_target); +#if QT_CONFIG(quick_shadereffect) if (d->extra.isAllocated() && d->extra->layer && d->extra->layer->enabled()) { d = QQuickItemPrivate::get(d->extra->layer->m_effectSource); } - +#endif m_helper->node = d->itemNode(); } @@ -625,7 +627,8 @@ QQuickUniformAnimatorJob::QQuickUniformAnimatorJob() void QQuickUniformAnimatorJob::setTarget(QQuickItem *target) { - if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != nullptr) + QQuickShaderEffect* effect = qobject_cast<QQuickShaderEffect*>(target); + if (effect && effect->isOpenGLShaderEffect()) m_target = target; } diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index 7788635e3e..0ceed7f681 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -151,6 +151,8 @@ QQuickTextureFactory *QQuickTextureFactory::textureFactoryForImage(const QImage If you are using QRunnable as base for your QQuickImageResponse ensure automatic deletion is disabled. + See the \l {imageresponseprovider}{Image Response Provider Example} for a complete implementation. + \sa QQuickImageProvider */ @@ -181,7 +183,12 @@ QString QQuickImageResponse::errorString() const It may be reimplemented to cancel a request in the provider side, however, it is not mandatory. - A cancelled QQuickImageResponse still needs to emit finished(). + A cancelled QQuickImageResponse still needs to emit finished() so that the + engine may clean up the QQuickImageResponse. + + \note finished() should not be emitted until the response is complete, + regardless of whether or not cancel() was called. If it is called prematurely, + the engine may destroy the response while it is still active, leading to a crash. */ void QQuickImageResponse::cancel() { @@ -190,7 +197,12 @@ void QQuickImageResponse::cancel() /*! \fn void QQuickImageResponse::finished() - Signals that the job execution has finished (be it successfully, because an error happened or because it was cancelled). + Signals that the job execution has finished (be it successfully, because an + error happened or because it was cancelled). + + \note Emission of this signal must be the final action the response performs: + once the signal is received, the response will subsequently be destroyed by + the engine. */ /*! @@ -466,6 +478,8 @@ QQuickTextureFactory *QQuickImageProvider::requestTexture(const QString &id, QSi \inmodule QtQuick \brief The QQuickAsyncImageProvider class provides an interface for for asynchronous control of QML image requests. + See the \l {imageresponseprovider}{Image Response Provider Example} for a complete implementation. + \sa QQuickImageProvider */ QQuickAsyncImageProvider::QQuickAsyncImageProvider() |