From 54eab024611c6b9675ef6d48a15e7d69ed28df87 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 1 Jun 2015 17:49:37 +0200 Subject: Introduce QQuickPointerHandler: base class for nested event handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They will be Tech Preview in 5.8, so they shouldn't be available unless you explicitly import them. Task-number: QTBUG-54824 Change-Id: I290854a4e2b76e2cdfef5c216c7fdeb47fbcd390 Reviewed-by: Jan Arve Sæther --- src/imports/handlers/handlers.pro | 11 +++ src/imports/handlers/plugin.cpp | 88 ++++++++++++++++++++ src/imports/handlers/qmldir | 5 ++ src/imports/imports.pro | 1 + src/quick/handlers/handlers.pri | 7 ++ src/quick/handlers/qquickhandlersmodule.cpp | 90 +++++++++++++++++++++ src/quick/handlers/qquickhandlersmodule_p.h | 68 ++++++++++++++++ src/quick/handlers/qquickpointerhandler.cpp | 120 ++++++++++++++++++++++++++++ src/quick/handlers/qquickpointerhandler_p.h | 101 +++++++++++++++++++++++ src/quick/items/qquickflickable.cpp | 3 + src/quick/items/qquickitem.cpp | 17 +++- src/quick/items/qquickitem_p.h | 4 + src/quick/items/qquickwindow.cpp | 13 ++- src/quick/quick.pro | 1 + 14 files changed, 527 insertions(+), 2 deletions(-) create mode 100644 src/imports/handlers/handlers.pro create mode 100644 src/imports/handlers/plugin.cpp create mode 100644 src/imports/handlers/qmldir create mode 100644 src/quick/handlers/handlers.pri create mode 100644 src/quick/handlers/qquickhandlersmodule.cpp create mode 100644 src/quick/handlers/qquickhandlersmodule_p.h create mode 100644 src/quick/handlers/qquickpointerhandler.cpp create mode 100644 src/quick/handlers/qquickpointerhandler_p.h (limited to 'src') diff --git a/src/imports/handlers/handlers.pro b/src/imports/handlers/handlers.pro new file mode 100644 index 0000000000..0e32644773 --- /dev/null +++ b/src/imports/handlers/handlers.pro @@ -0,0 +1,11 @@ +CXX_MODULE = qml +TARGET = handlersplugin +TARGETPATH = Qt/labs/handlers +IMPORT_VERSION = 1.0 + +SOURCES += \ + plugin.cpp + +QT += quick-private qml-private + +load(qml_plugin) diff --git a/src/imports/handlers/plugin.cpp b/src/imports/handlers/plugin.cpp new file mode 100644 index 0000000000..21b9764611 --- /dev/null +++ b/src/imports/handlers/plugin.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 + +#include + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_handlers_1); +#endif +} + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule Qt.labs.handlers 1.0 + \title Qt Quick Pointer Handlers + \ingroup qmlmodules + \brief Provides QML types for handling pointer events. + + This QML module contains types for handling pointer events, which are an abstraction + of mouse, touch and tablet events. + + To use the types in this module, import the module with the following line: + + \code + import Qt.labs.handlers 1.0 + \endcode +*/ + + +//![class decl] +class QtQuickHandlersPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + QtQuickHandlersPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.handlers")); + Q_UNUSED(uri); + QQuickHandlersModule::defineModule(); + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/handlers/qmldir b/src/imports/handlers/qmldir new file mode 100644 index 0000000000..4896348c2e --- /dev/null +++ b/src/imports/handlers/qmldir @@ -0,0 +1,5 @@ +module Qt.labs.handlers +plugin handlersplugin +classname QtQuickHandlersPlugin +typeinfo plugins.qmltypes + diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 9e3cdf3f42..ade744ec19 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -11,6 +11,7 @@ SUBDIRS += \ qtHaveModule(quick) { SUBDIRS += \ + handlers \ layouts \ qtquick2 \ window \ diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri new file mode 100644 index 0000000000..0c6bd2ab17 --- /dev/null +++ b/src/quick/handlers/handlers.pri @@ -0,0 +1,7 @@ +HEADERS += \ + $$PWD/qquickpointerhandler_p.h \ + $$PWD/qquickhandlersmodule_p.h \ + +SOURCES += \ + $$PWD/qquickpointerhandler.cpp \ + $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp new file mode 100644 index 0000000000..dd50c367ec --- /dev/null +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 "qquickhandlersmodule_p.h" +#include "qquickpointerhandler_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_handlers); +#endif +} + +QT_BEGIN_NAMESPACE + +static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent) +{ + if (QQuickItem *parentItem = qmlobject_cast(parent)) { + QQuickPointerHandler *handler = qmlobject_cast(obj); + if (handler && !handler->target()) { + handler->setTarget(parentItem); + return QQmlPrivate::Parented; + } + } + return QQmlPrivate::IncompatibleObject; +} + +static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) +{ + QQmlPrivate::RegisterAutoParent autoparent = { 0, &handler_autoParent }; + QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent); + qmlRegisterUncreatableType(uri, major, minor, "PointerEvent", + QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler")); + qmlRegisterUncreatableType(uri, major, minor, "PointerDevice", + QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent")); + qRegisterMetaType("QPointerUniqueId"); + qmlRegisterUncreatableType(uri, major, minor, "PointerUniqueId", + QQuickPointerHandler::tr("PointerUniqueId is only available as a property of PointerEvent")); + + qmlRegisterType(uri,major,minor,"PointerHandler"); +} + +void QQuickHandlersModule::defineModule() +{ + initResources(); + + const char uri[] = "Qt.labs.handlers"; + int majorVersion = 1; + int minorVersion = 0; + + qt_quickhandlers_defineModule(uri, majorVersion, minorVersion); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickhandlersmodule_p.h b/src/quick/handlers/qquickhandlersmodule_p.h new file mode 100644 index 0000000000..7eb8d39b98 --- /dev/null +++ b/src/quick/handlers/qquickhandlersmodule_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 QQUICKHANDLERSMODULE_P_H +#define QQUICKHANDLERSMODULE_P_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 +#include + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QQuickHandlersModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif // QQUICKHANDLERSMODULE_P_H + diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp new file mode 100644 index 0000000000..9964d8f465 --- /dev/null +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 "qquickpointerhandler_p.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch") + +/*! + \qmltype PointerHandler + \instantiates QQuickPointerHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for pointer events + + PointerHandler is a handler for pointer events regardless of source. + They may represent events from a touch, mouse or tablet device. +*/ + +QQuickPointerHandler::QQuickPointerHandler(QObject *parent) + : QObject(parent) + , m_currentEvent(nullptr) + , m_target(nullptr) + , m_enabled(true) +{ +} + +void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) +{ + // TODO eventually the handler itself should be the grabber, instead of the target Item + if (grab) + point->setGrabber(m_target); + else if (point->grabber() == m_target) + point->setGrabber(nullptr); +} + +/*! + \qmlproperty QQuickPointerHandler::enabled + + If a PointerHandler is disabled, it will reject all events + and no signals will be emitted. + + TODO is it too extreme not even to emit pressed/updated/released? + or should we disable only the higher-level interpretation, in subclasses? +*/ +void QQuickPointerHandler::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + m_enabled = enabled; + emit enabledChanged(); +} + +void QQuickPointerHandler::setTarget(QQuickItem *target) +{ + if (m_target == target) + return; + + if (m_target) { + QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); + p->extra.value().pointerHandlers.removeOne(this); + } + + m_target = target; + if (m_target) { + QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); + p->extra.value().pointerHandlers.append(this); + // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, + // because there can be multiple handlers... + m_target->setAcceptedMouseButtons(Qt::AllButtons); + } + emit targetChanged(); +} + +void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + if (!m_enabled) + return; + m_currentEvent = event; +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h new file mode 100644 index 0000000000..b20f20d8f9 --- /dev/null +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 QQUICKPOINTERHANDLER_H +#define QQUICKPOINTERHANDLER_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 "qevent.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged) + +public: + QQuickPointerHandler(QObject *parent = 0); + virtual ~QQuickPointerHandler() { } + +public: + bool enabled() const { return m_enabled; } + void setEnabled(bool enabled); + + QQuickItem *target() const { return m_target; } + void setTarget(QQuickItem *target); + + void handlePointerEvent(QQuickPointerEvent *event) { handlePointerEventImpl(event); } + +Q_SIGNALS: + void enabledChanged(); + void targetChanged(); + +protected: + QQuickPointerEvent *currentEvent() { return m_currentEvent; } + virtual void handlePointerEventImpl(QQuickPointerEvent *event); + void setGrab(QQuickEventPoint *point, bool grab); + +private: + QQuickPointerEvent *m_currentEvent; + QQuickItem *m_target; + bool m_enabled; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerHandler) + +#endif // QQUICKPOINTERHANDLER_H diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index dcba5c2d71..b4650a215a 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -44,6 +44,7 @@ #include "qquickwindow_p.h" #include "qquickevents_p_p.h" +#include #include #include @@ -1763,6 +1764,8 @@ void QQuickFlickablePrivate::data_append(QQmlListProperty *prop, QObjec i->setParentItem(static_cast(prop->data)->contentItem); } else { o->setParent(prop->object); // XXX todo - do we want this? + if (QQuickPointerHandler *pointerHandler = qmlobject_cast(o)) + pointerHandler->setTarget(static_cast(prop->object)); } } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 84f9b0f169..a4be47380e 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET) Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) +Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) { @@ -3187,7 +3189,9 @@ void QQuickItemPrivate::data_append(QQmlListProperty *prop, QObject *o) } else { if (o->inherits("QGraphicsItem")) qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); - else { + else if (QQuickPointerHandler *pointerHandler = qmlobject_cast(o)) { + pointerHandler->setTarget(that); + } else { QQuickWindow *thisWindow = qmlobject_cast(o); QQuickItem *item = that; QQuickWindow *itemWindow = that->window(); @@ -4964,6 +4968,17 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e) } #endif // QT_NO_IM +void QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event) +{ + Q_Q(QQuickItem); + if (extra.isAllocated()) { + for (QQuickPointerHandler *handler : extra->pointerHandlers) { + qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; + handler->handlePointerEvent(event); + } + } +} + /*! Called when \a change occurs for this item. diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 9ea2712e18..94180309a3 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -85,6 +85,7 @@ class QQuickItemKeyFilter; class QQuickLayoutMirroringAttached; class QQuickEnterKeyAttached; class QQuickScreenAttached; +class QQuickPointerHandler; class QQuickContents : public QQuickItemChangeListener { @@ -340,6 +341,7 @@ public: QQuickLayoutMirroringAttached* layoutDirectionAttached; QQuickEnterKeyAttached *enterKeyAttached; QQuickItemKeyFilter *keyHandler; + QVector pointerHandlers; mutable QQuickItemLayer *layer; #ifndef QT_NO_CURSOR QCursor cursor; @@ -555,6 +557,8 @@ public: void deliverInputMethodEvent(QInputMethodEvent *); #endif + virtual void handlePointerEvent(QQuickPointerEvent *); + bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 16cbb2aa2f..d73f8cd2c4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1618,6 +1618,12 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven return; } + // Let the Item's handlers (if any) have the event first. + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(grabber); + itemPrivate->handlePointerEvent(pointerEvent); + if (pointerEvent->allPointsAccepted()) + return; + // send update QPointF localPos = grabber->mapFromScene(lastMousePosition); auto me = pointerEvent->asMouseEvent(localPos); @@ -2232,6 +2238,12 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet *hasFiltered) { Q_Q(QQuickWindow); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + + // Let the Item's handlers (if any) have the event first. + itemPrivate->handlePointerEvent(pointerEvent); + if (pointerEvent->allPointsAccepted()) + return true; // TODO: unite this mouse point delivery with the synthetic mouse event below if (auto event = pointerEvent->asPointerMouseEvent()) { @@ -2283,7 +2295,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event if (deliverTouchAsMouse(item, event)) diff --git a/src/quick/quick.pro b/src/quick/quick.pro index f74a554aa9..e89db182f3 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -32,6 +32,7 @@ ANDROID_BUNDLED_FILES += \ include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) +include(handlers/handlers.pri) include(designer/designer.pri) contains(QT_CONFIG, accessibility) { include(accessible/accessible.pri) -- cgit v1.2.3 From 199692e06c839c64dd888a0a85c403e5c247c40e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 5 Aug 2016 08:28:15 +0200 Subject: QQuickPointerHandler: add eventPos() accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to compensate for the fact that the EventPoint does not store the Item-localized position, only scenePos. Either it should store it or we have to recalculate it in each Handler. Change-Id: I5b02814d5fd7930bb32b547c1af00808b8e23b80 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 5 +++++ src/quick/handlers/qquickpointerhandler_p.h | 1 + 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 9964d8f465..37238c4d51 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -71,6 +71,11 @@ void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) point->setGrabber(nullptr); } +QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const +{ + return (m_target ? m_target->mapFromScene(point->scenePos()) : point->scenePos()); +} + /*! \qmlproperty QQuickPointerHandler::enabled diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index b20f20d8f9..c6978b027c 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -87,6 +87,7 @@ protected: QQuickPointerEvent *currentEvent() { return m_currentEvent; } virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setGrab(QQuickEventPoint *point, bool grab); + QPointF eventPos(const QQuickEventPoint *point) const; private: QQuickPointerEvent *m_currentEvent; -- cgit v1.2.3 From 6ba9707e38ae3e2f59aeb35d5367093a7e513165 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 15 Aug 2016 17:25:41 +0200 Subject: add QQuickPointerHandler::targetContains(QQuickEventPoint*) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most handlers will need to check this. Change-Id: I44858c0ac4ccb58a9a2de70382e3954af6554ef3 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 7 +++++++ src/quick/handlers/qquickpointerhandler_p.h | 1 + 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 37238c4d51..c0815a5a27 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -76,6 +76,13 @@ QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const return (m_target ? m_target->mapFromScene(point->scenePos()) : point->scenePos()); } +bool QQuickPointerHandler::targetContains(const QQuickEventPoint *point) const +{ + if (!m_target || !point) + return false; + return m_target->contains(m_target->mapFromScene(point->scenePos())); +} + /*! \qmlproperty QQuickPointerHandler::enabled diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index c6978b027c..7f335146c0 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -88,6 +88,7 @@ protected: virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setGrab(QQuickEventPoint *point, bool grab); QPointF eventPos(const QQuickEventPoint *point) const; + bool targetContains(const QQuickEventPoint *point) const; private: QQuickPointerEvent *m_currentEvent; -- cgit v1.2.3 From 3eed20c22ee046c42fec81b0d9d6e47a27bdbe03 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 17 Aug 2016 13:59:33 +0200 Subject: Make ctor and dtor of QQuickPointerDevice private Since its used in a Q_GLOBAL_STATIC which requires its type to have a public ctor, we have to delegate the ctor to a subclass (named ConstructableQQuickPointerDevice). Change-Id: I51932f16254f6ec6e512c78b280b307d6e0a300e Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 10 +++++++++- src/quick/items/qquickevents_p_p.h | 29 ++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 59faafa023..47cfec14da 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -446,7 +446,15 @@ Item { typedef QHash PointerDeviceForTouchDeviceHash; Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices) -Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice, +struct ConstructableQQuickPointerDevice : public QQuickPointerDevice +{ + ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, + int maxPoints, int buttonCount, const QString &name, + qint64 uniqueId = 0) + : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {} + +}; +Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice, (QQuickPointerDevice::Mouse, QQuickPointerDevice::GenericPointer, QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover, diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 61bbb4ecda..bb3ab52d1a 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -495,20 +495,7 @@ public: Q_ENUM(CapabilityFlag) Q_FLAG(Capabilities) - QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) - : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) - , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) - { - if (m_deviceType == Mouse) { - m_event = new QQuickPointerMouseEvent; - } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { - m_event = new QQuickPointerTouchEvent; - } else { - Q_ASSERT(false); - } - } - ~QQuickPointerDevice() { delete m_event; } DeviceType type() const { return m_deviceType; } PointerType pointerType() const { return m_pointerType; } Capabilities capabilities() const { return m_capabilities; } @@ -524,6 +511,21 @@ public: static QQuickPointerDevice *genericMouseDevice(); static QQuickPointerDevice *tabletDevice(qint64); +private: + QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) + : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) + , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr) + { + if (m_deviceType == Mouse) { + m_event = new QQuickPointerMouseEvent; + } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) { + m_event = new QQuickPointerTouchEvent; + } else { + Q_ASSERT(false); + } + } + ~QQuickPointerDevice() { delete m_event; } + private: DeviceType m_deviceType; PointerType m_pointerType; @@ -536,6 +538,7 @@ private: QQuickPointerEvent *m_event; Q_DISABLE_COPY(QQuickPointerDevice) + friend struct ConstructableQQuickPointerDevice; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes) -- cgit v1.2.3 From 4e2392cf14d8c33bd8478f3a0e584946f6c5b151 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 19 Aug 2016 14:19:45 +0200 Subject: use device ID 1 for mouse events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't want it to be zero. Change-Id: Id5648bf254ad113d212ce8f1c64ec5ed3d5fa803 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 89434f3804..51a7e8aec7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -596,7 +596,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) default: break; } - m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0 + m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 32, ev->timestamp()); // mouse has device ID 1 return this; } -- cgit v1.2.3 From ad96378ba27d5a89cf447d912cdb8ae6cd6c34ec Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 15 Aug 2016 16:11:59 +0200 Subject: add QQuickEventPoint::velocity property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ifc3fc767546fc8d18ebaba0a07a1143239c2e194 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 7 ++++--- src/quick/items/qquickevents_p_p.h | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 51a7e8aec7..7e84d27836 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -511,7 +511,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } -void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp) +void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp, QVector2D velocity) { m_scenePos = scenePos; m_pointId = pointId; @@ -521,7 +521,8 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint6 m_timestamp = timestamp; if (state == Qt::TouchPointPressed) m_pressTimestamp = timestamp; - // TODO calculate velocity + // TODO if Q_LIKELY(velocity.isNull) calculate velocity + m_velocity = velocity; } QQuickItem *QQuickEventPoint::grabber() const @@ -548,7 +549,7 @@ QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { - QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp); + QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity()); m_rotation = tp.rotation(); m_pressure = tp.pressure(); m_uniqueId = tp.uniqueId(); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index bb3ab52d1a..c64478e856 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -250,6 +250,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) @@ -268,12 +269,13 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp); + void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp, QVector2D velocity = QVector2D()); void invalidate() { m_valid = false; } QQuickPointerEvent *pointerEvent() const; QPointF scenePos() const { return m_scenePos; } + QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } quint64 pointId() const { return m_pointId; } bool isValid() const { return m_valid; } @@ -285,6 +287,7 @@ public: private: QPointF m_scenePos; + QVector2D m_velocity; quint64 m_pointId; QPointer m_grabber; ulong m_timestamp; -- cgit v1.2.3 From a0cb20c08db13003f7b8adc2f02fdcadf96fb8d1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 15 Aug 2016 14:14:07 +0200 Subject: add QQuickPointerHandler::wantsPointerEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There will be an inheritance hierarchy of pointer handlers. The base class doesn't have enough information to know whether an event should be accepted or rejected, so it cannot change the state of the event, so there's no way for a subclass to benefit from the base class's checking of m_enabled, if it has no way to communicate to the subclasses. An intermediate class has more information which contributes to the decision of whether to accept or reject, but still not enough: the leaf class must make the final decision. So we need a virtual bool function, at least as an implementation detail, even though from the perspective of event delivery, there's only one outcome: the event is accepted, or not. We have eschewed public bool functions to avoid ambiguity during delivery: there is no return value to pay attention to, only the accepted state of the event. Change-Id: Ieaca4968fce0064f80b5460aa4c10b431036e12a Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 14 ++++++++++++-- src/quick/handlers/qquickpointerhandler_p.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index c0815a5a27..293eae914e 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -122,10 +122,20 @@ void QQuickPointerHandler::setTarget(QQuickItem *target) emit targetChanged(); } +void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) +{ + if (wantsPointerEvent(event)) + handlePointerEventImpl(event); +} + +bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + Q_UNUSED(event) + return m_enabled; +} + void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) { - if (!m_enabled) - return; m_currentEvent = event; } diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 7f335146c0..ada77d8eaa 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -77,7 +77,7 @@ public: QQuickItem *target() const { return m_target; } void setTarget(QQuickItem *target); - void handlePointerEvent(QQuickPointerEvent *event) { handlePointerEventImpl(event); } + void handlePointerEvent(QQuickPointerEvent *event); Q_SIGNALS: void enabledChanged(); @@ -85,6 +85,7 @@ Q_SIGNALS: protected: QQuickPointerEvent *currentEvent() { return m_currentEvent; } + virtual bool wantsPointerEvent(QQuickPointerEvent *event); virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setGrab(QQuickEventPoint *point, bool grab); QPointF eventPos(const QQuickEventPoint *point) const; -- cgit v1.2.3 From 8a06075f48ad352acfc70111682e7decf8fedb33 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 8 Aug 2016 17:09:15 +0200 Subject: QQuickEventPoint::grabber: allow grabbing either an item or a handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQuickPointerHandler and QQuickItem have only QObject in common. Change-Id: I8fb68cc1779f42049db1e0eb5ff60019a1c674d3 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 7 ++- src/quick/items/qquickevents.cpp | 56 +++++++++++++++++------ src/quick/items/qquickevents_p_p.h | 24 ++++++---- src/quick/items/qquickwindow.cpp | 69 ++++++++++++++++------------- src/quick/items/qquickwindow_p.h | 2 +- 5 files changed, 101 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 293eae914e..038e7f2571 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -64,11 +64,10 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) { - // TODO eventually the handler itself should be the grabber, instead of the target Item if (grab) - point->setGrabber(m_target); - else if (point->grabber() == m_target) - point->setGrabber(nullptr); + point->setPointerHandlerGrabber(this); + else if (point->pointerHandlerGrabber() == this) + point->setPointerHandlerGrabber(nullptr); } QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 7e84d27836..e8f333f953 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -40,12 +40,14 @@ #include "qquickevents_p_p.h" #include #include +#include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events") +Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) /*! \qmltype KeyEvent @@ -525,14 +527,40 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint6 m_velocity = velocity; } -QQuickItem *QQuickEventPoint::grabber() const +QObject *QQuickEventPoint::grabber() const { return m_grabber.data(); } -void QQuickEventPoint::setGrabber(QQuickItem *grabber) +void QQuickEventPoint::setGrabber(QObject *grabber) { - m_grabber = QPointer(grabber); + qCDebug(lcPointerHandlerDispatch) << this << grabber; + m_grabber = QPointer(grabber); + m_grabberIsHandler = (qmlobject_cast(grabber) != nullptr); +} + +QQuickItem *QQuickEventPoint::itemGrabber() const +{ + return (m_grabberIsHandler ? nullptr : static_cast(m_grabber.data())); +} + +void QQuickEventPoint::setItemGrabber(QQuickItem *grabber) +{ + qCDebug(lcPointerHandlerDispatch) << this << grabber; + m_grabber = QPointer(grabber); + m_grabberIsHandler = false; +} + +QQuickPointerHandler *QQuickEventPoint::pointerHandlerGrabber() const +{ + return (m_grabberIsHandler ? static_cast(m_grabber.data()) : nullptr); +} + +void QQuickEventPoint::setPointerHandlerGrabber(QQuickPointerHandler *grabber) +{ + qCDebug(lcPointerHandlerDispatch) << this << grabber; + m_grabber = QPointer(grabber); + m_grabberIsHandler = true; } void QQuickEventPoint::setAccepted(bool accepted) @@ -620,11 +648,11 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); // Make sure the grabbers are right from one event to the next - QVector grabbers; + QVector grabbers; // Copy all grabbers, because the order of points might have changed in the event. // The ID is all that we can rely on (release might remove the first point etc). for (int i = 0; i < newPointCount; ++i) { - QQuickItem *grabber = nullptr; + QObject *grabber = nullptr; if (auto point = pointById(tps.at(i).id())) grabber = point->grabber(); grabbers.append(grabber); @@ -636,7 +664,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) if (point->state() == QQuickEventPoint::Pressed) { if (grabbers.at(i)) qWarning() << "TouchPointPressed without previous release event" << point; - point->setGrabber(nullptr); + point->setItemGrabber(nullptr); } else { point->setGrabber(grabbers.at(i)); } @@ -659,7 +687,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), - m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false) + m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false), m_grabberIsHandler(false) { Q_UNUSED(m_reserved); } @@ -680,16 +708,16 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons return event; } -QVector QQuickPointerMouseEvent::grabbers() const +QVector QQuickPointerMouseEvent::grabbers() const { - QVector result; - if (QQuickItem *grabber = m_mousePoint->grabber()) + QVector result; + if (QObject *grabber = m_mousePoint->grabber()) result << grabber; return result; } void QQuickPointerMouseEvent::clearGrabbers() const { - m_mousePoint->setGrabber(nullptr); + m_mousePoint->setItemGrabber(nullptr); } bool QQuickPointerMouseEvent::isPressEvent() const @@ -707,12 +735,12 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } -QVector QQuickPointerTouchEvent::grabbers() const +QVector QQuickPointerTouchEvent::grabbers() const { - QVector result; + QVector result; for (int i = 0; i < m_pointCount; ++i) { auto point = m_touchPoints.at(i); - if (QQuickItem *grabber = point->grabber()) { + if (QObject *grabber = point->grabber()) { if (!result.contains(grabber)) result << grabber; } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index c64478e856..b954c8a4ec 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -68,6 +68,7 @@ class QQuickPointerEvent; class QQuickPointerMouseEvent; class QQuickPointerTabletEvent; class QQuickPointerTouchEvent; +class QQuickPointerHandler; class QQuickKeyEvent : public QObject { @@ -255,7 +256,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber) + Q_PROPERTY(QObject *grabber READ grabber WRITE setGrabber) public: enum State { @@ -282,20 +283,27 @@ public: qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); - QQuickItem *grabber() const; - void setGrabber(QQuickItem *grabber); + QObject *grabber() const; + void setGrabber(QObject *grabber); + + QQuickItem *itemGrabber() const; + void setItemGrabber(QQuickItem *grabber); + + QQuickPointerHandler *pointerHandlerGrabber() const; + void setPointerHandlerGrabber(QQuickPointerHandler *grabber); private: QPointF m_scenePos; QVector2D m_velocity; quint64 m_pointId; - QPointer m_grabber; + QPointer m_grabber; ulong m_timestamp; ulong m_pressTimestamp; State m_state; bool m_valid : 1; bool m_accept : 1; - int m_reserved : 30; + bool m_grabberIsHandler : 1; + int m_reserved : 29; Q_DISABLE_COPY(QQuickEventPoint) }; @@ -368,7 +376,7 @@ public: // helpers for C++ only (during event delivery) virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; - virtual QVector grabbers() const = 0; + virtual QVector grabbers() const = 0; virtual void clearGrabbers() const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -397,7 +405,7 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; - QVector grabbers() const override; + QVector grabbers() const override; void clearGrabbers() const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -427,7 +435,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; - QVector grabbers() const override; + QVector grabbers() const override; void clearGrabbers() const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fc6bfc43a4..b2727f113e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -46,6 +46,7 @@ #include "qquickevents_p_p.h" #include +#include #include #include @@ -656,7 +657,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!q->mouseGrabberItem()) item->grabMouse(); auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setGrabber(item); + pointerEventPoint->setItemGrabber(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); @@ -740,14 +741,14 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabber(grabber); + event->point(0)->setItemGrabber(grabber); if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) - point->setGrabber(grabber); + point->setItemGrabber(grabber); } if (oldGrabber) { @@ -758,10 +759,10 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) } } -void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector &ids) +void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector &ids) { Q_Q(QQuickWindow); - QSet ungrab; + QSet ungrab; for (int i = 0; i < ids.count(); ++i) { // FIXME: deprecate this function, we need a device const auto touchDevices = QQuickPointerDevice::touchDevices(); @@ -769,7 +770,7 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectorpointerEvent()->pointById(ids.at(i)); if (!point) continue; - QQuickItem *oldGrabber = point->grabber(); + QObject *oldGrabber = point->grabber(); if (oldGrabber == grabber) continue; @@ -784,8 +785,10 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVectortouchUngrabEvent(); + for (QObject *oldGrabber : qAsConst(ungrab)) + if (QQuickItem *item = qmlobject_cast(oldGrabber)) + item->touchUngrabEvent(); + // TODO else if the old grabber was a PointerHandler, notify it somehow? } void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) @@ -797,7 +800,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->grabber() == grabber) { - pointerEvent->point(i)->setGrabber(nullptr); + pointerEvent->point(i)->setItemGrabber(nullptr); // FIXME send ungrab event only once grabber->touchUngrabEvent(); } @@ -1466,7 +1469,7 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount()); - return event->point(0)->grabber(); + return event->point(0)->itemGrabber(); } @@ -1609,7 +1612,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven Q_Q(QQuickWindow); auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - QQuickItem *grabber = point->grabber(); + + if (point->pointerHandlerGrabber()) { + point->pointerHandlerGrabber()->handlePointerEvent(pointerEvent); + return; + } + + QQuickItem *grabber = point->itemGrabber(); if (grabber) { // if the update consists of changing button state, then don't accept it // unless the button is one in which the item is interested @@ -1620,12 +1629,6 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven return; } - // Let the Item's handlers (if any) have the event first. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(grabber); - itemPrivate->handlePointerEvent(pointerEvent); - if (pointerEvent->allPointsAccepted()) - return; - // send update QPointF localPos = grabber->mapFromScene(lastMousePosition); auto me = pointerEvent->asMouseEvent(localPos); @@ -1845,10 +1848,14 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); - QVector grabbers = pointerEvent->grabbers(); - - for (QQuickItem *grabber: qAsConst(grabbers)) { - q->sendEvent(grabber, event); + QVector grabbers = pointerEvent->grabbers(); + + for (QObject *grabber: qAsConst(grabbers)) { + if (QQuickItem *grabberItem = qmlobject_cast(grabber)) + q->sendEvent(grabberItem, event); + else //if (QQuickPointerHandler *grabberHandler = qmlobject_cast(grabber)) +// grabberHandler->handlePointerEvent() + qWarning("unexpected: can't deliver touch cancel to a PointerHandler (yet?)"); } touchMouseId = -1; touchMouseDevice = nullptr; @@ -1986,8 +1993,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { - Q_Q(QQuickWindow); - if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2020,7 +2025,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!q->mouseGrabberItem()) { + if (!QQuickPointerDevice::genericMouseDevice()->pointerEvent()->point(0)->grabber()) { QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); @@ -2188,7 +2193,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; - point->setGrabber(nullptr); + point->setItemGrabber(nullptr); if (id == touchMouseId) { touchMouseId = -1; touchMouseDevice = nullptr; @@ -2208,8 +2213,12 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) { const auto grabbers = event->grabbers(); - for (auto grabber : grabbers) - deliverMatchingPointsToItem(grabber, event, hasFiltered); + for (auto grabber : grabbers) { + if (QQuickItem *grabberItem = qmlobject_cast(grabber)) + deliverMatchingPointsToItem(grabberItem, event, hasFiltered); + else if (QQuickPointerHandler *grabberHandler = qmlobject_cast(grabber)) + deliverMatchingPointsToItem(grabberHandler->target(), event, hasFiltered); + } return false; } @@ -2310,7 +2319,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto pointerEventPoint = event->pointById(point.id()); pointerEventPoint->setAccepted(); if (point.state() == Qt::TouchPointPressed) - pointerEventPoint->setGrabber(item); + pointerEventPoint->setItemGrabber(item); } } else { // But if the event was not accepted then we know this item @@ -2319,7 +2328,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (point.state() == Qt::TouchPointPressed) { if (event->pointById(point.id())->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabber(nullptr); + event->pointById(point.id())->setItemGrabber(nullptr); } } } @@ -2558,7 +2567,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; touchMouseId = tp.id(); touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setItemGrabber(target); target->grabMouse(); } filtered = true; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 3328eb65c4..a2a53fd2ce 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -141,7 +141,7 @@ public: bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent); void translateTouchEvent(QTouchEvent *touchEvent); void setMouseGrabber(QQuickItem *grabber); - void grabTouchPoints(QQuickItem *grabber, const QVector &ids); + void grabTouchPoints(QObject *grabber, const QVector &ids); void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); -- cgit v1.2.3 From 76c627b1db48390e61d1320d33743447690d3d4d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 23 Aug 2016 16:01:35 +0200 Subject: QQuickEventPoint::reset: pass scenePos and velocity as const references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ife4dff906e17f92bcbfa2003ceb9f77e42cbb6ff Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 2 +- src/quick/items/qquickevents_p_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index e8f333f953..e9d9631ed5 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -513,7 +513,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } -void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp, QVector2D velocity) +void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity) { m_scenePos = scenePos; m_pointId = pointId; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b954c8a4ec..e50819ae61 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -270,7 +270,7 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp, QVector2D velocity = QVector2D()); + void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); void invalidate() { m_valid = false; } -- cgit v1.2.3 From be763508cf45e34970c9969b49d974061bdb6c92 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 22 Aug 2016 15:15:58 +0200 Subject: add QQuickPointerHandler::active property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, a handler is active whenever wantsPointerEvent() returns true, and inactive when it returns false. Change-Id: I627762ba8f4eed167f675f220ffaed79c93c8448 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 13 ++++++++++++- src/quick/handlers/qquickpointerhandler_p.h | 8 +++++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 038e7f2571..08a1cfc86f 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -59,6 +59,7 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) , m_currentEvent(nullptr) , m_target(nullptr) , m_enabled(true) + , m_active(false) { } @@ -123,7 +124,9 @@ void QQuickPointerHandler::setTarget(QQuickItem *target) void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) { - if (wantsPointerEvent(event)) + const bool wants = wantsPointerEvent(event); + setActive(wants); + if (wants) handlePointerEventImpl(event); } @@ -133,6 +136,14 @@ bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) return m_enabled; } +void QQuickPointerHandler::setActive(bool active) +{ + if (m_active != active) { + m_active = active; + emit activeChanged(); + } +} + void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) { m_currentEvent = event; diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index ada77d8eaa..d7ac713638 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -64,6 +64,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject { Q_OBJECT Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged) public: @@ -74,6 +75,8 @@ public: bool enabled() const { return m_enabled; } void setEnabled(bool enabled); + bool active() const { return m_active; } + QQuickItem *target() const { return m_target; } void setTarget(QQuickItem *target); @@ -81,11 +84,13 @@ public: Q_SIGNALS: void enabledChanged(); + void activeChanged(); void targetChanged(); protected: QQuickPointerEvent *currentEvent() { return m_currentEvent; } virtual bool wantsPointerEvent(QQuickPointerEvent *event); + virtual void setActive(bool active); virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setGrab(QQuickEventPoint *point, bool grab); QPointF eventPos(const QQuickEventPoint *point) const; @@ -94,7 +99,8 @@ protected: private: QQuickPointerEvent *m_currentEvent; QQuickItem *m_target; - bool m_enabled; + bool m_enabled : 1; + bool m_active : 1; }; QT_END_NAMESPACE -- cgit v1.2.3 From 5df47067e21eb0cd0cf1eed9ed7cb3d857c1f42a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 23 Aug 2016 16:26:30 +0200 Subject: optimize QQuickWindowPrivate::deliverUpdatedTouchPoints() slightly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid doing qmlobject_cast twice: if it fails in one case, we know that the type must be the other one. The grabber must be either an item or a handler. Change-Id: Ie557a67963654acb006a1a3543837ea669709a23 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b2727f113e..66b87850f8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2214,10 +2214,12 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve { const auto grabbers = event->grabbers(); for (auto grabber : grabbers) { - if (QQuickItem *grabberItem = qmlobject_cast(grabber)) - deliverMatchingPointsToItem(grabberItem, event, hasFiltered); - else if (QQuickPointerHandler *grabberHandler = qmlobject_cast(grabber)) - deliverMatchingPointsToItem(grabberHandler->target(), event, hasFiltered); + // The grabber is guaranteed to be either an item or a handler, but + // we need the item in order to call deliverMatchingPointsToItem(). + QQuickItem *receiver = qmlobject_cast(grabber); + if (!receiver) + receiver = static_cast(grabber)->target(); + deliverMatchingPointsToItem(receiver, event, hasFiltered); } return false; -- cgit v1.2.3 From e2910a5263dcec0ef5e2574a5a09eea100d29443 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Jul 2016 14:22:15 +0200 Subject: add QQuickWindowPrivate::dragOverThreshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now want to start checkng this with QQuickPointerEvent. Change-Id: I6f439df4a11dcf6dc901dd55002f4c108b628c87 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 9 +++++++++ src/quick/items/qquickwindow_p.h | 1 + 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 66b87850f8..29c9a23012 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2628,6 +2628,15 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouch return overThreshold; } +bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QQuickEventPoint *p, int startDragThreshold) +{ + QStyleHints *styleHints = qApp->styleHints(); + bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance()); + qreal velocity = axis == Qt::XAxis ? p->velocity().x() : p->velocity().y(); + overThreshold |= qAbs(velocity) > styleHints->startDragVelocity(); + return overThreshold; +} + /*! \qmlproperty list Window::data \default diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index a2a53fd2ce..b13dda4ef8 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -271,6 +271,7 @@ public: static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1); static bool dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold = -1); + static bool dragOverThreshold(qreal d, Qt::Axis axis, const QQuickEventPoint *tp, int startDragThreshold = -1); // data property static void data_append(QQmlListProperty *, QObject *); -- cgit v1.2.3 From 400322244093979f42846a8547a6416a1c857868 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Jul 2016 15:36:10 +0200 Subject: add QQuickPointerDeviceHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many handlers are interested only in events which come from specific devices, device types, or mouse buttons. This provides common filtering for them. Change-Id: I30f406ea0c91eb016bc5fb989636787eb680dcbf Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 2 + src/quick/handlers/qquickpointerdevicehandler.cpp | 109 ++++++++++++++++++++++ src/quick/handlers/qquickpointerdevicehandler_p.h | 97 +++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 src/quick/handlers/qquickpointerdevicehandler.cpp create mode 100644 src/quick/handlers/qquickpointerdevicehandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index 0c6bd2ab17..2eed3855d3 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -1,7 +1,9 @@ HEADERS += \ $$PWD/qquickpointerhandler_p.h \ + $$PWD/qquickpointerdevicehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ SOURCES += \ $$PWD/qquickpointerhandler.cpp \ + $$PWD/qquickpointerdevicehandler.cpp \ $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp new file mode 100644 index 0000000000..03704ac09e --- /dev/null +++ b/src/quick/handlers/qquickpointerdevicehandler.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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 "qquickpointerdevicehandler_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + An intermediate class (not registered as a QML type) + for handlers which allow filtering based on device type, + pointer type, or device-specific buttons (such as mouse or stylus buttons). + */ +QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent) + : QQuickPointerHandler(parent) + , m_acceptedDevices(QQuickPointerDevice::AllDevices) + , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes) + , m_acceptedButtons(Qt::AllButtons) +{ +} + +QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler() +{ +} + +void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices) +{ + if (m_acceptedDevices == acceptedDevices) + return; + + m_acceptedDevices = acceptedDevices; + emit acceptedDevicesChanged(); +} + +void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes) +{ + if (m_acceptedPointerTypes == acceptedPointerTypes) + return; + + m_acceptedPointerTypes = acceptedPointerTypes; + emit acceptedPointerTypesChanged(); +} + +void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (m_acceptedButtons == buttons) + return; + + m_acceptedButtons = buttons; + emit acceptedButtonsChanged(); +} + +bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerHandler::wantsPointerEvent(event)) + return false; + qCDebug(lcPointerHandlerDispatch) << objectName() + << "checking device type" << m_acceptedDevices + << "pointer type" << m_acceptedPointerTypes + << "buttons" << m_acceptedButtons; + if ((event->device()->type() & m_acceptedDevices) == 0) + return false; + if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0) + return false; + if (event->device()->pointerType() != QQuickPointerDevice::Finger && + (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0) + return false; + return true; +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h new file mode 100644 index 0000000000..9e93d79e7c --- /dev/null +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 QQUICKPOINTERDEVICEHANDLER_H +#define QQUICKPOINTERDEVICEHANDLER_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 "qquickpointerhandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler +{ + Q_OBJECT + Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged) + Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged) + Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) + +public: + QQuickPointerDeviceHandler(QObject *parent = 0); + ~QQuickPointerDeviceHandler(); + + QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; } + QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; } + Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } + +public slots: + void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices); + void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes); + void setAcceptedButtons(Qt::MouseButtons buttons); + +Q_SIGNALS: + void acceptedDevicesChanged(); + void acceptedPointerTypesChanged(); + void acceptedButtonsChanged(); + +protected: + bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; + void setPressed(bool pressed); + +protected: + QQuickPointerDevice::DeviceTypes m_acceptedDevices; + QQuickPointerDevice::PointerTypes m_acceptedPointerTypes; + Qt::MouseButtons m_acceptedButtons; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerDeviceHandler) + +#endif // QQUICKPOINTERDEVICEHANDLER_H -- cgit v1.2.3 From a2dd1ee46ccdff16d5ba589cdce03e36ca93497e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 15 Aug 2016 15:01:14 +0200 Subject: add QQuickPointerSingleHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An intermediate class for convenience in implementing handlers which are only interested in single points. Such handlers grab the first point which occurs inside the target item, and ignore all other points. Change-Id: Ib4cdd8c12c0e74dbfdcf701d3ec06e0429f2bcc9 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 2 + src/quick/handlers/qquickpointersinglehandler.cpp | 105 ++++++++++++++++++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 83 +++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 src/quick/handlers/qquickpointersinglehandler.cpp create mode 100644 src/quick/handlers/qquickpointersinglehandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index 2eed3855d3..c91b259107 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -1,9 +1,11 @@ HEADERS += \ $$PWD/qquickpointerhandler_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ SOURCES += \ $$PWD/qquickpointerhandler.cpp \ $$PWD/qquickpointerdevicehandler.cpp \ + $$PWD/qquickpointersinglehandler.cpp \ $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp new file mode 100644 index 0000000000..135726105b --- /dev/null +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 "qquickpointersinglehandler_p.h" + +QT_BEGIN_NAMESPACE + +/*! + An intermediate class (not registered as a QML type) + for the most common handlers: those which expect only a single point. + wantsPointerEvent() will choose the first point which is inside the + \l target item, and return true as long as the event contains that point. + Override handleEventPoint() to implement a single-point handler. +*/ + +QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) + : QQuickPointerDeviceHandler(parent) + , m_currentPoint(nullptr) + , m_currentPointId(0) +{ +} + +bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) + return false; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) { + QQuickEventPoint *p = event->point(i); + if (m_currentPointId) { + if (m_currentPointId == p->pointId()) { + m_currentPoint = p; + return true; + } + } else { + if (p->grabber()) { + if (p->grabber() == target()) + setCurrentPoint(p); + else + continue; + } else { + if (targetContains(p)) + setCurrentPoint(p); + } + if (m_currentPoint) + return true; + } + } + // If we didn't return yet, there are no interesting points + setCurrentPoint(nullptr); + return false; +} + +void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + QQuickPointerDeviceHandler::handlePointerEventImpl(event); + handleEventPoint(m_currentPoint); + // If the point is released or was rejected, this handler is done handling that point. + if (m_currentPoint->state() == QQuickEventPoint::Released || !m_currentPoint->grabber()) + setCurrentPoint(nullptr); +} + +void QQuickPointerSingleHandler::setCurrentPoint(QQuickEventPoint *p) +{ + m_currentPoint = p; + m_currentPointId = p ? p->pointId() : 0; +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h new file mode 100644 index 0000000000..c05a5cfc62 --- /dev/null +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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 QQUICKPOINTERSINGLEHANDLER_H +#define QQUICKPOINTERSINGLEHANDLER_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 "qquickpointerdevicehandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler +{ + Q_OBJECT + +public: + QQuickPointerSingleHandler(QObject *parent = 0); + virtual ~QQuickPointerSingleHandler() { } + +protected: + bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; + void handlePointerEventImpl(QQuickPointerEvent *event) Q_DECL_OVERRIDE; + virtual void handleEventPoint(QQuickEventPoint *point) = 0; + +private: + void setCurrentPoint(QQuickEventPoint *p); + +private: + QQuickEventPoint *m_currentPoint; + quint64 m_currentPointId; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPointerSingleHandler) + +#endif // QQUICKPOINTERSINGLEHANDLER_H -- cgit v1.2.3 From 31e757ad30f415a0e106b436388ed9a5aae1d177 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 Aug 2016 16:29:39 +0200 Subject: QQuickPointerSingleHandler: auto-grab if the point is accepted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5af5127883c687997653ed3184183bdce0512996 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 135726105b..45cb23f4bf 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -91,8 +91,9 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even { QQuickPointerDeviceHandler::handlePointerEventImpl(event); handleEventPoint(m_currentPoint); - // If the point is released or was rejected, this handler is done handling that point. - if (m_currentPoint->state() == QQuickEventPoint::Released || !m_currentPoint->grabber()) + bool grab = m_currentPoint->isAccepted() && m_currentPoint->state() != QQuickEventPoint::Released; + setGrab(m_currentPoint, grab); + if (!grab) setCurrentPoint(nullptr); } -- cgit v1.2.3 From d0922fcd0ac6707a46cbfd8422aeee5e36a49202 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 22 Aug 2016 14:57:02 +0200 Subject: add QQuickEventPoint::scenePressPos property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's important for some handlers to track the total movement so far. Change-Id: If729c48731725aa035c32d203d719ea80df83078 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 4 +++- src/quick/items/qquickevents_p_p.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index e9d9631ed5..619ec455ff 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -521,8 +521,10 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, m_accept = false; m_state = static_cast(state); m_timestamp = timestamp; - if (state == Qt::TouchPointPressed) + if (state == Qt::TouchPointPressed) { m_pressTimestamp = timestamp; + m_scenePressPos = scenePos; + } // TODO if Q_LIKELY(velocity.isNull) calculate velocity m_velocity = velocity; } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index e50819ae61..5e663a4680 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -251,6 +251,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT Q_PROPERTY(QPointF scenePos READ scenePos) + Q_PROPERTY(QPointF scenePressPos READ scenePressPos) Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) @@ -276,6 +277,7 @@ public: QQuickPointerEvent *pointerEvent() const; QPointF scenePos() const { return m_scenePos; } + QPointF scenePressPos() const { return m_scenePressPos; } QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } quint64 pointId() const { return m_pointId; } @@ -294,6 +296,7 @@ public: private: QPointF m_scenePos; + QPointF m_scenePressPos; QVector2D m_velocity; quint64 m_pointId; QPointer m_grabber; -- cgit v1.2.3 From 42d2d12e150ef92bfa08c3238fb8ddfb0e77fef1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 23 Aug 2016 07:26:53 +0200 Subject: add QQuickEventPoint::sceneGrabPos property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some handlers need to track total movement of the points from the time that the gesture begins. So far it seems that will always be the same as the moment that the handler decides to grab; so it's enough to store a copy of the current position when the grab occurs. Also change setGrabber to call either setItemGrabber or setPointerHandlerGrabber so that we only need to add new features like this one in two places instead of three. Change-Id: Ia944bf27302fc305a21957e8b02174909c024a90 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 9 ++++++--- src/quick/items/qquickevents_p_p.h | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 619ec455ff..d0a32929f1 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -536,9 +536,10 @@ QObject *QQuickEventPoint::grabber() const void QQuickEventPoint::setGrabber(QObject *grabber) { - qCDebug(lcPointerHandlerDispatch) << this << grabber; - m_grabber = QPointer(grabber); - m_grabberIsHandler = (qmlobject_cast(grabber) != nullptr); + if (QQuickPointerHandler *phGrabber = qmlobject_cast(grabber)) + setPointerHandlerGrabber(phGrabber); + else + setItemGrabber(static_cast(grabber)); } QQuickItem *QQuickEventPoint::itemGrabber() const @@ -551,6 +552,7 @@ void QQuickEventPoint::setItemGrabber(QQuickItem *grabber) qCDebug(lcPointerHandlerDispatch) << this << grabber; m_grabber = QPointer(grabber); m_grabberIsHandler = false; + m_sceneGrabPos = m_scenePos; } QQuickPointerHandler *QQuickEventPoint::pointerHandlerGrabber() const @@ -563,6 +565,7 @@ void QQuickEventPoint::setPointerHandlerGrabber(QQuickPointerHandler *grabber) qCDebug(lcPointerHandlerDispatch) << this << grabber; m_grabber = QPointer(grabber); m_grabberIsHandler = true; + m_sceneGrabPos = m_scenePos; } void QQuickEventPoint::setAccepted(bool accepted) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5e663a4680..66393170ac 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -252,6 +252,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_OBJECT Q_PROPERTY(QPointF scenePos READ scenePos) Q_PROPERTY(QPointF scenePressPos READ scenePressPos) + Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) @@ -278,6 +279,7 @@ public: QQuickPointerEvent *pointerEvent() const; QPointF scenePos() const { return m_scenePos; } QPointF scenePressPos() const { return m_scenePressPos; } + QPointF sceneGrabPos() const { return m_sceneGrabPos; } QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } quint64 pointId() const { return m_pointId; } @@ -297,6 +299,7 @@ public: private: QPointF m_scenePos; QPointF m_scenePressPos; + QPointF m_sceneGrabPos; QVector2D m_velocity; quint64 m_pointId; QPointer m_grabber; -- cgit v1.2.3 From 124181973f586a67990c9a68e25a3642623b998a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Jul 2016 14:22:15 +0200 Subject: Introduce DragHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A handler for dragging Items around by touch or mouse. Change-Id: Id83fea568095eb6374f3f1abc6f550d81f3731df Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 4 + src/quick/handlers/qquickdraghandler.cpp | 160 ++++++++++++++++++++++++++++ src/quick/handlers/qquickdraghandler_p.h | 130 ++++++++++++++++++++++ src/quick/handlers/qquickhandlersmodule.cpp | 4 + 4 files changed, 298 insertions(+) create mode 100644 src/quick/handlers/qquickdraghandler.cpp create mode 100644 src/quick/handlers/qquickdraghandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index c91b259107..c337253759 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -1,10 +1,14 @@ HEADERS += \ + $$PWD/qquickdraghandler_p.h \ $$PWD/qquickpointerhandler_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ + $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickhandlersmodule_p.h \ SOURCES += \ + $$PWD/qquickdraghandler.cpp \ $$PWD/qquickpointerhandler.cpp \ $$PWD/qquickpointerdevicehandler.cpp \ $$PWD/qquickpointersinglehandler.cpp \ diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp new file mode 100644 index 0000000000..e812b76399 --- /dev/null +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 "qquickdraghandler_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype DragHandler + \instantiates QQuickDragHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for dragging + + DragHandler is a handler that is used to interactively move an Item. + + At this time, drag-and-drop is not yet supported. + + \sa Drag, MouseArea +*/ + +QQuickDragHandler::QQuickDragHandler(QObject *parent) + : QQuickPointerSingleHandler(parent) + , m_dragging(false) +{ +} + +QQuickDragHandler::~QQuickDragHandler() +{ +} + +void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) +{ + // If there's no target or the target has no parent, we shouldn't be dragging + if (!target() || !target()->parentItem()) + return; + point->setAccepted(); + switch (point->state()) { + case QQuickEventPoint::Pressed: + m_startPos = target()->mapToScene(QPointF()); + break; + case QQuickEventPoint::Updated: { + QPointF delta = point->scenePos() - point->scenePressPos(); + if (!m_xAxis.enabled()) + delta.setX(0); + if (!m_yAxis.enabled()) + delta.setY(0); + if (m_dragging) { + QPointF pos = target()->parentItem()->mapFromScene(m_startPos) + delta; + enforceAxisConstraints(&pos); + target()->setPosition(pos); + } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || + (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { + m_dragging = true; + emit draggingChanged(); + } + } break; + case QQuickEventPoint::Released: + if (m_dragging) { + m_dragging = false; + emit draggingChanged(); + } + break; + default: + break; + } +} + +void QQuickDragHandler::enforceConstraints() +{ + if (!target() || !target()->parentItem()) + return; + QPointF pos = target()->position(); + QPointF copy(pos); + enforceAxisConstraints(&pos); + if (pos != copy) + target()->setPosition(pos); +} + +void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos) +{ + if (m_xAxis.enabled()) + localPos->setX(qBound(m_xAxis.minimum(), localPos->x(), m_xAxis.maximum())); + if (m_yAxis.enabled()) + localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum())); +} + +QQuickDragAxis::QQuickDragAxis() + : m_minimum(-DBL_MAX) + , m_maximum(DBL_MAX) + , m_enabled(true) +{ +} + +void QQuickDragAxis::setMinimum(qreal minimum) +{ + if (m_minimum == minimum) + return; + + m_minimum = minimum; + emit minimumChanged(); +} + +void QQuickDragAxis::setMaximum(qreal maximum) +{ + if (m_maximum == maximum) + return; + + m_maximum = maximum; + emit maximumChanged(); +} + +void QQuickDragAxis::setEnabled(bool enabled) +{ + if (m_enabled == enabled) + return; + + m_enabled = enabled; + emit enabledChanged(); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h new file mode 100644 index 0000000000..0ce03c2c4c --- /dev/null +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** 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 QQUICKDRAGHANDLER_H +#define QQUICKDRAGHANDLER_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 "qquickpointersinglehandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged) + Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + +public: + QQuickDragAxis(); + + qreal minimum() const { return m_minimum; } + void setMinimum(qreal minimum); + + qreal maximum() const { return m_maximum; } + void setMaximum(qreal maximum); + + bool enabled() const { return m_enabled; } + void setEnabled(bool enabled); + +signals: + void minimumChanged(); + void maximumChanged(); + void enabledChanged(); + +private: + qreal m_minimum; + qreal m_maximum; + bool m_enabled; +}; + +class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickPointerSingleHandler +{ + Q_OBJECT + Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) + Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT) + Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) + +public: + QQuickDragHandler(QObject *parent = 0); + ~QQuickDragHandler(); + + void handleEventPoint(QQuickEventPoint *point) Q_DECL_OVERRIDE; + + QQuickDragAxis *xAxis() { return &m_xAxis; } + QQuickDragAxis *yAxis() { return &m_yAxis; } + + bool dragging() const { return m_dragging; } + + Q_INVOKABLE void enforceConstraints(); + +Q_SIGNALS: +// void gestureStarted(QQuickGestureEvent *gesture); + void draggingChanged(); + +private: + void ungrab(); + void enforceAxisConstraints(QPointF *localPos); + +private: + bool m_dragging; + QPointF m_startPos; + QQuickDragAxis m_xAxis; + QQuickDragAxis m_yAxis; + + friend class QQuickDragAxis; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDragHandler) +QML_DECLARE_TYPE(QQuickDragAxis) + +#endif // QQUICKDRAGHANDLER_H diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index dd50c367ec..398ca0484f 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -39,6 +39,7 @@ #include "qquickhandlersmodule_p.h" #include "qquickpointerhandler_p.h" +#include "qquickdraghandler_p.h" static void initResources() { @@ -74,6 +75,9 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) QQuickPointerHandler::tr("PointerUniqueId is only available as a property of PointerEvent")); qmlRegisterType(uri,major,minor,"PointerHandler"); + qmlRegisterType(uri,major,minor,"DragHandler"); + qmlRegisterUncreatableType(uri, major, minor, "DragAxis", + QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler")); } void QQuickHandlersModule::defineModule() -- cgit v1.2.3 From aac7d771a6be0ea15c521e271f4b1437fb0b6077 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 08:50:13 +0200 Subject: QQuickEventPoint: guard setPointerHandlerGrabber and setItemGrabber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If they get called too often, ensure that m_sceneGrabPos isn't getting reset each time. Change-Id: Ia280dca5a2e39b3a641f9a0b76e5a588ebcdc99c Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d0a32929f1..45e2fd3e73 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -549,10 +549,12 @@ QQuickItem *QQuickEventPoint::itemGrabber() const void QQuickEventPoint::setItemGrabber(QQuickItem *grabber) { - qCDebug(lcPointerHandlerDispatch) << this << grabber; - m_grabber = QPointer(grabber); - m_grabberIsHandler = false; - m_sceneGrabPos = m_scenePos; + if (grabber != m_grabber.data()) { + qCDebug(lcPointerHandlerDispatch) << this << grabber; + m_grabber = QPointer(grabber); + m_grabberIsHandler = false; + m_sceneGrabPos = m_scenePos; + } } QQuickPointerHandler *QQuickEventPoint::pointerHandlerGrabber() const @@ -562,10 +564,12 @@ QQuickPointerHandler *QQuickEventPoint::pointerHandlerGrabber() const void QQuickEventPoint::setPointerHandlerGrabber(QQuickPointerHandler *grabber) { - qCDebug(lcPointerHandlerDispatch) << this << grabber; - m_grabber = QPointer(grabber); - m_grabberIsHandler = true; - m_sceneGrabPos = m_scenePos; + if (grabber != m_grabber.data()) { + qCDebug(lcPointerHandlerDispatch) << this << grabber; + m_grabber = QPointer(grabber); + m_grabberIsHandler = true; + m_sceneGrabPos = m_scenePos; + } } void QQuickEventPoint::setAccepted(bool accepted) -- cgit v1.2.3 From 149911b0f45fbe1d1a4766a0bb0c9b7c3b54366d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 23 Aug 2016 12:24:14 +0200 Subject: add QQuickMultiPointerHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An intermediate base class for handlers which are concerned with multiple simultaneous touchpoints (such as PinchHandler). Change-Id: I92e1d223b6a227a6c93cff72524d62dcbf235b83 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 2 + src/quick/handlers/qquickmultipointerhandler.cpp | 198 +++++++++++++++++++++++ src/quick/handlers/qquickmultipointerhandler_p.h | 103 ++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 src/quick/handlers/qquickmultipointerhandler.cpp create mode 100644 src/quick/handlers/qquickmultipointerhandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index c337253759..3d7e656071 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -2,6 +2,7 @@ HEADERS += \ $$PWD/qquickdraghandler_p.h \ $$PWD/qquickpointerhandler_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickmultipointerhandler_p.h \ $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ @@ -11,5 +12,6 @@ SOURCES += \ $$PWD/qquickdraghandler.cpp \ $$PWD/qquickpointerhandler.cpp \ $$PWD/qquickpointerdevicehandler.cpp \ + $$PWD/qquickmultipointerhandler.cpp \ $$PWD/qquickpointersinglehandler.cpp \ $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp new file mode 100644 index 0000000000..1a000c5239 --- /dev/null +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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 "qquickmultipointerhandler_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + An intermediate class (not registered as a QML type) + for the type of handler which requires and acts upon a specific number + of multiple touchpoints. +*/ +QQuickMultiPointerHandler::QQuickMultiPointerHandler(QObject *parent, int requiredPointCount) + : QQuickPointerDeviceHandler(parent) + , m_requiredPointCount(requiredPointCount) + , m_pointDistanceThreshold(0) +{ +} + +QQuickMultiPointerHandler::~QQuickMultiPointerHandler() +{ +} + +bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) +{ + if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) + return false; + if (event->pointCount() < m_requiredPointCount) + return false; + if (sameAsCurrentPoints(event)) + return true; + m_currentPoints = pointsInsideOrNearTarget(event); + return (m_currentPoints.size() == m_requiredPointCount); +} + +QVector QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event) +{ + QVector ret; + int c = event->pointCount(); + QRectF targetBounds = target()->mapRectToScene(target()->boundingRect()) + .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold)); + for (int i = 0; i < c; ++i) { + QQuickEventPoint *p = event->point(i); + if (targetBounds.contains(p->scenePos())) + ret << p; + } + return ret; +} + +void QQuickMultiPointerHandler::setRequiredPointCount(int c) +{ + if (m_requiredPointCount == c) + return; + + m_requiredPointCount = c; + emit requiredPointCountChanged(); +} + +void QQuickMultiPointerHandler::setPointDistanceThreshold(qreal pointDistanceThreshold) +{ + if (m_pointDistanceThreshold == pointDistanceThreshold) + return; + + m_pointDistanceThreshold = pointDistanceThreshold; + emit pointDistanceThresholdChanged(); +} + +bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event) +{ + bool ret = true; + int c = event->pointCount(); + if (c != m_currentPoints.size()) + return false; + // TODO optimize: either ensure the points are sorted, + // or use std::equal with a predicate + for (int i = 0; ret && i < c; ++i) { + bool found = false; + quint64 pointId = event->point(i)->pointId(); + for (QQuickEventPoint *o : qAsConst(m_currentPoints)) + if (o && pointId == o->pointId()) + found = true; + if (!found) + ret = false; + } + return ret; +} + +// TODO make templates for these functions somehow? +QPointF QQuickMultiPointerHandler::touchPointCentroid() +{ + QPointF ret; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += point->scenePos(); + return ret / m_currentPoints.size(); +} + +QPointF QQuickMultiPointerHandler::startingCentroid() +{ + // TODO cache it in setActive()? + QPointF ret; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += point->sceneGrabPos(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref) +{ + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += (point->scenePos() - ref).manhattanLength(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageStartingDistance(const QPointF &ref) +{ + // TODO cache it in setActive()? + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += (point->sceneGrabPos() - ref).manhattanLength(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageTouchPointAngle(const QPointF &ref) +{ + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += QLineF(ref, point->scenePos()).angle(); + return ret / m_currentPoints.size(); +} + +qreal QQuickMultiPointerHandler::averageStartingAngle(const QPointF &ref) +{ + // TODO cache it in setActive()? + qreal ret = 0; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += QLineF(ref, point->sceneGrabPos()).angle(); + return ret / m_currentPoints.size(); +} + +void QQuickMultiPointerHandler::grabPoints(QVector points) +{ + for (QQuickEventPoint* point : points) + point->setPointerHandlerGrabber(this); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h new file mode 100644 index 0000000000..b63bd00ad2 --- /dev/null +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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 QQUICKPOINTERMULTIHANDLER_H +#define QQUICKPOINTERMULTIHANDLER_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 "qquickitem.h" +#include "qevent.h" +#include "qquickpointerdevicehandler_p.h" +#include "../items/qquickmultipointtoucharea_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickMultiPointerHandler : public QQuickPointerDeviceHandler +{ + Q_OBJECT + Q_PROPERTY(int requiredPointCount READ requiredPointCount WRITE setRequiredPointCount NOTIFY requiredPointCountChanged) + Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged) + +public: + QQuickMultiPointerHandler(QObject *parent = 0, int requiredPointCount = 2); + ~QQuickMultiPointerHandler(); + + int requiredPointCount() const { return m_requiredPointCount; } + void setRequiredPointCount(int c); + + qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; } + void setPointDistanceThreshold(qreal pointDistanceThreshold); + +signals: + void requiredPointCountChanged(); + void pointDistanceThresholdChanged(); + +protected: + bool wantsPointerEvent(QQuickPointerEvent *event) override; + bool sameAsCurrentPoints(QQuickPointerEvent *event); + QVector pointsInsideOrNearTarget(QQuickPointerEvent *event); + QPointF touchPointCentroid(); + QPointF startingCentroid(); + qreal averageTouchPointDistance(const QPointF &ref); + qreal averageStartingDistance(const QPointF &ref); + qreal averageTouchPointAngle(const QPointF &ref); + qreal averageStartingAngle(const QPointF &ref); + void grabPoints(QVector points); + +protected: + QVector m_currentPoints; + int m_requiredPointCount; + qreal m_pointDistanceThreshold; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickMultiPointerHandler) + +#endif // QQUICKPOINTERMULTIHANDLER_H -- cgit v1.2.3 From 678d3de4b53f22ead68a0874df2892137ad3b7f7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 11 Aug 2015 18:14:02 +0200 Subject: Introduce PinchHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to PinchArea but with improvements: - Allows rotating and scaling about an arbitrary point rather than the center - It's possible to require more than 2 fingers. E.g. maybe you use 3 fingers to manage a window, 2 fingers to manipulate content inside. This could be achieved with two independent PinchHandlers. Change-Id: Ifd40cfee115d7bc298378b26a58318bea40a8230 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 2 + src/quick/handlers/qquickhandlersmodule.cpp | 2 + src/quick/handlers/qquickpinchhandler.cpp | 201 ++++++++++++++++++++++++++++ src/quick/handlers/qquickpinchhandler_p.h | 131 ++++++++++++++++++ 4 files changed, 336 insertions(+) create mode 100644 src/quick/handlers/qquickpinchhandler.cpp create mode 100644 src/quick/handlers/qquickpinchhandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index 3d7e656071..f32f330d4e 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -3,6 +3,7 @@ HEADERS += \ $$PWD/qquickpointerhandler_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ $$PWD/qquickmultipointerhandler_p.h \ + $$PWD/qquickpinchhandler_p.h \ $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ @@ -13,5 +14,6 @@ SOURCES += \ $$PWD/qquickpointerhandler.cpp \ $$PWD/qquickpointerdevicehandler.cpp \ $$PWD/qquickmultipointerhandler.cpp \ + $$PWD/qquickpinchhandler.cpp \ $$PWD/qquickpointersinglehandler.cpp \ $$PWD/qquickhandlersmodule.cpp \ diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index 398ca0484f..97d6f1d499 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -40,6 +40,7 @@ #include "qquickhandlersmodule_p.h" #include "qquickpointerhandler_p.h" #include "qquickdraghandler_p.h" +#include "qquickpinchhandler_p.h" static void initResources() { @@ -78,6 +79,7 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri,major,minor,"DragHandler"); qmlRegisterUncreatableType(uri, major, minor, "DragAxis", QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler")); + qmlRegisterType(uri,major,minor,"PinchHandler"); } void QQuickHandlersModule::defineModule() diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp new file mode 100644 index 0000000000..ddec7eee87 --- /dev/null +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** 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 "qquickpinchhandler_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") + +/*! + \qmltype PinchHandler + \instantiates QQuickPinchHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for pinch gestures + + PinchHandler is a handler that is used to interactively rotate and zoom an Item. +*/ + +QQuickPinchHandler::QQuickPinchHandler(QObject *parent) + : QQuickMultiPointerHandler(parent, 2) + , m_startScale(1) + , m_startRotation(0) + , m_minimumScale(-qInf()) + , m_maximumScale(qInf()) + , m_minimumRotation(-qInf()) + , m_maximumRotation(qInf()) + , m_pinchOrigin(PinchCenter) +{ + connect(this, &QQuickPinchHandler::activeChanged, this, &QQuickPinchHandler::onActiveChanged); + connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); +} + +QQuickPinchHandler::~QQuickPinchHandler() +{ +} + +void QQuickPinchHandler::setMinimumScale(qreal minimumScale) +{ + if (m_minimumScale == minimumScale) + return; + + m_minimumScale = minimumScale; + emit minimumScaleChanged(); +} + +void QQuickPinchHandler::setMaximumScale(qreal maximumScale) +{ + if (m_maximumScale == maximumScale) + return; + + m_maximumScale = maximumScale; + emit maximumScaleChanged(); +} + +void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation) +{ + if (m_minimumRotation == minimumRotation) + return; + + m_minimumRotation = minimumRotation; + emit minimumRotationChanged(); +} + +void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation) +{ + if (m_maximumRotation == maximumRotation) + return; + + m_maximumRotation = maximumRotation; + emit maximumRotationChanged(); +} + +void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin) +{ + if (m_pinchOrigin == pinchOrigin) + return; + + m_pinchOrigin = pinchOrigin; + emit pinchOriginChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::minimumTouchPoints + + The pinch begins when this number of fingers are pressed. + Until then, PinchHandler tracks the positions of any pressed fingers, + but if it's an insufficient number, it does not scale or rotate + its \l target, and the \l active property will remain false. +*/ + +/*! + \qmlproperty QQuickPinchHandler::active +*/ + +void QQuickPinchHandler::onActiveChanged() +{ + if (active()) { + m_startScale = m_scaleTransform.xScale(); // TODO incompatible with independent x/y scaling + m_startRotation = m_rotationTransform.angle(); + qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + grabPoints(m_currentPoints); + } +} + +void QQuickPinchHandler::onTargetChanged() +{ + if (target()) { + // TODO if m_target was previously set differently, + // does prepending to the new target remove it from the old one? + // If not, should we fix that there, or here? + m_scaleTransform.prependToItem(target()); + m_rotationTransform.prependToItem(target()); + } +} + +void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) +{ + Q_UNUSED(event) + + if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) { + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos(); + } + + // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter + QPointF startCentroid = startingCentroid(); + QPointF centroid = touchPointCentroid(); + QVector3D origin(centroid); + + qreal startDist = averageStartingDistance(startCentroid); + qreal dist = averageTouchPointDistance(centroid); + qreal zoom = dist / startDist; + + qreal newScale = qBound(m_minimumScale, zoom * m_startScale, m_maximumScale); + m_scaleTransform.setOrigin(origin); + // TODO optionally allow separate x and y scaling? + m_scaleTransform.setXScale(newScale); + m_scaleTransform.setYScale(newScale); + + qreal startAngle = averageStartingAngle(startCentroid); + qreal angle = averageTouchPointAngle(centroid); + qreal angleDelta = startAngle - angle; + m_rotationTransform.setOrigin(origin); + m_rotationTransform.setAngle(qMin(m_maximumRotation, qMax(m_minimumRotation, m_startRotation + angleDelta))); + + // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place + + qCDebug(lcPinchHandler) << "startCentroid" << startCentroid << "centroid" << centroid << "dist" << dist << "starting dist" << startDist + << "zoom" << zoom << "startScale" << m_startScale << "startAngle" << startAngle << "angle" << angle + << "scale" << scale() << "rotation" << rotation(); + + emit updated(); +} + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h new file mode 100644 index 0000000000..0a66d5e02e --- /dev/null +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 QQUICKPINCHHANDLER_H +#define QQUICKPINCHHANDLER_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 "qquickitem.h" +#include "qevent.h" +#include "qquickmultipointerhandler_p.h" +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler +{ + Q_OBJECT + Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) + Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) + Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) + Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) + Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged) + Q_PROPERTY(QPointF center READ center NOTIFY updated) + Q_PROPERTY(qreal scale READ scale NOTIFY updated) + Q_PROPERTY(qreal rotation READ rotation NOTIFY updated) + +public: + enum PinchOrigin { + FirstPoint, PinchCenter, TargetCenter + }; + Q_ENUM(PinchOrigin) + + QQuickPinchHandler(QObject *parent = 0); + ~QQuickPinchHandler(); + + qreal minimumScale() const { return m_minimumScale; } + void setMinimumScale(qreal minimumScale); + + qreal maximumScale() const { return m_maximumScale; } + void setMaximumScale(qreal maximumScale); + + qreal minimumRotation() const { return m_minimumRotation; } + void setMinimumRotation(qreal minimumRotation); + + qreal maximumRotation() const { return m_maximumRotation; } + void setMaximumRotation(qreal maximumRotation); + + PinchOrigin pinchOrigin() const { return m_pinchOrigin; } + void setPinchOrigin(PinchOrigin pinchOrigin); + + QPointF center() const { return m_scaleTransform.origin().toPointF(); } + qreal scale() const { return m_scaleTransform.xScale(); } + qreal rotation() const { return m_rotationTransform.angle(); } + +signals: + void requiredPointCountChanged(); + void minimumScaleChanged(); + void maximumScaleChanged(); + void minimumRotationChanged(); + void maximumRotationChanged(); + void pinchOriginChanged(); + void updated(); + +protected: + void onActiveChanged(); + void onTargetChanged(); + void handlePointerEventImpl(QQuickPointerEvent *event) override; + +private: + qreal m_startScale; + qreal m_startRotation; + qreal m_minimumScale; + qreal m_maximumScale; + qreal m_minimumRotation; + qreal m_maximumRotation; + PinchOrigin m_pinchOrigin; + QQuickScale m_scaleTransform; + QQuickRotation m_rotationTransform; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPinchHandler) + +#endif // QQUICKPINCHHANDLER_H -- cgit v1.2.3 From e2b45bc19fd56b4a6484baa65f8c74f69e17ed82 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 25 Aug 2016 16:35:07 +0200 Subject: QQuickPointerSingleHandler: add currentPoint accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subclasses which override wantsPointerEvent need to be able to access the current point, but the current point ID is private. Change-Id: I42441b44b5964f2f8cfc5ec30348f3803095ddfa Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler_p.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index c05a5cfc62..cc6faf019d 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -67,6 +67,7 @@ protected: bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; void handlePointerEventImpl(QQuickPointerEvent *event) Q_DECL_OVERRIDE; virtual void handleEventPoint(QQuickEventPoint *point) = 0; + QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } private: void setCurrentPoint(QQuickEventPoint *p); -- cgit v1.2.3 From 2057e6f3a20b89ad9a9c84ea0f113beaf66ee24f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 25 Aug 2016 16:36:38 +0200 Subject: QQuickPointerSingleHandler::handlePointerEventImpl: accept current point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If wantsPointerEvent returned true, we can auto-accept. Change-Id: I968e4e5f9f7f18724557a4ef4779970550d9f9a6 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 45cb23f4bf..2a1c1008bc 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -90,6 +90,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) { QQuickPointerDeviceHandler::handlePointerEventImpl(event); + m_currentPoint->setAccepted(true); handleEventPoint(m_currentPoint); bool grab = m_currentPoint->isAccepted() && m_currentPoint->state() != QQuickEventPoint::Released; setGrab(m_currentPoint, grab); -- cgit v1.2.3 From 856332456d9f289a05bc039cfa4e875f21441207 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 07:07:54 +0200 Subject: simplify QQuickPointerSingleHandler::wantsPointerEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Storing both the current point ID and the QQuickEventPoint pointer is redundant; it was done because handlePointerEventImpl can then call handleEventPoint() directly with that pointer. But pointById isn't so expensive. Also, after 8a06075f48ad352acfc70111682e7decf8fedb33 if the handler is the grabber, the item will not be, so we don't need to consider that case as before, where the item was the grabber on behalf of the handler. Change-Id: I3657ee46c823aa955e7bfd7714bbb4c5776f7d17 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 54 ++++++++--------------- src/quick/handlers/qquickpointersinglehandler_p.h | 4 -- 2 files changed, 19 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 2a1c1008bc..1fc84cd523 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -51,7 +51,6 @@ QT_BEGIN_NAMESPACE QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) : QQuickPointerDeviceHandler(parent) - , m_currentPoint(nullptr) , m_currentPointId(0) { } @@ -60,48 +59,33 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; - int c = event->pointCount(); - for (int i = 0; i < c; ++i) { - QQuickEventPoint *p = event->point(i); - if (m_currentPointId) { - if (m_currentPointId == p->pointId()) { - m_currentPoint = p; - return true; - } - } else { - if (p->grabber()) { - if (p->grabber() == target()) - setCurrentPoint(p); - else - continue; - } else { - if (targetContains(p)) - setCurrentPoint(p); - } - if (m_currentPoint) - return true; + if (m_currentPointId) { + // We already know which one we want, so check whether it's there. + // It's expected to be an update or a release. + return (event->pointById(m_currentPointId) != nullptr); + } else { + // We have not yet chosen a point; choose the first one within target bounds. + int c = event->pointCount(); + for (int i = 0; i < c && !m_currentPointId; ++i) { + QQuickEventPoint *p = event->point(i); + if (!p->grabber() && targetContains(p)) + m_currentPointId = p->pointId(); } } - // If we didn't return yet, there are no interesting points - setCurrentPoint(nullptr); - return false; + return m_currentPointId; } void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) { QQuickPointerDeviceHandler::handlePointerEventImpl(event); - m_currentPoint->setAccepted(true); - handleEventPoint(m_currentPoint); - bool grab = m_currentPoint->isAccepted() && m_currentPoint->state() != QQuickEventPoint::Released; - setGrab(m_currentPoint, grab); + QQuickEventPoint *currentPoint = event->pointById(m_currentPointId); + Q_ASSERT(currentPoint); + currentPoint->setAccepted(true); + handleEventPoint(currentPoint); + bool grab = currentPoint->isAccepted() && currentPoint->state() != QQuickEventPoint::Released; + setGrab(currentPoint, grab); if (!grab) - setCurrentPoint(nullptr); -} - -void QQuickPointerSingleHandler::setCurrentPoint(QQuickEventPoint *p) -{ - m_currentPoint = p; - m_currentPointId = p ? p->pointId() : 0; + m_currentPointId = 0; } QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index cc6faf019d..d130ed0146 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -70,10 +70,6 @@ protected: QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } private: - void setCurrentPoint(QQuickEventPoint *p); - -private: - QQuickEventPoint *m_currentPoint; quint64 m_currentPointId; }; -- cgit v1.2.3 From 0dbb301e167292f728a845625556f2bef4699022 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 31 Aug 2016 15:01:45 +0200 Subject: QQuickPointerEvent and EventPoint: add localize(QQuickItem *) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During delivery we need to call QQuickItem::mapFromScene(point->scenePos()) so let's do it as early as possible and store the result right in the EventPoint. That way the pos() property can be made available, for the benefit of QML/JS signal handlers and also for C++ handlers. Change-Id: I2706ffd6bc51a449eeba64f69c9ffdfb913c5ca6 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 19 +++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 7 +++++++ src/quick/items/qquickwindow.cpp | 6 +++--- 3 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 45e2fd3e73..c3156d0328 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -529,6 +529,14 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, m_velocity = velocity; } +void QQuickEventPoint::localize(QQuickItem *target) +{ + if (target) + m_pos = target->mapFromScene(scenePos()); + else + m_pos = QPointF(); +} + QObject *QQuickEventPoint::grabber() const { return m_grabber.data(); @@ -638,6 +646,11 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) return this; } +void QQuickPointerMouseEvent::localize(QQuickItem *target) +{ + m_mousePoint->localize(target); +} + QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) { auto ev = static_cast(event); @@ -682,6 +695,12 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) return this; } +void QQuickPointerTouchEvent::localize(QQuickItem *target) +{ + for (auto point : qAsConst(m_touchPoints)) + point->localize(target); +} + QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { if (i == 0) return m_mousePoint; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 66393170ac..1c524e9e3b 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -250,6 +250,7 @@ private: class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT + Q_PROPERTY(QPointF pos READ pos) Q_PROPERTY(QPointF scenePos READ scenePos) Q_PROPERTY(QPointF scenePressPos READ scenePressPos) Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) @@ -273,10 +274,12 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); + void localize(QQuickItem *target); void invalidate() { m_valid = false; } QQuickPointerEvent *pointerEvent() const; + QPointF pos() const { return m_pos; } QPointF scenePos() const { return m_scenePos; } QPointF scenePressPos() const { return m_scenePressPos; } QPointF sceneGrabPos() const { return m_sceneGrabPos; } @@ -297,6 +300,7 @@ public: void setPointerHandlerGrabber(QQuickPointerHandler *grabber); private: + QPointF m_pos; QPointF m_scenePos; QPointF m_scenePressPos; QPointF m_sceneGrabPos; @@ -365,6 +369,7 @@ public: // property accessors public: // helpers for C++ only (during event delivery) virtual QQuickPointerEvent *reset(QEvent *ev) = 0; + virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } @@ -404,6 +409,7 @@ public: : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } @@ -433,6 +439,7 @@ public: { } QQuickPointerEvent *reset(QEvent *) override; + void localize(QQuickItem *target) override; bool isPressEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 24943cb49d..20d4b3c5f6 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2252,6 +2252,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); // Let the Item's handlers (if any) have the event first. itemPrivate->handlePointerEvent(pointerEvent); @@ -2264,9 +2265,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto point = event->point(0); if (point->isAccepted()) return false; - QPointF localPos = item->mapFromScene(point->scenePos()); - Q_ASSERT(item->contains(localPos)); // transform is checked already - QMouseEvent *me = event->asMouseEvent(localPos); + Q_ASSERT(item->contains(point->pos())); // transform is checked already + QMouseEvent *me = event->asMouseEvent(point->pos()); me->accept(); q->sendEvent(item, me); if (me->isAccepted()) { -- cgit v1.2.3 From 071d45469cb2674d7ac6dd4ac50551dc97878526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Thu, 8 Sep 2016 13:15:09 +0200 Subject: Do not use manhattanLength for measuring distance Since we use averageStartingDistance and averageTouchPointDistance to calculate the scale factor for the PinchHandler, we got some unacceptable scale artifacts, especially when rotating (for a 45 degree rotation, the manhattan length is sqrt(2) times longer than the actual length). This is (probably) a bit slower, but the number of points are usually quite low (range 2-5, where 2 is the most common, so the sqrt should not be calculated very often. Change-Id: Ibb3d4cc036a4acdd86a5c79c852ccf7df7ef4ebc Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 1a000c5239..81eda8c147 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -153,7 +153,7 @@ qreal QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref) if (Q_UNLIKELY(m_currentPoints.size() == 0)) return ret; for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += (point->scenePos() - ref).manhattanLength(); + ret += QVector2D(point->scenePos() - ref).length(); return ret / m_currentPoints.size(); } @@ -164,7 +164,7 @@ qreal QQuickMultiPointerHandler::averageStartingDistance(const QPointF &ref) if (Q_UNLIKELY(m_currentPoints.size() == 0)) return ret; for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += (point->sceneGrabPos() - ref).manhattanLength(); + ret += QVector2D(point->sceneGrabPos() - ref).length(); return ret / m_currentPoints.size(); } -- cgit v1.2.3 From b8106804c87f70c3be034dc3e191966293a8b877 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 2 Sep 2016 12:58:43 +0200 Subject: Move device id to be in bits 24-31 of the point id This allows us for lossless conversion between QTouchEvent::TouchPoint id and QQuickEventPoint::pointId (both ways). Change-Id: I2087847a579dd8bc5b526515ad07b55c9ae8aa42 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c3156d0328..f4b203ac53 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -642,7 +642,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) default: break; } - m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 32, ev->timestamp()); // mouse has device ID 1 + m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1 return this; } -- cgit v1.2.3 From c364b935f06256ec7adfaf486ef35b3996152804 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 2 Sep 2016 17:07:57 +0200 Subject: Fix rotation in PinchHandler If we want to allow rotation with more than 2 fingers, its not straightforward to calculate the rotation angle, because we cannot anymore calculate the angle between the two touchpoints. The approach chosen was to calculate the angle between the centroid and the touchpoints, and take the average between the angles. Then, for the next event we calculated the new average of the angles. However, this is not really reliable in some scenarios, suppose we have these three angles: 0 120 240, avg = 360/3 = 120 next, touch points rotate clockwise with 2 degrees, and we get these angles: 358 118 238, avg = 714/3 = 238 So, just by rotating all fingers by 2 degrees, we got a jump by 118 degrees "in average". Instead we need to track the angles of *all* touch points, and when the next touch event is received we calculate how much the angle has changed per touch point. We then take the average of those angles as the effective "rotation" of the PinchHandler Change-Id: I2bfdf80b886751177efe81bcc7b698af0d2938e3 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 52 ++++++--- src/quick/handlers/qquickmultipointerhandler_p.h | 9 ++ src/quick/handlers/qquickpinchhandler.cpp | 128 ++++++++++++++++++----- src/quick/handlers/qquickpinchhandler_p.h | 54 ++++++++-- 4 files changed, 195 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 81eda8c147..dcc536147f 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -168,25 +168,47 @@ qreal QQuickMultiPointerHandler::averageStartingDistance(const QPointF &ref) return ret / m_currentPoints.size(); } -qreal QQuickMultiPointerHandler::averageTouchPointAngle(const QPointF &ref) +QVector QQuickMultiPointerHandler::angles(const QPointF &ref) const { - qreal ret = 0; - if (Q_UNLIKELY(m_currentPoints.size() == 0)) - return ret; - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += QLineF(ref, point->scenePos()).angle(); - return ret / m_currentPoints.size(); + QVector angles; + angles.reserve(m_currentPoints.count()); + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { + qreal angle = QLineF(ref, point->scenePos()).angle(); + angles.append(PointData(point->pointId(), angle)); + } + return angles; } -qreal QQuickMultiPointerHandler::averageStartingAngle(const QPointF &ref) +qreal QQuickMultiPointerHandler::averageAngleDelta(const QVector &old, const QVector &newAngles) { - // TODO cache it in setActive()? - qreal ret = 0; - if (Q_UNLIKELY(m_currentPoints.size() == 0)) - return ret; - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += QLineF(ref, point->sceneGrabPos()).angle(); - return ret / m_currentPoints.size(); + qreal avgAngleDelta = 0; + int numSamples = 0; + + auto oldBegin = old.constBegin(); + + for (PointData newData : newAngles) { + quint64 id = newData.id; + auto it = std::find_if(oldBegin, old.constEnd(), [id] (PointData pd) { return pd.id == id; }); + qreal angleD = 0; + if (it != old.constEnd()) { + PointData oldData = *it; + // We might rotate from 359 degrees to 1 degree. However, this + // should be interpreted as a rotation of +2 degrees instead of + // -358 degrees. Therefore, we call remainder() to translate the angle + // to be in the range [-180, 180] (-350 to +10 etc) + angleD = remainder(newData.angle - oldData.angle, qreal(360)); + // optimization: narrow down the O(n^2) search to optimally O(n) + // if both vectors have the same points and they are in the same order + if (it == oldBegin) + ++oldBegin; + numSamples++; + } + avgAngleDelta += angleD; + } + if (numSamples > 1) + avgAngleDelta /= numSamples; + + return avgAngleDelta; } void QQuickMultiPointerHandler::grabPoints(QVector points) diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index b63bd00ad2..66ee335986 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -79,6 +79,13 @@ signals: void pointDistanceThresholdChanged(); protected: + struct PointData { + PointData() : id(0), angle(0) {} + PointData(quint64 id, qreal angle) : id(id), angle(angle) {} + quint64 id; + qreal angle; + }; + bool wantsPointerEvent(QQuickPointerEvent *event) override; bool sameAsCurrentPoints(QQuickPointerEvent *event); QVector pointsInsideOrNearTarget(QQuickPointerEvent *event); @@ -88,6 +95,8 @@ protected: qreal averageStartingDistance(const QPointF &ref); qreal averageTouchPointAngle(const QPointF &ref); qreal averageStartingAngle(const QPointF &ref); + QVector angles(const QPointF &ref) const; + static qreal averageAngleDelta(const QVector &old, const QVector &newAngles); void grabPoints(QVector points); protected: diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index ddec7eee87..417a45318d 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -64,13 +64,20 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") QQuickPinchHandler::QQuickPinchHandler(QObject *parent) : QQuickMultiPointerHandler(parent, 2) - , m_startScale(1) - , m_startRotation(0) + , m_scale(1) + , m_rotation(0) + , m_translation(0,0) , m_minimumScale(-qInf()) , m_maximumScale(qInf()) , m_minimumRotation(-qInf()) , m_maximumRotation(qInf()) + , m_minimumX(-qInf()) + , m_maximumX(qInf()) + , m_minimumY(-qInf()) + , m_maximumY(qInf()) , m_pinchOrigin(PinchCenter) + , m_startScale(1) + , m_startRotation(0) { connect(this, &QQuickPinchHandler::activeChanged, this, &QQuickPinchHandler::onActiveChanged); connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); @@ -125,6 +132,58 @@ void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOri emit pinchOriginChanged(); } +/*! + \qmlproperty QQuickPinchHandler::minimumX + + The minimum acceptable x coordinate of the centroid + */ +void QQuickPinchHandler::setMinimumX(qreal minX) +{ + if (m_minimumX == minX) + return; + m_minimumX = minX; + emit minimumXChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::maximumX + + The maximum acceptable x coordinate of the centroid + */ +void QQuickPinchHandler::setMaximumX(qreal maxX) +{ + if (m_maximumX == maxX) + return; + m_maximumX = maxX; + emit maximumXChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::minimumY + + The minimum acceptable y coordinate of the centroid + */ +void QQuickPinchHandler::setMinimumY(qreal minY) +{ + if (m_minimumY == minY) + return; + m_minimumY = minY; + emit minimumYChanged(); +} + +/*! + \qmlproperty QQuickPinchHandler::maximumY + + The maximum acceptable y coordinate of the centroid + */ +void QQuickPinchHandler::setMaximumY(qreal maxY) +{ + if (m_maximumY == maxY) + return; + m_maximumY = maxY; + emit maximumYChanged(); +} + /*! \qmlproperty QQuickPinchHandler::minimumTouchPoints @@ -141,8 +200,11 @@ void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOri void QQuickPinchHandler::onActiveChanged() { if (active()) { - m_startScale = m_scaleTransform.xScale(); // TODO incompatible with independent x/y scaling - m_startRotation = m_rotationTransform.angle(); + m_startScale = m_scale; // TODO incompatible with independent x/y scaling + m_startRotation = m_rotation; + m_startAngles = angles(touchPointCentroid()); + m_activeRotation = 0; + m_startMatrix = m_transform.matrix(); qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; grabPoints(m_currentPoints); } @@ -154,8 +216,7 @@ void QQuickPinchHandler::onTargetChanged() // TODO if m_target was previously set differently, // does prepending to the new target remove it from the old one? // If not, should we fix that there, or here? - m_scaleTransform.prependToItem(target()); - m_rotationTransform.prependToItem(target()); + m_transform.prependToItem(target()); } } @@ -170,30 +231,47 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter QPointF startCentroid = startingCentroid(); - QPointF centroid = touchPointCentroid(); - QVector3D origin(centroid); + m_centroid = touchPointCentroid(); + m_centroid = QPointF(qBound(m_minimumX, m_centroid.x(), m_maximumX), + qBound(m_minimumY, m_centroid.y(), m_maximumY)); - qreal startDist = averageStartingDistance(startCentroid); - qreal dist = averageTouchPointDistance(centroid); - qreal zoom = dist / startDist; - - qreal newScale = qBound(m_minimumScale, zoom * m_startScale, m_maximumScale); - m_scaleTransform.setOrigin(origin); - // TODO optionally allow separate x and y scaling? - m_scaleTransform.setXScale(newScale); - m_scaleTransform.setYScale(newScale); - qreal startAngle = averageStartingAngle(startCentroid); - qreal angle = averageTouchPointAngle(centroid); - qreal angleDelta = startAngle - angle; - m_rotationTransform.setOrigin(origin); - m_rotationTransform.setAngle(qMin(m_maximumRotation, qMax(m_minimumRotation, m_startRotation + angleDelta))); + // 1. scale + qreal startDist = averageStartingDistance(startCentroid); + qreal dist = averageTouchPointDistance(m_centroid); + qreal activeScale = dist / startDist; + activeScale = qBound(m_minimumScale/m_startScale, activeScale, m_maximumScale/m_startScale); + m_scale = m_startScale * activeScale; + + // 2. rotate + QVector newAngles = angles(m_centroid); + const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); + m_activeRotation += angleDelta; + const qreal totalRotation = m_startRotation + m_activeRotation; + m_rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); + m_activeRotation += (m_rotation - totalRotation); //adjust for the potential bounding above + m_startAngles = std::move(newAngles); + + // 3. Drag/translate + QPointF activeTranslation(m_centroid - startCentroid); + + // apply rotation + scaling around the centroid - then apply translation. + QMatrix4x4 mat; + QVector3D xlatOrigin(m_centroid - target()->position()); + mat.translate(xlatOrigin); + mat.rotate(m_activeRotation, 0, 0, -1); + mat.scale(activeScale); + mat.translate(-xlatOrigin); + mat.translate(QVector3D(activeTranslation)); // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place - qCDebug(lcPinchHandler) << "startCentroid" << startCentroid << "centroid" << centroid << "dist" << dist << "starting dist" << startDist - << "zoom" << zoom << "startScale" << m_startScale << "startAngle" << startAngle << "angle" << angle - << "scale" << scale() << "rotation" << rotation(); + qCDebug(lcPinchHandler) << "startCentroid" << startCentroid << "centroid" << m_centroid << "dist" << dist << "starting dist" << startDist + << "startScale" << m_startScale << "activeRotation" << m_activeRotation + << "scale" << m_scale << "rotation" << m_rotation; + + mat = mat * m_startMatrix; + m_transform.setMatrix(mat); emit updated(); } diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 0a66d5e02e..338b930373 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -66,9 +66,14 @@ class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged) - Q_PROPERTY(QPointF center READ center NOTIFY updated) + Q_PROPERTY(QPointF centroid READ centroid NOTIFY updated) Q_PROPERTY(qreal scale READ scale NOTIFY updated) Q_PROPERTY(qreal rotation READ rotation NOTIFY updated) + Q_PROPERTY(QPointF translation READ translation NOTIFY updated) + Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged) public: enum PinchOrigin { @@ -94,9 +99,19 @@ public: PinchOrigin pinchOrigin() const { return m_pinchOrigin; } void setPinchOrigin(PinchOrigin pinchOrigin); - QPointF center() const { return m_scaleTransform.origin().toPointF(); } - qreal scale() const { return m_scaleTransform.xScale(); } - qreal rotation() const { return m_rotationTransform.angle(); } + QPointF translation() const { return m_translation; } + qreal scale() const { return m_scale; } + qreal rotation() const { return m_rotation; } + QPointF centroid() const { return m_centroid; } + + qreal minimumX() const { return m_minimumX; } + void setMinimumX(qreal minX); + qreal maximumX() const { return m_maximumX; } + void setMaximumX(qreal maxX); + qreal minimumY() const { return m_minimumY; } + void setMinimumY(qreal minY); + qreal maximumY() const { return m_maximumY; } + void setMaximumY(qreal maxY); signals: void requiredPointCountChanged(); @@ -104,6 +119,10 @@ signals: void maximumScaleChanged(); void minimumRotationChanged(); void maximumRotationChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); void pinchOriginChanged(); void updated(); @@ -113,15 +132,34 @@ protected: void handlePointerEventImpl(QQuickPointerEvent *event) override; private: - qreal m_startScale; - qreal m_startRotation; + // properties + qreal m_scale; + qreal m_rotation; + QPointF m_translation; + QPointF m_centroid; + qreal m_minimumScale; qreal m_maximumScale; + qreal m_minimumRotation; qreal m_maximumRotation; + + qreal m_minimumX; + qreal m_maximumX; + qreal m_minimumY; + qreal m_maximumY; + PinchOrigin m_pinchOrigin; - QQuickScale m_scaleTransform; - QQuickRotation m_rotationTransform; + + // internal + qreal m_startScale; + qreal m_startRotation; + qreal m_activeRotation; + + QVector m_startAngles; + QMatrix4x4 m_startMatrix; + QQuickMatrix4x4 m_transform; + }; QT_END_NAMESPACE -- cgit v1.2.3 From b9a80c04b52a38ae2f24998e6b58a67bb20d8941 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 16:39:22 +0200 Subject: QQuickEventPoint::invalidate(): set m_pointId to zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One reason is that if event->pointById(someKnownId) returns true, a handler may assume that the point is still valid. So either pointById needs to check the valid flag, or we must reset the pointId so that it won't be found. Change-Id: I20e93f058737a77ed801f0148ab7525ab880902d Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 6 ++++++ src/quick/items/qquickevents_p_p.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index f4b203ac53..4bb591fae3 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -537,6 +537,12 @@ void QQuickEventPoint::localize(QQuickItem *target) m_pos = QPointF(); } +void QQuickEventPoint::invalidate() +{ + m_valid = false; + m_pointId = 0; +} + QObject *QQuickEventPoint::grabber() const { return m_grabber.data(); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 1c524e9e3b..132246f59b 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -276,7 +276,7 @@ public: void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); void localize(QQuickItem *target); - void invalidate() { m_valid = false; } + void invalidate(); QQuickPointerEvent *pointerEvent() const; QPointF pos() const { return m_pos; } -- cgit v1.2.3 From b9dc856eca10775c5e37eec653b9ba28ff14472b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 16 Sep 2016 14:10:57 +0200 Subject: QQuickEventPoint: add event property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a signal emits a QQuickEventPoint to QML, it's sometimes useful to be able to access properties of the event: buttons, modifiers, device. Change-Id: I6230e31c5abb5e584858d3c0d45cf19199d84bf9 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents_p_p.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 132246f59b..31d6033f76 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -250,6 +250,7 @@ private: class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject { Q_OBJECT + Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent) Q_PROPERTY(QPointF pos READ pos) Q_PROPERTY(QPointF scenePos READ scenePos) Q_PROPERTY(QPointF scenePressPos READ scenePressPos) -- cgit v1.2.3 From adcfaae0fd8934ee5dc944fe2ab224afa6ab075d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 24 Aug 2016 10:41:16 +0200 Subject: QQuickEventPoint: rename itemGrabber -> grabberItem etc. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQuickWindow::mouseGrabberItem already existed, so we have a precedent for "specific thing which grabs" to come after the word Grabber. Change-Id: I65847ee0a005fac9b6292b9b91bb16f5f180e9bb Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 2 +- src/quick/handlers/qquickpointerhandler.cpp | 6 +++--- src/quick/items/qquickevents.cpp | 16 ++++++++-------- src/quick/items/qquickevents_p_p.h | 8 ++++---- src/quick/items/qquickwindow.cpp | 24 ++++++++++++------------ 5 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index dcc536147f..4e350c0b58 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -214,7 +214,7 @@ qreal QQuickMultiPointerHandler::averageAngleDelta(const QVector &old void QQuickMultiPointerHandler::grabPoints(QVector points) { for (QQuickEventPoint* point : points) - point->setPointerHandlerGrabber(this); + point->setGrabberPointerHandler(this); } QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 08a1cfc86f..4c60180f46 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -66,9 +66,9 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) { if (grab) - point->setPointerHandlerGrabber(this); - else if (point->pointerHandlerGrabber() == this) - point->setPointerHandlerGrabber(nullptr); + point->setGrabberPointerHandler(this); + else if (point->grabberPointerHandler() == this) + point->setGrabberPointerHandler(nullptr); } QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 4bb591fae3..fd1afb7160 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -551,17 +551,17 @@ QObject *QQuickEventPoint::grabber() const void QQuickEventPoint::setGrabber(QObject *grabber) { if (QQuickPointerHandler *phGrabber = qmlobject_cast(grabber)) - setPointerHandlerGrabber(phGrabber); + setGrabberPointerHandler(phGrabber); else - setItemGrabber(static_cast(grabber)); + setGrabberItem(static_cast(grabber)); } -QQuickItem *QQuickEventPoint::itemGrabber() const +QQuickItem *QQuickEventPoint::grabberItem() const { return (m_grabberIsHandler ? nullptr : static_cast(m_grabber.data())); } -void QQuickEventPoint::setItemGrabber(QQuickItem *grabber) +void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) { if (grabber != m_grabber.data()) { qCDebug(lcPointerHandlerDispatch) << this << grabber; @@ -571,12 +571,12 @@ void QQuickEventPoint::setItemGrabber(QQuickItem *grabber) } } -QQuickPointerHandler *QQuickEventPoint::pointerHandlerGrabber() const +QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const { return (m_grabberIsHandler ? static_cast(m_grabber.data()) : nullptr); } -void QQuickEventPoint::setPointerHandlerGrabber(QQuickPointerHandler *grabber) +void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber) { if (grabber != m_grabber.data()) { qCDebug(lcPointerHandlerDispatch) << this << grabber; @@ -692,7 +692,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) if (point->state() == QQuickEventPoint::Pressed) { if (grabbers.at(i)) qWarning() << "TouchPointPressed without previous release event" << point; - point->setItemGrabber(nullptr); + point->setGrabberItem(nullptr); } else { point->setGrabber(grabbers.at(i)); } @@ -751,7 +751,7 @@ QVector QQuickPointerMouseEvent::grabbers() const } void QQuickPointerMouseEvent::clearGrabbers() const { - m_mousePoint->setItemGrabber(nullptr); + m_mousePoint->setGrabberItem(nullptr); } bool QQuickPointerMouseEvent::isPressEvent() const diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 31d6033f76..821f19cae9 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -294,11 +294,11 @@ public: QObject *grabber() const; void setGrabber(QObject *grabber); - QQuickItem *itemGrabber() const; - void setItemGrabber(QQuickItem *grabber); + QQuickItem *grabberItem() const; + void setGrabberItem(QQuickItem *grabber); - QQuickPointerHandler *pointerHandlerGrabber() const; - void setPointerHandlerGrabber(QQuickPointerHandler *grabber); + QQuickPointerHandler *grabberPointerHandler() const; + void setGrabberPointerHandler(QQuickPointerHandler *grabber); private: QPointF m_pos; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 20d4b3c5f6..1c6685e12b 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -657,7 +657,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!q->mouseGrabberItem()) item->grabMouse(); auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setItemGrabber(item); + pointerEventPoint->setGrabberItem(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false)); @@ -741,14 +741,14 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setItemGrabber(grabber); + event->point(0)->setGrabberItem(grabber); if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) - point->setItemGrabber(grabber); + point->setGrabberItem(grabber); } if (oldGrabber) { @@ -800,7 +800,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->grabber() == grabber) { - pointerEvent->point(i)->setItemGrabber(nullptr); + pointerEvent->point(i)->setGrabberItem(nullptr); // FIXME send ungrab event only once grabber->touchUngrabEvent(); } @@ -1469,7 +1469,7 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount()); - return event->point(0)->itemGrabber(); + return event->point(0)->grabberItem(); } @@ -1613,12 +1613,12 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - if (point->pointerHandlerGrabber()) { - point->pointerHandlerGrabber()->handlePointerEvent(pointerEvent); + if (point->grabberPointerHandler()) { + point->grabberPointerHandler()->handlePointerEvent(pointerEvent); return; } - QQuickItem *grabber = point->itemGrabber(); + QQuickItem *grabber = point->grabberItem(); if (grabber) { // if the update consists of changing button state, then don't accept it // unless the button is one in which the item is interested @@ -2193,7 +2193,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; - point->setItemGrabber(nullptr); + point->setGrabberItem(nullptr); if (id == touchMouseId) { touchMouseId = -1; touchMouseDevice = nullptr; @@ -2325,7 +2325,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo auto pointerEventPoint = event->pointById(point.id()); pointerEventPoint->setAccepted(); if (point.state() == Qt::TouchPointPressed) - pointerEventPoint->setItemGrabber(item); + pointerEventPoint->setGrabberItem(item); } } else { // But if the event was not accepted then we know this item @@ -2334,7 +2334,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (point.state() == Qt::TouchPointPressed) { if (event->pointById(point.id())->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setItemGrabber(nullptr); + event->pointById(point.id())->setGrabberItem(nullptr); } } } @@ -2573,7 +2573,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; touchMouseId = tp.id(); touchMouseDevice = event->device(); - touchMouseDevice->pointerEvent()->pointById(tp.id())->setItemGrabber(target); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(target); target->grabMouse(); } filtered = true; -- cgit v1.2.3 From b026715ff336f1493579145269a7323ed15b22e3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 20:28:08 +0200 Subject: QQuickPointerSingleHandler: add pressedButtons property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A handler which handles only one point can only have one device; maybe this doesn't make sense to put in QQuickPointerDeviceHandler because there could be multiple devices with various buttons, in general. Otherwise it could be moved up the hierarchy. Change-Id: Ied241fe98babc2026ceac7e2f6388a3c606c046d Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 8 ++++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 10 ++++++++++ 2 files changed, 18 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 1fc84cd523..5675990a22 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -88,4 +88,12 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even m_currentPointId = 0; } +void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) +{ + if (buttons != m_pressedButtons) { + m_pressedButtons = buttons; + emit pressedButtonsChanged(); + } +} + QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index d130ed0146..b13926f669 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -58,19 +58,29 @@ QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler { Q_OBJECT + Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedButtonsChanged) public: QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } + Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } + +signals: + void pressedButtonsChanged(); + protected: bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; void handlePointerEventImpl(QQuickPointerEvent *event) Q_DECL_OVERRIDE; virtual void handleEventPoint(QQuickEventPoint *point) = 0; QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } +private: + void setPressedButtons(Qt::MouseButtons buttons); + private: quint64 m_currentPointId; + Qt::MouseButtons m_pressedButtons; }; QT_END_NAMESPACE -- cgit v1.2.3 From 37b3c6ecc5cb17021d7251cfca1d6e31d7edda4d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 16:37:01 +0200 Subject: QQuickPointerSingleHandler: choose the current point only if pressed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This type of handler doesn't steal from other grabbers. Change-Id: I237910fb5a7478ec20c09a759ec3ec813e63139b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 5675990a22..081d445559 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -68,7 +68,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) int c = event->pointCount(); for (int i = 0; i < c && !m_currentPointId; ++i) { QQuickEventPoint *p = event->point(i); - if (!p->grabber() && targetContains(p)) + if (p->state() == QQuickEventPoint::Pressed && !p->grabber() && targetContains(p)) m_currentPointId = p->pointId(); } } -- cgit v1.2.3 From 68a0023bd5330ff57961240aecdd26e30a62d338 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 26 Aug 2016 16:27:25 +0200 Subject: add QQuickPointerHandler::handleGrabCancel; call in setGrab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only one handler can be the grabber at one time, so we need to ensure that we notify the others when they lose the grab. Also handle this in QQuickPointerSingleHandler: forget m_currentPointId, so that wantsPointerEvent will not continue to "want" the same ID. Also add the canceled(QQuickEventPoint *point) signal, which is one of the four possible "state signals" (press, update, release, cancel) but which we didn't commit to adding yet. Change-Id: I46256acb75ece863d84e812af2d30cb0b12e3c1f Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 17 +++++++++++++++-- src/quick/handlers/qquickpointerhandler_p.h | 4 ++++ src/quick/handlers/qquickpointersinglehandler.cpp | 6 ++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 1 + src/quick/items/qquickevents.cpp | 19 ++++++++++++++++++- src/quick/items/qquickevents_p_p.h | 2 ++ 6 files changed, 46 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 4c60180f46..731de295e9 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -65,10 +65,14 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) { - if (grab) + if (grab) { + QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); + if (oldGrabber && oldGrabber != this) + oldGrabber->handleGrabCancel(point); point->setGrabberPointerHandler(this); - else if (point->grabberPointerHandler() == this) + } else if (point->grabberPointerHandler() == this) { point->setGrabberPointerHandler(nullptr); + } } QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const @@ -130,6 +134,15 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) handlePointerEventImpl(event); } +void QQuickPointerHandler::handleGrabCancel(QQuickEventPoint *point) +{ + qCDebug(lcPointerHandlerDispatch) << point; + Q_ASSERT(point); + setActive(false); + point->setAccepted(false); + emit canceled(point); +} + bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) { Q_UNUSED(event) diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index d7ac713638..42d9489f84 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -86,6 +86,7 @@ Q_SIGNALS: void enabledChanged(); void activeChanged(); void targetChanged(); + void canceled(QQuickEventPoint *point); protected: QQuickPointerEvent *currentEvent() { return m_currentEvent; } @@ -93,6 +94,7 @@ protected: virtual void setActive(bool active); virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setGrab(QQuickEventPoint *point, bool grab); + virtual void handleGrabCancel(QQuickEventPoint *point); QPointF eventPos(const QQuickEventPoint *point) const; bool targetContains(const QQuickEventPoint *point) const; @@ -101,6 +103,8 @@ private: QQuickItem *m_target; bool m_enabled : 1; bool m_active : 1; + + friend class QQuickEventPoint; }; QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 081d445559..6f48ed25bc 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -88,6 +88,12 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even m_currentPointId = 0; } +void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) +{ + QQuickPointerHandler::handleGrabCancel(point); + m_currentPointId = 0; +} + void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) { if (buttons != m_pressedButtons) { diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index b13926f669..adc2b92983 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -74,6 +74,7 @@ protected: void handlePointerEventImpl(QQuickPointerEvent *event) Q_DECL_OVERRIDE; virtual void handleEventPoint(QQuickEventPoint *point) = 0; QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } + void handleGrabCancel(QQuickEventPoint *point) override; private: void setPressedButtons(Qt::MouseButtons buttons); diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 12b2b80dce..672aadd0e3 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -516,7 +516,13 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity) { m_scenePos = scenePos; - m_pointId = pointId; + if (m_pointId != pointId) { + if (m_grabber) { + qWarning() << m_grabber << "failed to ungrab previous point" << m_pointId; + cancelGrab(); + } + m_pointId = pointId; + } m_valid = true; m_accept = false; m_state = static_cast(state); @@ -600,6 +606,17 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber) } } +void QQuickEventPoint::cancelGrab() +{ + if (m_grabber.isNull()) { + qWarning("cancelGrab: no grabber"); + return; + } + if (auto handler = grabberPointerHandler()) + handler->handleGrabCancel(this); + m_grabber.clear(); +} + void QQuickEventPoint::setAccepted(bool accepted) { if (m_accept != accepted) { diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 821f19cae9..7753a447a6 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -300,6 +300,8 @@ public: QQuickPointerHandler *grabberPointerHandler() const; void setGrabberPointerHandler(QQuickPointerHandler *grabber); + Q_INVOKABLE void cancelGrab(); + private: QPointF m_pos; QPointF m_scenePos; -- cgit v1.2.3 From 12a3eff085bc7eb30a5283e32009d2899b464278 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 22 Sep 2016 15:22:15 +0200 Subject: DragHandler: grab when dragging over the threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I529987797a3fdce967cdac21f29d3a067e0cbd4b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index e812b76399..0b77bb46c6 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -90,6 +90,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { m_dragging = true; + setGrab(point, true); emit draggingChanged(); } } break; -- cgit v1.2.3 From 542eda976c2767ed6c1eecb4dbcbf838e0ca5a19 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 18 Oct 2016 11:49:13 +0200 Subject: Fixed some problems with the centroid in PinchHandler The centroid was not always mapped to the correct coordinate system In addition, the return value of startingCentroid() was not always desirable, because its implementation calls sceneGrabPos(). We therefore had to get the starting centroid by querying touchPointCentroid() whenever the handler became active. Change-Id: I69de6b832b9bda208fda4eb90a8a95cc975405c2 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpinchhandler.cpp | 67 ++++++++++++++++++------------- src/quick/handlers/qquickpinchhandler_p.h | 2 + 2 files changed, 41 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 417a45318d..09e4f9f17c 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -202,11 +202,15 @@ void QQuickPinchHandler::onActiveChanged() if (active()) { m_startScale = m_scale; // TODO incompatible with independent x/y scaling m_startRotation = m_rotation; - m_startAngles = angles(touchPointCentroid()); + m_startCentroid = touchPointCentroid(); + m_startAngles = angles(m_startCentroid); + m_startDistance = averageTouchPointDistance(m_startCentroid); m_activeRotation = 0; m_startMatrix = m_transform.matrix(); qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; grabPoints(m_currentPoints); + } else { + qCInfo(lcPinchHandler) << "deactivated with scale" << m_scale << "rotation" << m_rotation; } } @@ -230,16 +234,19 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) } // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter - QPointF startCentroid = startingCentroid(); m_centroid = touchPointCentroid(); - m_centroid = QPointF(qBound(m_minimumX, m_centroid.x(), m_maximumX), - qBound(m_minimumY, m_centroid.y(), m_maximumY)); - - + QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); + // avoid mapping the minima and maxima, as they might have unmappable values + // such as -inf/+inf. Because of this we perform the bounding to min/max in local coords. + QPointF centroidLocalPos; + if (target() && target()->parentItem()) { + centroidLocalPos = target()->parentItem()->mapFromScene(m_centroid); + centroidLocalPos = QPointF(qBound(bounds.left(), centroidLocalPos.x(), bounds.right()), + qBound(bounds.top(), centroidLocalPos.y(), bounds.bottom())); + } // 1. scale - qreal startDist = averageStartingDistance(startCentroid); qreal dist = averageTouchPointDistance(m_centroid); - qreal activeScale = dist / startDist; + qreal activeScale = dist / m_startDistance; activeScale = qBound(m_minimumScale/m_startScale, activeScale, m_maximumScale/m_startScale); m_scale = m_startScale * activeScale; @@ -252,26 +259,30 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) m_activeRotation += (m_rotation - totalRotation); //adjust for the potential bounding above m_startAngles = std::move(newAngles); - // 3. Drag/translate - QPointF activeTranslation(m_centroid - startCentroid); - - // apply rotation + scaling around the centroid - then apply translation. - QMatrix4x4 mat; - QVector3D xlatOrigin(m_centroid - target()->position()); - mat.translate(xlatOrigin); - mat.rotate(m_activeRotation, 0, 0, -1); - mat.scale(activeScale); - mat.translate(-xlatOrigin); - mat.translate(QVector3D(activeTranslation)); - - // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place - - qCDebug(lcPinchHandler) << "startCentroid" << startCentroid << "centroid" << m_centroid << "dist" << dist << "starting dist" << startDist - << "startScale" << m_startScale << "activeRotation" << m_activeRotation - << "scale" << m_scale << "rotation" << m_rotation; - - mat = mat * m_startMatrix; - m_transform.setMatrix(mat); + if (target() && target()->parentItem()) { + // 3. Drag/translate + QPointF activeTranslation(centroidLocalPos - target()->parentItem()->mapFromScene(m_startCentroid)); + + // apply rotation + scaling around the centroid - then apply translation. + QMatrix4x4 mat; + QVector3D xlatOrigin(centroidLocalPos - target()->position()); + mat.translate(xlatOrigin); + mat.rotate(m_activeRotation, 0, 0, -1); + mat.scale(activeScale); + mat.translate(-xlatOrigin); + mat.translate(QVector3D(activeTranslation)); + + // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place + + qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid + << ", distance" << m_startDistance << "->" << dist + << ", startScale" << m_startScale << "->" << m_scale + << ", activeRotation" << m_activeRotation + << ", rotation" << m_rotation; + + mat = mat * m_startMatrix; + m_transform.setMatrix(mat); + } emit updated(); } diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 338b930373..5107a5c50b 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -155,6 +155,8 @@ private: qreal m_startScale; qreal m_startRotation; qreal m_activeRotation; + QPointF m_startCentroid; + qreal m_startDistance; QVector m_startAngles; QMatrix4x4 m_startMatrix; -- cgit v1.2.3 From 567811a52ecbd4de312750ba7d7855e560a1d7af Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 18 Nov 2016 10:11:58 +0100 Subject: Remove QQuickMultiPointerHandler::startingCentroid startingCentroid() used sceneGrabPos() in order to calculate the starting centroid. Unfortunately, the starting centroid should not always be calculated by the touch points initial grab position. Consider the following: 1. Two touch points (P1 and P2) are pressed at position s1 and s2. - Their sceneGrabPos() will return s1 and s1 respectively. - startingCentroid() returns the point between s1 and s2. 2. The two touch points are moved to position m1 and m2. - sceneGrabPos() will still return s1 and s1 - startingCentroid() returns the point between s1 and s2. 3. Touch point P1 is released, and then pressed again. - P1->sceneGrabPos() returns m1 - P2->sceneGrabPos() returns s2 - startingCentroid() returns the point between m1 and s2, which is not what we expect. It is expected that the startingCentroid then would return the point between m1 and m2. Because of this, startingCentroid cannot live up to its promise. Good news is that implementations would just need to query the starting centroid themselves by for instance querying touchPointCentroid() when the handler becomes active. Change-Id: I90fc23f2dd071998efab881cea9ba29b232cf118 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 11 ----------- src/quick/handlers/qquickmultipointerhandler_p.h | 1 - 2 files changed, 12 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 4e350c0b58..62d045b2da 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -136,17 +136,6 @@ QPointF QQuickMultiPointerHandler::touchPointCentroid() return ret / m_currentPoints.size(); } -QPointF QQuickMultiPointerHandler::startingCentroid() -{ - // TODO cache it in setActive()? - QPointF ret; - if (Q_UNLIKELY(m_currentPoints.size() == 0)) - return ret; - for (QQuickEventPoint *point : qAsConst(m_currentPoints)) - ret += point->sceneGrabPos(); - return ret / m_currentPoints.size(); -} - qreal QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref) { qreal ret = 0; diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index 66ee335986..e622500dc8 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -90,7 +90,6 @@ protected: bool sameAsCurrentPoints(QQuickPointerEvent *event); QVector pointsInsideOrNearTarget(QQuickPointerEvent *event); QPointF touchPointCentroid(); - QPointF startingCentroid(); qreal averageTouchPointDistance(const QPointF &ref); qreal averageStartingDistance(const QPointF &ref); qreal averageTouchPointAngle(const QPointF &ref); -- cgit v1.2.3 From 7aa312be3ce7ff8f7f4b8e484747565c2cae88a7 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 18 Oct 2016 16:07:40 +0200 Subject: PinchHandler: populate the translation property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's intended to be total translation of the target item relative to its declared position, independent of scale. Change-Id: I2b8f1bd62ba34c0200ea88b3461a45e1dcf61166 Reviewed-by: Shawn Rutledge Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpinchhandler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 09e4f9f17c..c6294092e6 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -282,6 +282,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) mat = mat * m_startMatrix; m_transform.setMatrix(mat); + m_translation = QPointF(mat.constData()[12], mat.constData()[13]); } emit updated(); -- cgit v1.2.3 From 29cf234ce28e4fcf26b60e906e1f4ae23eab5f54 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 23 Nov 2016 14:46:06 +0100 Subject: QQuickPointerHandler: add read-only parent property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parent is the Item in which the handler is instantiated. It cannot (so far) be reassigned to a different item, so this is not the same as QQuickItem's parent property: it's rather a convenience to get the QObject parent and cast it. Change-Id: Ib352213589532f45f104c8d83f04fd14107a4a20 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 12 ++++++++++++ src/quick/handlers/qquickpointerhandler_p.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 731de295e9..9f7b1c4cd0 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -162,4 +162,16 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) m_currentEvent = event; } +/*! + \qmlproperty QQuickPointerHandler::parent + + The \l Item which is the scope of the handler; the Item in which it was declared. + The handler will handle events on behalf of this Item, which means a + pointer event is relevant if at least one of its event points occurs within + the Item's interior. Initially \l target() is the same, but target() + can be reassigned. + + \sa QQuickPointerHandler::target(), QObject::parent() +*/ + QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 42d9489f84..5e691bea5e 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -66,6 +66,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool active READ active NOTIFY activeChanged) Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT) public: QQuickPointerHandler(QObject *parent = 0); @@ -80,6 +81,8 @@ public: QQuickItem *target() const { return m_target; } void setTarget(QQuickItem *target); + QQuickItem * parentItem() const { return static_cast(QObject::parent()); } + void handlePointerEvent(QQuickPointerEvent *event); Q_SIGNALS: -- cgit v1.2.3 From 8a98f14b55ae4e0d672d5c162beac7cfdd48e11d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 22 Nov 2016 17:34:53 +0100 Subject: PinchHandler: accept all points involved in the pinch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is one part of preventing sibling handlers (such as DragHandler) from stealing the grab: mark the points accepted so that allPointsAccepted() will be true, so that delivery stops. Change-Id: Icb650599254dccd949de90b4b7fc44969f6a7c62 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 6 ++++++ src/quick/handlers/qquickmultipointerhandler_p.h | 1 + src/quick/handlers/qquickpinchhandler.cpp | 1 + 3 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 62d045b2da..145070154c 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -200,6 +200,12 @@ qreal QQuickMultiPointerHandler::averageAngleDelta(const QVector &old return avgAngleDelta; } +void QQuickMultiPointerHandler::acceptPoints(const QVector &points) +{ + for (QQuickEventPoint* point : points) + point->setAccepted(); +} + void QQuickMultiPointerHandler::grabPoints(QVector points) { for (QQuickEventPoint* point : points) diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index e622500dc8..2a23a14f8a 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -96,6 +96,7 @@ protected: qreal averageStartingAngle(const QPointF &ref); QVector angles(const QPointF &ref) const; static qreal averageAngleDelta(const QVector &old, const QVector &newAngles); + void acceptPoints(const QVector &points); void grabPoints(QVector points); protected: diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index c6294092e6..4fb0547140 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -285,6 +285,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) m_translation = QPointF(mat.constData()[12], mat.constData()[13]); } + acceptPoints(m_currentPoints); emit updated(); } -- cgit v1.2.3 From c79d397925d535d72c0407af7c45f6b35df64cc2 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Dec 2016 16:43:01 +0100 Subject: pointerhandlers: replace Q_DECL_OVERRIDE with override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I63464ccafc56e2e398448ed90a9cb852304e90eb Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler_p.h | 2 +- src/quick/handlers/qquickpointerdevicehandler_p.h | 2 +- src/quick/handlers/qquickpointersinglehandler_p.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 0ce03c2c4c..82846db615 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -96,7 +96,7 @@ public: QQuickDragHandler(QObject *parent = 0); ~QQuickDragHandler(); - void handleEventPoint(QQuickEventPoint *point) Q_DECL_OVERRIDE; + void handleEventPoint(QQuickEventPoint *point) override; QQuickDragAxis *xAxis() { return &m_xAxis; } QQuickDragAxis *yAxis() { return &m_yAxis; } diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index 9e93d79e7c..bb12d3ccdb 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -81,7 +81,7 @@ Q_SIGNALS: void acceptedButtonsChanged(); protected: - bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; + bool wantsPointerEvent(QQuickPointerEvent *event) override; void setPressed(bool pressed); protected: diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index adc2b92983..e20a59124b 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -70,8 +70,8 @@ signals: void pressedButtonsChanged(); protected: - bool wantsPointerEvent(QQuickPointerEvent *event) Q_DECL_OVERRIDE; - void handlePointerEventImpl(QQuickPointerEvent *event) Q_DECL_OVERRIDE; + bool wantsPointerEvent(QQuickPointerEvent *event) override; + void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } void handleGrabCancel(QQuickEventPoint *point) override; -- cgit v1.2.3 From c363b0324ac7a19e8c30c075ce001da0a0a8b6bd Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Dec 2016 16:59:14 +0100 Subject: add virtual QQuickPointerHandler::onActiveChanged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and override it in PinchHandler. Many handlers will need to do something in response to change in active state, so calling a virtual when the state changes and overriding it in subclasses is more efficient than for subclasses to connect to the activeChanged signal. This also helps us control the signal emit order: the subclass can deal with any consequences (such as changing its own properties) before activeChanged is emitted. Change-Id: I6d05643e8facfd1941aa9152de6865932edb1b3a Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpinchhandler.cpp | 1 - src/quick/handlers/qquickpinchhandler_p.h | 2 +- src/quick/handlers/qquickpointerhandler.cpp | 1 + src/quick/handlers/qquickpointerhandler_p.h | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 4fb0547140..8ee8e2c6b6 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -79,7 +79,6 @@ QQuickPinchHandler::QQuickPinchHandler(QObject *parent) , m_startScale(1) , m_startRotation(0) { - connect(this, &QQuickPinchHandler::activeChanged, this, &QQuickPinchHandler::onActiveChanged); connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); } diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 5107a5c50b..83d6447b15 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -127,7 +127,7 @@ signals: void updated(); protected: - void onActiveChanged(); + void onActiveChanged() override; void onTargetChanged(); void handlePointerEventImpl(QQuickPointerEvent *event) override; diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 9f7b1c4cd0..da8fccbfa0 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -153,6 +153,7 @@ void QQuickPointerHandler::setActive(bool active) { if (m_active != active) { m_active = active; + onActiveChanged(); emit activeChanged(); } } diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 5e691bea5e..95900c50f0 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -94,8 +94,9 @@ Q_SIGNALS: protected: QQuickPointerEvent *currentEvent() { return m_currentEvent; } virtual bool wantsPointerEvent(QQuickPointerEvent *event); - virtual void setActive(bool active); virtual void handlePointerEventImpl(QQuickPointerEvent *event); + void setActive(bool active); + virtual void onActiveChanged() { } void setGrab(QQuickEventPoint *point, bool grab); virtual void handleGrabCancel(QQuickEventPoint *point); QPointF eventPos(const QQuickEventPoint *point) const; -- cgit v1.2.3 From a1c6480360560ba73d9d4797ed970103c53ba7a4 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Dec 2016 14:35:56 +0100 Subject: Fix projection matrix for DepthAware QSGRenderNodes Unlike renderUnmergedBatches(), renderRenderNode() did not adjust the projection matrix. Change-Id: Ie8ab0024a5f743ae30fe469f694805f803116613 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 81aa641e03..ae13163ea7 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2775,8 +2775,13 @@ void Renderer::renderRenderNode(Batch *batch) updateClip(rd->m_clip_list, batch); - RenderNodeState state; QMatrix4x4 pm = projectionMatrix(); + if (m_useDepthBuffer) { + pm(2, 2) = m_zRange; + pm(2, 3) = 1.0f - e->order * m_zRange; + } + + RenderNodeState state; state.m_projectionMatrix = ± state.m_scissorEnabled = m_currentClipType & ScissorClip; state.m_stencilEnabled = m_currentClipType & StencilClip; -- cgit v1.2.3 From 492ed4ae2615729b43d7d85624ab9754f6e0106e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Dec 2016 19:16:47 +0100 Subject: add virtual QQuickPointerHandler::onGrabChanged and signal grabChanged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some handlers will need to react to gaining or losing a grab, in both C++ and QML. Change-Id: I25b94800e3eaeabb0782315c679813ec735d4d51 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 4 ++++ src/quick/handlers/qquickpointerhandler_p.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index da8fccbfa0..77f0db41b7 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -70,8 +70,12 @@ void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) if (oldGrabber && oldGrabber != this) oldGrabber->handleGrabCancel(point); point->setGrabberPointerHandler(this); + onGrabChanged(point); + emit grabChanged(point); } else if (point->grabberPointerHandler() == this) { point->setGrabberPointerHandler(nullptr); + onGrabChanged(point); + emit grabChanged(point); } } diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 95900c50f0..9595897d72 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -89,6 +89,7 @@ Q_SIGNALS: void enabledChanged(); void activeChanged(); void targetChanged(); + void grabChanged(QQuickEventPoint *point); void canceled(QQuickEventPoint *point); protected: @@ -97,6 +98,7 @@ protected: virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setActive(bool active); virtual void onActiveChanged() { } + virtual void onGrabChanged(QQuickEventPoint *) { } void setGrab(QQuickEventPoint *point, bool grab); virtual void handleGrabCancel(QQuickEventPoint *point); QPointF eventPos(const QQuickEventPoint *point) const; -- cgit v1.2.3 From 345ca490d0e88f80e02780d96e4d50759da1f88b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 14 Oct 2016 23:51:15 +0200 Subject: SingleHandler: add virtual wantsEventPoint(QQuickEventPoint *) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subclasses of QQuickPointerSingleHandler should only ever need to pay attention to single points. This helps to complete the abstraction. Change-Id: Ied19b25601020e158935a104a9af8f4561ead0b1 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 36 +++++++++++++++++++---- src/quick/handlers/qquickpointersinglehandler_p.h | 1 + 2 files changed, 32 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 6f48ed25bc..d97348ea22 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -40,6 +40,7 @@ #include "qquickpointersinglehandler_p.h" QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET) /*! An intermediate class (not registered as a QML type) @@ -62,14 +63,28 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) if (m_currentPointId) { // We already know which one we want, so check whether it's there. // It's expected to be an update or a release. - return (event->pointById(m_currentPointId) != nullptr); + // If we no longer want it, cancel the grab. + if (auto point = event->pointById(m_currentPointId)) { + if (wantsEventPoint(point)) { + point->setAccepted(); + return true; + } else if (point->grabber() == this) { + point->cancelGrab(); + } + } else { + qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_currentPointId + << "is missing from current event, but was neither canceled nor released"; + return false; + } } else { - // We have not yet chosen a point; choose the first one within target bounds. + // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true. int c = event->pointCount(); for (int i = 0; i < c && !m_currentPointId; ++i) { QQuickEventPoint *p = event->point(i); - if (p->state() == QQuickEventPoint::Pressed && !p->grabber() && targetContains(p)) + if (!p->grabber() && wantsEventPoint(p)) { m_currentPointId = p->pointId(); + p->setAccepted(); + } } } return m_currentPointId; @@ -80,18 +95,29 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even QQuickPointerDeviceHandler::handlePointerEventImpl(event); QQuickEventPoint *currentPoint = event->pointById(m_currentPointId); Q_ASSERT(currentPoint); - currentPoint->setAccepted(true); - handleEventPoint(currentPoint); + if (!m_currentPointId || !currentPoint->isAccepted()) { + m_currentPointId = 0; + setPressedButtons(Qt::NoButton); + } else { + setPressedButtons(event->buttons()); + handleEventPoint(currentPoint); + } bool grab = currentPoint->isAccepted() && currentPoint->state() != QQuickEventPoint::Released; setGrab(currentPoint, grab); if (!grab) m_currentPointId = 0; } +bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) +{ + return targetContains(point); +} + void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) { QQuickPointerHandler::handleGrabCancel(point); m_currentPointId = 0; + setPressedButtons(Qt::NoButton); } void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index e20a59124b..a2feb7545c 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -71,6 +71,7 @@ signals: protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; + virtual bool wantsEventPoint(QQuickEventPoint *point); void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } -- cgit v1.2.3 From 9c9bb1b97ff80bf5469ca15d34bd4ccfeefedf9c Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Thu, 8 Dec 2016 13:28:42 +0100 Subject: Fixed a bug where a 2-finger pinchhandler became active with 3 fingers Fix is to only update m_currentPoints if the result of pointsInsideOrNearTarget() is equal to the required point count. Change-Id: I1504dd27a1aed176325da15257c78bbda69c8546 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 145070154c..746d33a12c 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -69,8 +69,12 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) return false; if (sameAsCurrentPoints(event)) return true; - m_currentPoints = pointsInsideOrNearTarget(event); - return (m_currentPoints.size() == m_requiredPointCount); + + const QVector candidatePoints = pointsInsideOrNearTarget(event); + const bool ret = (candidatePoints.size() == m_requiredPointCount); + if (ret) + m_currentPoints = candidatePoints; + return ret; } QVector QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event) -- cgit v1.2.3 From 2816d05c7b254e501562b96786936f59d9b45cea Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 22 Sep 2016 10:26:14 +0200 Subject: DragHandler: override wantsEventPoint to retain interest out of bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In any use case where the movement is constrained, as in a slider or scrollbar, it's possible to keep dragging even when the eventpoint is out of bounds. QQuickPointerSingleHandler::wantsEventPoint() returns false when the point is out of bounds, so we have to override it. Change-Id: Id80f614d6c38f28e6520ee8eacf7649f7317a5ef Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 7 +++++++ src/quick/handlers/qquickdraghandler_p.h | 3 +++ src/quick/handlers/qquickpointersinglehandler_p.h | 1 + 3 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 0b77bb46c6..dadf8bba04 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -67,6 +67,13 @@ QQuickDragHandler::~QQuickDragHandler() { } +bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) +{ + // If we've already been interested in a point, stay interested, even if it has strayed outside bounds. + return ((point->state() != QQuickEventPoint::Pressed && currentPointId() == point->pointId()) + || QQuickPointerSingleHandler::wantsEventPoint(point)); +} + void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) { // If there's no target or the target has no parent, we shouldn't be dragging diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 82846db615..e7cb675025 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -109,6 +109,9 @@ Q_SIGNALS: // void gestureStarted(QQuickGestureEvent *gesture); void draggingChanged(); +protected: + bool wantsEventPoint(QQuickEventPoint *point) override; + private: void ungrab(); void enforceAxisConstraints(QPointF *localPos); diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index a2feb7545c..69a3263271 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -74,6 +74,7 @@ protected: virtual bool wantsEventPoint(QQuickEventPoint *point); void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; + quint64 currentPointId() const { return m_currentPointId; } QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } void handleGrabCancel(QQuickEventPoint *point) override; -- cgit v1.2.3 From 6f94828e8f1865259ff1b1cd7fda5064ffd9576c Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 7 Dec 2016 14:19:03 +0100 Subject: Deactivate the handler when wantsPointerEvent returns false Change-Id: I34db4be279a68a7bf66adb604b12f8aff3c1bac4 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointerhandler.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 77f0db41b7..6225d2a24a 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -136,6 +136,8 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) setActive(wants); if (wants) handlePointerEventImpl(event); + else + setActive(false); } void QQuickPointerHandler::handleGrabCancel(QQuickEventPoint *point) -- cgit v1.2.3 From c9023c28764e70cd1c6f9cfc3506e6185299548e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sat, 3 Dec 2016 21:30:12 +0100 Subject: Add QQuickPathItem and its backend infra The generic backend uses the triangulator from QtGui, but is in fact OpenGL-only for now due to materials. The NVPR backend uses GL_NV_path_rendering on NVIDIA hardware with OpenGL 4.3+ or OpenGL ES 3.1+. The software backend simply uses QPainter. With the generic backend each PathItem is backed by a non-visual root node and 0, 1 or 2 child geometry nodes, depending on the presence of visible stroking and filling. The potentially expensive triangulation happens on updatePolish(), on the gui thread. This is proven to provide much smoother results when compared to doing the geometry generation on the render thread in updatePaintNode(), in particular on power-limited embedded devices. The NVPR backend uses a QSGRenderNode in DepthAware mode so that the batch renderer can continue to rely on the depth buffer and use opaque batches. Due to not relying on slow CPU-side triangulation, this backend uses 5-10 times less CPU, even when properties of the path or its elements are animated. The path itself is specified with the PathView's Path, PathLine, PathArc, PathQuad, etc. types. This allows for consistency with PathView and the 2D Canvas and avoids a naming mess in the API. However, there won't be a 100% symmetry: backends like NVPR will not rely on QPainterPath but process the path elements on their own (as QPainterPath is essentially useless with these APIs), which can lead to differences in the supported path elements. The supported common set is currently Move, Line, Quad, Cubic, Arc. The patch introduces PathMove, which is essentially PathLine but maps to moveTo instead of lineTo. More types may get added later (e.g. NVPR can do a wide variety of optimized rounded rects, but this requires directly specifying a GL_ROUNDED_RECTx_NV command, thus neededing a dedicated Path type on our side too) For filling with gradients only linear gradients are supported at the moment. In addition to the declarative API, a more lightweight, QObject-less JS-callable API should be considered as well for the future. Change-Id: I335ad64b425ee279505d60e3e57ac6841e1cbd24 Reviewed-by: Andy Nichols --- src/quick/items/items.pri | 15 +- src/quick/items/items.qrc | 4 + src/quick/items/qquickitemsmodule.cpp | 9 + src/quick/items/qquickpathitem.cpp | 792 +++++++++++++++++++++ src/quick/items/qquickpathitem_p.h | 281 ++++++++ src/quick/items/qquickpathitem_p_p.h | 177 +++++ src/quick/items/qquickpathitemgenericrenderer.cpp | 510 +++++++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 232 ++++++ src/quick/items/qquickpathitemnvprrenderer.cpp | 567 +++++++++++++++ src/quick/items/qquickpathitemnvprrenderer_p.h | 194 +++++ src/quick/items/qquickpathitemsoftwarerenderer.cpp | 234 ++++++ src/quick/items/qquickpathitemsoftwarerenderer_p.h | 127 ++++ src/quick/items/shaders/lineargradient.frag | 9 + src/quick/items/shaders/lineargradient.vert | 15 + src/quick/items/shaders/lineargradient_core.frag | 12 + src/quick/items/shaders/lineargradient_core.vert | 17 + src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 4 + src/quick/scenegraph/coreapi/qsgrendernode.cpp | 3 - src/quick/scenegraph/qsgdefaultrendercontext.cpp | 2 +- src/quick/scenegraph/util/qsgtexture.cpp | 24 +- src/quick/scenegraph/util/qsgtexture.h | 3 +- src/quick/scenegraph/util/qsgtexture_p.h | 4 +- src/quick/scenegraph/util/qsgtexturematerial.cpp | 4 +- src/quick/util/qquicknvprfunctions.cpp | 284 ++++++++ src/quick/util/qquicknvprfunctions_p.h | 406 +++++++++++ src/quick/util/qquicknvprfunctions_p_p.h | 70 ++ src/quick/util/qquickpath.cpp | 70 +- src/quick/util/qquickpath_p.h | 10 + src/quick/util/util.pri | 7 + 29 files changed, 4068 insertions(+), 18 deletions(-) create mode 100644 src/quick/items/qquickpathitem.cpp create mode 100644 src/quick/items/qquickpathitem_p.h create mode 100644 src/quick/items/qquickpathitem_p_p.h create mode 100644 src/quick/items/qquickpathitemgenericrenderer.cpp create mode 100644 src/quick/items/qquickpathitemgenericrenderer_p.h create mode 100644 src/quick/items/qquickpathitemnvprrenderer.cpp create mode 100644 src/quick/items/qquickpathitemnvprrenderer_p.h create mode 100644 src/quick/items/qquickpathitemsoftwarerenderer.cpp create mode 100644 src/quick/items/qquickpathitemsoftwarerenderer_p.h create mode 100644 src/quick/items/shaders/lineargradient.frag create mode 100644 src/quick/items/shaders/lineargradient.vert create mode 100644 src/quick/items/shaders/lineargradient_core.frag create mode 100644 src/quick/items/shaders/lineargradient_core.vert create mode 100644 src/quick/util/qquicknvprfunctions.cpp create mode 100644 src/quick/util/qquicknvprfunctions_p.h create mode 100644 src/quick/util/qquicknvprfunctions_p_p.h (limited to 'src') diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 0f8061b5ef..511c6f18d8 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -148,9 +148,20 @@ qtConfig(quick-listview) { qtConfig(quick-pathview) { HEADERS += \ $$PWD/qquickpathview_p.h \ - $$PWD/qquickpathview_p_p.h + $$PWD/qquickpathview_p_p.h \ + $$PWD/qquickpathitem_p.h \ + $$PWD/qquickpathitem_p_p.h \ + $$PWD/qquickpathitemgenericrenderer_p.h \ + $$PWD/qquickpathitemsoftwarerenderer_p.h SOURCES += \ - $$PWD/qquickpathview.cpp + $$PWD/qquickpathview.cpp \ + $$PWD/qquickpathitem.cpp \ + $$PWD/qquickpathitemgenericrenderer.cpp \ + $$PWD/qquickpathitemsoftwarerenderer.cpp + qtConfig(opengl) { + HEADERS += $$PWD/qquickpathitemnvprrenderer_p.h + SOURCES += $$PWD/qquickpathitemnvprrenderer.cpp + } } qtConfig(quick-positioners) { diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc index 6aaf757c29..da9bf0c828 100644 --- a/src/quick/items/items.qrc +++ b/src/quick/items/items.qrc @@ -8,5 +8,9 @@ shaders/shadereffect_core.vert shaders/shadereffectfallback_core.frag shaders/shadereffectfallback_core.vert + shaders/lineargradient.vert + shaders/lineargradient.frag + shaders/lineargradient_core.vert + shaders/lineargradient_core.frag diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index dbe30fbc83..b0d7f7f8a3 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -70,6 +70,7 @@ #if QT_CONFIG(quick_path) #include #include +#include "qquickpathitem_p.h" #endif #if QT_CONFIG(quick_positioners) #include "qquickpositioners_p.h" @@ -370,6 +371,14 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #endif qmlRegisterType(uri, 2, 9, "MouseArea"); + +#if QT_CONFIG(quick_path) + qmlRegisterType(uri, 2, 9, "PathMove"); + qmlRegisterType(uri, 2, 9, "PathItem"); + qmlRegisterType(uri, 2, 9, "PathGradientStop"); + qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); + qmlRegisterType(uri, 2, 9, "PathLinearGradient"); +#endif } static void initResources() diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp new file mode 100644 index 0000000000..0d2c7a8bbe --- /dev/null +++ b/src/quick/items/qquickpathitem.cpp @@ -0,0 +1,792 @@ +/**************************************************************************** +** +** 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 "qquickpathitem_p.h" +#include "qquickpathitem_p_p.h" +#include "qquickpathitemgenericrenderer_p.h" +#include "qquickpathitemnvprrenderer_p.h" +#include "qquickpathitemsoftwarerenderer_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickPathItemPrivate::QQuickPathItemPrivate() + : rendererType(QQuickPathItem::UnknownRenderer), + renderer(nullptr), + path(nullptr), + dirty(DirtyAll), + strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickPathItem::OddEvenFill), + joinStyle(QQuickPathItem::BevelJoin), + miterLimit(2), + capStyle(QQuickPathItem::SquareCap), + strokeStyle(QQuickPathItem::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QQuickPathItemPrivate::~QQuickPathItemPrivate() +{ + delete renderer; +} + +/*! + \qmltype PathItem + \instantiates QQuickPathItem + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using an extension like \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. Therefore + it is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. + + Nonetheless it is important to be aware of performance implications, in + particular when the application is running on the generic PathItem + implementation due to not having support for accelerated path rendering. + The geometry generation happens entirely on the CPU in this case, and this + is potentially expensive. Changing the set of path elements, changing the + properties of these elements, or changing certain properties of the + PathItem itself all lead to retriangulation on every change. Therefore, + applying animation to such properties can heavily affect performance on + less powerful systems. If animating properties other than stroke and fill + colors is a must, it is recommended to target systems providing + \c{GL_NV_path_rendering} where the cost of path property changes is much + smaller. + + \note The types for specifying path elements are shared between PathView + and PathItem. However, not all PathItem implementations support all path + element types, while some may not make sense for PathView. PathItem's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc. + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc +*/ + +QQuickPathItem::QQuickPathItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPathItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickPathItem::~QQuickPathItem() +{ +} + +QQuickPathItem::RendererType QQuickPathItem::rendererType() const +{ + Q_D(const QQuickPathItem); + return d->rendererType; +} + +/*! + \qmlproperty Path QtQuick::PathItem::path + This property holds the path to be rendered. + For more information see the \l Path documentation. +*/ +QQuickPath *QQuickPathItem::path() const +{ + Q_D(const QQuickPathItem); + return d->path; +} + +void QQuickPathItem::setPath(QQuickPath *path) +{ + Q_D(QQuickPathItem); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickPathItem, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickPathItem, SLOT(_q_pathChanged())); + + d->dirty |= QQuickPathItemPrivate::DirtyPath; + emit pathChanged(); + polish(); +} + +void QQuickPathItemPrivate::_q_pathChanged() +{ + Q_Q(QQuickPathItem); + dirty |= DirtyPath; + q->polish(); +} + +QColor QQuickPathItem::strokeColor() const +{ + Q_D(const QQuickPathItem); + return d->strokeColor; +} + +void QQuickPathItem::setStrokeColor(const QColor &color) +{ + Q_D(QQuickPathItem); + if (d->strokeColor != color) { + d->strokeColor = color; + d->dirty |= QQuickPathItemPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + polish(); + } +} + +qreal QQuickPathItem::strokeWidth() const +{ + Q_D(const QQuickPathItem); + return d->strokeWidth; +} + +void QQuickPathItem::setStrokeWidth(qreal w) +{ + Q_D(QQuickPathItem); + if (d->strokeWidth != w) { + d->strokeWidth = w; + d->dirty |= QQuickPathItemPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + polish(); + } +} + +QColor QQuickPathItem::fillColor() const +{ + Q_D(const QQuickPathItem); + return d->fillColor; +} + +void QQuickPathItem::setFillColor(const QColor &color) +{ + Q_D(QQuickPathItem); + if (d->fillColor != color) { + d->fillColor = color; + d->dirty |= QQuickPathItemPrivate::DirtyFillColor; + emit fillColorChanged(); + polish(); + } +} + +QQuickPathItem::FillRule QQuickPathItem::fillRule() const +{ + Q_D(const QQuickPathItem); + return d->fillRule; +} + +void QQuickPathItem::setFillRule(FillRule fillRule) +{ + Q_D(QQuickPathItem); + if (d->fillRule != fillRule) { + d->fillRule = fillRule; + d->dirty |= QQuickPathItemPrivate::DirtyFillRule; + emit fillRuleChanged(); + polish(); + } +} + +QQuickPathItem::JoinStyle QQuickPathItem::joinStyle() const +{ + Q_D(const QQuickPathItem); + return d->joinStyle; +} + +void QQuickPathItem::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickPathItem); + if (d->joinStyle != style) { + d->joinStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit joinStyleChanged(); + polish(); + } +} + +int QQuickPathItem::miterLimit() const +{ + Q_D(const QQuickPathItem); + return d->miterLimit; +} + +void QQuickPathItem::setMiterLimit(int limit) +{ + Q_D(QQuickPathItem); + if (d->miterLimit != limit) { + d->miterLimit = limit; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit miterLimitChanged(); + polish(); + } +} + +QQuickPathItem::CapStyle QQuickPathItem::capStyle() const +{ + Q_D(const QQuickPathItem); + return d->capStyle; +} + +void QQuickPathItem::setCapStyle(CapStyle style) +{ + Q_D(QQuickPathItem); + if (d->capStyle != style) { + d->capStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyStyle; + emit capStyleChanged(); + polish(); + } +} + +QQuickPathItem::StrokeStyle QQuickPathItem::strokeStyle() const +{ + Q_D(const QQuickPathItem); + return d->strokeStyle; +} + +void QQuickPathItem::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickPathItem); + if (d->strokeStyle != style) { + d->strokeStyle = style; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit strokeStyleChanged(); + polish(); + } +} + +qreal QQuickPathItem::dashOffset() const +{ + Q_D(const QQuickPathItem); + return d->dashOffset; +} + +void QQuickPathItem::setDashOffset(qreal offset) +{ + Q_D(QQuickPathItem); + if (d->dashOffset != offset) { + d->dashOffset = offset; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit dashOffsetChanged(); + polish(); + } +} + +QVector QQuickPathItem::dashPattern() const +{ + Q_D(const QQuickPathItem); + return d->dashPattern; +} + +void QQuickPathItem::setDashPattern(const QVector &array) +{ + Q_D(QQuickPathItem); + if (d->dashPattern != array) { + d->dashPattern = array; + d->dirty |= QQuickPathItemPrivate::DirtyDash; + emit dashPatternChanged(); + polish(); + } +} + +QQuickPathGradient *QQuickPathItem::fillGradient() const +{ + Q_D(const QQuickPathItem); + return d->fillGradient; +} + +void QQuickPathItem::setFillGradient(QQuickPathGradient *gradient) +{ + Q_D(QQuickPathItem); + if (d->fillGradient != gradient) { + if (d->fillGradient) + qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickPathItem, SLOT(_q_fillGradientChanged())); + d->fillGradient = gradient; + if (d->fillGradient) + qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickPathItem, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickPathItemPrivate::DirtyFillGradient; + polish(); + } +} + +void QQuickPathItemPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickPathItem); + dirty |= DirtyFillGradient; + q->polish(); +} + +void QQuickPathItem::resetFillGradient() +{ + setFillGradient(nullptr); +} + +void QQuickPathItem::updatePolish() +{ + Q_D(QQuickPathItem); + + if (!d->dirty) + return; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen, depending on the + // backend. Therefore do this only when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) +{ + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + polish(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickPathItem); + if (d->renderer) { + if (!node) + node = d->createRenderNode(); + d->renderer->updatePathRenderNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickPathItemPrivate::createRenderer() +{ + Q_Q(QQuickPathItem); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (QQuickPathItemNvprRenderNode::isSupported()) { + rendererType = QQuickPathItem::NvprRenderer; + renderer = new QQuickPathItemNvprRenderer; + } else { + rendererType = QQuickPathItem::GeometryRenderer; + renderer = new QQuickPathItemGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickPathItem::SoftwareRenderer; + renderer = new QQuickPathItemSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickPathItemPrivate::createRenderNode() +{ + Q_Q(QQuickPathItem); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + const bool hasFill = fillColor != Qt::transparent; + const bool hasStroke = !qFuzzyIsNull(strokeWidth) && strokeColor != Qt::transparent; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (QQuickPathItemNvprRenderNode::isSupported()) { + node = new QQuickPathItemNvprRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke); + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickPathItemSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +void QQuickPathItemPrivate::sync() +{ + renderer->beginSync(); + + if (dirty & QQuickPathItemPrivate::DirtyPath) + renderer->setPath(path); + if (dirty & DirtyStrokeColor) + renderer->setStrokeColor(strokeColor); + if (dirty & DirtyStrokeWidth) + renderer->setStrokeWidth(strokeWidth); + if (dirty & DirtyFillColor) + renderer->setFillColor(fillColor); + if (dirty & DirtyFillRule) + renderer->setFillRule(fillRule); + if (dirty & DirtyStyle) { + renderer->setJoinStyle(joinStyle, miterLimit); + renderer->setCapStyle(capStyle); + } + if (dirty & DirtyDash) + renderer->setStrokeStyle(strokeStyle, dashOffset, dashPattern); + if (dirty & DirtyFillGradient) + renderer->setFillGradient(fillGradient); + + renderer->endSync(); + dirty = 0; +} + +// ***** gradient support ***** + +QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +qreal QQuickPathGradientStop::position() const +{ + return m_position; +} + +void QQuickPathGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +QColor QQuickPathGradientStop::color() const +{ + return m_color; +} + +void QQuickPathGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +QQuickPathGradient::QQuickPathGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickPathGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); + return; + } + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +QQmlListProperty QQuickPathGradient::stops() +{ + return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); +} + +QGradientStops QQuickPathGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickPathGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const +{ + return m_spread; +} + +void QQuickPathGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) + : QQuickPathGradient(parent) +{ +} + +qreal QQuickPathLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickPathLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickPathLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickPathLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickPathLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickPathItemGradientCacheWrapper +{ +public: + QQuickPathItemGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() +{ + static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickPathItemGradientCache::~QQuickPathItemGradientCache() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickPathGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickPathGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickPathGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h new file mode 100644 index 0000000000..39b407cf87 --- /dev/null +++ b/src/quick/items/qquickpathitem_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEM_P_H +#define QQUICKPATHITEM_P_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 "qquickitem.h" + +#include +#include +#include + +QT_REQUIRE_CONFIG(quick_path); + +QT_BEGIN_NAMESPACE + +class QQuickPathItemPrivate; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickPathGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickPathGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathLinearGradient : public QQuickPathGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickPathLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + + Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + QQuickPathItem(QQuickItem *parent = nullptr); + ~QQuickPathItem(); + + RendererType rendererType() const; + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickPathGradient *fillGradient() const; + void setFillGradient(QQuickPathGradient *gradient); + void resetFillGradient(); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + +Q_SIGNALS: + void rendererChanged(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickPathItem) + Q_DECLARE_PRIVATE(QQuickPathItem) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPathItem) + +#endif // QQUICKPATHITEM_P_H diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h new file mode 100644 index 0000000000..366628d867 --- /dev/null +++ b/src/quick/items/qquickpathitem_p_p.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEM_P_P_H +#define QQUICKPATHITEM_P_P_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 "qquickpathitem_p.h" +#include "qquickitem_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +class QQuickAbstractPathRenderer +{ +public: + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync() = 0; + virtual void setPath(const QQuickPath *path) = 0; + virtual void setStrokeColor(const QColor &color) = 0; + virtual void setStrokeWidth(qreal w) = 0; + virtual void setFillColor(const QColor &color) = 0; + virtual void setFillRule(QQuickPathItem::FillRule fillRule) = 0; + virtual void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(QQuickPathItem::CapStyle capStyle) = 0; + virtual void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) = 0; + virtual void setFillGradient(QQuickPathGradient *gradient) = 0; + virtual void endSync() = 0; + + // Render thread, with gui blocked + virtual void updatePathRenderNode() = 0; +}; + +class QQuickPathItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathItem) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickPathItemPrivate(); + ~QQuickPathItemPrivate(); + + void createRenderer(); + QSGNode *createRenderNode(); + void sync(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + QQuickPathItem::RendererType rendererType; + QQuickAbstractPathRenderer *renderer; + QQuickPath *path; + int dirty; + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickPathItem::FillRule fillRule; + QQuickPathItem::JoinStyle joinStyle; + int miterLimit; + QQuickPathItem::CapStyle capStyle; + QQuickPathItem::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickPathGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickPathItemGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickPathItemGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp new file mode 100644 index 0000000000..9bd03c0e51 --- /dev/null +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -0,0 +1,510 @@ +/**************************************************************************** +** +** 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 "qquickpathitemgenericrenderer_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +static const qreal SCALE = 100; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickPathItemGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickPathItemGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickPathItemGenericRootRenderNode::QQuickPathItemGenericRootRenderNode(QQuickWindow *window, + bool hasFill, + bool hasStroke) + : m_fillNode(nullptr), + m_strokeNode(nullptr) +{ + if (hasFill) { + m_fillNode = new QQuickPathItemGenericRenderNode(window, this); + appendChildNode(m_fillNode); + } + if (hasStroke) { + m_strokeNode = new QQuickPathItemGenericRenderNode(window, this); + appendChildNode(m_strokeNode); + } +} + +QQuickPathItemGenericRootRenderNode::~QQuickPathItemGenericRootRenderNode() +{ +} + +QQuickPathItemGenericRenderNode::QQuickPathItemGenericRenderNode(QQuickWindow *window, + QQuickPathItemGenericRootRenderNode *rootNode) + : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0), + m_window(window), + m_rootNode(rootNode), + m_material(nullptr) +{ + setGeometry(&m_geometry); + activateMaterial(MatSolidColor); +} + +QQuickPathItemGenericRenderNode::~QQuickPathItemGenericRenderNode() +{ +} + +void QQuickPathItemGenericRenderNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +// sync, and so triangulation too, happens on the gui thread +void QQuickPathItemGenericRenderer::beginSync() +{ + m_syncDirty = 0; +} + +void QQuickPathItemGenericRenderer::setPath(const QQuickPath *path) +{ + m_path = path ? path->path() : QPainterPath(); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color) +{ + m_strokeColor = colorToColor4ub(color); + m_syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w) +{ + m_pen.setWidthF(w); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setFillColor(const QColor &color) +{ + m_fillColor = colorToColor4ub(color); + m_syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = Qt::FillRule(fillRule); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + m_pen.setMiterLimit(miterLimit); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + m_pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickPathItem::DashLine) { + m_pen.setDashPattern(dashPattern); + m_pen.setDashOffset(dashOffset); + } + m_syncDirty |= DirtyGeom; +} + +void QQuickPathItemGenericRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + m_fillGradientActive = gradient != nullptr; + if (gradient) { + m_fillGradient.stops = gradient->sortedGradientStops(); + m_fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + m_fillGradient.start = QPointF(g->x1(), g->y1()); + m_fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + m_syncDirty |= DirtyFillGradient; +} + +void QQuickPathItemGenericRenderer::endSync() +{ + if (!m_syncDirty) + return; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to + // updatePathRenderNode() on the render thread (with the gui thread + // blocked). For our purposes here m_syncDirty is still required since + // geometry regeneration must only happen when there was an actual change + // in this particular sync round. + m_effectiveDirty |= m_syncDirty; + + if (m_path.isEmpty()) { + m_fillVertices.clear(); + m_fillIndices.clear(); + m_strokeVertices.clear(); + return; + } + + triangulateFill(); + triangulateStroke(); +} + +void QQuickPathItemGenericRenderer::triangulateFill() +{ + m_path.setFillRule(m_fillRule); + + const QVectorPath &vp = qtVectorPathForPath(m_path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + m_fillVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(m_fillVertices.data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, m_fillColor); + + m_fillIndices.resize(ts.indices.size()); + quint16 *idst = m_fillIndices.data(); + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + memcpy(idst, ts.indices.data(), m_fillIndices.count() * sizeof(quint16)); + } else { + const quint32 *isrc = (const quint32 *) ts.indices.data(); + for (int i = 0; i < m_fillIndices.count(); ++i) + idst[i] = isrc[i]; + } +} + +void QQuickPathItemGenericRenderer::triangulateStroke() +{ + const QVectorPath &vp = qtVectorPathForPath(m_path); + + const QRectF clip(0, 0, m_item->width(), m_item->height()); + const qreal inverseScale = 1.0 / SCALE; + m_stroker.setInvScale(inverseScale); + if (m_pen.style() == Qt::SolidLine) { + m_stroker.process(vp, m_pen, clip, 0); + } else { + m_dashStroker.setInvScale(inverseScale); + m_dashStroker.process(vp, m_pen, clip, 0); + QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(), + m_dashStroker.elementTypes(), 0); + m_stroker.process(dashStroke, m_pen, clip, 0); + } + + if (!m_stroker.vertexCount()) { + m_strokeVertices.clear(); + return; + } + + const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + m_strokeVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(m_strokeVertices.data()); + const float *vsrc = m_stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], m_strokeColor); +} + +void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericRootRenderNode *rn) +{ + if (m_rootNode != rn) { + m_rootNode = rn; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_effectiveDirty = DirtyAll; + } +} + +// on the render thread with gui blocked +void QQuickPathItemGenericRenderer::updatePathRenderNode() +{ + if (!m_effectiveDirty || !m_rootNode) + return; + + if (m_fillColor.a == 0) { + delete m_rootNode->m_fillNode; + m_rootNode->m_fillNode = nullptr; + } else if (!m_rootNode->m_fillNode) { + m_rootNode->m_fillNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); + if (m_rootNode->m_strokeNode) + m_rootNode->removeChildNode(m_rootNode->m_strokeNode); + m_rootNode->appendChildNode(m_rootNode->m_fillNode); + if (m_rootNode->m_strokeNode) + m_rootNode->appendChildNode(m_rootNode->m_strokeNode); + m_effectiveDirty |= DirtyGeom; + } + + if (qFuzzyIsNull(m_pen.widthF()) || m_strokeColor.a == 0) { + delete m_rootNode->m_strokeNode; + m_rootNode->m_strokeNode = nullptr; + } else if (!m_rootNode->m_strokeNode) { + m_rootNode->m_strokeNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); + m_rootNode->appendChildNode(m_rootNode->m_strokeNode); + m_effectiveDirty |= DirtyGeom; + } + + updateFillNode(); + updateStrokeNode(); + + m_effectiveDirty = 0; +} + +void QQuickPathItemGenericRenderer::updateFillNode() +{ + if (!m_rootNode->m_fillNode) + return; + + QQuickPathItemGenericRenderNode *n = m_rootNode->m_fillNode; + QSGGeometry *g = &n->m_geometry; + if (m_fillVertices.isEmpty()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + + if (m_fillGradientActive) { + n->activateMaterial(QQuickPathItemGenericRenderNode::MatLinearGradient); + if (m_effectiveDirty & DirtyFillGradient) { + // Make a copy of the data that will be accessed by the material on + // the render thread. + n->m_fillGradient = m_fillGradient; + // Gradients are implemented via a texture-based material. + n->markDirty(QSGNode::DirtyMaterial); + // stop here if only the gradient changed; no need to touch the geometry + if (!(m_effectiveDirty & DirtyGeom)) + return; + } + } else { + n->activateMaterial(QQuickPathItemGenericRenderNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, m_fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + g->allocate(m_fillVertices.count(), m_fillIndices.count()); + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), m_fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), m_fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + + n->markDirty(QSGNode::DirtyGeometry); +} + +void QQuickPathItemGenericRenderer::updateStrokeNode() +{ + if (!m_rootNode->m_strokeNode) + return; + if (m_effectiveDirty == DirtyFillGradient) // not applicable + return; + + QQuickPathItemGenericRenderNode *n = m_rootNode->m_strokeNode; + n->markDirty(QSGNode::DirtyGeometry); + + QSGGeometry *g = &n->m_geometry; + if (m_strokeVertices.isEmpty()) { + g->allocate(0, 0); + return; + } + + if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, m_strokeColor); + return; + } + + g->allocate(m_strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), m_strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickPathItemGenericRenderNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickPathItemLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickPathItemLinearGradientShader::type; + +QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickPathItemLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickPathItemLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickPathItemGenericRenderNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickPathItemLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickPathItemLinearGradientMaterial *m = static_cast(other); + + QQuickPathItemGenericRenderNode *a = node(); + QQuickPathItemGenericRenderNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h new file mode 100644 index 0000000000..c186959c88 --- /dev/null +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMGENERICRENDERER_P_H +#define QQUICKPATHITEMGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemGenericRootRenderNode; + +class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyGeom = 0x01, + DirtyColor = 0x02, + DirtyFillGradient = 0x04, + + DirtyAll = 0xFF + }; + + QQuickPathItemGenericRenderer(QQuickItem *item) + : m_item(item), + m_rootNode(nullptr), + m_effectiveDirty(0) + { } + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setRootNode(QQuickPathItemGenericRootRenderNode *rn); + + struct Color4ub { unsigned char r, g, b, a; }; + +private: + void triangulateFill(); + void triangulateStroke(); + void updateFillNode(); + void updateStrokeNode(); + + QQuickItem *m_item; + QQuickPathItemGenericRootRenderNode *m_rootNode; + QTriangulatingStroker m_stroker; + QDashedStrokeProcessor m_dashStroker; + + QPen m_pen; + Color4ub m_strokeColor; + Color4ub m_fillColor; + Qt::FillRule m_fillRule; + QPainterPath m_path; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + + QVector m_fillVertices; + QVector m_fillIndices; + QVector m_strokeVertices; + + int m_syncDirty; + int m_effectiveDirty; +}; + +class QQuickPathItemGenericRenderNode : public QSGGeometryNode +{ +public: + QQuickPathItemGenericRenderNode(QQuickWindow *window, QQuickPathItemGenericRootRenderNode *rootNode); + ~QQuickPathItemGenericRenderNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + QQuickPathItemGenericRootRenderNode *rootNode() const { return m_rootNode; } + + // shadow data for custom materials + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry m_geometry; + QQuickWindow *m_window; + QQuickPathItemGenericRootRenderNode *m_rootNode; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericRootRenderNode : public QSGNode +{ +public: + QQuickPathItemGenericRootRenderNode(QQuickWindow *window, bool hasFill, bool hasStroke); + ~QQuickPathItemGenericRootRenderNode(); + +private: + QQuickPathItemGenericRenderNode *m_fillNode; + QQuickPathItemGenericRenderNode *m_strokeNode; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericRenderNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickPathItemLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickPathItemLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericRenderNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the PathItem-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickPathItemLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickPathItemLinearGradientShader; + } + + QQuickPathItemGenericRenderNode *node() const { return m_node; } + +private: + QQuickPathItemGenericRenderNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp new file mode 100644 index 0000000000..d07a63c86d --- /dev/null +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** 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 "qquickpathitemnvprrenderer_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemNvprRenderer::beginSync() +{ + // nothing to do here +} + +void QQuickPathItemNvprRenderer::setPath(const QQuickPath *path) +{ + convertPath(path); + m_dirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setStrokeColor(const QColor &color) +{ + m_strokeColor = color; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeWidth(qreal w) +{ + m_strokeWidth = w; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillColor(const QColor &color) +{ + m_fillColor = color; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = fillRule; + m_dirty |= DirtyFillRule; +} + +void QQuickPathItemNvprRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_joinStyle = joinStyle; + m_miterLimit = miterLimit; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_capStyle = capStyle; + m_dirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + m_dashActive = strokeStyle == QQuickPathItem::DashLine; + m_dashOffset = dashOffset; + m_dashPattern = dashPattern; + m_dirty |= DirtyDash; +} + +void QQuickPathItemNvprRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + m_fillGradientActive = gradient != nullptr; + if (gradient) { + m_fillGradient.stops = gradient->sortedGradientStops(); + m_fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + m_fillGradient.start = QPointF(g->x1(), g->y1()); + m_fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + m_dirty |= DirtyFillGradient; +} + +void QQuickPathItemNvprRenderer::endSync() +{ + // nothing to do here +} + +void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_dirty = DirtyAll; + } +} + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path) +{ + QDebugStateSaver saver(debug); + debug.space().noquote(); + debug << "Path with" << path.cmd.count() << "commands"; + int ci = 0; + for (GLubyte cmd : path.cmd) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) +{ + m_path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + QPointF pos(path->startX(), path->startY()); + m_path.cmd.append(GL_MOVE_TO_NV); + m_path.coord.append(pos.x()); + m_path.coord.append(pos.y()); + + for (QQuickPathElement *e : pp) { + if (QQuickPathMove *o = qobject_cast(e)) { + m_path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathLine *o = qobject_cast(e)) { + m_path.cmd.append(GL_LINE_TO_NV); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + m_path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&m_path.coord, o, pos); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathCubic *o = qobject_cast(e)) { + m_path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&m_path.coord, o, pos); + appendControl2Coords(&m_path.coord, o, pos); + appendCoords(&m_path.coord, o, &pos); + } else if (QQuickPathArc *o = qobject_cast(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + m_path.cmd.append(cmd); + m_path.coord.append(o->radiusX()); + m_path.coord.append(o->radiusY()); + m_path.coord.append(0.0f); // X axis rotation + appendCoords(&m_path.coord, o, &pos); + } else { + qWarning() << "PathItem/NVPR: unsupported Path element" << e; + } + } + + if (qFuzzyCompare(pos.x(), path->startX()) && qFuzzyCompare(pos.y(), path->startY())) + m_path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickPathItemNvprRenderer::updatePathRenderNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_dirty) + return; + + // updatePathRenderNode() can be called several times with different dirty + // state before render() gets invoked. So accumulate. + m_node->m_dirty |= m_dirty; + + if (m_dirty & DirtyPath) + m_node->m_source = m_path; + + if (m_dirty & DirtyStyle) { + m_node->m_strokeWidth = m_strokeWidth; + m_node->m_strokeColor = qsg_premultiply(m_strokeColor, 1.0f); + m_node->m_fillColor = qsg_premultiply(m_fillColor, 1.0f); + switch (m_joinStyle) { + case QQuickPathItem::MiterJoin: + m_node->m_joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickPathItem::BevelJoin: + m_node->m_joinStyle = GL_BEVEL_NV; + break; + case QQuickPathItem::RoundJoin: + m_node->m_joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + m_node->m_miterLimit = m_miterLimit; + switch (m_capStyle) { + case QQuickPathItem::FlatCap: + m_node->m_capStyle = GL_FLAT; + break; + case QQuickPathItem::SquareCap: + m_node->m_capStyle = GL_SQUARE_NV; + break; + case QQuickPathItem::RoundCap: + m_node->m_capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (m_dirty & DirtyFillRule) { + switch (m_fillRule) { + case QQuickPathItem::OddEvenFill: + m_node->m_fillRule = GL_COUNT_UP_NV; + break; + case QQuickPathItem::WindingFill: + m_node->m_fillRule = GL_INVERT; + break; + default: + Q_UNREACHABLE(); + } + } + + if (m_dirty & DirtyDash) { + m_node->m_dashOffset = m_dashOffset; + if (m_dashActive) { + m_node->m_dashPattern.resize(m_dashPattern.count()); + // Multiply by strokeWidth because the PathItem API follows QPen + // meaning the input dash pattern here is in width units. + for (int i = 0; i < m_dashPattern.count(); ++i) + m_node->m_dashPattern[i] = GLfloat(m_dashPattern[i]) * m_strokeWidth; + } else { + m_node->m_dashPattern.clear(); + } + } + + if (m_dirty & DirtyFillGradient) { + m_node->m_fillGradientActive = m_fillGradientActive; + if (m_fillGradientActive) + m_node->m_fillGradient = m_fillGradient; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirty = 0; +} + +bool QQuickPathItemNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; + +QQuickPathItemNvprRenderNode::QQuickPathItemNvprRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemNvprRenderNode::releaseResources() +{ + if (m_path) { + nvpr.deletePaths(m_path, 1); + m_path = 0; + } +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickPathItemNvprRenderNode::updatePath() +{ + if (m_dirty & QQuickPathItemNvprRenderer::DirtyPath) { + if (!m_path) { + m_path = nvpr.genPaths(1); + Q_ASSERT(m_path != 0); + } + nvpr.pathCommands(m_path, m_source.cmd.count(), m_source.cmd.constData(), + m_source.coord.count(), GL_FLOAT, m_source.coord.constData()); + } + + if (m_dirty & QQuickPathItemNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(m_path, GL_PATH_STROKE_WIDTH_NV, m_strokeWidth); + nvpr.pathParameteri(m_path, GL_PATH_JOIN_STYLE_NV, m_joinStyle); + nvpr.pathParameteri(m_path, GL_PATH_MITER_LIMIT_NV, m_miterLimit); + nvpr.pathParameteri(m_path, GL_PATH_END_CAPS_NV, m_capStyle); + nvpr.pathParameteri(m_path, GL_PATH_DASH_CAPS_NV, m_capStyle); + } + + if (m_dirty & QQuickPathItemNvprRenderer::DirtyDash) { + nvpr.pathParameterf(m_path, GL_PATH_DASH_OFFSET_NV, m_dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(m_path, m_dashPattern.count(), m_dashPattern.constData()); + } +} + +void QQuickPathItemNvprRenderNode::render(const RenderState *state) +{ + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + updatePath(); + + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + f->glUseProgram(0); + QQuickNvprMaterialManager::MaterialDesc *mtl; + if (m_fillGradientActive) + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + else + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + if (!mtl) + return; + + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + + if (state->scissorEnabled()) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + if (!qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive) { + if (m_fillGradientActive) { + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + } else { + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); + } + + if (!qFuzzyIsNull(m_strokeWidth) && !qFuzzyIsNull(m_strokeColor.w())) { + if (m_fillGradientActive) + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + nvpr.stencilThenCoverStrokePath(m_path, 0x1, ~0, GL_CONVEX_HULL_NV); + } + + f->glBindProgramPipeline(0); + + m_dirty = 0; +} + +QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +QRectF QQuickPathItemNvprRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + +bool QQuickPathItemNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h new file mode 100644 index 0000000000..0075572324 --- /dev/null +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMNVPRRENDERER_P_H +#define QQUICKPATHITEMNVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickPathItemNvprRenderNode; + +class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + + DirtyAll = 0xFF + }; + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setNode(QQuickPathItemNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + }; + +private: + void convertPath(const QQuickPath *path); + + QQuickPathItemNvprRenderNode *m_node = nullptr; + int m_dirty = 0; + + NvprPath m_path; + qreal m_strokeWidth; + QColor m_strokeColor; + QColor m_fillColor; + QQuickPathItem::JoinStyle m_joinStyle; + int m_miterLimit; + QQuickPathItem::CapStyle m_capStyle; + QQuickPathItem::FillRule m_fillRule; + bool m_dashActive; + qreal m_dashOffset; + QVector m_dashPattern; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; +}; + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickPathItemNvprRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemNvprRenderNode(QQuickPathItem *item); + ~QQuickPathItemNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + + static bool isSupported(); + +private: + void updatePath(); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickPathItem *m_item; + GLuint m_path = 0; + int m_dirty = 0; + + QQuickPathItemNvprRenderer::NvprPath m_source; + GLfloat m_strokeWidth; + QVector4D m_strokeColor; + QVector4D m_fillColor; + GLenum m_joinStyle; + GLint m_miterLimit; + GLenum m_capStyle; + GLenum m_fillRule; + GLfloat m_dashOffset; + QVector m_dashPattern; + bool m_fillGradientActive; + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + + friend class QQuickPathItemNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp new file mode 100644 index 0000000000..40732e4bf9 --- /dev/null +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** 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 "qquickpathitemsoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemSoftwareRenderer::beginSync() +{ + // nothing to do here +} + +void QQuickPathItemSoftwareRenderer::setPath(const QQuickPath *path) +{ + m_path = path ? path->path() : QPainterPath(); + m_dirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color) +{ + m_pen.setColor(color); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w) +{ + m_pen.setWidthF(w); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillColor(const QColor &color) +{ + m_fillColor = color; + m_brush.setColor(m_fillColor); + m_dirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +{ + m_fillRule = Qt::FillRule(fillRule); + m_dirty |= DirtyFillRule; +} + +void QQuickPathItemSoftwareRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +{ + m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + m_pen.setMiterLimit(miterLimit); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +{ + m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + switch (strokeStyle) { + case QQuickPathItem::SolidLine: + m_pen.setStyle(Qt::SolidLine); + break; + case QQuickPathItem::DashLine: + m_pen.setStyle(Qt::CustomDashLine); + m_pen.setDashPattern(dashPattern); + m_pen.setDashOffset(dashOffset); + break; + default: + break; + } + m_dirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradient) +{ + if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickPathGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickPathGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickPathGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + m_brush = QBrush(painterGradient); + } else { + m_brush = QBrush(m_fillColor); + } + m_dirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::endSync() +{ + // nothing to do here +} + +void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + // Scenegraph nodes can be destroyed and then replaced by new ones over + // time; hence it is important to mark everything dirty for + // updatePathRenderNode(). We can assume the renderer has a full sync + // of the data at this point. + m_dirty = DirtyAll; + } +} + +void QQuickPathItemSoftwareRenderer::updatePathRenderNode() +{ + if (!m_dirty) + return; + + // updatePathRenderNode() can be called several times with different dirty + // state before render() gets invoked. So accumulate. + m_node->m_dirty |= m_dirty; + + if (m_dirty & DirtyPath) { + m_node->m_path = m_path; + m_node->m_path.setFillRule(m_fillRule); + } + + if (m_dirty & DirtyFillRule) + m_node->m_path.setFillRule(m_fillRule); + + if (m_dirty & DirtyPen) + m_node->m_pen = m_pen; + + if (m_dirty & DirtyBrush) + m_node->m_brush = m_brush; + + m_node->markDirty(QSGNode::DirtyMaterial); + m_dirty = 0; +} + +QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemSoftwareRenderNode::releaseResources() +{ +} + +void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) +{ + if (m_path.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::IntersectClip); + + p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); + p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); + p->drawPath(m_path); + + m_dirty = 0; +} + +QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickPathItemSoftwareRenderNode::rect() const +{ + return QRect(0, 0, m_item->width(), m_item->height()); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h new file mode 100644 index 0000000000..584771425d --- /dev/null +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMSOFTWARERENDERER_P_H +#define QQUICKPATHITEMSOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemSoftwareRenderNode; + +class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + + DirtyAll = 0xFF + }; + + void beginSync() override; + void setPath(const QQuickPath *path) override; + void setStrokeColor(const QColor &color) override; + void setStrokeWidth(qreal w) override; + void setFillColor(const QColor &color) override; + void setFillRule(QQuickPathItem::FillRule fillRule) override; + void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(QQuickPathItem::CapStyle capStyle) override; + void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(QQuickPathGradient *gradient) override; + void endSync() override; + void updatePathRenderNode() override; + + void setNode(QQuickPathItemSoftwareRenderNode *node); + +private: + QQuickPathItemSoftwareRenderNode *m_node = nullptr; + int m_dirty = 0; + + QPainterPath m_path; + QPen m_pen; + QColor m_fillColor; + QBrush m_brush; + Qt::FillRule m_fillRule; +}; + +class QQuickPathItemSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); + ~QQuickPathItemSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickPathItem *m_item; + int m_dirty = 0; + + QPainterPath m_path; + QPen m_pen; + QBrush m_brush; + + friend class QQuickPathItemSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/items/shaders/lineargradient.frag b/src/quick/items/shaders/lineargradient.frag new file mode 100644 index 0000000000..7f4a739109 --- /dev/null +++ b/src/quick/items/shaders/lineargradient.frag @@ -0,0 +1,9 @@ +uniform sampler2D gradTabTexture; +uniform highp float opacity; + +varying highp float gradTabIndex; + +void main() +{ + gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quick/items/shaders/lineargradient.vert b/src/quick/items/shaders/lineargradient.vert new file mode 100644 index 0000000000..eb21b8886b --- /dev/null +++ b/src/quick/items/shaders/lineargradient.vert @@ -0,0 +1,15 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +varying float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quick/items/shaders/lineargradient_core.frag b/src/quick/items/shaders/lineargradient_core.frag new file mode 100644 index 0000000000..5908acfa67 --- /dev/null +++ b/src/quick/items/shaders/lineargradient_core.frag @@ -0,0 +1,12 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +in float gradTabIndex; +out vec4 fragColor; + +void main() +{ + fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/quick/items/shaders/lineargradient_core.vert b/src/quick/items/shaders/lineargradient_core.vert new file mode 100644 index 0000000000..60b56f38e3 --- /dev/null +++ b/src/quick/items/shaders/lineargradient_core.vert @@ -0,0 +1,17 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +out float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 8de205fc98..b8ed190b33 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -978,6 +978,10 @@ void Renderer::nodeChangedBatchRoot(Node *node, Node *root) e->root = root; e->boundsComputed = false; } + } else if (node->type() == QSGNode::RenderNodeType) { + RenderNodeElement *e = node->renderNodeElement(); + if (e) + e->root = root; } SHADOWNODE_TRAVERSE(node) diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 5915d51f2b..1bc0210b72 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -160,10 +160,7 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const \list \li glDepthMask(false) \li glDisable(GL_DEPTH_TEST) - \li glStencilMask(0) - \li glEnable(GL_STENCIL_TEST)/glDisable(GL_STENCIL_TEST) depending on clip \li glStencilFunc(GL_EQUAL, state.stencilValue, 0xff) depending on clip - \li glEnable(GL_SCISSOR_TEST)/glDisable(GL_SCISSOR_TEST) depending on clip \li glScissor(state.scissorRect.x(), state.scissorRect.y(), state.scissorRect.width(), state.scissorRect.height()) depending on clip \li glEnable(GL_BLEND) diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 2c5b4ff5c8..aaf4635c0c 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -260,7 +260,7 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri if (vertexCode || fragmentCode) { Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0, "QSGRenderContext::compile()", - "materials with custom compile step cannot have custom vertex/fragment code"); + "materials with custom compile step cannot have modified vertex or fragment code"); QOpenGLShaderProgram *p = shader->program(); p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader()); p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader()); diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 47248f2f37..d761eac62f 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -251,11 +251,15 @@ static void qt_debug_remove_texture(QSGTexture* texture) Specifies how the texture should treat texture coordinates. - \value Repeat Only the factional part of the texture coordiante is + \value Repeat Only the fractional part of the texture coordinate is used, causing values above 1 and below 0 to repeat. \value ClampToEdge Values above 1 are clamped to 1 and values below 0 are clamped to 0. + + \value MirroredRepeat When the texture coordinate is even, only the + fractional part is used. When odd, the texture coordinate is set to + \c{1 - fractional part}. This value has been introduced in Qt 5.10. */ /*! @@ -550,7 +554,9 @@ void QSGTexture::updateBindOptions(bool force) if (force || d->wrapChanged) { #ifndef QT_NO_DEBUG - if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) { + if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat + || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat) + { bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); QSize size = textureSize(); bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height()); @@ -558,8 +564,18 @@ void QSGTexture::updateBindOptions(bool force) qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures."); } #endif - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + GLenum wrapS = GL_CLAMP_TO_EDGE; + if (d->horizontalWrap == Repeat) + wrapS = GL_REPEAT; + else if (d->horizontalWrap == MirroredRepeat) + wrapS = GL_MIRRORED_REPEAT; + GLenum wrapT = GL_CLAMP_TO_EDGE; + if (d->verticalWrap == Repeat) + wrapT = GL_REPEAT; + else if (d->verticalWrap == MirroredRepeat) + wrapT = GL_MIRRORED_REPEAT; + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); d->wrapChanged = false; } #else diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h index f0509b58ae..2b29525efd 100644 --- a/src/quick/scenegraph/util/qsgtexture.h +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -58,7 +58,8 @@ public: enum WrapMode { Repeat, - ClampToEdge + ClampToEdge, + MirroredRepeat }; enum Filtering { diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index b6fcfc31c4..3a10d85b4d 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -70,8 +70,8 @@ public: uint wrapChanged : 1; uint filteringChanged : 1; - uint horizontalWrap : 1; - uint verticalWrap : 1; + uint horizontalWrap : 2; + uint verticalWrap : 2; uint mipmapMode : 2; uint filterMode : 2; }; diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index 9326ea640d..fcafbba6a2 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -278,7 +278,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) Returns this material's horizontal wrap mode. - The default horizontal wrap mode is \c QSGTexutre::ClampToEdge. + The default horizontal wrap mode is \c QSGTexture::ClampToEdge. */ @@ -299,7 +299,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) Returns this material's vertical wrap mode. - The default vertical wrap mode is \c QSGTexutre::ClampToEdge. + The default vertical wrap mode is \c QSGTexture::ClampToEdge. */ diff --git a/src/quick/util/qquicknvprfunctions.cpp b/src/quick/util/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..40eb2bb932 --- /dev/null +++ b/src/quick/util/qquicknvprfunctions.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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 "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); + q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); + q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); + q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); + q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); + q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/quick/util/qquicknvprfunctions_p.h b/src/quick/util/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..7900388305 --- /dev/null +++ b/src/quick/util/qquicknvprfunctions_p.h @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; + PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; + PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; + PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; + PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; + PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/quick/util/qquicknvprfunctions_p_p.h b/src/quick/util/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/quick/util/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 25a4433a9b..a6fa21d696 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE \instantiates QQuickPath \inqmlmodule QtQuick \ingroup qtquick-animation-paths - \brief Defines a path for use by \l PathView + \brief Defines a path for use by \l PathView and \l PathItem A Path is composed of one or more path segments - PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg. @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE PathAttribute allows named attributes with values to be defined along the path. - \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) : QObject(*(new QQuickPathPrivate), parent) @@ -1017,7 +1017,7 @@ void QQuickPathAttribute::setValue(qreal value) } \endqml - \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathMove */ /*! @@ -1059,6 +1059,66 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) /****************************************************************************/ +/*! + \qmltype PathMove + \instantiates QQuickPathMove + \inqmlmodule QtQuick + \ingroup qtquick-animation-paths + \brief Moves the Path's position + + While not relevant with PathView, for Path elements used with PathItem it + is important to distinguish between the operations of drawing a straight + line and moving the path position without drawing anything. + + \note PathMove should not be used in a Path associated with a PathView. Use + PathLine instead. + + The example below creates a path consisting of two horizontal lines with + some empty space between them. All three segments have a width of 100: + + \qml + Path { + startX: 0; startY: 100 + PathLine { relativeX: 100; y: 100 } + PathMove { relativeX: 100; y: 100 } + PathLine { relativeX: 100; y: 100 } + } + \endqml + + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine +*/ + +/*! + \qmlproperty real QtQuick::PathMove::x + \qmlproperty real QtQuick::PathMove::y + + Defines the position to move to. + + \sa relativeX, relativeY +*/ + +/*! + \qmlproperty real QtQuick::PathMove::relativeX + \qmlproperty real QtQuick::PathMove::relativeY + + Defines the position to move to relative to its start. + + If both a relative and absolute end position are specified for a single axis, the relative + position will be used. + + Relative and absolute positions can be mixed, for example it is valid to set a relative x + and an absolute y. + + \sa x, y +*/ + +void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data) +{ + path.moveTo(positionForCurve(data, path.currentPosition())); +} + +/****************************************************************************/ + /*! \qmltype PathQuad \instantiates QQuickPathQuad @@ -1655,6 +1715,7 @@ void QQuickPathArc::setRadiusX(qreal radius) _radiusX = radius; emit radiusXChanged(); + emit changed(); } qreal QQuickPathArc::radiusY() const @@ -1669,6 +1730,7 @@ void QQuickPathArc::setRadiusY(qreal radius) _radiusY = radius; emit radiusYChanged(); + emit changed(); } /*! @@ -1702,6 +1764,7 @@ void QQuickPathArc::setUseLargeArc(bool largeArc) _useLargeArc = largeArc; emit useLargeArcChanged(); + emit changed(); } /*! @@ -1733,6 +1796,7 @@ void QQuickPathArc::setDirection(ArcDirection direction) _direction = direction; emit directionChanged(); + emit changed(); } void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 06ad389b49..283283f377 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -159,6 +159,15 @@ public: void addToPath(QPainterPath &path, const QQuickPathData &) override; }; +class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve +{ + Q_OBJECT +public: + QQuickPathMove(QObject *parent=0) : QQuickCurve(parent) {} + + void addToPath(QPainterPath &path, const QQuickPathData &) override; +}; + class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve { Q_OBJECT @@ -459,6 +468,7 @@ QML_DECLARE_TYPE(QQuickPathElement) QML_DECLARE_TYPE(QQuickPathAttribute) QML_DECLARE_TYPE(QQuickCurve) QML_DECLARE_TYPE(QQuickPathLine) +QML_DECLARE_TYPE(QQuickPathMove) QML_DECLARE_TYPE(QQuickPathQuad) QML_DECLARE_TYPE(QQuickPathCubic) QML_DECLARE_TYPE(QQuickPathCatmullRomCurve) diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 1ef1018a31..22f23f7598 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -74,4 +74,11 @@ qtConfig(quick-path) { $$PWD/qquickpath_p.h \ $$PWD/qquickpath_p_p.h \ $$PWD/qquickpathinterpolator_p.h + qtConfig(opengl) { + SOURCES += \ + $$PWD/qquicknvprfunctions.cpp + HEADERS += \ + $$PWD/qquicknvprfunctions_p.h \ + $$PWD/qquicknvprfunctions_p_p.h + } } -- cgit v1.2.3 From bf7c6226264bd45095711b2c0556c42b6f267f72 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 19 Dec 2016 13:53:29 +0100 Subject: software backend: Fix clipping of QSGRenderNodes Change-Id: I27aa5f94165fb07807d2bb711d81eade552b9f76 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 8 ++++---- .../adaptations/software/qsgsoftwarerenderablenode.cpp | 17 ++++++++++------- src/quick/scenegraph/coreapi/qsgrendernode.cpp | 5 ++++- 3 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 40732e4bf9..f089904fdf 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -202,12 +202,12 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); Q_ASSERT(p); - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - const QRegion *clipRegion = state->clipRegion(); if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::IntersectClip); + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 59c47db0c4..7adb39d9a8 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -254,18 +254,21 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu QMatrix4x4 m = m_transform; rd->m_matrix = &m; rd->m_opacity = m_opacity; - RenderNodeState rs; - rs.cr = m_clipRegion; - const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) - ? m_boundingRect : - QRect(0, 0, painter->device()->width(), painter->device()->height()); + // all the clip region below is in world coordinates, taking m_transform into account already + QRegion cr = m_dirtyRegion; + if (m_clipRegion.rectCount() > 1) + cr &= m_clipRegion; painter->save(); - painter->setClipRegion(br, Qt::ReplaceClip); + RenderNodeState rs; + rs.cr = cr; m_handle.renderNode->render(&rs); painter->restore(); + const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering) + ? m_boundingRect // already mapped to world + : QRect(0, 0, painter->device()->width(), painter->device()->height()); m_previousDirtyRegion = QRegion(br); m_isDirty = false; m_dirtyRegion = QRegion(); @@ -276,7 +279,7 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu painter->save(); painter->setOpacity(m_opacity); - // Set clipRegion to m_dirtyRegion (in world coordinates) + // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below) // as m_dirtyRegion already accounts for clipRegion painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip); if (m_clipRegion.rectCount() > 1) diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 1bc0210b72..3007e0000c 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -359,7 +359,10 @@ QSGRenderNode::RenderState::~RenderState() of the render state is not in use. However, the clip region that can be set on the QPainter still has to be communicated since reconstructing this manually in render() is not reasonable. It can therefore be queried via - this function. + this function. The region is in world coordinates and can be passed + to QPainter::setClipRegion() with Qt::ReplaceClip. This must be done before + calling QPainter::setTransform() since the clip region is already mapped to + the transform provided in QSGRenderNode::matrix(). */ /*! -- cgit v1.2.3 From 79831caa0533ad5f48322568557622596b85ed0f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 21 Dec 2016 13:15:38 +0100 Subject: Treat 0 as a valid strokeWidth value Use negative values as the trigger to disable stroking altogether. This matches both QPainter and NVPR better. Change-Id: I51395ae310fce8a8da0c06174eafa1dc17aae1db Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemgenericrenderer.cpp | 6 ++++-- src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 0d2c7a8bbe..720916c900 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -455,7 +455,7 @@ QSGNode *QQuickPathItemPrivate::createRenderNode() return node; const bool hasFill = fillColor != Qt::transparent; - const bool hasStroke = !qFuzzyIsNull(strokeWidth) && strokeColor != Qt::transparent; + const bool hasStroke = strokeWidth >= 0.0f && strokeColor != Qt::transparent; switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 9bd03c0e51..4b15daef9a 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -145,7 +145,9 @@ void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color) void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w) { - m_pen.setWidthF(w); + m_strokeWidth = w; + if (w >= 0.0f) + m_pen.setWidthF(w); m_syncDirty |= DirtyGeom; } @@ -311,7 +313,7 @@ void QQuickPathItemGenericRenderer::updatePathRenderNode() m_effectiveDirty |= DirtyGeom; } - if (qFuzzyIsNull(m_pen.widthF()) || m_strokeColor.a == 0) { + if (m_strokeWidth < 0.0f || m_strokeColor.a == 0) { delete m_rootNode->m_strokeNode; m_rootNode->m_strokeNode = nullptr; } else if (!m_rootNode->m_strokeNode) { diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index c186959c88..a094b9fca6 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -107,6 +107,7 @@ private: QTriangulatingStroker m_stroker; QDashedStrokeProcessor m_dashStroker; + float m_strokeWidth; QPen m_pen; Color4ub m_strokeColor; Color4ub m_fillColor; diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index d07a63c86d..e0c458bfca 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -529,7 +529,7 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); } - if (!qFuzzyIsNull(m_strokeWidth) && !qFuzzyIsNull(m_strokeColor.w())) { + if (m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w())) { if (m_fillGradientActive) mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], -- cgit v1.2.3 From f7d7cf58330ea0b2d6687a227c7e7302709ca47f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 15 Dec 2016 23:24:43 +0100 Subject: more renaming QPointerUniqueId -> QPointingDeviceUniqueId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I171ab6ff2ef5022c637d45ff27ec472bf5c1770b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickhandlersmodule.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index 97d6f1d499..0daa1841f8 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -71,9 +71,9 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler")); qmlRegisterUncreatableType(uri, major, minor, "PointerDevice", QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent")); - qRegisterMetaType("QPointerUniqueId"); - qmlRegisterUncreatableType(uri, major, minor, "PointerUniqueId", - QQuickPointerHandler::tr("PointerUniqueId is only available as a property of PointerEvent")); + qRegisterMetaType("QPointingDeviceUniqueId"); + qmlRegisterUncreatableType(uri, major, minor, "PointingDeviceUniqueId", + QQuickPointerHandler::tr("PointingDeviceUniqueId is only available as a property of PointerEvent")); qmlRegisterType(uri,major,minor,"PointerHandler"); qmlRegisterType(uri,major,minor,"DragHandler"); -- cgit v1.2.3 From 4f69895cf15b9ff76b9a4404709a28153d34de5e Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Mon, 19 Dec 2016 16:19:09 +0100 Subject: Fixed bug in DragHandler when dragging in scaled coordinate system Calculate the new position in the target items' parent coordinate system in order to not be affected by any transformations applied on the target item. And after all, the item's position is specified in parent coordinates. Also check target() and target()->parentItem() rather than assuming that they are non-null. Change-Id: Ib00c72cce7b324b508884287e4070bedaf02b95e Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index dadf8bba04..f2a1b74d6c 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -82,7 +82,8 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) point->setAccepted(); switch (point->state()) { case QQuickEventPoint::Pressed: - m_startPos = target()->mapToScene(QPointF()); + if (target() && target()->parentItem()) + m_startPos = target()->parentItem()->mapToScene(target()->position()); break; case QQuickEventPoint::Updated: { QPointF delta = point->scenePos() - point->scenePressPos(); @@ -91,9 +92,11 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) if (!m_yAxis.enabled()) delta.setY(0); if (m_dragging) { - QPointF pos = target()->parentItem()->mapFromScene(m_startPos) + delta; - enforceAxisConstraints(&pos); - target()->setPosition(pos); + if (target() && target()->parentItem()) { + QPointF pos = target()->parentItem()->mapFromScene(m_startPos + delta); + enforceAxisConstraints(&pos); + target()->setPosition(pos); + } } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { m_dragging = true; -- cgit v1.2.3 From 3eeffae835b3474c8a3ca62125cb8ec24acdaa84 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 13 Dec 2016 11:38:23 +0100 Subject: Let pinchhandler operate on QQuickItem properties This requires coordinate system mapping that varies with the transformOrigin. The properties exposed from PinchHandler (rotation, scale and translation) are currently relative to the point when the pinch became active. (Therefore, rotation, will reset back to 0 when a new pinch is activated). Its still unclear how the properties that limits the transform should influence. With this patch, they are like this: * {min,max}imumRotation applies to the actual rotation of the item. * {min,max}imumScale applies to the actual scale of the item. * {min,max}imum{X,Y} applies to the actual position of the item. (This has some unfortunate side-effects when the item is scaled or rotated, since the items actual position will change as it rotates) In addition, the behavior described above means that the limits won't have any effect if there is no target item. Change-Id: I279fb03667cd75324e8337039ae2594658265d13 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 2 +- src/quick/handlers/qquickpinchhandler.cpp | 101 ++++++++++++----------- src/quick/handlers/qquickpinchhandler_p.h | 15 ++-- 3 files changed, 62 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 746d33a12c..c2060ca3c5 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -167,7 +167,7 @@ QVector QQuickMultiPointerHandler::angles( angles.reserve(m_currentPoints.count()); for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { qreal angle = QLineF(ref, point->scenePos()).angle(); - angles.append(PointData(point->pointId(), angle)); + angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation } return angles; } diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 8ee8e2c6b6..45d8235dff 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -64,9 +64,9 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch") QQuickPinchHandler::QQuickPinchHandler(QObject *parent) : QQuickMultiPointerHandler(parent, 2) - , m_scale(1) - , m_rotation(0) - , m_translation(0,0) + , m_activeScale(1) + , m_activeRotation(0) + , m_activeTranslation(0,0) , m_minimumScale(-qInf()) , m_maximumScale(qInf()) , m_minimumRotation(-qInf()) @@ -79,7 +79,6 @@ QQuickPinchHandler::QQuickPinchHandler(QObject *parent) , m_startScale(1) , m_startRotation(0) { - connect(this, &QQuickPinchHandler::targetChanged, this, &QQuickPinchHandler::onTargetChanged); } QQuickPinchHandler::~QQuickPinchHandler() @@ -199,27 +198,26 @@ void QQuickPinchHandler::setMaximumY(qreal maxY) void QQuickPinchHandler::onActiveChanged() { if (active()) { - m_startScale = m_scale; // TODO incompatible with independent x/y scaling - m_startRotation = m_rotation; - m_startCentroid = touchPointCentroid(); - m_startAngles = angles(m_startCentroid); - m_startDistance = averageTouchPointDistance(m_startCentroid); - m_activeRotation = 0; - m_startMatrix = m_transform.matrix(); - qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; - grabPoints(m_currentPoints); + if (const QQuickItem *t = target()) { + m_startScale = t->scale(); // TODO incompatible with independent x/y scaling + m_startRotation = t->rotation(); + m_startCentroid = touchPointCentroid(); + m_startAngles = angles(m_startCentroid); + m_startDistance = averageTouchPointDistance(m_startCentroid); + QVector3D xformOrigin(t->transformOriginPoint()); + m_startMatrix = QMatrix4x4(); + m_startMatrix.translate(t->x(), t->y()); + m_startMatrix.translate(xformOrigin); + m_startMatrix.scale(m_startScale); + m_startMatrix.rotate(m_startRotation, 0, 0, -1); + m_startMatrix.translate(-xformOrigin); + m_activeRotation = 0; + m_activeTranslation = QPointF(0,0); + qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; + grabPoints(m_currentPoints); + } } else { - qCInfo(lcPinchHandler) << "deactivated with scale" << m_scale << "rotation" << m_rotation; - } -} - -void QQuickPinchHandler::onTargetChanged() -{ - if (target()) { - // TODO if m_target was previously set differently, - // does prepending to the new target remove it from the old one? - // If not, should we fix that there, or here? - m_transform.prependToItem(target()); + qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation; } } @@ -237,51 +235,60 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); // avoid mapping the minima and maxima, as they might have unmappable values // such as -inf/+inf. Because of this we perform the bounding to min/max in local coords. - QPointF centroidLocalPos; + QPointF centroidParentPos; if (target() && target()->parentItem()) { - centroidLocalPos = target()->parentItem()->mapFromScene(m_centroid); - centroidLocalPos = QPointF(qBound(bounds.left(), centroidLocalPos.x(), bounds.right()), - qBound(bounds.top(), centroidLocalPos.y(), bounds.bottom())); + centroidParentPos = target()->parentItem()->mapFromScene(m_centroid); + centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()), + qBound(bounds.top(), centroidParentPos.y(), bounds.bottom())); } // 1. scale - qreal dist = averageTouchPointDistance(m_centroid); - qreal activeScale = dist / m_startDistance; - activeScale = qBound(m_minimumScale/m_startScale, activeScale, m_maximumScale/m_startScale); - m_scale = m_startScale * activeScale; + const qreal dist = averageTouchPointDistance(m_centroid); + m_activeScale = dist / m_startDistance; + m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale); + const qreal scale = m_startScale * m_activeScale; // 2. rotate QVector newAngles = angles(m_centroid); const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles); m_activeRotation += angleDelta; const qreal totalRotation = m_startRotation + m_activeRotation; - m_rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); - m_activeRotation += (m_rotation - totalRotation); //adjust for the potential bounding above + const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation); + m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above m_startAngles = std::move(newAngles); if (target() && target()->parentItem()) { // 3. Drag/translate - QPointF activeTranslation(centroidLocalPos - target()->parentItem()->mapFromScene(m_startCentroid)); + const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid); + m_activeTranslation = centroidParentPos - centroidStartParentPos; // apply rotation + scaling around the centroid - then apply translation. QMatrix4x4 mat; - QVector3D xlatOrigin(centroidLocalPos - target()->position()); - mat.translate(xlatOrigin); - mat.rotate(m_activeRotation, 0, 0, -1); - mat.scale(activeScale); - mat.translate(-xlatOrigin); - mat.translate(QVector3D(activeTranslation)); + + const QVector3D centroidParentVector(centroidParentPos); + mat.translate(centroidParentVector); + mat.rotate(m_activeRotation, 0, 0, 1); + mat.scale(m_activeScale); + mat.translate(-centroidParentVector); + mat.translate(QVector3D(m_activeTranslation)); + + mat = mat * m_startMatrix; + + QPointF xformOriginPoint = target()->transformOriginPoint(); + QPointF pos = mat * xformOriginPoint; + pos -= xformOriginPoint; + + target()->setPosition(pos); + target()->setRotation(rotation); + target()->setScale(scale); + // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid << ", distance" << m_startDistance << "->" << dist - << ", startScale" << m_startScale << "->" << m_scale + << ", startScale" << m_startScale << "->" << scale << ", activeRotation" << m_activeRotation - << ", rotation" << m_rotation; - - mat = mat * m_startMatrix; - m_transform.setMatrix(mat); - m_translation = QPointF(mat.constData()[12], mat.constData()[13]); + << ", rotation" << rotation; } acceptPoints(m_currentPoints); diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 83d6447b15..0861368682 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -99,9 +99,9 @@ public: PinchOrigin pinchOrigin() const { return m_pinchOrigin; } void setPinchOrigin(PinchOrigin pinchOrigin); - QPointF translation() const { return m_translation; } - qreal scale() const { return m_scale; } - qreal rotation() const { return m_rotation; } + QPointF translation() const { return m_activeTranslation; } + qreal scale() const { return m_activeScale; } + qreal rotation() const { return m_activeRotation; } QPointF centroid() const { return m_centroid; } qreal minimumX() const { return m_minimumX; } @@ -128,14 +128,13 @@ signals: protected: void onActiveChanged() override; - void onTargetChanged(); void handlePointerEventImpl(QQuickPointerEvent *event) override; private: // properties - qreal m_scale; - qreal m_rotation; - QPointF m_translation; + qreal m_activeScale; + qreal m_activeRotation; + QPointF m_activeTranslation; QPointF m_centroid; qreal m_minimumScale; @@ -154,9 +153,9 @@ private: // internal qreal m_startScale; qreal m_startRotation; - qreal m_activeRotation; QPointF m_startCentroid; qreal m_startDistance; + QPointF m_startPos; QVector m_startAngles; QMatrix4x4 m_startMatrix; -- cgit v1.2.3 From c4c4e9ddc1f659abd3abb57ba04b8524e2224bfe Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 22 Jul 2016 14:59:04 +0200 Subject: Estimate the velocity of a point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I45cbfc69a533ea9e295825aef23edc70c88fa2c6 Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 64 ++++++++++++++++++++++++++++++++++++-- src/quick/items/qquickevents_p_p.h | 4 +++ 2 files changed, 66 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 1fd9014408..727819b3f0 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qquickevents_p_p.h" +#include #include #include #include @@ -531,8 +532,7 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, m_pressTimestamp = timestamp; m_scenePressPos = scenePos; } - // TODO if Q_LIKELY(velocity.isNull) calculate velocity - m_velocity = velocity; + m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity); } void QQuickEventPoint::localize(QQuickItem *target) @@ -637,6 +637,66 @@ void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong times m_uniqueId = tp.uniqueId(); } +struct PointVelocityData { + QVector2D velocity; + QPointF pos; + ulong timestamp; +}; + +typedef QMap PointDataForPointIdMap; +Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData) +static const int PointVelocityAgeLimit = 500; // milliseconds + +/*! + * \interal + * \brief Estimates the velocity based on a weighted average of all previous velocities. + * The older the velocity is, the less significant it becomes for the estimate. + * \return + */ +QVector2D QQuickEventPoint::estimatedVelocity() const +{ + PointVelocityData *prevPoint = g_previousPointData->value(m_pointId); + if (!prevPoint) { + // cleanup events older than PointVelocityAgeLimit + auto end = g_previousPointData->end(); + for (auto it = g_previousPointData->begin(); it != end; ) { + PointVelocityData *data = it.value(); + if (m_timestamp - data->timestamp > PointVelocityAgeLimit) { + it = g_previousPointData->erase(it); + delete data; + } else { + ++it; + } + } + // TODO optimize: stop this dynamic memory thrashing + prevPoint = new PointVelocityData; + prevPoint->velocity = QVector2D(); + prevPoint->timestamp = 0; + prevPoint->pos = QPointF(); + g_previousPointData->insert(m_pointId, prevPoint); + } + if (prevPoint) { + const ulong timeElapsed = m_timestamp - prevPoint->timestamp; + if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint + return m_velocity; + + QVector2D newVelocity; + if (prevPoint->timestamp != 0) + newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed; + + // VERY simple kalman filter: does a weighted average + // where the older velocities get less and less significant + static const float KalmanGain = 0.7f; + QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain); + + prevPoint->velocity = filteredVelocity; + prevPoint->pos = m_scenePos; + prevPoint->timestamp = m_timestamp; + return filteredVelocity; + } + return QVector2D(); +} + /*! \internal \class QQuickPointerEvent diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 33574deb9d..d378388184 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -259,6 +259,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(State state READ state) Q_PROPERTY(quint64 pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) + Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) Q_PROPERTY(QObject *grabber READ grabber WRITE setGrabber) @@ -302,6 +303,9 @@ public: Q_INVOKABLE void cancelGrab(); +private: + QVector2D estimatedVelocity() const; + private: QPointF m_pos; QPointF m_scenePos; -- cgit v1.2.3 From 953b5070160a3a1bed7aaf986a1bb1c3b33b0dca Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 21 Nov 2016 12:40:15 +0100 Subject: add QQuickEventPoint::isDraggedOverThreshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be reusable functionality. OTOH it considers both axes at once, which is not sufficient for DragHandler to use it. If only Qt::XAxis and Qt::YAxis were ORable flags, we could use a flags parameter (with a default) to control which axes to check. Change-Id: I930b39ad35bc669fcc95229b5a8dac7e99c45d78 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 7 +++++++ src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 727819b3f0..05998fc500 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -625,6 +625,13 @@ void QQuickEventPoint::setAccepted(bool accepted) } } +bool QQuickEventPoint::isDraggedOverThreshold() const +{ + QPointF delta = scenePos() - scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, this) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, this)); +} + QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) {} diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index d378388184..5e85aa421e 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -292,6 +292,7 @@ public: qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); + bool isDraggedOverThreshold() const; QObject *grabber() const; void setGrabber(QObject *grabber); -- cgit v1.2.3 From 70113ef66b245e58ce12e2a9d26268e2eaeb3a42 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 23 Dec 2016 15:30:33 +0100 Subject: Don't assume that target is the parent item To summarize: A pointer handler always gets its pointer events from its parent item. It applies its effect (drag, pinch, ...) on the item referenced to by the target property. By default, target refers to the parent, but that is not always the case. In addition to this we also have to handle the case when the target is null Change-Id: If62108abf0aeb713906bf88472ad9a32a74efff6 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler.cpp | 3 -- src/quick/handlers/qquickhandlersmodule.cpp | 6 ++-- src/quick/handlers/qquickpointerhandler.cpp | 42 ++++++++++++++--------- src/quick/handlers/qquickpointerhandler_p.h | 7 ++-- src/quick/handlers/qquickpointersinglehandler.cpp | 2 +- src/quick/items/qquickflickable.cpp | 2 -- src/quick/items/qquickitem.cpp | 7 +++- src/quick/items/qquickwindow.cpp | 2 +- 8 files changed, 40 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index f2a1b74d6c..a1c3486bd0 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -76,9 +76,6 @@ bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) { - // If there's no target or the target has no parent, we shouldn't be dragging - if (!target() || !target()->parentItem()) - return; point->setAccepted(); switch (point->state()) { case QQuickEventPoint::Pressed: diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index 0daa1841f8..697a198e67 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -53,10 +53,10 @@ QT_BEGIN_NAMESPACE static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent) { - if (QQuickItem *parentItem = qmlobject_cast(parent)) { + if (qmlobject_cast(parent)) { QQuickPointerHandler *handler = qmlobject_cast(obj); - if (handler && !handler->target()) { - handler->setTarget(parentItem); + if (handler) { + handler->setParent(parent); return QQmlPrivate::Parented; } } diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 6225d2a24a..d51835a24b 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -60,9 +60,19 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) , m_target(nullptr) , m_enabled(true) , m_active(false) + , m_targetExplicitlySet(false) { } +QQuickPointerHandler::~QQuickPointerHandler() +{ + QQuickItem *parItem = parentItem(); + if (parItem) { + QQuickItemPrivate *p = QQuickItemPrivate::get(parItem); + p->extra.value().pointerHandlers.removeOne(this); + } +} + void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) { if (grab) { @@ -81,14 +91,16 @@ void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const { - return (m_target ? m_target->mapFromScene(point->scenePos()) : point->scenePos()); + return (target() ? target()->mapFromScene(point->scenePos()) : point->scenePos()); } -bool QQuickPointerHandler::targetContains(const QQuickEventPoint *point) const +bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const { - if (!m_target || !point) - return false; - return m_target->contains(m_target->mapFromScene(point->scenePos())); + if (point) { + if (QQuickItem *par = parentItem()) + return par->contains(par->mapFromScene(point->scenePos())); + } + return false; } /*! @@ -111,25 +123,21 @@ void QQuickPointerHandler::setEnabled(bool enabled) void QQuickPointerHandler::setTarget(QQuickItem *target) { + m_targetExplicitlySet = true; if (m_target == target) return; - if (m_target) { - QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); - p->extra.value().pointerHandlers.removeOne(this); - } - m_target = target; - if (m_target) { - QQuickItemPrivate *p = QQuickItemPrivate::get(m_target); - p->extra.value().pointerHandlers.append(this); - // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, - // because there can be multiple handlers... - m_target->setAcceptedMouseButtons(Qt::AllButtons); - } emit targetChanged(); } +QQuickItem *QQuickPointerHandler::target() const +{ + if (!m_targetExplicitlySet) + return parentItem(); + return m_target; +} + void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) { const bool wants = wantsPointerEvent(event); diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 9595897d72..85f1096410 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -70,7 +70,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject public: QQuickPointerHandler(QObject *parent = 0); - virtual ~QQuickPointerHandler() { } + virtual ~QQuickPointerHandler(); public: bool enabled() const { return m_enabled; } @@ -78,7 +78,7 @@ public: bool active() const { return m_active; } - QQuickItem *target() const { return m_target; } + QQuickItem *target() const; void setTarget(QQuickItem *target); QQuickItem * parentItem() const { return static_cast(QObject::parent()); } @@ -102,13 +102,14 @@ protected: void setGrab(QQuickEventPoint *point, bool grab); virtual void handleGrabCancel(QQuickEventPoint *point); QPointF eventPos(const QQuickEventPoint *point) const; - bool targetContains(const QQuickEventPoint *point) const; + bool parentContains(const QQuickEventPoint *point) const; private: QQuickPointerEvent *m_currentEvent; QQuickItem *m_target; bool m_enabled : 1; bool m_active : 1; + bool m_targetExplicitlySet : 1; friend class QQuickEventPoint; }; diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index d97348ea22..5b2d6b0801 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -110,7 +110,7 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) { - return targetContains(point); + return parentContains(point); } void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 5561f8d976..80e34b033d 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1785,8 +1785,6 @@ void QQuickFlickablePrivate::data_append(QQmlListProperty *prop, QObjec i->setParentItem(static_cast(prop->data)->contentItem); } else { o->setParent(prop->object); // XXX todo - do we want this? - if (QQuickPointerHandler *pointerHandler = qmlobject_cast(o)) - pointerHandler->setTarget(static_cast(prop->object)); } } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 97a4e51def..55e82a7a4d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3222,7 +3222,12 @@ void QQuickItemPrivate::data_append(QQmlListProperty *prop, QObject *o) if (o->inherits("QGraphicsItem")) qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); else if (QQuickPointerHandler *pointerHandler = qmlobject_cast(o)) { - pointerHandler->setTarget(that); + Q_ASSERT(pointerHandler->parentItem() == that); + // Accept all buttons, and leave filtering to pointerEvent() and/or user JS, + // because there can be multiple handlers... + that->setAcceptedMouseButtons(Qt::AllButtons); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + p->extra.value().pointerHandlers.append(pointerHandler); } else { QQuickWindow *thisWindow = qmlobject_cast(o); QQuickItem *item = that; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 96c2937038..7aa6d1aa66 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2252,7 +2252,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve // we need the item in order to call deliverMatchingPointsToItem(). QQuickItem *receiver = qmlobject_cast(grabber); if (!receiver) - receiver = static_cast(grabber)->target(); + receiver = static_cast(grabber)->parentItem(); deliverMatchingPointsToItem(receiver, event, hasFiltered); } -- cgit v1.2.3 From 871314ced803afe0ac31033cfbd79fa4c1b18e3d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 8 Dec 2016 19:19:04 +0100 Subject: DragHandler active property replaces dragging; same as grabbing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a QQuickPointerSingleHandler grabs a point, it's definitely in the active state: doing something with the point. (The converse is not always true though: e.g. TapHandler can sometimes detect a tap without ever grabbing.) In DragHandler, the "dragging" property means the same as "active": we always grab when dragging, to be sure to get the updates. So the "dragging" property is removed because it's redundant. In QQuickPointerHandler we don't say that "wanting" an event is the same as being active, because 1) it won't necessarily grab right away and 2) every handler which was active should "want" the release event, yet it needs to setActive(false) as soon as it's done processing it. Change-Id: Ie010db54714a7914109da6469e79865f9a0a18e4 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 11 +---------- src/quick/handlers/qquickdraghandler_p.h | 5 ----- src/quick/handlers/qquickpinchhandler.cpp | 4 ++++ src/quick/handlers/qquickpointerhandler.cpp | 4 +--- src/quick/handlers/qquickpointersinglehandler.cpp | 11 +++++++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 1 + 6 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index a1c3486bd0..dc3116e300 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE QQuickDragHandler::QQuickDragHandler(QObject *parent) : QQuickPointerSingleHandler(parent) - , m_dragging(false) { } @@ -88,7 +87,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) delta.setX(0); if (!m_yAxis.enabled()) delta.setY(0); - if (m_dragging) { + if (active()) { if (target() && target()->parentItem()) { QPointF pos = target()->parentItem()->mapFromScene(m_startPos + delta); enforceAxisConstraints(&pos); @@ -96,17 +95,9 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) } } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { - m_dragging = true; setGrab(point, true); - emit draggingChanged(); } } break; - case QQuickEventPoint::Released: - if (m_dragging) { - m_dragging = false; - emit draggingChanged(); - } - break; default: break; } diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index e7cb675025..8f27d51b4f 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -90,7 +90,6 @@ class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickPointerSingleHandler Q_OBJECT Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT) - Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) public: QQuickDragHandler(QObject *parent = 0); @@ -101,13 +100,10 @@ public: QQuickDragAxis *xAxis() { return &m_xAxis; } QQuickDragAxis *yAxis() { return &m_yAxis; } - bool dragging() const { return m_dragging; } - Q_INVOKABLE void enforceConstraints(); Q_SIGNALS: // void gestureStarted(QQuickGestureEvent *gesture); - void draggingChanged(); protected: bool wantsEventPoint(QQuickEventPoint *point) override; @@ -117,7 +113,6 @@ private: void enforceAxisConstraints(QPointF *localPos); private: - bool m_dragging; QPointF m_startPos; QQuickDragAxis m_xAxis; QQuickDragAxis m_yAxis; diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 45d8235dff..4832d300c2 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -224,6 +224,10 @@ void QQuickPinchHandler::onActiveChanged() void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) { Q_UNUSED(event) + // TODO wait for the drag threshold before setting active + // but the old behavior was that whenever we "want" all the points, we're active + // so that behavior is retained here temporarily + setActive(m_currentPoints.count() > 0 && m_currentPoints.at(0)->state() != QQuickEventPoint::Released); if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) { for (QQuickEventPoint *point : qAsConst(m_currentPoints)) diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index d51835a24b..ed08eed93e 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -140,9 +140,7 @@ QQuickItem *QQuickPointerHandler::target() const void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) { - const bool wants = wantsPointerEvent(event); - setActive(wants); - if (wants) + if (wantsPointerEvent(event)) handlePointerEventImpl(event); else setActive(false); diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 5b2d6b0801..3a67d66e8c 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -101,6 +101,11 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even } else { setPressedButtons(event->buttons()); handleEventPoint(currentPoint); + if (currentPoint->state() == QQuickEventPoint::Released) { + m_currentPointId = 0; + setPressedButtons(Qt::NoButton); + setGrab(currentPoint, false); + } } bool grab = currentPoint->isAccepted() && currentPoint->state() != QQuickEventPoint::Released; setGrab(currentPoint, grab); @@ -120,6 +125,12 @@ void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) setPressedButtons(Qt::NoButton); } +void QQuickPointerSingleHandler::onGrabChanged(QQuickEventPoint *point) +{ + bool grabbing = (point->grabber() == this); + setActive(grabbing); +} + void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) { if (buttons != m_pressedButtons) { diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 69a3263271..e3c6bfaddf 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -77,6 +77,7 @@ protected: quint64 currentPointId() const { return m_currentPointId; } QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } void handleGrabCancel(QQuickEventPoint *point) override; + void onGrabChanged(QQuickEventPoint *point) override; private: void setPressedButtons(Qt::MouseButtons buttons); -- cgit v1.2.3 From de3896d2ee2a57aad83eea237ef1c39e3581190b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 29 Dec 2016 14:46:31 +0100 Subject: QQuickMultiPointerHandler::grabPoints cancels existing grabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a pointer handler wants to grab a point, it must always call QQuickPointerHandler::setGrab() because that's where handleGrabCancel() will be called, if necessary. QQuickEventPoint::setGrabberPointerHandler() is too low-level for direct use in handlers: it does not notify any existing grabber that it has lost its grab. Change-Id: I8dcbc029dc0fd8b3becbeda87965572eb274502f Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index c2060ca3c5..d3b986bcce 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -213,7 +213,7 @@ void QQuickMultiPointerHandler::acceptPoints(const QVector & void QQuickMultiPointerHandler::grabPoints(QVector points) { for (QQuickEventPoint* point : points) - point->setGrabberPointerHandler(this); + setGrab(point, true); } QT_END_NAMESPACE -- cgit v1.2.3 From 9aa0e84d974d86cf9366714107e9c84dcde15490 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 23 Dec 2016 15:35:43 +0100 Subject: Add a DragHandler::translation property This allows the application developer to get the translation of the dragged pointer, and apply it in a custom way. This should usually be combined with setting target to null. This will for instance be needed when we want to drag QtLocations map, where a map is dragged by specifying the geo location of the center of the map. The map2.qml example demonstrates this. Change-Id: I652d9fc92fa9b6dfd3796c7147832f25af0cc5bc Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler.cpp | 10 ++++++++++ src/quick/handlers/qquickdraghandler_p.h | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index dc3116e300..945d60119d 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -88,6 +88,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) if (!m_yAxis.enabled()) delta.setY(0); if (active()) { + setTranslation(delta); if (target() && target()->parentItem()) { QPointF pos = target()->parentItem()->mapFromScene(m_startPos + delta); enforceAxisConstraints(&pos); @@ -122,6 +123,15 @@ void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos) localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum())); } +void QQuickDragHandler::setTranslation(const QPointF &trans) +{ + if (trans == m_translation) // fuzzy compare? + return; + m_translation = trans; + emit translationChanged(); +} + + QQuickDragAxis::QQuickDragAxis() : m_minimum(-DBL_MAX) , m_maximum(DBL_MAX) diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 8f27d51b4f..69fa297b3c 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -90,6 +90,7 @@ class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickPointerSingleHandler Q_OBJECT Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT) Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT) + Q_PROPERTY(QPointF translation READ translation NOTIFY translationChanged) public: QQuickDragHandler(QObject *parent = 0); @@ -100,10 +101,14 @@ public: QQuickDragAxis *xAxis() { return &m_xAxis; } QQuickDragAxis *yAxis() { return &m_yAxis; } + QPointF translation() const { return m_translation; } + void setTranslation(const QPointF &trans); + Q_INVOKABLE void enforceConstraints(); Q_SIGNALS: // void gestureStarted(QQuickGestureEvent *gesture); + void translationChanged(); protected: bool wantsEventPoint(QQuickEventPoint *point) override; @@ -114,6 +119,7 @@ private: private: QPointF m_startPos; + QPointF m_translation; QQuickDragAxis m_xAxis; QQuickDragAxis m_yAxis; -- cgit v1.2.3 From 9da67896e7f84bba5db66b49cba659077c35c870 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 29 Dec 2016 15:20:40 +0100 Subject: add QQuickEventTouchPoint.ellipseDiameters property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 201f89f463ae82fe8e9239d7312907062e9a8d15 in qtbase added it to QTouchEvent::TouchPoint. Change-Id: I4e5e08afa0326fd44368777bbd8694d9ef120761 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 1 + src/quick/items/qquickevents_p_p.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 05998fc500..2b145044e8 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -641,6 +641,7 @@ void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong times QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity()); m_rotation = tp.rotation(); m_pressure = tp.pressure(); + m_ellipseDiameters = tp.ellipseDiameters(); m_uniqueId = tp.uniqueId(); } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5e85aa421e..7fc50ab682 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -331,6 +331,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint Q_OBJECT Q_PROPERTY(qreal rotation READ rotation) Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) public: @@ -340,11 +341,13 @@ public: qreal rotation() const { return m_rotation; } qreal pressure() const { return m_pressure; } + QSizeF ellipseDiameters() const { return m_ellipseDiameters; } QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } private: qreal m_rotation; qreal m_pressure; + QSizeF m_ellipseDiameters; QPointingDeviceUniqueId m_uniqueId; Q_DISABLE_COPY(QQuickEventTouchPoint) -- cgit v1.2.3 From c05f596d958349e67c3e3cb89b3bc6d35d2052ea Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 30 Dec 2016 10:04:51 +0100 Subject: QQuickPointerHandler::setGrab: don't emit grabChanged if it didn't MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When requested to grab, it was emitting this signal and calling onGrabChanged every time. Change-Id: I32ce80e4d6ff8b8e950b5d2f977bd737e31b8ac8 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index ed08eed93e..0398863ba3 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -75,14 +75,14 @@ QQuickPointerHandler::~QQuickPointerHandler() void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) { - if (grab) { - QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); - if (oldGrabber && oldGrabber != this) + QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); + if (grab && oldGrabber != this) { + if (oldGrabber) oldGrabber->handleGrabCancel(point); point->setGrabberPointerHandler(this); onGrabChanged(point); emit grabChanged(point); - } else if (point->grabberPointerHandler() == this) { + } else if (!grab && oldGrabber == this) { point->setGrabberPointerHandler(nullptr); onGrabChanged(point); emit grabChanged(point); -- cgit v1.2.3 From f1fa7f18b09cad465867ff036ea500d4e2eda868 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 30 Dec 2016 11:24:44 +0100 Subject: QQuickWindowPrivate::deliverMouseEvent: localize to grabber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we are delivering to an existing pointer handler grabber, it's guaranteed to have a parentItem, and the event needs to be localized to that item before delivery. Change-Id: I440d73a5daed7620283e79a61b956e47f9fce80d Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7aa6d1aa66..0c5f9fe024 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1646,8 +1646,9 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - if (point->grabberPointerHandler()) { - point->grabberPointerHandler()->handlePointerEvent(pointerEvent); + if (auto handler = point->grabberPointerHandler()) { + pointerEvent->localize(handler->parentItem()); + handler->handlePointerEvent(pointerEvent); return; } -- cgit v1.2.3 From 5c6245b7ff7a941c0a2425f32909c0dc0982e199 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 25 Nov 2016 15:38:42 +0100 Subject: QQPSingleHandler: copy some values from QQuickEventPoint to properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't copy the eventpoint and we can't continue to refer to it after delivery, either. So we can't have an event property. Some QML use cases depend on being able to access last-known values between events. Change-Id: Ice8a1763015f2554275d0cb76824fd0366eaef56 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 2 +- src/quick/handlers/qquickpointersinglehandler.cpp | 80 +++++++++++++++++------ src/quick/handlers/qquickpointersinglehandler_p.h | 39 ++++++++++- 3 files changed, 96 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 945d60119d..6caee6be13 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -69,7 +69,7 @@ QQuickDragHandler::~QQuickDragHandler() bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) { // If we've already been interested in a point, stay interested, even if it has strayed outside bounds. - return ((point->state() != QQuickEventPoint::Pressed && currentPointId() == point->pointId()) + return ((point->state() != QQuickEventPoint::Pressed && pointId() == point->pointId()) || QQuickPointerSingleHandler::wantsEventPoint(point)); } diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 3a67d66e8c..f39d8ac976 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -52,7 +52,9 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET) QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) : QQuickPointerDeviceHandler(parent) - , m_currentPointId(0) + , m_pointId(0) + , m_rotation(0) + , m_pressure(0) { } @@ -60,11 +62,11 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; - if (m_currentPointId) { + if (m_pointId) { // We already know which one we want, so check whether it's there. // It's expected to be an update or a release. // If we no longer want it, cancel the grab. - if (auto point = event->pointById(m_currentPointId)) { + if (auto point = event->pointById(m_pointId)) { if (wantsEventPoint(point)) { point->setAccepted(); return true; @@ -72,45 +74,62 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) point->cancelGrab(); } } else { - qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_currentPointId + qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_pointId << "is missing from current event, but was neither canceled nor released"; return false; } } else { // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true. int c = event->pointCount(); - for (int i = 0; i < c && !m_currentPointId; ++i) { + for (int i = 0; i < c && !m_pointId; ++i) { QQuickEventPoint *p = event->point(i); if (!p->grabber() && wantsEventPoint(p)) { - m_currentPointId = p->pointId(); + m_pointId = p->pointId(); p->setAccepted(); } } } - return m_currentPointId; + return m_pointId; } void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) { QQuickPointerDeviceHandler::handlePointerEventImpl(event); - QQuickEventPoint *currentPoint = event->pointById(m_currentPointId); + QQuickEventPoint *currentPoint = event->pointById(m_pointId); Q_ASSERT(currentPoint); - if (!m_currentPointId || !currentPoint->isAccepted()) { - m_currentPointId = 0; - setPressedButtons(Qt::NoButton); + bool grab = false; + if (!m_pointId || !currentPoint->isAccepted() || currentPoint->state() == QQuickEventPoint::Released) { + reset(); + grab = false; } else { - setPressedButtons(event->buttons()); + if (event->asPointerTouchEvent()) { + QQuickEventTouchPoint *tp = static_cast(currentPoint); + m_uniquePointId = tp->uniqueId(); + m_rotation = tp->rotation(); + m_pressure = tp->pressure(); + m_ellipseDiameters = tp->ellipseDiameters(); + } else if (event->asPointerTabletEvent()) { + // TODO + } else { + m_uniquePointId = event->device()->uniqueId(); + m_rotation = 0; + m_pressure = event->buttons() ? 1 : 0; + m_ellipseDiameters = QSizeF(); + } + m_pos = currentPoint->pos(); + m_velocity = currentPoint->velocity(); handleEventPoint(currentPoint); - if (currentPoint->state() == QQuickEventPoint::Released) { - m_currentPointId = 0; - setPressedButtons(Qt::NoButton); - setGrab(currentPoint, false); + if (currentPoint->state() == QQuickEventPoint::Pressed) { + m_pressPos = currentPoint->pos(); + emit pointIdChanged(); } + setPressedButtons(event->buttons()); + grab = true; } - bool grab = currentPoint->isAccepted() && currentPoint->state() != QQuickEventPoint::Released; + emit eventPointHandled(); + // TODO don't call setGrab(true) here, only setGrab(false), because concrete subclasses may + // wait for the drag threshold to be exceeded, for example setGrab(currentPoint, grab); - if (!grab) - m_currentPointId = 0; } bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) @@ -121,14 +140,16 @@ bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) { QQuickPointerHandler::handleGrabCancel(point); - m_currentPointId = 0; - setPressedButtons(Qt::NoButton); + reset(); } void QQuickPointerSingleHandler::onGrabChanged(QQuickEventPoint *point) { bool grabbing = (point->grabber() == this); setActive(grabbing); + if (grabbing) + m_sceneGrabPos = point->sceneGrabPos(); + emit singlePointGrabChanged(); } void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) @@ -139,4 +160,21 @@ void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) } } +void QQuickPointerSingleHandler::reset() +{ + bool pointIdChange = m_pointId != 0; + m_pointId = 0; + m_uniquePointId = QPointingDeviceUniqueId(); + m_pos = QPointF(); + m_pressPos = QPointF(); + m_sceneGrabPos = QPointF(); + m_velocity = QVector2D(); + m_rotation = 0; + m_pressure = 0; + m_ellipseDiameters = QSizeF(); + setPressedButtons(Qt::NoButton); + if (pointIdChange) + emit pointIdChanged(); +} + QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index e3c6bfaddf..8858b2c080 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -58,33 +58,66 @@ QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler { Q_OBJECT + Q_PROPERTY(quint64 pointId READ pointId NOTIFY pointIdChanged) + Q_PROPERTY(QPointingDeviceUniqueId uniquePointId READ uniquePointId NOTIFY pointIdChanged) + Q_PROPERTY(QPointF pos READ pos NOTIFY eventPointHandled) + Q_PROPERTY(QPointF scenePos READ scenePos NOTIFY eventPointHandled) + Q_PROPERTY(QPointF pressPos READ pressPos NOTIFY pressedButtonsChanged) + Q_PROPERTY(QPointF scenePressPos READ scenePressPos NOTIFY pressedButtonsChanged) + Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos NOTIFY singlePointGrabChanged) Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedButtonsChanged) + Q_PROPERTY(QVector2D velocity READ velocity NOTIFY eventPointHandled) + Q_PROPERTY(qreal rotation READ rotation NOTIFY eventPointHandled) + Q_PROPERTY(qreal pressure READ pressure NOTIFY eventPointHandled) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters NOTIFY eventPointHandled) public: QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } + QPointF pressPos() const { return m_pressPos; } + QPointF scenePressPos() const { return parentItem()->mapToScene(m_pressPos); } + QPointF sceneGrabPos() const { return m_sceneGrabPos; } + QPointF pos() const { return m_pos; } + QPointF scenePos() const { return parentItem()->mapToScene(m_pos); } + QVector2D velocity() const { return m_velocity; } + qreal rotation() const { return m_rotation; } + qreal pressure() const { return m_pressure; } + QSizeF ellipseDiameters() const { return m_ellipseDiameters; } + QPointingDeviceUniqueId uniquePointId() const { return m_uniquePointId; } signals: + void pointIdChanged(); void pressedButtonsChanged(); + void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here + void eventPointHandled(); protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; virtual bool wantsEventPoint(QQuickEventPoint *point); void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; - quint64 currentPointId() const { return m_currentPointId; } - QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_currentPointId); } + quint64 pointId() const { return m_pointId; } + QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } void handleGrabCancel(QQuickEventPoint *point) override; void onGrabChanged(QQuickEventPoint *point) override; private: void setPressedButtons(Qt::MouseButtons buttons); + void reset(); private: - quint64 m_currentPointId; + quint64 m_pointId; + QPointingDeviceUniqueId m_uniquePointId; Qt::MouseButtons m_pressedButtons; + QPointF m_pos; + QPointF m_pressPos; + QPointF m_sceneGrabPos; + QVector2D m_velocity; + qreal m_rotation; + qreal m_pressure; + QSizeF m_ellipseDiameters; }; QT_END_NAMESPACE -- cgit v1.2.3 From aa24b8938bb03e688633e544dddeca5aff91940e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 19 Dec 2016 13:54:58 +0100 Subject: Stencil clipping for NVPR Fix also the fill rule interpretation on NVPR - it was the opposite of what QPainter was doing. Change-Id: I23ff3b20e3b066d4b4e07aaa68b7da1e09d9127d Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer.cpp | 274 +++++++++++++++++++++---- src/quick/items/qquickpathitemnvprrenderer_p.h | 33 +++ 3 files changed, 270 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 720916c900..0a4c721a74 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -426,7 +426,7 @@ void QQuickPathItemPrivate::createRenderer() case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; + renderer = new QQuickPathItemNvprRenderer(q); } else { rendererType = QQuickPathItem::GeometryRenderer; renderer = new QQuickPathItemGenericRenderer(q); diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index e0c458bfca..bd88023891 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -39,6 +39,9 @@ #include "qquickpathitemnvprrenderer_p.h" #include +#include +#include +#include #include QT_BEGIN_NAMESPACE @@ -307,10 +310,10 @@ void QQuickPathItemNvprRenderer::updatePathRenderNode() if (m_dirty & DirtyFillRule) { switch (m_fillRule) { case QQuickPathItem::OddEvenFill: - m_node->m_fillRule = GL_COUNT_UP_NV; + m_node->m_fillRule = GL_INVERT; break; case QQuickPathItem::WindingFill: - m_node->m_fillRule = GL_INVERT; + m_node->m_fillRule = GL_COUNT_UP_NV; break; default: Q_UNREACHABLE(); @@ -360,6 +363,13 @@ void QQuickPathItemNvprRenderNode::releaseResources() nvpr.deletePaths(m_path, 1); m_path = 0; } + + if (m_fallbackFbo) { + delete m_fallbackFbo; + m_fallbackFbo = nullptr; + } + + m_fallbackBlitter.destroy(); } void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) @@ -465,8 +475,95 @@ void QQuickPathItemNvprRenderNode::updatePath() } } +void QQuickPathItemNvprRenderNode::renderStroke(int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(m_path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickPathItemNvprRenderNode::renderFill() +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (m_fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + } else { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + const int writeMask = 0xFF; + nvpr.stencilThenCoverFillPath(m_path, m_fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickPathItemNvprRenderNode::renderOffscreenFill() +{ + QQuickWindow *w = m_item->window(); + const qreal dpr = w->effectiveDevicePixelRatio(); + QSize itemSize = QSize(m_item->width(), m_item->height()) * dpr; + QSize rtSize = w->renderTargetSize(); + if (rtSize.isEmpty()) + rtSize = w->size() * dpr; + + if (m_fallbackFbo && m_fallbackFbo->size() != itemSize) { + delete m_fallbackFbo; + m_fallbackFbo = nullptr; + } + if (!m_fallbackFbo) + m_fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!m_fallbackFbo->bind()) + return; + + f->glViewport(0, 0, itemSize.width(), itemSize.height()); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + nvpr.matrixLoadIdentity(GL_PATH_MODELVIEW_NV); + QMatrix4x4 proj; + proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(); + + m_fallbackFbo->release(); + f->glViewport(0, 0, rtSize.width(), rtSize.height()); +} + +void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + void QQuickPathItemNvprRenderNode::render(const RenderState *state) { + f = QOpenGLContext::currentContext()->extraFunctions(); + if (!nvprInited) { if (!nvpr.create()) { qWarning("NVPR init failed"); @@ -478,22 +575,23 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) updatePath(); - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); f->glUseProgram(0); - QQuickNvprMaterialManager::MaterialDesc *mtl; - if (m_fillGradientActive) - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - else - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - if (!mtl) - return; - - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. f->glStencilMask(~0); f->glEnable(GL_STENCIL_TEST); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + + const bool hasFill = !qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive; + const bool hasStroke = m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w()); + + if (hasFill && stencilClip) { + // Fall back to a texture when complex clipping is in use and we have + // to fill. Reconciling glStencilFillPath's and the scenegraph's clip + // stencil semantics has not succeeded so far... + renderOffscreenFill(); + } // Depth test against the opaque batches rendered before. f->glEnable(GL_DEPTH_TEST); @@ -509,35 +607,43 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) f->glEnable(GL_SCISSOR_TEST); } - if (!qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive) { - if (m_fillGradientActive) { - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); - tx->bind(); - // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) - // where x and y are in path coordinate space, which is just what - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(); } else { - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + if (!m_fallbackBlitter.isCreated()) + m_fallbackBlitter.create(); + f->glStencilFunc(GL_EQUAL, sv, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + m_fallbackBlitter.texturedQuad(m_fallbackFbo->texture(), m_fallbackFbo->size(), + *state->projectionMatrix(), *matrix(), + inheritedOpacity()); } - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - nvpr.stencilThenCoverFillPath(m_path, m_fillRule, 0xFF, GL_BOUNDING_BOX_NV); } - if (m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w())) { - if (m_fillGradientActive) - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - nvpr.stencilThenCoverStrokePath(m_path, 0x1, ~0, GL_CONVEX_HULL_NV); + // Stroke! + if (hasStroke) { + const int strokeStencilValue = 0x80; + const int writeMask = 0x80; + + setupStencilForCover(stencilClip, sv); + if (stencilClip) { + // for the stencil step (eff. read mask == 0xFF & ~writeMask) + nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); + // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. + // This assumes the clip stencil value is <= 127. + if (sv >= strokeStencilValue) + qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(strokeStencilValue, writeMask); } + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + f->glBindProgramPipeline(0); m_dirty = 0; @@ -564,4 +670,96 @@ bool QQuickPathItemNvprRenderNode::isSupported() return !nvprDisabled && QQuickNvprFunctions::isSupported(); } +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 0075572324..7dd2d564c5 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -63,6 +63,9 @@ QT_BEGIN_NAMESPACE class QQuickPathItemNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer { @@ -77,6 +80,10 @@ public: DirtyAll = 0xFF }; + QQuickPathItemNvprRenderer(QQuickItem *item) + : m_item(item) + { } + void beginSync() override; void setPath(const QQuickPath *path) override; void setStrokeColor(const QColor &color) override; @@ -101,6 +108,7 @@ public: private: void convertPath(const QQuickPath *path); + QQuickItem *m_item; QQuickPathItemNvprRenderNode *m_node = nullptr; int m_dirty = 0; @@ -146,6 +154,24 @@ private: MaterialDesc m_materials[NMaterials]; }; +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + class QQuickPathItemNvprRenderNode : public QSGRenderNode { public: @@ -162,6 +188,10 @@ public: private: void updatePath(); + void renderStroke(int strokeStencilValue, int writeMask); + void renderFill(); + void renderOffscreenFill(); + void setupStencilForCover(bool stencilClip, int sv); static bool nvprInited; static QQuickNvprFunctions nvpr; @@ -183,6 +213,9 @@ private: QVector m_dashPattern; bool m_fillGradientActive; QQuickPathItemGradientCache::GradientDesc m_fillGradient; + QOpenGLFramebufferObject *m_fallbackFbo = nullptr; + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; friend class QQuickPathItemNvprRenderer; }; -- cgit v1.2.3 From 917a9382df8a5a673556a21b1b46e4343231d674 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:14:04 +0100 Subject: Fix negative stroke width for software PathItem Like the generic one, this also needs to deal with the fact that QPen does not take negative widths. Store the value separately. Change-Id: Ia332c2d83e98c03125a05c838cb8184346f8303a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 10 +++++++--- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index f089904fdf..c7b52e641f 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -61,7 +61,9 @@ void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color) void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w) { - m_pen.setWidthF(w); + m_strokeWidth = w; + if (w >= 0.0f) + m_pen.setWidthF(w); m_dirty |= DirtyPen; } @@ -169,8 +171,10 @@ void QQuickPathItemSoftwareRenderer::updatePathRenderNode() if (m_dirty & DirtyFillRule) m_node->m_path.setFillRule(m_fillRule); - if (m_dirty & DirtyPen) + if (m_dirty & DirtyPen) { m_node->m_pen = m_pen; + m_node->m_strokeWidth = m_strokeWidth; + } if (m_dirty & DirtyBrush) m_node->m_brush = m_brush; @@ -209,7 +213,7 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) p->setTransform(matrix()->toTransform()); p->setOpacity(inheritedOpacity()); - p->setPen(!qFuzzyIsNull(m_pen.widthF()) && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); + p->setPen(m_strokeWidth >= 0.0f && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); p->drawPath(m_path); diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 584771425d..6c7d052596 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -94,6 +94,7 @@ private: QPainterPath m_path; QPen m_pen; + float m_strokeWidth; QColor m_fillColor; QBrush m_brush; Qt::FillRule m_fillRule; @@ -117,6 +118,7 @@ private: QPainterPath m_path; QPen m_pen; + float m_strokeWidth; QBrush m_brush; friend class QQuickPathItemSoftwareRenderer; -- cgit v1.2.3 From 81d204e2a5118b2d81862fa9d9a45e5522396a45 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:31:23 +0100 Subject: Add X axis rotation property to PathArc It is a standard feature of elliptical arc. While perhaps deemed too advanced for PathView purposes, rendering the path using PathItem must offer the ability to specify a non-zero X axis rotation for the ellipses of which the arc is a section of. Change-Id: I53f01713b7e0e97c40f22d75d46f75a140830683 Reviewed-by: Andy Nichols --- src/quick/doc/images/declarative-arcrotation.png | Bin 0 -> 4315 bytes src/quick/doc/snippets/qml/path/arcrotation.qml | 52 +++++++++++++++++++++++ src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/util/qquickpath.cpp | 38 ++++++++++++++++- src/quick/util/qquickpath_p.h | 8 +++- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/quick/doc/images/declarative-arcrotation.png create mode 100644 src/quick/doc/snippets/qml/path/arcrotation.qml (limited to 'src') diff --git a/src/quick/doc/images/declarative-arcrotation.png b/src/quick/doc/images/declarative-arcrotation.png new file mode 100644 index 0000000000..03f009bc12 Binary files /dev/null and b/src/quick/doc/images/declarative-arcrotation.png differ diff --git a/src/quick/doc/snippets/qml/path/arcrotation.qml b/src/quick/doc/snippets/qml/path/arcrotation.qml new file mode 100644 index 0000000000..c73d67ff17 --- /dev/null +++ b/src/quick/doc/snippets/qml/path/arcrotation.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation 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$ +** +****************************************************************************/ + +import QtQuick 2.9 +//![0] +Path { + startX: 50; startY: 100 + + PathArc { + x: 150; y: 100 + radiusX: 50; radiusY: 20 + xAxisRotation: 45 + } +} +//![0] diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index b0d7f7f8a3..9c70daab1f 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -373,6 +373,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri, 2, 9, "MouseArea"); #if QT_CONFIG(quick_path) + qmlRegisterType(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); qmlRegisterType(uri, 2, 9, "PathItem"); qmlRegisterType(uri, 2, 9, "PathGradientStop"); diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index a6fa21d696..228056fdb5 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -1799,6 +1799,42 @@ void QQuickPathArc::setDirection(ArcDirection direction) emit changed(); } +/*! + \qmlproperty real QtQuick::PathArc::xAxisRotation + + Defines the rotation of the arc, in degrees. The default value is 0. + + An arc is a section of circles or ellipses. Given the radius and the start + and end points, there are two ellipses that connect the points. This + property defines the rotation of the X axis of these ellipses. + + \note The value is only useful when the x and y radius differ, meaning the + arc is a section of ellipses. + + The following QML demonstrates how different radius values can be used to change + the shape of the arc: + \table + \row + \li \image declarative-arcrotation.png + \li \snippet qml/path/arcrotation.qml 0 + \endtable +*/ + +qreal QQuickPathArc::xAxisRotation() const +{ + return _xAxisRotation; +} + +void QQuickPathArc::setXAxisRotation(qreal rotation) +{ + if (_xAxisRotation == rotation) + return; + + _xAxisRotation = rotation; + emit xAxisRotationChanged(); + emit changed(); +} + void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) { const QPointF &startPoint = path.currentPosition(); @@ -1806,7 +1842,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) QQuickSvgParser::pathArc(path, _radiusX, _radiusY, - 0, //xAxisRotation + _xAxisRotation, _useLargeArc, _direction == Clockwise ? 1 : 0, endPoint.x(), diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 283283f377..4bcefb032e 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -290,10 +290,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged) Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged) Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2) public: QQuickPathArc(QObject *parent=0) - : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {} + : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise), _xAxisRotation(0) {} enum ArcDirection { Clockwise, Counterclockwise }; Q_ENUM(ArcDirection) @@ -310,6 +311,9 @@ public: ArcDirection direction() const; void setDirection(ArcDirection direction); + qreal xAxisRotation() const; + void setXAxisRotation(qreal rotation); + void addToPath(QPainterPath &path, const QQuickPathData &) override; Q_SIGNALS: @@ -317,12 +321,14 @@ Q_SIGNALS: void radiusYChanged(); void useLargeArcChanged(); void directionChanged(); + Q_REVISION(2) void xAxisRotationChanged(); private: qreal _radiusX; qreal _radiusY; bool _useLargeArc; ArcDirection _direction; + qreal _xAxisRotation; }; class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve -- cgit v1.2.3 From 7599fbffbf8ad88fa4487cd7d8bad90eb0b2d952 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 14:34:51 +0100 Subject: NVPR: Take X axis rotation into account for PathArc Change-Id: I70238e0e4b458ddfde9f32c40b76382c1a183e98 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index bd88023891..5641d33f85 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -242,7 +242,7 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) m_path.cmd.append(cmd); m_path.coord.append(o->radiusX()); m_path.coord.append(o->radiusY()); - m_path.coord.append(0.0f); // X axis rotation + m_path.coord.append(o->xAxisRotation()); appendCoords(&m_path.coord, o, &pos); } else { qWarning() << "PathItem/NVPR: unsupported Path element" << e; -- cgit v1.2.3 From 0271da9ff4001d26596a9172329691674e147ada Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 4 Jan 2017 15:12:32 +0100 Subject: Allow multiple paths in a PathItem Instead of PathItem { item properties stroke/fill properties path: Path { ... } } switch to PathItem { item properties VisualPath { stroke/fill settings Path { ... } } VisualPath { stroke/fill settings Path { ... } } ... } Limiting PathItem to a single path is arguably too limited. Applications will likely try to work this around by using multiple PathItems. While this is not particularly bad for the generic (geometry node based) implementation, it is a massive overkill for the rendernode-based ones. Therefore, avoid the hassle and allow multiple paths with different stroke/fill parameters inside a single PathItem. Change-Id: Ie7980cd656deb7d4cb1ee4eaa3c090c4b0493c7d Reviewed-by: Andy Nichols --- src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/items/qquickpathitem.cpp | 460 +++++++++++------- src/quick/items/qquickpathitem_p.h | 70 ++- src/quick/items/qquickpathitem_p_p.h | 68 ++- src/quick/items/qquickpathitemgenericrenderer.cpp | 358 +++++++------- src/quick/items/qquickpathitemgenericrenderer_p.h | 105 ++--- src/quick/items/qquickpathitemnvprrenderer.cpp | 519 ++++++++++++--------- src/quick/items/qquickpathitemnvprrenderer_p.h | 108 +++-- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 162 ++++--- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 57 +-- src/quick/util/qquickpath.cpp | 73 +++ 11 files changed, 1158 insertions(+), 823 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 9c70daab1f..e9439275ac 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -376,6 +376,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); qmlRegisterType(uri, 2, 9, "PathItem"); + qmlRegisterType(uri, 2, 9, "VisualPath"); qmlRegisterType(uri, 2, 9, "PathGradientStop"); qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); qmlRegisterType(uri, 2, 9, "PathLinearGradient"); diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 0a4c721a74..0dce376945 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -48,333 +48,425 @@ QT_BEGIN_NAMESPACE -QQuickPathItemPrivate::QQuickPathItemPrivate() - : rendererType(QQuickPathItem::UnknownRenderer), - renderer(nullptr), - path(nullptr), +QQuickVisualPathPrivate::QQuickVisualPathPrivate() + : path(nullptr), dirty(DirtyAll), strokeColor(Qt::white), strokeWidth(1), fillColor(Qt::white), - fillRule(QQuickPathItem::OddEvenFill), - joinStyle(QQuickPathItem::BevelJoin), + fillRule(QQuickVisualPath::OddEvenFill), + joinStyle(QQuickVisualPath::BevelJoin), miterLimit(2), - capStyle(QQuickPathItem::SquareCap), - strokeStyle(QQuickPathItem::SolidLine), + capStyle(QQuickVisualPath::SquareCap), + strokeStyle(QQuickVisualPath::SolidLine), dashOffset(0), fillGradient(nullptr) { dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space } -QQuickPathItemPrivate::~QQuickPathItemPrivate() +QQuickVisualPath::QQuickVisualPath(QObject *parent) + : QObject(*(new QQuickVisualPathPrivate), parent) { - delete renderer; } -/*! - \qmltype PathItem - \instantiates QQuickPathItem - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Item - \brief Renders a path - - Renders a path either by generating geometry via QPainterPath and manual - triangulation or by using an extension like \c{GL_NV_path_rendering}. - - This approach is different from rendering shapes via QQuickPaintedItem or - the 2D Canvas because the path never gets rasterized in software. Therefore - it is suitable for creating shapes spreading over larger areas of the - screen, avoiding the performance penalty for texture uploads or framebuffer - blits. - - Nonetheless it is important to be aware of performance implications, in - particular when the application is running on the generic PathItem - implementation due to not having support for accelerated path rendering. - The geometry generation happens entirely on the CPU in this case, and this - is potentially expensive. Changing the set of path elements, changing the - properties of these elements, or changing certain properties of the - PathItem itself all lead to retriangulation on every change. Therefore, - applying animation to such properties can heavily affect performance on - less powerful systems. If animating properties other than stroke and fill - colors is a must, it is recommended to target systems providing - \c{GL_NV_path_rendering} where the cost of path property changes is much - smaller. - - \note The types for specifying path elements are shared between PathView - and PathItem. However, not all PathItem implementations support all path - element types, while some may not make sense for PathView. PathItem's - currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, - PathArc. - - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc -*/ - -QQuickPathItem::QQuickPathItem(QQuickItem *parent) - : QQuickItem(*(new QQuickPathItemPrivate), parent) +QQuickVisualPath::~QQuickVisualPath() { - setFlag(ItemHasContents); } -QQuickPathItem::~QQuickPathItem() +QQuickPath *QQuickVisualPath::path() const { -} - -QQuickPathItem::RendererType QQuickPathItem::rendererType() const -{ - Q_D(const QQuickPathItem); - return d->rendererType; -} - -/*! - \qmlproperty Path QtQuick::PathItem::path - This property holds the path to be rendered. - For more information see the \l Path documentation. -*/ -QQuickPath *QQuickPathItem::path() const -{ - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->path; } -void QQuickPathItem::setPath(QQuickPath *path) +void QQuickVisualPath::setPath(QQuickPath *path) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->path == path) return; if (d->path) qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickPathItem, SLOT(_q_pathChanged())); + this, QQuickVisualPath, SLOT(_q_pathChanged())); d->path = path; qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickPathItem, SLOT(_q_pathChanged())); + this, QQuickVisualPath, SLOT(_q_pathChanged())); - d->dirty |= QQuickPathItemPrivate::DirtyPath; + d->dirty |= QQuickVisualPathPrivate::DirtyPath; emit pathChanged(); - polish(); + emit changed(); } -void QQuickPathItemPrivate::_q_pathChanged() +void QQuickVisualPathPrivate::_q_pathChanged() { - Q_Q(QQuickPathItem); + Q_Q(QQuickVisualPath); dirty |= DirtyPath; - q->polish(); + emit q->changed(); } -QColor QQuickPathItem::strokeColor() const +QColor QQuickVisualPath::strokeColor() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->strokeColor; } -void QQuickPathItem::setStrokeColor(const QColor &color) +void QQuickVisualPath::setStrokeColor(const QColor &color) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->strokeColor != color) { d->strokeColor = color; - d->dirty |= QQuickPathItemPrivate::DirtyStrokeColor; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; emit strokeColorChanged(); - polish(); + emit changed(); } } -qreal QQuickPathItem::strokeWidth() const +qreal QQuickVisualPath::strokeWidth() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->strokeWidth; } -void QQuickPathItem::setStrokeWidth(qreal w) +void QQuickVisualPath::setStrokeWidth(qreal w) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->strokeWidth != w) { d->strokeWidth = w; - d->dirty |= QQuickPathItemPrivate::DirtyStrokeWidth; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; emit strokeWidthChanged(); - polish(); + emit changed(); } } -QColor QQuickPathItem::fillColor() const +QColor QQuickVisualPath::fillColor() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->fillColor; } -void QQuickPathItem::setFillColor(const QColor &color) +void QQuickVisualPath::setFillColor(const QColor &color) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->fillColor != color) { d->fillColor = color; - d->dirty |= QQuickPathItemPrivate::DirtyFillColor; + d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; emit fillColorChanged(); - polish(); + emit changed(); } } -QQuickPathItem::FillRule QQuickPathItem::fillRule() const +QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->fillRule; } -void QQuickPathItem::setFillRule(FillRule fillRule) +void QQuickVisualPath::setFillRule(FillRule fillRule) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->fillRule != fillRule) { d->fillRule = fillRule; - d->dirty |= QQuickPathItemPrivate::DirtyFillRule; + d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; emit fillRuleChanged(); - polish(); + emit changed(); } } -QQuickPathItem::JoinStyle QQuickPathItem::joinStyle() const +QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->joinStyle; } -void QQuickPathItem::setJoinStyle(JoinStyle style) +void QQuickVisualPath::setJoinStyle(JoinStyle style) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->joinStyle != style) { d->joinStyle = style; - d->dirty |= QQuickPathItemPrivate::DirtyStyle; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit joinStyleChanged(); - polish(); + emit changed(); } } -int QQuickPathItem::miterLimit() const +int QQuickVisualPath::miterLimit() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->miterLimit; } -void QQuickPathItem::setMiterLimit(int limit) +void QQuickVisualPath::setMiterLimit(int limit) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->miterLimit != limit) { d->miterLimit = limit; - d->dirty |= QQuickPathItemPrivate::DirtyStyle; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit miterLimitChanged(); - polish(); + emit changed(); } } -QQuickPathItem::CapStyle QQuickPathItem::capStyle() const +QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->capStyle; } -void QQuickPathItem::setCapStyle(CapStyle style) +void QQuickVisualPath::setCapStyle(CapStyle style) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->capStyle != style) { d->capStyle = style; - d->dirty |= QQuickPathItemPrivate::DirtyStyle; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit capStyleChanged(); - polish(); + emit changed(); } } -QQuickPathItem::StrokeStyle QQuickPathItem::strokeStyle() const +QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->strokeStyle; } -void QQuickPathItem::setStrokeStyle(StrokeStyle style) +void QQuickVisualPath::setStrokeStyle(StrokeStyle style) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->strokeStyle != style) { d->strokeStyle = style; - d->dirty |= QQuickPathItemPrivate::DirtyDash; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit strokeStyleChanged(); - polish(); + emit changed(); } } -qreal QQuickPathItem::dashOffset() const +qreal QQuickVisualPath::dashOffset() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->dashOffset; } -void QQuickPathItem::setDashOffset(qreal offset) +void QQuickVisualPath::setDashOffset(qreal offset) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->dashOffset != offset) { d->dashOffset = offset; - d->dirty |= QQuickPathItemPrivate::DirtyDash; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashOffsetChanged(); - polish(); + emit changed(); } } -QVector QQuickPathItem::dashPattern() const +QVector QQuickVisualPath::dashPattern() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->dashPattern; } -void QQuickPathItem::setDashPattern(const QVector &array) +void QQuickVisualPath::setDashPattern(const QVector &array) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->dashPattern != array) { d->dashPattern = array; - d->dirty |= QQuickPathItemPrivate::DirtyDash; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashPatternChanged(); - polish(); + emit changed(); } } -QQuickPathGradient *QQuickPathItem::fillGradient() const +QQuickPathGradient *QQuickVisualPath::fillGradient() const { - Q_D(const QQuickPathItem); + Q_D(const QQuickVisualPath); return d->fillGradient; } -void QQuickPathItem::setFillGradient(QQuickPathGradient *gradient) +void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) { - Q_D(QQuickPathItem); + Q_D(QQuickVisualPath); if (d->fillGradient != gradient) { if (d->fillGradient) qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickPathItem, SLOT(_q_fillGradientChanged())); + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); d->fillGradient = gradient; if (d->fillGradient) qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickPathItem, SLOT(_q_fillGradientChanged())); - d->dirty |= QQuickPathItemPrivate::DirtyFillGradient; - polish(); + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; + emit changed(); } } -void QQuickPathItemPrivate::_q_fillGradientChanged() +void QQuickVisualPathPrivate::_q_fillGradientChanged() { - Q_Q(QQuickPathItem); + Q_Q(QQuickVisualPath); dirty |= DirtyFillGradient; - q->polish(); + emit q->changed(); } -void QQuickPathItem::resetFillGradient() +void QQuickVisualPath::resetFillGradient() { setFillGradient(nullptr); } +/*! + \qmltype PathItem + \instantiates QQuickPathItem + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path + \since 5.10 + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using an extension like \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. Therefore + it is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. + + Nonetheless it is important to be aware of performance implications, in + particular when the application is running on the generic PathItem + implementation due to not having support for accelerated path rendering. + The geometry generation happens entirely on the CPU in this case, and this + is potentially expensive. Changing the set of path elements, changing the + properties of these elements, or changing certain properties of the + PathItem itself all lead to retriangulation on every change. Therefore, + applying animation to such properties can heavily affect performance on + less powerful systems. If animating properties other than stroke and fill + colors is a must, it is recommended to target systems providing + \c{GL_NV_path_rendering} where the cost of path property changes is much + smaller. + + \note The types for specifying path elements are shared between \l PathView + and PathItem. However, not all PathItem implementations support all path + element types, while some may not make sense for PathView. PathItem's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc. + + \note Limited support for PathSvg is also provided in most cases. However, + there is no guarantee that this element is going to be supported for all + future PathItem backends. It is recommended to avoid the PathSvg element in + practice. + + See \l Path for a detailed overview of the supported path elements. + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickPathItemPrivate::QQuickPathItemPrivate() + : componentComplete(true), + vpChanged(false), + rendererType(QQuickPathItem::UnknownRenderer), + renderer(nullptr) +{ +} + +QQuickPathItemPrivate::~QQuickPathItemPrivate() +{ + delete renderer; +} + +void QQuickPathItemPrivate::_q_visualPathChanged() +{ + Q_Q(QQuickPathItem); + vpChanged = true; + q->polish(); +} + +QQuickPathItem::QQuickPathItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPathItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickPathItem::~QQuickPathItem() +{ +} + +QQuickPathItem::RendererType QQuickPathItem::rendererType() const +{ + Q_D(const QQuickPathItem); + return d->rendererType; +} + +static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->vp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + d->vp.append(obj); + + if (d->componentComplete) { + QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + d->_q_visualPathChanged(); + } +} + +static int vpe_count(QQmlListProperty *property) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->vp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + + for (QQuickVisualPath *p : d->vp) + QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + + d->vp.clear(); + + if (d->componentComplete) + d->_q_visualPathChanged(); +} + +QQmlListProperty QQuickPathItem::visualPaths() +{ + return QQmlListProperty(this, + nullptr, + vpe_append, + vpe_count, + vpe_at, + vpe_clear); +} + +void QQuickPathItem::classBegin() +{ + Q_D(QQuickPathItem); + d->componentComplete = false; +} + +void QQuickPathItem::componentComplete() +{ + Q_D(QQuickPathItem); + d->componentComplete = true; + + for (QQuickVisualPath *p : d->vp) + connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); + + d->_q_visualPathChanged(); +} + void QQuickPathItem::updatePolish() { Q_D(QQuickPathItem); - if (!d->dirty) + if (!d->vpChanged) return; + d->vpChanged = false; + if (!d->renderer) { d->createRenderer(); if (!d->renderer) @@ -392,9 +484,11 @@ void QQuickPathItem::updatePolish() void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) { + Q_D(QQuickPathItem); + // sync may have been deferred; do it now if the item became visible if (change == ItemVisibleHasChanged && data.boolValue) - polish(); + d->_q_visualPathChanged(); QQuickItem::itemChange(change, data); } @@ -407,8 +501,8 @@ QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) Q_D(QQuickPathItem); if (d->renderer) { if (!node) - node = d->createRenderNode(); - d->renderer->updatePathRenderNode(); + node = d->createNode(); + d->renderer->updateNode(); } return node; } @@ -444,7 +538,7 @@ void QQuickPathItemPrivate::createRenderer() } // the node lives on the render thread -QSGNode *QQuickPathItemPrivate::createRenderNode() +QSGNode *QQuickPathItemPrivate::createNode() { Q_Q(QQuickPathItem); QSGNode *node = nullptr; @@ -454,9 +548,6 @@ QSGNode *QQuickPathItemPrivate::createRenderNode() if (!ri) return node; - const bool hasFill = fillColor != Qt::transparent; - const bool hasStroke = strokeWidth >= 0.0f && strokeColor != Qt::transparent; - switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: @@ -465,9 +556,9 @@ QSGNode *QQuickPathItemPrivate::createRenderNode() static_cast(renderer)->setNode( static_cast(node)); } else { - node = new QQuickPathItemGenericRootRenderNode(q->window(), hasFill, hasStroke); + node = new QQuickPathItemGenericNode; static_cast(renderer)->setRootNode( - static_cast(node)); + static_cast(node)); } break; #endif @@ -486,29 +577,36 @@ QSGNode *QQuickPathItemPrivate::createRenderNode() void QQuickPathItemPrivate::sync() { - renderer->beginSync(); - - if (dirty & QQuickPathItemPrivate::DirtyPath) - renderer->setPath(path); - if (dirty & DirtyStrokeColor) - renderer->setStrokeColor(strokeColor); - if (dirty & DirtyStrokeWidth) - renderer->setStrokeWidth(strokeWidth); - if (dirty & DirtyFillColor) - renderer->setFillColor(fillColor); - if (dirty & DirtyFillRule) - renderer->setFillRule(fillRule); - if (dirty & DirtyStyle) { - renderer->setJoinStyle(joinStyle, miterLimit); - renderer->setCapStyle(capStyle); + const int count = vp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickVisualPath *p = vp[i]; + int &dirty(QQuickVisualPathPrivate::get(p)->dirty); + + if (dirty & QQuickVisualPathPrivate::DirtyPath) + renderer->setPath(i, p->path()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickVisualPathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickVisualPathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickVisualPathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickVisualPathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; } - if (dirty & DirtyDash) - renderer->setStrokeStyle(strokeStyle, dashOffset, dashPattern); - if (dirty & DirtyFillGradient) - renderer->setFillGradient(fillGradient); renderer->endSync(); - dirty = 0; } // ***** gradient support ***** diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 39b407cf87..0c1d0f061b 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -61,6 +61,7 @@ QT_REQUIRE_CONFIG(quick_path); QT_BEGIN_NAMESPACE +class QQuickVisualPathPrivate; class QQuickPathItemPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject @@ -150,13 +151,12 @@ private: QPointF m_end; }; -class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem +class Q_QUICK_PRIVATE_EXPORT QQuickVisualPath : public QObject { Q_OBJECT - Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) - Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) + Q_CLASSINFO("DefaultProperty", "path") Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) @@ -197,18 +197,8 @@ public: }; Q_ENUM(StrokeStyle) - enum RendererType { - UnknownRenderer, - GeometryRenderer, - NvprRenderer, - SoftwareRenderer - }; - Q_ENUM(RendererType) - - QQuickPathItem(QQuickItem *parent = nullptr); - ~QQuickPathItem(); - - RendererType rendererType() const; + QQuickVisualPath(QObject *parent = nullptr); + ~QQuickVisualPath(); QQuickPath *path() const; void setPath(QQuickPath *path); @@ -247,13 +237,8 @@ public: void setFillGradient(QQuickPathGradient *gradient); void resetFillGradient(); -protected: - QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; - void updatePolish() override; - void itemChange(ItemChange change, const ItemChangeData &data) override; - Q_SIGNALS: - void rendererChanged(); + void changed(); void pathChanged(); void strokeColorChanged(); void strokeWidthChanged(); @@ -268,12 +253,51 @@ Q_SIGNALS: void fillGradientChanged(); private: - Q_DISABLE_COPY(QQuickPathItem) - Q_DECLARE_PRIVATE(QQuickPathItem) + Q_DISABLE_COPY(QQuickVisualPath) + Q_DECLARE_PRIVATE(QQuickVisualPath) Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) }; +class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) + Q_CLASSINFO("DefaultProperty", "visualPaths") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + QQuickPathItem(QQuickItem *parent = nullptr); + ~QQuickPathItem(); + + RendererType rendererType() const; + + QQmlListProperty visualPaths(); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + void componentComplete() override; + void classBegin() override; + +Q_SIGNALS: + void rendererChanged(); + +private: + Q_DISABLE_COPY(QQuickPathItem) + Q_DECLARE_PRIVATE(QQuickPathItem) + Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) +}; + QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickPathItem) diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 366628d867..5e6400edc6 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -68,26 +68,26 @@ public: virtual ~QQuickAbstractPathRenderer() { } // Gui thread - virtual void beginSync() = 0; - virtual void setPath(const QQuickPath *path) = 0; - virtual void setStrokeColor(const QColor &color) = 0; - virtual void setStrokeWidth(qreal w) = 0; - virtual void setFillColor(const QColor &color) = 0; - virtual void setFillRule(QQuickPathItem::FillRule fillRule) = 0; - virtual void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) = 0; - virtual void setCapStyle(QQuickPathItem::CapStyle capStyle) = 0; - virtual void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + virtual void beginSync(int totalCount) = 0; + virtual void setPath(int index, const QQuickPath *path) = 0; + virtual void setStrokeColor(int index, const QColor &color) = 0; + virtual void setStrokeWidth(int index, qreal w) = 0; + virtual void setFillColor(int index, const QColor &color) = 0; + virtual void setFillRule(int index, QQuickVisualPath::FillRule fillRule) = 0; + virtual void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) = 0; + virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) = 0; - virtual void setFillGradient(QQuickPathGradient *gradient) = 0; + virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; virtual void endSync() = 0; // Render thread, with gui blocked - virtual void updatePathRenderNode() = 0; + virtual void updateNode() = 0; }; -class QQuickPathItemPrivate : public QQuickItemPrivate +class QQuickVisualPathPrivate : public QObjectPrivate { - Q_DECLARE_PUBLIC(QQuickPathItem) + Q_DECLARE_PUBLIC(QQuickVisualPath) public: enum Dirty { @@ -103,33 +103,51 @@ public: DirtyAll = 0xFF }; - QQuickPathItemPrivate(); - ~QQuickPathItemPrivate(); - - void createRenderer(); - QSGNode *createRenderNode(); - void sync(); + QQuickVisualPathPrivate(); void _q_pathChanged(); void _q_fillGradientChanged(); - QQuickPathItem::RendererType rendererType; - QQuickAbstractPathRenderer *renderer; + static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } + QQuickPath *path; int dirty; QColor strokeColor; qreal strokeWidth; QColor fillColor; - QQuickPathItem::FillRule fillRule; - QQuickPathItem::JoinStyle joinStyle; + QQuickVisualPath::FillRule fillRule; + QQuickVisualPath::JoinStyle joinStyle; int miterLimit; - QQuickPathItem::CapStyle capStyle; - QQuickPathItem::StrokeStyle strokeStyle; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::StrokeStyle strokeStyle; qreal dashOffset; QVector dashPattern; QQuickPathGradient *fillGradient; }; +class QQuickPathItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathItem) + +public: + QQuickPathItemPrivate(); + ~QQuickPathItemPrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_visualPathChanged(); + + static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } + + bool componentComplete; + bool vpChanged; + QQuickPathItem::RendererType rendererType; + QQuickAbstractPathRenderer *renderer; + QVector vp; +}; + #ifndef QT_NO_OPENGL class QQuickPathItemGradientCache : public QOpenGLSharedResource diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 4b15daef9a..b42ff1d4b0 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -66,42 +66,16 @@ static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QCol return color; } -QQuickPathItemGenericRootRenderNode::QQuickPathItemGenericRootRenderNode(QQuickWindow *window, - bool hasFill, - bool hasStroke) - : m_fillNode(nullptr), - m_strokeNode(nullptr) -{ - if (hasFill) { - m_fillNode = new QQuickPathItemGenericRenderNode(window, this); - appendChildNode(m_fillNode); - } - if (hasStroke) { - m_strokeNode = new QQuickPathItemGenericRenderNode(window, this); - appendChildNode(m_strokeNode); - } -} - -QQuickPathItemGenericRootRenderNode::~QQuickPathItemGenericRootRenderNode() -{ -} - -QQuickPathItemGenericRenderNode::QQuickPathItemGenericRenderNode(QQuickWindow *window, - QQuickPathItemGenericRootRenderNode *rootNode) +QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0), m_window(window), - m_rootNode(rootNode), m_material(nullptr) { setGeometry(&m_geometry); activateMaterial(MatSolidColor); } -QQuickPathItemGenericRenderNode::~QQuickPathItemGenericRenderNode() -{ -} - -void QQuickPathItemGenericRenderNode::activateMaterial(Material m) +void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) { switch (m) { case MatSolidColor: @@ -126,279 +100,335 @@ void QQuickPathItemGenericRenderNode::activateMaterial(Material m) } // sync, and so triangulation too, happens on the gui thread -void QQuickPathItemGenericRenderer::beginSync() +void QQuickPathItemGenericRenderer::beginSync(int totalCount) { - m_syncDirty = 0; + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (VisualPathData &d : m_vp) + d.syncDirty = 0; } -void QQuickPathItemGenericRenderer::setPath(const QQuickPath *path) +void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) { - m_path = path ? path->path() : QPainterPath(); - m_syncDirty |= DirtyGeom; + VisualPathData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setStrokeColor(const QColor &color) +void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) { - m_strokeColor = colorToColor4ub(color); - m_syncDirty |= DirtyColor; + VisualPathData &d(m_vp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; } -void QQuickPathItemGenericRenderer::setStrokeWidth(qreal w) +void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) { - m_strokeWidth = w; + VisualPathData &d(m_vp[index]); + d.strokeWidth = w; if (w >= 0.0f) - m_pen.setWidthF(w); - m_syncDirty |= DirtyGeom; + d.pen.setWidthF(w); + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setFillColor(const QColor &color) +void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) { - m_fillColor = colorToColor4ub(color); - m_syncDirty |= DirtyColor; + VisualPathData &d(m_vp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; } -void QQuickPathItemGenericRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) { - m_fillRule = Qt::FillRule(fillRule); - m_syncDirty |= DirtyGeom; + VisualPathData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) { - m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - m_pen.setMiterLimit(miterLimit); - m_syncDirty |= DirtyGeom; + VisualPathData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) { - m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); - m_syncDirty |= DirtyGeom; + VisualPathData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, +void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) { - m_pen.setStyle(Qt::PenStyle(strokeStyle)); - if (strokeStyle == QQuickPathItem::DashLine) { - m_pen.setDashPattern(dashPattern); - m_pen.setDashOffset(dashOffset); + VisualPathData &d(m_vp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickVisualPath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); } - m_syncDirty |= DirtyGeom; + d.syncDirty |= DirtyGeom; } -void QQuickPathItemGenericRenderer::setFillGradient(QQuickPathGradient *gradient) +void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) { - m_fillGradientActive = gradient != nullptr; + VisualPathData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; if (gradient) { - m_fillGradient.stops = gradient->sortedGradientStops(); - m_fillGradient.spread = gradient->spread(); + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - m_fillGradient.start = QPointF(g->x1(), g->y1()); - m_fillGradient.end = QPointF(g->x2(), g->y2()); + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); } else { Q_UNREACHABLE(); } } - m_syncDirty |= DirtyFillGradient; + d.syncDirty |= DirtyFillGradient; } void QQuickPathItemGenericRenderer::endSync() { - if (!m_syncDirty) - return; + for (VisualPathData &d : m_vp) { + if (!d.syncDirty) + continue; + + m_accDirty |= d.syncDirty; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to updateNode() + // on the render thread (with the gui thread blocked). For our purposes + // here syncDirty is still required since geometry regeneration must only + // happen when there was an actual change in this particular sync round. + d.effectiveDirty |= d.syncDirty; + + if (d.path.isEmpty()) { + d.fillVertices.clear(); + d.fillIndices.clear(); + d.strokeVertices.clear(); + continue; + } - // Use a shadow dirty flag in order to avoid losing state in case there are - // multiple syncs with different dirty flags before we get to - // updatePathRenderNode() on the render thread (with the gui thread - // blocked). For our purposes here m_syncDirty is still required since - // geometry regeneration must only happen when there was an actual change - // in this particular sync round. - m_effectiveDirty |= m_syncDirty; - - if (m_path.isEmpty()) { - m_fillVertices.clear(); - m_fillIndices.clear(); - m_strokeVertices.clear(); - return; + if (d.syncDirty & DirtyGeom) { + if (d.fillColor.a) + triangulateFill(&d); + if (d.strokeWidth >= 0.0f && d.strokeColor.a) + triangulateStroke(&d); + } } - - triangulateFill(); - triangulateStroke(); } -void QQuickPathItemGenericRenderer::triangulateFill() +void QQuickPathItemGenericRenderer::triangulateFill(VisualPathData *d) { - m_path.setFillRule(m_fillRule); + d->path.setFillRule(d->fillRule); - const QVectorPath &vp = qtVectorPathForPath(m_path); + const QVectorPath &vp = qtVectorPathForPath(d->path); QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - m_fillVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(m_fillVertices.data()); + d->fillVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(d->fillVertices.data()); const qreal *vsrc = ts.vertices.constData(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, m_fillColor); + vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, d->fillColor); - m_fillIndices.resize(ts.indices.size()); - quint16 *idst = m_fillIndices.data(); + d->fillIndices.resize(ts.indices.size()); + quint16 *idst = d->fillIndices.data(); if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - memcpy(idst, ts.indices.data(), m_fillIndices.count() * sizeof(quint16)); + memcpy(idst, ts.indices.data(), d->fillIndices.count() * sizeof(quint16)); } else { const quint32 *isrc = (const quint32 *) ts.indices.data(); - for (int i = 0; i < m_fillIndices.count(); ++i) + for (int i = 0; i < d->fillIndices.count(); ++i) idst[i] = isrc[i]; } } -void QQuickPathItemGenericRenderer::triangulateStroke() +void QQuickPathItemGenericRenderer::triangulateStroke(VisualPathData *d) { - const QVectorPath &vp = qtVectorPathForPath(m_path); + const QVectorPath &vp = qtVectorPathForPath(d->path); const QRectF clip(0, 0, m_item->width(), m_item->height()); const qreal inverseScale = 1.0 / SCALE; m_stroker.setInvScale(inverseScale); - if (m_pen.style() == Qt::SolidLine) { - m_stroker.process(vp, m_pen, clip, 0); + if (d->pen.style() == Qt::SolidLine) { + m_stroker.process(vp, d->pen, clip, 0); } else { m_dashStroker.setInvScale(inverseScale); - m_dashStroker.process(vp, m_pen, clip, 0); + m_dashStroker.process(vp, d->pen, clip, 0); QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(), m_dashStroker.elementTypes(), 0); - m_stroker.process(dashStroke, m_pen, clip, 0); + m_stroker.process(dashStroke, d->pen, clip, 0); } if (!m_stroker.vertexCount()) { - m_strokeVertices.clear(); + d->strokeVertices.clear(); return; } const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - m_strokeVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(m_strokeVertices.data()); + d->strokeVertices.resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(d->strokeVertices.data()); const float *vsrc = m_stroker.vertices(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], m_strokeColor); + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], d->strokeColor); } -void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericRootRenderNode *rn) +void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) { - if (m_rootNode != rn) { - m_rootNode = rn; - // Scenegraph nodes can be destroyed and then replaced by new ones over - // time; hence it is important to mark everything dirty for - // updatePathRenderNode(). We can assume the renderer has a full sync - // of the data at this point. - m_effectiveDirty = DirtyAll; + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; } } // on the render thread with gui blocked -void QQuickPathItemGenericRenderer::updatePathRenderNode() +void QQuickPathItemGenericRenderer::updateNode() { - if (!m_effectiveDirty || !m_rootNode) + if (!m_rootNode || !m_accDirty) return; - if (m_fillColor.a == 0) { - delete m_rootNode->m_fillNode; - m_rootNode->m_fillNode = nullptr; - } else if (!m_rootNode->m_fillNode) { - m_rootNode->m_fillNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); - if (m_rootNode->m_strokeNode) - m_rootNode->removeChildNode(m_rootNode->m_strokeNode); - m_rootNode->appendChildNode(m_rootNode->m_fillNode); - if (m_rootNode->m_strokeNode) - m_rootNode->appendChildNode(m_rootNode->m_strokeNode); - m_effectiveDirty |= DirtyGeom; - } +// [ m_rootNode ] +// / / / +// #0 [ fill ] [ stroke ] [ next ] +// / / | +// #1 [ fill ] [ stroke ] [ next ] +// / / | +// #2 [ fill ] [ stroke ] [ next ] +// ... +// ... + + QQuickPathItemGenericNode **nodePtr = &m_rootNode; + QQuickPathItemGenericNode *prevNode = nullptr; + + for (VisualPathData &d : m_vp) { + if (!*nodePtr) { + *nodePtr = new QQuickPathItemGenericNode; + prevNode->m_next = *nodePtr; + prevNode->appendChildNode(*nodePtr); + } - if (m_strokeWidth < 0.0f || m_strokeColor.a == 0) { - delete m_rootNode->m_strokeNode; - m_rootNode->m_strokeNode = nullptr; - } else if (!m_rootNode->m_strokeNode) { - m_rootNode->m_strokeNode = new QQuickPathItemGenericRenderNode(m_item->window(), m_rootNode); - m_rootNode->appendChildNode(m_rootNode->m_strokeNode); - m_effectiveDirty |= DirtyGeom; + QQuickPathItemGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyGeom; + if (!d.effectiveDirty) + continue; + + if (d.fillColor.a == 0) { + delete node->m_fillNode; + node->m_fillNode = nullptr; + } else if (!node->m_fillNode) { + node->m_fillNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); + if (node->m_strokeNode) + node->removeChildNode(node->m_strokeNode); + node->appendChildNode(node->m_fillNode); + if (node->m_strokeNode) + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyGeom; + } + + if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { + delete node->m_strokeNode; + node->m_strokeNode = nullptr; + } else if (!node->m_strokeNode) { + node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; } - updateFillNode(); - updateStrokeNode(); + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } - m_effectiveDirty = 0; + m_accDirty = 0; } -void QQuickPathItemGenericRenderer::updateFillNode() +void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) { - if (!m_rootNode->m_fillNode) + if (!node->m_fillNode) return; - QQuickPathItemGenericRenderNode *n = m_rootNode->m_fillNode; + QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; QSGGeometry *g = &n->m_geometry; - if (m_fillVertices.isEmpty()) { + if (d->fillVertices.isEmpty()) { g->allocate(0, 0); n->markDirty(QSGNode::DirtyGeometry); return; } - if (m_fillGradientActive) { - n->activateMaterial(QQuickPathItemGenericRenderNode::MatLinearGradient); - if (m_effectiveDirty & DirtyFillGradient) { + if (d->fillGradientActive) { + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); + if (d->effectiveDirty & DirtyFillGradient) { // Make a copy of the data that will be accessed by the material on // the render thread. - n->m_fillGradient = m_fillGradient; + n->m_fillGradient = d->fillGradient; // Gradients are implemented via a texture-based material. n->markDirty(QSGNode::DirtyMaterial); // stop here if only the gradient changed; no need to touch the geometry - if (!(m_effectiveDirty & DirtyGeom)) + if (!(d->effectiveDirty & DirtyGeom)) return; } } else { - n->activateMaterial(QQuickPathItemGenericRenderNode::MatSolidColor); + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); // fast path for updating only color values when no change in vertex positions - if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, m_fillColor); + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); n->markDirty(QSGNode::DirtyGeometry); return; } } - g->allocate(m_fillVertices.count(), m_fillIndices.count()); + g->allocate(d->fillVertices.count(), d->fillIndices.count()); g->setDrawingMode(QSGGeometry::DrawTriangles); - memcpy(g->vertexData(), m_fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); - memcpy(g->indexData(), m_fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); n->markDirty(QSGNode::DirtyGeometry); } -void QQuickPathItemGenericRenderer::updateStrokeNode() +void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node) { - if (!m_rootNode->m_strokeNode) + if (!node->m_strokeNode) return; - if (m_effectiveDirty == DirtyFillGradient) // not applicable + if (d->effectiveDirty == DirtyFillGradient) // not applicable return; - QQuickPathItemGenericRenderNode *n = m_rootNode->m_strokeNode; + QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; n->markDirty(QSGNode::DirtyGeometry); QSGGeometry *g = &n->m_geometry; - if (m_strokeVertices.isEmpty()) { + if (d->strokeVertices.isEmpty()) { g->allocate(0, 0); return; } - if ((m_effectiveDirty & DirtyColor) && !(m_effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, m_strokeColor); + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); return; } - g->allocate(m_strokeVertices.count(), 0); + g->allocate(d->strokeVertices.count(), 0); g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memcpy(g->vertexData(), m_strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); } QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) @@ -415,7 +445,7 @@ QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindo } QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickPathItemGenericRenderNode *node) + QQuickPathItemGenericStrokeFillNode *node) { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); @@ -458,7 +488,7 @@ void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, Q if (state.isMatrixDirty()) program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - QQuickPathItemGenericRenderNode *node = m->node(); + QQuickPathItemGenericStrokeFillNode *node = m->node(); program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); @@ -477,8 +507,8 @@ int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) cons Q_ASSERT(other && type() == other->type()); const QQuickPathItemLinearGradientMaterial *m = static_cast(other); - QQuickPathItemGenericRenderNode *a = node(); - QQuickPathItemGenericRenderNode *b = m->node(); + QQuickPathItemGenericStrokeFillNode *a = node(); + QQuickPathItemGenericStrokeFillNode *b = m->node(); Q_ASSERT(a && b); if (a == b) return 0; diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index a094b9fca6..4454b14a13 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE -class QQuickPathItemGenericRootRenderNode; +class QQuickPathItemGenericNode; class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer { @@ -68,67 +68,69 @@ public: DirtyGeom = 0x01, DirtyColor = 0x02, DirtyFillGradient = 0x04, - - DirtyAll = 0xFF + DirtyList = 0x08 }; QQuickPathItemGenericRenderer(QQuickItem *item) : m_item(item), m_rootNode(nullptr), - m_effectiveDirty(0) + m_accDirty(0) { } - void beginSync() override; - void setPath(const QQuickPath *path) override; - void setStrokeColor(const QColor &color) override; - void setStrokeWidth(qreal w) override; - void setFillColor(const QColor &color) override; - void setFillRule(QQuickPathItem::FillRule fillRule) override; - void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(QQuickPathItem::CapStyle capStyle) override; - void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(QQuickPathGradient *gradient) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; void endSync() override; - void updatePathRenderNode() override; - void setRootNode(QQuickPathItemGenericRootRenderNode *rn); + void updateNode() override; + + void setRootNode(QQuickPathItemGenericNode *node); struct Color4ub { unsigned char r, g, b, a; }; private: - void triangulateFill(); - void triangulateStroke(); - void updateFillNode(); - void updateStrokeNode(); + struct VisualPathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + QVector fillVertices; + QVector fillIndices; + QVector strokeVertices; + int syncDirty; + int effectiveDirty = 0; + }; + + void triangulateFill(VisualPathData *d); + void triangulateStroke(VisualPathData *d); + + void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); + void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); QQuickItem *m_item; - QQuickPathItemGenericRootRenderNode *m_rootNode; + QQuickPathItemGenericNode *m_rootNode; QTriangulatingStroker m_stroker; QDashedStrokeProcessor m_dashStroker; - - float m_strokeWidth; - QPen m_pen; - Color4ub m_strokeColor; - Color4ub m_fillColor; - Qt::FillRule m_fillRule; - QPainterPath m_path; - bool m_fillGradientActive; - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - - QVector m_fillVertices; - QVector m_fillIndices; - QVector m_strokeVertices; - - int m_syncDirty; - int m_effectiveDirty; + QVector m_vp; + int m_accDirty; }; -class QQuickPathItemGenericRenderNode : public QSGGeometryNode +class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode { public: - QQuickPathItemGenericRenderNode(QQuickWindow *window, QQuickPathItemGenericRootRenderNode *rootNode); - ~QQuickPathItemGenericRenderNode(); + QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); enum Material { MatSolidColor, @@ -138,7 +140,6 @@ public: void activateMaterial(Material m); QQuickWindow *window() const { return m_window; } - QQuickPathItemGenericRootRenderNode *rootNode() const { return m_rootNode; } // shadow data for custom materials QQuickPathItemGradientCache::GradientDesc m_fillGradient; @@ -146,7 +147,6 @@ public: private: QSGGeometry m_geometry; QQuickWindow *m_window; - QQuickPathItemGenericRootRenderNode *m_rootNode; QSGMaterial *m_material; QScopedPointer m_solidColorMaterial; QScopedPointer m_linearGradientMaterial; @@ -154,24 +154,19 @@ private: friend class QQuickPathItemGenericRenderer; }; -class QQuickPathItemGenericRootRenderNode : public QSGNode +class QQuickPathItemGenericNode : public QSGNode { public: - QQuickPathItemGenericRootRenderNode(QQuickWindow *window, bool hasFill, bool hasStroke); - ~QQuickPathItemGenericRootRenderNode(); - -private: - QQuickPathItemGenericRenderNode *m_fillNode; - QQuickPathItemGenericRenderNode *m_strokeNode; - - friend class QQuickPathItemGenericRenderer; + QQuickPathItemGenericStrokeFillNode *m_fillNode = nullptr; + QQuickPathItemGenericStrokeFillNode *m_strokeNode = nullptr; + QQuickPathItemGenericNode *m_next = nullptr; }; class QQuickPathItemGenericMaterialFactory { public: static QSGMaterial *createVertexColor(QQuickWindow *window); - static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericRenderNode *node); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericStrokeFillNode *node); }; #ifndef QT_NO_OPENGL @@ -197,7 +192,7 @@ private: class QQuickPathItemLinearGradientMaterial : public QSGMaterial { public: - QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericRenderNode *node) + QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) : m_node(node) { // Passing RequiresFullMatrix is essential in order to prevent the @@ -220,10 +215,10 @@ public: return new QQuickPathItemLinearGradientShader; } - QQuickPathItemGenericRenderNode *node() const { return m_node; } + QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } private: - QQuickPathItemGenericRenderNode *m_node; + QQuickPathItemGenericStrokeFillNode *m_node; }; #endif // QT_NO_OPENGL diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 5641d33f85..2338e51ff8 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -46,93 +46,109 @@ QT_BEGIN_NAMESPACE -void QQuickPathItemNvprRenderer::beginSync() +void QQuickPathItemNvprRenderer::beginSync(int totalCount) { - // nothing to do here + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } } -void QQuickPathItemNvprRenderer::setPath(const QQuickPath *path) +void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) { - convertPath(path); - m_dirty |= DirtyPath; + VisualPathGuiData &d(m_vp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; } -void QQuickPathItemNvprRenderer::setStrokeColor(const QColor &color) +void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) { - m_strokeColor = color; - m_dirty |= DirtyStyle; + VisualPathGuiData &d(m_vp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; } -void QQuickPathItemNvprRenderer::setStrokeWidth(qreal w) +void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) { - m_strokeWidth = w; - m_dirty |= DirtyStyle; + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; } -void QQuickPathItemNvprRenderer::setFillColor(const QColor &color) +void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) { - m_fillColor = color; - m_dirty |= DirtyStyle; + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; } -void QQuickPathItemNvprRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) { - m_fillRule = fillRule; - m_dirty |= DirtyFillRule; + VisualPathGuiData &d(m_vp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; } -void QQuickPathItemNvprRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) { - m_joinStyle = joinStyle; - m_miterLimit = miterLimit; - m_dirty |= DirtyStyle; + VisualPathGuiData &d(m_vp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; } -void QQuickPathItemNvprRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) { - m_capStyle = capStyle; - m_dirty |= DirtyStyle; + VisualPathGuiData &d(m_vp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; } -void QQuickPathItemNvprRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) +void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) { - m_dashActive = strokeStyle == QQuickPathItem::DashLine; - m_dashOffset = dashOffset; - m_dashPattern = dashPattern; - m_dirty |= DirtyDash; + VisualPathGuiData &d(m_vp[index]); + d.dashActive = strokeStyle == QQuickVisualPath::DashLine; + d.dashOffset = dashOffset; + d.dashPattern = dashPattern; + d.dirty |= DirtyDash; + m_accDirty |= DirtyDash; } -void QQuickPathItemNvprRenderer::setFillGradient(QQuickPathGradient *gradient) +void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient *gradient) { - m_fillGradientActive = gradient != nullptr; + VisualPathGuiData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; if (gradient) { - m_fillGradient.stops = gradient->sortedGradientStops(); - m_fillGradient.spread = gradient->spread(); + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - m_fillGradient.start = QPointF(g->x1(), g->y1()); - m_fillGradient.end = QPointF(g->x2(), g->y2()); + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); } else { Q_UNREACHABLE(); } } - m_dirty |= DirtyFillGradient; + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; } void QQuickPathItemNvprRenderer::endSync() { - // nothing to do here } void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) { if (m_node != node) { m_node = node; - // Scenegraph nodes can be destroyed and then replaced by new ones over - // time; hence it is important to mark everything dirty for - // updatePathRenderNode(). We can assume the renderer has a full sync - // of the data at this point. - m_dirty = DirtyAll; + m_accDirty |= DirtyList; } } @@ -140,6 +156,10 @@ QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path { QDebugStateSaver saver(debug); debug.space().noquote(); + if (!path.str.isEmpty()) { + debug << "Path with SVG string" << path.str; + return debug; + } debug << "Path with" << path.cmd.count() << "commands"; int ci = 0; for (GLubyte cmd : path.cmd) { @@ -201,9 +221,9 @@ static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, v->append(p.y()); } -void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) +void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) { - m_path = NvprPath(); + d->path = NvprPath(); if (!path) return; @@ -211,27 +231,31 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) if (pp.isEmpty()) return; - QPointF pos(path->startX(), path->startY()); - m_path.cmd.append(GL_MOVE_TO_NV); - m_path.coord.append(pos.x()); - m_path.coord.append(pos.y()); + QPointF startPos(path->startX(), path->startY()); + QPointF pos(startPos); + if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { + d->path.cmd.append(GL_MOVE_TO_NV); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + } for (QQuickPathElement *e : pp) { if (QQuickPathMove *o = qobject_cast(e)) { - m_path.cmd.append(GL_MOVE_TO_NV); - appendCoords(&m_path.coord, o, &pos); + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; } else if (QQuickPathLine *o = qobject_cast(e)) { - m_path.cmd.append(GL_LINE_TO_NV); - appendCoords(&m_path.coord, o, &pos); + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); } else if (QQuickPathQuad *o = qobject_cast(e)) { - m_path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - appendControlCoords(&m_path.coord, o, pos); - appendCoords(&m_path.coord, o, &pos); + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); } else if (QQuickPathCubic *o = qobject_cast(e)) { - m_path.cmd.append(GL_CUBIC_CURVE_TO_NV); - appendControl1Coords(&m_path.coord, o, pos); - appendControl2Coords(&m_path.coord, o, pos); - appendCoords(&m_path.coord, o, &pos); + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&d->path.coord, o, pos); + appendControl2Coords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); } else if (QQuickPathArc *o = qobject_cast(e)) { const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo GLenum cmd; @@ -239,18 +263,29 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path) cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; else cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - m_path.cmd.append(cmd); - m_path.coord.append(o->radiusX()); - m_path.coord.append(o->radiusY()); - m_path.coord.append(o->xAxisRotation()); - appendCoords(&m_path.coord, o, &pos); + d->path.cmd.append(cmd); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(o->xAxisRotation()); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathSvg *o = qobject_cast(e)) { + // PathSvg cannot be combined with other elements. But take at + // least startX and startY into account. + if (d->path.str.isEmpty()) + d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); + d->path.str.append(o->path().toUtf8()); } else { qWarning() << "PathItem/NVPR: unsupported Path element" << e; } } - if (qFuzzyCompare(pos.x(), path->startX()) && qFuzzyCompare(pos.y(), path->startY())) - m_path.cmd.append(GL_CLOSE_PATH_NV); + // For compatibility with QTriangulatingStroker. SVG and others would not + // implicitly close the path when end_pos == start_pos (start_pos being the + // last moveTo pos); that would still need an explicit 'z' or similar. We + // don't have an explicit close command, so just fake a close when the + // positions match. + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); } static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) @@ -259,88 +294,103 @@ static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); } -void QQuickPathItemNvprRenderer::updatePathRenderNode() +void QQuickPathItemNvprRenderer::updateNode() { // Called on the render thread with gui blocked -> update the node with its // own copy of all relevant data. - if (!m_dirty) + if (!m_accDirty) return; - // updatePathRenderNode() can be called several times with different dirty - // state before render() gets invoked. So accumulate. - m_node->m_dirty |= m_dirty; - - if (m_dirty & DirtyPath) - m_node->m_source = m_path; - - if (m_dirty & DirtyStyle) { - m_node->m_strokeWidth = m_strokeWidth; - m_node->m_strokeColor = qsg_premultiply(m_strokeColor, 1.0f); - m_node->m_fillColor = qsg_premultiply(m_fillColor, 1.0f); - switch (m_joinStyle) { - case QQuickPathItem::MiterJoin: - m_node->m_joinStyle = GL_MITER_TRUNCATE_NV; - break; - case QQuickPathItem::BevelJoin: - m_node->m_joinStyle = GL_BEVEL_NV; - break; - case QQuickPathItem::RoundJoin: - m_node->m_joinStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); - } - m_node->m_miterLimit = m_miterLimit; - switch (m_capStyle) { - case QQuickPathItem::FlatCap: - m_node->m_capStyle = GL_FLAT; - break; - case QQuickPathItem::SquareCap: - m_node->m_capStyle = GL_SQUARE_NV; - break; - case QQuickPathItem::RoundCap: - m_node->m_capStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); + const int count = m_vp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_vp.resize(count); + + for (int i = 0; i < count; ++i) { + VisualPathGuiData &src(m_vp[i]); + QQuickPathItemNvprRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); + + int dirty = src.dirty; + src.dirty = 0; + if (listChanged) + dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; + + // updateNode() can be called several times with different dirty + // states before render() gets invoked. So accumulate. + dst.dirty |= dirty; + + if (dirty & DirtyPath) + dst.source = src.path; + + if (dirty & DirtyStyle) { + dst.strokeWidth = src.strokeWidth; + dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); + dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); + switch (src.joinStyle) { + case QQuickVisualPath::MiterJoin: + dst.joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickVisualPath::BevelJoin: + dst.joinStyle = GL_BEVEL_NV; + break; + case QQuickVisualPath::RoundJoin: + dst.joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + dst.miterLimit = src.miterLimit; + switch (src.capStyle) { + case QQuickVisualPath::FlatCap: + dst.capStyle = GL_FLAT; + break; + case QQuickVisualPath::SquareCap: + dst.capStyle = GL_SQUARE_NV; + break; + case QQuickVisualPath::RoundCap: + dst.capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } } - } - if (m_dirty & DirtyFillRule) { - switch (m_fillRule) { - case QQuickPathItem::OddEvenFill: - m_node->m_fillRule = GL_INVERT; - break; - case QQuickPathItem::WindingFill: - m_node->m_fillRule = GL_COUNT_UP_NV; - break; - default: - Q_UNREACHABLE(); + if (dirty & DirtyFillRule) { + switch (src.fillRule) { + case QQuickVisualPath::OddEvenFill: + dst.fillRule = GL_INVERT; + break; + case QQuickVisualPath::WindingFill: + dst.fillRule = GL_COUNT_UP_NV; + break; + default: + Q_UNREACHABLE(); + } } - } - if (m_dirty & DirtyDash) { - m_node->m_dashOffset = m_dashOffset; - if (m_dashActive) { - m_node->m_dashPattern.resize(m_dashPattern.count()); - // Multiply by strokeWidth because the PathItem API follows QPen - // meaning the input dash pattern here is in width units. - for (int i = 0; i < m_dashPattern.count(); ++i) - m_node->m_dashPattern[i] = GLfloat(m_dashPattern[i]) * m_strokeWidth; - } else { - m_node->m_dashPattern.clear(); + if (dirty & DirtyDash) { + dst.dashOffset = src.dashOffset; + if (src.dashActive) { + dst.dashPattern.resize(src.dashPattern.count()); + // Multiply by strokeWidth because the PathItem API follows QPen + // meaning the input dash pattern here is in width units. + for (int i = 0; i < src.dashPattern.count(); ++i) + dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + } else { + dst.dashPattern.clear(); + } } - } - if (m_dirty & DirtyFillGradient) { - m_node->m_fillGradientActive = m_fillGradientActive; - if (m_fillGradientActive) - m_node->m_fillGradient = m_fillGradient; + if (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } } m_node->markDirty(QSGNode::DirtyMaterial); - m_dirty = 0; + m_accDirty = 0; } bool QQuickPathItemNvprRenderNode::nvprInited = false; @@ -359,14 +409,15 @@ QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() void QQuickPathItemNvprRenderNode::releaseResources() { - if (m_path) { - nvpr.deletePaths(m_path, 1); - m_path = 0; - } - - if (m_fallbackFbo) { - delete m_fallbackFbo; - m_fallbackFbo = nullptr; + for (VisualPathRenderData &d : m_vp) { + if (d.path) { + nvpr.deletePaths(d.path, 1); + d.path = 0; + } + if (d.fallbackFbo) { + delete d.fallbackFbo; + d.fallbackFbo = nullptr; + } } m_fallbackBlitter.destroy(); @@ -449,48 +500,52 @@ QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMate return &mtl; } -void QQuickPathItemNvprRenderNode::updatePath() +void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) { - if (m_dirty & QQuickPathItemNvprRenderer::DirtyPath) { - if (!m_path) { - m_path = nvpr.genPaths(1); - Q_ASSERT(m_path != 0); + if (d->dirty & QQuickPathItemNvprRenderer::DirtyPath) { + if (!d->path) { + d->path = nvpr.genPaths(1); + Q_ASSERT(d->path != 0); + } + if (d->source.str.isEmpty()) { + nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), + d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); + } else { + nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); } - nvpr.pathCommands(m_path, m_source.cmd.count(), m_source.cmd.constData(), - m_source.coord.count(), GL_FLOAT, m_source.coord.constData()); } - if (m_dirty & QQuickPathItemNvprRenderer::DirtyStyle) { - nvpr.pathParameterf(m_path, GL_PATH_STROKE_WIDTH_NV, m_strokeWidth); - nvpr.pathParameteri(m_path, GL_PATH_JOIN_STYLE_NV, m_joinStyle); - nvpr.pathParameteri(m_path, GL_PATH_MITER_LIMIT_NV, m_miterLimit); - nvpr.pathParameteri(m_path, GL_PATH_END_CAPS_NV, m_capStyle); - nvpr.pathParameteri(m_path, GL_PATH_DASH_CAPS_NV, m_capStyle); + if (d->dirty & QQuickPathItemNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); + nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); + nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); + nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); + nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); } - if (m_dirty & QQuickPathItemNvprRenderer::DirtyDash) { - nvpr.pathParameterf(m_path, GL_PATH_DASH_OFFSET_NV, m_dashOffset); + if (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); // count == 0 -> no dash - nvpr.pathDashArray(m_path, m_dashPattern.count(), m_dashPattern.constData()); + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); } } -void QQuickPathItemNvprRenderNode::renderStroke(int strokeStencilValue, int writeMask) +void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) { QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_strokeColor.x(), m_strokeColor.y(), m_strokeColor.z(), m_strokeColor.w()); + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - nvpr.stencilThenCoverStrokePath(m_path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); } -void QQuickPathItemNvprRenderNode::renderFill() +void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) { QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; - if (m_fillGradientActive) { + if (d->fillGradientActive) { mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(m_fillGradient); + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(d->fillGradient); tx->bind(); // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) // where x and y are in path coordinate space, which is just what @@ -498,20 +553,20 @@ void QQuickPathItemNvprRenderNode::renderFill() GLfloat coeff[6] = { 1, 0, 0, 0, 1, 0 }; nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], m_fillGradient.start.x(), m_fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], m_fillGradient.end.x(), m_fillGradient.end.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); } else { mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - m_fillColor.x(), m_fillColor.y(), m_fillColor.z(), m_fillColor.w()); + d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); } f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); const int writeMask = 0xFF; - nvpr.stencilThenCoverFillPath(m_path, m_fillRule, writeMask, GL_BOUNDING_BOX_NV); + nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); } -void QQuickPathItemNvprRenderNode::renderOffscreenFill() +void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) { QQuickWindow *w = m_item->window(); const qreal dpr = w->effectiveDevicePixelRatio(); @@ -520,13 +575,13 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill() if (rtSize.isEmpty()) rtSize = w->size() * dpr; - if (m_fallbackFbo && m_fallbackFbo->size() != itemSize) { - delete m_fallbackFbo; - m_fallbackFbo = nullptr; + if (d->fallbackFbo && d->fallbackFbo->size() != itemSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; } - if (!m_fallbackFbo) - m_fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); - if (!m_fallbackFbo->bind()) + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) return; f->glViewport(0, 0, itemSize.width(), itemSize.height()); @@ -541,9 +596,9 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill() proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1); nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); - renderFill(); + renderFill(d); - m_fallbackFbo->release(); + d->fallbackFbo->release(); f->glViewport(0, 0, rtSize.width(), rtSize.height()); } @@ -573,8 +628,6 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) nvprInited = true; } - updatePath(); - f->glUseProgram(0); f->glStencilMask(~0); f->glEnable(GL_STENCIL_TEST); @@ -583,70 +636,74 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) // when true, the stencil buffer already has a clip path with a ref value of sv const int sv = state->stencilValue(); - const bool hasFill = !qFuzzyIsNull(m_fillColor.w()) || m_fillGradientActive; - const bool hasStroke = m_strokeWidth >= 0.0f && !qFuzzyIsNull(m_strokeColor.w()); + for (VisualPathRenderData &d : m_vp) { + updatePath(&d); - if (hasFill && stencilClip) { - // Fall back to a texture when complex clipping is in use and we have - // to fill. Reconciling glStencilFillPath's and the scenegraph's clip - // stencil semantics has not succeeded so far... - renderOffscreenFill(); - } + const bool hasFill = !qFuzzyIsNull(d.fillColor.w()) || d.fillGradientActive; + const bool hasStroke = d.strokeWidth >= 0.0f && !qFuzzyIsNull(d.strokeColor.w()); + + if (hasFill && stencilClip) { + // Fall back to a texture when complex clipping is in use and we have + // to fill. Reconciling glStencilFillPath's and the scenegraph's clip + // stencil semantics has not succeeded so far... + renderOffscreenFill(&d); + } - // Depth test against the opaque batches rendered before. - f->glEnable(GL_DEPTH_TEST); - f->glDepthFunc(GL_LESS); - nvpr.pathCoverDepthFunc(GL_LESS); - nvpr.pathStencilDepthOffset(-0.05f, -1); + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - if (state->scissorEnabled()) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); - } + if (state->scissorEnabled()) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } - // Fill! - if (hasFill) { - if (!stencilClip) { - setupStencilForCover(false, 0); - renderFill(); - } else { - if (!m_fallbackBlitter.isCreated()) - m_fallbackBlitter.create(); - f->glStencilFunc(GL_EQUAL, sv, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - m_fallbackBlitter.texturedQuad(m_fallbackFbo->texture(), m_fallbackFbo->size(), - *state->projectionMatrix(), *matrix(), - inheritedOpacity()); + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(&d); + } else { + if (!m_fallbackBlitter.isCreated()) + m_fallbackBlitter.create(); + f->glStencilFunc(GL_EQUAL, sv, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), *matrix(), + inheritedOpacity()); + } } - } - // Stroke! - if (hasStroke) { - const int strokeStencilValue = 0x80; - const int writeMask = 0x80; - - setupStencilForCover(stencilClip, sv); - if (stencilClip) { - // for the stencil step (eff. read mask == 0xFF & ~writeMask) - nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); - // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. - // This assumes the clip stencil value is <= 127. - if (sv >= strokeStencilValue) - qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + // Stroke! + if (hasStroke) { + const int strokeStencilValue = 0x80; + const int writeMask = 0x80; + + setupStencilForCover(stencilClip, sv); + if (stencilClip) { + // for the stencil step (eff. read mask == 0xFF & ~writeMask) + nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); + // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. + // This assumes the clip stencil value is <= 127. + if (sv >= strokeStencilValue) + qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(&d, strokeStencilValue, writeMask); } - renderStroke(strokeStencilValue, writeMask); - } + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); - if (stencilClip) - nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + d.dirty = 0; + } f->glBindProgramPipeline(0); - - m_dirty = 0; } QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 7dd2d564c5..f594609e83 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -76,55 +76,61 @@ public: DirtyFillRule = 0x04, DirtyDash = 0x08, DirtyFillGradient = 0x10, - - DirtyAll = 0xFF + DirtyList = 0x20 }; QQuickPathItemNvprRenderer(QQuickItem *item) : m_item(item) { } - void beginSync() override; - void setPath(const QQuickPath *path) override; - void setStrokeColor(const QColor &color) override; - void setStrokeWidth(qreal w) override; - void setFillColor(const QColor &color) override; - void setFillRule(QQuickPathItem::FillRule fillRule) override; - void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(QQuickPathItem::CapStyle capStyle) override; - void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(QQuickPathGradient *gradient) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; void endSync() override; - void updatePathRenderNode() override; + + void updateNode() override; void setNode(QQuickPathItemNvprRenderNode *node); struct NvprPath { QVector cmd; QVector coord; + QByteArray str; }; private: - void convertPath(const QQuickPath *path); + struct VisualPathGuiData { + int dirty = 0; + NvprPath path; + qreal strokeWidth; + QColor strokeColor; + QColor fillColor; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::FillRule fillRule; + bool dashActive; + qreal dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, VisualPathGuiData *d); QQuickItem *m_item; QQuickPathItemNvprRenderNode *m_node = nullptr; - int m_dirty = 0; - - NvprPath m_path; - qreal m_strokeWidth; - QColor m_strokeColor; - QColor m_fillColor; - QQuickPathItem::JoinStyle m_joinStyle; - int m_miterLimit; - QQuickPathItem::CapStyle m_capStyle; - QQuickPathItem::FillRule m_fillRule; - bool m_dashActive; - qreal m_dashOffset; - QVector m_dashPattern; - bool m_fillGradientActive; - QQuickPathItemGradientCache::GradientDesc m_fillGradient; + int m_accDirty = 0; + + QVector m_vp; }; QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); @@ -187,10 +193,28 @@ public: static bool isSupported(); private: - void updatePath(); - void renderStroke(int strokeStencilValue, int writeMask); - void renderFill(); - void renderOffscreenFill(); + struct VisualPathRenderData { + GLuint path = 0; + int dirty = 0; + QQuickPathItemNvprRenderer::NvprPath source; + GLfloat strokeWidth; + QVector4D strokeColor; + QVector4D fillColor; + GLenum joinStyle; + GLint miterLimit; + GLenum capStyle; + GLenum fillRule; + GLfloat dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + }; + + void updatePath(VisualPathRenderData *d); + void renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask); + void renderFill(VisualPathRenderData *d); + void renderOffscreenFill(VisualPathRenderData *d); void setupStencilForCover(bool stencilClip, int sv); static bool nvprInited; @@ -198,25 +222,11 @@ private: static QQuickNvprMaterialManager mtlmgr; QQuickPathItem *m_item; - GLuint m_path = 0; - int m_dirty = 0; - - QQuickPathItemNvprRenderer::NvprPath m_source; - GLfloat m_strokeWidth; - QVector4D m_strokeColor; - QVector4D m_fillColor; - GLenum m_joinStyle; - GLint m_miterLimit; - GLenum m_capStyle; - GLenum m_fillRule; - GLfloat m_dashOffset; - QVector m_dashPattern; - bool m_fillGradientActive; - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - QOpenGLFramebufferObject *m_fallbackFbo = nullptr; QQuickNvprBlitter m_fallbackBlitter; QOpenGLExtraFunctions *f = nullptr; + QVector m_vp; + friend class QQuickPathItemNvprRenderer; }; diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index c7b52e641f..6c3cf73a56 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -42,77 +42,97 @@ QT_BEGIN_NAMESPACE -void QQuickPathItemSoftwareRenderer::beginSync() +void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) { - // nothing to do here + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } } -void QQuickPathItemSoftwareRenderer::setPath(const QQuickPath *path) +void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) { - m_path = path ? path->path() : QPainterPath(); - m_dirty |= DirtyPath; + VisualPathGuiData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; } -void QQuickPathItemSoftwareRenderer::setStrokeColor(const QColor &color) +void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) { - m_pen.setColor(color); - m_dirty |= DirtyPen; + VisualPathGuiData &d(m_vp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; } -void QQuickPathItemSoftwareRenderer::setStrokeWidth(qreal w) +void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) { - m_strokeWidth = w; + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; if (w >= 0.0f) - m_pen.setWidthF(w); - m_dirty |= DirtyPen; + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; } -void QQuickPathItemSoftwareRenderer::setFillColor(const QColor &color) +void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) { - m_fillColor = color; - m_brush.setColor(m_fillColor); - m_dirty |= DirtyBrush; + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; } -void QQuickPathItemSoftwareRenderer::setFillRule(QQuickPathItem::FillRule fillRule) +void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) { - m_fillRule = Qt::FillRule(fillRule); - m_dirty |= DirtyFillRule; + VisualPathGuiData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; } -void QQuickPathItemSoftwareRenderer::setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) +void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) { - m_pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - m_pen.setMiterLimit(miterLimit); - m_dirty |= DirtyPen; + VisualPathGuiData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; } -void QQuickPathItemSoftwareRenderer::setCapStyle(QQuickPathItem::CapStyle capStyle) +void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) { - m_pen.setCapStyle(Qt::PenCapStyle(capStyle)); - m_dirty |= DirtyPen; + VisualPathGuiData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; } -void QQuickPathItemSoftwareRenderer::setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) +void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) { + VisualPathGuiData &d(m_vp[index]); switch (strokeStyle) { - case QQuickPathItem::SolidLine: - m_pen.setStyle(Qt::SolidLine); + case QQuickVisualPath::SolidLine: + d.pen.setStyle(Qt::SolidLine); break; - case QQuickPathItem::DashLine: - m_pen.setStyle(Qt::CustomDashLine); - m_pen.setDashPattern(dashPattern); - m_pen.setDashOffset(dashOffset); + case QQuickVisualPath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); break; default: break; } - m_dirty |= DirtyPen; + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; } -void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradient) +void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) { + VisualPathGuiData &d(m_vp[index]); if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), linearGradient->x2(), linearGradient->y2()); @@ -130,57 +150,61 @@ void QQuickPathItemSoftwareRenderer::setFillGradient(QQuickPathGradient *gradien default: break; } - m_brush = QBrush(painterGradient); + d.brush = QBrush(painterGradient); } else { - m_brush = QBrush(m_fillColor); + d.brush = QBrush(d.fillColor); } - m_dirty |= DirtyBrush; + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; } void QQuickPathItemSoftwareRenderer::endSync() { - // nothing to do here } void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) { if (m_node != node) { m_node = node; - // Scenegraph nodes can be destroyed and then replaced by new ones over - // time; hence it is important to mark everything dirty for - // updatePathRenderNode(). We can assume the renderer has a full sync - // of the data at this point. - m_dirty = DirtyAll; + m_accDirty |= DirtyList; } } -void QQuickPathItemSoftwareRenderer::updatePathRenderNode() +void QQuickPathItemSoftwareRenderer::updateNode() { - if (!m_dirty) + if (!m_accDirty) return; - // updatePathRenderNode() can be called several times with different dirty - // state before render() gets invoked. So accumulate. - m_node->m_dirty |= m_dirty; + const int count = m_vp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_vp.resize(count); - if (m_dirty & DirtyPath) { - m_node->m_path = m_path; - m_node->m_path.setFillRule(m_fillRule); - } + for (int i = 0; i < count; ++i) { + VisualPathGuiData &src(m_vp[i]); + QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - if (m_dirty & DirtyFillRule) - m_node->m_path.setFillRule(m_fillRule); + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } - if (m_dirty & DirtyPen) { - m_node->m_pen = m_pen; - m_node->m_strokeWidth = m_strokeWidth; - } + if (listChanged || (src.dirty & DirtyFillRule)) + dst.path.setFillRule(src.fillRule); + + if (listChanged || (src.dirty & DirtyPen)) { + dst.pen = src.pen; + dst.strokeWidth = src.strokeWidth; + } + + if (listChanged || (src.dirty & DirtyBrush)) + dst.brush = src.brush; - if (m_dirty & DirtyBrush) - m_node->m_brush = m_brush; + src.dirty = 0; + } m_node->markDirty(QSGNode::DirtyMaterial); - m_dirty = 0; + m_accDirty = 0; } QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) @@ -199,7 +223,7 @@ void QQuickPathItemSoftwareRenderNode::releaseResources() void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) { - if (m_path.isEmpty()) + if (m_vp.isEmpty()) return; QSGRendererInterface *rif = m_item->window()->rendererInterface(); @@ -213,11 +237,11 @@ void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) p->setTransform(matrix()->toTransform()); p->setOpacity(inheritedOpacity()); - p->setPen(m_strokeWidth >= 0.0f && m_pen.color() != Qt::transparent ? m_pen : Qt::NoPen); - p->setBrush(m_brush.color() != Qt::transparent ? m_brush : Qt::NoBrush); - p->drawPath(m_path); - - m_dirty = 0; + for (const VisualPathRenderData &d : qAsConst(m_vp)) { + p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); + p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); + p->drawPath(d.path); + } } QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 6c7d052596..60b52e2def 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -68,36 +68,39 @@ public: DirtyPen = 0x02, DirtyFillRule = 0x04, DirtyBrush = 0x08, - - DirtyAll = 0xFF + DirtyList = 0x10 }; - void beginSync() override; - void setPath(const QQuickPath *path) override; - void setStrokeColor(const QColor &color) override; - void setStrokeWidth(qreal w) override; - void setFillColor(const QColor &color) override; - void setFillRule(QQuickPathItem::FillRule fillRule) override; - void setJoinStyle(QQuickPathItem::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(QQuickPathItem::CapStyle capStyle) override; - void setStrokeStyle(QQuickPathItem::StrokeStyle strokeStyle, + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(QQuickPathGradient *gradient) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; void endSync() override; - void updatePathRenderNode() override; + + void updateNode() override; void setNode(QQuickPathItemSoftwareRenderNode *node); private: QQuickPathItemSoftwareRenderNode *m_node = nullptr; - int m_dirty = 0; - - QPainterPath m_path; - QPen m_pen; - float m_strokeWidth; - QColor m_fillColor; - QBrush m_brush; - Qt::FillRule m_fillRule; + int m_accDirty = 0; + struct VisualPathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector m_vp; }; class QQuickPathItemSoftwareRenderNode : public QSGRenderNode @@ -114,12 +117,14 @@ public: private: QQuickPathItem *m_item; - int m_dirty = 0; - QPainterPath m_path; - QPen m_pen; - float m_strokeWidth; - QBrush m_brush; + struct VisualPathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector m_vp; friend class QQuickPathItemSoftwareRenderer; }; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 228056fdb5..337d63e53b 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -79,6 +79,73 @@ QT_BEGIN_NAMESPACE PathAttribute allows named attributes with values to be defined along the path. + Path and the other types for specifying path elements are shared between + \l PathView and \l PathItem. The following table provides an overview of the + applicability of the various path elements: + + \table + \header + \li Element + \li PathView + \li PathItem + \li PathItem, GL_NV_path_rendering + \li PathItem, software + \row + \li PathMove + \li N/A + \li Yes + \li Yes + \li Yes + \row + \li PathLine + \li Yes + \li Yes + \li Yes + \li Yes + \row + \li PathQuad + \li Yes + \li Yes + \li Yes + \li Yes + \row + \li PathCubic + \li Yes + \li Yes + \li Yes + \li Yes + \row + \li PathArc + \li Yes + \li Yes + \li Yes + \li Yes + \row + \li PathSvg + \li Yes + \li Yes + \li Yes, with limitations + \li Yes + \row + \li PathAttribute + \li Yes + \li N/A + \li N/A + \li N/A + \row + \li PathPercent + \li Yes + \li N/A + \li N/A + \li N/A + \row + \li PathCurve + \li Yes + \li No + \li No + \li No + \endtable + \sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) @@ -1872,6 +1939,11 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) \endqml \endtable + \note Mixing PathSvg with other type of elements is not always supported, + therefore it is strongly recommended to avoid this. For example, when + \l PathItem is backed by \c{GL_NV_path_rendering}, a Path can contain one or + more PathSvg elements, or one or more other type of elements, but not both. + \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve */ @@ -1896,6 +1968,7 @@ void QQuickPathSvg::setPath(const QString &path) _path = path; emit pathChanged(); + emit changed(); } void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &) -- cgit v1.2.3 From d57ede19dff1c45840f0d60881cada194330a6f3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 14:08:31 +0100 Subject: PathItem/NVPR: Remove unused member variable Change-Id: I8228b39d01097179e8b969b2ed479c18b80e9759 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer_p.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 0dce376945..d7131b3833 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -520,7 +520,7 @@ void QQuickPathItemPrivate::createRenderer() case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer(q); + renderer = new QQuickPathItemNvprRenderer; } else { rendererType = QQuickPathItem::GeometryRenderer; renderer = new QQuickPathItemGenericRenderer(q); diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index f594609e83..067ecf7355 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -79,10 +79,6 @@ public: DirtyList = 0x20 }; - QQuickPathItemNvprRenderer(QQuickItem *item) - : m_item(item) - { } - void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; void setStrokeColor(int index, const QColor &color) override; @@ -126,7 +122,6 @@ private: void convertPath(const QQuickPath *path, VisualPathGuiData *d); - QQuickItem *m_item; QQuickPathItemNvprRenderNode *m_node = nullptr; int m_accDirty = 0; -- cgit v1.2.3 From e0200d5a72d29a3918bf66290e498f99c0a62a0a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 8 Jan 2017 14:47:27 +0100 Subject: Clean up pathitem example grid view Combine stroke-only and fill-only. Drop the Repeater since it shows the bad practice of unnecessarily creating multiple PathItem instances. Drop the PathSvg example for now since this is a discouraged element. Move the tiger into a separate file and use an async Loader in order to avoid the 2 step activation. Change-Id: Ie65eb0284bdb8957f8fd5af7557542a6d044ef61 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index b42ff1d4b0..dbbc14d3c8 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -73,6 +73,9 @@ QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickW { setGeometry(&m_geometry); activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif } void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -- cgit v1.2.3 From f02e234fc012f4430378bc5205f32914822e4dff Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 8 Jan 2017 16:08:34 +0100 Subject: Reduce state changes with NVPR-backed PathItems Not setting the matrices and other states again and again for every path in the item saves a little CPU time. Change-Id: Iec9447de4c22cd25343462160bd4a8b9aa44368a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemnvprrenderer.cpp | 37 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 2338e51ff8..0c4357145a 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -585,6 +585,7 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) return; f->glViewport(0, 0, itemSize.width(), itemSize.height()); + f->glDisable(GL_DEPTH_TEST); f->glClearColor(0, 0, 0, 0); f->glClearStencil(0); f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); @@ -599,6 +600,7 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) renderFill(d); d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); f->glViewport(0, 0, rtSize.width(), rtSize.height()); } @@ -635,6 +637,20 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) const bool stencilClip = state->stencilEnabled(); // when true, the stencil buffer already has a clip path with a ref value of sv const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + bool reloadMatrices = true; for (VisualPathRenderData &d : m_vp) { updatePath(&d); @@ -646,21 +662,18 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) // Fall back to a texture when complex clipping is in use and we have // to fill. Reconciling glStencilFillPath's and the scenegraph's clip // stencil semantics has not succeeded so far... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); } - // Depth test against the opaque batches rendered before. - f->glEnable(GL_DEPTH_TEST); - f->glDepthFunc(GL_LESS); - nvpr.pathCoverDepthFunc(GL_LESS); - nvpr.pathStencilDepthOffset(-0.05f, -1); - - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - - if (state->scissorEnabled()) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); } // Fill! -- cgit v1.2.3 From 5575226f6b25bcf507c3412677756e07ce671cef Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 9 Jan 2017 11:43:31 +0100 Subject: PathItem: add optional threaded triangulation Spending 300+ ms in polish for the tiger when running with the generic backend is not going to fly. Therefore, add an optional mode for asynchronous triangulation. Change-Id: Ida44c7b8a28d38fb11243a6657221f039c62e21b Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 20 ++- src/quick/items/qquickpathitem_p.h | 5 + src/quick/items/qquickpathitem_p_p.h | 3 +- src/quick/items/qquickpathitemgenericrenderer.cpp | 197 ++++++++++++++++----- src/quick/items/qquickpathitemgenericrenderer_p.h | 70 ++++++-- src/quick/items/qquickpathitemnvprrenderer.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer_p.h | 2 +- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 2 +- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 2 +- 9 files changed, 243 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index d7131b3833..964d997806 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -361,6 +361,7 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() : componentComplete(true), vpChanged(false), rendererType(QQuickPathItem::UnknownRenderer), + async(false), renderer(nullptr) { } @@ -393,6 +394,23 @@ QQuickPathItem::RendererType QQuickPathItem::rendererType() const return d->rendererType; } +bool QQuickPathItem::asynchronous() const +{ + Q_D(const QQuickPathItem); + return d->async; +} + +void QQuickPathItem::setAsynchronous(bool async) +{ + Q_D(QQuickPathItem); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_visualPathChanged(); + } +} + static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); @@ -606,7 +624,7 @@ void QQuickPathItemPrivate::sync() dirty = 0; } - renderer->endSync(); + renderer->endSync(async); } // ***** gradient support ***** diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 0c1d0f061b..7c962d01fc 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -263,6 +263,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem { Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) Q_CLASSINFO("DefaultProperty", "visualPaths") @@ -280,6 +281,9 @@ public: RendererType rendererType() const; + bool asynchronous() const; + void setAsynchronous(bool async); + QQmlListProperty visualPaths(); protected: @@ -291,6 +295,7 @@ protected: Q_SIGNALS: void rendererChanged(); + void asynchronousChanged(); private: Q_DISABLE_COPY(QQuickPathItem) diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 5e6400edc6..faf7b1e451 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -79,7 +79,7 @@ public: virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - virtual void endSync() = 0; + virtual void endSync(bool async) = 0; // Render thread, with gui blocked virtual void updateNode() = 0; @@ -144,6 +144,7 @@ public: bool componentComplete; bool vpChanged; QQuickPathItem::RendererType rendererType; + bool async; QQuickAbstractPathRenderer *renderer; QVector vp; }; diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index dbbc14d3c8..6e4c89742a 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -39,7 +39,9 @@ #include "qquickpathitemgenericrenderer_p.h" #include +#include #include +#include QT_BEGIN_NAMESPACE @@ -103,6 +105,8 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) } // sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + void QQuickPathItemGenericRenderer::beginSync(int totalCount) { if (m_vp.count() != totalCount) { @@ -194,9 +198,22 @@ void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradien d.syncDirty |= DirtyFillGradient; } -void QQuickPathItemGenericRenderer::endSync() +void QQuickPathItemFillRunnable::run() { - for (VisualPathData &d : m_vp) { + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices); + emit done(this); +} + +void QQuickPathItemStrokeRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickPathItemGenericRenderer::endSync(bool async) +{ + for (int i = 0; i < m_vp.count(); ++i) { + VisualPathData &d(m_vp[i]); if (!d.syncDirty) continue; @@ -217,67 +234,145 @@ void QQuickPathItemGenericRenderer::endSync() } if (d.syncDirty & DirtyGeom) { - if (d.fillColor.a) - triangulateFill(&d); - if (d.strokeWidth >= 0.0f && d.strokeColor.a) - triangulateStroke(&d); + static QThreadPool threadPool; + static bool threadPoolReady = false; + if (async && !threadPoolReady) { + threadPoolReady = true; + const int idealCount = QThread::idealThreadCount(); + threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + if (d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + threadPool.start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); + } + } + if (d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + threadPool.start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } } } } -void QQuickPathItemGenericRenderer::triangulateFill(VisualPathData *d) +void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() { - d->path.setFillRule(d->fillRule); + for (const VisualPathData &d : qAsConst(m_vp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyGeom; + m_item->update(); +} - const QVectorPath &vp = qtVectorPathForPath(d->path); +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VerticesType *fillVertices, + IndicesType *fillIndices) +{ + const QVectorPath &vp = qtVectorPathForPath(path); QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - d->fillVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(d->fillVertices.data()); + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); const qreal *vsrc = ts.vertices.constData(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, d->fillColor); + vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, fillColor); - d->fillIndices.resize(ts.indices.size()); - quint16 *idst = d->fillIndices.data(); + fillIndices->resize(ts.indices.size()); + quint16 *idst = fillIndices->data(); if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - memcpy(idst, ts.indices.data(), d->fillIndices.count() * sizeof(quint16)); + memcpy(idst, ts.indices.data(), fillIndices->count() * sizeof(quint16)); } else { const quint32 *isrc = (const quint32 *) ts.indices.data(); - for (int i = 0; i < d->fillIndices.count(); ++i) + for (int i = 0; i < fillIndices->count(); ++i) idst[i] = isrc[i]; } } -void QQuickPathItemGenericRenderer::triangulateStroke(VisualPathData *d) +void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VerticesType *strokeVertices, + const QSize &clipSize) { - const QVectorPath &vp = qtVectorPathForPath(d->path); - - const QRectF clip(0, 0, m_item->width(), m_item->height()); + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); const qreal inverseScale = 1.0 / SCALE; - m_stroker.setInvScale(inverseScale); - if (d->pen.style() == Qt::SolidLine) { - m_stroker.process(vp, d->pen, clip, 0); + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); } else { - m_dashStroker.setInvScale(inverseScale); - m_dashStroker.process(vp, d->pen, clip, 0); - QVectorPath dashStroke(m_dashStroker.points(), m_dashStroker.elementCount(), - m_dashStroker.elementTypes(), 0); - m_stroker.process(dashStroke, d->pen, clip, 0); + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); } - if (!m_stroker.vertexCount()) { - d->strokeVertices.clear(); + if (!stroker.vertexCount()) { + strokeVertices->clear(); return; } - const int vertexCount = m_stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - d->strokeVertices.resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(d->strokeVertices.data()); - const float *vsrc = m_stroker.vertices(); + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], d->strokeColor); + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); } void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) @@ -317,7 +412,7 @@ void QQuickPathItemGenericRenderer::updateNode() QQuickPathItemGenericNode *node = *nodePtr; if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyGeom | DirtyColor | DirtyFillGradient; if (!d.effectiveDirty) continue; @@ -361,25 +456,36 @@ void QQuickPathItemGenericRenderer::updateNode() m_accDirty = 0; } +void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) { if (!node->m_fillNode) return; + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + QSGGeometry *g = &n->m_geometry; if (d->fillVertices.isEmpty()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } return; } if (d->fillGradientActive) { n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); if (d->effectiveDirty & DirtyFillGradient) { - // Make a copy of the data that will be accessed by the material on - // the render thread. - n->m_fillGradient = d->fillGradient; // Gradients are implemented via a texture-based material. n->markDirty(QSGNode::DirtyMaterial); // stop here if only the gradient changed; no need to touch the geometry @@ -414,14 +520,17 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - n->markDirty(QSGNode::DirtyGeometry); - QSGGeometry *g = &n->m_geometry; if (d->strokeVertices.isEmpty()) { - g->allocate(0, 0); + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } return; } + n->markDirty(QSGNode::DirtyGeometry); + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 4454b14a13..a4ed090004 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -55,11 +55,14 @@ #include #include #include -#include +#include QT_BEGIN_NAMESPACE class QQuickPathItemGenericNode; +class QQuickPathItemGenericStrokeFillNode; +class QQuickPathItemFillRunnable; +class QQuickPathItemStrokeRunnable; class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer { @@ -88,15 +91,29 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; void setRootNode(QQuickPathItemGenericNode *node); struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VerticesType; + typedef QVector IndicesType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VerticesType *fillVertices, + IndicesType *fillIndices); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VerticesType *strokeVertices, + const QSize &clipSize); private: + void maybeUpdateAsyncItem(); + struct VisualPathData { float strokeWidth; QPen pen; @@ -106,27 +123,60 @@ private: QPainterPath path; bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; - QVector fillVertices; - QVector fillIndices; - QVector strokeVertices; + VerticesType fillVertices; + IndicesType fillIndices; + VerticesType strokeVertices; int syncDirty; int effectiveDirty = 0; + QQuickPathItemFillRunnable *pendingFill = nullptr; + QQuickPathItemStrokeRunnable *pendingStroke = nullptr; }; - void triangulateFill(VisualPathData *d); - void triangulateStroke(VisualPathData *d); - + void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); QQuickItem *m_item; QQuickPathItemGenericNode *m_rootNode; - QTriangulatingStroker m_stroker; - QDashedStrokeProcessor m_dashStroker; QVector m_vp; int m_accDirty; }; +class QQuickPathItemFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + QPainterPath path; + QQuickPathItemGenericRenderer::Color4ub fillColor; + QQuickPathItemGenericRenderer::VerticesType fillVertices; + QQuickPathItemGenericRenderer::IndicesType fillIndices; + +Q_SIGNALS: + void done(QQuickPathItemFillRunnable *self); +}; + +class QQuickPathItemStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + QPainterPath path; + QPen pen; + QQuickPathItemGenericRenderer::Color4ub strokeColor; + QQuickPathItemGenericRenderer::VerticesType strokeVertices; + QSize clipSize; + +Q_SIGNALS: + void done(QQuickPathItemStrokeRunnable *self); +}; + class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode { public: diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 0c4357145a..9303f698ac 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -140,7 +140,7 @@ void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient * m_accDirty |= DirtyFillGradient; } -void QQuickPathItemNvprRenderer::endSync() +void QQuickPathItemNvprRenderer::endSync(bool) { } diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 067ecf7355..1617de17e6 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -90,7 +90,7 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 6c3cf73a56..63d4f8f6d4 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -158,7 +158,7 @@ void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradie m_accDirty |= DirtyBrush; } -void QQuickPathItemSoftwareRenderer::endSync() +void QQuickPathItemSoftwareRenderer::endSync(bool) { } diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 60b52e2def..38130d7301 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -82,7 +82,7 @@ public: void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync() override; + void endSync(bool async) override; void updateNode() override; -- cgit v1.2.3 From 72cfdac50b752b8f2d45929265cf4f09cb990bd2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 10:29:38 +0100 Subject: Add PathItem.status to report async processing progress Change-Id: I09ccdf9c542ebca56187284a0d1776b64c98bfe8 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 38 +++++++++++++++++++++-- src/quick/items/qquickpathitem_p.h | 11 +++++++ src/quick/items/qquickpathitem_p_p.h | 11 +++++++ src/quick/items/qquickpathitemgenericrenderer.cpp | 15 +++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 7 ++++- 5 files changed, 78 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 964d997806..306e79dc1e 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -362,6 +362,7 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() vpChanged(false), rendererType(QQuickPathItem::UnknownRenderer), async(false), + status(QQuickPathItem::Null), renderer(nullptr) { } @@ -378,6 +379,15 @@ void QQuickPathItemPrivate::_q_visualPathChanged() q->polish(); } +void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) +{ + Q_Q(QQuickPathItem); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + QQuickPathItem::QQuickPathItem(QQuickItem *parent) : QQuickItem(*(new QQuickPathItemPrivate), parent) { @@ -411,6 +421,12 @@ void QQuickPathItem::setAsynchronous(bool async) } } +QQuickPathItem::Status QQuickPathItem::status() const +{ + Q_D(const QQuickPathItem); + return d->status; +} + static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); @@ -492,8 +508,9 @@ void QQuickPathItem::updatePolish() emit rendererChanged(); } - // endSync() is where expensive calculations may happen, depending on the - // backend. Therefore do this only when the item is visible. + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. if (isVisible()) d->sync(); @@ -593,8 +610,20 @@ QSGNode *QQuickPathItemPrivate::createNode() return node; } +static void q_asyncPathItemReady(void *data) +{ + QQuickPathItemPrivate *self = static_cast(data); + self->setStatus(QQuickPathItem::Ready); +} + void QQuickPathItemPrivate::sync() { + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickPathItem::Processing); + renderer->setAsyncCallback(q_asyncPathItemReady, this); + } + const int count = vp.count(); renderer->beginSync(count); @@ -624,7 +653,10 @@ void QQuickPathItemPrivate::sync() dirty = 0; } - renderer->endSync(async); + renderer->endSync(useAsync); + + if (!useAsync) + setStatus(QQuickPathItem::Ready); } // ***** gradient support ***** diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 7c962d01fc..1b36348cd2 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -264,6 +264,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) Q_CLASSINFO("DefaultProperty", "visualPaths") @@ -276,6 +277,13 @@ public: }; Q_ENUM(RendererType) + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + QQuickPathItem(QQuickItem *parent = nullptr); ~QQuickPathItem(); @@ -284,6 +292,8 @@ public: bool asynchronous() const; void setAsynchronous(bool async); + Status status() const; + QQmlListProperty visualPaths(); protected: @@ -296,6 +306,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); + void statusChanged(); private: Q_DISABLE_COPY(QQuickPathItem) diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index faf7b1e451..3c63ec6dc2 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -65,6 +65,11 @@ class QSGPlainTexture; class QQuickAbstractPathRenderer { public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + virtual ~QQuickAbstractPathRenderer() { } // Gui thread @@ -80,11 +85,15 @@ public: qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } // Render thread, with gui blocked virtual void updateNode() = 0; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + class QQuickVisualPathPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickVisualPath) @@ -138,6 +147,7 @@ public: void sync(); void _q_visualPathChanged(); + void setStatus(QQuickPathItem::Status newStatus); static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } @@ -145,6 +155,7 @@ public: bool vpChanged; QQuickPathItem::RendererType rendererType; bool async; + QQuickPathItem::Status status; QQuickAbstractPathRenderer *renderer; QVector vp; }; diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 6e4c89742a..59b03e0fd2 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -210,8 +210,16 @@ void QQuickPathItemStrokeRunnable::run() emit done(this); } +void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + void QQuickPathItemGenericRenderer::endSync(bool async) { + bool didKickOffAsync = false; + for (int i = 0; i < m_vp.count(); ++i) { VisualPathData &d(m_vp[i]); if (!d.syncDirty) @@ -264,6 +272,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } r->deleteLater(); }); + didKickOffAsync = true; threadPool.start(r); } else { triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); @@ -290,6 +299,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } r->deleteLater(); }); + didKickOffAsync = true; threadPool.start(r); } else { triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, @@ -298,6 +308,9 @@ void QQuickPathItemGenericRenderer::endSync(bool async) } } } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); } void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() @@ -308,6 +321,8 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() } m_accDirty |= DirtyGeom; m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); } // the stroke/fill triangulation functions may be invoked either on the gui diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index a4ed090004..3193c27cb3 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -77,7 +77,8 @@ public: QQuickPathItemGenericRenderer(QQuickItem *item) : m_item(item), m_rootNode(nullptr), - m_accDirty(0) + m_accDirty(0), + m_asyncCallback(nullptr) { } void beginSync(int totalCount) override; @@ -92,6 +93,8 @@ public: qreal dashOffset, const QVector &dashPattern) override; void setFillGradient(int index, QQuickPathGradient *gradient) override; void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } void updateNode() override; @@ -140,6 +143,8 @@ private: QQuickPathItemGenericNode *m_rootNode; QVector m_vp; int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; }; class QQuickPathItemFillRunnable : public QObject, public QRunnable -- cgit v1.2.3 From 20c2c8b627b119b9ed72dd40f5b1f77786fad1de Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 11:42:44 +0100 Subject: Separate fill and stroke geometry updates in generic PathItem A change in parameters affecting fill or stroke only does not need touching the other set of geometry at all. Split DirtyGeom into DirtyFillGeom and DirtyStrokeGeom. As a result, neither triangulation nor the QSGGeometry update will happen for fill when only a stroke-related parameter changes, and vice versa. Change-Id: I839873899d687b6c1c8541c9186c9d93438b999a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 157 +++++++++++----------- src/quick/items/qquickpathitemgenericrenderer_p.h | 9 +- 2 files changed, 85 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 59b03e0fd2..367fac985e 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -121,7 +121,7 @@ void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) { VisualPathData &d(m_vp[index]); d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) @@ -137,7 +137,7 @@ void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) d.strokeWidth = w; if (w >= 0.0f) d.pen.setWidthF(w); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) @@ -151,7 +151,7 @@ void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::Fil { VisualPathData &d(m_vp[index]); d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyFillGeom; } void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) @@ -159,14 +159,14 @@ void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::Jo VisualPathData &d(m_vp[index]); d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) { VisualPathData &d(m_vp[index]); d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, @@ -178,7 +178,7 @@ void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath:: d.pen.setDashPattern(dashPattern); d.pen.setDashOffset(dashOffset); } - d.syncDirty |= DirtyGeom; + d.syncDirty |= DirtyStrokeGeom; } void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) @@ -241,70 +241,71 @@ void QQuickPathItemGenericRenderer::endSync(bool async) continue; } - if (d.syncDirty & DirtyGeom) { - static QThreadPool threadPool; - static bool threadPoolReady = false; - if (async && !threadPoolReady) { - threadPoolReady = true; - const int idealCount = QThread::idealThreadCount(); - threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - if (d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - threadPool.start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); - } + static QThreadPool threadPool; + static bool threadPoolReady = false; + + if (async && !threadPoolReady) { + threadPoolReady = true; + const int idealCount = QThread::idealThreadCount(); + threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + threadPool.start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); } - if (d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - threadPool.start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + threadPool.start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); } } } @@ -319,7 +320,7 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() if (d.pendingFill || d.pendingStroke) return; } - m_accDirty |= DirtyGeom; + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; m_item->update(); if (m_asyncCallback) m_asyncCallback(m_asyncCallbackData); @@ -427,7 +428,7 @@ void QQuickPathItemGenericRenderer::updateNode() QQuickPathItemGenericNode *node = *nodePtr; if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyGeom | DirtyColor | DirtyFillGradient; + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; if (!d.effectiveDirty) continue; @@ -441,7 +442,7 @@ void QQuickPathItemGenericRenderer::updateNode() node->appendChildNode(node->m_fillNode); if (node->m_strokeNode) node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyFillGeom; } if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { @@ -450,7 +451,7 @@ void QQuickPathItemGenericRenderer::updateNode() } else if (!node->m_strokeNode) { node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyGeom; + d.effectiveDirty |= DirtyStrokeGeom; } updateFillNode(&d, node); @@ -483,6 +484,8 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath { if (!node->m_fillNode) return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; // Make a copy of the data that will be accessed by the material on // the render thread. This must be done even when we bail out below. @@ -504,13 +507,13 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath // Gradients are implemented via a texture-based material. n->markDirty(QSGNode::DirtyMaterial); // stop here if only the gradient changed; no need to touch the geometry - if (!(d->effectiveDirty & DirtyGeom)) + if (!(d->effectiveDirty & DirtyFillGeom)) return; } } else { n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); @@ -531,7 +534,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa { if (!node->m_strokeNode) return; - if (d->effectiveDirty == DirtyFillGradient) // not applicable + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; @@ -546,7 +549,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa n->markDirty(QSGNode::DirtyGeometry); - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyGeom)) { + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 3193c27cb3..17c1f73310 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -68,10 +68,11 @@ class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer { public: enum Dirty { - DirtyGeom = 0x01, - DirtyColor = 0x02, - DirtyFillGradient = 0x04, - DirtyList = 0x08 + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty }; QQuickPathItemGenericRenderer(QQuickItem *item) -- cgit v1.2.3 From 77614c6c48ce8caf5697afb53a26caf1b165fed3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 10 Jan 2017 17:17:11 +0100 Subject: Correct software PathItem bounding rect Reporting the QQuickItem geometry is incorrect. Report the area touched by the path instead. This fixes the tiger in the pathitem example. Change-Id: Ib443f442411befabe7864eff6473604926527f4e Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 9 ++++++++- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 1 + src/quick/scenegraph/coreapi/qsgrendernode.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 63d4f8f6d4..46ebcbfe6d 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -180,6 +180,8 @@ void QQuickPathItemSoftwareRenderer::updateNode() if (listChanged) m_node->m_vp.resize(count); + m_node->m_boundingRect = QRectF(); + for (int i = 0; i < count; ++i) { VisualPathGuiData &src(m_vp[i]); QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); @@ -201,6 +203,11 @@ void QQuickPathItemSoftwareRenderer::updateNode() dst.brush = src.brush; src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; } m_node->markDirty(QSGNode::DirtyMaterial); @@ -256,7 +263,7 @@ QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const QRectF QQuickPathItemSoftwareRenderNode::rect() const { - return QRect(0, 0, m_item->width(), m_item->height()); + return m_boundingRect; } QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 38130d7301..64280b436e 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -125,6 +125,7 @@ private: QBrush brush; }; QVector m_vp; + QRectF m_boundingRect; friend class QQuickPathItemSoftwareRenderer; }; diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index 3007e0000c..a8954848d6 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -267,6 +267,10 @@ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const For rendernodes covering the entire area of a corresponding QQuickItem the return value will be (0, 0, item->width(), item->height()). + \note Nodes are also free to render outside the boundaries specified by the + item's width and height, since the scenegraph nodes are not bounded by the + QQuickItem geometry, as long as this is reported correctly from this function. + \sa flags() */ QRectF QSGRenderNode::rect() const -- cgit v1.2.3 From a2252ba7b3942bbafbbb8f4db144f2cfbf0787ab Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 22 Jan 2017 16:48:15 +0100 Subject: Fix case when async PathItem goes down before finish Marking the work orphaned (to be ignored) is sufficient since runnables exist independently of the renderer or item instances. (even if the lambda executes after the entire Quick infra is gone, we are still fine, as long as we bail out in time and do not dereference the captured this) Change-Id: I0cfb09aaa8b363191ff576b17a88bb7c1c38f3ad Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 12 ++++++++++++ src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + 2 files changed, 13 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 367fac985e..e36654ad0c 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -104,6 +104,16 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) setMaterial(m_material); } +QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() +{ + for (VisualPathData &d : m_vp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + // sync, and so triangulation too, happens on the gui thread // - except when async is set, in which case triangulation is moved to worker threads @@ -263,6 +273,8 @@ void QQuickPathItemGenericRenderer::endSync(bool async) // Unlikely in practice but in theory m_vp could be // resized. Therefore, capture 'i' instead of 'd'. QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). if (!r->orphaned && i < m_vp.count()) { VisualPathData &d(m_vp[i]); d.fillVertices = r->fillVertices; diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 17c1f73310..ca05492841 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -81,6 +81,7 @@ public: m_accDirty(0), m_asyncCallback(nullptr) { } + ~QQuickPathItemGenericRenderer(); void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; -- cgit v1.2.3 From b9f060f6fae5734216d7088afd0e3b52165551c5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 11 Jan 2017 12:49:33 +0100 Subject: Path generic PathItem work with both ushort and uint index type Default to uint but keep supporting systems without ElementIndexUint. Change-Id: Ic15f66408e2e2ffb6406c7e854fa65ee1e0f56b3 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 100 ++++++++++++++++++---- src/quick/items/qquickpathitemgenericrenderer_p.h | 41 ++++++--- 2 files changed, 111 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index e36654ad0c..bf9506ba65 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -40,9 +40,15 @@ #include "qquickpathitemgenericrenderer_p.h" #include #include -#include #include +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + QT_BEGIN_NAMESPACE static const qreal SCALE = 100; @@ -69,17 +75,22 @@ static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QCol } QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0), + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), m_window(window), m_material(nullptr) { - setGeometry(&m_geometry); + setGeometry(m_geometry); activateMaterial(MatSolidColor); #ifdef QSG_RUNTIME_DESCRIPTION qsgnode_set_description(this, QLatin1String("stroke-fill")); #endif } +QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() +{ + delete m_geometry; +} + void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) { switch (m) { @@ -104,6 +115,39 @@ void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) setMaterial(m_material); } +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() { for (VisualPathData &d : m_vp) { @@ -210,7 +254,7 @@ void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradien void QQuickPathItemFillRunnable::run() { - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices); + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); emit done(this); } @@ -262,6 +306,8 @@ void QQuickPathItemGenericRenderer::endSync(bool async) if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); if (async) { QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; r->setAutoDelete(false); @@ -270,6 +316,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) d.pendingFill = r; r->path = d.path; r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); // Unlikely in practice but in theory m_vp could be // resized. Therefore, capture 'i' instead of 'd'. QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { @@ -279,6 +326,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) VisualPathData &d(m_vp[i]); d.fillVertices = r->fillVertices; d.fillIndices = r->fillIndices; + d.indexType = r->indexType; d.pendingFill = nullptr; d.effectiveDirty |= DirtyFillGeom; maybeUpdateAsyncItem(); @@ -288,7 +336,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) didKickOffAsync = true; threadPool.start(r); } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices); + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); } } @@ -342,12 +390,14 @@ void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() // thread or some worker thread and must thus be self-contained. void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, const Color4ub &fillColor, - VerticesType *fillVertices, - IndicesType *fillIndices) + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) { const QVectorPath &vp = qtVectorPathForPath(path); - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE)); + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE), 1, supportsElementIndexUint); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 fillVertices->resize(vertexCount); ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); @@ -355,21 +405,25 @@ void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, for (int i = 0; i < vertexCount; ++i) vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, fillColor); - fillIndices->resize(ts.indices.size()); - quint16 *idst = fillIndices->data(); + size_t indexByteSize; if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - memcpy(idst, ts.indices.data(), fillIndices->count() * sizeof(quint16)); + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); } else { - const quint32 *isrc = (const quint32 *) ts.indices.data(); - for (int i = 0; i < fillIndices->count(); ++i) - idst[i] = isrc[i]; + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); } void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, const QPen &pen, const Color4ub &strokeColor, - VerticesType *strokeVertices, + VertexContainerType *strokeVertices, const QSize &clipSize) { const QVectorPath &vp = qtVectorPathForPath(path); @@ -504,7 +558,7 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; updateShadowDataInNode(d, n); - QSGGeometry *g = &n->m_geometry; + QSGGeometry *g = n->m_geometry; if (d->fillVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); @@ -534,7 +588,17 @@ void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPath } } - g->allocate(d->fillVertices.count(), d->fillIndices.count()); + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } g->setDrawingMode(QSGGeometry::DrawTriangles); memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); @@ -550,7 +614,7 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa return; QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = &n->m_geometry; + QSGGeometry *g = n->m_geometry; if (d->strokeVertices.isEmpty()) { if (g->vertexCount() || g->indexCount()) { g->allocate(0, 0); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index ca05492841..045c52d610 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -55,6 +55,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -77,6 +78,7 @@ public: QQuickPathItemGenericRenderer(QQuickItem *item) : m_item(item), + m_api(QSGRendererInterface::Unknown), m_rootNode(nullptr), m_accDirty(0), m_asyncCallback(nullptr) @@ -103,17 +105,19 @@ public: void setRootNode(QQuickPathItemGenericNode *node); struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VerticesType; - typedef QVector IndicesType; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; static void triangulateFill(const QPainterPath &path, const Color4ub &fillColor, - VerticesType *fillVertices, - IndicesType *fillIndices); + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); static void triangulateStroke(const QPainterPath &path, const QPen &pen, const Color4ub &strokeColor, - VerticesType *strokeVertices, + VertexContainerType *strokeVertices, const QSize &clipSize); private: @@ -128,9 +132,10 @@ private: QPainterPath path; bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; - VerticesType fillVertices; - IndicesType fillIndices; - VerticesType strokeVertices; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; int syncDirty; int effectiveDirty = 0; QQuickPathItemFillRunnable *pendingFill = nullptr; @@ -142,6 +147,7 @@ private: void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; QQuickPathItemGenericNode *m_rootNode; QVector m_vp; int m_accDirty; @@ -157,10 +163,16 @@ public: void run() override; bool orphaned = false; + + // input QPainterPath path; QQuickPathItemGenericRenderer::Color4ub fillColor; - QQuickPathItemGenericRenderer::VerticesType fillVertices; - QQuickPathItemGenericRenderer::IndicesType fillIndices; + bool supportsElementIndexUint; + + // output + QQuickPathItemGenericRenderer::VertexContainerType fillVertices; + QQuickPathItemGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; Q_SIGNALS: void done(QQuickPathItemFillRunnable *self); @@ -174,12 +186,16 @@ public: void run() override; bool orphaned = false; + + // input QPainterPath path; QPen pen; QQuickPathItemGenericRenderer::Color4ub strokeColor; - QQuickPathItemGenericRenderer::VerticesType strokeVertices; QSize clipSize; + // output + QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; + Q_SIGNALS: void done(QQuickPathItemStrokeRunnable *self); }; @@ -188,6 +204,7 @@ class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode { public: QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); + ~QQuickPathItemGenericStrokeFillNode(); enum Material { MatSolidColor, @@ -202,7 +219,7 @@ public: QQuickPathItemGradientCache::GradientDesc m_fillGradient; private: - QSGGeometry m_geometry; + QSGGeometry *m_geometry; QQuickWindow *m_window; QSGMaterial *m_material; QScopedPointer m_solidColorMaterial; -- cgit v1.2.3 From 5daaec1e193bc69f55d4ddbfef8911ce9810ea28 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 11 Jan 2017 17:05:14 +0100 Subject: Improve stencil clipping with NVPR So that it actually performs when clipping the tiger. Add an example. Change-Id: I7c1c6244710febdb6b02852ebca094665adec417 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitemnvprrenderer.cpp | 54 ++++++++++++++------------ src/quick/items/qquickpathitemnvprrenderer_p.h | 9 +++-- 3 files changed, 36 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 306e79dc1e..b07a7a6c06 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -587,7 +587,7 @@ QSGNode *QQuickPathItemPrivate::createNode() #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: if (QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode(q); + node = new QQuickPathItemNvprRenderNode; static_cast(renderer)->setNode( static_cast(node)); } else { diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 9303f698ac..13fab2dc76 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -397,11 +397,6 @@ bool QQuickPathItemNvprRenderNode::nvprInited = false; QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; -QQuickPathItemNvprRenderNode::QQuickPathItemNvprRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() { releaseResources(); @@ -528,6 +523,9 @@ void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) // count == 0 -> no dash nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); } + + if (d->dirty) + d->fallbackValid = false; } void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) @@ -568,23 +566,28 @@ void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) { - QQuickWindow *w = m_item->window(); - const qreal dpr = w->effectiveDevicePixelRatio(); - QSize itemSize = QSize(m_item->width(), m_item->height()) * dpr; - QSize rtSize = w->renderTargetSize(); - if (rtSize.isEmpty()) - rtSize = w->size() * dpr; + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); - if (d->fallbackFbo && d->fallbackFbo->size() != itemSize) { + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { delete d->fallbackFbo; d->fallbackFbo = nullptr; } if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(itemSize, QOpenGLFramebufferObject::CombinedDepthStencil); + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); if (!d->fallbackFbo->bind()) return; - f->glViewport(0, 0, itemSize.width(), itemSize.height()); + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); f->glDisable(GL_DEPTH_TEST); f->glClearColor(0, 0, 0, 0); f->glClearStencil(0); @@ -592,16 +595,20 @@ void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - nvpr.matrixLoadIdentity(GL_PATH_MODELVIEW_NV); + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); QMatrix4x4 proj; - proj.ortho(0, itemSize.width(), itemSize.height(), 0, 1, -1); + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); renderFill(d); d->fallbackFbo->release(); f->glEnable(GL_DEPTH_TEST); - f->glViewport(0, 0, rtSize.width(), rtSize.height()); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; } void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) @@ -655,8 +662,8 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) for (VisualPathRenderData &d : m_vp) { updatePath(&d); - const bool hasFill = !qFuzzyIsNull(d.fillColor.w()) || d.fillGradientActive; - const bool hasStroke = d.strokeWidth >= 0.0f && !qFuzzyIsNull(d.strokeColor.w()); + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); if (hasFill && stencilClip) { // Fall back to a texture when complex clipping is in use and we have @@ -686,8 +693,10 @@ void QQuickPathItemNvprRenderNode::render(const RenderState *state) m_fallbackBlitter.create(); f->glStencilFunc(GL_EQUAL, sv, 0xFF); f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), *matrix(), + *state->projectionMatrix(), mv, inheritedOpacity()); } } @@ -729,11 +738,6 @@ QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer } -QRectF QQuickPathItemNvprRenderNode::rect() const -{ - return QRect(0, 0, m_item->width(), m_item->height()); -} - bool QQuickPathItemNvprRenderNode::isSupported() { static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 1617de17e6..61f8b5ebb9 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -176,14 +176,12 @@ private: class QQuickPathItemNvprRenderNode : public QSGRenderNode { public: - QQuickPathItemNvprRenderNode(QQuickPathItem *item); ~QQuickPathItemNvprRenderNode(); void render(const RenderState *state) override; void releaseResources() override; StateFlags changedStates() const override; RenderingFlags flags() const override; - QRectF rect() const override; static bool isSupported(); @@ -204,6 +202,12 @@ private: bool fillGradientActive; QQuickPathItemGradientCache::GradientDesc fillGradient; QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } }; void updatePath(VisualPathRenderData *d); @@ -216,7 +220,6 @@ private: static QQuickNvprFunctions nvpr; static QQuickNvprMaterialManager mtlmgr; - QQuickPathItem *m_item; QQuickNvprBlitter m_fallbackBlitter; QOpenGLExtraFunctions *f = nullptr; -- cgit v1.2.3 From 364de6e01c88cef1ec2f1a6d13dc8d356e159ad0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 16 Jan 2017 11:26:52 +0100 Subject: PathItem API cleanup: rename visualPaths to elements visualPaths looks somewhat odd. Not that it matters much since it is the default property anyway, but pick the commonly used "elements" instead. This also allows easier renaming of VisualPath to something better in the future, in case a suitable candidate comes up. Change-Id: I0cdc18cdec49ff821a75f45073598b31f5de9bf8 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 2 +- src/quick/items/qquickpathitem_p.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index b07a7a6c06..c5ad73e960 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -465,7 +465,7 @@ static void vpe_clear(QQmlListProperty *property) d->_q_visualPathChanged(); } -QQmlListProperty QQuickPathItem::visualPaths() +QQmlListProperty QQuickPathItem::elements() { return QQmlListProperty(this, nullptr, diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 1b36348cd2..9ca1418d9e 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -265,8 +265,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty visualPaths READ visualPaths) - Q_CLASSINFO("DefaultProperty", "visualPaths") + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") public: enum RendererType { @@ -294,7 +294,7 @@ public: Status status() const; - QQmlListProperty visualPaths(); + QQmlListProperty elements(); protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; -- cgit v1.2.3 From 5c2ba6db53b2190160081695faff521bc367e33d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Sun, 22 Jan 2017 17:25:17 +0100 Subject: Destroy the async PathItem work pool properly Avoid getting warnings from QWaitCondition on Windows by managing the lifetime of the QThreadPool ourselves. Change-Id: Idc3545a2336f7971bd382560aa958a325ac69fc6 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index bf9506ba65..3de01a5bc7 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -270,6 +270,14 @@ void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), v m_asyncCallbackData = data; } +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + void QQuickPathItemGenericRenderer::endSync(bool async) { bool didKickOffAsync = false; @@ -295,13 +303,11 @@ void QQuickPathItemGenericRenderer::endSync(bool async) continue; } - static QThreadPool threadPool; - static bool threadPoolReady = false; - - if (async && !threadPoolReady) { - threadPoolReady = true; + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; const int idealCount = QThread::idealThreadCount(); - threadPool.setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); } if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { @@ -334,7 +340,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) r->deleteLater(); }); didKickOffAsync = true; - threadPool.start(r); + pathWorkThreadPool->start(r); } else { triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); } @@ -362,7 +368,7 @@ void QQuickPathItemGenericRenderer::endSync(bool async) r->deleteLater(); }); didKickOffAsync = true; - threadPool.start(r); + pathWorkThreadPool->start(r); } else { triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, QSize(m_item->width(), m_item->height())); -- cgit v1.2.3 From a48244d5aa1d14c286b7cd39afebcfff9c9dcb60 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 24 Jan 2017 18:27:27 +0100 Subject: JS API for defining static paths and draw params In order to avoid the over head of one QObject for each VisualPath, Path and PathXxxx element, provide an optional JavaScript API. PathItem { VisualPath { strokeWidth: 4; fillColor: "blue"; dashPattern: [ 1, 2, 3, 4]; capStyle: RoundCap path: Path { PathMove { x: 100; y: 200 } PathLine { x: 300; y: 300 } } } can now also be written as, at least when the path and the stroke/fill params are static and do not need changing/animating later on: PathItem { Component.onCompleted: { var path = newPath(); var sfp = newStrokeFillParams(); path.moveTo(100, 200); path.lineTo(300, 300); sfp.strokeWidth = 4; sfp.fillColor = "blue"; sfp.dashPattern = [ 1, 2, 3, 4 ]; sfp.capStyle = VisualPath.RoundCap; appendVisualPath(path, sfp); commitVisualPaths(); } } In order to emphasize the difference from an imperative API (like context2d), keep the path and the path stroke/fill parameters separate. To preserve our sanity, extras like gradients are not mapped to JavaScript, instead, one still references an QML-defined object from properties like fillGradient. The objects from newPath() and newStrokeFillParams() are reusable by calling clear(). This avoids the need for multiple temp objects when there are multiple paths. Add a simple test and a hidden stress test with the tiger to the manual test. Change-Id: I3b1e275bacf8c8fc52f585fbed5d6f9354d5ae8e Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 814 +++++++++++++++++++-- src/quick/items/qquickpathitem_p.h | 7 + src/quick/items/qquickpathitem_p_p.h | 103 ++- src/quick/items/qquickpathitemgenericrenderer.cpp | 7 + src/quick/items/qquickpathitemgenericrenderer_p.h | 1 + src/quick/items/qquickpathitemnvprrenderer.cpp | 84 +++ src/quick/items/qquickpathitemnvprrenderer_p.h | 2 + src/quick/items/qquickpathitemsoftwarerenderer.cpp | 8 + src/quick/items/qquickpathitemsoftwarerenderer_p.h | 1 + 9 files changed, 938 insertions(+), 89 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index c5ad73e960..61216be9b0 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -43,15 +43,20 @@ #include "qquickpathitemnvprrenderer_p.h" #include "qquickpathitemsoftwarerenderer_p.h" #include +#include #include #include +#include +#include +#include +#include +#include + QT_BEGIN_NAMESPACE -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll), - strokeColor(Qt::white), +QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() + : strokeColor(Qt::white), strokeWidth(1), fillColor(Qt::white), fillRule(QQuickVisualPath::OddEvenFill), @@ -65,6 +70,56 @@ QQuickVisualPathPrivate::QQuickVisualPathPrivate() dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space } +QPainterPath QQuickPathItemPath::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickPathItemPath::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +QQuickVisualPathPrivate::QQuickVisualPathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + QQuickVisualPath::QQuickVisualPath(QObject *parent) : QObject(*(new QQuickVisualPathPrivate), parent) { @@ -108,14 +163,14 @@ void QQuickVisualPathPrivate::_q_pathChanged() QColor QQuickVisualPath::strokeColor() const { Q_D(const QQuickVisualPath); - return d->strokeColor; + return d->sfp.strokeColor; } void QQuickVisualPath::setStrokeColor(const QColor &color) { Q_D(QQuickVisualPath); - if (d->strokeColor != color) { - d->strokeColor = color; + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; emit strokeColorChanged(); emit changed(); @@ -125,14 +180,14 @@ void QQuickVisualPath::setStrokeColor(const QColor &color) qreal QQuickVisualPath::strokeWidth() const { Q_D(const QQuickVisualPath); - return d->strokeWidth; + return d->sfp.strokeWidth; } void QQuickVisualPath::setStrokeWidth(qreal w) { Q_D(QQuickVisualPath); - if (d->strokeWidth != w) { - d->strokeWidth = w; + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; emit strokeWidthChanged(); emit changed(); @@ -142,14 +197,14 @@ void QQuickVisualPath::setStrokeWidth(qreal w) QColor QQuickVisualPath::fillColor() const { Q_D(const QQuickVisualPath); - return d->fillColor; + return d->sfp.fillColor; } void QQuickVisualPath::setFillColor(const QColor &color) { Q_D(QQuickVisualPath); - if (d->fillColor != color) { - d->fillColor = color; + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; emit fillColorChanged(); emit changed(); @@ -159,14 +214,14 @@ void QQuickVisualPath::setFillColor(const QColor &color) QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const { Q_D(const QQuickVisualPath); - return d->fillRule; + return d->sfp.fillRule; } void QQuickVisualPath::setFillRule(FillRule fillRule) { Q_D(QQuickVisualPath); - if (d->fillRule != fillRule) { - d->fillRule = fillRule; + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; emit fillRuleChanged(); emit changed(); @@ -176,14 +231,14 @@ void QQuickVisualPath::setFillRule(FillRule fillRule) QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const { Q_D(const QQuickVisualPath); - return d->joinStyle; + return d->sfp.joinStyle; } void QQuickVisualPath::setJoinStyle(JoinStyle style) { Q_D(QQuickVisualPath); - if (d->joinStyle != style) { - d->joinStyle = style; + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit joinStyleChanged(); emit changed(); @@ -193,14 +248,14 @@ void QQuickVisualPath::setJoinStyle(JoinStyle style) int QQuickVisualPath::miterLimit() const { Q_D(const QQuickVisualPath); - return d->miterLimit; + return d->sfp.miterLimit; } void QQuickVisualPath::setMiterLimit(int limit) { Q_D(QQuickVisualPath); - if (d->miterLimit != limit) { - d->miterLimit = limit; + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit miterLimitChanged(); emit changed(); @@ -210,14 +265,14 @@ void QQuickVisualPath::setMiterLimit(int limit) QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const { Q_D(const QQuickVisualPath); - return d->capStyle; + return d->sfp.capStyle; } void QQuickVisualPath::setCapStyle(CapStyle style) { Q_D(QQuickVisualPath); - if (d->capStyle != style) { - d->capStyle = style; + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyStyle; emit capStyleChanged(); emit changed(); @@ -227,14 +282,14 @@ void QQuickVisualPath::setCapStyle(CapStyle style) QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const { Q_D(const QQuickVisualPath); - return d->strokeStyle; + return d->sfp.strokeStyle; } void QQuickVisualPath::setStrokeStyle(StrokeStyle style) { Q_D(QQuickVisualPath); - if (d->strokeStyle != style) { - d->strokeStyle = style; + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit strokeStyleChanged(); emit changed(); @@ -244,14 +299,14 @@ void QQuickVisualPath::setStrokeStyle(StrokeStyle style) qreal QQuickVisualPath::dashOffset() const { Q_D(const QQuickVisualPath); - return d->dashOffset; + return d->sfp.dashOffset; } void QQuickVisualPath::setDashOffset(qreal offset) { Q_D(QQuickVisualPath); - if (d->dashOffset != offset) { - d->dashOffset = offset; + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashOffsetChanged(); emit changed(); @@ -261,14 +316,14 @@ void QQuickVisualPath::setDashOffset(qreal offset) QVector QQuickVisualPath::dashPattern() const { Q_D(const QQuickVisualPath); - return d->dashPattern; + return d->sfp.dashPattern; } void QQuickVisualPath::setDashPattern(const QVector &array) { Q_D(QQuickVisualPath); - if (d->dashPattern != array) { - d->dashPattern = array; + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; d->dirty |= QQuickVisualPathPrivate::DirtyDash; emit dashPatternChanged(); emit changed(); @@ -278,19 +333,19 @@ void QQuickVisualPath::setDashPattern(const QVector &array) QQuickPathGradient *QQuickVisualPath::fillGradient() const { Q_D(const QQuickVisualPath); - return d->fillGradient; + return d->sfp.fillGradient; } void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) { Q_D(QQuickVisualPath); - if (d->fillGradient != gradient) { - if (d->fillGradient) - qmlobject_disconnect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->fillGradient = gradient; - if (d->fillGradient) - qmlobject_connect(d->fillGradient, QQuickPathGradient, SIGNAL(updated()), + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; emit changed(); @@ -430,14 +485,14 @@ QQuickPathItem::Status QQuickPathItem::status() const static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->vp.at(index); + return d->qmlData.vp.at(index); } static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) { QQuickPathItem *item = static_cast(property->object); QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->vp.append(obj); + d->qmlData.vp.append(obj); if (d->componentComplete) { QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); @@ -448,7 +503,7 @@ static void vpe_append(QQmlListProperty *property, QQuickVisua static int vpe_count(QQmlListProperty *property) { QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->vp.count(); + return d->qmlData.vp.count(); } static void vpe_clear(QQmlListProperty *property) @@ -456,10 +511,10 @@ static void vpe_clear(QQmlListProperty *property) QQuickPathItem *item = static_cast(property->object); QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - for (QQuickVisualPath *p : d->vp) + for (QQuickVisualPath *p : d->qmlData.vp) QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->vp.clear(); + d->qmlData.vp.clear(); if (d->componentComplete) d->_q_visualPathChanged(); @@ -486,7 +541,7 @@ void QQuickPathItem::componentComplete() Q_D(QQuickPathItem); d->componentComplete = true; - for (QQuickVisualPath *p : d->vp) + for (QQuickVisualPath *p : d->qmlData.vp) connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); d->_q_visualPathChanged(); @@ -624,36 +679,60 @@ void QQuickPathItemPrivate::sync() renderer->setAsyncCallback(q_asyncPathItemReady, this); } - const int count = vp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickVisualPath *p = vp[i]; - int &dirty(QQuickVisualPathPrivate::get(p)->dirty); - - if (dirty & QQuickVisualPathPrivate::DirtyPath) - renderer->setPath(i, p->path()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) - renderer->setStrokeColor(i, p->strokeColor()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) - renderer->setStrokeWidth(i, p->strokeWidth()); - if (dirty & QQuickVisualPathPrivate::DirtyFillColor) - renderer->setFillColor(i, p->fillColor()); - if (dirty & QQuickVisualPathPrivate::DirtyFillRule) - renderer->setFillRule(i, p->fillRule()); - if (dirty & QQuickVisualPathPrivate::DirtyStyle) { - renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); - renderer->setCapStyle(i, p->capStyle()); + if (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // VisualPath and Path. + const int count = qmlData.vp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickVisualPath *p = qmlData.vp[i]; + int &dirty(QQuickVisualPathPrivate::get(p)->dirty); + + if (dirty & QQuickVisualPathPrivate::DirtyPath) + renderer->setPath(i, p->path()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickVisualPathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickVisualPathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickVisualPathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickVisualPathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; } - if (dirty & QQuickVisualPathPrivate::DirtyDash) - renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); - if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) - renderer->setFillGradient(i, p->fillGradient()); - dirty = 0; - } + renderer->endSync(useAsync); + } else { + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } - renderer->endSync(useAsync); + renderer->endSync(useAsync); + } if (!useAsync) setStatus(QQuickPathItem::Ready); @@ -935,6 +1014,593 @@ QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) #endif // QT_NO_OPENGL +// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** + +class QQuickPathItemJSEngineData : public QV8Engine::Deletable +{ +public: + QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); + + QV4::PersistentValue pathProto; + QV4::PersistentValue strokeFillParamsProto; +}; + +V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) + +namespace QV4 { +namespace Heap { + +struct QQuickPathItemJSPathPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSPath : Object { + void init() { Object::init(); } + QQuickPathItemPathObject *obj; +}; + +struct QQuickPathItemJSStrokeFillParamsPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSStrokeFillParams : Object { + void init() { Object::init(); } + QQuickPathItemStrokeFillParamsObject *obj; +}; + +} // namespace Heap +} // namespace QV4 + +struct QQuickPathItemJSPathPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); + o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); + o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); + o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); + o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); + +struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); + +struct QQuickPathItemJSPath : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); + +struct QQuickPathItemJSStrokeFillParams : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) + + static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); + +void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::MoveTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::LineTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 4) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::QuadTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // cx + p->path.coords.append(v[1].toNumber()); // cy + p->path.coords.append(v[2].toNumber()); // x + p->path.coords.append(v[3].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 6) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::CubicTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // c1x + p->path.coords.append(v[1].toNumber()); // c1y + p->path.coords.append(v[2].toNumber()); // c2x + p->path.coords.append(v[3].toNumber()); // c2y + p->path.coords.append(v[4].toNumber()); // x + p->path.coords.append(v[5].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 7) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::ArcTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // radiusX + p->path.coords.append(v[1].toNumber()); // radiusY + p->path.coords.append(v[2].toNumber()); // xAxisRotation + p->path.coords.append(v[3].toNumber()); // x + p->path.coords.append(v[4].toNumber()); // y + p->path.coords.append(v[5].toNumber()); // sweepFlag + p->path.coords.append(v[6].toNumber()); // largeArc + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +extern QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp + +static inline QString qt_color_string(const QColor &color) +{ + if (color.alpha() == 255) + return color.name(); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.strokeColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.strokeWidth = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.fillColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); +} + +void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.miterLimit = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.dashOffset = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + a->arrayReserve(p->sfp.dashPattern.count()); + QV4::ScopedValue v(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) + a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); + a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); + + scope.result = a.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isObject()) { + QV4::Scoped ao(scope, value); + if (!!ao) { + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + p->sfp.dashPattern.resize(ao->getLength()); + QV4::ScopedValue val(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { + val = ao->getIndexed(i); + p->sfp.dashPattern[i] = val->toNumber(); + } + } + } + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = r->d()->obj->v4fillGradient.value(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + QV4::Scoped qobjectWrapper(scope, value); + if (!!qobjectWrapper) { + if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { + r->d()->obj->v4fillGradient.set(scope.engine, value); + r->d()->obj->sfp.fillGradient = grad; + } + } else { + r->d()->obj->v4fillGradient.set(scope.engine, nullptr); + r->d()->obj->sfp.fillGradient = nullptr; + } + + scope.result = QV4::Encode::undefined(); +} + +QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) +{ + QV4::Scope scope(v4); + + QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); + pathProto = proto; + + proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); + + proto->defineAccessorProperty(QStringLiteral("strokeColor"), + QQuickPathItemJSStrokeFillParams::method_get_strokeColor, + QQuickPathItemJSStrokeFillParams::method_set_strokeColor); + proto->defineAccessorProperty(QStringLiteral("strokeWidth"), + QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, + QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); + proto->defineAccessorProperty(QStringLiteral("fillColor"), + QQuickPathItemJSStrokeFillParams::method_get_fillColor, + QQuickPathItemJSStrokeFillParams::method_set_fillColor); + proto->defineAccessorProperty(QStringLiteral("fillRule"), + QQuickPathItemJSStrokeFillParams::method_get_fillRule, + QQuickPathItemJSStrokeFillParams::method_set_fillRule); + proto->defineAccessorProperty(QStringLiteral("joinStyle"), + QQuickPathItemJSStrokeFillParams::method_get_joinStyle, + QQuickPathItemJSStrokeFillParams::method_set_joinStyle); + proto->defineAccessorProperty(QStringLiteral("miterLimit"), + QQuickPathItemJSStrokeFillParams::method_get_miterLimit, + QQuickPathItemJSStrokeFillParams::method_set_miterLimit); + proto->defineAccessorProperty(QStringLiteral("capStyle"), + QQuickPathItemJSStrokeFillParams::method_get_capStyle, + QQuickPathItemJSStrokeFillParams::method_set_capStyle); + proto->defineAccessorProperty(QStringLiteral("strokeStyle"), + QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, + QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); + proto->defineAccessorProperty(QStringLiteral("dashOffset"), + QQuickPathItemJSStrokeFillParams::method_get_dashOffset, + QQuickPathItemJSStrokeFillParams::method_set_dashOffset); + proto->defineAccessorProperty(QStringLiteral("dashPattern"), + QQuickPathItemJSStrokeFillParams::method_get_dashPattern, + QQuickPathItemJSStrokeFillParams::method_set_dashPattern); + proto->defineAccessorProperty(QStringLiteral("fillGradient"), + QQuickPathItemJSStrokeFillParams::method_get_fillGradient, + QQuickPathItemJSStrokeFillParams::method_set_fillGradient); + + strokeFillParamsProto = proto; +} + +void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->pathProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +void QQuickPathItemPathObject::clear() +{ + path = QQuickPathItemPath(); +} + +void QQuickPathItemStrokeFillParamsObject::clear() +{ + sfp = QQuickPathItemStrokeFillParams(); + if (!v4fillGradient.isNullOrUndefined()) + v4fillGradient.set(v4fillGradient.engine(), nullptr); +} + +void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +void QQuickPathItem::newPath(QQmlV4Function *args) +{ + QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) +{ + QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->jsData.paths.clear(); + d->jsData.sfp.clear(); +} + +void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->_q_visualPathChanged(); +} + +void QQuickPathItem::appendVisualPath(QQmlV4Function *args) +{ + if (args->length() < 2) + return; + + Q_D(QQuickPathItem); + QV4::Scope scope(args->v4engine()); + QV4::Scoped jsp(scope, (*args)[0]); + QV4::Scoped jssfp(scope, (*args)[1]); + + const QQuickPathItemPath &path(jsp->d()->obj->path); + const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); + + d->jsData.paths.append(path); + d->jsData.sfp.append(sfp); +} + QT_END_NAMESPACE #include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 9ca1418d9e..991ab7a2bf 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -55,6 +55,7 @@ #include #include +#include #include QT_REQUIRE_CONFIG(quick_path); @@ -296,6 +297,12 @@ public: QQmlListProperty elements(); + Q_INVOKABLE void newPath(QQmlV4Function *args); + Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); + Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); + protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; void updatePolish() override; diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 3c63ec6dc2..091a453c0b 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -62,6 +62,39 @@ QT_BEGIN_NAMESPACE class QSGPlainTexture; +struct QQuickPathItemPath +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickPathItemStrokeFillParams +{ + QQuickPathItemStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickVisualPath::FillRule fillRule; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + class QQuickAbstractPathRenderer { public: @@ -74,7 +107,14 @@ public: // Gui thread virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; + // - stroke/fill parameters virtual void setStrokeColor(int index, const QColor &color) = 0; virtual void setStrokeWidth(int index, qreal w) = 0; virtual void setFillColor(int index, const QColor &color) = 0; @@ -84,9 +124,6 @@ public: virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, qreal dashOffset, const QVector &dashPattern) = 0; virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } // Render thread, with gui blocked virtual void updateNode() = 0; @@ -121,17 +158,7 @@ public: QQuickPath *path; int dirty; - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; + QQuickPathItemStrokeFillParams sfp; }; class QQuickPathItemPrivate : public QQuickItemPrivate @@ -157,7 +184,53 @@ public: bool async; QQuickPathItem::Status status; QQuickAbstractPathRenderer *renderer; - QVector vp; + + struct { + QVector vp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; +}; + +class QQuickPathItemPathObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemPath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickPathItemStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; }; #ifndef QT_NO_OPENGL diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 3de01a5bc7..a76a5e2b43 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -178,6 +178,13 @@ void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; } +void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) { VisualPathData &d(m_vp[index]); diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h index 045c52d610..70a9e88d2f 100644 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ b/src/quick/items/qquickpathitemgenericrenderer_p.h @@ -87,6 +87,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp index 13fab2dc76..f8504f9985 100644 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ b/src/quick/items/qquickpathitemnvprrenderer.cpp @@ -62,6 +62,14 @@ void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } +void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) { VisualPathGuiData &d(m_vp[index]); @@ -288,6 +296,82 @@ void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathG d->path.cmd.append(GL_CLOSE_PATH_NV); } +void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickPathItemPath::Command cmd : path.cmd) { + switch (cmd) { + case QQuickPathItemPath::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) { const float o = c.alphaF() * globalOpacity; diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h index 61f8b5ebb9..deab9cf7f9 100644 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ b/src/quick/items/qquickpathitemnvprrenderer_p.h @@ -81,6 +81,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; @@ -121,6 +122,7 @@ private: }; void convertPath(const QQuickPath *path, VisualPathGuiData *d); + void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); QQuickPathItemNvprRenderNode *m_node = nullptr; int m_accDirty = 0; diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp index 46ebcbfe6d..b7aa93bf65 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ b/src/quick/items/qquickpathitemsoftwarerenderer.cpp @@ -58,6 +58,14 @@ void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } +void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) { VisualPathGuiData &d(m_vp[index]); diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h index 64280b436e..e76590bdfe 100644 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ b/src/quick/items/qquickpathitemsoftwarerenderer_p.h @@ -73,6 +73,7 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; -- cgit v1.2.3 From 9b5fc80af28580e9672792dd511d876a93947882 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 28 Dec 2016 20:27:18 +0100 Subject: build a vector of child-filtering parents before delivery of pointer event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Formerly during normal mouse or touch event delivery, sending it to the Item needed to be done via QQuickWindow::sendEvent, which would then call sendFilteredMouseEvent, which is a recursive function to visit all the item's parents, check whether filtersChildMouseEvents() returns true, if so then return early if childMouseEventFilter() returns true. This is the mechanism by which Flickable (for example) can monitor the movements of an eventpoint even while one of its children has an exclusive grab, and can steal the grab away. Now, we do this by building a vector of such parents first, then visiting them in order. It might be more efficient, it eliminates the recursion, and should eliminate the need for a QSet to ensure that we don't visit the same parent more than once. We can't change the behavior of QQuickWindow::sendEvent() because it's public API, but now we don't use it as much internally. Change-Id: I686fc5612c66eac09ec05c381a648ec65dec3923 Reviewed-by: Qt CI Bot Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickflickable.cpp | 3 ++ src/quick/items/qquickwindow.cpp | 84 +++++++++++++++++++++++++++++++++++-- src/quick/items/qquickwindow_p.h | 5 +++ 3 files changed, 89 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 45bb2a367d..a09ffff816 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1557,6 +1557,8 @@ void QQuickFlickablePrivate::replayDelayedPress() // If we have the grab, release before delivering the event if (QQuickWindow *w = q->window()) { + QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w); + wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay replayingPressEvent = true; if (w->mouseGrabberItem() == q) q->ungrabMouse(); @@ -1564,6 +1566,7 @@ void QQuickFlickablePrivate::replayDelayedPress() // Use the event handler that will take care of finding the proper item to propagate the event QCoreApplication::sendEvent(w, mouseEvent.data()); replayingPressEvent = false; + wpriv->allowChildEventFiltering = true; } } } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fa3093c245..83aff5e07e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -496,6 +496,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , persistentSceneGraph(true) , lastWheelEventAccepted(false) , componentCompleted(true) + , allowChildEventFiltering(true) , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) @@ -1663,11 +1664,16 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven return; } + if (allowChildEventFiltering) { + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; + } + // send update QPointF localPos = grabber->mapFromScene(lastMousePosition); auto me = pointerEvent->asMouseEvent(localPos); me->accept(); - q->sendEvent(grabber, me); + QCoreApplication::sendEvent(grabber, me); point->setAccepted(me->isAccepted()); // release event, make sure to ungrab if there still is a grabber @@ -1887,7 +1893,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) for (QObject *grabber: qAsConst(grabbers)) { if (QQuickItem *grabberItem = qmlobject_cast(grabber)) - q->sendEvent(grabberItem, event); + QCoreApplication::sendEvent(grabberItem, event); else //if (QQuickPointerHandler *grabberHandler = qmlobject_cast(grabber)) // grabberHandler->handlePointerEvent() qWarning("unexpected: can't deliver touch cancel to a PointerHandler (yet?)"); @@ -2255,6 +2261,8 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickItem *receiver = qmlobject_cast(grabber); if (!receiver) receiver = static_cast(grabber)->parentItem(); + if (allowChildEventFiltering && sendFilteredPointerEvent(event, receiver)) + return true; deliverMatchingPointsToItem(receiver, event, hasFiltered); } @@ -2275,6 +2283,12 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSetallPointsAccepted()) @@ -2308,7 +2322,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo Q_ASSERT(item->contains(localPos)); // transform is checked already QMouseEvent *me = event->asMouseEvent(localPos); me->accept(); - q->sendEvent(item, me); + QCoreApplication::sendEvent(item, me); if (me->isAccepted()) { auto mouseGrabber = q->mouseGrabberItem(); if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { @@ -2553,6 +2567,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif +// TODO assimilate this logic and remove this function bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) { Q_Q(QQuickWindow); @@ -2628,6 +2643,69 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; } +void QQuickWindowPrivate::updateFilteringParentItems(const QVector &targetItems) +{ + if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) { + // qDebug() << map(&objectName, targetItems) but done the hard way because C++ is still that primitive + QStringList targetNames; + for (QQuickItem *t : targetItems) + targetNames << (QLatin1String(t->metaObject()->className()) + QLatin1Char(' ') + t->objectName()); + qCDebug(DBG_MOUSE_TARGET) << "finding potential filtering parents of" << targetNames; + } + filteringParentItems.clear(); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); + // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant + if (!item->acceptedMouseButtons() && !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) + continue; // TODO there's no acceptTouchEvents, so it's hard to avoid skipping any items which handle only touch + QQuickItem *parent = item->parentItem(); + while (parent) { + if (parent->filtersChildMouseEvents()) { + bool foundParent = false; + for (const QPair existingItemAndParent : filteringParentItems) + if (existingItemAndParent.second == parent) + foundParent = true; + if (!foundParent) + filteringParentItems.append(QPair(item, parent)); + } + parent = parent->parentItem(); + } + } +} + +bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver) +{ + bool ret = false; + if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { + for (QPair itemAndParent : filteringParentItems) { + QQuickItem *item = receiver ? receiver : itemAndParent.first; + QQuickItem *filteringParent = itemAndParent.second; + if (item == filteringParent) + continue; // a filtering item never needs to filter for itself + QPointF localPos = item->mapFromScene(pme->point(0)->scenePos()); + QMouseEvent *me = pme->asMouseEvent(localPos); + if (filteringParent->childMouseEventFilter(item, me)) { + event->setAccepted(true); + ret = true; + } + } + } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { + QTouchEvent *te = pte->asTouchEvent(); + for (QPair itemAndParent : filteringParentItems) { + QQuickItem *item = receiver ? receiver : itemAndParent.first; + QQuickItem *filteringParent = itemAndParent.second; + if (item == filteringParent) + continue; // a filtering item never needs to filter for itself + if (filteringParent->childMouseEventFilter(item, te)) { + for (auto point: qAsConst(te->touchPoints())) + event->pointById(point.id())->setAccepted(); + return true; + } + } + } + return ret; +} + bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet *hasFiltered) { if (!target) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 30e3b71d0a..38c1b0a4d4 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -148,6 +148,7 @@ public: static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); + bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver); #if QT_CONFIG(wheelevent) bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif @@ -174,6 +175,7 @@ public: QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; + void updateFilteringParentItems(const QVector &targetItems); // hover delivery bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted); @@ -222,6 +224,7 @@ public: QList cleanupNodeList; QVector itemsToPolish; + QVector > filteringParentItems; // item:parent pairs qreal devicePixelRatio; QMetaObject::Connection physicalDpiChangedConnection; @@ -259,6 +262,8 @@ public: uint lastWheelEventAccepted : 1; bool componentCompleted : 1; + bool allowChildEventFiltering : 1; + Qt::FocusReason lastFocusReason; QOpenGLFramebufferObject *renderTarget; -- cgit v1.2.3 From e2fd141372335f917c2d216051abb00d8b15f87c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 1 Sep 2016 06:16:18 +0200 Subject: QQuickWindow: deliver updates to handlers even if they don't grab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "weak grab" concept depends on this. First we deliver to grabbers, then we deliver to non-grabbing handlers (but not to non-grabbing items). Avoid re-delivering to grabbing handlers which already received the same event. Change-Id: If51e1cd9372e3bed1daea3758e9ef8e37c0ba5e3 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 27 +++++++++++++++++++++++++ src/quick/items/qquickevents_p_p.h | 6 ++++++ src/quick/items/qquickitem.cpp | 9 +++++++-- src/quick/items/qquickitem_p.h | 2 +- src/quick/items/qquickwindow.cpp | 40 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 79 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0c18cb4104..a68e72b0b7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -835,6 +835,11 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } +bool QQuickPointerMouseEvent::allPointsGrabbed() const +{ + return m_mousePoint->grabber() != nullptr; +} + QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const { auto event = static_cast(m_event); @@ -854,6 +859,11 @@ void QQuickPointerMouseEvent::clearGrabbers() const { m_mousePoint->setGrabberItem(nullptr); } +bool QQuickPointerMouseEvent::hasGrabber(const QQuickPointerHandler *handler) const +{ + return m_mousePoint->grabber() == handler; +} + bool QQuickPointerMouseEvent::isPressEvent() const { auto me = static_cast(m_event); @@ -869,6 +879,15 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } +bool QQuickPointerTouchEvent::allPointsGrabbed() const +{ + for (int i = 0; i < m_pointCount; ++i) { + if (!m_touchPoints.at(i)->grabber()) + return false; + } + return true; +} + QVector QQuickPointerTouchEvent::grabbers() const { QVector result; @@ -887,6 +906,14 @@ void QQuickPointerTouchEvent::clearGrabbers() const { point->setGrabber(nullptr); } +bool QQuickPointerTouchEvent::hasGrabber(const QQuickPointerHandler *handler) const +{ + for (auto point: m_touchPoints) + if (point->grabber() == handler) + return true; + return false; +} + bool QQuickPointerTouchEvent::isPressEvent() const { return static_cast(m_event)->touchPointStates() & Qt::TouchPointPressed; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 4a78e98705..bbfc34dacf 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -393,6 +393,7 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + virtual bool allPointsGrabbed() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } QVector unacceptedPressedPointScenePositions() const; @@ -402,6 +403,7 @@ public: // helpers for C++ only (during event delivery) virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; virtual QVector grabbers() const = 0; virtual void clearGrabbers() const = 0; + virtual bool hasGrabber(const QQuickPointerHandler *handler) const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -430,8 +432,10 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; + bool hasGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -461,8 +465,10 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; + bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; + bool hasGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 98cc3112df..46e381db7d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5091,15 +5091,20 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } } -void QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event) +bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidGrabber) { Q_Q(QQuickItem); + bool delivered = false; if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; - handler->handlePointerEvent(event); + if (!avoidGrabber || !event->hasGrabber(handler)) { + handler->handlePointerEvent(event); + delivered = true; + } } } + return delivered; } /*! diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index e7ead7fa87..b39c437da5 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -563,7 +563,7 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); - virtual void handlePointerEvent(QQuickPointerEvent *); + virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidGrabber = false); bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 6897240fbd..f7d6b245b2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1685,11 +1685,23 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); } else { - // send initial press bool delivered = false; if (pointerEvent->isPressEvent()) { + // send initial press QSet hasFiltered; delivered = deliverPressEvent(pointerEvent, &hasFiltered); + } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { + // if this is an update or release from an actual mouse, + // and the point wasn't grabbed, deliver only to non-grabber PointerHandlers + QVector targetItems = pointerTargets(contentItem, point->scenePos(), false); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + if (point->grabber()) + break; + } } if (!delivered) @@ -2082,7 +2094,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) accepted = clearHover(event->timestamp()); } event->setAccepted(accepted); - return; } deliverPointerEvent(pointerEventInstance(event)); break; @@ -2271,6 +2282,31 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve deliverMatchingPointsToItem(receiver, event, hasFiltered); } + // If some points weren't grabbed, deliver only to non-grabber PointerHandlers + if (!event->allPointsGrabbed()) { + QVector targetItems; + int pointCount = event->pointCount(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } + + for (QQuickItem *item: targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } + } + return false; } -- cgit v1.2.3 From fb8058e5bfbd412e4503eb280721e4875fda0936 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 18 Jan 2017 13:53:07 +0100 Subject: QQuickWindow: use QVector eventDeliveryTargets to avoid repeated delivery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If no handler or item accepts a newly-pressed point, the event will be re-delivered in deliverTouchEvent(). It doesn't make sense to re-deliver to the same handlers though. A temporary QSet isn't cheap to create, whereas it seems we will need to keep track of handlers which have already been visited, in order to avoid visiting passively-grabbing handlers multiple times. Since both a QVector and a QSet are heap-allocated, and we expect to have a limited quantity of handlers grabbing at one time, a retained QVector (cleared between events) seems to be the cheapest data structure. Change-Id: I831e9a2576b2fcb9095e065795f2baff58115a49 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 2 ++ src/quick/items/qquickevents_p_p.h | 3 +++ src/quick/items/qquickitem.cpp | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index a68e72b0b7..d7b5fe79de 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -731,6 +731,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) return this; m_device = QQuickPointerDevice::genericMouseDevice(); + m_device->eventDeliveryTargets().clear(); m_button = ev->button(); m_pressedButtons = ev->buttons(); Qt::TouchPointState state = Qt::TouchPointStationary; @@ -765,6 +766,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) return this; m_device = QQuickPointerDevice::touchDevice(ev->device()); + m_device->eventDeliveryTargets().clear(); m_button = Qt::NoButton; m_pressedButtons = Qt::NoButton; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index bbfc34dacf..1751e366b2 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -553,6 +553,8 @@ public: static QQuickPointerDevice *genericMouseDevice(); static QQuickPointerDevice *tabletDevice(qint64); + QVector &eventDeliveryTargets() { return m_eventDeliveryTargets; } + private: QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0) : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps) @@ -579,6 +581,7 @@ private: QPointingDeviceUniqueId m_uniqueId; // the device-specific event instance which is reused during event delivery QQuickPointerEvent *m_event; + QVector m_eventDeliveryTargets; // during delivery, handlers which have already seen the event Q_DISABLE_COPY(QQuickPointerDevice) friend struct ConstructableQQuickPointerDevice; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 46e381db7d..9b91b4bda4 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5095,12 +5095,14 @@ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoid { Q_Q(QQuickItem); bool delivered = false; + QVector &eventDeliveryTargets = event->device()->eventDeliveryTargets(); if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; - if (!avoidGrabber || !event->hasGrabber(handler)) { + if ((!avoidGrabber || !event->hasGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { handler->handlePointerEvent(event); delivered = true; + eventDeliveryTargets.append(handler); } } } -- cgit v1.2.3 From 782fa9aef5e02f914ed2efff997fc8d0421c634c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 22 Nov 2016 17:36:55 +0100 Subject: deliverUpdatedTouchPoints: if a handler is the grabber, it goes first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deliverMatchingPointsToItem() does not discriminate between handlers: it delivers to them in the order they are in the item's vector. In the map.qml manual test, this meant that DragHandler could steal the grab even when the pinch is active. On one hand, always delivering to all handlers could help them to cooperate better. On the other hand, since we decided to let handlers grab directly (instead of only allowing items to grab), that should be respected. And DragHandler so far does not refuse to allow dragging when two fingers are pressed inside the parent item: it will grab one point and ignore the other. Change-Id: I245eceafaea96096030ba6ff7263d95069e286ba Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f7d6b245b2..5663f3982c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2272,13 +2272,21 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve { const auto grabbers = event->grabbers(); for (auto grabber : grabbers) { - // The grabber is guaranteed to be either an item or a handler, but - // we need the item in order to call deliverMatchingPointsToItem(). + // The grabber is guaranteed to be either an item or a handler. QQuickItem *receiver = qmlobject_cast(grabber); - if (!receiver) + if (!receiver) { + // The grabber is not an item? It's a handler then. Let it have the event first. + QQuickPointerHandler *handler = static_cast(grabber); receiver = static_cast(grabber)->parentItem(); - if (allowChildEventFiltering && sendFilteredPointerEvent(event, receiver)) - return true; + if (allowChildEventFiltering && sendFilteredPointerEvent(event, receiver)) + return true; + event->localize(receiver); + handler->handlePointerEvent(event); + if (event->allPointsAccepted()) + return true; + } + // If the grabber is an item or the grabbing handler didn't handle it, + // then deliver the event to the item (which may have multiple handlers). deliverMatchingPointsToItem(receiver, event, hasFiltered); } -- cgit v1.2.3 From 6de499a7d042194e98fcb26731d99cd5d8eefadb Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 19 Jan 2017 19:03:37 +0100 Subject: corrections: copy some values from QQuickEventPoint to properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5c6245b7ff7a941c0a2425f32909c0dc0982e199 in its final form was not what we need for weak/passive grabs, although earlier versions were. Only the PointerHandler subclass can decide whether to do an exclusive grab, and we must always ensure that QQuickPointerSingleHandler::handleEventPoint is called, even when a point is released, otherwise some types of handlers aren't possible. Also, DragHandler needs to retain terminal velocity until after release. onActiveChanged is a handy place to check the velocity at the point of release, to start an animation for example. So when the point is released, we'd like the velocity property to continue providing the last-known velocity for as long as possible. Setting it back to zero and emitting velocityChanged should be one of the last things it does. This makes flicking possible. Change-Id: Ibf3bec27162b016ba3f93269d90959c9ccb74d77 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index f39d8ac976..aeaa9d8309 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -97,10 +97,8 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even QQuickPointerDeviceHandler::handlePointerEventImpl(event); QQuickEventPoint *currentPoint = event->pointById(m_pointId); Q_ASSERT(currentPoint); - bool grab = false; - if (!m_pointId || !currentPoint->isAccepted() || currentPoint->state() == QQuickEventPoint::Released) { + if (!m_pointId || !currentPoint->isAccepted()) { reset(); - grab = false; } else { if (event->asPointerTouchEvent()) { QQuickEventTouchPoint *tp = static_cast(currentPoint); @@ -117,19 +115,25 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even m_ellipseDiameters = QSizeF(); } m_pos = currentPoint->pos(); - m_velocity = currentPoint->velocity(); + if (currentPoint->state() == QQuickEventPoint::Updated) + m_velocity = currentPoint->velocity(); handleEventPoint(currentPoint); - if (currentPoint->state() == QQuickEventPoint::Pressed) { + switch (currentPoint->state()) { + case QQuickEventPoint::Pressed: m_pressPos = currentPoint->pos(); + setPressedButtons(event->buttons()); emit pointIdChanged(); + break; + case QQuickEventPoint::Released: + setGrab(currentPoint, false); + reset(); + break; + default: + setPressedButtons(event->buttons()); + break; } - setPressedButtons(event->buttons()); - grab = true; } emit eventPointHandled(); - // TODO don't call setGrab(true) here, only setGrab(false), because concrete subclasses may - // wait for the drag threshold to be exceeded, for example - setGrab(currentPoint, grab); } bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) -- cgit v1.2.3 From 55970e2cd6f5a24f654e92febf27d6aeb245d03f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 8 Feb 2017 12:42:53 +0100 Subject: remove undefined QQuickPointerDeviceHandler::setPressed method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie9e8bea2d1131fb3c81aefeb6d269160051cabc9 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerdevicehandler_p.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index bb12d3ccdb..2e126381b0 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -82,7 +82,6 @@ Q_SIGNALS: protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; - void setPressed(bool pressed); protected: QQuickPointerDevice::DeviceTypes m_acceptedDevices; -- cgit v1.2.3 From 2d61a39dedfb2718cdb6c26b370b7e4103db9c4a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 28 Jul 2015 15:24:29 +0200 Subject: Introduce TapHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device-agnostic tap/click detection. Also detect whether the taps or clicks occur close enough together in both time and space to be considered part of a multi-tap gesture. Change-Id: I41a378feea3340b9f0409118273746a289641d6c Reviewed-by: Jan Arve Sæther --- src/quick/handlers/handlers.pri | 3 + src/quick/handlers/qquickhandlersmodule.cpp | 4 +- src/quick/handlers/qquicktaphandler.cpp | 177 ++++++++++++++++++++++++++++ src/quick/handlers/qquicktaphandler_p.h | 104 ++++++++++++++++ 4 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 src/quick/handlers/qquicktaphandler.cpp create mode 100644 src/quick/handlers/qquicktaphandler_p.h (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index f32f330d4e..749e4cc4e0 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -7,6 +7,7 @@ HEADERS += \ $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquicktaphandler_p.h \ $$PWD/qquickhandlersmodule_p.h \ SOURCES += \ @@ -16,4 +17,6 @@ SOURCES += \ $$PWD/qquickmultipointerhandler.cpp \ $$PWD/qquickpinchhandler.cpp \ $$PWD/qquickpointersinglehandler.cpp \ + $$PWD/qquicktaphandler.cpp \ $$PWD/qquickhandlersmodule.cpp \ + diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index 697a198e67..4bfdc020d8 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -41,6 +41,7 @@ #include "qquickpointerhandler_p.h" #include "qquickdraghandler_p.h" #include "qquickpinchhandler_p.h" +#include "qquicktaphandler_p.h" static void initResources() { @@ -80,6 +81,7 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) qmlRegisterUncreatableType(uri, major, minor, "DragAxis", QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler")); qmlRegisterType(uri,major,minor,"PinchHandler"); + qmlRegisterType(uri,major,minor,"TapHandler"); } void QQuickHandlersModule::defineModule() diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp new file mode 100644 index 0000000000..e0e66be01c --- /dev/null +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qquicktaphandler_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +qreal QQuickTapHandler::m_multiTapInterval(0.0); +// single tap distance is the same as the drag threshold +int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1); +int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); + +/*! + \qmltype TapHandler + \instantiates QQuickTapHandler + \inqmlmodule QtQuick + \ingroup qtquick-handlers + \brief Handler for taps and clicks + + TapHandler is a handler for taps on a touchscreen or clicks on a mouse. + + It requires that any movement between the press and release remains + less than the drag threshold for a single tap, and less than + QPlatformTheme::MouseDoubleClickDistance for multi-tap gestures + (double-tap, triple-tap, etc.) with the mouse, or 10 pixels with touch. + It also requires that the time between press and release remains + less than QStyleHints::mouseDoubleClickInterval() for a single tap, + and that the time from one press/release sequence to the next remains + less than QStyleHints::mouseDoubleClickInterval() for multi-tap gestures. + + Note that buttons (such as QPushButton) are often implemented not to care + whether the press and release occur close together: if you press the button + and then change your mind, you need to drag all the way off the edge of the + button in order to cancel the click. If you want to achieve such behavior, + it's enough to use a PointerHandler and consider the button clicked on + every \l {QQuickPointerHandler:}{released} event. But TapHandler requires + that the events occur close together in both space and time, which is anyway + necessary to detect double clicks or multi-click gestures. + + \sa MouseArea +*/ + +QQuickTapHandler::QQuickTapHandler(QObject *parent) + : QQuickPointerSingleHandler(parent) + , m_pressed(false) + , m_tapCount(0) + , m_lastTapTimestamp(0.0) +{ + setAcceptedButtons(Qt::LeftButton); + if (m_mouseMultiClickDistanceSquared < 0) { + m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0; + m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()-> + themeHint(QPlatformTheme::MouseDoubleClickDistance).toInt(); + m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared; + m_touchMultiTapDistanceSquared = QGuiApplicationPrivate::platformTheme()-> + themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt(); + m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared; + } +} + +QQuickTapHandler::~QQuickTapHandler() +{ +} + +bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) +{ + if (point->state() == QQuickEventPoint::Pressed && parentContains(point)) + return true; + // If the user has not dragged too far, it could be a tap. + // Otherwise we want to give up the grab so that a competing handler + // (e.g. DragHandler) gets a chance to take over. + // Don't forget to emit released in case of a cancel. + return !point->isDraggedOverThreshold(); +} + +void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) +{ + switch (point->state()) { + case QQuickEventPoint::Pressed: + setPressed(true, false, point); + break; + case QQuickEventPoint::Released: + if ((point->pointerEvent()->buttons() & m_acceptedButtons) == Qt::NoButton) + setPressed(false, false, point); + break; + default: + break; + } +} + +void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) +{ + if (m_pressed != press) { + m_pressed = press; + if (!cancel && !press && point->timeHeld() < m_multiTapInterval) { + // Assuming here that pointerEvent()->timestamp() is in ms. + qreal ts = point->pointerEvent()->timestamp() / 1000.0; + if (ts - m_lastTapTimestamp < m_multiTapInterval && + QVector2D(point->scenePos() - m_lastTapPos).lengthSquared() < + (point->pointerEvent()->device()->type() == QQuickPointerDevice::Mouse ? + m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared)) + ++m_tapCount; + else + m_tapCount = 1; + emit tapped(point); + emit tapCountChanged(); + m_lastTapTimestamp = ts; + m_lastTapPos = point->scenePos(); + } + emit pressedChanged(); + } +} + +void QQuickTapHandler::handleGrabCancel(QQuickEventPoint *point) +{ + QQuickPointerSingleHandler::handleGrabCancel(point); + setPressed(false, true, point); +} + +/*! + \qmlproperty tapCount + + The number of taps which have occurred within the time and space + constraints to be considered a single gesture. For example, to detect + a double-tap, you can write + + \qml + Rectangle { + width: 100; height: 30 + signal doubleTap + TapHandler { + acceptedButtons: Qt.AllButtons + onTapped: if (tapCount == 2) doubleTap() + } + } +*/ + +QT_END_NAMESPACE diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h new file mode 100644 index 0000000000..c11d51ee03 --- /dev/null +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QQUICKTAPHANDLER_H +#define QQUICKTAPHANDLER_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 "qquickitem.h" +#include "qevent.h" +#include "qquickpointersinglehandler_p.h" + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler +{ + Q_OBJECT + Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged) + Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged) + +public: + QQuickTapHandler(QObject *parent = 0); + ~QQuickTapHandler(); + + bool wantsEventPoint(QQuickEventPoint *point) override; + void handleEventPoint(QQuickEventPoint *point) override; + + bool isPressed() const { return m_pressed; } + + int tapCount() const { return m_tapCount; } + +Q_SIGNALS: + void pressedChanged(); + void tapCountChanged(); + void tapped(QQuickEventPoint *point); + +protected: + void handleGrabCancel(QQuickEventPoint *point) override; + +private: + void setPressed(bool press, bool cancel, QQuickEventPoint *point); + +private: + bool m_pressed; + int m_tapCount; + QPointF m_lastTapPos; + qreal m_lastTapTimestamp; + + static qreal m_tapInterval; + static qreal m_multiTapInterval; + static int m_mouseMultiClickDistanceSquared; + static int m_touchMultiTapDistanceSquared; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTapHandler) + +#endif // QQUICKTAPHANDLER_H -- cgit v1.2.3 From 5c639a07fd90916d39823e800d5d89f779d892e9 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 18 Nov 2016 15:02:18 +0100 Subject: TapHandler: add long-press feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a longPressed signal, emitted when the point is held long enough. Add the longPressThreshold to control how long that is. Change-Id: I95a65f1e4c62eb41fb9ea02b14bdc3f16aa72ec2 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 44 ++++++++++++++++++++++++++++++++- src/quick/handlers/qquicktaphandler_p.h | 11 +++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index e0e66be01c..f1bb2f96b6 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -83,6 +83,7 @@ QQuickTapHandler::QQuickTapHandler(QObject *parent) : QQuickPointerSingleHandler(parent) , m_pressed(false) , m_tapCount(0) + , m_longPressThreshold(-1) , m_lastTapTimestamp(0.0) { setAcceptedButtons(Qt::LeftButton); @@ -127,11 +128,52 @@ void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) } } +/*! + \qmlproperty longPressThreshold + + The time in seconds that an event point must be pressed in order to + trigger a long press gesture and emit the \l longPressed() signal. + If the point is released before this time limit, a tap can be detected + if the other constraints are satisfied. The default value is + QStyleHints::mousePressAndHoldInterval() converted to seconds. +*/ +qreal QQuickTapHandler::longPressThreshold() const +{ + return longPressThresholdMilliseconds() / 1000.0; +} + +void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold) +{ + int ms = qRound(longPressThreshold * 1000); + if (m_longPressThreshold == ms) + return; + + m_longPressThreshold = ms; + emit longPressThresholdChanged(); +} + +int QQuickTapHandler::longPressThresholdMilliseconds() const +{ + return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold); +} + +void QQuickTapHandler::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_longPressTimer.timerId()) { + m_longPressTimer.stop(); + emit longPressed(); + } +} + void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) { if (m_pressed != press) { m_pressed = press; - if (!cancel && !press && point->timeHeld() < m_multiTapInterval) { + if (press) + m_longPressTimer.start(longPressThresholdMilliseconds(), this); + else + m_longPressTimer.stop(); + if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; if (ts - m_lastTapTimestamp < m_multiTapInterval && diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index c11d51ee03..0559089fbf 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -54,6 +54,7 @@ #include "qquickitem.h" #include "qevent.h" #include "qquickpointersinglehandler_p.h" +#include QT_BEGIN_NAMESPACE @@ -62,6 +63,7 @@ class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler Q_OBJECT Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged) Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged) + Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged) public: QQuickTapHandler(QObject *parent = 0); @@ -74,20 +76,29 @@ public: int tapCount() const { return m_tapCount; } + qreal longPressThreshold() const; + void setLongPressThreshold(qreal longPressThreshold); + Q_SIGNALS: void pressedChanged(); void tapCountChanged(); + void longPressThresholdChanged(); void tapped(QQuickEventPoint *point); + void longPressed(); protected: void handleGrabCancel(QQuickEventPoint *point) override; + void timerEvent(QTimerEvent *event) override; private: void setPressed(bool press, bool cancel, QQuickEventPoint *point); + int longPressThresholdMilliseconds() const; private: bool m_pressed; int m_tapCount; + int m_longPressThreshold; + QBasicTimer m_longPressTimer; QPointF m_lastTapPos; qreal m_lastTapTimestamp; -- cgit v1.2.3 From cb78d5c91ed33543a8e7fe7717f74f95834e4cc3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 18 Oct 2016 18:45:49 +0200 Subject: TapHandler: add gesturePolicy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now it behaved as if this was set to DragThreshold: give up on the tap as soon as you are clearly dragging rather than tapping. But that's not what is normally wanted when building a Button control, for example. So provide 3 options: give up past the drag threshold, when the pointer goes outside the bounds, or when it's released outside the bounds. The longPressThreshold also constrains all three cases: holding (or dragging) for too long will not result in an immediate cancellation, but it also will not be a tap gesture. Change-Id: I95aec978e783892b55371391a27642751d91d9ff Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 116 ++++++++++++++++++++++++++------ src/quick/handlers/qquicktaphandler_p.h | 14 +++- 2 files changed, 110 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index f1bb2f96b6..11d4301eb6 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -58,23 +58,19 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); TapHandler is a handler for taps on a touchscreen or clicks on a mouse. - It requires that any movement between the press and release remains - less than the drag threshold for a single tap, and less than - QPlatformTheme::MouseDoubleClickDistance for multi-tap gestures - (double-tap, triple-tap, etc.) with the mouse, or 10 pixels with touch. - It also requires that the time between press and release remains - less than QStyleHints::mouseDoubleClickInterval() for a single tap, - and that the time from one press/release sequence to the next remains - less than QStyleHints::mouseDoubleClickInterval() for multi-tap gestures. - + Detection of a valid tap gesture depends on \l gesturePolicy. Note that buttons (such as QPushButton) are often implemented not to care whether the press and release occur close together: if you press the button and then change your mind, you need to drag all the way off the edge of the - button in order to cancel the click. If you want to achieve such behavior, - it's enough to use a PointerHandler and consider the button clicked on - every \l {QQuickPointerHandler:}{released} event. But TapHandler requires - that the events occur close together in both space and time, which is anyway - necessary to detect double clicks or multi-click gestures. + button in order to cancel the click. Therefore the default + \l gesturePolicy is \l ReleaseWithinBounds. If you want to require + that the press and release are close together in both space and time, + set it to \l DragThreshold. + + For multi-tap gestures (double-tap, triple-tap etc.), the distance moved + must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and + QPlatformTheme::TouchDoubleTapDistance with touch, and the time between + taps must not exceed QStyleHints::mouseDoubleClickInterval(). \sa MouseArea */ @@ -82,6 +78,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); QQuickTapHandler::QQuickTapHandler(QObject *parent) : QQuickPointerSingleHandler(parent) , m_pressed(false) + , m_gesturePolicy(ReleaseWithinBounds) , m_tapCount(0) , m_longPressThreshold(-1) , m_lastTapTimestamp(0.0) @@ -102,15 +99,46 @@ QQuickTapHandler::~QQuickTapHandler() { } +static bool dragOverThreshold(QQuickEventPoint *point) +{ + QPointF delta = point->scenePos() - point->scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)); +} + bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) { - if (point->state() == QQuickEventPoint::Pressed && parentContains(point)) - return true; - // If the user has not dragged too far, it could be a tap. + // If the user has not violated any constraint, it could be a tap. // Otherwise we want to give up the grab so that a competing handler // (e.g. DragHandler) gets a chance to take over. // Don't forget to emit released in case of a cancel. - return !point->isDraggedOverThreshold(); + bool ret = false; + switch (point->state()) { + case QQuickEventPoint::Pressed: + case QQuickEventPoint::Released: + ret = parentContains(point); + break; + default: // update or stationary + switch (m_gesturePolicy) { + case DragThreshold: + ret = !dragOverThreshold(point); + break; + case WithinBounds: + ret = parentContains(point); + break; + case ReleaseWithinBounds: + ret = true; + break; + } + break; + } + // If this is the grabber, returning false from this function will + // cancel the grab, so handleGrabCancel() and setPressed(false) will be called. + // But when m_gesturePolicy is DragThreshold, we don't grab, but + // we still don't want to be pressed anymore. + if (!ret) + setPressed(false, true, point); + return ret; } void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) @@ -134,7 +162,7 @@ void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) The time in seconds that an event point must be pressed in order to trigger a long press gesture and emit the \l longPressed() signal. If the point is released before this time limit, a tap can be detected - if the other constraints are satisfied. The default value is + if the \l gesturePolicy constraint is satisfied. The default value is QStyleHints::mousePressAndHoldInterval() converted to seconds. */ qreal QQuickTapHandler::longPressThreshold() const @@ -165,6 +193,54 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) } } +/*! + \qmlproperty gesturePolicy + + The spatial constraint for a tap or long press gesture to be recognized, + in addition to the constraint that the release must occur before + \l longPressThreshold has elapsed. If these constraints are not satisfied, + the \l tapped signal is not emitted, and \l tapCount is not incremented. + If the spatial constraint is violated, \l isPressed transitions immediately + from true to false, regardless of the time held. + + \c DragThreshold means that the event point must not move significantly. + If the mouse, finger or stylus moves past the system-wide drag threshold + (QStyleHints::startDragDistance), the tap gesture is canceled, even if + the button or finger is still pressed. This policy can be useful whenever + TapHandler needs to cooperate with other pointer handlers (for example + \l DragHandler), because in this case TapHandler will never grab. + + \c WithinBounds means that if the event point leaves the bounds of the + \l target item, the tap gesture is canceled. The TapHandler will grab on + press, but release the grab as soon as the boundary constraint is no + longer satisfied. + + \c ReleaseWithinBounds (the default value) means that at the time of release + (the mouse button is released or the finger is lifted), if the event point + is outside the bounds of the \l target item, a tap gesture is not + recognized. This is the default value, because it corresponds to typical + button behavior: you can cancel a click by dragging outside the button, and + you can also change your mind by dragging back inside the button before + release. Note that it's necessary for TapHandler to grab on press and + retain it until release (greedy grab) in order to detect this gesture. +*/ +void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy) +{ + if (m_gesturePolicy == gesturePolicy) + return; + + m_gesturePolicy = gesturePolicy; + emit gesturePolicyChanged(); +} + +/*! + \qmlproperty pressed + + This property will be true whenever the mouse or touch point is pressed, + and any movement since the press is compliant with the current + \l gesturePolicy. When the event point is released or the policy is + violated, pressed will change to false. +*/ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) { if (m_pressed != press) { @@ -173,6 +249,8 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_longPressTimer.start(longPressThresholdMilliseconds(), this); else m_longPressTimer.stop(); + if (m_gesturePolicy != DragThreshold) + setGrab(point, press); if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 0559089fbf..93fe817844 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -64,8 +64,16 @@ class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged) Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged) Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged) + Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged) public: + enum GesturePolicy { + DragThreshold, + WithinBounds, + ReleaseWithinBounds + }; + Q_ENUM(GesturePolicy) + QQuickTapHandler(QObject *parent = 0); ~QQuickTapHandler(); @@ -79,10 +87,14 @@ public: qreal longPressThreshold() const; void setLongPressThreshold(qreal longPressThreshold); + GesturePolicy gesturePolicy() const { return m_gesturePolicy; } + void setGesturePolicy(GesturePolicy gesturePolicy); + Q_SIGNALS: void pressedChanged(); void tapCountChanged(); void longPressThresholdChanged(); + void gesturePolicyChanged(); void tapped(QQuickEventPoint *point); void longPressed(); @@ -96,13 +108,13 @@ private: private: bool m_pressed; + GesturePolicy m_gesturePolicy; int m_tapCount; int m_longPressThreshold; QBasicTimer m_longPressTimer; QPointF m_lastTapPos; qreal m_lastTapTimestamp; - static qreal m_tapInterval; static qreal m_multiTapInterval; static int m_mouseMultiClickDistanceSquared; static int m_touchMultiTapDistanceSquared; -- cgit v1.2.3 From 12f3f20be7e160b4f8de4ee267909fec61673111 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 5 Aug 2015 12:45:48 +0200 Subject: TapHandler: add timeHeld property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It enables long-press gestures to have continuous feedback. Change-Id: Idd0838aff6213ebfc2fce66639bbc932e77208b4 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 34 +++++++++++++++++++++++++++++++-- src/quick/handlers/qquicktaphandler_p.h | 6 ++++++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 11d4301eb6..51a496001c 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -245,10 +245,14 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi { if (m_pressed != press) { m_pressed = press; - if (press) + connectPreRenderSignal(press); + if (press) { m_longPressTimer.start(longPressThresholdMilliseconds(), this); - else + m_holdTimer.start(); + } else { m_longPressTimer.stop(); + m_holdTimer.invalidate(); + } if (m_gesturePolicy != DragThreshold) setGrab(point, press); if (!cancel && !press && point->timeHeld() < longPressThreshold()) { @@ -276,6 +280,19 @@ void QQuickTapHandler::handleGrabCancel(QQuickEventPoint *point) setPressed(false, true, point); } +void QQuickTapHandler::connectPreRenderSignal(bool conn) +{ + if (conn) + connect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); + else + disconnect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld); +} + +void QQuickTapHandler::updateTimeHeld() +{ + emit timeHeldChanged(); +} + /*! \qmlproperty tapCount @@ -294,4 +311,17 @@ void QQuickTapHandler::handleGrabCancel(QQuickEventPoint *point) } */ +/*! + \qmlproperty timeHeld + + The amount of time in seconds that a pressed point has been held, without + moving beyond the drag threshold. It will be updated at least once per + frame rendered, which enables rendering an animation showing the progress + towards an action which will be triggered by a long-press. It is also + possible to trigger one of a series of actions depending on how long the + press is held. + + A value less than zero means no point is being held within this handler's Item. +*/ + QT_END_NAMESPACE diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 93fe817844..bc292ecb12 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -63,6 +63,7 @@ class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler Q_OBJECT Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged) Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged) + Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged) Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged) Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged) @@ -83,6 +84,7 @@ public: bool isPressed() const { return m_pressed; } int tapCount() const { return m_tapCount; } + qreal timeHeld() const { return (m_holdTimer.isValid() ? m_holdTimer.elapsed() / 1000.0 : -1.0); } qreal longPressThreshold() const; void setLongPressThreshold(qreal longPressThreshold); @@ -93,6 +95,7 @@ public: Q_SIGNALS: void pressedChanged(); void tapCountChanged(); + void timeHeldChanged(); void longPressThresholdChanged(); void gesturePolicyChanged(); void tapped(QQuickEventPoint *point); @@ -105,6 +108,8 @@ protected: private: void setPressed(bool press, bool cancel, QQuickEventPoint *point); int longPressThresholdMilliseconds() const; + void connectPreRenderSignal(bool conn = true); + void updateTimeHeld(); private: bool m_pressed; @@ -112,6 +117,7 @@ private: int m_tapCount; int m_longPressThreshold; QBasicTimer m_longPressTimer; + QElapsedTimer m_holdTimer; QPointF m_lastTapPos; qreal m_lastTapTimestamp; -- cgit v1.2.3 From a3750f4f1b67302cb850addcc08c516a2baa5803 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 22 Sep 2016 14:54:59 +0200 Subject: QQuickWindow: refactor ungrab on mouse release We need to ungrab any PointerHandler which is grabbing, not only an Item. And we need to let filtering parents see the event before we deliver to a PointerHandler. Change-Id: Ifa2e563f78566e92a71d7fec15dfdbb9a6e0515d Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 5663f3982c..ddd0336fc2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -1648,42 +1648,43 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { - Q_Q(QQuickWindow); auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - if (auto handler = point->grabberPointerHandler()) { - pointerEvent->localize(handler->parentItem()); - handler->handlePointerEvent(pointerEvent); - return; - } - - QQuickItem *grabber = point->grabberItem(); - if (grabber) { - // if the update consists of changing button state, then don't accept it - // unless the button is one in which the item is interested - if (pointerEvent->button() != Qt::NoButton - && grabber->acceptedMouseButtons() - && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { - pointerEvent->setAccepted(false); - return; - } - - if (allowChildEventFiltering) { - if (sendFilteredPointerEvent(pointerEvent, grabber)) + if (point->grabber()) { + bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); + if (auto grabber = point->grabberItem()) { + if (allowChildEventFiltering) { + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; + } + // if the grabber is an Item: + // if the update consists of changing button state, don't accept it unless + // the button is one in which the grabber is interested + if (pointerEvent->button() != Qt::NoButton && grabber->acceptedMouseButtons() + && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { + pointerEvent->setAccepted(false); return; - } + } - // send update - QPointF localPos = grabber->mapFromScene(lastMousePosition); - auto me = pointerEvent->asMouseEvent(localPos); - me->accept(); - QCoreApplication::sendEvent(grabber, me); - point->setAccepted(me->isAccepted()); + // send update + QPointF localPos = grabber->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); + me->accept(); + QCoreApplication::sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); - // release event, make sure to ungrab if there still is a grabber - if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) - q->mouseGrabberItem()->ungrabMouse(); + // release event: ungrab if no buttons are pressed anymore + if (mouseIsReleased) + setMouseGrabber(nullptr); + } else { + // if the grabber is not an Item, it must be a PointerHandler + auto handler = point->grabberPointerHandler(); + pointerEvent->localize(handler->parentItem()); + handler->handlePointerEvent(pointerEvent); + if (mouseIsReleased) + point->setGrabberPointerHandler(nullptr); + } } else { bool delivered = false; if (pointerEvent->isPressEvent()) { -- cgit v1.2.3 From 02f430462b4e9256b6ae021e3d415c0d2180404d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 20 Jan 2017 11:14:50 +0100 Subject: check allowChildEventFiltering inside QQWPriv::sendFilteredPointerEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out that we always have to check the allowed flag before doing it, so this will remove some code duplication (especially given that we'll soon need to call it from more places). Change-Id: Idfb7bf9487d8de200cbdb3f47b82df754137aa12 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ddd0336fc2..fc53420cdd 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1654,10 +1654,8 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven if (point->grabber()) { bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); if (auto grabber = point->grabberItem()) { - if (allowChildEventFiltering) { - if (sendFilteredPointerEvent(pointerEvent, grabber)) - return; - } + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; // if the grabber is an Item: // if the update consists of changing button state, don't accept it unless // the button is one in which the grabber is interested @@ -2279,7 +2277,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve // The grabber is not an item? It's a handler then. Let it have the event first. QQuickPointerHandler *handler = static_cast(grabber); receiver = static_cast(grabber)->parentItem(); - if (allowChildEventFiltering && sendFilteredPointerEvent(event, receiver)) + if (sendFilteredPointerEvent(event, receiver)) return true; event->localize(receiver); handler->handlePointerEvent(event); @@ -2725,6 +2723,8 @@ void QQuickWindowPrivate::updateFilteringParentItems(const QVector bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver) { + if (!allowChildEventFiltering) + return false; bool ret = false; if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { for (QPair itemAndParent : filteringParentItems) { -- cgit v1.2.3 From 50aaca40fe09a3ab6c927f7a550b2e97cf332a9c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 29 Nov 2016 14:50:05 +0100 Subject: start making explicit exclusive or passive grabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4a6e3c72d69e893fec2e39f4faab24af6d00c7e0 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 5 +- src/quick/handlers/qquickdraghandler_p.h | 2 +- src/quick/handlers/qquickmultipointerhandler.cpp | 2 +- src/quick/handlers/qquickpointerhandler.cpp | 34 +++++-- src/quick/handlers/qquickpointerhandler_p.h | 6 +- src/quick/handlers/qquickpointersinglehandler.cpp | 8 +- src/quick/handlers/qquicktaphandler.cpp | 3 +- src/quick/items/qquickevents.cpp | 109 ++++++++++++++++------ src/quick/items/qquickevents_p_p.h | 15 ++- src/quick/items/qquickwindow.cpp | 6 +- 10 files changed, 138 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 6caee6be13..9575f7ede9 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -80,6 +80,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) case QQuickEventPoint::Pressed: if (target() && target()->parentItem()) m_startPos = target()->parentItem()->mapToScene(target()->position()); + setPassiveGrab(point); break; case QQuickEventPoint::Updated: { QPointF delta = point->scenePos() - point->scenePressPos(); @@ -96,7 +97,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) } } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { - setGrab(point, true); + setExclusiveGrab(point); } } break; default: diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 69fa297b3c..6250615091 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index d3b986bcce..4b931641a2 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -213,7 +213,7 @@ void QQuickMultiPointerHandler::acceptPoints(const QVector & void QQuickMultiPointerHandler::grabPoints(QVector points) { for (QQuickEventPoint* point : points) - setGrab(point, true); + setExclusiveGrab(point); } QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 0398863ba3..c91fb2de88 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch") +Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active") /*! \qmltype PointerHandler @@ -73,22 +74,42 @@ QQuickPointerHandler::~QQuickPointerHandler() } } -void QQuickPointerHandler::setGrab(QQuickEventPoint *point, bool grab) +void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) +{ + if (grab) { + point->setGrabberPointerHandler(this, false); + emit grabChanged(point); + } else if (point->grabberPointerHandler() == this) { + // TODO should giving up passive grab imply giving up exclusive grab too? + // we're being inconsistent here: check whether the exclusive grabber is this, + // then say that the passive grab was canceled. + point->cancelPassiveGrab(this); + emit grabChanged(point); + } +} + +void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) { QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); if (grab && oldGrabber != this) { if (oldGrabber) oldGrabber->handleGrabCancel(point); - point->setGrabberPointerHandler(this); + point->setGrabberPointerHandler(this, true); onGrabChanged(point); - emit grabChanged(point); +// emit grabChanged(point); // TODO maybe } else if (!grab && oldGrabber == this) { - point->setGrabberPointerHandler(nullptr); + point->setGrabberPointerHandler(nullptr, true); onGrabChanged(point); - emit grabChanged(point); +// emit grabChanged(point); // TODO maybe } } +void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point) +{ + point->cancelAllGrabs(this); + emit grabChanged(point); +} + QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const { return (target() ? target()->mapFromScene(point->scenePos()) : point->scenePos()); @@ -164,6 +185,7 @@ bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) void QQuickPointerHandler::setActive(bool active) { if (m_active != active) { + qCDebug(lcPointerHandlerActive) << this << m_active << "->" << active; m_active = active; onActiveChanged(); emit activeChanged(); diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 85f1096410..526a60262b 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -99,7 +99,9 @@ protected: void setActive(bool active); virtual void onActiveChanged() { } virtual void onGrabChanged(QQuickEventPoint *) { } - void setGrab(QQuickEventPoint *point, bool grab); + void setPassiveGrab(QQuickEventPoint *point, bool grab = true); + void setExclusiveGrab(QQuickEventPoint *point, bool grab = true); + void cancelAllGrabs(QQuickEventPoint *point); virtual void handleGrabCancel(QQuickEventPoint *point); QPointF eventPos(const QQuickEventPoint *point) const; bool parentContains(const QQuickEventPoint *point) const; diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index aeaa9d8309..41c9e1f322 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -70,8 +70,8 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) if (wantsEventPoint(point)) { point->setAccepted(); return true; - } else if (point->grabber() == this) { - point->cancelGrab(); + } else { + point->cancelAllGrabs(this); } } else { qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_pointId @@ -125,7 +125,7 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even emit pointIdChanged(); break; case QQuickEventPoint::Released: - setGrab(currentPoint, false); + setExclusiveGrab(currentPoint, false); reset(); break; default: diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 51a496001c..5065e87e8e 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -253,8 +253,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_longPressTimer.stop(); m_holdTimer.invalidate(); } - if (m_gesturePolicy != DragThreshold) - setGrab(point, press); + setPassiveGrab(point, press); if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index d7b5fe79de..eb4a6636f2 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -519,9 +519,9 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, { m_scenePos = scenePos; if (m_pointId != pointId) { - if (m_grabber) { - qWarning() << m_grabber << "failed to ungrab previous point" << m_pointId; - cancelGrab(); + if (m_exclusiveGrabber) { + qWarning() << m_exclusiveGrabber << "failed to ungrab previous point" << m_pointId; + cancelExclusiveGrab(); } m_pointId = pointId; } @@ -552,34 +552,34 @@ void QQuickEventPoint::invalidate() QObject *QQuickEventPoint::grabber() const { - return m_grabber.data(); + return m_exclusiveGrabber.data(); } void QQuickEventPoint::setGrabber(QObject *grabber) { if (QQuickPointerHandler *phGrabber = qmlobject_cast(grabber)) - setGrabberPointerHandler(phGrabber); + setGrabberPointerHandler(phGrabber, true); else setGrabberItem(static_cast(grabber)); } QQuickItem *QQuickEventPoint::grabberItem() const { - return (m_grabberIsHandler ? nullptr : static_cast(m_grabber.data())); + return (m_grabberIsHandler ? nullptr : static_cast(m_exclusiveGrabber.data())); } void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) { - if (grabber != m_grabber.data()) { + if (grabber != m_exclusiveGrabber.data()) { if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { auto device = static_cast(parent())->device(); static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); QString deviceName = (device ? device->name() : QLatin1String("null device")); deviceName.resize(16, ' '); // shorten, and align in case of sequential output qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) - << ": grab" << m_grabber << "->" << grabber; + << ": grab" << m_exclusiveGrabber << "->" << grabber; } - m_grabber = QPointer(grabber); + m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = false; m_sceneGrabPos = m_scenePos; } @@ -587,35 +587,76 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const { - return (m_grabberIsHandler ? static_cast(m_grabber.data()) : nullptr); + return (m_grabberIsHandler ? static_cast(m_exclusiveGrabber.data()) : nullptr); } -void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber) +void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive) { - if (grabber != m_grabber.data()) { - if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { - auto device = static_cast(parent())->device(); - static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output + if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { + auto device = static_cast(parent())->device(); + static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); + QString deviceName = (device ? device->name() : QLatin1String("null device")); + deviceName.resize(16, ' '); // shorten, and align in case of sequential output + if (exclusive) { + if (m_exclusiveGrabber != grabber) + qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber; + } else { qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) - << ": grab" << m_grabber << "->" << grabber; + << ": grab (passive)" << grabber; } - m_grabber = QPointer(grabber); - m_grabberIsHandler = true; - m_sceneGrabPos = m_scenePos; + } + if (exclusive) { + if (grabber != m_exclusiveGrabber.data()) { + m_exclusiveGrabber = QPointer(grabber); + m_grabberIsHandler = true; + m_sceneGrabPos = m_scenePos; + m_passiveGrabbers.removeAll(QPointer(grabber)); + } + } else { + if (!grabber) { + qDebug() << "can't set passive grabber to null"; + return; + } + auto ptr = QPointer(grabber); + if (!m_passiveGrabbers.contains(ptr)) + m_passiveGrabbers.append(ptr); } } -void QQuickEventPoint::cancelGrab() +void QQuickEventPoint::cancelExclusiveGrab() { - if (m_grabber.isNull()) { + if (m_exclusiveGrabber.isNull()) { qWarning("cancelGrab: no grabber"); return; } + if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { + // TODO somehow avoid duplicating this code from setGrabberPointerHandler + auto device = static_cast(parent())->device(); + static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); + QString deviceName = (device ? device->name() : QLatin1String("null device")); + deviceName.resize(16, ' '); // shorten, and align in case of sequential output + qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; + } if (auto handler = grabberPointerHandler()) handler->handleGrabCancel(this); - m_grabber.clear(); + m_exclusiveGrabber.clear(); +} + +void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) +{ + if (m_passiveGrabbers.removeOne(QPointer(handler))) + handler->handleGrabCancel(this); +} + +void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) +{ + if (m_exclusiveGrabber == handler) { + handler->handleGrabCancel(this); + m_exclusiveGrabber.clear(); + } + cancelPassiveGrab(handler); } void QQuickEventPoint::setAccepted(bool accepted) @@ -738,6 +779,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) switch (ev->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: + m_mousePoint->clearPassiveGrabbers(); state = Qt::TouchPointPressed; break; case QEvent::MouseButtonRelease: @@ -779,13 +821,19 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) // Make sure the grabbers are right from one event to the next QVector grabbers; + QVector > > passiveGrabberses; + passiveGrabberses.reserve(newPointCount); // Copy all grabbers, because the order of points might have changed in the event. // The ID is all that we can rely on (release might remove the first point etc). for (int i = 0; i < newPointCount; ++i) { QObject *grabber = nullptr; - if (auto point = pointById(tps.at(i).id())) + QVector > passiveGrabbers; + if (auto point = pointById(tps.at(i).id())) { grabber = point->grabber(); + passiveGrabbers = point->passiveGrabbers(); + } grabbers.append(grabber); + passiveGrabberses.append(passiveGrabbers); } for (int i = 0; i < newPointCount; ++i) { @@ -795,8 +843,10 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) if (grabbers.at(i)) qWarning() << "TouchPointPressed without previous release event" << point; point->setGrabberItem(nullptr); + point->clearPassiveGrabbers(); } else { point->setGrabber(grabbers.at(i)); + point->setPassiveGrabbers(passiveGrabberses.at(i)); } } m_pointCount = newPointCount; @@ -822,7 +872,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { } QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) - : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0), + : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0), m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false), m_grabberIsHandler(false) { Q_UNUSED(m_reserved); @@ -859,6 +909,7 @@ QVector QQuickPointerMouseEvent::grabbers() const void QQuickPointerMouseEvent::clearGrabbers() const { m_mousePoint->setGrabberItem(nullptr); + m_mousePoint->clearPassiveGrabbers(); } bool QQuickPointerMouseEvent::hasGrabber(const QQuickPointerHandler *handler) const @@ -904,8 +955,10 @@ QVector QQuickPointerTouchEvent::grabbers() const } void QQuickPointerTouchEvent::clearGrabbers() const { - for (auto point: m_touchPoints) + for (auto point: m_touchPoints) { point->setGrabber(nullptr); + point->clearPassiveGrabbers(); + } } bool QQuickPointerTouchEvent::hasGrabber(const QQuickPointerHandler *handler) const diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 1751e366b2..5386eacad9 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -302,9 +302,15 @@ public: void setGrabberItem(QQuickItem *grabber); QQuickPointerHandler *grabberPointerHandler() const; - void setGrabberPointerHandler(QQuickPointerHandler *grabber); + void setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive = false); - Q_INVOKABLE void cancelGrab(); + Q_INVOKABLE void cancelExclusiveGrab(); + Q_INVOKABLE void cancelPassiveGrab(QQuickPointerHandler *handler); + void cancelAllGrabs(QQuickPointerHandler *handler); + + QVector > passiveGrabbers() const { return m_passiveGrabbers; } + void setPassiveGrabbers(const QVector > &grabbers) { m_passiveGrabbers = grabbers; } + void clearPassiveGrabbers() { m_passiveGrabbers.clear(); } private: QVector2D estimatedVelocity() const; @@ -316,7 +322,8 @@ private: QPointF m_sceneGrabPos; QVector2D m_velocity; quint64 m_pointId; - QPointer m_grabber; + QPointer m_exclusiveGrabber; + QVector > m_passiveGrabbers; ulong m_timestamp; ulong m_pressTimestamp; State m_state; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index fc53420cdd..457d59ea39 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1680,8 +1680,10 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven auto handler = point->grabberPointerHandler(); pointerEvent->localize(handler->parentItem()); handler->handlePointerEvent(pointerEvent); - if (mouseIsReleased) - point->setGrabberPointerHandler(nullptr); + if (mouseIsReleased) { + point->setGrabberPointerHandler(nullptr, true); + point->clearPassiveGrabbers(); + } } } else { bool delivered = false; -- cgit v1.2.3 From 146c0bd566a0a1d4bd92619eefeae272d45a44bc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 29 Nov 2016 14:50:05 +0100 Subject: add categorized log message in QQuickEventPoint::cancelPassiveGrab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and move some duplicated formatting code into helper functions Change-Id: If572278f0342f7476ee1ea32b0cfb51cf18f11da Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 44 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index eb4a6636f2..bd4e48fb05 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -467,6 +467,22 @@ Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice typedef QHash PointerDeviceForDeviceIdHash; Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices) +// debugging helpers +static const char *pointStateString(const QQuickEventPoint *point) +{ + static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State")); + return stateMetaEnum.valueToKey(point->state()); +} + +static const QString pointDeviceName(const QQuickEventPoint *point) +{ + auto device = static_cast(point->parent())->device(); + QString deviceName = (device ? device->name() : QLatin1String("null device")); + deviceName.resize(16, ' '); // shorten, and align in case of sequential output + return deviceName; +} + + QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d) { if (g_touchDevices->contains(d)) @@ -572,11 +588,7 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) { if (grabber != m_exclusiveGrabber.data()) { if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { - auto device = static_cast(parent())->device(); - static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output - qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab" << m_exclusiveGrabber << "->" << grabber; } m_exclusiveGrabber = QPointer(grabber); @@ -593,16 +605,12 @@ QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive) { if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { - auto device = static_cast(parent())->device(); - static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output if (exclusive) { if (m_exclusiveGrabber != grabber) - qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber; } else { - qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab (passive)" << grabber; } } @@ -631,12 +639,7 @@ void QQuickEventPoint::cancelExclusiveGrab() return; } if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { - // TODO somehow avoid duplicating this code from setGrabberPointerHandler - auto device = static_cast(parent())->device(); - static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State")); - QString deviceName = (device ? device->name() : QLatin1String("null device")); - deviceName.resize(16, ' '); // shorten, and align in case of sequential output - qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state()) + qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; } if (auto handler = grabberPointerHandler()) @@ -646,8 +649,13 @@ void QQuickEventPoint::cancelExclusiveGrab() void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) { - if (m_passiveGrabbers.removeOne(QPointer(handler))) + if (m_passiveGrabbers.removeOne(QPointer(handler))) { + if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { + qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) + << ": grab (passive)" << handler << "removed"; + } handler->handleGrabCancel(this); + } } void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) -- cgit v1.2.3 From f3cf71bae2e4bdc83ffa661d7273f6709b006615 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 5 Dec 2016 12:08:02 +0100 Subject: notify all passive-grabbing PointerHandlers when exclusive grab changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I45df7abdbd91e068a69101ed27e2ec44272e3899 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 7 +++++++ src/quick/handlers/qquickpointerhandler_p.h | 1 + src/quick/items/qquickevents.cpp | 3 ++- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index c91fb2de88..641324e61e 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -176,6 +176,13 @@ void QQuickPointerHandler::handleGrabCancel(QQuickEventPoint *point) emit canceled(point); } +void QQuickPointerHandler::handleGrab(QQuickEventPoint *point, QQuickPointerHandler *grabber, bool grab) +{ + Q_UNUSED(point); + Q_UNUSED(grabber); + Q_UNUSED(grab); +} + bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) { Q_UNUSED(event) diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 526a60262b..0931cfcfad 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -103,6 +103,7 @@ protected: void setExclusiveGrab(QQuickEventPoint *point, bool grab = true); void cancelAllGrabs(QQuickEventPoint *point); virtual void handleGrabCancel(QQuickEventPoint *point); + virtual void handleGrab(QQuickEventPoint *point, QQuickPointerHandler *grabber, bool grab); QPointF eventPos(const QQuickEventPoint *point) const; bool parentContains(const QQuickEventPoint *point) const; diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index bd4e48fb05..c3f6964ba6 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -619,7 +619,8 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = true; m_sceneGrabPos = m_scenePos; - m_passiveGrabbers.removeAll(QPointer(grabber)); + for (QPointer passiveGrabber : m_passiveGrabbers) + passiveGrabber->handleGrab(this, grabber, true); } } else { if (!grabber) { -- cgit v1.2.3 From 34d17ce0243ffad278ac7cf57804bf150524940a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 5 Dec 2016 12:20:31 +0100 Subject: prioritize delivery to passive grabbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I445c53ea74e26318aa1193a963cbcae601b0f76d Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 74 +++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 457d59ea39..10144e6eb9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1693,15 +1693,27 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven delivered = deliverPressEvent(pointerEvent, &hasFiltered); } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { // if this is an update or release from an actual mouse, - // and the point wasn't grabbed, deliver only to non-grabber PointerHandlers - QVector targetItems = pointerTargets(contentItem, point->scenePos(), false); - for (QQuickItem *item : targetItems) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - pointerEvent->localize(item); - if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers - delivered = true; - if (point->grabber()) - break; + // and the point wasn't grabbed, deliver only to PointerHandlers: + // passive grabbers first, then the rest + QVector &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + for (auto handler : point->passiveGrabbers()) { + // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + handler->handlePointerEvent(pointerEvent); + eventDeliveryTargets.append(handler); + } + } + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!pointerEvent->allPointsGrabbed()) { + QVector targetItems = pointerTargets(contentItem, point->scenePos(), false); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + if (point->grabber()) + break; + } } } @@ -2293,26 +2305,42 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve // If some points weren't grabbed, deliver only to non-grabber PointerHandlers if (!event->allPointsGrabbed()) { - QVector targetItems; int pointCount = event->pointCount(); + + // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) + QVector &eventDeliveryTargets = event->device()->eventDeliveryTargets(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); - QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); - if (targetItems.count()) { - targetItems = mergePointerTargets(targetItems, targetItemsForPoint); - } else { - targetItems = targetItemsForPoint; + for (auto handler : point->passiveGrabbers()) { + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + handler->handlePointerEvent(event); + eventDeliveryTargets.append(handler); + } } } - for (QQuickItem *item: targetItems) { - if (grabbers.contains(item)) - continue; - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - event->localize(item); - itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers - if (event->allPointsGrabbed()) - break; + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!event->allPointsGrabbed()) { + QVector targetItems; + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } + + for (QQuickItem *item: targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } } } -- cgit v1.2.3 From af21677f473bce12ee7c725b1151ae794384ae76 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 7 Feb 2017 13:24:49 +0100 Subject: QQuickWindow: remove sendFilteredTouchEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functionality is now completely assimilated into sendFilteredPointerEvent. Flickable can now steal the touch grab from a TapHandler child, for example: the TapHandler grabbed the eventpoint, whereas QQuickFlickable::childMouseEventFilter() was given a synthetic mouse event derived from the touchpoint which the eventpoint represented, and we fully follow through the consequences of that. If an Item filters a press event, avoid doing normal delivery to that same Item again. Change-Id: Icd9c88ab752f2b728f7d612504013c6dc72ff9fe Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 13 +++ src/quick/items/qquickevents_p_p.h | 3 + src/quick/items/qquickwindow.cpp | 181 +++++++++++++++---------------------- src/quick/items/qquickwindow_p.h | 7 +- 4 files changed, 91 insertions(+), 113 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c3f6964ba6..0af19ff750 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -896,6 +896,10 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const { return m_mousePoint->isAccepted(); } +bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const { + return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted(); +} + bool QQuickPointerMouseEvent::allPointsGrabbed() const { return m_mousePoint->grabber() != nullptr; @@ -941,6 +945,15 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const { return true; } +bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const { + for (int i = 0; i < m_pointCount; ++i) { + auto point = m_touchPoints.at(i); + if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted()) + return false; + } + return true; +} + bool QQuickPointerTouchEvent::allPointsGrabbed() const { for (int i = 0; i < m_pointCount; ++i) { diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 5386eacad9..d042f318e9 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -400,6 +400,7 @@ public: // helpers for C++ only (during event delivery) virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; } bool isValid() const { return m_event != nullptr; } virtual bool allPointsAccepted() const = 0; + virtual bool allUpdatedPointsAccepted() const = 0; virtual bool allPointsGrabbed() const = 0; bool isAccepted() { return m_event->isAccepted(); } void setAccepted(bool accepted) { m_event->setAccepted(accepted); } @@ -439,6 +440,7 @@ public: QQuickEventPoint *point(int i) const override; QQuickEventPoint *pointById(quint64 pointId) const override; bool allPointsAccepted() const override; + bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; @@ -472,6 +474,7 @@ public: QQuickEventPoint *pointById(quint64 pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; + bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; QVector grabbers() const override; void clearGrabbers() const override; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 10144e6eb9..7321ebdfb2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1689,8 +1689,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven bool delivered = false; if (pointerEvent->isPressEvent()) { // send initial press - QSet hasFiltered; - delivered = deliverPressEvent(pointerEvent, &hasFiltered); + delivered = deliverPressEvent(pointerEvent); } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { // if this is an update or release from an actual mouse, // and the point wasn't grabbed, deliver only to PointerHandlers: @@ -2250,11 +2249,10 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - QSet hasFiltered; if (event->isPressEvent()) - deliverPressEvent(event, &hasFiltered); - if (!event->allPointsAccepted()) - deliverUpdatedTouchPoints(event, &hasFiltered); + deliverPressEvent(event); + if (!event->allUpdatedPointsAccepted()) + deliverUpdatedTouchPoints(event); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2281,7 +2279,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } // Deliver touch points to existing grabbers -bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) { const auto grabbers = event->grabbers(); for (auto grabber : grabbers) { @@ -2300,7 +2298,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). - deliverMatchingPointsToItem(receiver, event, hasFiltered); + deliverMatchingPointsToItem(receiver, event); } // If some points weren't grabbed, deliver only to non-grabber PointerHandlers @@ -2324,6 +2322,8 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QVector targetItems; for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); + if (point->state() == QQuickEventPoint::Pressed) + continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); @@ -2348,7 +2348,7 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } // Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) { const QVector points = event->unacceptedPressedPointScenePositions(); QVector targetItems; @@ -2368,7 +2368,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSetallPointsAccepted()) break; } @@ -2376,7 +2376,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSetallPointsAccepted(); } -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *hasFiltered) +bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2415,28 +2415,22 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo return false; } - QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); - if (!event) + QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); + if (!ptEvent) return false; - QScopedPointer touchEvent(event->touchEventForItem(item)); + QScopedPointer touchEvent(ptEvent->touchEventForItem(item)); if (!touchEvent) return false; - qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; - // First check whether the parent wants to be a filter, - // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { - // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints - qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - for (auto point: qAsConst(touchEvent->touchPoints())) { - event->pointById(point.id())->setAccepted(); - } + // If any parent filters the event, we're done. + // updateFilteringParentItems was called when the press occurred, + // and we assume that the filtering relationships don't change between press and release. + if (sendFilteredPointerEvent(pointerEvent, item)) return true; - } // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; @@ -2446,7 +2440,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, event)) + if (deliverTouchAsMouse(item, ptEvent)) eventAccepted = true; } @@ -2454,7 +2448,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // If the touch was accepted (regardless by whom or in what form), // update accepted new points. for (auto point: qAsConst(touchEvent->touchPoints())) { - auto pointerEventPoint = event->pointById(point.id()); + auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); if (point.state() == Qt::TouchPointPressed) pointerEventPoint->setGrabberItem(item); @@ -2464,9 +2458,9 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // will not be interested in further updates for those touchpoint IDs either. for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { - if (event->pointById(point.id())->grabber() == item) { + if (ptEvent->pointById(point.id())->grabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabberItem(nullptr); + ptEvent->pointById(point.id())->setGrabberItem(nullptr); } } } @@ -2645,82 +2639,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -// TODO assimilate this logic and remove this function -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *hasFiltered) -{ - Q_Q(QQuickWindow); - - if (!target) - return false; - - bool filtered = false; - - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { - hasFiltered->insert(target); - QScopedPointer targetEvent(event->touchEventForItem(target, true)); - if (targetEvent) { - if (target->childMouseEventFilter(item, targetEvent.data())) { - qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; - QVector touchIds; - const int touchPointCount = targetEvent->touchPoints().size(); - touchIds.reserve(touchPointCount); - for (int i = 0; i < touchPointCount; ++i) - touchIds.append(targetEvent->touchPoints().at(i).id()); - target->grabTouchPoints(touchIds); - if (q->mouseGrabberItem()) { - q->mouseGrabberItem()->ungrabMouse(); - touchMouseId = -1; - touchMouseDevice = nullptr; - } - filtered = true; - } - - for (int i = 0; i < targetEvent->touchPoints().size(); ++i) { - const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i); - - QEvent::Type t; - switch (tp.state()) { - case Qt::TouchPointPressed: - t = QEvent::MouseButtonPress; - break; - case Qt::TouchPointReleased: - t = QEvent::MouseButtonRelease; - break; - case Qt::TouchPointStationary: - continue; - default: - t = QEvent::MouseMove; - break; - } - - // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if (touchMouseId == -1 || touchMouseId == tp.id()) { - // targetEvent is already transformed wrt local position, velocity, etc. - - // FIXME: remove asTouchEvent!!! - QScopedPointer mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); - 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())->setGrabberItem(target); - target->grabMouse(); - } - filtered = true; - } - // Only one event can be filtered as a mouse event. - break; - } - } - } - } - - return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; -} - void QQuickWindowPrivate::updateFilteringParentItems(const QVector &targetItems) { if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) { @@ -2770,16 +2688,61 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ } } } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { - QTouchEvent *te = pte->asTouchEvent(); for (QPair itemAndParent : filteringParentItems) { QQuickItem *item = receiver ? receiver : itemAndParent.first; QQuickItem *filteringParent = itemAndParent.second; if (item == filteringParent) continue; // a filtering item never needs to filter for itself - if (filteringParent->childMouseEventFilter(item, te)) { - for (auto point: qAsConst(te->touchPoints())) + // get a touch event customized for delivery to filteringParent + QScopedPointer filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true)); + if (!filteringParentTouchEvent) + continue; // all points are stationary, or no touchpoints inside that parent + if (filteringParent->childMouseEventFilter(item, filteringParentTouchEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; + for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) event->pointById(point.id())->setAccepted(); - return true; + ret = true; + break; + } + // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. + for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) { + const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i); + + QEvent::Type t; + switch (tp.state()) { + case Qt::TouchPointPressed: + t = QEvent::MouseButtonPress; + break; + case Qt::TouchPointReleased: + t = QEvent::MouseButtonRelease; + break; + case Qt::TouchPointStationary: + continue; + default: + t = QEvent::MouseMove; + break; + } + + // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId + if (touchMouseId == -1 || touchMouseId == tp.id()) { + // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) + // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that + QScopedPointer mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false)); + if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; + if (t != QEvent::MouseButtonRelease) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << filteringParent; + touchMouseId = tp.id(); + touchMouseDevice = event->device(); + touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent); + filteringParent->grabMouse(); + } + ret = true; + } + // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter + // has been called once, we're done with this loop over the touchpoints. + break; + } } } } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 38c1b0a4d4..b0d4d1ce01 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -168,10 +168,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressEvent(QQuickPointerEvent *, QSet *); - bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet *hasFiltered); - bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet *filtered); - bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet *filtered); + bool deliverPressEvent(QQuickPointerEvent *); + bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); + bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; -- cgit v1.2.3 From 0e7b9b949cd2090c25875df6e863847ce7c8f19a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 14 Feb 2017 15:23:43 +0100 Subject: QQuickWindow cat logging: show touchpoint IDs in hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup to 035c17791c2525cbfce4b8a9cc0e0f0c28eae46d Change-Id: I0dc6fc2af3143d6aa4b73c12a43bfd27fe7821a3 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 7321ebdfb2..ddb6ee0a1e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -654,7 +654,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!item->contains(pos)) break; - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << item; QScopedPointer mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false)); // Send a single press and see if that's accepted @@ -690,7 +690,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << mouseGrabberItem; } return event->isAccepted(); } else { @@ -2459,7 +2459,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { if (ptEvent->pointById(point.id())->grabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; + qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated"; ptEvent->pointById(point.id())->setGrabberItem(nullptr); } } @@ -2731,7 +2731,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) { qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; if (t != QEvent::MouseButtonRelease) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << filteringParent; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent; touchMouseId = tp.id(); touchMouseDevice = event->device(); touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent); -- cgit v1.2.3 From bc98a541c810e5cf162aa42fa3458c50673629b3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 8 Feb 2017 09:06:32 +0100 Subject: remove unused return values in QQuickWindowPrivate delivery functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie75bd04f1ec12805b83f218d86f7b235ba44e0fc Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 30 ++++++++++++------------------ src/quick/items/qquickwindow_p.h | 4 ++-- 2 files changed, 14 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index ddb6ee0a1e..4bd63b3cf8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2279,7 +2279,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } // Deliver touch points to existing grabbers -bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) +void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) { const auto grabbers = event->grabbers(); for (auto grabber : grabbers) { @@ -2290,11 +2290,11 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickPointerHandler *handler = static_cast(grabber); receiver = static_cast(grabber)->parentItem(); if (sendFilteredPointerEvent(event, receiver)) - return true; + return; event->localize(receiver); handler->handlePointerEvent(event); if (event->allPointsAccepted()) - return true; + return; } // If the grabber is an item or the grabbing handler didn't handle it, // then deliver the event to the item (which may have multiple handlers). @@ -2343,8 +2343,6 @@ bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } } } - - return false; } // Deliver newly pressed touch points @@ -2376,7 +2374,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) return event->allPointsAccepted(); } -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2385,14 +2383,14 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // Let the Item's handlers (if any) have the event first. itemPrivate->handlePointerEvent(pointerEvent); if (pointerEvent->allPointsAccepted()) - return true; + return; // TODO: unite this mouse point delivery with the synthetic mouse event below - if (auto event = pointerEvent->asPointerMouseEvent()) { - if (item->acceptedMouseButtons() & event->button()) { + auto event = pointerEvent->asPointerMouseEvent(); + if (event && item->acceptedMouseButtons() & event->button()) { auto point = event->point(0); if (point->isAccepted()) - return false; + return; // The only reason to already have a mouse grabber here is // synthetic events - flickable sends one when setPressDelay is used. auto oldMouseGrabber = q->mouseGrabberItem(); @@ -2410,18 +2408,16 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo } point->setAccepted(true); } - return me->isAccepted(); - } - return false; + return; } QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); if (!ptEvent) - return false; + return; QScopedPointer touchEvent(ptEvent->touchEventForItem(item)); if (!touchEvent) - return false; + return; qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; @@ -2430,7 +2426,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // updateFilteringParentItems was called when the press occurred, // and we assume that the filtering relationships don't change between press and release. if (sendFilteredPointerEvent(pointerEvent, item)) - return true; + return; // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; @@ -2465,8 +2461,6 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo } } } - - return eventAccepted; } #if QT_CONFIG(draganddrop) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index b0d4d1ce01..78f3b68ebb 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -169,8 +169,8 @@ public: void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); bool deliverPressEvent(QQuickPointerEvent *); - bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); - bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); + void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); + void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; -- cgit v1.2.3 From 87e74766d33bf364e42839d73e6b8b5e9cd39a07 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 15 Feb 2017 10:18:51 +0100 Subject: Notify timeHeld property changed whenever press state changes Otherwise, we might read the previous timeHeld value (when we released) in for instance the onPressed handler. Change-Id: I2678ad95fad9bd5062573b7ca6d2bff08ce29b87 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquicktaphandler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 5065e87e8e..228b6bbaf3 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -246,6 +246,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi if (m_pressed != press) { m_pressed = press; connectPreRenderSignal(press); + updateTimeHeld(); if (press) { m_longPressTimer.start(longPressThresholdMilliseconds(), this); m_holdTimer.start(); -- cgit v1.2.3 From 7042cfd9cb1b552c5fd753b6912439ce604eb1a0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 20 Jan 2017 11:41:23 +0100 Subject: allow stealing grab from handlers; notify passive grabbers when stolen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now if a Button with a TapHandler is pressed and has only a passive grab, Flickable can take the exclusive grab, and it notifies the TapHandler so that the button can go back to released state. This reverts parts of commit e2fd141372335f917c2d216051abb00d8b15f87c such that more of tst_PointerHandlers is working the same as it was before we started adding the passive grab concept. Change-Id: I88970716fcbbfb066a313fcefb233cf9263da944 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickwindow.cpp | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 4bd63b3cf8..669e521b7f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -746,21 +746,28 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; QQuickItem *oldGrabber = q->mouseGrabberItem(); + qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); - if (point) + if (point) { point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); + } } else { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabberItem(grabber); + auto point = event->point(0); + point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); } + if (oldGrabber) { QEvent e(QEvent::UngrabMouse); QSet hasFiltered; @@ -1679,7 +1686,8 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven // if the grabber is not an Item, it must be a PointerHandler auto handler = point->grabberPointerHandler(); pointerEvent->localize(handler->parentItem()); - handler->handlePointerEvent(pointerEvent); + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); if (mouseIsReleased) { point->setGrabberPointerHandler(nullptr, true); point->clearPassiveGrabbers(); @@ -1698,18 +1706,21 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven for (auto handler : point->passiveGrabbers()) { // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { - handler->handlePointerEvent(pointerEvent); + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); eventDeliveryTargets.append(handler); } } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order - if (!pointerEvent->allPointsGrabbed()) { + if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { QVector targetItems = pointerTargets(contentItem, point->scenePos(), false); for (QQuickItem *item : targetItems) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); pointerEvent->localize(item); - if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers - delivered = true; + if (!sendFilteredPointerEvent(pointerEvent, item)) { + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + } if (point->grabber()) break; } @@ -2311,6 +2322,8 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickEventPoint *point = event->point(i); for (auto handler : point->passiveGrabbers()) { if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + if (sendFilteredPointerEvent(event, handler->parentItem())) + return; handler->handlePointerEvent(event); eventDeliveryTargets.append(handler); } @@ -2682,6 +2695,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ } } } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { + QVarLengthArray, 32> passiveGrabsToCancel; for (QPair itemAndParent : filteringParentItems) { QQuickItem *item = receiver ? receiver : itemAndParent.first; QQuickItem *filteringParent = itemAndParent.second; @@ -2730,6 +2744,12 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ touchMouseDevice = event->device(); touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent); filteringParent->grabMouse(); + auto pointerEventPoint = pte->pointById(tp.id()); + for (auto handler : pointerEventPoint->passiveGrabbers()) { + QPair grab(handler, pointerEventPoint); + if (!passiveGrabsToCancel.contains(grab)) + passiveGrabsToCancel.append(grab); + } } ret = true; } @@ -2739,6 +2759,8 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ } } } + for (auto grab : passiveGrabsToCancel) + grab.second->cancelPassiveGrab(grab.first); } return ret; } -- cgit v1.2.3 From e9a2155cfd76666043dee8c034708a0149f3e2e6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 10 Feb 2017 10:01:39 +0100 Subject: DragHandler: keep the grab (prevent stealing) when dragging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whenever the DragHandler gets an exclusive grab, it needs to prevent a parent Flickable (for example) from taking over that grab. Since it waits until the drag threshold is exceeded in an allowed dragging direction, this does not prevent the Flickable from being draggable in the other direction. We restore the state of keepTouchGrab and keepMouseGrab when ungrabbing. Change-Id: Id9d456c99322e0cb6996d1f690b38fcd6becc6f9 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 8 ++++++++ src/quick/handlers/qquickpointerhandler.cpp | 10 ++++++++++ src/quick/handlers/qquickpointerhandler_p.h | 2 ++ 3 files changed, 20 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 9575f7ede9..db17df36ca 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -98,6 +98,14 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { setExclusiveGrab(point); + if (target()) { + if (point->pointerEvent()->asPointerTouchEvent()) + target()->setKeepTouchGrab(true); + // tablet and mouse are treated the same by Item's legacy event handling, and + // touch becomes synth-mouse for Flickable, so we need to prevent stealing + // mouse grab too, whenever dragging occurs in an enabled direction + target()->setKeepMouseGrab(true); + } } } break; default: diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 641324e61e..90d07b5d6d 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -62,6 +62,8 @@ QQuickPointerHandler::QQuickPointerHandler(QObject *parent) , m_enabled(true) , m_active(false) , m_targetExplicitlySet(false) + , m_hadKeepMouseGrab(false) + , m_hadKeepTouchGrab(false) { } @@ -92,12 +94,20 @@ void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) { QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); if (grab && oldGrabber != this) { + if (target()) { + m_hadKeepMouseGrab = target()->keepMouseGrab(); + m_hadKeepTouchGrab = target()->keepTouchGrab(); + } if (oldGrabber) oldGrabber->handleGrabCancel(point); point->setGrabberPointerHandler(this, true); onGrabChanged(point); // emit grabChanged(point); // TODO maybe } else if (!grab && oldGrabber == this) { + if (auto tgt = target()) { + tgt->setKeepMouseGrab(m_hadKeepMouseGrab); + tgt->setKeepTouchGrab(m_hadKeepTouchGrab); + } point->setGrabberPointerHandler(nullptr, true); onGrabChanged(point); // emit grabChanged(point); // TODO maybe diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 0931cfcfad..39362ca2e0 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -113,6 +113,8 @@ private: bool m_enabled : 1; bool m_active : 1; bool m_targetExplicitlySet : 1; + bool m_hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state + bool m_hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state friend class QQuickEventPoint; }; -- cgit v1.2.3 From 8967a1b7b86306879a3113b290610b03727670ff Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 13 Feb 2017 15:18:43 +0100 Subject: clarify further exclusive vs. passive grabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add documentation to the grab-related event and eventpoint methods. Rename "grabber" functions which relate only to the exclusive grab, in cases where it would otherwise be ambiguous. And a few other documentation changes. Change-Id: I1a203c8c06a19d4abdb000f08b387c38341ef476 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 4 +- src/quick/items/qquickevents.cpp | 124 ++++++++++++++++++---- src/quick/items/qquickevents_p_p.h | 22 ++-- src/quick/items/qquickitem.cpp | 11 +- src/quick/items/qquickitem_p.h | 2 +- src/quick/items/qquickwindow.cpp | 24 ++--- 6 files changed, 137 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 41c9e1f322..c0c88f3852 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -83,7 +83,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) int c = event->pointCount(); for (int i = 0; i < c && !m_pointId; ++i) { QQuickEventPoint *p = event->point(i); - if (!p->grabber() && wantsEventPoint(p)) { + if (!p->exclusiveGrabber() && wantsEventPoint(p)) { m_pointId = p->pointId(); p->setAccepted(); } @@ -149,7 +149,7 @@ void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) void QQuickPointerSingleHandler::onGrabChanged(QQuickEventPoint *point) { - bool grabbing = (point->grabber() == this); + bool grabbing = (point->exclusiveGrabber() == this); setActive(grabbing); if (grabbing) m_sceneGrabPos = point->sceneGrabPos(); diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0af19ff750..1843d2b656 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -566,12 +566,26 @@ void QQuickEventPoint::invalidate() m_pointId = 0; } -QObject *QQuickEventPoint::grabber() const +/*! + If this point has an exclusive grabber, returns a pointer to it; else + returns null, if there is no grabber. The grabber could be either + an Item or a PointerHandler. +*/ +QObject *QQuickEventPoint::exclusiveGrabber() const { return m_exclusiveGrabber.data(); } -void QQuickEventPoint::setGrabber(QObject *grabber) +/*! + Set the given Item or PointerHandler as the exclusive grabber of this point. + If there was already an exclusive grab, it will be canceled. If there + were passive grabbers, they will continue to lurk, but the exclusive grab + is a behavioral override of the passive grab as long as it remains. + If you already know whether the grabber is to be an Item or a PointerHandler, + you should instead call setGrabberItem() or setGrabberPointerHandler(), + because it is slightly more efficient. +*/ +void QQuickEventPoint::setExclusiveGrabber(QObject *grabber) { if (QQuickPointerHandler *phGrabber = qmlobject_cast(grabber)) setGrabberPointerHandler(phGrabber, true); @@ -579,11 +593,22 @@ void QQuickEventPoint::setGrabber(QObject *grabber) setGrabberItem(static_cast(grabber)); } +/*! + If the exclusive grabber of this point is an Item, returns a + pointer to that Item; else returns null, if there is no grabber or if + the grabber is a PointerHandler. +*/ QQuickItem *QQuickEventPoint::grabberItem() const { return (m_grabberIsHandler ? nullptr : static_cast(m_exclusiveGrabber.data())); } +/*! + Set the given Item \a grabber as the exclusive grabber of this point. + If there was already an exclusive grab, it will be canceled. If there + were passive grabbers, they will continue to lurk, but the exclusive grab + is a behavioral override of the passive grab as long as it remains. +*/ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) { if (grabber != m_exclusiveGrabber.data()) { @@ -597,11 +622,21 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) } } +/*! + If the exclusive grabber of this point is a PointerHandler, returns a + pointer to that handler; else returns null, if there is no grabber or if + the grabber is an Item. +*/ QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const { return (m_grabberIsHandler ? static_cast(m_exclusiveGrabber.data()) : nullptr); } +/*! + Set the given PointerHandler \a grabber as grabber of this point. If \a + exclusive is true, it will override any other grabs; if false, \a grabber + will be added to the list of passive grabbers of this point. +*/ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive) { if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { @@ -633,6 +668,11 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b } } +/*! + If this point has an existing exclusive grabber (Item or PointerHandler), + inform the grabber that its grab is canceled, and remove it as grabber. + This normally happens when the grab is stolen by another Item. +*/ void QQuickEventPoint::cancelExclusiveGrab() { if (m_exclusiveGrabber.isNull()) { @@ -648,6 +688,11 @@ void QQuickEventPoint::cancelExclusiveGrab() m_exclusiveGrabber.clear(); } +/*! + If this point has the given \a handler as a passive grabber, + inform the grabber that its grab is canceled, and remove it as grabber. + This normally happens when another Item or PointerHandler does an exclusive grab. +*/ void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) { if (m_passiveGrabbers.removeOne(QPointer(handler))) { @@ -659,6 +704,17 @@ void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) } } +/*! + If the given \a handler is grabbing this point passively, exclusively + or both, cancel the grab and remove it as grabber. + This normally happens when the handler decides that the behavior of this + point can no longer satisfy the handler's behavioral constraints within + the remainder of the gesture which the user is performing: for example + the handler tries to detect a tap but a drag is occurring instead, or + it tries to detect a drag in one direction but the drag is going in + another direction. In such cases the handler no longer needs or wants + to be informed of any further movements of this point. +*/ void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) { if (m_exclusiveGrabber == handler) { @@ -668,6 +724,12 @@ void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) cancelPassiveGrab(handler); } +/*! + Set this point as \a accepted (true) or rejected (false). + Accepting a point is intended to stop event propagation. + It does not imply any kind of grab, passive or exclusive. + TODO explain further under what conditions propagation really does stop... +*/ void QQuickEventPoint::setAccepted(bool accepted) { if (m_accept != accepted) { @@ -707,11 +769,10 @@ Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData) static const int PointVelocityAgeLimit = 500; // milliseconds /*! - * \interal - * \brief Estimates the velocity based on a weighted average of all previous velocities. - * The older the velocity is, the less significant it becomes for the estimate. - * \return - */ + \internal + Estimates the velocity based on a weighted average of all previous velocities. + The older the velocity is, the less significant it becomes for the estimate. +*/ QVector2D QQuickEventPoint::estimatedVelocity() const { PointVelocityData *prevPoint = g_previousPointData->value(m_pointId); @@ -838,7 +899,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) QObject *grabber = nullptr; QVector > passiveGrabbers; if (auto point = pointById(tps.at(i).id())) { - grabber = point->grabber(); + grabber = point->exclusiveGrabber(); passiveGrabbers = point->passiveGrabbers(); } grabbers.append(grabber); @@ -854,7 +915,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) point->setGrabberItem(nullptr); point->clearPassiveGrabbers(); } else { - point->setGrabber(grabbers.at(i)); + point->setExclusiveGrabber(grabbers.at(i)); point->setPassiveGrabbers(passiveGrabberses.at(i)); } } @@ -902,7 +963,7 @@ bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const { bool QQuickPointerMouseEvent::allPointsGrabbed() const { - return m_mousePoint->grabber() != nullptr; + return m_mousePoint->exclusiveGrabber() != nullptr; } QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const @@ -912,22 +973,31 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons return event; } -QVector QQuickPointerMouseEvent::grabbers() const +/*! + Returns the exclusive grabber of this event, if any, in a vector. +*/ +QVector QQuickPointerMouseEvent::exclusiveGrabbers() const { QVector result; - if (QObject *grabber = m_mousePoint->grabber()) + if (QObject *grabber = m_mousePoint->exclusiveGrabber()) result << grabber; return result; } +/*! + Remove all passive and exclusive grabbers of this event, without notifying. +*/ void QQuickPointerMouseEvent::clearGrabbers() const { m_mousePoint->setGrabberItem(nullptr); m_mousePoint->clearPassiveGrabbers(); } -bool QQuickPointerMouseEvent::hasGrabber(const QQuickPointerHandler *handler) const +/*! + Returns whether the given \a handler is the exclusive grabber of this event. +*/ +bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const { - return m_mousePoint->grabber() == handler; + return m_mousePoint->exclusiveGrabber() == handler; } bool QQuickPointerMouseEvent::isPressEvent() const @@ -957,18 +1027,20 @@ bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const { bool QQuickPointerTouchEvent::allPointsGrabbed() const { for (int i = 0; i < m_pointCount; ++i) { - if (!m_touchPoints.at(i)->grabber()) + if (!m_touchPoints.at(i)->exclusiveGrabber()) return false; } return true; } -QVector QQuickPointerTouchEvent::grabbers() const +/*! + Returns the exclusive grabbers of all points in this event, if any, in a vector. +*/ +QVector QQuickPointerTouchEvent::exclusiveGrabbers() const { QVector result; for (int i = 0; i < m_pointCount; ++i) { - auto point = m_touchPoints.at(i); - if (QObject *grabber = point->grabber()) { + if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) { if (!result.contains(grabber)) result << grabber; } @@ -976,17 +1048,25 @@ QVector QQuickPointerTouchEvent::grabbers() const return result; } +/*! + Remove all passive and exclusive grabbers of all touchpoints in this event, + without notifying. +*/ void QQuickPointerTouchEvent::clearGrabbers() const { for (auto point: m_touchPoints) { - point->setGrabber(nullptr); + point->setGrabberItem(nullptr); point->clearPassiveGrabbers(); } } -bool QQuickPointerTouchEvent::hasGrabber(const QQuickPointerHandler *handler) const +/*! + Returns whether the given \a handler is the exclusive grabber of any + touchpoint within this event. +*/ +bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const { for (auto point: m_touchPoints) - if (point->grabber() == handler) + if (point->exclusiveGrabber() == handler) return true; return false; } @@ -1111,7 +1191,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i auto p = m_touchPoints.at(i); if (p->isAccepted()) continue; - bool isGrabber = p->grabber() == item; + bool isGrabber = p->exclusiveGrabber() == item; bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos())); if (!(isGrabber || isPressInside || isFiltering)) continue; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index d042f318e9..00da53eb48 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -263,7 +263,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - Q_PROPERTY(QObject *grabber READ grabber WRITE setGrabber) + Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber) public: enum State { @@ -295,14 +295,14 @@ public: bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); bool isDraggedOverThreshold() const; - QObject *grabber() const; - void setGrabber(QObject *grabber); + QObject *exclusiveGrabber() const; + void setExclusiveGrabber(QObject *exclusiveGrabber); QQuickItem *grabberItem() const; - void setGrabberItem(QQuickItem *grabber); + void setGrabberItem(QQuickItem *exclusiveGrabber); QQuickPointerHandler *grabberPointerHandler() const; - void setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive = false); + void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false); Q_INVOKABLE void cancelExclusiveGrab(); Q_INVOKABLE void cancelPassiveGrab(QQuickPointerHandler *handler); @@ -409,9 +409,9 @@ public: // helpers for C++ only (during event delivery) virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; - virtual QVector grabbers() const = 0; + virtual QVector exclusiveGrabbers() const = 0; virtual void clearGrabbers() const = 0; - virtual bool hasGrabber(const QQuickPointerHandler *handler) const = 0; + virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0; ulong timestamp() const { return m_event->timestamp(); } @@ -442,9 +442,9 @@ public: bool allPointsAccepted() const override; bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; - QVector grabbers() const override; + QVector exclusiveGrabbers() const override; void clearGrabbers() const override; - bool hasGrabber(const QQuickPointerHandler *handler) const override; + bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *asMouseEvent(const QPointF& localPos) const; @@ -476,9 +476,9 @@ public: bool allPointsAccepted() const override; bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; - QVector grabbers() const override; + QVector exclusiveGrabbers() const override; void clearGrabbers() const override; - bool hasGrabber(const QQuickPointerHandler *handler) const override; + bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override; QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const; QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 9b91b4bda4..7adf455ac6 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5091,7 +5091,14 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) } } -bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidGrabber) +/*! + \internal + Deliver the \a event to all PointerHandlers which are in the pre-determined + eventDeliveryTargets() vector. If \a avoidExclusiveGrabber is true, it skips + delivery to any handler which is the exclusive grabber of any point within this event + (because delivery to exclusive grabbers is handled separately). +*/ +bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber) { Q_Q(QQuickItem); bool delivered = false; @@ -5099,7 +5106,7 @@ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoid if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; - if ((!avoidGrabber || !event->hasGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { + if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { handler->handlePointerEvent(event); delivered = true; eventDeliveryTargets.append(handler); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index b39c437da5..6ce6a233d6 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -563,7 +563,7 @@ public: #endif void deliverShortcutOverrideEvent(QKeyEvent *); - virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidGrabber = false); + virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false); bool isTransparentForPositioner() const; void setTransparentForPositioner(bool trans); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 669e521b7f..0cd9357b9a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -790,7 +790,7 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector & auto point = touchMouseDevice->pointerEvent()->pointById(id); auto touchMouseGrabber = point->grabberItem(); if (touchMouseGrabber) { - point->setGrabber(nullptr); + point->setExclusiveGrabber(nullptr); touchMouseGrabber->mouseUngrabEvent(); ungrab.insert(touchMouseGrabber); touchMouseDevice = nullptr; @@ -804,11 +804,11 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector & auto point = device->pointerEvent()->pointById(id); if (!point) continue; - QObject *oldGrabber = point->grabber(); + QObject *oldGrabber = point->exclusiveGrabber(); if (oldGrabber == grabber) continue; - point->setGrabber(grabber); + point->setExclusiveGrabber(grabber); if (oldGrabber) ungrab.insert(oldGrabber); } @@ -827,7 +827,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to for (auto device : touchDevices) { auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { - if (pointerEvent->point(i)->grabber() == grabber) { + if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { pointerEvent->point(i)->setGrabberItem(nullptr); // FIXME send ungrab event only once grabber->touchUngrabEvent(); @@ -1658,7 +1658,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven auto point = pointerEvent->point(0); lastMousePosition = point->scenePos(); - if (point->grabber()) { + if (point->exclusiveGrabber()) { bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); if (auto grabber = point->grabberItem()) { if (sendFilteredPointerEvent(pointerEvent, grabber)) @@ -1721,7 +1721,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers delivered = true; } - if (point->grabber()) + if (point->exclusiveGrabber()) break; } } @@ -1929,7 +1929,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); - QVector grabbers = pointerEvent->grabbers(); + QVector grabbers = pointerEvent->exclusiveGrabbers(); for (QObject *grabber: qAsConst(grabbers)) { if (QQuickItem *grabberItem = qmlobject_cast(grabber)) @@ -2106,7 +2106,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!QQuickPointerDevice::genericMouseDevice()->pointerEvent()->point(0)->grabber()) { + if (!QQuickPointerDevice::genericMouseDevice()->pointerEvent()->point(0)->exclusiveGrabber()) { QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); @@ -2283,8 +2283,8 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } } - if (allReleased && !event->grabbers().isEmpty()) { - qWarning() << "No release received for some grabbers" << event->grabbers(); + if (allReleased && !event->exclusiveGrabbers().isEmpty()) { + qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers(); event->clearGrabbers(); } } @@ -2292,7 +2292,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) // Deliver touch points to existing grabbers void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) { - const auto grabbers = event->grabbers(); + const auto grabbers = event->exclusiveGrabbers(); for (auto grabber : grabbers) { // The grabber is guaranteed to be either an item or a handler. QQuickItem *receiver = qmlobject_cast(grabber); @@ -2467,7 +2467,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // will not be interested in further updates for those touchpoint IDs either. for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { - if (ptEvent->pointById(point.id())->grabber() == item) { + if (ptEvent->pointById(point.id())->exclusiveGrabber() == item) { qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated"; ptEvent->pointById(point.id())->setGrabberItem(nullptr); } -- cgit v1.2.3 From 507efe5a8a2390813fb620a91b0b3b6b383f599d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 14 Feb 2017 10:04:49 +0100 Subject: unify handler grab state handling into onGrabChanged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onGrabChanged and handleGrab looked redundant. It was also not clear how important it is for handlers to react to passive ungrabs, overrides or cancellations. Rather than debating about when to call one of these and when not to, let's centralize the responsibility in QQuickEventPoint (because the grabber pointers are stored there, so it's the ultimate destination of any grab change), and let's notify all the relevant handlers about all changes, with enough information that each handler can decide for itself what's important and what isn't. But so far most handlers don't need to override this virtual. The base class QQuickPointerHandler takes care of setting the active property to false, rejecting the eventpoint, and unsetting keepMouseGrab and keepTouchGrab whenever grab is lost; and emitting grabChanged or canceled as appropriate to notify any QML code which needs to know. Subclasses mainly care about the change of active state: they must initiate active state themselves, and may react when it reverts to false. Change-Id: I6c7f29472d12564d74ae091b0c81fa08fe131ce7 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 99 +++++++++++++---------- src/quick/handlers/qquickpointerhandler_p.h | 4 +- src/quick/handlers/qquickpointersinglehandler.cpp | 30 ++++--- src/quick/handlers/qquickpointersinglehandler_p.h | 3 +- src/quick/handlers/qquicktaphandler.cpp | 18 +++-- src/quick/handlers/qquicktaphandler_p.h | 2 +- src/quick/items/qquickevents.cpp | 32 ++++++-- src/quick/items/qquickevents_p_p.h | 12 +++ 8 files changed, 126 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 90d07b5d6d..88c6800dcc 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -76,48 +76,75 @@ QQuickPointerHandler::~QQuickPointerHandler() } } +/*! + Notification that the grab has changed in some way which is relevant to this handler. + The \a grabber (subject) will be the PointerHandler whose state is changing, + or null if the state change regards an Item. (TODO do we have any such cases?) + The \a stateChange (verb) tells what happened. + The \a point (object) is the point that was grabbed or ungrabbed. + EventPoint has the sole responsibility to call this function. + The PointerHandler must react in whatever way is appropriate, and must + emit the relevant signals (for the benefit of QML code). + A subclass is allowed to override this virtual function, but must always + call its parent class's implementation in addition to (usually after) + whatever custom behavior it implements. +*/ +void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) +{ + qCDebug(lcPointerHandlerDispatch) << point << stateChange << grabber; + Q_ASSERT(point); + if (grabber == this) { + bool wasCanceled = false; + emit grabChanged(point); + switch (stateChange) { + case QQuickEventPoint::GrabPassive: + case QQuickEventPoint::GrabExclusive: + break; + case QQuickEventPoint::CancelGrabPassive: + case QQuickEventPoint::CancelGrabExclusive: + wasCanceled = true; // the grab was stolen by something else + Q_FALLTHROUGH(); + case QQuickEventPoint::UngrabPassive: + case QQuickEventPoint::UngrabExclusive: + setActive(false); + point->setAccepted(false); + if (auto par = parentItem()) { + par->setKeepMouseGrab(m_hadKeepMouseGrab); + par->setKeepTouchGrab(m_hadKeepTouchGrab); + } + case QQuickEventPoint::OverrideGrabPassive: + // Passive grab is still there, but we won't receive point updates right now. + // No need to notify about this. + return; + } + if (wasCanceled) + emit canceled(point); + else + emit grabChanged(point); + } +} + void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab) { + qCDebug(lcPointerHandlerDispatch) << point << grab; if (grab) { point->setGrabberPointerHandler(this, false); - emit grabChanged(point); - } else if (point->grabberPointerHandler() == this) { - // TODO should giving up passive grab imply giving up exclusive grab too? - // we're being inconsistent here: check whether the exclusive grabber is this, - // then say that the passive grab was canceled. - point->cancelPassiveGrab(this); - emit grabChanged(point); + } else { + point->removePassiveGrabber(this); } } void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) { - QQuickPointerHandler *oldGrabber = point->grabberPointerHandler(); - if (grab && oldGrabber != this) { - if (target()) { - m_hadKeepMouseGrab = target()->keepMouseGrab(); - m_hadKeepTouchGrab = target()->keepTouchGrab(); - } - if (oldGrabber) - oldGrabber->handleGrabCancel(point); - point->setGrabberPointerHandler(this, true); - onGrabChanged(point); -// emit grabChanged(point); // TODO maybe - } else if (!grab && oldGrabber == this) { - if (auto tgt = target()) { - tgt->setKeepMouseGrab(m_hadKeepMouseGrab); - tgt->setKeepTouchGrab(m_hadKeepTouchGrab); - } - point->setGrabberPointerHandler(nullptr, true); - onGrabChanged(point); -// emit grabChanged(point); // TODO maybe - } + // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab + qCDebug(lcPointerHandlerDispatch) << point << grab; + point->setGrabberPointerHandler(grab ? this : nullptr, true); } void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point) { + qCDebug(lcPointerHandlerDispatch) << point; point->cancelAllGrabs(this); - emit grabChanged(point); } QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const @@ -177,22 +204,6 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) setActive(false); } -void QQuickPointerHandler::handleGrabCancel(QQuickEventPoint *point) -{ - qCDebug(lcPointerHandlerDispatch) << point; - Q_ASSERT(point); - setActive(false); - point->setAccepted(false); - emit canceled(point); -} - -void QQuickPointerHandler::handleGrab(QQuickEventPoint *point, QQuickPointerHandler *grabber, bool grab) -{ - Q_UNUSED(point); - Q_UNUSED(grabber); - Q_UNUSED(grab); -} - bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) { Q_UNUSED(event) diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 39362ca2e0..715000114e 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -98,12 +98,10 @@ protected: virtual void handlePointerEventImpl(QQuickPointerEvent *event); void setActive(bool active); virtual void onActiveChanged() { } - virtual void onGrabChanged(QQuickEventPoint *) { } + virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point); void setPassiveGrab(QQuickEventPoint *point, bool grab = true); void setExclusiveGrab(QQuickEventPoint *point, bool grab = true); void cancelAllGrabs(QQuickEventPoint *point); - virtual void handleGrabCancel(QQuickEventPoint *point); - virtual void handleGrab(QQuickEventPoint *point, QQuickPointerHandler *grabber, bool grab); QPointF eventPos(const QQuickEventPoint *point) const; bool parentContains(const QQuickEventPoint *point) const; diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index c0c88f3852..ec66112519 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -141,18 +141,28 @@ bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) return parentContains(point); } -void QQuickPointerSingleHandler::handleGrabCancel(QQuickEventPoint *point) +void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { - QQuickPointerHandler::handleGrabCancel(point); - reset(); -} - -void QQuickPointerSingleHandler::onGrabChanged(QQuickEventPoint *point) -{ - bool grabbing = (point->exclusiveGrabber() == this); - setActive(grabbing); - if (grabbing) + QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); + if (grabber != this) + return; + switch (stateChange) { + case QQuickEventPoint::GrabExclusive: + setActive(true); + Q_FALLTHROUGH(); + case QQuickEventPoint::GrabPassive: m_sceneGrabPos = point->sceneGrabPos(); + break; + case QQuickEventPoint::OverrideGrabPassive: + return; // don't emit + case QQuickEventPoint::UngrabPassive: + case QQuickEventPoint::UngrabExclusive: + case QQuickEventPoint::CancelGrabPassive: + case QQuickEventPoint::CancelGrabExclusive: + // the grab is lost or relinquished, so the point is no longer relevant + reset(); + break; + } emit singlePointGrabChanged(); } diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 8858b2c080..ca907e7489 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -100,8 +100,7 @@ protected: virtual void handleEventPoint(QQuickEventPoint *point) = 0; quint64 pointId() const { return m_pointId; } QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } - void handleGrabCancel(QQuickEventPoint *point) override; - void onGrabChanged(QQuickEventPoint *point) override; + void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; private: void setPressedButtons(Qt::MouseButtons buttons); diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 228b6bbaf3..b05d32f4b4 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -132,9 +132,9 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) } break; } - // If this is the grabber, returning false from this function will - // cancel the grab, so handleGrabCancel() and setPressed(false) will be called. - // But when m_gesturePolicy is DragThreshold, we don't grab, but + // If this is the grabber, returning false from this function will cancel the grab, + // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called. + // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but // we still don't want to be pressed anymore. if (!ret) setPressed(false, true, point); @@ -254,7 +254,10 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_longPressTimer.stop(); m_holdTimer.invalidate(); } - setPassiveGrab(point, press); + if (m_gesturePolicy == DragThreshold) + setPassiveGrab(point, press); + else + setExclusiveGrab(point, press); if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; @@ -274,10 +277,11 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi } } -void QQuickTapHandler::handleGrabCancel(QQuickEventPoint *point) +void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { - QQuickPointerSingleHandler::handleGrabCancel(point); - setPressed(false, true, point); + QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); + if (grabber == this && stateChange == QQuickEventPoint::CancelGrabExclusive) + setPressed(false, true, point); } void QQuickTapHandler::connectPreRenderSignal(bool conn) diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index bc292ecb12..5956a7f185 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -102,7 +102,7 @@ Q_SIGNALS: void longPressed(); protected: - void handleGrabCancel(QQuickEventPoint *point) override; + void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; void timerEvent(QTimerEvent *event) override; private: diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 1843d2b656..cddcf02955 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -651,11 +651,18 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b } if (exclusive) { if (grabber != m_exclusiveGrabber.data()) { + if (grabber) { + grabber->onGrabChanged(grabber, GrabExclusive, this); + for (QPointer passiveGrabber : m_passiveGrabbers) { + if (passiveGrabber != grabber) + passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this); + } + } else if (QQuickPointerHandler *oldGrabberPointerHandler = qmlobject_cast(m_exclusiveGrabber.data())) { + oldGrabberPointerHandler->onGrabChanged(oldGrabberPointerHandler, UngrabExclusive, this); + } m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = true; m_sceneGrabPos = m_scenePos; - for (QPointer passiveGrabber : m_passiveGrabbers) - passiveGrabber->handleGrab(this, grabber, true); } } else { if (!grabber) { @@ -663,8 +670,10 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b return; } auto ptr = QPointer(grabber); - if (!m_passiveGrabbers.contains(ptr)) + if (!m_passiveGrabbers.contains(ptr)) { m_passiveGrabbers.append(ptr); + grabber->onGrabChanged(grabber, GrabPassive, this); + } } } @@ -684,7 +693,7 @@ void QQuickEventPoint::cancelExclusiveGrab() << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; } if (auto handler = grabberPointerHandler()) - handler->handleGrabCancel(this); + handler->onGrabChanged(handler, CancelGrabExclusive, this); m_exclusiveGrabber.clear(); } @@ -695,15 +704,24 @@ void QQuickEventPoint::cancelExclusiveGrab() */ void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) { - if (m_passiveGrabbers.removeOne(QPointer(handler))) { + if (removePassiveGrabber(handler)) { if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab (passive)" << handler << "removed"; } - handler->handleGrabCancel(this); + handler->onGrabChanged(handler, CancelGrabPassive, this); } } +/*! + If this point has the given \a handler as a passive grabber, remove it as grabber. + Returns true if it was removed, false if it wasn't a grabber. +*/ +bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler) +{ + return m_passiveGrabbers.removeOne(handler); +} + /*! If the given \a handler is grabbing this point passively, exclusively or both, cancel the grab and remove it as grabber. @@ -718,7 +736,7 @@ void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler) void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler) { if (m_exclusiveGrabber == handler) { - handler->handleGrabCancel(this); + handler->onGrabChanged(handler, CancelGrabExclusive, this); m_exclusiveGrabber.clear(); } cancelPassiveGrab(handler); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 00da53eb48..ede0ebfde6 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -275,6 +275,17 @@ public: }; Q_ENUM(State) + enum GrabState { + GrabPassive = 0, + GrabExclusive, + UngrabPassive, + UngrabExclusive, + CancelGrabPassive, + CancelGrabExclusive, + OverrideGrabPassive + }; + Q_ENUM(GrabState) + QQuickEventPoint(QQuickPointerEvent *parent); void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); @@ -306,6 +317,7 @@ public: Q_INVOKABLE void cancelExclusiveGrab(); Q_INVOKABLE void cancelPassiveGrab(QQuickPointerHandler *handler); + bool removePassiveGrabber(QQuickPointerHandler *handler); void cancelAllGrabs(QQuickPointerHandler *handler); QVector > passiveGrabbers() const { return m_passiveGrabbers; } -- cgit v1.2.3 From fcfaaa12278efd3e306841b2c77d4be0247bfbd3 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 21 Feb 2017 15:32:58 +0100 Subject: Make all handler constructors explicit Change-Id: I17b3865d70bdc07912d7454b459dea40b9c98df0 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler_p.h | 2 +- src/quick/handlers/qquickmultipointerhandler_p.h | 2 +- src/quick/handlers/qquickpinchhandler_p.h | 2 +- src/quick/handlers/qquickpointerdevicehandler_p.h | 2 +- src/quick/handlers/qquickpointerhandler_p.h | 2 +- src/quick/handlers/qquickpointersinglehandler_p.h | 2 +- src/quick/handlers/qquicktaphandler_p.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index 6250615091..cfccc19115 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -93,7 +93,7 @@ class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickPointerSingleHandler Q_PROPERTY(QPointF translation READ translation NOTIFY translationChanged) public: - QQuickDragHandler(QObject *parent = 0); + explicit QQuickDragHandler(QObject *parent = 0); ~QQuickDragHandler(); void handleEventPoint(QQuickEventPoint *point) override; diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index 2a23a14f8a..d6d660ec04 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -65,7 +65,7 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointerHandler : public QQuickPointerDeviceHa Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged) public: - QQuickMultiPointerHandler(QObject *parent = 0, int requiredPointCount = 2); + explicit QQuickMultiPointerHandler(QObject *parent = 0, int requiredPointCount = 2); ~QQuickMultiPointerHandler(); int requiredPointCount() const { return m_requiredPointCount; } diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index 0861368682..d788e2cb36 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -81,7 +81,7 @@ public: }; Q_ENUM(PinchOrigin) - QQuickPinchHandler(QObject *parent = 0); + explicit QQuickPinchHandler(QObject *parent = 0); ~QQuickPinchHandler(); qreal minimumScale() const { return m_minimumScale; } diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index 2e126381b0..2ca7310da8 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -63,7 +63,7 @@ class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) public: - QQuickPointerDeviceHandler(QObject *parent = 0); + explicit QQuickPointerDeviceHandler(QObject *parent = 0); ~QQuickPointerDeviceHandler(); QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; } diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 715000114e..b5da8cba1f 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -69,7 +69,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT) public: - QQuickPointerHandler(QObject *parent = 0); + explicit QQuickPointerHandler(QObject *parent = 0); virtual ~QQuickPointerHandler(); public: diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index ca907e7489..a82ec72ad6 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -72,7 +72,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDe Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters NOTIFY eventPointHandled) public: - QQuickPointerSingleHandler(QObject *parent = 0); + explicit QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 5956a7f185..115effa3f7 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -75,7 +75,7 @@ public: }; Q_ENUM(GesturePolicy) - QQuickTapHandler(QObject *parent = 0); + explicit QQuickTapHandler(QObject *parent = 0); ~QQuickTapHandler(); bool wantsEventPoint(QQuickEventPoint *point) override; -- cgit v1.2.3 From 7c3baca698d5e3e6481a248a7d6c6abc41696304 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 21 Feb 2017 16:10:27 +0100 Subject: QQuickEventPoint API changes after API review * localize -> localizePosition * remove isValid and invalidate * remove isDraggedOverThreshold * remove Q_INVOKABLEs Change-Id: I21f788beb1e86275a9b7a1f1014998b2569001d0 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 24 +++++------------------- src/quick/items/qquickevents_p_p.h | 11 +++-------- src/quick/items/qquickwindow_p.h | 8 ++++++++ 3 files changed, 16 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index cddcf02955..0c06ecd677 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -541,7 +541,6 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, } m_pointId = pointId; } - m_valid = true; m_accept = false; m_state = static_cast(state); m_timestamp = timestamp; @@ -552,7 +551,7 @@ void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity); } -void QQuickEventPoint::localize(QQuickItem *target) +void QQuickEventPoint::localizePosition(QQuickItem *target) { if (target) m_pos = target->mapFromScene(scenePos()); @@ -560,12 +559,6 @@ void QQuickEventPoint::localize(QQuickItem *target) m_pos = QPointF(); } -void QQuickEventPoint::invalidate() -{ - m_valid = false; - m_pointId = 0; -} - /*! If this point has an exclusive grabber, returns a pointer to it; else returns null, if there is no grabber. The grabber could be either @@ -756,13 +749,6 @@ void QQuickEventPoint::setAccepted(bool accepted) } } -bool QQuickEventPoint::isDraggedOverThreshold() const -{ - QPointF delta = scenePos() - scenePressPos(); - return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, this) || - QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, this)); -} - QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) : QQuickEventPoint(parent), m_rotation(0), m_pressure(0) {} @@ -885,7 +871,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) void QQuickPointerMouseEvent::localize(QQuickItem *target) { - m_mousePoint->localize(target); + m_mousePoint->localizePosition(target); } QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) @@ -944,7 +930,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) void QQuickPointerTouchEvent::localize(QQuickItem *target) { for (auto point : qAsConst(m_touchPoints)) - point->localize(target); + point->localizePosition(target); } QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const { @@ -961,7 +947,7 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const { QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent) : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0), - m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false), m_grabberIsHandler(false) + m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false) { Q_UNUSED(m_reserved); } @@ -1306,7 +1292,7 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) { QDebugStateSaver saver(dbg); dbg.nospace(); - dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted() + dbg << "QQuickEventPoint(accepted:" << event->isAccepted() << " state:"; QtDebugUtils::formatQEnum(dbg, event->state()); dbg << " scenePos:" << event->scenePos() << " id:" << hex << event->pointId() << dec diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index ede0ebfde6..51e31bd7d7 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -289,9 +289,7 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); - void localize(QQuickItem *target); - - void invalidate(); + void localizePosition(QQuickItem *target); QQuickPointerEvent *pointerEvent() const; QPointF pos() const { return m_pos; } @@ -301,11 +299,9 @@ public: QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } quint64 pointId() const { return m_pointId; } - bool isValid() const { return m_valid; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); - bool isDraggedOverThreshold() const; QObject *exclusiveGrabber() const; void setExclusiveGrabber(QObject *exclusiveGrabber); @@ -315,8 +311,8 @@ public: QQuickPointerHandler *grabberPointerHandler() const; void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false); - Q_INVOKABLE void cancelExclusiveGrab(); - Q_INVOKABLE void cancelPassiveGrab(QQuickPointerHandler *handler); + void cancelExclusiveGrab(); + void cancelPassiveGrab(QQuickPointerHandler *handler); bool removePassiveGrabber(QQuickPointerHandler *handler); void cancelAllGrabs(QQuickPointerHandler *handler); @@ -339,7 +335,6 @@ private: ulong m_timestamp; ulong m_pressTimestamp; State m_state; - bool m_valid : 1; bool m_accept : 1; bool m_grabberIsHandler : 1; int m_reserved : 29; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 78f3b68ebb..b8131a056f 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -290,6 +290,14 @@ public: return overThreshold; } + static bool dragOverThreshold(const QQuickEventPoint *point) + { + QPointF delta = point->scenePos() - point->scenePressPos(); + return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) || + QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)); + } + + // data property static void data_append(QQmlListProperty *, QObject *); static int data_count(QQmlListProperty *); -- cgit v1.2.3 From 013032ae44c5118ae2d096d0dba47bc4114d96d1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 20 Feb 2017 17:52:08 +0100 Subject: notify a PointerHandler when it loses grab via EventPoint::setGrabberItem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an Item gets exclusive grab, that replaces any existing PointerHandler's exclusive grab, and overrides any passive grabbers as well. Change-Id: I659137a08e7069c8e72ddbb1f27e16d4b19828b9 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0c06ecd677..c78df999e8 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -609,6 +609,10 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab" << m_exclusiveGrabber << "->" << grabber; } + if (auto handler = grabberPointerHandler()) + handler->onGrabChanged(handler, CancelGrabExclusive, this); + for (QPointer passiveGrabber : m_passiveGrabbers) + passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = false; m_sceneGrabPos = m_scenePos; -- cgit v1.2.3 From 3a6a6d96ec443e66e3a07e9d16cd50b6c5d8c599 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 21 Feb 2017 17:51:34 +0100 Subject: Change QQuickEventPoint::pointId to be plain int After change 8984c42d1779b13fd29d95274af2d01d32528e52 in qtbase, a QTouchEvent::TouchPoint::id is already guaranteed to be unique across devices. We therefore don't need a larger value space for QQuickEventPoint::pointId, since that value is the same value as we got from the QTouchEvent Change-Id: I044630a812706f3c114bb28cffb29536f9feeeb3 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 2 +- src/quick/handlers/qquickpointersinglehandler_p.h | 6 +++--- src/quick/items/qquickevents.cpp | 8 ++++---- src/quick/items/qquickevents_p_p.h | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 4b931641a2..373f10dc16 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -119,7 +119,7 @@ bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event) // or use std::equal with a predicate for (int i = 0; ret && i < c; ++i) { bool found = false; - quint64 pointId = event->point(i)->pointId(); + int pointId = event->point(i)->pointId(); for (QQuickEventPoint *o : qAsConst(m_currentPoints)) if (o && pointId == o->pointId()) found = true; diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index a82ec72ad6..ad2d663054 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler { Q_OBJECT - Q_PROPERTY(quint64 pointId READ pointId NOTIFY pointIdChanged) + Q_PROPERTY(int pointId READ pointId NOTIFY pointIdChanged) Q_PROPERTY(QPointingDeviceUniqueId uniquePointId READ uniquePointId NOTIFY pointIdChanged) Q_PROPERTY(QPointF pos READ pos NOTIFY eventPointHandled) Q_PROPERTY(QPointF scenePos READ scenePos NOTIFY eventPointHandled) @@ -98,7 +98,7 @@ protected: virtual bool wantsEventPoint(QQuickEventPoint *point); void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; - quint64 pointId() const { return m_pointId; } + int pointId() const { return m_pointId; } QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; @@ -107,7 +107,7 @@ private: void reset(); private: - quint64 m_pointId; + int m_pointId; QPointingDeviceUniqueId m_uniquePointId; Qt::MouseButtons m_pressedButtons; QPointF m_pos; diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index c78df999e8..a7ab0f01b0 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -531,7 +531,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) return nullptr; } -void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity) +void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity) { m_scenePos = scenePos; if (m_pointId != pointId) { @@ -1146,15 +1146,15 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte \l {QQuickEventPoint::pointId}{pointId}. Returns nullptr if there is no point with that ID. - \fn QQuickPointerEvent::pointById(quint64 pointId) const + \fn QQuickPointerEvent::pointById(int pointId) const */ -QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const { +QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const { if (m_mousePoint && pointId == m_mousePoint->pointId()) return m_mousePoint; return nullptr; } -QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const { +QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const { auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(), [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } ); if (it != m_touchPoints.constEnd()) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 51e31bd7d7..455648a743 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -259,7 +259,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) - Q_PROPERTY(quint64 pointId READ pointId) + Q_PROPERTY(int pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) @@ -288,7 +288,7 @@ public: QQuickEventPoint(QQuickPointerEvent *parent); - void reset(Qt::TouchPointState state, const QPointF &scenePos, quint64 pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); + void reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity = QVector2D()); void localizePosition(QQuickItem *target); QQuickPointerEvent *pointerEvent() const; @@ -298,7 +298,7 @@ public: QPointF sceneGrabPos() const { return m_sceneGrabPos; } QVector2D velocity() const { return m_velocity; } State state() const { return m_state; } - quint64 pointId() const { return m_pointId; } + int pointId() const { return m_pointId; } qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; } bool isAccepted() const { return m_accept; } void setAccepted(bool accepted = true); @@ -329,7 +329,7 @@ private: QPointF m_scenePressPos; QPointF m_sceneGrabPos; QVector2D m_velocity; - quint64 m_pointId; + int m_pointId; QPointer m_exclusiveGrabber; QVector > m_passiveGrabbers; ulong m_timestamp; @@ -415,7 +415,7 @@ public: // helpers for C++ only (during event delivery) virtual int pointCount() const = 0; virtual QQuickEventPoint *point(int i) const = 0; - virtual QQuickEventPoint *pointById(quint64 pointId) const = 0; + virtual QQuickEventPoint *pointById(int pointId) const = 0; virtual QVector exclusiveGrabbers() const = 0; virtual void clearGrabbers() const = 0; virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0; @@ -445,7 +445,7 @@ public: const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(quint64 pointId) const override; + QQuickEventPoint *pointById(int pointId) const override; bool allPointsAccepted() const override; bool allUpdatedPointsAccepted() const override; bool allPointsGrabbed() const override; @@ -478,7 +478,7 @@ public: const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } QQuickEventPoint *point(int i) const override; - QQuickEventPoint *pointById(quint64 pointId) const override; + QQuickEventPoint *pointById(int pointId) const override; const QTouchEvent::TouchPoint *touchPointById(int pointId) const; bool allPointsAccepted() const override; bool allUpdatedPointsAccepted() const override; -- cgit v1.2.3 From ac61ca1a2e18bd472d36fdad730a797a913fe347 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 22 Feb 2017 13:59:32 +0100 Subject: Make QQuickPointerSingleHandler::pointId() public Change-Id: I47441db09e6880eccd3077ea9c5c2248ea05d295 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointersinglehandler_p.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index ad2d663054..8c25b9efa4 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -75,6 +75,7 @@ public: explicit QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } + int pointId() const { return m_pointId; } Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } QPointF pressPos() const { return m_pressPos; } QPointF scenePressPos() const { return parentItem()->mapToScene(m_pressPos); } @@ -98,7 +99,7 @@ protected: virtual bool wantsEventPoint(QQuickEventPoint *point); void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; - int pointId() const { return m_pointId; } + QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; -- cgit v1.2.3 From e53510944169ac9f6753e0d14e1b24a24ff7bd9a Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 22 Feb 2017 14:19:12 +0100 Subject: API: Move acceptedButtons to QQuickPointerSingleHandler Change-Id: I8cb393986e587e69d550ec03f691258c79d9237a Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointerdevicehandler.cpp | 16 +--------------- src/quick/handlers/qquickpointerdevicehandler_p.h | 5 ----- src/quick/handlers/qquickpointersinglehandler.cpp | 14 ++++++++++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 8 ++++++-- src/quick/handlers/qquicktaphandler.cpp | 2 +- 5 files changed, 22 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp index 03704ac09e..3a320fdb32 100644 --- a/src/quick/handlers/qquickpointerdevicehandler.cpp +++ b/src/quick/handlers/qquickpointerdevicehandler.cpp @@ -53,7 +53,6 @@ QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent) : QQuickPointerHandler(parent) , m_acceptedDevices(QQuickPointerDevice::AllDevices) , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes) - , m_acceptedButtons(Qt::AllButtons) { } @@ -79,30 +78,17 @@ void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::Po emit acceptedPointerTypesChanged(); } -void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons) -{ - if (m_acceptedButtons == buttons) - return; - - m_acceptedButtons = buttons; - emit acceptedButtonsChanged(); -} - bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerHandler::wantsPointerEvent(event)) return false; qCDebug(lcPointerHandlerDispatch) << objectName() << "checking device type" << m_acceptedDevices - << "pointer type" << m_acceptedPointerTypes - << "buttons" << m_acceptedButtons; + << "pointer type" << m_acceptedPointerTypes; if ((event->device()->type() & m_acceptedDevices) == 0) return false; if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0) return false; - if (event->device()->pointerType() != QQuickPointerDevice::Finger && - (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0) - return false; return true; } diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index 2ca7310da8..76c9cc44f0 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -60,7 +60,6 @@ class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler Q_OBJECT Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged) Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged) - Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) public: explicit QQuickPointerDeviceHandler(QObject *parent = 0); @@ -68,17 +67,14 @@ public: QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; } QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; } - Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } public slots: void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices); void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes); - void setAcceptedButtons(Qt::MouseButtons buttons); Q_SIGNALS: void acceptedDevicesChanged(); void acceptedPointerTypesChanged(); - void acceptedButtonsChanged(); protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; @@ -86,7 +82,6 @@ protected: protected: QQuickPointerDevice::DeviceTypes m_acceptedDevices; QQuickPointerDevice::PointerTypes m_acceptedPointerTypes; - Qt::MouseButtons m_acceptedButtons; }; QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index ec66112519..5a8ec2a7fd 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -55,6 +55,7 @@ QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) , m_pointId(0) , m_rotation(0) , m_pressure(0) + , m_acceptedButtons(Qt::AllButtons) { } @@ -62,6 +63,10 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; + if (event->device()->pointerType() != QQuickPointerDevice::Finger && + (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0) + return false; + if (m_pointId) { // We already know which one we want, so check whether it's there. // It's expected to be an update or a release. @@ -174,6 +179,15 @@ void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) } } +void QQuickPointerSingleHandler::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (m_acceptedButtons == buttons) + return; + + m_acceptedButtons = buttons; + emit acceptedButtonsChanged(); +} + void QQuickPointerSingleHandler::reset() { bool pointIdChange = m_pointId != 0; diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 8c25b9efa4..9c854c4d0d 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -70,13 +70,15 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDe Q_PROPERTY(qreal rotation READ rotation NOTIFY eventPointHandled) Q_PROPERTY(qreal pressure READ pressure NOTIFY eventPointHandled) Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters NOTIFY eventPointHandled) - + Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) public: explicit QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } int pointId() const { return m_pointId; } Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } + Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } + void setAcceptedButtons(Qt::MouseButtons buttons); QPointF pressPos() const { return m_pressPos; } QPointF scenePressPos() const { return parentItem()->mapToScene(m_pressPos); } QPointF sceneGrabPos() const { return m_sceneGrabPos; } @@ -88,9 +90,10 @@ public: QSizeF ellipseDiameters() const { return m_ellipseDiameters; } QPointingDeviceUniqueId uniquePointId() const { return m_uniquePointId; } -signals: +Q_SIGNALS: void pointIdChanged(); void pressedButtonsChanged(); + void acceptedButtonsChanged(); void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here void eventPointHandled(); @@ -118,6 +121,7 @@ private: qreal m_rotation; qreal m_pressure; QSizeF m_ellipseDiameters; + Qt::MouseButtons m_acceptedButtons; }; QT_END_NAMESPACE diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index b05d32f4b4..5ee415d710 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -148,7 +148,7 @@ void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) setPressed(true, false, point); break; case QQuickEventPoint::Released: - if ((point->pointerEvent()->buttons() & m_acceptedButtons) == Qt::NoButton) + if ((point->pointerEvent()->buttons() & acceptedButtons()) == Qt::NoButton) setPressed(false, false, point); break; default: -- cgit v1.2.3 From 59c753bc75c7cfd4068fbbba3c25e1f54c46f4c0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 16 Feb 2017 10:48:37 +0100 Subject: QQuickWindowPrivate::deliverTouchCancelEvent: deliver to handlers too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that onGrabChanged() is unified, we have a means to tell the handler when it loses its grab due to a touch event being canceled. Change-Id: Idf3649242233ac7fb8c1fa80ad257ee14b861090 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler_p.h | 1 + src/quick/items/qquickevents.cpp | 16 +++++++++++++--- src/quick/items/qquickevents_p_p.h | 5 +++++ src/quick/items/qquickwindow.cpp | 15 ++++----------- 4 files changed, 23 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index b5da8cba1f..24a058275d 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -115,6 +115,7 @@ private: bool m_hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state friend class QQuickEventPoint; + friend class QQuickWindowPrivate; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index a7ab0f01b0..bbea32f905 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -681,16 +681,26 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b */ void QQuickEventPoint::cancelExclusiveGrab() { - if (m_exclusiveGrabber.isNull()) { + if (m_exclusiveGrabber.isNull()) qWarning("cancelGrab: no grabber"); + else + cancelExclusiveGrabImpl(); +} + +void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent) +{ + if (m_exclusiveGrabber.isNull()) return; - } if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) { qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr"; } - if (auto handler = grabberPointerHandler()) + if (auto handler = grabberPointerHandler()) { handler->onGrabChanged(handler, CancelGrabExclusive, this); + } else if (auto item = grabberItem()) { + if (cancelEvent) + QCoreApplication::sendEvent(item, cancelEvent); + } m_exclusiveGrabber.clear(); } diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 455648a743..d046480f25 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -320,6 +320,9 @@ public: void setPassiveGrabbers(const QVector > &grabbers) { m_passiveGrabbers = grabbers; } void clearPassiveGrabbers() { m_passiveGrabbers.clear(); } +protected: + void cancelExclusiveGrabImpl(QTouchEvent *cancelEvent = nullptr); + private: QVector2D estimatedVelocity() const; @@ -339,6 +342,8 @@ private: bool m_grabberIsHandler : 1; int m_reserved : 29; + friend class QQuickWindowPrivate; + Q_DISABLE_COPY(QQuickEventPoint) }; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0cd9357b9a..3089e3c2fa 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1927,23 +1927,16 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) Q_Q(QQuickWindow); // A TouchCancel event will typically not contain any points. - // Deliver it to all items that have active touches. + // Deliver it to all items and handlers that have active touches. QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent(); - QVector grabbers = pointerEvent->exclusiveGrabbers(); - - for (QObject *grabber: qAsConst(grabbers)) { - if (QQuickItem *grabberItem = qmlobject_cast(grabber)) - QCoreApplication::sendEvent(grabberItem, event); - else //if (QQuickPointerHandler *grabberHandler = qmlobject_cast(grabber)) -// grabberHandler->handlePointerEvent() - qWarning("unexpected: can't deliver touch cancel to a PointerHandler (yet?)"); - } + for (int i = 0; i < pointerEvent->pointCount(); ++i) + pointerEvent->point(i)->cancelExclusiveGrabImpl(event); touchMouseId = -1; touchMouseDevice = nullptr; if (q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); - // The next touch event can only be a TouchBegin so clean up. + // The next touch event can only be a TouchBegin, so clean up. pointerEvent->clearGrabbers(); return true; } -- cgit v1.2.3 From 86269c2bb7be54249e135acb74ce517be29d38d2 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 22 Feb 2017 14:21:13 +0100 Subject: Property velocity was double defined Change-Id: I7de8e58c6ea5e6ba9ddefcf6018ae180880f6cb5 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index d046480f25..105cc9a3ea 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -257,7 +257,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject Q_PROPERTY(QPointF scenePos READ scenePos) Q_PROPERTY(QPointF scenePressPos READ scenePressPos) Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos) - Q_PROPERTY(QVector2D velocity READ velocity) Q_PROPERTY(State state READ state) Q_PROPERTY(int pointId READ pointId) Q_PROPERTY(qreal timeHeld READ timeHeld) -- cgit v1.2.3 From 79d3fe1bba4589c7f5780bb505eac7872061ee32 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 22 Feb 2017 14:24:54 +0100 Subject: API: Make some virtuals protected instead of public Change-Id: Ib61471a929e387ac64c31d07c9ec5a941cc92d3a Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquicktaphandler_p.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 115effa3f7..0e9a6f0411 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -78,9 +78,6 @@ public: explicit QQuickTapHandler(QObject *parent = 0); ~QQuickTapHandler(); - bool wantsEventPoint(QQuickEventPoint *point) override; - void handleEventPoint(QQuickEventPoint *point) override; - bool isPressed() const { return m_pressed; } int tapCount() const { return m_tapCount; } @@ -104,6 +101,8 @@ Q_SIGNALS: protected: void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; void timerEvent(QTimerEvent *event) override; + bool wantsEventPoint(QQuickEventPoint *point) override; + void handleEventPoint(QQuickEventPoint *point) override; private: void setPressed(bool press, bool cancel, QQuickEventPoint *point); -- cgit v1.2.3 From 781f76176239bfbfe6041f2676e2f2804337d312 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 16 Feb 2017 11:26:23 +0100 Subject: notify a PointerHandler when it loses grab due to Item::grabTouchPoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and move more notification responsibility into QQuickEventPoint, thus simplifying QQuickWindowPrivate::grabTouchPoints() which is the implementation behind QQuickItem::grabTouchPoints. It's important for QQuickEventPoint::setGrabberItem to change local state first and then notify, to prevent recursive notify/ungrab loops. MPTA for example does an ungrab when it receives touchUngrabEvent, which then notifies again if the first ungrab was not already fully completed. Change-Id: I6f7b939c8cd76ac5f2d1ddda8b210fa3d31d619a Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 22 ++++++++++++++++++---- src/quick/items/qquickitem.h | 1 + src/quick/items/qquickwindow.cpp | 26 ++++++++++++++------------ 3 files changed, 33 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index bbea32f905..97183c55bc 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -609,13 +609,20 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this) << ": grab" << m_exclusiveGrabber << "->" << grabber; } - if (auto handler = grabberPointerHandler()) - handler->onGrabChanged(handler, CancelGrabExclusive, this); - for (QPointer passiveGrabber : m_passiveGrabbers) - passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); + QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler(); + QQuickItem *oldGrabberItem = grabberItem(); m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = false; m_sceneGrabPos = m_scenePos; + if (oldGrabberHandler) + oldGrabberHandler->onGrabChanged(oldGrabberHandler, CancelGrabExclusive, this); + else if (oldGrabberItem && oldGrabberItem != grabber) { + auto pte = pointerEvent()->asPointerTouchEvent(); + if (pte && pte->asTouchEvent() && pte->asTouchEvent()->touchPointStates() == Qt::TouchPointReleased) + oldGrabberItem->touchUngrabEvent(); + } + for (QPointer passiveGrabber : m_passiveGrabbers) + passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); } } @@ -656,7 +663,14 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b } } else if (QQuickPointerHandler *oldGrabberPointerHandler = qmlobject_cast(m_exclusiveGrabber.data())) { oldGrabberPointerHandler->onGrabChanged(oldGrabberPointerHandler, UngrabExclusive, this); + } else if (!m_exclusiveGrabber.isNull()) { + // If there is a previous grabber and it's not a PointerHandler, it must be an Item. + QQuickItem *oldGrabberItem = static_cast(m_exclusiveGrabber.data()); + // If this point came from a touchscreen, notify that previous grabber Item that it's losing its touch grab. + if (pointerEvent()->asPointerTouchEvent()) + oldGrabberItem->touchUngrabEvent(); } + m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = true; m_sceneGrabPos = m_scenePos; diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index c9494d91bd..279d052db9 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -447,6 +447,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *)) Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *)) + friend class QQuickEventPoint; friend class QQuickWindow; friend class QQuickWindowPrivate; friend class QSGRenderer; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 57d9ac4ad2..0105e84cd6 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -778,9 +778,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector &ids) { - QSet ungrab; for (int i = 0; i < ids.count(); ++i) { - // FIXME: deprecate this function, we need a device int id = ids.at(i); if (Q_UNLIKELY(id < 0)) { qWarning("ignoring grab of touchpoint %d", id); @@ -792,7 +790,7 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector & if (touchMouseGrabber) { point->setExclusiveGrabber(nullptr); touchMouseGrabber->mouseUngrabEvent(); - ungrab.insert(touchMouseGrabber); + touchMouseGrabber->touchUngrabEvent(); touchMouseDevice = nullptr; touchMouseId = -1; } @@ -807,18 +805,20 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector & QObject *oldGrabber = point->exclusiveGrabber(); if (oldGrabber == grabber) continue; - point->setExclusiveGrabber(grabber); - if (oldGrabber) - ungrab.insert(oldGrabber); } } - for (QObject *oldGrabber : qAsConst(ungrab)) - if (QQuickItem *item = qmlobject_cast(oldGrabber)) - item->touchUngrabEvent(); - // TODO else if the old grabber was a PointerHandler, notify it somehow? } +/*! + Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber. + This should not be called when processing a release event - that's redundant. + It is called in other cases, when the points may not be released, but the item + nevertheless must lose its grab due to becoming disabled, invisible, etc. + QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released, + but if not all points are released, it cannot be sure whether to call touchUngrabEvent() + or not; so we have to do it here. +*/ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); @@ -827,17 +827,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to setMouseGrabber(nullptr); } if (Q_LIKELY(touch)) { + bool ungrab = false; const auto touchDevices = QQuickPointerDevice::touchDevices(); for (auto device : touchDevices) { auto pointerEvent = device->pointerEvent(); for (int i = 0; i < pointerEvent->pointCount(); ++i) { if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { pointerEvent->point(i)->setGrabberItem(nullptr); - // FIXME send ungrab event only once - grabber->touchUngrabEvent(); + ungrab = true; } } } + if (ungrab) + grabber->touchUngrabEvent(); } } -- cgit v1.2.3 From 2917d56ee6338c4a25c918383efc2356c229ff72 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 2 Mar 2017 11:55:47 +0100 Subject: QQuickPointerSingleHandler warning: show pointId in hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I86b14b10de86428c30d7aeb939eeb1fd14c50920 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 5a8ec2a7fd..d9a9d692ae 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -79,7 +79,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) point->cancelAllGrabs(this); } } else { - qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << m_pointId + qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointId << "is missing from current event, but was neither canceled nor released"; return false; } -- cgit v1.2.3 From 8c659c6c723e4f5f97f46a4555a4765e85c26f1d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 2 Mar 2017 11:56:10 +0100 Subject: QQuickEventPoint::reset: don't cancel grabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because touchpoints can be released in a different order than they were pressed, and even the QPA plugins don't guarantee that points in a QTouchEvent will always stay in the same order while they are still pressed, we cannot assume that the order of points will not change every time a touch update occurs. And QQuickPointerTouchEvent has a vector m_touchPoints in which the current points come first: if the first point in that vector is released, it will be reset() to represent a different touchpoint with a different ID. So it follows that QQuickEventPoint::reset doesn't have enough information to decide when a change of pointId means an implicit ungrab; and this code was just a failsafe anyway (thus the qWarning). QQuickPointerTouchEvent::reset() is responsible for identifying which grabs need to be retained in which points from one event to the next, and also currently warns if a point was pressed while still being grabbed from previous interactions. However, a mouse event only has one point, and we rely on QQuickEventPoint to retain the grabbers. Efficiency-wise it doesn't make sense to clear m_exclusiveGrabber and m_passiveGrabbers and then re-populate them each time the mouse moves. Since the risk of reordered points is only for touch events, clearing the grabbers is now done in QQuickEventTouchPoint::reset(). One manifestation of this bug was in multibuttons.qml: sometimes pressing a second button would cause the first button to be released. It turned out to occur whenever the touchpoints got reordered. Change-Id: Ia641f619d763e1a1ea7b59c1e1d08bce9d88e707 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 10 +++------- src/quick/items/qquickevents_p_p.h | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 97183c55bc..49ed0f050e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -534,13 +534,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id) void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity) { m_scenePos = scenePos; - if (m_pointId != pointId) { - if (m_exclusiveGrabber) { - qWarning() << m_exclusiveGrabber << "failed to ungrab previous point" << m_pointId; - cancelExclusiveGrab(); - } - m_pointId = pointId; - } + m_pointId = pointId; m_accept = false; m_state = static_cast(state); m_timestamp = timestamp; @@ -784,6 +778,8 @@ QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent) void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp) { QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity()); + m_exclusiveGrabber.clear(); + m_passiveGrabbers.clear(); m_rotation = tp.rotation(); m_pressure = tp.pressure(); m_ellipseDiameters = tp.ellipseDiameters(); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 105cc9a3ea..c4200ac550 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -325,7 +325,7 @@ protected: private: QVector2D estimatedVelocity() const; -private: +protected: QPointF m_pos; QPointF m_scenePos; QPointF m_scenePressPos; -- cgit v1.2.3 From 2f204d3e6a4e5107e09e9b87b841fe91bb6743ac Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 2 Mar 2017 20:54:23 +0100 Subject: TapHandler: grab before emitting on press, after emitting on release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The issue was that in an onTapped handler, the position property would be wrong and active would be false, because setActive(false) occurred too early. Change-Id: I71b43da703aa2f007a367c239d2ded64e6e7e850 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 5ee415d710..fbbc6f1c7d 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -254,10 +254,13 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_longPressTimer.stop(); m_holdTimer.invalidate(); } - if (m_gesturePolicy == DragThreshold) - setPassiveGrab(point, press); - else - setExclusiveGrab(point, press); + if (press) { + // on press, grab before emitting changed signals + if (m_gesturePolicy == DragThreshold) + setPassiveGrab(point, press); + else + setExclusiveGrab(point, press); + } if (!cancel && !press && point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; @@ -274,6 +277,13 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi m_lastTapPos = point->scenePos(); } emit pressedChanged(); + if (!press) { + // on release, ungrab after emitting changed signals + if (m_gesturePolicy == DragThreshold) + setPassiveGrab(point, press); + else + setExclusiveGrab(point, press); + } } } -- cgit v1.2.3 From 3b3c8103496f61ebc4b72e73035a4d43fcdd03b0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 7 Mar 2017 17:10:43 +0100 Subject: PointerHandlers: fix some grab notification and signal order problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The singlePointHandlerProperties manual test showed how this was broken in a couple of ways after 8c659c6c723e4f5f97f46a4555a4765e85c26f1d : - When QQuickPointerTouchEvent::reset() is swapping one point instance for another, and consequently transferring the grabbers from one to another, it should not cause onGrabChanged to occur. Every point update was triggering DragHandler.onGrabChanged. - The order of signal emission is important so that sceneGrabPos will be correct in onGrabChanged. Change-Id: I62a302d6e54126ae10834b6d622e82aa0e434bab Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 7 +++++-- src/quick/items/qquickevents.cpp | 11 ++++++++--- src/quick/items/qquickevents_p_p.h | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index d9a9d692ae..112c680d3c 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -148,15 +148,17 @@ bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { - QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); if (grabber != this) return; switch (stateChange) { case QQuickEventPoint::GrabExclusive: + m_sceneGrabPos = point->sceneGrabPos(); setActive(true); - Q_FALLTHROUGH(); + QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); + break; case QQuickEventPoint::GrabPassive: m_sceneGrabPos = point->sceneGrabPos(); + QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); break; case QQuickEventPoint::OverrideGrabPassive: return; // don't emit @@ -165,6 +167,7 @@ void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQ case QQuickEventPoint::CancelGrabPassive: case QQuickEventPoint::CancelGrabExclusive: // the grab is lost or relinquished, so the point is no longer relevant + QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); reset(); break; } diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 49ed0f050e..61d843210e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -650,6 +650,10 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b if (exclusive) { if (grabber != m_exclusiveGrabber.data()) { if (grabber) { + // set variables before notifying the new grabber + m_exclusiveGrabber = QPointer(grabber); + m_grabberIsHandler = true; + m_sceneGrabPos = m_scenePos; grabber->onGrabChanged(grabber, GrabExclusive, this); for (QPointer passiveGrabber : m_passiveGrabbers) { if (passiveGrabber != grabber) @@ -664,7 +668,7 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b if (pointerEvent()->asPointerTouchEvent()) oldGrabberItem->touchUngrabEvent(); } - + // set variables after notifying the old grabber m_exclusiveGrabber = QPointer(grabber); m_grabberIsHandler = true; m_sceneGrabPos = m_scenePos; @@ -943,8 +947,9 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) point->setGrabberItem(nullptr); point->clearPassiveGrabbers(); } else { - point->setExclusiveGrabber(grabbers.at(i)); - point->setPassiveGrabbers(passiveGrabberses.at(i)); + // Restore the grabbers without notifying (don't call onGrabChanged) + point->m_exclusiveGrabber = grabbers.at(i); + point->m_passiveGrabbers = passiveGrabberses.at(i); } } m_pointCount = newPointCount; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index c4200ac550..2eebf131bc 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -370,6 +370,8 @@ private: QSizeF m_ellipseDiameters; QPointingDeviceUniqueId m_uniqueId; + friend class QQuickPointerTouchEvent; + Q_DISABLE_COPY(QQuickEventTouchPoint) }; -- cgit v1.2.3 From 018a76816789adf839f238957cddee13ef7de2e8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 9 Mar 2017 09:05:28 +0100 Subject: QQuickPointerTouchEvent::reset: detect the type of exclusive grabber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correction to 3b3c8103496f61ebc4b72e73035a4d43fcdd03b0: never set m_exclusiveGrabber without setting m_grabberIsHandler at the same time, otherwise a crash is possible when static_casting to the wrong type and attempting to call methods on the item or handler. Change-Id: I74e015b4fa2edb68cbd5c45bc3aaa6083eec7784 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 61d843210e..2671b9c9a2 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -949,6 +949,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) } else { // Restore the grabbers without notifying (don't call onGrabChanged) point->m_exclusiveGrabber = grabbers.at(i); + point->m_grabberIsHandler = (qmlobject_cast(point->m_exclusiveGrabber) != nullptr); point->m_passiveGrabbers = passiveGrabberses.at(i); } } -- cgit v1.2.3 From 25c24cfcb6d4bc43b86659870a9fce3cb601760f Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Thu, 9 Mar 2017 13:31:35 +0100 Subject: Deactivate when reset() is called Change-Id: If35a45e1a7bbb404b804efbd1aab70116165d684 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointersinglehandler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 112c680d3c..8e00bd419a 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -193,6 +193,7 @@ void QQuickPointerSingleHandler::setAcceptedButtons(Qt::MouseButtons buttons) void QQuickPointerSingleHandler::reset() { + setActive(false); bool pointIdChange = m_pointId != 0; m_pointId = 0; m_uniquePointId = QPointingDeviceUniqueId(); -- cgit v1.2.3 From fee26bb6f83bfb79845f8da889090b33e2b8fb4d Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Mon, 13 Mar 2017 13:47:09 +0100 Subject: Don't include exclusiveGrabbers when matching requiredPointCount More specifically, only count points that are not exclusively grabbed or points that are exclusively grabbed by this handler. Change-Id: I8c873e0553635102cb7cf5c98f0bf318e039cb18 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 373f10dc16..c6534e6bb6 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -65,7 +65,19 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; - if (event->pointCount() < m_requiredPointCount) + + const int pCount = event->pointCount(); + int pointCandidateCount = 0; + + // points that are grabbed by other handlers are not candidates for this handler + for (int i = 0; i < pCount; ++i) { + QQuickEventPoint *pt = event->point(i); + QObject *exclusiveGrabber = pt->exclusiveGrabber(); + if (!exclusiveGrabber || exclusiveGrabber == this) + ++pointCandidateCount; + } + + if (pointCandidateCount < m_requiredPointCount) return false; if (sameAsCurrentPoints(event)) return true; -- cgit v1.2.3 From 0ff3093e32b2534b29d296b5733a6c2849e5b2ac Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Wed, 8 Mar 2017 16:16:36 +0100 Subject: PinchHandler: Do not grab immediately Do not grab until at least one of the points have moved beyond the drag threshold Change-Id: I30de6332cc8e2b0238cacf5c4f0f70efbdc4d41d Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpinchhandler.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 4832d300c2..5469df206c 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -214,7 +214,6 @@ void QQuickPinchHandler::onActiveChanged() m_activeRotation = 0; m_activeTranslation = QPointF(0,0); qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation; - grabPoints(m_currentPoints); } } else { qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation; @@ -224,16 +223,23 @@ void QQuickPinchHandler::onActiveChanged() void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) { Q_UNUSED(event) - // TODO wait for the drag threshold before setting active - // but the old behavior was that whenever we "want" all the points, we're active - // so that behavior is retained here temporarily - setActive(m_currentPoints.count() > 0 && m_currentPoints.at(0)->state() != QQuickEventPoint::Released); - if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) { for (QQuickEventPoint *point : qAsConst(m_currentPoints)) qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos(); } + if (!active()) { + // Verify that least one of the points have moved beyond threshold needed to activate the handler + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { + if (QQuickWindowPrivate::dragOverThreshold(point)) { + grabPoints(m_currentPoints); + setActive(true); + break; + } + } + if (!active()) + return; + } // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter m_centroid = touchPointCentroid(); QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); -- cgit v1.2.3 From d55f0433c102dbdbd0b69c8ddbc397a0854d86a0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 17 Mar 2017 14:18:40 +0100 Subject: Fix for disappearing VisualPath entries in generic PathItem Prevent internal overflows in tesselation due to 100x scale and keep the correct scenegraph node - VisualPath entry associations. Change-Id: I181eae6872ea5c5af5783b68a8757a5249c074e5 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index a76a5e2b43..dfa01f4d42 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE -static const qreal SCALE = 100; +static const qreal TRI_SCALE = 1; struct ColoredVertex // must match QSGGeometry::ColoredPoint2D { @@ -410,13 +410,13 @@ void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, { const QVectorPath &vp = qtVectorPathForPath(path); - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(SCALE, SCALE), 1, supportsElementIndexUint); + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 fillVertices->resize(vertexCount); ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); const qreal *vsrc = ts.vertices.constData(); for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / SCALE, vsrc[i * 2 + 1] / SCALE, fillColor); + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); size_t indexByteSize; if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { @@ -441,7 +441,7 @@ void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, { const QVectorPath &vp = qtVectorPathForPath(path); const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / SCALE; + const qreal inverseScale = 1.0 / TRI_SCALE; QTriangulatingStroker stroker; stroker.setInvScale(inverseScale); @@ -508,8 +508,12 @@ void QQuickPathItemGenericRenderer::updateNode() if (m_accDirty & DirtyList) d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - if (!d.effectiveDirty) + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; continue; + } if (d.fillColor.a == 0) { delete node->m_fillNode; -- cgit v1.2.3 From 3523b676382db4aa39adeb9126d8bb2185e84403 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 17 Mar 2017 15:39:45 +0100 Subject: DragHandler: restrict dragging to *only* one finger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This means we can do a one-finger drag, then add two fingers to do a 3-finger pinch. Change-Id: I62c0184cfeeb3cc240cba20c6c2238852f68a79a Reviewed-by: Jan Arve Sæther Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointersinglehandler.cpp | 32 +++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 8e00bd419a..6a4c4da7e6 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -71,8 +71,20 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) // We already know which one we want, so check whether it's there. // It's expected to be an update or a release. // If we no longer want it, cancel the grab. - if (auto point = event->pointById(m_pointId)) { - if (wantsEventPoint(point)) { + int candidatePointCount = 0; + QQuickEventPoint *point = nullptr; + int c = event->pointCount(); + for (int i = 0; i < c; ++i) { + QQuickEventPoint *p = event->point(i); + if (p->pointId() == m_pointId) { + point = p; + ++candidatePointCount; + } else if (wantsEventPoint(p)) { + ++candidatePointCount; + } + } + if (point) { + if (candidatePointCount == 1) { point->setAccepted(); return true; } else { @@ -85,13 +97,17 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) } } else { // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true. + int candidatePointCount = 0; int c = event->pointCount(); - for (int i = 0; i < c && !m_pointId; ++i) { - QQuickEventPoint *p = event->point(i); - if (!p->exclusiveGrabber() && wantsEventPoint(p)) { - m_pointId = p->pointId(); - p->setAccepted(); - } + QQuickEventPoint *p = nullptr; + for (int i = 0; i < c; ++i) { + p = event->point(i); + if (!p->exclusiveGrabber() && wantsEventPoint(p)) + ++candidatePointCount; + } + if (p && candidatePointCount == 1) { + m_pointId = p->pointId(); + p->setAccepted(); } } return m_pointId; -- cgit v1.2.3 From 55970b212fff45161f94ffe637b49473e8f5925b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Mar 2017 13:29:04 +0200 Subject: Fix async PathItem results not always showing up Change-Id: Ie8eb1dd5ea8a560f8160384637cc5d76e3bb7c60 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index dfa01f4d42..7a259617dd 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -642,6 +642,12 @@ void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPa n->markDirty(QSGNode::DirtyGeometry); + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { ColoredVertex *vdst = reinterpret_cast(g->vertexData()); for (int i = 0; i < g->vertexCount(); ++i) -- cgit v1.2.3 From 83fc08cc6faa5f52a010d7bd821c9606f13d5ae9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 27 Mar 2017 15:37:29 +0200 Subject: PathItem docs for the declarative API Also add the missing property for disabling vendor extensions like NVPR. Change-Id: Id7dfe245305c8ecdfad87359894e7f496c4100d0 Reviewed-by: Andy Nichols --- src/quick/doc/images/pathitem-code-example.png | Bin 0 -> 5989 bytes src/quick/doc/images/visualpath-code-example.png | Bin 0 -> 844 bytes src/quick/items/qquickpathitem.cpp | 457 ++++++++++++++++++++++- src/quick/items/qquickpathitem_p.h | 5 + src/quick/items/qquickpathitem_p_p.h | 2 + 5 files changed, 447 insertions(+), 17 deletions(-) create mode 100644 src/quick/doc/images/pathitem-code-example.png create mode 100644 src/quick/doc/images/visualpath-code-example.png (limited to 'src') diff --git a/src/quick/doc/images/pathitem-code-example.png b/src/quick/doc/images/pathitem-code-example.png new file mode 100644 index 0000000000..25dbe8b311 Binary files /dev/null and b/src/quick/doc/images/pathitem-code-example.png differ diff --git a/src/quick/doc/images/visualpath-code-example.png b/src/quick/doc/images/visualpath-code-example.png new file mode 100644 index 0000000000..429e85aa32 Binary files /dev/null and b/src/quick/doc/images/visualpath-code-example.png differ diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index 61216be9b0..b14fc1caba 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -114,6 +114,60 @@ QPainterPath QQuickPathItemPath::toPainterPath() const return p; } +/*! + \qmltype VisualPath + \instantiates QQuickVisualPath + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A PathItem contains one or more VisualPath elements. At least one + VisualPath is necessary in order to have a PathItem output anything + visible. A VisualPath in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the PathItem. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + VisualPath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: VisualPath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a PathItem, here is the output with a joinStyleIndex + of 2 (VisualPath.RoundJoin): + + \image visualpath-code-example.png + */ + QQuickVisualPathPrivate::QQuickVisualPathPrivate() : path(nullptr), dirty(DirtyAll) @@ -129,6 +183,14 @@ QQuickVisualPath::~QQuickVisualPath() { } +/*! + \qmlproperty Path QtQuick::VisualPath::path + + This property holds the Path object. + + \default + */ + QQuickPath *QQuickVisualPath::path() const { Q_D(const QQuickVisualPath); @@ -160,6 +222,16 @@ void QQuickVisualPathPrivate::_q_pathChanged() emit q->changed(); } +/*! + \qmlproperty color QtQuick::VisualPath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + QColor QQuickVisualPath::strokeColor() const { Q_D(const QQuickVisualPath); @@ -177,6 +249,16 @@ void QQuickVisualPath::setStrokeColor(const QColor &color) } } +/*! + \qmlproperty color QtQuick::VisualPath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + qreal QQuickVisualPath::strokeWidth() const { Q_D(const QQuickVisualPath); @@ -194,6 +276,16 @@ void QQuickVisualPath::setStrokeWidth(qreal w) } } +/*! + \qmlproperty color QtQuick::VisualPath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + QColor QQuickVisualPath::fillColor() const { Q_D(const QQuickVisualPath); @@ -211,6 +303,19 @@ void QQuickVisualPath::setFillColor(const QColor &color) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::fillRule + + This property holds the fill rule. The default value is + VisualPath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li VisualPath.OddEvenFill + \li VisualPath.WindingFill + \endlist + */ + QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const { Q_D(const QQuickVisualPath); @@ -228,6 +333,19 @@ void QQuickVisualPath::setFillRule(FillRule fillRule) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is VisualPath.BevelJoin. + + \list + \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. + \li VisualPath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const { Q_D(const QQuickVisualPath); @@ -245,6 +363,15 @@ void QQuickVisualPath::setJoinStyle(JoinStyle style) } } +/*! + \qmlproperty int QtQuick::VisualPath::miterLimit + + When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + int QQuickVisualPath::miterLimit() const { Q_D(const QQuickVisualPath); @@ -262,6 +389,19 @@ void QQuickVisualPath::setMiterLimit(int limit) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::capStyle + + This property defines how the end points of lines are drawn. The + default value is VisualPath.SquareCap. + + \list + \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. + \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li VisualPath.RoundCap - A rounded line end. + \endlist + */ + QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const { Q_D(const QQuickVisualPath); @@ -279,6 +419,18 @@ void QQuickVisualPath::setCapStyle(CapStyle style) } } +/*! + \qmlproperty enumeration QtQuick::VisualPath::strokeStyle + + This property defines the style of stroking. The default value is + VisualPath.SolidLine. + + \list + \li VisualPath.SolidLine - A plain line. + \li VisualPath.DashLine - Dashes separated by a few pixels. + \endlist + */ + QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const { Q_D(const QQuickVisualPath); @@ -296,6 +448,17 @@ void QQuickVisualPath::setStrokeStyle(StrokeStyle style) } } +/*! + \qmlproperty real QtQuick::VisualPath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + qreal QQuickVisualPath::dashOffset() const { Q_D(const QQuickVisualPath); @@ -313,6 +476,20 @@ void QQuickVisualPath::setDashOffset(qreal offset) } } +/*! + \qmlproperty list QtQuick::VisualPath::dashPattern + + This property defines the dash pattern when VisualPath.strokeStyle is set + to VisualPath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth + pixels followed by a space of 2 * VisualPath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + QVector QQuickVisualPath::dashPattern() const { Q_D(const QQuickVisualPath); @@ -330,6 +507,17 @@ void QQuickVisualPath::setDashPattern(const QVector &array) } } +/*! + \qmlproperty PathGradient QtQuick::VisualPath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of VisuaLPath.fillColor. + + When set, VisualPath.fillColor is ignored and filling is done using one of + the PathGradient subtypes. + */ + QQuickPathGradient *QQuickVisualPath::fillGradient() const { Q_D(const QQuickVisualPath); @@ -375,15 +563,56 @@ void QQuickVisualPath::resetFillGradient() \since 5.10 Renders a path either by generating geometry via QPainterPath and manual - triangulation or by using an extension like \c{GL_NV_path_rendering}. + triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}. This approach is different from rendering shapes via QQuickPaintedItem or the 2D Canvas because the path never gets rasterized in software. Therefore - it is suitable for creating shapes spreading over larger areas of the + PathItem is suitable for creating shapes spreading over larger areas of the screen, avoiding the performance penalty for texture uploads or framebuffer - blits. + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + The types for specifying path elements are shared between \l PathView and + PathItem. However, not all PathItem implementations support all path + element types, while some may not make sense for PathView. PathItem's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + PathItem { + width: 200 + height: 150 + anchors.centerIn: parent + VisualPath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: PathLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + PathGradientStop { position: 0; color: "blue" } + PathGradientStop { position: 0.2; color: "green" } + PathGradientStop { position: 0.4; color: "red" } + PathGradientStop { position: 0.6; color: "yellow" } + PathGradientStop { position: 1; color: "cyan" } + } + strokeStyle: VisualPath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png - Nonetheless it is important to be aware of performance implications, in + \note It is important to be aware of performance implications, in particular when the application is running on the generic PathItem implementation due to not having support for accelerated path rendering. The geometry generation happens entirely on the CPU in this case, and this @@ -396,18 +625,25 @@ void QQuickVisualPath::resetFillGradient() \c{GL_NV_path_rendering} where the cost of path property changes is much smaller. - \note The types for specifying path elements are shared between \l PathView - and PathItem. However, not all PathItem implementations support all path - element types, while some may not make sense for PathView. PathItem's - currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, - PathArc. + The following list summarizes the available PathItem rendering approaches: - \note Limited support for PathSvg is also provided in most cases. However, - there is no guarantee that this element is going to be supported for all - future PathItem backends. It is recommended to avoid the PathSvg element in - practice. + \list - See \l Path for a detailed overview of the supported path elements. + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + PathItem.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg */ @@ -418,7 +654,8 @@ QQuickPathItemPrivate::QQuickPathItemPrivate() rendererType(QQuickPathItem::UnknownRenderer), async(false), status(QQuickPathItem::Null), - renderer(nullptr) + renderer(nullptr), + enableVendorExts(true) { } @@ -453,12 +690,54 @@ QQuickPathItem::~QQuickPathItem() { } +/*! + \qmlproperty enumeration QtQuick::PathItem::rendererType + + This property determines which path rendering backend is active. + + \list + + \li PathItem.UnknownRenderer - The renderer is unknown. + + \li PathItem.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + QQuickPathItem::RendererType QQuickPathItem::rendererType() const { Q_D(const QQuickPathItem); return d->rendererType; } +/*! + \qmlproperty bool QtQuick::PathItem::asynchronous + + When PathItem.rendererType is PathItem.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the PathItem. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a PathItem visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + bool QQuickPathItem::asynchronous() const { Q_D(const QQuickPathItem); @@ -476,6 +755,49 @@ void QQuickPathItem::setAsynchronous(bool async) } } +/*! + \qmlproperty bool QtQuick::PathItem::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickPathItem::enableVendorExtensions() const +{ + Q_D(const QQuickPathItem); + return d->enableVendorExts; +} + +void QQuickPathItem::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickPathItem); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::status + + This property determines the status of the PathItem and is relevant when + PathItem.asynchronous is set to \c true. + + \list + + \li PathItem.Null - Not yet initialized. + + \li PathItem.Ready - The PathItem has finished processing. + + \li PathItem.Processing - The path is being processed. + + \endlist + */ + QQuickPathItem::Status QQuickPathItem::status() const { Q_D(const QQuickPathItem); @@ -520,6 +842,15 @@ static void vpe_clear(QQmlListProperty *property) d->_q_visualPathChanged(); } +/*! + \qmlproperty list QtQuick::PathItem::elements + + This property holds the VisualPath objects that define the contents of the + PathItem. + + \default + */ + QQmlListProperty QQuickPathItem::elements() { return QQmlListProperty(this, @@ -608,7 +939,7 @@ void QQuickPathItemPrivate::createRenderer() switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: - if (QQuickPathItemNvprRenderNode::isSupported()) { + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { rendererType = QQuickPathItem::NvprRenderer; renderer = new QQuickPathItemNvprRenderer; } else { @@ -641,7 +972,7 @@ QSGNode *QQuickPathItemPrivate::createNode() switch (ri->graphicsApi()) { #ifndef QT_NO_OPENGL case QSGRendererInterface::OpenGL: - if (QQuickPathItemNvprRenderNode::isSupported()) { + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { node = new QQuickPathItemNvprRenderNode; static_cast(renderer)->setNode( static_cast(node)); @@ -740,6 +1071,17 @@ void QQuickPathItemPrivate::sync() // ***** gradient support ***** +/*! + \qmltype PathGradientStop + \instantiates QQuickPathGradientStop + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) : QObject(parent), m_position(0), @@ -747,6 +1089,15 @@ QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) { } +/*! + \qmlproperty real QtQuick::PathGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + qreal QQuickPathGradientStop::position() const { return m_position; @@ -761,6 +1112,15 @@ void QQuickPathGradientStop::setPosition(qreal position) } } +/*! + \qmlproperty real QtQuick::PathGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + QColor QQuickPathGradientStop::color() const { return m_color; @@ -775,6 +1135,20 @@ void QQuickPathGradientStop::setColor(const QColor &color) } } +/*! + \qmltype PathGradient + \instantiates QQuickPathGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of PathItem fill gradients + \since 5.10 + + This is an abstract base class for gradients like PathLinearGradient and + cannot be created directly. + */ + QQuickPathGradient::QQuickPathGradient(QObject *parent) : QObject(parent), m_spread(PadSpread) @@ -794,6 +1168,14 @@ void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *st grad->m_stops.append(sstop); } +/*! + \qmlproperty list QtQuick::PathGradient::stops + \default + + The list of PathGradientStop objects defining the colors at given positions + in the gradient. + */ + QQmlListProperty QQuickPathGradient::stops() { return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); @@ -812,6 +1194,19 @@ QGradientStops QQuickPathGradient::sortedGradientStops() const return result; } +/*! + \qmlproperty enumeration QtQuick::PathGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is PathGradient.PadSpread. + + \list + \li PathGradient.PadSpread - The area is filled with the closest stop color. + \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const { return m_spread; @@ -826,11 +1221,39 @@ void QQuickPathGradient::setSpread(SpreadMode mode) } } +/*! + \qmltype PathLinearGradient + \instantiates QQuickPathLinearGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits PathGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) : QQuickPathGradient(parent) { } +/*! + \qmlproperty real QtQuick::PathLinearGradient::x1 + \qmlproperty real QtQuick::PathLinearGradient::y1 + \qmlproperty real QtQuick::PathLinearGradient::x2 + \qmlproperty real QtQuick::PathLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + qreal QQuickPathLinearGradient::x1() const { return m_start.x(); diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 991ab7a2bf..6d789aadbc 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -265,6 +265,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty elements READ elements) Q_CLASSINFO("DefaultProperty", "elements") @@ -293,6 +294,9 @@ public: bool asynchronous() const; void setAsynchronous(bool async); + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + Status status() const; QQmlListProperty elements(); @@ -313,6 +317,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); + void enableVendorExtensionsChanged(); void statusChanged(); private: diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h index 091a453c0b..c9a2904a25 100644 --- a/src/quick/items/qquickpathitem_p_p.h +++ b/src/quick/items/qquickpathitem_p_p.h @@ -194,6 +194,8 @@ public: QVector paths; QVector sfp; } jsData; + + bool enableVendorExts; }; class QQuickPathItemPathObject : public QObject -- cgit v1.2.3 From 4cddb73882ed950f652fd1e079bc4cf8ccde93d5 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 28 Mar 2017 13:19:29 +0200 Subject: Add a PathItem autotest for the declarative API Change-Id: I276c185c93122e5eb05ef6678ab62fa6928f2523 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 20 +++++++++++++++++++- src/quick/items/qquickpathitem_p.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index b14fc1caba..fff666c205 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -1155,6 +1155,20 @@ QQuickPathGradient::QQuickPathGradient(QObject *parent) { } +int QQuickPathGradient::countStops(QQmlListProperty *list) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) { QQuickPathGradientStop *sstop = qobject_cast(stop); @@ -1178,7 +1192,11 @@ void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *st QQmlListProperty QQuickPathGradient::stops() { - return QQmlListProperty(this, nullptr, &QQuickPathGradient::appendStop, nullptr, nullptr, nullptr); + return QQmlListProperty(this, nullptr, + &QQuickPathGradient::appendStop, + &QQuickPathGradient::countStops, + &QQuickPathGradient::atStop, + nullptr); } QGradientStops QQuickPathGradient::sortedGradientStops() const diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h index 6d789aadbc..37b23dee6f 100644 --- a/src/quick/items/qquickpathitem_p.h +++ b/src/quick/items/qquickpathitem_p.h @@ -114,6 +114,8 @@ signals: void spreadChanged(); private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); static void appendStop(QQmlListProperty *list, QObject *stop); QVector m_stops; -- cgit v1.2.3 From 4f2c5aa8970958b333998ad841d9162a5bfee644 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Mar 2017 13:37:56 +0200 Subject: PathItem/generic: Fix spread modes in gradient material Not including the mode in the comparison makes the scenegraph think that two Pathitems with two linear gradients that only differ in the spread mode can reuse the same material. That would be wrong. Change-Id: I34ad1b74ec8f98ec3a11d6a2565e0a7e5ae3673a Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitemgenericrenderer.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp index 7a259617dd..4e8fe55df2 100644 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ b/src/quick/items/qquickpathitemgenericrenderer.cpp @@ -744,6 +744,10 @@ int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) cons const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + if (int d = ga->start.x() - gb->start.x()) return d; if (int d = ga->start.y() - gb->start.y()) -- cgit v1.2.3 From 349d3400c11c0ad1c9aaec01c44b174dbb6ebf9a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 29 Mar 2017 14:17:51 +0200 Subject: PathItem: add some docs for the JS API Change-Id: I25a1e14e755350350f9a37ab0ac711576c25f535 Reviewed-by: Andy Nichols --- src/quick/items/qquickpathitem.cpp | 211 +++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp index fff666c205..fae16064e5 100644 --- a/src/quick/items/qquickpathitem.cpp +++ b/src/quick/items/qquickpathitem.cpp @@ -1973,11 +1973,142 @@ void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) m_v4value = wrapper; } +/*! + \qmltype JSPath + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes a path via a JavaScript API + */ + +/*! + \qmlmethod void QtQuick::JSPath::moveTo(x, y) + + Moves the path's position to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::lineTo(x, y) + + Defines a straight line to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) + + Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an + end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) + + Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and + (\a c2x, \a c2y), and an end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) + + Defines an elliptical arc, following the elliptical arc command in SVG. See + \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the + SVG path specification} for details on the parameters. + */ + +/*! + \qmlmethod void QtQuick::JSPath::clear() + + Clears the path object removing all path elements. This is more lightweight + than creating a new JSPath object. + */ + void QQuickPathItemPathObject::clear() { path = QQuickPathItemPath(); } +/*! + \qmltype StrokeFillParams + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes stroke and fill parameters via a JavaScript API + + The properties of StrokeFillParams objects correspond 1:1 to VisualPath + properties. The possible values for enumerations are the same as well, for + example: + + \code + sfp.strokeStyle = VisualPath.DashLine; + sfp.capStyle = VisualPath.RoundCap; + \endcode + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::strokeColor + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::strokeWidth + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::fillColor + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle + */ + +/*! + \qmlproperty int QtQuick::StrokeFillParams::miterLimit + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::dashOffset + */ + +/*! + \qmlproperty list QtQuick::StrokeFillParams::dashPattern + + The dash pattern can be specified using JavaScript arrays. + + \code + sfp.dashPattern = [ 4, 2 ]; + \endcode + */ + +/*! + \qmlproperty object QtQuick::StrokeFillParams::fillGradient + + Sets the fill gradient. The default value is null. Gradients cannot be + created from JavaScript. Instead, reference a PathLinearGradient or other + item by id. + + \code + PathLinearGradient { id: grad; ... } + ... + sfp.fillGradient = grad; + \endcode + */ + +/*! + \qmlmethod void QtQuick::StrokeFillParams::clear() + + Resets all values to their defaults. This is more lightweight than creating + a new StrokeFillParams object. + */ + void QQuickPathItemStrokeFillParamsObject::clear() { sfp = QQuickPathItemStrokeFillParams(); @@ -1996,6 +2127,45 @@ void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *eng m_v4value = wrapper; } +/*! + \qmlmethod JSPath QtQuick::PathItem::newPath() + + Creates and returns a new object that describes a path and offers a + JavaScript API. Paired with a stroke-fill parameter object it is + equivalent to a VisualPath. + + The following two snippets are equivalent when it comes to the end result: + + \code + var p = pathItem.newPath(); + var sfp = pathItem.newStrokeFillParams(); + sfp.fillColor = "white"; + sfp.strokeColor = "black"; + sfp.strokeWidth = 0.172; + p.moveTo(-122.304, 84.285); + p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); + pathItem.appendVisualPath(p, sfp); + \endcode + + \code + PathItem { + VisualPath { + fillColor: "white" + strokeColor: "black" + strokeWidth: 0.172 + Path { + startX: -122.304; startY: 84.285 + PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } + } + } + } + \endcode + + The latter offers a full declarative API, with the possibility to binding + to and animating properties, while the former uses less resources due to + greatly reducing the number of QObject instances created. +*/ + void QQuickPathItem::newPath(QQmlV4Function *args) { QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); @@ -2003,6 +2173,14 @@ void QQuickPathItem::newPath(QQmlV4Function *args) args->setReturnValue(obj->v4value()); } +/*! + \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() + + Creates and returns a new object that describes stroke and fill parameters + and offers a JavaScript API. Paired with a path object it is equivalent to + a VisualPath. + */ + void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) { QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); @@ -2010,6 +2188,15 @@ void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) args->setReturnValue(obj->v4value()); } +/*! + \qmlmethod void QtQuick::PathItem::clearVisualPaths() + + Clears the list of visual paths. + + \note This applies only to path and stroke-fill parameter objects registered + via appendVisualPaths(). + */ + void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) { Q_UNUSED(args); @@ -2018,6 +2205,17 @@ void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) d->jsData.sfp.clear(); } +/*! + \qmlmethod void QtQuick::PathItem::commitVisualPaths() + + Updates the PathItem. + + In order to avoid rendering a half-prepared PathItem, calling + PathItem.appendVisualPath() does not trigger any processing. Instead, + applications must call this function when all path and stroke-fill + parameter objects are registered. + */ + void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) { Q_UNUSED(args); @@ -2025,6 +2223,19 @@ void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) d->_q_visualPathChanged(); } +/*! + \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) + + Adds the visual path compoes of \a path and \a strokeFillParams into the + PathItem. + + \note The declarative and imprative (JavaScript) APIs of PathItem use + independent data structures. Calling this function has no effect on the + PathItem.elements property and vice versa. Once this function is called, + the PathItem will only consider the data registered via this function and + will ignore the declarative elements property. + */ + void QQuickPathItem::appendVisualPath(QQmlV4Function *args) { if (args->length() < 2) -- cgit v1.2.3 From 1c46cae880759f0601bae5a0033ab22a53349a4c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 17 Mar 2017 15:57:45 +0100 Subject: DragHandler: don't steal exclusive grab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I643d0e93e180bba5d9fea4543b93cbb66668c94d Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index db17df36ca..3b1ec1c344 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -95,8 +95,9 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) enforceAxisConstraints(&pos); target()->setPosition(pos); } - } else if ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || - (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point))) { + } else if (!point->exclusiveGrabber() && + ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || + (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)))) { setExclusiveGrab(point); if (target()) { if (point->pointerEvent()->asPointerTouchEvent()) -- cgit v1.2.3 From d0ce320646b7f852a24f6e0a9e9621ddcedef554 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 21 Mar 2017 14:38:25 +0100 Subject: PointerHandlers always ungrab if wantsPointerEvent returns false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That means the PinchHandler for example will require exactly requiredPointCount touchpoints. If it was satisfied, then one finger is released, it must give up its grabs so that another PointerHandler has a chance to take over. Change-Id: I28e32d6d3f255c7de8023f054dc480528bb14852 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 88c6800dcc..f625b50f10 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -198,10 +198,17 @@ QQuickItem *QQuickPointerHandler::target() const void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) { - if (wantsPointerEvent(event)) + if (wantsPointerEvent(event)) { handlePointerEventImpl(event); - else + } else { setActive(false); + int pCount = event->pointCount(); + for (int i = 0; i < pCount; ++i) { + QQuickEventPoint *pt = event->point(i); + if (pt->grabberPointerHandler() == this) + pt->cancelExclusiveGrab(); + } + } } bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) -- cgit v1.2.3 From 1457df74f4c1d770e1e820de8cd082be1bd2489e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 5 Apr 2017 11:21:29 +0200 Subject: Add QQuickItem acceptTouchEvents/setAcceptTouchEvents; require for touch It has been suboptimal to speculatively deliver touch events to Items which are not interested; even worse is when we must deliver to a parent item which is filtering events, when the child Item will not accept the touch event anyway. So now it is required that any QQuickItem subclass which wishes to accept touch events must call setAcceptTouchEvents(true) (typically in its constructor). If it does not do this, it will not get any touch events (and this saves us the trouble of looking for parents which filter touch events, too). It is consistent with needing to call setAcceptHoverEvents() to get hover events, and setAcceptedMouseButtons() to get mouse events. [ChangeLog][QtQuick][QQuickItem] When subclassing QQuickItem, it is now required to call setAcceptTouchEvents(true) if you need the item to receive touch events. Change-Id: Idc76c04f4e7f1d4a613087e756e96dac368f4f23 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 27 ++++++++++++++++++++++ src/quick/items/qquickitem.h | 2 ++ src/quick/items/qquickitem_p.h | 1 + src/quick/items/qquickmultipointtoucharea.cpp | 1 + src/quick/items/qquickpincharea.cpp | 1 + src/quick/items/qquickwindow.cpp | 32 ++++++++++++++++++--------- src/quick/items/qquickwindow_p.h | 2 +- 7 files changed, 54 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index bfc4a59851..ef22af965a 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3172,6 +3172,7 @@ QQuickItemPrivate::QQuickItemPrivate() , antialiasingValid(false) , isTabFence(false) , replayingPressEvent(false) + , touchEnabled(false) , dirtyAttributes(0) , nextDirtyItem(0) , prevDirtyItem(0) @@ -7190,6 +7191,32 @@ void QQuickItem::setAcceptHoverEvents(bool enabled) d->setHasHoverInChild(enabled); } +/*! + Returns whether touch events are accepted by this item. + + The default value is false. + + If this is false, then the item will not receive any touch events through + the touchEvent() function. +*/ +bool QQuickItem::acceptTouchEvents() const +{ + Q_D(const QQuickItem); + return d->touchEnabled; +} + +/*! + If \a enabled is true, this sets the item to accept touch events; + otherwise, touch events are not accepted by this item. + + \sa acceptTouchEvents() +*/ +void QQuickItem::setAcceptTouchEvents(bool accept) +{ + Q_D(QQuickItem); + d->touchEnabled = accept; +} + void QQuickItemPrivate::setHasCursorInChild(bool hasCursor) { #if QT_CONFIG(cursor) diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 1d174ca979..b7a25581a7 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -292,6 +292,8 @@ public: void setAcceptedMouseButtons(Qt::MouseButtons buttons); bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); + bool acceptTouchEvents() const; + void setAcceptTouchEvents(bool accept); #if QT_CONFIG(cursor) QCursor cursor() const; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 263cb19d20..6b010fc24d 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -442,6 +442,7 @@ public: // focus chain and prevents tabbing outside. bool isTabFence:1; bool replayingPressEvent:1; + bool touchEnabled:1; enum DirtyType { TransformOrigin = 0x00000001, diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 62119effb2..9b2b4daa58 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -420,6 +420,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent) if (qmlVisualTouchDebugging()) { setFlag(QQuickItem::ItemHasContents); } + setAcceptTouchEvents(true); #ifdef Q_OS_OSX setAcceptHoverEvents(true); // needed to enable touch events on mouse hover. #endif diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 6295aa1932..171b98cd31 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -288,6 +288,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent) { Q_D(QQuickPinchArea); d->init(); + setAcceptTouchEvents(true); #ifdef Q_OS_OSX setAcceptHoverEvents(true); // needed to enable touch events on mouse hover. #endif diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 888b0cde85..e6f77ea69a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1720,7 +1720,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { - QVector targetItems = pointerTargets(contentItem, point->scenePos(), false); + QVector targetItems = pointerTargets(contentItem, point->scenePos(), false, false); for (QQuickItem *item : targetItems) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); pointerEvent->localize(item); @@ -2206,7 +2206,10 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // check if item or any of its child items contain the point // FIXME: should this be iterative instead of recursive? -QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const +// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added. +// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR +// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't. +QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const { QVector targets; auto itemPrivate = QQuickItemPrivate::get(item); @@ -2224,13 +2227,16 @@ QVector QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons auto childPrivate = QQuickItemPrivate::get(child); if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - targets << pointerTargets(child, scenePos, false); + targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch); } - if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { - // add this item last - children take precedence - targets << item; - } + bool relevant = item->contains(itemPos); + if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) + relevant = false; + if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) + relevant = false; + if (relevant) + targets << item; // add this item last: children take precedence return targets; } @@ -2337,7 +2343,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve QQuickEventPoint *point = event->point(i); if (point->state() == QQuickEventPoint::Pressed) continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints - QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false); + QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), false, false); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2363,8 +2369,9 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) { const QVector points = event->unacceptedPressedPointScenePositions(); QVector targetItems; + bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); for (QPointF point: points) { - QVector targetItemsForPoint = pointerTargets(contentItem, point, false); + QVector targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2661,8 +2668,9 @@ void QQuickWindowPrivate::updateFilteringParentItems(const QVector for (QQuickItem *item : targetItems) { QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant - if (!item->acceptedMouseButtons() && !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) - continue; // TODO there's no acceptTouchEvents, so it's hard to avoid skipping any items which handle only touch + if (!item->acceptedMouseButtons() && !item->acceptTouchEvents() && + !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) + continue; QQuickItem *parent = item->parentItem(); while (parent) { if (parent->filtersChildMouseEvents()) { @@ -2700,6 +2708,8 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ QVarLengthArray, 32> passiveGrabsToCancel; for (QPair itemAndParent : filteringParentItems) { QQuickItem *item = receiver ? receiver : itemAndParent.first; + if (!item->acceptTouchEvents() && !item->acceptedMouseButtons()) + continue; // if this item won't accept, parents don't need to filter the touch for it QQuickItem *filteringParent = itemAndParent.second; if (item == filteringParent) continue; // a filtering item never needs to filter for itself diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index b8131a056f..f66809cc8a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -172,7 +172,7 @@ public: void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); - QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const; + QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; void updateFilteringParentItems(const QVector &targetItems); -- cgit v1.2.3 From 67ced4791ddabebfa19a2f101aa292b5eba60b90 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 5 Apr 2017 09:33:15 +0200 Subject: Make tst_qquickflickable::nestedSliderUsingTouch pass Revisions to 9b5fc80af28580e9672792dd511d876a93947882 and 781f76176239bfbfe6041f2676e2f2804337d312 are necessary to allow Flickable to steal synth-mouse events from children which accept only touch events. Change-Id: Id779368d7a44c1561da99a9f2c37e8d32278773e Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 7 ++----- src/quick/items/qquickwindow.cpp | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 19dd81b6fc..3b14f72870 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -610,11 +610,8 @@ void QQuickEventPoint::setGrabberItem(QQuickItem *grabber) m_sceneGrabPos = m_scenePos; if (oldGrabberHandler) oldGrabberHandler->onGrabChanged(oldGrabberHandler, CancelGrabExclusive, this); - else if (oldGrabberItem && oldGrabberItem != grabber) { - auto pte = pointerEvent()->asPointerTouchEvent(); - if (pte && pte->asTouchEvent() && pte->asTouchEvent()->touchPointStates() == Qt::TouchPointReleased) - oldGrabberItem->touchUngrabEvent(); - } + else if (oldGrabberItem && oldGrabberItem != grabber && grabber && pointerEvent()->asPointerTouchEvent()) + oldGrabberItem->touchUngrabEvent(); for (QPointer passiveGrabber : m_passiveGrabbers) passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this); } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e6f77ea69a..5538cadc06 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -748,7 +748,6 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) QQuickItem *oldGrabber = q->mouseGrabberItem(); qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber; - bool fromTouch = false; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber @@ -759,7 +758,6 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) for (auto handler : point->passiveGrabbers()) point->cancelPassiveGrab(handler); } - fromTouch = true; } else { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); @@ -773,11 +771,8 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (oldGrabber) { QEvent e(QEvent::UngrabMouse); QSet hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) oldGrabber->mouseUngrabEvent(); - if (fromTouch) - oldGrabber->touchUngrabEvent(); - } } } -- cgit v1.2.3 From 1b6cefc4d137fe4f029eb0a43b458f584211cc56 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 10:08:11 +0200 Subject: Fix remaining QQWindow qCDebugs to show eventpoint IDs in hex Change-Id: I04122218499733856136f5a49b72707a0e8885e5 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 5538cadc06..359763bfb4 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -751,7 +751,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem(); + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) { point->setGrabberItem(grabber); @@ -2273,7 +2273,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) QQuickEventPoint *point = event->point(i); if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); - qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released"; + qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released"; point->setGrabberItem(nullptr); if (id == touchMouseId) { touchMouseId = -1; -- cgit v1.2.3 From 97e2050f1c942da0b4d49df19b408c10a68e14b8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 10:07:02 +0200 Subject: TapHandler: add qt.quick.handler.tap logging category PinchHandler has its own. Maybe every pointerhandler should. Change-Id: Ic87494c8e333dcd502d4e259789b68337c3de349 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquicktaphandler.cpp | 37 ++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index fbbc6f1c7d..00bc3e27f0 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -44,6 +44,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap") + qreal QQuickTapHandler::m_multiTapInterval(0.0); // single tap distance is the same as the drag threshold int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1); @@ -189,6 +191,7 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) { if (event->timerId() == m_longPressTimer.timerId()) { m_longPressTimer.stop(); + qCDebug(lcTapHandler) << objectName() << "longPressed"; emit longPressed(); } } @@ -244,6 +247,7 @@ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gestureP void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) { if (m_pressed != press) { + qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press << (cancel ? "CANCEL" : "") << point; m_pressed = press; connectPreRenderSignal(press); updateTimeHeld(); @@ -261,20 +265,25 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi else setExclusiveGrab(point, press); } - if (!cancel && !press && point->timeHeld() < longPressThreshold()) { - // Assuming here that pointerEvent()->timestamp() is in ms. - qreal ts = point->pointerEvent()->timestamp() / 1000.0; - if (ts - m_lastTapTimestamp < m_multiTapInterval && - QVector2D(point->scenePos() - m_lastTapPos).lengthSquared() < - (point->pointerEvent()->device()->type() == QQuickPointerDevice::Mouse ? - m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared)) - ++m_tapCount; - else - m_tapCount = 1; - emit tapped(point); - emit tapCountChanged(); - m_lastTapTimestamp = ts; - m_lastTapPos = point->scenePos(); + if (!cancel && !press) { + if (point->timeHeld() < longPressThreshold()) { + // Assuming here that pointerEvent()->timestamp() is in ms. + qreal ts = point->pointerEvent()->timestamp() / 1000.0; + if (ts - m_lastTapTimestamp < m_multiTapInterval && + QVector2D(point->scenePos() - m_lastTapPos).lengthSquared() < + (point->pointerEvent()->device()->type() == QQuickPointerDevice::Mouse ? + m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared)) + ++m_tapCount; + else + m_tapCount = 1; + qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times"; + emit tapped(point); + emit tapCountChanged(); + m_lastTapTimestamp = ts; + m_lastTapPos = point->scenePos(); + } else { + qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point->timeHeld(); + } } emit pressedChanged(); if (!press) { -- cgit v1.2.3 From c2d6f267a74f7fdd9422af88e35ade280cf0a3d3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 31 Mar 2017 23:42:04 +0200 Subject: PinchHandler: add centroidVelocity Change-Id: I34cc9146155bded8311c1173e4b8d34d8b17b034 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler.cpp | 10 ++++++++++ src/quick/handlers/qquickmultipointerhandler_p.h | 1 + src/quick/handlers/qquickpinchhandler.cpp | 1 + src/quick/handlers/qquickpinchhandler_p.h | 3 +++ 4 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index c6534e6bb6..d1394e9516 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -152,6 +152,16 @@ QPointF QQuickMultiPointerHandler::touchPointCentroid() return ret / m_currentPoints.size(); } +QVector2D QQuickMultiPointerHandler::touchPointCentroidVelocity() +{ + QVector2D ret; + if (Q_UNLIKELY(m_currentPoints.size() == 0)) + return ret; + for (QQuickEventPoint *point : qAsConst(m_currentPoints)) + ret += point->velocity(); + return ret / m_currentPoints.size(); +} + qreal QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref) { qreal ret = 0; diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index d6d660ec04..be92a39631 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -90,6 +90,7 @@ protected: bool sameAsCurrentPoints(QQuickPointerEvent *event); QVector pointsInsideOrNearTarget(QQuickPointerEvent *event); QPointF touchPointCentroid(); + QVector2D touchPointCentroidVelocity(); qreal averageTouchPointDistance(const QPointF &ref); qreal averageStartingDistance(const QPointF &ref); qreal averageTouchPointAngle(const QPointF &ref); diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 5469df206c..67d32cdb18 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -242,6 +242,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) } // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter m_centroid = touchPointCentroid(); + m_centroidVelocity = touchPointCentroidVelocity(); QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY); // avoid mapping the minima and maxima, as they might have unmappable values // such as -inf/+inf. Because of this we perform the bounding to min/max in local coords. diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h index d788e2cb36..6768196909 100644 --- a/src/quick/handlers/qquickpinchhandler_p.h +++ b/src/quick/handlers/qquickpinchhandler_p.h @@ -67,6 +67,7 @@ class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged) Q_PROPERTY(QPointF centroid READ centroid NOTIFY updated) + Q_PROPERTY(QVector2D centroidVelocity READ centroidVelocity NOTIFY updated) Q_PROPERTY(qreal scale READ scale NOTIFY updated) Q_PROPERTY(qreal rotation READ rotation NOTIFY updated) Q_PROPERTY(QPointF translation READ translation NOTIFY updated) @@ -103,6 +104,7 @@ public: qreal scale() const { return m_activeScale; } qreal rotation() const { return m_activeRotation; } QPointF centroid() const { return m_centroid; } + QVector2D centroidVelocity() const { return m_centroidVelocity; } qreal minimumX() const { return m_minimumX; } void setMinimumX(qreal minX); @@ -136,6 +138,7 @@ private: qreal m_activeRotation; QPointF m_activeTranslation; QPointF m_centroid; + QVector2D m_centroidVelocity; qreal m_minimumScale; qreal m_maximumScale; -- cgit v1.2.3 From 083160b082a9e622c5266803ab8adb19028c31b6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 31 Mar 2017 16:43:03 +0200 Subject: QQuickEventPoint: always inform grabberItem on touch grab cancel If we have a TouchCancel event we send it to the Item via sendEvent, so it can be detected in the Item's event() or touchEvent() override. If not, it still needs to be informed that it lost the grab, so call touchUngrabEvent() instead. MultiPointTouchArea for example will stop showing that all its points are pressed, if the grab is stolen by something else. Followup to 59c753bc75c7cfd4068fbbba3c25e1f54c46f4c0 Change-Id: I211c02d51cdde8f8722600cf914485adcc2aa1f3 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 3b14f72870..00dcdf2fca 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -709,6 +709,8 @@ void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent) } else if (auto item = grabberItem()) { if (cancelEvent) QCoreApplication::sendEvent(item, cancelEvent); + else + item->touchUngrabEvent(); } m_exclusiveGrabber.clear(); } -- cgit v1.2.3 From e58241c5d4630b4cb00cb28d7cf6bb31b861ec6b Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 11:55:08 +0200 Subject: QQuickPointerTouchEvent::reset(): preserve reordered touchpoints better It's not just the grabbers we have to worry about. If touchpoints in one event are in a different order than in the previous one, we need to also preserve anything that was remembered from when that touchpoint was pressed: that is pressTimestamp, scenePressPos, and maybe sceneGrabPos. So now we use a vector of structs for that purpose, which is more extensible and easier to read. Change-Id: Ibf2c0079693ed10988f0066184d53ee9106f2eca Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 45 +++++++++++++++++++++++++------------- src/quick/items/qquickevents_p_p.h | 1 + 2 files changed, 31 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 00dcdf2fca..ba93a4195e 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -920,36 +920,51 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event) for (int i = m_touchPoints.size(); i < newPointCount; ++i) m_touchPoints.insert(i, new QQuickEventTouchPoint(this)); - // Make sure the grabbers are right from one event to the next - QVector grabbers; - QVector > > passiveGrabberses; - passiveGrabberses.reserve(newPointCount); - // Copy all grabbers, because the order of points might have changed in the event. + // Make sure the grabbers and on-pressed values are right from one event to the next + struct ToPreserve { + int pointId; // just for double-checking + ulong pressTimestamp; + QPointF scenePressPos; + QPointF sceneGrabPos; + QObject * grabber; + QVector > passiveGrabbers; + + ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {} + }; + QVector preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event + + // Copy stuff we need to preserve, because the order of points might have changed in the event. // The ID is all that we can rely on (release might remove the first point etc). for (int i = 0; i < newPointCount; ++i) { - QObject *grabber = nullptr; - QVector > passiveGrabbers; - if (auto point = pointById(tps.at(i).id())) { - grabber = point->exclusiveGrabber(); - passiveGrabbers = point->passiveGrabbers(); + int pid = tps.at(i).id(); + if (auto point = pointById(pid)) { + preserves[i].pointId = pid; + preserves[i].pressTimestamp = point->m_pressTimestamp; + preserves[i].scenePressPos = point->scenePressPos(); + preserves[i].sceneGrabPos = point->sceneGrabPos(); + preserves[i].grabber = point->exclusiveGrabber(); + preserves[i].passiveGrabbers = point->passiveGrabbers(); } - grabbers.append(grabber); - passiveGrabberses.append(passiveGrabbers); } for (int i = 0; i < newPointCount; ++i) { auto point = m_touchPoints.at(i); point->reset(tps.at(i), ev->timestamp()); + const auto &preserved = preserves.at(i); if (point->state() == QQuickEventPoint::Pressed) { - if (grabbers.at(i)) + if (preserved.grabber) qWarning() << "TouchPointPressed without previous release event" << point; point->setGrabberItem(nullptr); point->clearPassiveGrabbers(); } else { // Restore the grabbers without notifying (don't call onGrabChanged) - point->m_exclusiveGrabber = grabbers.at(i); + Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId()); + point->m_pressTimestamp = preserved.pressTimestamp; + point->m_scenePressPos = preserved.scenePressPos; + point->m_sceneGrabPos = preserved.sceneGrabPos; + point->m_exclusiveGrabber = preserved.grabber; point->m_grabberIsHandler = (qmlobject_cast(point->m_exclusiveGrabber) != nullptr); - point->m_passiveGrabbers = passiveGrabberses.at(i); + point->m_passiveGrabbers = preserved.passiveGrabbers; } } m_pointCount = newPointCount; diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 2eebf131bc..72d5be54c9 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -341,6 +341,7 @@ protected: bool m_grabberIsHandler : 1; int m_reserved : 29; + friend class QQuickPointerTouchEvent; friend class QQuickWindowPrivate; Q_DISABLE_COPY(QQuickEventPoint) -- cgit v1.2.3 From dbcc38455369f15920d0e18c9be8d909010d5af8 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 18 Apr 2017 09:52:29 +0200 Subject: TapHandler:wants: don't setPressed(false) unless pointId matches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the multibuttons manual test: the first button would see that a second point was pressed, and that it didn't want it, because it's outside the bounds. This is not a good reason to discontinue responding to the first touchpoint, which is the one it was first interested in. Change-Id: I7004a667873f235d3dda84b4261113d37d911763 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 00bc3e27f0..85188700d9 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -138,7 +138,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called. // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but // we still don't want to be pressed anymore. - if (!ret) + if (!ret && point->pointId() == pointId()) setPressed(false, true, point); return ret; } -- cgit v1.2.3 From 80083b0d82a70e7d4181a46a7ad205fb683bb5fc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 19 Apr 2017 21:00:52 +0200 Subject: TapHandler: don't "want" every updated/stationary point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the policy is ReleaseWithinBounds, we assumed wrongly that wantsEventPoint() would not be asked about a point which QQuickPointerSingleHandler::wantsEventPoint() would already rule out, or about a point which was not grabbed either passively or exclusively. But in fact it is asked about every point. A tap is not going to occur unless the press occurs within bounds; if that happened, then QQuickPointerSingleHandler::m_pointId will have been set. So a TapHandler with this policy should remain interested in an updated or stationary point only if it is the same one which it was already interested in when it was pressed. This gets the multipoint button example and autotest working. Change-Id: I399c06071fd804cd6994d5f0153c307cec9d2f90 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 85188700d9..aeda9a0357 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -129,7 +129,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) ret = parentContains(point); break; case ReleaseWithinBounds: - ret = true; + ret = point->pointId() == pointId(); break; } break; -- cgit v1.2.3 From fdb7c432514c828a7a921270d55e1c134151e19a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 19 Apr 2017 21:32:27 +0200 Subject: QQPSingleHandler: verify wantsEventPoint() even when pointId is chosen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows TapHandler to veto the "wanting" during an update, if the policy is violated. For example, if the policy is WithinBounds, and the point leaves the bounds, it will say that it does not want the point; but without this change, it is not asked again when the point is moved outside the bounds. Change-Id: Id764b3d24966ce9c3c4795e9f2a0aa37821caa42 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 6a4c4da7e6..5d61c75fbc 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -76,11 +76,10 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) int c = event->pointCount(); for (int i = 0; i < c; ++i) { QQuickEventPoint *p = event->point(i); - if (p->pointId() == m_pointId) { - point = p; - ++candidatePointCount; - } else if (wantsEventPoint(p)) { + if (wantsEventPoint(p)) { ++candidatePointCount; + if (p->pointId() == m_pointId) + point = p; } } if (point) { -- cgit v1.2.3 From dcc7367997e7241918cdf0c702c7bb8325eb1ad4 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 09:59:00 +0200 Subject: TapHandler: do not react to stationary touchpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In autotests, stationary points normally have invalid position. A TapHandler does not need to react to them anyway. But we must also avoid having a grab cancelation due to a stationary point, and that applies to all PointerHandlers in general. Change-Id: I99493ad7d859e0c4ef155afc699aa34f28ffdbc7 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 2 +- src/quick/handlers/qquicktaphandler.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index f625b50f10..8f113ddd56 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -205,7 +205,7 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) int pCount = event->pointCount(); for (int i = 0; i < pCount; ++i) { QQuickEventPoint *pt = event->point(i); - if (pt->grabberPointerHandler() == this) + if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary) pt->cancelExclusiveGrab(); } } diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index aeda9a0357..14aeb07864 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -120,7 +120,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) case QQuickEventPoint::Released: ret = parentContains(point); break; - default: // update or stationary + case QQuickEventPoint::Updated: switch (m_gesturePolicy) { case DragThreshold: ret = !dragOverThreshold(point); @@ -133,12 +133,18 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) break; } break; + case QQuickEventPoint::Stationary: + // Never react in any way when the point hasn't moved. + // In autotests, the point's position may not even be correct, because + // QTest::touchEvent(window, touchDevice).stationary(1) + // provides no opportunity to give a position, so it ends up being random. + break; } // If this is the grabber, returning false from this function will cancel the grab, // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called. // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but // we still don't want to be pressed anymore. - if (!ret && point->pointId() == pointId()) + if (!ret && point->pointId() == pointId() && point->state() != QQuickEventPoint::Stationary) setPressed(false, true, point); return ret; } -- cgit v1.2.3 From df0649fc57de1276ec1d0eb22af8b1b7dfc81a57 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 20 Apr 2017 10:01:34 +0200 Subject: QQPSingleHandler: don't accept a touchpoint other than the chosen one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also caused a failure in the new TapHandler test: wantsPointerEvent() can see stationary touchpoints, and they may end up being "candidates" (although not if wantsEventPoint() returns false for all stationary points), but don't get confused by that: we chose a particular point, always the first for which wantsEventPoint() returns true, not any subsequent points for which wantsEventPoint() returns true. It's important to accept only that point, because in other places where we don't have access to the return value from wantsEventPoint(), we treat the accepted state as the _memory_ of what wantsEventPoint() returned. So it must be consistently the same. Change-Id: Ia121bde50d956b45b2cf62eddf326293a5c02274 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 5d61c75fbc..020d3ef7de 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -98,15 +98,18 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true. int candidatePointCount = 0; int c = event->pointCount(); - QQuickEventPoint *p = nullptr; + QQuickEventPoint *chosen = nullptr; for (int i = 0; i < c; ++i) { - p = event->point(i); - if (!p->exclusiveGrabber() && wantsEventPoint(p)) + QQuickEventPoint *p = event->point(i); + if (!p->exclusiveGrabber() && wantsEventPoint(p)) { + if (!chosen) + chosen = p; ++candidatePointCount; + } } - if (p && candidatePointCount == 1) { - m_pointId = p->pointId(); - p->setAccepted(); + if (chosen && candidatePointCount == 1) { + m_pointId = chosen->pointId(); + chosen->setAccepted(); } } return m_pointId; -- cgit v1.2.3 From d728ad7ace09c2b35d6753fc08c4092ea8813193 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Sat, 15 Apr 2017 10:12:10 +0200 Subject: DragHandler: onGrabChanged, enforceConstraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the grab is stolen by a parent such as a Flickable, ensure that the DragHandler doesn't keep dragging. This helps to prevent dragging sliders out of their "groove" constraints. Change-Id: Id24f53e137ed186b1c02ab9c73a69a59022e80b0 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 6 ++++++ src/quick/handlers/qquickdraghandler_p.h | 1 + 2 files changed, 7 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 3b1ec1c344..73ad97712e 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -73,6 +73,12 @@ bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) || QQuickPointerSingleHandler::wantsEventPoint(point)); } +void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) +{ + enforceConstraints(); + QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); +} + void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) { point->setAccepted(); diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index cfccc19115..a75ea6f2c7 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -112,6 +112,7 @@ Q_SIGNALS: protected: bool wantsEventPoint(QQuickEventPoint *point) override; + void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; private: void ungrab(); -- cgit v1.2.3 From b8dbafcae9ccfbf5c2ef7a173c51a73e57476681 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 30 Mar 2017 15:39:44 +0200 Subject: QQuickPointerSingleHandler: add setIgnoreAdditionalPoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After 3523b676382db4aa39adeb9126d8bb2185e84403 it became impossible to write a subclass which handles one point and ignores the rest. In some cases this is necessary. Change-Id: I27511e6112a93ef98a4cf45c8531287781698b5b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 8 +++++++- src/quick/handlers/qquickpointersinglehandler_p.h | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 020d3ef7de..5ad60291dd 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -56,6 +56,7 @@ QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) , m_rotation(0) , m_pressure(0) , m_acceptedButtons(Qt::AllButtons) + , m_ignoreAdditionalPoints(false) { } @@ -83,7 +84,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) } } if (point) { - if (candidatePointCount == 1) { + if (candidatePointCount == 1 || (candidatePointCount > 1 && m_ignoreAdditionalPoints)) { point->setAccepted(); return true; } else { @@ -192,6 +193,11 @@ void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQ emit singlePointGrabChanged(); } +void QQuickPointerSingleHandler::setIgnoreAdditionalPoints(bool v) +{ + m_ignoreAdditionalPoints = v; +} + void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) { if (buttons != m_pressedButtons) { diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 9c854c4d0d..466de1a1eb 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -106,6 +106,8 @@ protected: QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; + void setIgnoreAdditionalPoints(bool v = true); + private: void setPressedButtons(Qt::MouseButtons buttons); void reset(); @@ -122,6 +124,7 @@ private: qreal m_pressure; QSizeF m_ellipseDiameters; Qt::MouseButtons m_acceptedButtons; + bool m_ignoreAdditionalPoints : 1; }; QT_END_NAMESPACE -- cgit v1.2.3 From f569af401aae651ea9bfa1f3a8ff68a58f8d71d1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 31 Mar 2017 18:36:53 +0200 Subject: QQuickPointerDeviceHandler: add acceptedModifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes you want to require holding down a key in order to enable some interaction. As with the other "accepted" flags, it's better to do this with a property than with Javascript. Change-Id: Ie29880f5f9f496ddca1bee462e2c0e6dd30fa9f5 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerdevicehandler.cpp | 40 ++++++++++++++++++++++- src/quick/handlers/qquickpointerdevicehandler_p.h | 5 +++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp index 3a320fdb32..203f712179 100644 --- a/src/quick/handlers/qquickpointerdevicehandler.cpp +++ b/src/quick/handlers/qquickpointerdevicehandler.cpp @@ -53,6 +53,7 @@ QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent) : QQuickPointerHandler(parent) , m_acceptedDevices(QQuickPointerDevice::AllDevices) , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes) + , m_acceptedModifiers(Qt::KeyboardModifierMask) { } @@ -78,17 +79,54 @@ void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::Po emit acceptedPointerTypesChanged(); } +/*! + \qmlproperty QQuickPointerDeviceHandler::acceptedModifiers + + If this property is set, it will require the given keyboard modifiers to + be pressed in order to react to pointer events, and otherwise ignore them. + + If this property is set to Qt.KeyboardModifierMask (the default value), + then the PointerHandler ignores the modifier keys. + + For example an Item could have two handlers of the same type, one of which + is enabled only if the required keyboard modifiers are pressed: + + \qml + Item { + TapHandler { + acceptedModifiers: Qt.ControlModifier + onTapped: console.log("control-tapped") + } + TapHandler { + acceptedModifiers: Qt.NoModifier + onTapped: console.log("tapped") + } + } + \endqml +*/ +void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers) +{ + if (m_acceptedModifiers == acceptedModifiers) + return; + + m_acceptedModifiers = acceptedModifiers; + emit acceptedModifiersChanged(); +} + bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event) { if (!QQuickPointerHandler::wantsPointerEvent(event)) return false; qCDebug(lcPointerHandlerDispatch) << objectName() << "checking device type" << m_acceptedDevices - << "pointer type" << m_acceptedPointerTypes; + << "pointer type" << m_acceptedPointerTypes + << "modifiers" << m_acceptedModifiers; if ((event->device()->type() & m_acceptedDevices) == 0) return false; if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0) return false; + if (m_acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != m_acceptedModifiers) + return false; return true; } diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h index 76c9cc44f0..9e30fa0be4 100644 --- a/src/quick/handlers/qquickpointerdevicehandler_p.h +++ b/src/quick/handlers/qquickpointerdevicehandler_p.h @@ -60,6 +60,7 @@ class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler Q_OBJECT Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged) Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged) + Q_PROPERTY(Qt::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged) public: explicit QQuickPointerDeviceHandler(QObject *parent = 0); @@ -67,14 +68,17 @@ public: QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; } QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; } + Qt::KeyboardModifiers acceptedModifiers() const { return m_acceptedModifiers; } public slots: void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices); void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes); + void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers); Q_SIGNALS: void acceptedDevicesChanged(); void acceptedPointerTypesChanged(); + void acceptedModifiersChanged(); protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; @@ -82,6 +86,7 @@ protected: protected: QQuickPointerDevice::DeviceTypes m_acceptedDevices; QQuickPointerDevice::PointerTypes m_acceptedPointerTypes; + Qt::KeyboardModifiers m_acceptedModifiers; }; QT_END_NAMESPACE -- cgit v1.2.3 From e2520ff76be49c5aa917741cc6a380fe1549e47d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 3 Apr 2017 11:58:46 +0200 Subject: Move PathItem to qt.labs Change-Id: I1cd686cff60bd40fe2cbbc34f917fac7835b6b7d Reviewed-by: Qt CI Bot Reviewed-by: Andy Nichols Reviewed-by: Robin Burchell --- src/imports/imports.pro | 2 + src/imports/pathitem/pathitem.pro | 31 + src/imports/pathitem/plugin.cpp | 74 + src/imports/pathitem/plugins.qmltypes | 312 +++ src/imports/pathitem/qmldir | 4 + src/imports/pathitem/qquicknvprfunctions.cpp | 284 +++ src/imports/pathitem/qquicknvprfunctions_p.h | 406 ++++ src/imports/pathitem/qquicknvprfunctions_p_p.h | 70 + src/imports/pathitem/qquickpathitem.cpp | 2258 ++++++++++++++++++++ src/imports/pathitem/qquickpathitem_p.h | 333 +++ src/imports/pathitem/qquickpathitem_p_p.h | 282 +++ .../pathitem/qquickpathitemgenericrenderer.cpp | 775 +++++++ .../pathitem/qquickpathitemgenericrenderer_p.h | 303 +++ .../pathitem/qquickpathitemnvprrenderer.cpp | 923 ++++++++ .../pathitem/qquickpathitemnvprrenderer_p.h | 237 ++ .../pathitem/qquickpathitemsoftwarerenderer.cpp | 277 +++ .../pathitem/qquickpathitemsoftwarerenderer_p.h | 136 ++ src/quick/items/context2d/qquickcontext2d.cpp | 3 +- src/quick/items/items.pri | 15 +- src/quick/items/qquickitemsmodule.cpp | 6 - src/quick/items/qquickpathitem.cpp | 2258 -------------------- src/quick/items/qquickpathitem_p.h | 335 --- src/quick/items/qquickpathitem_p_p.h | 282 --- src/quick/items/qquickpathitemgenericrenderer.cpp | 775 ------- src/quick/items/qquickpathitemgenericrenderer_p.h | 303 --- src/quick/items/qquickpathitemnvprrenderer.cpp | 923 -------- src/quick/items/qquickpathitemnvprrenderer_p.h | 237 -- src/quick/items/qquickpathitemsoftwarerenderer.cpp | 277 --- src/quick/items/qquickpathitemsoftwarerenderer_p.h | 136 -- src/quick/util/qquicknvprfunctions.cpp | 284 --- src/quick/util/qquicknvprfunctions_p.h | 406 ---- src/quick/util/qquicknvprfunctions_p_p.h | 70 - src/quick/util/qquicksvgparser_p.h | 7 +- src/quick/util/util.pri | 7 - 34 files changed, 6715 insertions(+), 6316 deletions(-) create mode 100644 src/imports/pathitem/pathitem.pro create mode 100644 src/imports/pathitem/plugin.cpp create mode 100644 src/imports/pathitem/plugins.qmltypes create mode 100644 src/imports/pathitem/qmldir create mode 100644 src/imports/pathitem/qquicknvprfunctions.cpp create mode 100644 src/imports/pathitem/qquicknvprfunctions_p.h create mode 100644 src/imports/pathitem/qquicknvprfunctions_p_p.h create mode 100644 src/imports/pathitem/qquickpathitem.cpp create mode 100644 src/imports/pathitem/qquickpathitem_p.h create mode 100644 src/imports/pathitem/qquickpathitem_p_p.h create mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer_p.h create mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer_p.h create mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp create mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h delete mode 100644 src/quick/items/qquickpathitem.cpp delete mode 100644 src/quick/items/qquickpathitem_p.h delete mode 100644 src/quick/items/qquickpathitem_p_p.h delete mode 100644 src/quick/items/qquickpathitemgenericrenderer.cpp delete mode 100644 src/quick/items/qquickpathitemgenericrenderer_p.h delete mode 100644 src/quick/items/qquickpathitemnvprrenderer.cpp delete mode 100644 src/quick/items/qquickpathitemnvprrenderer_p.h delete mode 100644 src/quick/items/qquickpathitemsoftwarerenderer.cpp delete mode 100644 src/quick/items/qquickpathitemsoftwarerenderer_p.h delete mode 100644 src/quick/util/qquicknvprfunctions.cpp delete mode 100644 src/quick/util/qquicknvprfunctions_p.h delete mode 100644 src/quick/util/qquicknvprfunctions_p_p.h (limited to 'src') diff --git a/src/imports/imports.pro b/src/imports/imports.pro index c03224958c..df0ad01c06 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -22,6 +22,8 @@ qtHaveModule(quick) { qtConfig(systemsemaphore): SUBDIRS += sharedimage qtConfig(quick-particles): \ SUBDIRS += particles + + SUBDIRS += pathitem } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/imports/pathitem/pathitem.pro b/src/imports/pathitem/pathitem.pro new file mode 100644 index 0000000000..d70bb6f203 --- /dev/null +++ b/src/imports/pathitem/pathitem.pro @@ -0,0 +1,31 @@ +CXX_MODULE = qml +TARGET = qmlpathitemplugin +TARGETPATH = Qt/labs/pathitem +IMPORT_VERSION = 1.0 + +QT = core gui qml quick quick-private + +HEADERS += \ + qquickpathitem_p.h \ + qquickpathitem_p_p.h \ + qquickpathitemgenericrenderer_p.h \ + qquickpathitemsoftwarerenderer_p.h + +SOURCES += \ + plugin.cpp \ + qquickpathitem.cpp \ + qquickpathitemgenericrenderer.cpp \ + qquickpathitemsoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + qquicknvprfunctions_p.h \ + qquicknvprfunctions_p_p.h \ + qquickpathitemnvprrenderer_p.h + + SOURCES += \ + qquicknvprfunctions.cpp \ + qquickpathitemnvprrenderer.cpp +} + +load(qml_plugin) diff --git a/src/imports/pathitem/plugin.cpp b/src/imports/pathitem/plugin.cpp new file mode 100644 index 0000000000..6b43e8f398 --- /dev/null +++ b/src/imports/pathitem/plugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 +#include + +#include "qquickpathitem_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_pathitem); +#endif +} + +QT_BEGIN_NAMESPACE + +class QmlPathItemPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QmlPathItemPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.pathitem")); + qmlRegisterType(uri, 1, 0, "PathItem"); + qmlRegisterType(uri, 1, 0, "VisualPath"); + qmlRegisterType(uri, 1, 0, "PathGradientStop"); + qmlRegisterUncreatableType(uri, 1, 0, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); + qmlRegisterType(uri, 1, 0, "PathLinearGradient"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/pathitem/plugins.qmltypes b/src/imports/pathitem/plugins.qmltypes new file mode 100644 index 0000000000..03f26e243c --- /dev/null +++ b/src/imports/pathitem/plugins.qmltypes @@ -0,0 +1,312 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.pathitem 1.0' + +Module { + dependencies: [] + Component { + name: "QQuickItem" + defaultProperty: "data" + prototype: "QObject" + Enum { + name: "TransformOrigin" + values: { + "TopLeft": 0, + "Top": 1, + "TopRight": 2, + "Left": 3, + "Center": 4, + "Right": 5, + "BottomLeft": 6, + "Bottom": 7, + "BottomRight": 8 + } + } + Property { name: "parent"; type: "QQuickItem"; isPointer: true } + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "x"; type: "double" } + Property { name: "y"; type: "double" } + Property { name: "z"; type: "double" } + Property { name: "width"; type: "double" } + Property { name: "height"; type: "double" } + Property { name: "opacity"; type: "double" } + Property { name: "enabled"; type: "bool" } + Property { name: "visible"; type: "bool" } + Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } + Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } + Property { name: "state"; type: "string" } + Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } + Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } + Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baselineOffset"; type: "double" } + Property { name: "clip"; type: "bool" } + Property { name: "focus"; type: "bool" } + Property { name: "activeFocus"; type: "bool"; isReadonly: true } + Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } + Property { name: "rotation"; type: "double" } + Property { name: "scale"; type: "double" } + Property { name: "transformOrigin"; type: "TransformOrigin" } + Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } + Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } + Property { name: "smooth"; type: "bool" } + Property { name: "antialiasing"; type: "bool" } + Property { name: "implicitWidth"; type: "double" } + Property { name: "implicitHeight"; type: "double" } + Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } + Signal { + name: "childrenRectChanged" + Parameter { type: "QRectF" } + } + Signal { + name: "baselineOffsetChanged" + Parameter { type: "double" } + } + Signal { + name: "stateChanged" + Parameter { type: "string" } + } + Signal { + name: "focusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusOnTabChanged" + revision: 1 + Parameter { type: "bool" } + } + Signal { + name: "parentChanged" + Parameter { type: "QQuickItem"; isPointer: true } + } + Signal { + name: "transformOriginChanged" + Parameter { type: "TransformOrigin" } + } + Signal { + name: "smoothChanged" + Parameter { type: "bool" } + } + Signal { + name: "antialiasingChanged" + Parameter { type: "bool" } + } + Signal { + name: "clipChanged" + Parameter { type: "bool" } + } + Signal { + name: "windowChanged" + revision: 1 + Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } + } + Method { name: "update" } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + Parameter { name: "targetSize"; type: "QSize" } + } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + } + Method { + name: "contains" + type: "bool" + Parameter { name: "point"; type: "QPointF" } + } + Method { + name: "mapFromItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapFromGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { name: "forceActiveFocus" } + Method { + name: "forceActiveFocus" + Parameter { name: "reason"; type: "Qt::FocusReason" } + } + Method { + name: "nextItemInFocusChain" + revision: 1 + type: "QQuickItem*" + Parameter { name: "forward"; type: "bool" } + } + Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } + Method { + name: "childAt" + type: "QQuickItem*" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + } + Component { + name: "QQuickPathGradient" + defaultProperty: "stops" + prototype: "QObject" + exports: ["Qt.labs.pathitem/PathGradient 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "SpreadMode" + values: { + "PadSpread": 0, + "RepeatSpread": 1, + "ReflectSpread": 2 + } + } + Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "spread"; type: "SpreadMode" } + Signal { name: "updated" } + } + Component { + name: "QQuickPathGradientStop" + prototype: "QObject" + exports: ["Qt.labs.pathitem/PathGradientStop 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "position"; type: "double" } + Property { name: "color"; type: "QColor" } + } + Component { + name: "QQuickPathItem" + defaultProperty: "elements" + prototype: "QQuickItem" + exports: ["Qt.labs.pathitem/PathItem 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "RendererType" + values: { + "UnknownRenderer": 0, + "GeometryRenderer": 1, + "NvprRenderer": 2, + "SoftwareRenderer": 3 + } + } + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Processing": 2 + } + } + Property { name: "renderer"; type: "RendererType"; isReadonly: true } + Property { name: "asynchronous"; type: "bool" } + Property { name: "enableVendorExtensions"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "elements"; type: "QQuickVisualPath"; isList: true; isReadonly: true } + Method { + name: "newPath" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "newStrokeFillParams" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "clearVisualPaths" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "commitVisualPaths" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "appendVisualPath" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + } + Component { + name: "QQuickPathLinearGradient" + defaultProperty: "stops" + prototype: "QQuickPathGradient" + exports: ["Qt.labs.pathitem/PathLinearGradient 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "x1"; type: "double" } + Property { name: "y1"; type: "double" } + Property { name: "x2"; type: "double" } + Property { name: "y2"; type: "double" } + } + Component { + name: "QQuickVisualPath" + defaultProperty: "path" + prototype: "QObject" + exports: ["Qt.labs.pathitem/VisualPath 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "FillRule" + values: { + "OddEvenFill": 0, + "WindingFill": 1 + } + } + Enum { + name: "JoinStyle" + values: { + "MiterJoin": 0, + "BevelJoin": 64, + "RoundJoin": 128 + } + } + Enum { + name: "CapStyle" + values: { + "FlatCap": 0, + "SquareCap": 16, + "RoundCap": 32 + } + } + Enum { + name: "StrokeStyle" + values: { + "SolidLine": 1, + "DashLine": 2 + } + } + Property { name: "path"; type: "QQuickPath"; isPointer: true } + Property { name: "strokeColor"; type: "QColor" } + Property { name: "strokeWidth"; type: "double" } + Property { name: "fillColor"; type: "QColor" } + Property { name: "fillRule"; type: "FillRule" } + Property { name: "joinStyle"; type: "JoinStyle" } + Property { name: "miterLimit"; type: "int" } + Property { name: "capStyle"; type: "CapStyle" } + Property { name: "strokeStyle"; type: "StrokeStyle" } + Property { name: "dashOffset"; type: "double" } + Property { name: "dashPattern"; type: "QVector" } + Property { name: "fillGradient"; type: "QQuickPathGradient"; isPointer: true } + Signal { name: "changed" } + } +} diff --git a/src/imports/pathitem/qmldir b/src/imports/pathitem/qmldir new file mode 100644 index 0000000000..277b8a199b --- /dev/null +++ b/src/imports/pathitem/qmldir @@ -0,0 +1,4 @@ +module Qt.labs.pathitem +plugin qmlpathitemplugin +classname QmlPathItemPlugin +typeinfo plugins.qmltypes diff --git a/src/imports/pathitem/qquicknvprfunctions.cpp b/src/imports/pathitem/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..40eb2bb932 --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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 "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); + q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); + q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); + q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); + q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); + q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/imports/pathitem/qquicknvprfunctions_p.h b/src/imports/pathitem/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..7900388305 --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions_p.h @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; + PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; + PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; + PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; + PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; + PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/pathitem/qquicknvprfunctions_p_p.h b/src/imports/pathitem/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/imports/pathitem/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/pathitem/qquickpathitem.cpp b/src/imports/pathitem/qquickpathitem.cpp new file mode 100644 index 0000000000..5255a55798 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem.cpp @@ -0,0 +1,2258 @@ +/**************************************************************************** +** +** 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 "qquickpathitem_p.h" +#include "qquickpathitem_p_p.h" +#include "qquickpathitemgenericrenderer_p.h" +#include "qquickpathitemnvprrenderer_p.h" +#include "qquickpathitemsoftwarerenderer_p.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() + : strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickVisualPath::OddEvenFill), + joinStyle(QQuickVisualPath::BevelJoin), + miterLimit(2), + capStyle(QQuickVisualPath::SquareCap), + strokeStyle(QQuickVisualPath::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QPainterPath QQuickPathItemPath::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickPathItemPath::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +/*! + \qmltype VisualPath + \instantiates QQuickVisualPath + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A PathItem contains one or more VisualPath elements. At least one + VisualPath is necessary in order to have a PathItem output anything + visible. A VisualPath in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the PathItem. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + VisualPath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: VisualPath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a PathItem, here is the output with a joinStyleIndex + of 2 (VisualPath.RoundJoin): + + \image visualpath-code-example.png + */ + +QQuickVisualPathPrivate::QQuickVisualPathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + +QQuickVisualPath::QQuickVisualPath(QObject *parent) + : QObject(*(new QQuickVisualPathPrivate), parent) +{ +} + +QQuickVisualPath::~QQuickVisualPath() +{ +} + +/*! + \qmlproperty Path QtQuick::VisualPath::path + + This property holds the Path object. + + \default + */ + +QQuickPath *QQuickVisualPath::path() const +{ + Q_D(const QQuickVisualPath); + return d->path; +} + +void QQuickVisualPath::setPath(QQuickPath *path) +{ + Q_D(QQuickVisualPath); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickVisualPath, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickVisualPath, SLOT(_q_pathChanged())); + + d->dirty |= QQuickVisualPathPrivate::DirtyPath; + emit pathChanged(); + emit changed(); +} + +void QQuickVisualPathPrivate::_q_pathChanged() +{ + Q_Q(QQuickVisualPath); + dirty |= DirtyPath; + emit q->changed(); +} + +/*! + \qmlproperty color QtQuick::VisualPath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + +QColor QQuickVisualPath::strokeColor() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeColor; +} + +void QQuickVisualPath::setStrokeColor(const QColor &color) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick::VisualPath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + +qreal QQuickVisualPath::strokeWidth() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeWidth; +} + +void QQuickVisualPath::setStrokeWidth(qreal w) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; + d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick::VisualPath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + +QColor QQuickVisualPath::fillColor() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillColor; +} + +void QQuickVisualPath::setFillColor(const QColor &color) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; + d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; + emit fillColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::fillRule + + This property holds the fill rule. The default value is + VisualPath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li VisualPath.OddEvenFill + \li VisualPath.WindingFill + \endlist + */ + +QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillRule; +} + +void QQuickVisualPath::setFillRule(FillRule fillRule) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; + d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; + emit fillRuleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is VisualPath.BevelJoin. + + \list + \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. + \li VisualPath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + +QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.joinStyle; +} + +void QQuickVisualPath::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit joinStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty int QtQuick::VisualPath::miterLimit + + When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + +int QQuickVisualPath::miterLimit() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.miterLimit; +} + +void QQuickVisualPath::setMiterLimit(int limit) +{ + Q_D(QQuickVisualPath); + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit miterLimitChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::capStyle + + This property defines how the end points of lines are drawn. The + default value is VisualPath.SquareCap. + + \list + \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. + \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li VisualPath.RoundCap - A rounded line end. + \endlist + */ + +QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.capStyle; +} + +void QQuickVisualPath::setCapStyle(CapStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyStyle; + emit capStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick::VisualPath::strokeStyle + + This property defines the style of stroking. The default value is + VisualPath.SolidLine. + + \list + \li VisualPath.SolidLine - A plain line. + \li VisualPath.DashLine - Dashes separated by a few pixels. + \endlist + */ + +QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.strokeStyle; +} + +void QQuickVisualPath::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickVisualPath); + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit strokeStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real QtQuick::VisualPath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + +qreal QQuickVisualPath::dashOffset() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.dashOffset; +} + +void QQuickVisualPath::setDashOffset(qreal offset) +{ + Q_D(QQuickVisualPath); + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit dashOffsetChanged(); + emit changed(); + } +} + +/*! + \qmlproperty list QtQuick::VisualPath::dashPattern + + This property defines the dash pattern when VisualPath.strokeStyle is set + to VisualPath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth + pixels followed by a space of 2 * VisualPath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + +QVector QQuickVisualPath::dashPattern() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.dashPattern; +} + +void QQuickVisualPath::setDashPattern(const QVector &array) +{ + Q_D(QQuickVisualPath); + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; + d->dirty |= QQuickVisualPathPrivate::DirtyDash; + emit dashPatternChanged(); + emit changed(); + } +} + +/*! + \qmlproperty PathGradient QtQuick::VisualPath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of VisuaLPath.fillColor. + + When set, VisualPath.fillColor is ignored and filling is done using one of + the PathGradient subtypes. + */ + +QQuickPathGradient *QQuickVisualPath::fillGradient() const +{ + Q_D(const QQuickVisualPath); + return d->sfp.fillGradient; +} + +void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) +{ + Q_D(QQuickVisualPath); + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), + this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; + emit changed(); + } +} + +void QQuickVisualPathPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickVisualPath); + dirty |= DirtyFillGradient; + emit q->changed(); +} + +void QQuickVisualPath::resetFillGradient() +{ + setFillGradient(nullptr); +} + +/*! + \qmltype PathItem + \instantiates QQuickPathItem + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path + \since 5.10 + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. Therefore + PathItem is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + The types for specifying path elements are shared between \l PathView and + PathItem. However, not all PathItem implementations support all path + element types, while some may not make sense for PathView. PathItem's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + PathItem { + width: 200 + height: 150 + anchors.centerIn: parent + VisualPath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: PathLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + PathGradientStop { position: 0; color: "blue" } + PathGradientStop { position: 0.2; color: "green" } + PathGradientStop { position: 0.4; color: "red" } + PathGradientStop { position: 0.6; color: "yellow" } + PathGradientStop { position: 1; color: "cyan" } + } + strokeStyle: VisualPath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png + + \note It is important to be aware of performance implications, in + particular when the application is running on the generic PathItem + implementation due to not having support for accelerated path rendering. + The geometry generation happens entirely on the CPU in this case, and this + is potentially expensive. Changing the set of path elements, changing the + properties of these elements, or changing certain properties of the + PathItem itself all lead to retriangulation on every change. Therefore, + applying animation to such properties can heavily affect performance on + less powerful systems. If animating properties other than stroke and fill + colors is a must, it is recommended to target systems providing + \c{GL_NV_path_rendering} where the cost of path property changes is much + smaller. + + The following list summarizes the available PathItem rendering approaches: + + \list + + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + PathItem.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickPathItemPrivate::QQuickPathItemPrivate() + : componentComplete(true), + vpChanged(false), + rendererType(QQuickPathItem::UnknownRenderer), + async(false), + status(QQuickPathItem::Null), + renderer(nullptr), + enableVendorExts(true) +{ +} + +QQuickPathItemPrivate::~QQuickPathItemPrivate() +{ + delete renderer; +} + +void QQuickPathItemPrivate::_q_visualPathChanged() +{ + Q_Q(QQuickPathItem); + vpChanged = true; + q->polish(); +} + +void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) +{ + Q_Q(QQuickPathItem); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + +QQuickPathItem::QQuickPathItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPathItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickPathItem::~QQuickPathItem() +{ +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::rendererType + + This property determines which path rendering backend is active. + + \list + + \li PathItem.UnknownRenderer - The renderer is unknown. + + \li PathItem.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + +QQuickPathItem::RendererType QQuickPathItem::rendererType() const +{ + Q_D(const QQuickPathItem); + return d->rendererType; +} + +/*! + \qmlproperty bool QtQuick::PathItem::asynchronous + + When PathItem.rendererType is PathItem.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the PathItem. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a PathItem visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + +bool QQuickPathItem::asynchronous() const +{ + Q_D(const QQuickPathItem); + return d->async; +} + +void QQuickPathItem::setAsynchronous(bool async) +{ + Q_D(QQuickPathItem); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_visualPathChanged(); + } +} + +/*! + \qmlproperty bool QtQuick::PathItem::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickPathItem::enableVendorExtensions() const +{ + Q_D(const QQuickPathItem); + return d->enableVendorExts; +} + +void QQuickPathItem::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickPathItem); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick::PathItem::status + + This property determines the status of the PathItem and is relevant when + PathItem.asynchronous is set to \c true. + + \list + + \li PathItem.Null - Not yet initialized. + + \li PathItem.Ready - The PathItem has finished processing. + + \li PathItem.Processing - The path is being processed. + + \endlist + */ + +QQuickPathItem::Status QQuickPathItem::status() const +{ + Q_D(const QQuickPathItem); + return d->status; +} + +static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->qmlData.vp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + d->qmlData.vp.append(obj); + + if (d->componentComplete) { + QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + d->_q_visualPathChanged(); + } +} + +static int vpe_count(QQmlListProperty *property) +{ + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); + return d->qmlData.vp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickPathItem *item = static_cast(property->object); + QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); + + for (QQuickVisualPath *p : d->qmlData.vp) + QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); + + d->qmlData.vp.clear(); + + if (d->componentComplete) + d->_q_visualPathChanged(); +} + +/*! + \qmlproperty list QtQuick::PathItem::elements + + This property holds the VisualPath objects that define the contents of the + PathItem. + + \default + */ + +QQmlListProperty QQuickPathItem::elements() +{ + return QQmlListProperty(this, + nullptr, + vpe_append, + vpe_count, + vpe_at, + vpe_clear); +} + +void QQuickPathItem::classBegin() +{ + Q_D(QQuickPathItem); + d->componentComplete = false; +} + +void QQuickPathItem::componentComplete() +{ + Q_D(QQuickPathItem); + d->componentComplete = true; + + for (QQuickVisualPath *p : d->qmlData.vp) + connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); + + d->_q_visualPathChanged(); +} + +void QQuickPathItem::updatePolish() +{ + Q_D(QQuickPathItem); + + if (!d->vpChanged) + return; + + d->vpChanged = false; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickPathItem); + + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + d->_q_visualPathChanged(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickPathItem); + if (d->renderer) { + if (!node) + node = d->createNode(); + d->renderer->updateNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickPathItemPrivate::createRenderer() +{ + Q_Q(QQuickPathItem); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { + rendererType = QQuickPathItem::NvprRenderer; + renderer = new QQuickPathItemNvprRenderer; + } else { + rendererType = QQuickPathItem::GeometryRenderer; + renderer = new QQuickPathItemGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickPathItem::SoftwareRenderer; + renderer = new QQuickPathItemSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickPathItemPrivate::createNode() +{ + Q_Q(QQuickPathItem); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { + node = new QQuickPathItemNvprRenderNode; + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickPathItemGenericNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickPathItemSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +static void q_asyncPathItemReady(void *data) +{ + QQuickPathItemPrivate *self = static_cast(data); + self->setStatus(QQuickPathItem::Ready); +} + +void QQuickPathItemPrivate::sync() +{ + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickPathItem::Processing); + renderer->setAsyncCallback(q_asyncPathItemReady, this); + } + + if (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // VisualPath and Path. + const int count = qmlData.vp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickVisualPath *p = qmlData.vp[i]; + int &dirty(QQuickVisualPathPrivate::get(p)->dirty); + + if (dirty & QQuickVisualPathPrivate::DirtyPath) + renderer->setPath(i, p->path()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickVisualPathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickVisualPathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickVisualPathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickVisualPathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; + } + + renderer->endSync(useAsync); + } else { + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } + + renderer->endSync(useAsync); + } + + if (!useAsync) + setStatus(QQuickPathItem::Ready); +} + +// ***** gradient support ***** + +/*! + \qmltype PathGradientStop + \instantiates QQuickPathGradientStop + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + +QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +/*! + \qmlproperty real QtQuick::PathGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + +qreal QQuickPathGradientStop::position() const +{ + return m_position; +} + +void QQuickPathGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmlproperty real QtQuick::PathGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + +QColor QQuickPathGradientStop::color() const +{ + return m_color; +} + +void QQuickPathGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickPathGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmltype PathGradient + \instantiates QQuickPathGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of PathItem fill gradients + \since 5.10 + + This is an abstract base class for gradients like PathLinearGradient and + cannot be created directly. + */ + +QQuickPathGradient::QQuickPathGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +int QQuickPathGradient::countStops(QQmlListProperty *list) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + +void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickPathGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); + return; + } + QQuickPathGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +/*! + \qmlproperty list QtQuick::PathGradient::stops + \default + + The list of PathGradientStop objects defining the colors at given positions + in the gradient. + */ + +QQmlListProperty QQuickPathGradient::stops() +{ + return QQmlListProperty(this, nullptr, + &QQuickPathGradient::appendStop, + &QQuickPathGradient::countStops, + &QQuickPathGradient::atStop, + nullptr); +} + +QGradientStops QQuickPathGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickPathGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +/*! + \qmlproperty enumeration QtQuick::PathGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is PathGradient.PadSpread. + + \list + \li PathGradient.PadSpread - The area is filled with the closest stop color. + \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + +QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const +{ + return m_spread; +} + +void QQuickPathGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +/*! + \qmltype PathLinearGradient + \instantiates QQuickPathLinearGradient + \inqmlmodule QtQuick + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits PathGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + +QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) + : QQuickPathGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick::PathLinearGradient::x1 + \qmlproperty real QtQuick::PathLinearGradient::y1 + \qmlproperty real QtQuick::PathLinearGradient::x2 + \qmlproperty real QtQuick::PathLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + +qreal QQuickPathLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickPathLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickPathLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickPathLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickPathLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickPathLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickPathItemGradientCacheWrapper +{ +public: + QQuickPathItemGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() +{ + static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickPathItemGradientCache::~QQuickPathItemGradientCache() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickPathGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickPathGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickPathGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** + +class QQuickPathItemJSEngineData : public QV8Engine::Deletable +{ +public: + QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); + + QV4::PersistentValue pathProto; + QV4::PersistentValue strokeFillParamsProto; +}; + +V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) + +namespace QV4 { +namespace Heap { + +struct QQuickPathItemJSPathPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSPath : Object { + void init() { Object::init(); } + QQuickPathItemPathObject *obj; +}; + +struct QQuickPathItemJSStrokeFillParamsPrototype : Object { + void init() { Object::init(); } +}; + +struct QQuickPathItemJSStrokeFillParams : Object { + void init() { Object::init(); } + QQuickPathItemStrokeFillParamsObject *obj; +}; + +} // namespace Heap +} // namespace QV4 + +struct QQuickPathItemJSPathPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); + o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); + o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); + o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); + o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); + +struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) +public: + static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) + { + QV4::Scope scope(engine); + auto obj = engine->memoryManager->allocObject(); + QV4::Scoped o(scope, obj); + + o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + + return o->d(); + } + + static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); + +struct QQuickPathItemJSPath : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); + +struct QQuickPathItemJSStrokeFillParams : public QV4::Object +{ + V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) + + static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +}; + +DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); + +void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::MoveTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 2) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::LineTo); + p->path.coords.append(callData->args[0].toNumber()); + p->path.coords.append(callData->args[1].toNumber()); + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 4) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::QuadTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // cx + p->path.coords.append(v[1].toNumber()); // cy + p->path.coords.append(v[2].toNumber()); // x + p->path.coords.append(v[3].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 6) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::CubicTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // c1x + p->path.coords.append(v[1].toNumber()); // c1y + p->path.coords.append(v[2].toNumber()); // c2x + p->path.coords.append(v[3].toNumber()); // c2y + p->path.coords.append(v[4].toNumber()); // x + p->path.coords.append(v[5].toNumber()); // y + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + if (callData->argc >= 7) { + QQuickPathItemPathObject *p = r->d()->obj; + p->path.cmd.append(QQuickPathItemPath::ArcTo); + const QV4::Value *v = callData->args; + p->path.coords.append(v[0].toNumber()); // radiusX + p->path.coords.append(v[1].toNumber()); // radiusY + p->path.coords.append(v[2].toNumber()); // xAxisRotation + p->path.coords.append(v[3].toNumber()); // x + p->path.coords.append(v[4].toNumber()); // y + p->path.coords.append(v[5].toNumber()); // sweepFlag + p->path.coords.append(v[6].toNumber()); // largeArc + } + + scope.result = callData->thisObject.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + r->d()->obj->clear(); + + scope.result = callData->thisObject.asReturnedValue(); +} + +Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp + +static inline QString qt_color_string(const QColor &color) +{ + if (color.alpha() == 255) + return color.name(); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.strokeColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.strokeWidth = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isString()) + r->d()->obj->sfp.fillColor = qt_color_from_string(value); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); +} + +void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.miterLimit = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); +} + +void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isInt32()) + r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + r->d()->obj->sfp.dashOffset = value->toNumber(); + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + a->arrayReserve(p->sfp.dashPattern.count()); + QV4::ScopedValue v(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) + a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); + a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); + + scope.result = a.asReturnedValue(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + if (value->isObject()) { + QV4::Scoped ao(scope, value); + if (!!ao) { + QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; + p->sfp.dashPattern.resize(ao->getLength()); + QV4::ScopedValue val(scope); + for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { + val = ao->getIndexed(i); + p->sfp.dashPattern[i] = val->toNumber(); + } + } + } + + scope.result = QV4::Encode::undefined(); +} + +void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + scope.result = r->d()->obj->v4fillGradient.value(); +} + +void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +{ + QV4::Scoped r(scope, callData->thisObject.as()); + + QV4::ScopedValue value(scope, callData->argument(0)); + QV4::Scoped qobjectWrapper(scope, value); + if (!!qobjectWrapper) { + if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { + r->d()->obj->v4fillGradient.set(scope.engine, value); + r->d()->obj->sfp.fillGradient = grad; + } + } else { + r->d()->obj->v4fillGradient.set(scope.engine, nullptr); + r->d()->obj->sfp.fillGradient = nullptr; + } + + scope.result = QV4::Encode::undefined(); +} + +QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) +{ + QV4::Scope scope(v4); + + QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); + pathProto = proto; + + proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); + + proto->defineAccessorProperty(QStringLiteral("strokeColor"), + QQuickPathItemJSStrokeFillParams::method_get_strokeColor, + QQuickPathItemJSStrokeFillParams::method_set_strokeColor); + proto->defineAccessorProperty(QStringLiteral("strokeWidth"), + QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, + QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); + proto->defineAccessorProperty(QStringLiteral("fillColor"), + QQuickPathItemJSStrokeFillParams::method_get_fillColor, + QQuickPathItemJSStrokeFillParams::method_set_fillColor); + proto->defineAccessorProperty(QStringLiteral("fillRule"), + QQuickPathItemJSStrokeFillParams::method_get_fillRule, + QQuickPathItemJSStrokeFillParams::method_set_fillRule); + proto->defineAccessorProperty(QStringLiteral("joinStyle"), + QQuickPathItemJSStrokeFillParams::method_get_joinStyle, + QQuickPathItemJSStrokeFillParams::method_set_joinStyle); + proto->defineAccessorProperty(QStringLiteral("miterLimit"), + QQuickPathItemJSStrokeFillParams::method_get_miterLimit, + QQuickPathItemJSStrokeFillParams::method_set_miterLimit); + proto->defineAccessorProperty(QStringLiteral("capStyle"), + QQuickPathItemJSStrokeFillParams::method_get_capStyle, + QQuickPathItemJSStrokeFillParams::method_set_capStyle); + proto->defineAccessorProperty(QStringLiteral("strokeStyle"), + QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, + QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); + proto->defineAccessorProperty(QStringLiteral("dashOffset"), + QQuickPathItemJSStrokeFillParams::method_get_dashOffset, + QQuickPathItemJSStrokeFillParams::method_set_dashOffset); + proto->defineAccessorProperty(QStringLiteral("dashPattern"), + QQuickPathItemJSStrokeFillParams::method_get_dashPattern, + QQuickPathItemJSStrokeFillParams::method_set_dashPattern); + proto->defineAccessorProperty(QStringLiteral("fillGradient"), + QQuickPathItemJSStrokeFillParams::method_get_fillGradient, + QQuickPathItemJSStrokeFillParams::method_set_fillGradient); + + strokeFillParamsProto = proto; +} + +void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->pathProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +/*! + \qmltype JSPath + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes a path via a JavaScript API + */ + +/*! + \qmlmethod void QtQuick::JSPath::moveTo(x, y) + + Moves the path's position to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::lineTo(x, y) + + Defines a straight line to the absolute position specified by (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) + + Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an + end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) + + Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and + (\a c2x, \a c2y), and an end point of (\a x, \a y). + */ + +/*! + \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) + + Defines an elliptical arc, following the elliptical arc command in SVG. See + \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the + SVG path specification} for details on the parameters. + */ + +/*! + \qmlmethod void QtQuick::JSPath::clear() + + Clears the path object removing all path elements. This is more lightweight + than creating a new JSPath object. + */ + +void QQuickPathItemPathObject::clear() +{ + path = QQuickPathItemPath(); +} + +/*! + \qmltype StrokeFillParams + \inqmlmodule QtQuick + \ingroup qtquick-path + \brief Describes stroke and fill parameters via a JavaScript API + + The properties of StrokeFillParams objects correspond 1:1 to VisualPath + properties. The possible values for enumerations are the same as well, for + example: + + \code + sfp.strokeStyle = VisualPath.DashLine; + sfp.capStyle = VisualPath.RoundCap; + \endcode + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::strokeColor + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::strokeWidth + */ + +/*! + \qmlproperty color QtQuick::StrokeFillParams::fillColor + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle + */ + +/*! + \qmlproperty int QtQuick::StrokeFillParams::miterLimit + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle + */ + +/*! + \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle + */ + +/*! + \qmlproperty real QtQuick::StrokeFillParams::dashOffset + */ + +/*! + \qmlproperty list QtQuick::StrokeFillParams::dashPattern + + The dash pattern can be specified using JavaScript arrays. + + \code + sfp.dashPattern = [ 4, 2 ]; + \endcode + */ + +/*! + \qmlproperty object QtQuick::StrokeFillParams::fillGradient + + Sets the fill gradient. The default value is null. Gradients cannot be + created from JavaScript. Instead, reference a PathLinearGradient or other + item by id. + + \code + PathLinearGradient { id: grad; ... } + ... + sfp.fillGradient = grad; + \endcode + */ + +/*! + \qmlmethod void QtQuick::StrokeFillParams::clear() + + Resets all values to their defaults. This is more lightweight than creating + a new StrokeFillParams object. + */ + +void QQuickPathItemStrokeFillParamsObject::clear() +{ + sfp = QQuickPathItemStrokeFillParams(); + if (!v4fillGradient.isNullOrUndefined()) + v4fillGradient.set(v4fillGradient.engine(), nullptr); +} + +void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) +{ + QQuickPathItemJSEngineData *ed = engineData(engine); + QV4::Scope scope(engine); + QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); + QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); + wrapper->setPrototype(p); + wrapper->d()->obj = this; + m_v4value = wrapper; +} + +/*! + \qmlmethod JSPath QtQuick::PathItem::newPath() + + Creates and returns a new object that describes a path and offers a + JavaScript API. Paired with a stroke-fill parameter object it is + equivalent to a VisualPath. + + The following two snippets are equivalent when it comes to the end result: + + \code + var p = pathItem.newPath(); + var sfp = pathItem.newStrokeFillParams(); + sfp.fillColor = "white"; + sfp.strokeColor = "black"; + sfp.strokeWidth = 0.172; + p.moveTo(-122.304, 84.285); + p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); + pathItem.appendVisualPath(p, sfp); + \endcode + + \code + PathItem { + VisualPath { + fillColor: "white" + strokeColor: "black" + strokeWidth: 0.172 + Path { + startX: -122.304; startY: 84.285 + PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } + } + } + } + \endcode + + The latter offers a full declarative API, with the possibility to binding + to and animating properties, while the former uses less resources due to + greatly reducing the number of QObject instances created. +*/ + +void QQuickPathItem::newPath(QQmlV4Function *args) +{ + QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +/*! + \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() + + Creates and returns a new object that describes stroke and fill parameters + and offers a JavaScript API. Paired with a path object it is equivalent to + a VisualPath. + */ + +void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) +{ + QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); + obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + args->setReturnValue(obj->v4value()); +} + +/*! + \qmlmethod void QtQuick::PathItem::clearVisualPaths() + + Clears the list of visual paths. + + \note This applies only to path and stroke-fill parameter objects registered + via appendVisualPaths(). + */ + +void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->jsData.paths.clear(); + d->jsData.sfp.clear(); +} + +/*! + \qmlmethod void QtQuick::PathItem::commitVisualPaths() + + Updates the PathItem. + + In order to avoid rendering a half-prepared PathItem, calling + PathItem.appendVisualPath() does not trigger any processing. Instead, + applications must call this function when all path and stroke-fill + parameter objects are registered. + */ + +void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) +{ + Q_UNUSED(args); + Q_D(QQuickPathItem); + d->_q_visualPathChanged(); +} + +/*! + \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) + + Adds the visual path compoes of \a path and \a strokeFillParams into the + PathItem. + + \note The declarative and imprative (JavaScript) APIs of PathItem use + independent data structures. Calling this function has no effect on the + PathItem.elements property and vice versa. Once this function is called, + the PathItem will only consider the data registered via this function and + will ignore the declarative elements property. + */ + +void QQuickPathItem::appendVisualPath(QQmlV4Function *args) +{ + if (args->length() < 2) + return; + + Q_D(QQuickPathItem); + QV4::Scope scope(args->v4engine()); + QV4::Scoped jsp(scope, (*args)[0]); + QV4::Scoped jssfp(scope, (*args)[1]); + + const QQuickPathItemPath &path(jsp->d()->obj->path); + const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); + + d->jsData.paths.append(path); + d->jsData.sfp.append(sfp); +} + +QT_END_NAMESPACE + +#include "moc_qquickpathitem_p.cpp" diff --git a/src/imports/pathitem/qquickpathitem_p.h b/src/imports/pathitem/qquickpathitem_p.h new file mode 100644 index 0000000000..c7c56fd5d8 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEM_P_H +#define QQUICKPATHITEM_P_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 "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickVisualPathPrivate; +class QQuickPathItemPrivate; + +class QQuickPathGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickPathGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class QQuickPathGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickPathGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class QQuickPathLinearGradient : public QQuickPathGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickPathLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class QQuickVisualPath : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) + Q_CLASSINFO("DefaultProperty", "path") + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + QQuickVisualPath(QObject *parent = nullptr); + ~QQuickVisualPath(); + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickPathGradient *fillGradient() const; + void setFillGradient(QQuickPathGradient *gradient); + void resetFillGradient(); + +Q_SIGNALS: + void changed(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickVisualPath) + Q_DECLARE_PRIVATE(QQuickVisualPath) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +class QQuickPathItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + + QQuickPathItem(QQuickItem *parent = nullptr); + ~QQuickPathItem(); + + RendererType rendererType() const; + + bool asynchronous() const; + void setAsynchronous(bool async); + + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + + Status status() const; + + QQmlListProperty elements(); + + Q_INVOKABLE void newPath(QQmlV4Function *args); + Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); + Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); + Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + void componentComplete() override; + void classBegin() override; + +Q_SIGNALS: + void rendererChanged(); + void asynchronousChanged(); + void enableVendorExtensionsChanged(); + void statusChanged(); + +private: + Q_DISABLE_COPY(QQuickPathItem) + Q_DECLARE_PRIVATE(QQuickPathItem) + Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPathItem) + +#endif // QQUICKPATHITEM_P_H diff --git a/src/imports/pathitem/qquickpathitem_p_p.h b/src/imports/pathitem/qquickpathitem_p_p.h new file mode 100644 index 0000000000..6dde314a30 --- /dev/null +++ b/src/imports/pathitem/qquickpathitem_p_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEM_P_P_H +#define QQUICKPATHITEM_P_P_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 "qquickpathitem_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +struct QQuickPathItemPath +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickPathItemStrokeFillParams +{ + QQuickPathItemStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickVisualPath::FillRule fillRule; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickPathGradient *fillGradient; +}; + +class QQuickAbstractPathRenderer +{ +public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API + virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; + // - stroke/fill parameters + virtual void setStrokeColor(int index, const QColor &color) = 0; + virtual void setStrokeWidth(int index, qreal w) = 0; + virtual void setFillColor(int index, const QColor &color) = 0; + virtual void setFillRule(int index, QQuickVisualPath::FillRule fillRule) = 0; + virtual void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) = 0; + virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) = 0; + virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; + + // Render thread, with gui blocked + virtual void updateNode() = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + +class QQuickVisualPathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickVisualPath) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickVisualPathPrivate(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } + + QQuickPath *path; + int dirty; + QQuickPathItemStrokeFillParams sfp; +}; + +class QQuickPathItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathItem) + +public: + QQuickPathItemPrivate(); + ~QQuickPathItemPrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_visualPathChanged(); + void setStatus(QQuickPathItem::Status newStatus); + + static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } + + bool componentComplete; + bool vpChanged; + QQuickPathItem::RendererType rendererType; + bool async; + QQuickPathItem::Status status; + QQuickAbstractPathRenderer *renderer; + + struct { + QVector vp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; + + bool enableVendorExts; +}; + +class QQuickPathItemPathObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemPath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickPathItemStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickPathItemStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickPathGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickPathItemGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickPathItemGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp new file mode 100644 index 0000000000..4e8fe55df2 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** 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 "qquickpathitemgenericrenderer_p.h" +#include +#include +#include + +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +static const qreal TRI_SCALE = 1; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickPathItemGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickPathItemGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), + m_window(window), + m_material(nullptr) +{ + setGeometry(m_geometry); + activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif +} + +QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() +{ + delete m_geometry; +} + +void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + +QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() +{ + for (VisualPathData &d : m_vp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + +// sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + +void QQuickPathItemGenericRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (VisualPathData &d : m_vp) + d.syncDirty = 0; +} + +void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathData &d(m_vp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathData &d(m_vp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathData &d(m_vp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyFillGeom; +} + +void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + VisualPathData &d(m_vp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickVisualPath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + } + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) +{ + VisualPathData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.syncDirty |= DirtyFillGradient; +} + +void QQuickPathItemFillRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); + emit done(this); +} + +void QQuickPathItemStrokeRunnable::run() +{ + QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + +void QQuickPathItemGenericRenderer::endSync(bool async) +{ + bool didKickOffAsync = false; + + for (int i = 0; i < m_vp.count(); ++i) { + VisualPathData &d(m_vp[i]); + if (!d.syncDirty) + continue; + + m_accDirty |= d.syncDirty; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to updateNode() + // on the render thread (with the gui thread blocked). For our purposes + // here syncDirty is still required since geometry regeneration must only + // happen when there was an actual change in this particular sync round. + d.effectiveDirty |= d.syncDirty; + + if (d.path.isEmpty()) { + d.fillVertices.clear(); + d.fillIndices.clear(); + d.strokeVertices.clear(); + continue; + } + + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; + const int idealCount = QThread::idealThreadCount(); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); + if (async) { + QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); + // Unlikely in practice but in theory m_vp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.indexType = r->indexType; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); + } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { + if (!r->orphaned && i < m_vp.count()) { + VisualPathData &d(m_vp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } + } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() +{ + for (const VisualPathData &d : qAsConst(m_vp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; + m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); + + size_t indexByteSize; + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); + } else { + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); + } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); +} + +void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); + const qreal inverseScale = 1.0 / TRI_SCALE; + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); + } else { + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); + } + + if (!stroker.vertexCount()) { + strokeVertices->clear(); + return; + } + + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); +} + +void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) +{ + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; + } +} + +// on the render thread with gui blocked +void QQuickPathItemGenericRenderer::updateNode() +{ + if (!m_rootNode || !m_accDirty) + return; + +// [ m_rootNode ] +// / / / +// #0 [ fill ] [ stroke ] [ next ] +// / / | +// #1 [ fill ] [ stroke ] [ next ] +// / / | +// #2 [ fill ] [ stroke ] [ next ] +// ... +// ... + + QQuickPathItemGenericNode **nodePtr = &m_rootNode; + QQuickPathItemGenericNode *prevNode = nullptr; + + for (VisualPathData &d : m_vp) { + if (!*nodePtr) { + *nodePtr = new QQuickPathItemGenericNode; + prevNode->m_next = *nodePtr; + prevNode->appendChildNode(*nodePtr); + } + + QQuickPathItemGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; + continue; + } + + if (d.fillColor.a == 0) { + delete node->m_fillNode; + node->m_fillNode = nullptr; + } else if (!node->m_fillNode) { + node->m_fillNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); + if (node->m_strokeNode) + node->removeChildNode(node->m_strokeNode); + node->appendChildNode(node->m_fillNode); + if (node->m_strokeNode) + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyFillGeom; + } + + if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { + delete node->m_strokeNode; + node->m_strokeNode = nullptr; + } else if (!node->m_strokeNode) { + node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyStrokeGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; + } + + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } + + m_accDirty = 0; +} + +void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + +void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) +{ + if (!node->m_fillNode) + return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; + + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. + QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + + QSGGeometry *g = n->m_geometry; + if (d->fillVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + if (d->fillGradientActive) { + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); + if (d->effectiveDirty & DirtyFillGradient) { + // Gradients are implemented via a texture-based material. + n->markDirty(QSGNode::DirtyMaterial); + // stop here if only the gradient changed; no need to touch the geometry + if (!(d->effectiveDirty & DirtyFillGeom)) + return; + } + } else { + n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + + n->markDirty(QSGNode::DirtyGeometry); +} + +void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node) +{ + if (!node->m_strokeNode) + return; + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) + return; + + QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; + QSGGeometry *g = n->m_geometry; + if (d->strokeVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + n->markDirty(QSGNode::DirtyGeometry); + + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); + return; + } + + g->allocate(d->strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickPathItemGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickPathItemLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickPathItemLinearGradientShader::type; + +QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickPathItemLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickPathItemLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickPathItemGenericStrokeFillNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickPathItemLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickPathItemLinearGradientMaterial *m = static_cast(other); + + QQuickPathItemGenericStrokeFillNode *a = node(); + QQuickPathItemGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h new file mode 100644 index 0000000000..70a9e88d2f --- /dev/null +++ b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMGENERICRENDERER_P_H +#define QQUICKPATHITEMGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemGenericNode; +class QQuickPathItemGenericStrokeFillNode; +class QQuickPathItemFillRunnable; +class QQuickPathItemStrokeRunnable; + +class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty + }; + + QQuickPathItemGenericRenderer(QQuickItem *item) + : m_item(item), + m_api(QSGRendererInterface::Unknown), + m_rootNode(nullptr), + m_accDirty(0), + m_asyncCallback(nullptr) + { } + ~QQuickPathItemGenericRenderer(); + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } + + void updateNode() override; + + void setRootNode(QQuickPathItemGenericNode *node); + + struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize); + +private: + void maybeUpdateAsyncItem(); + + struct VisualPathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; + int syncDirty; + int effectiveDirty = 0; + QQuickPathItemFillRunnable *pendingFill = nullptr; + QQuickPathItemStrokeRunnable *pendingStroke = nullptr; + }; + + void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); + void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); + void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); + + QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; + QQuickPathItemGenericNode *m_rootNode; + QVector m_vp; + int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; +}; + +class QQuickPathItemFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QQuickPathItemGenericRenderer::Color4ub fillColor; + bool supportsElementIndexUint; + + // output + QQuickPathItemGenericRenderer::VertexContainerType fillVertices; + QQuickPathItemGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; + +Q_SIGNALS: + void done(QQuickPathItemFillRunnable *self); +}; + +class QQuickPathItemStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QPen pen; + QQuickPathItemGenericRenderer::Color4ub strokeColor; + QSize clipSize; + + // output + QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; + +Q_SIGNALS: + void done(QQuickPathItemStrokeRunnable *self); +}; + +class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode +{ +public: + QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); + ~QQuickPathItemGenericStrokeFillNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + + // shadow data for custom materials + QQuickPathItemGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry *m_geometry; + QQuickWindow *m_window; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickPathItemGenericRenderer; +}; + +class QQuickPathItemGenericNode : public QSGNode +{ +public: + QQuickPathItemGenericStrokeFillNode *m_fillNode = nullptr; + QQuickPathItemGenericStrokeFillNode *m_strokeNode = nullptr; + QQuickPathItemGenericNode *m_next = nullptr; +}; + +class QQuickPathItemGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericStrokeFillNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickPathItemLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickPathItemLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickPathItemLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the PathItem-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickPathItemLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickPathItemLinearGradientShader; + } + + QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickPathItemGenericStrokeFillNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp new file mode 100644 index 0000000000..f8504f9985 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** 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 "qquickpathitemnvprrenderer_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemNvprRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathGuiData &d(m_vp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathGuiData &d(m_vp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathGuiData &d(m_vp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + VisualPathGuiData &d(m_vp[index]); + d.dashActive = strokeStyle == QQuickVisualPath::DashLine; + d.dashOffset = dashOffset; + d.dashPattern = dashPattern; + d.dirty |= DirtyDash; + m_accDirty |= DirtyDash; +} + +void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient *gradient) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; +} + +void QQuickPathItemNvprRenderer::endSync(bool) +{ +} + +void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path) +{ + QDebugStateSaver saver(debug); + debug.space().noquote(); + if (!path.str.isEmpty()) { + debug << "Path with SVG string" << path.str; + return debug; + } + debug << "Path with" << path.cmd.count() << "commands"; + int ci = 0; + for (GLubyte cmd : path.cmd) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + QPointF startPos(path->startX(), path->startY()); + QPointF pos(startPos); + if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { + d->path.cmd.append(GL_MOVE_TO_NV); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + } + + for (QQuickPathElement *e : pp) { + if (QQuickPathMove *o = qobject_cast(e)) { + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; + } else if (QQuickPathLine *o = qobject_cast(e)) { + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathCubic *o = qobject_cast(e)) { + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&d->path.coord, o, pos); + appendControl2Coords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathArc *o = qobject_cast(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(o->xAxisRotation()); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathSvg *o = qobject_cast(e)) { + // PathSvg cannot be combined with other elements. But take at + // least startX and startY into account. + if (d->path.str.isEmpty()) + d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); + d->path.str.append(o->path().toUtf8()); + } else { + qWarning() << "PathItem/NVPR: unsupported Path element" << e; + } + } + + // For compatibility with QTriangulatingStroker. SVG and others would not + // implicitly close the path when end_pos == start_pos (start_pos being the + // last moveTo pos); that would still need an explicit 'z' or similar. We + // don't have an explicit close command, so just fake a close when the + // positions match. + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickPathItemPath::Command cmd : path.cmd) { + switch (cmd) { + case QQuickPathItemPath::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickPathItemPath::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickPathItemPath::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickPathItemPath::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickPathItemNvprRenderer::updateNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_accDirty) + return; + + const int count = m_vp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_vp.resize(count); + + for (int i = 0; i < count; ++i) { + VisualPathGuiData &src(m_vp[i]); + QQuickPathItemNvprRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); + + int dirty = src.dirty; + src.dirty = 0; + if (listChanged) + dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; + + // updateNode() can be called several times with different dirty + // states before render() gets invoked. So accumulate. + dst.dirty |= dirty; + + if (dirty & DirtyPath) + dst.source = src.path; + + if (dirty & DirtyStyle) { + dst.strokeWidth = src.strokeWidth; + dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); + dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); + switch (src.joinStyle) { + case QQuickVisualPath::MiterJoin: + dst.joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickVisualPath::BevelJoin: + dst.joinStyle = GL_BEVEL_NV; + break; + case QQuickVisualPath::RoundJoin: + dst.joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + dst.miterLimit = src.miterLimit; + switch (src.capStyle) { + case QQuickVisualPath::FlatCap: + dst.capStyle = GL_FLAT; + break; + case QQuickVisualPath::SquareCap: + dst.capStyle = GL_SQUARE_NV; + break; + case QQuickVisualPath::RoundCap: + dst.capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyFillRule) { + switch (src.fillRule) { + case QQuickVisualPath::OddEvenFill: + dst.fillRule = GL_INVERT; + break; + case QQuickVisualPath::WindingFill: + dst.fillRule = GL_COUNT_UP_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyDash) { + dst.dashOffset = src.dashOffset; + if (src.dashActive) { + dst.dashPattern.resize(src.dashPattern.count()); + // Multiply by strokeWidth because the PathItem API follows QPen + // meaning the input dash pattern here is in width units. + for (int i = 0; i < src.dashPattern.count(); ++i) + dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + } else { + dst.dashPattern.clear(); + } + } + + if (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +bool QQuickPathItemNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; + +QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemNvprRenderNode::releaseResources() +{ + for (VisualPathRenderData &d : m_vp) { + if (d.path) { + nvpr.deletePaths(d.path, 1); + d.path = 0; + } + if (d.fallbackFbo) { + delete d.fallbackFbo; + d.fallbackFbo = nullptr; + } + } + + m_fallbackBlitter.destroy(); +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) +{ + if (d->dirty & QQuickPathItemNvprRenderer::DirtyPath) { + if (!d->path) { + d->path = nvpr.genPaths(1); + Q_ASSERT(d->path != 0); + } + if (d->source.str.isEmpty()) { + nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), + d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); + } else { + nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); + } + } + + if (d->dirty & QQuickPathItemNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); + nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); + nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); + nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); + nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); + } + + if (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); + } + + if (d->dirty) + d->fallbackValid = false; +} + +void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (d->fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(d->fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); + } else { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + const int writeMask = 0xFF; + nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) +{ + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); + + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; + } + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) + return; + + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); + f->glDisable(GL_DEPTH_TEST); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); + QMatrix4x4 proj; + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(d); + + d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; +} + +void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + +void QQuickPathItemNvprRenderNode::render(const RenderState *state) +{ + f = QOpenGLContext::currentContext()->extraFunctions(); + + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + f->glUseProgram(0); + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + bool reloadMatrices = true; + + for (VisualPathRenderData &d : m_vp) { + updatePath(&d); + + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); + + if (hasFill && stencilClip) { + // Fall back to a texture when complex clipping is in use and we have + // to fill. Reconciling glStencilFillPath's and the scenegraph's clip + // stencil semantics has not succeeded so far... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); + renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); + } + + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + } + + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(&d); + } else { + if (!m_fallbackBlitter.isCreated()) + m_fallbackBlitter.create(); + f->glStencilFunc(GL_EQUAL, sv, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), mv, + inheritedOpacity()); + } + } + + // Stroke! + if (hasStroke) { + const int strokeStencilValue = 0x80; + const int writeMask = 0x80; + + setupStencilForCover(stencilClip, sv); + if (stencilClip) { + // for the stencil step (eff. read mask == 0xFF & ~writeMask) + nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); + // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. + // This assumes the clip stencil value is <= 127. + if (sv >= strokeStencilValue) + qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(&d, strokeStencilValue, writeMask); + } + + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + + d.dirty = 0; + } + + f->glBindProgramPipeline(0); +} + +QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +bool QQuickPathItemNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h new file mode 100644 index 0000000000..cfe1c7eab9 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMNVPRRENDERER_P_H +#define QQUICKPATHITEMNVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include "qquicknvprfunctions_p.h" +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickPathItemNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; + +class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + DirtyList = 0x20 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickPathItemNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + QByteArray str; + }; + +private: + struct VisualPathGuiData { + int dirty = 0; + NvprPath path; + qreal strokeWidth; + QColor strokeColor; + QColor fillColor; + QQuickVisualPath::JoinStyle joinStyle; + int miterLimit; + QQuickVisualPath::CapStyle capStyle; + QQuickVisualPath::FillRule fillRule; + bool dashActive; + qreal dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, VisualPathGuiData *d); + void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); + + QQuickPathItemNvprRenderNode *m_node = nullptr; + int m_accDirty = 0; + + QVector m_vp; +}; + +QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + +class QQuickPathItemNvprRenderNode : public QSGRenderNode +{ +public: + ~QQuickPathItemNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + + static bool isSupported(); + +private: + struct VisualPathRenderData { + GLuint path = 0; + int dirty = 0; + QQuickPathItemNvprRenderer::NvprPath source; + GLfloat strokeWidth; + QVector4D strokeColor; + QVector4D fillColor; + GLenum joinStyle; + GLint miterLimit; + GLenum capStyle; + GLenum fillRule; + GLfloat dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickPathItemGradientCache::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } + }; + + void updatePath(VisualPathRenderData *d); + void renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask); + void renderFill(VisualPathRenderData *d); + void renderOffscreenFill(VisualPathRenderData *d); + void setupStencilForCover(bool stencilClip, int sv); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; + + QVector m_vp; + + friend class QQuickPathItemNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp new file mode 100644 index 0000000000..b7aa93bf65 --- /dev/null +++ b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 "qquickpathitemsoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) +{ + if (m_vp.count() != totalCount) { + m_vp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) +{ + VisualPathGuiData &d(m_vp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) +{ + VisualPathGuiData &d(m_vp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) +{ + VisualPathGuiData &d(m_vp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) +{ + VisualPathGuiData &d(m_vp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + VisualPathGuiData &d(m_vp[index]); + switch (strokeStyle) { + case QQuickVisualPath::SolidLine: + d.pen.setStyle(Qt::SolidLine); + break; + case QQuickVisualPath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + break; + default: + break; + } + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) +{ + VisualPathGuiData &d(m_vp[index]); + if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickPathGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickPathGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickPathGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + d.brush = QBrush(painterGradient); + } else { + d.brush = QBrush(d.fillColor); + } + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickPathItemSoftwareRenderer::endSync(bool) +{ +} + +void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +void QQuickPathItemSoftwareRenderer::updateNode() +{ + if (!m_accDirty) + return; + + const int count = m_vp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_vp.resize(count); + + m_node->m_boundingRect = QRectF(); + + for (int i = 0; i < count; ++i) { + VisualPathGuiData &src(m_vp[i]); + QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); + + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } + + if (listChanged || (src.dirty & DirtyFillRule)) + dst.path.setFillRule(src.fillRule); + + if (listChanged || (src.dirty & DirtyPen)) { + dst.pen = src.pen; + dst.strokeWidth = src.strokeWidth; + } + + if (listChanged || (src.dirty & DirtyBrush)) + dst.brush = src.brush; + + src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) + : m_item(item) +{ +} + +QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickPathItemSoftwareRenderNode::releaseResources() +{ +} + +void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) +{ + if (m_vp.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + for (const VisualPathRenderData &d : qAsConst(m_vp)) { + p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); + p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); + p->drawPath(d.path); + } +} + +QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickPathItemSoftwareRenderNode::rect() const +{ + return m_boundingRect; +} + +QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h new file mode 100644 index 0000000000..e76590bdfe --- /dev/null +++ b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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 QQUICKPATHITEMSOFTWARERENDERER_P_H +#define QQUICKPATHITEMSOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathitem_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickPathItemSoftwareRenderNode; + +class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + DirtyList = 0x10 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickPathItemPath &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickPathGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickPathItemSoftwareRenderNode *node); + +private: + QQuickPathItemSoftwareRenderNode *m_node = nullptr; + int m_accDirty = 0; + struct VisualPathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector m_vp; +}; + +class QQuickPathItemSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); + ~QQuickPathItemSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickPathItem *m_item; + + struct VisualPathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector m_vp; + QRectF m_boundingRect; + + friend class QQuickPathItemSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 1a6f530bfa..715fc4b2c7 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -40,6 +40,7 @@ #include "qquickcontext2d_p.h" #include "qquickcontext2dcommandbuffer_p.h" #include "qquickcanvasitem_p.h" +#include #include #include #if QT_CONFIG(quick_shadereffect) @@ -136,7 +137,7 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); THROW_GENERIC_ERROR("Not a Context2D object"); #define qClamp(val, min, max) qMin(qMax(val, min), max) #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9)) -QColor qt_color_from_string(const QV4::Value &name) +Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name) { QByteArray str = name.toQString().toUtf8(); diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 511c6f18d8..0f8061b5ef 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -148,20 +148,9 @@ qtConfig(quick-listview) { qtConfig(quick-pathview) { HEADERS += \ $$PWD/qquickpathview_p.h \ - $$PWD/qquickpathview_p_p.h \ - $$PWD/qquickpathitem_p.h \ - $$PWD/qquickpathitem_p_p.h \ - $$PWD/qquickpathitemgenericrenderer_p.h \ - $$PWD/qquickpathitemsoftwarerenderer_p.h + $$PWD/qquickpathview_p_p.h SOURCES += \ - $$PWD/qquickpathview.cpp \ - $$PWD/qquickpathitem.cpp \ - $$PWD/qquickpathitemgenericrenderer.cpp \ - $$PWD/qquickpathitemsoftwarerenderer.cpp - qtConfig(opengl) { - HEADERS += $$PWD/qquickpathitemnvprrenderer_p.h - SOURCES += $$PWD/qquickpathitemnvprrenderer.cpp - } + $$PWD/qquickpathview.cpp } qtConfig(quick-positioners) { diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 9e692da442..e6321e9365 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -70,7 +70,6 @@ #if QT_CONFIG(quick_path) #include #include -#include "qquickpathitem_p.h" #endif #if QT_CONFIG(quick_positioners) #include "qquickpositioners_p.h" @@ -381,11 +380,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #if QT_CONFIG(quick_path) qmlRegisterType(uri, 2, 9, "PathArc"); qmlRegisterType(uri, 2, 9, "PathMove"); - qmlRegisterType(uri, 2, 9, "PathItem"); - qmlRegisterType(uri, 2, 9, "VisualPath"); - qmlRegisterType(uri, 2, 9, "PathGradientStop"); - qmlRegisterUncreatableType(uri, 2, 9, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); - qmlRegisterType(uri, 2, 9, "PathLinearGradient"); #endif qmlRegisterType(uri, 2, 9, "Text"); diff --git a/src/quick/items/qquickpathitem.cpp b/src/quick/items/qquickpathitem.cpp deleted file mode 100644 index fae16064e5..0000000000 --- a/src/quick/items/qquickpathitem.cpp +++ /dev/null @@ -1,2258 +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 "qquickpathitem_p.h" -#include "qquickpathitem_p_p.h" -#include "qquickpathitemgenericrenderer_p.h" -#include "qquickpathitemnvprrenderer_p.h" -#include "qquickpathitemsoftwarerenderer_p.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() - : strokeColor(Qt::white), - strokeWidth(1), - fillColor(Qt::white), - fillRule(QQuickVisualPath::OddEvenFill), - joinStyle(QQuickVisualPath::BevelJoin), - miterLimit(2), - capStyle(QQuickVisualPath::SquareCap), - strokeStyle(QQuickVisualPath::SolidLine), - dashOffset(0), - fillGradient(nullptr) -{ - dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space -} - -QPainterPath QQuickPathItemPath::toPainterPath() const -{ - QPainterPath p; - int coordIdx = 0; - for (int i = 0; i < cmd.count(); ++i) { - switch (cmd[i]) { - case QQuickPathItemPath::MoveTo: - p.moveTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - p.lineTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - p.quadTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3]); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - p.cubicTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3], - coords[coordIdx + 4], coords[coordIdx + 5]); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser - QQuickSvgParser::pathArc(p, - coords[coordIdx], coords[coordIdx + 1], // radius - coords[coordIdx + 2], // xAxisRotation - !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc - !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag - coords[coordIdx + 3], coords[coordIdx + 4], // end - p.currentPosition().x(), p.currentPosition().y()); - coordIdx += 7; - break; - default: - qWarning("Unknown JS path command: %d", cmd[i]); - break; - } - } - return p; -} - -/*! - \qmltype VisualPath - \instantiates QQuickVisualPath - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Describes a Path and associated properties for stroking and filling - \since 5.10 - - A PathItem contains one or more VisualPath elements. At least one - VisualPath is necessary in order to have a PathItem output anything - visible. A VisualPath in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. - - Any property changes in these data sets will be bubble up and change the - output of the PathItem. This means that it is simple and easy to change, or - even animate, the starting and ending position, control points, or any - stroke or fill parameters using the usual QML bindings and animation types - like NumberAnimation. - - In the following example the line join style changes automatically based on - the value of joinStyleIndex: - - \code - VisualPath { - strokeColor: "black" - strokeWidth: 16 - fillColor: "transparent" - capStyle: VisualPath.RoundCap - - property int joinStyleIndex: 0 - property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] - - joinStyle: styles[joinStyleIndex] - - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } - } - \endcode - - Once associated with a PathItem, here is the output with a joinStyleIndex - of 2 (VisualPath.RoundJoin): - - \image visualpath-code-example.png - */ - -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll) -{ -} - -QQuickVisualPath::QQuickVisualPath(QObject *parent) - : QObject(*(new QQuickVisualPathPrivate), parent) -{ -} - -QQuickVisualPath::~QQuickVisualPath() -{ -} - -/*! - \qmlproperty Path QtQuick::VisualPath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickVisualPath::path() const -{ - Q_D(const QQuickVisualPath); - return d->path; -} - -void QQuickVisualPath::setPath(QQuickPath *path) -{ - Q_D(QQuickVisualPath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickVisualPathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickVisualPathPrivate::_q_pathChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyPath; - emit q->changed(); -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeColor - - This property holds the stroking color. - - When set to \c transparent, no stroking occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::strokeColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeColor; -} - -void QQuickVisualPath::setStrokeColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeColor != color) { - d->sfp.strokeColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; - emit strokeColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeWidth - - This property holds the stroke width. - - When set to a negative value, no stroking occurs. - - The default value is 1. - */ - -qreal QQuickVisualPath::strokeWidth() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeWidth; -} - -void QQuickVisualPath::setStrokeWidth(qreal w) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeWidth != w) { - d->sfp.strokeWidth = w; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; - emit strokeWidthChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::fillColor - - This property holds the fill color. - - When set to \c transparent, no filling occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::fillColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillColor; -} - -void QQuickVisualPath::setFillColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillColor != color) { - d->sfp.fillColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; - emit fillColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::fillRule - - This property holds the fill rule. The default value is - VisualPath.OddEvenFill. For an example on fill rules, see - QPainterPath::setFillRule(). - - \list - \li VisualPath.OddEvenFill - \li VisualPath.WindingFill - \endlist - */ - -QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillRule; -} - -void QQuickVisualPath::setFillRule(FillRule fillRule) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillRule != fillRule) { - d->sfp.fillRule = fillRule; - d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; - emit fillRuleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::joinStyle - - This property defines how joins between two connected lines are drawn. The - default value is VisualPath.BevelJoin. - - \list - \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. - \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. - \li VisualPath.RoundJoin - A circular arc between the two lines is filled. - \endlist - */ - -QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.joinStyle; -} - -void QQuickVisualPath::setJoinStyle(JoinStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.joinStyle != style) { - d->sfp.joinStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit joinStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty int QtQuick::VisualPath::miterLimit - - When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property - specifies how far the miter join can extend from the join point. - - The default value is 2. - */ - -int QQuickVisualPath::miterLimit() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.miterLimit; -} - -void QQuickVisualPath::setMiterLimit(int limit) -{ - Q_D(QQuickVisualPath); - if (d->sfp.miterLimit != limit) { - d->sfp.miterLimit = limit; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit miterLimitChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::capStyle - - This property defines how the end points of lines are drawn. The - default value is VisualPath.SquareCap. - - \list - \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. - \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. - \li VisualPath.RoundCap - A rounded line end. - \endlist - */ - -QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.capStyle; -} - -void QQuickVisualPath::setCapStyle(CapStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.capStyle != style) { - d->sfp.capStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit capStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::strokeStyle - - This property defines the style of stroking. The default value is - VisualPath.SolidLine. - - \list - \li VisualPath.SolidLine - A plain line. - \li VisualPath.DashLine - Dashes separated by a few pixels. - \endlist - */ - -QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeStyle; -} - -void QQuickVisualPath::setStrokeStyle(StrokeStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeStyle != style) { - d->sfp.strokeStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit strokeStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty real QtQuick::VisualPath::dashOffset - - This property defines the starting point on the dash pattern, measured in - units used to specify the dash pattern. - - The default value is 0. - - \sa QPen::setDashOffset() - */ - -qreal QQuickVisualPath::dashOffset() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashOffset; -} - -void QQuickVisualPath::setDashOffset(qreal offset) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashOffset != offset) { - d->sfp.dashOffset = offset; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashOffsetChanged(); - emit changed(); - } -} - -/*! - \qmlproperty list QtQuick::VisualPath::dashPattern - - This property defines the dash pattern when VisualPath.strokeStyle is set - to VisualPath.DashLine. The pattern must be specified as an even number of - positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... - are the spaces. The pattern is specified in units of the pen's width. - - The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth - pixels followed by a space of 2 * VisualPath.strokeWidth pixels. - - \sa QPen::setDashPattern() - */ - -QVector QQuickVisualPath::dashPattern() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashPattern; -} - -void QQuickVisualPath::setDashPattern(const QVector &array) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashPattern != array) { - d->sfp.dashPattern = array; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashPatternChanged(); - emit changed(); - } -} - -/*! - \qmlproperty PathGradient QtQuick::VisualPath::fillGradient - - This property defines the fill gradient. By default no gradient is enabled - and the value is \c null. In this case the fill uses a solid color based on - the value of VisuaLPath.fillColor. - - When set, VisualPath.fillColor is ignored and filling is done using one of - the PathGradient subtypes. - */ - -QQuickPathGradient *QQuickVisualPath::fillGradient() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillGradient; -} - -void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillGradient != gradient) { - if (d->sfp.fillGradient) - qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->sfp.fillGradient = gradient; - if (d->sfp.fillGradient) - qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; - emit changed(); - } -} - -void QQuickVisualPathPrivate::_q_fillGradientChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyFillGradient; - emit q->changed(); -} - -void QQuickVisualPath::resetFillGradient() -{ - setFillGradient(nullptr); -} - -/*! - \qmltype PathItem - \instantiates QQuickPathItem - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Item - \brief Renders a path - \since 5.10 - - Renders a path either by generating geometry via QPainterPath and manual - triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}. - - This approach is different from rendering shapes via QQuickPaintedItem or - the 2D Canvas because the path never gets rasterized in software. Therefore - PathItem is suitable for creating shapes spreading over larger areas of the - screen, avoiding the performance penalty for texture uploads or framebuffer - blits. In addition, the declarative API allows manipulating, binding to, - and even animating the path element properties like starting and ending - position, the control points, etc. - - The types for specifying path elements are shared between \l PathView and - PathItem. However, not all PathItem implementations support all path - element types, while some may not make sense for PathView. PathItem's - currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, - PathArc, PathSvg. - - See \l Path for a detailed overview of the supported path elements. - - \code - PathItem { - width: 200 - height: 150 - anchors.centerIn: parent - VisualPath { - strokeWidth: 4 - strokeColor: "red" - fillGradient: PathLinearGradient { - x1: 20; y1: 20 - x2: 180; y2: 130 - PathGradientStop { position: 0; color: "blue" } - PathGradientStop { position: 0.2; color: "green" } - PathGradientStop { position: 0.4; color: "red" } - PathGradientStop { position: 0.6; color: "yellow" } - PathGradientStop { position: 1; color: "cyan" } - } - strokeStyle: VisualPath.DashLine - dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } - } - } - \endcode - - \image pathitem-code-example.png - - \note It is important to be aware of performance implications, in - particular when the application is running on the generic PathItem - implementation due to not having support for accelerated path rendering. - The geometry generation happens entirely on the CPU in this case, and this - is potentially expensive. Changing the set of path elements, changing the - properties of these elements, or changing certain properties of the - PathItem itself all lead to retriangulation on every change. Therefore, - applying animation to such properties can heavily affect performance on - less powerful systems. If animating properties other than stroke and fill - colors is a must, it is recommended to target systems providing - \c{GL_NV_path_rendering} where the cost of path property changes is much - smaller. - - The following list summarizes the available PathItem rendering approaches: - - \list - - \li When running with the default, OpenGL backend of Qt Quick, both the - generic, triangulation-based and the NVIDIA-specific - \c{GL_NV_path_rendering} methods are available. The choice is made at - runtime, depending on the graphics driver's capabilities. When this is not - desired, applications can force using the generic method by setting the - PathItem.enableVendorExtensions property to \c false. - - \li The \c software backend is fully supported. The path is rendered via - QPainter::strokePath() and QPainter::fillPath() in this case. - - \li The Direct 3D 12 backend is not currently supported. - - \li The OpenVG backend is not currently supported. - - \endlist - - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg -*/ - -QQuickPathItemPrivate::QQuickPathItemPrivate() - : componentComplete(true), - vpChanged(false), - rendererType(QQuickPathItem::UnknownRenderer), - async(false), - status(QQuickPathItem::Null), - renderer(nullptr), - enableVendorExts(true) -{ -} - -QQuickPathItemPrivate::~QQuickPathItemPrivate() -{ - delete renderer; -} - -void QQuickPathItemPrivate::_q_visualPathChanged() -{ - Q_Q(QQuickPathItem); - vpChanged = true; - q->polish(); -} - -void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) -{ - Q_Q(QQuickPathItem); - if (status != newStatus) { - status = newStatus; - emit q->statusChanged(); - } -} - -QQuickPathItem::QQuickPathItem(QQuickItem *parent) - : QQuickItem(*(new QQuickPathItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -QQuickPathItem::~QQuickPathItem() -{ -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::rendererType - - This property determines which path rendering backend is active. - - \list - - \li PathItem.UnknownRenderer - The renderer is unknown. - - \li PathItem.GeometryRenderer - The generic, driver independent solution - for OpenGL. Uses the same CPU-based triangulation approach as QPainter's - OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the - default, OpenGL Qt Quick scenegraph backend is in use. - - \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL - calls using the \c{GL_NV_path_rendering} extension. This is the default on - NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in - use. - - \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster - paint engine. This is the default, and only, option when the Qt Quick - scenegraph is running with the \c software backend. - - \endlist -*/ - -QQuickPathItem::RendererType QQuickPathItem::rendererType() const -{ - Q_D(const QQuickPathItem); - return d->rendererType; -} - -/*! - \qmlproperty bool QtQuick::PathItem::asynchronous - - When PathItem.rendererType is PathItem.GeometryRenderer, the input path is - triangulated on the CPU during the polishing phase of the PathItem. This is - potentially expensive. To offload this work to separate worker threads, set - this property to \c true. - - When enabled, making a PathItem visible will not wait for the content to - become available. Instead, the gui/main thread is not blocked and the - results of the path rendering are shown only when all the asynchronous work - has been finished. - - The default value is \c false. - */ - -bool QQuickPathItem::asynchronous() const -{ - Q_D(const QQuickPathItem); - return d->async; -} - -void QQuickPathItem::setAsynchronous(bool async) -{ - Q_D(QQuickPathItem); - if (d->async != async) { - d->async = async; - emit asynchronousChanged(); - if (d->componentComplete) - d->_q_visualPathChanged(); - } -} - -/*! - \qmlproperty bool QtQuick::PathItem::enableVendorExtensions - - This property controls the usage of non-standard OpenGL extensions like - GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform - behavior regardless of the graphics card and drivers, set this property to - \c false. - - The default value is \c true. - */ - -bool QQuickPathItem::enableVendorExtensions() const -{ - Q_D(const QQuickPathItem); - return d->enableVendorExts; -} - -void QQuickPathItem::setEnableVendorExtensions(bool enable) -{ - Q_D(QQuickPathItem); - if (d->enableVendorExts != enable) { - d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::status - - This property determines the status of the PathItem and is relevant when - PathItem.asynchronous is set to \c true. - - \list - - \li PathItem.Null - Not yet initialized. - - \li PathItem.Ready - The PathItem has finished processing. - - \li PathItem.Processing - The path is being processed. - - \endlist - */ - -QQuickPathItem::Status QQuickPathItem::status() const -{ - Q_D(const QQuickPathItem); - return d->status; -} - -static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->qmlData.vp.append(obj); - - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->_q_visualPathChanged(); - } -} - -static int vpe_count(QQmlListProperty *property) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.count(); -} - -static void vpe_clear(QQmlListProperty *property) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - - for (QQuickVisualPath *p : d->qmlData.vp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - - d->qmlData.vp.clear(); - - if (d->componentComplete) - d->_q_visualPathChanged(); -} - -/*! - \qmlproperty list QtQuick::PathItem::elements - - This property holds the VisualPath objects that define the contents of the - PathItem. - - \default - */ - -QQmlListProperty QQuickPathItem::elements() -{ - return QQmlListProperty(this, - nullptr, - vpe_append, - vpe_count, - vpe_at, - vpe_clear); -} - -void QQuickPathItem::classBegin() -{ - Q_D(QQuickPathItem); - d->componentComplete = false; -} - -void QQuickPathItem::componentComplete() -{ - Q_D(QQuickPathItem); - d->componentComplete = true; - - for (QQuickVisualPath *p : d->qmlData.vp) - connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); - - d->_q_visualPathChanged(); -} - -void QQuickPathItem::updatePolish() -{ - Q_D(QQuickPathItem); - - if (!d->vpChanged) - return; - - d->vpChanged = false; - - if (!d->renderer) { - d->createRenderer(); - if (!d->renderer) - return; - emit rendererChanged(); - } - - // endSync() is where expensive calculations may happen (or get kicked off - // on worker threads), depending on the backend. Therefore do this only - // when the item is visible. - if (isVisible()) - d->sync(); - - update(); -} - -void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) -{ - Q_D(QQuickPathItem); - - // sync may have been deferred; do it now if the item became visible - if (change == ItemVisibleHasChanged && data.boolValue) - d->_q_visualPathChanged(); - - QQuickItem::itemChange(change, data); -} - -QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) -{ - // Called on the render thread, with the gui thread blocked. We can now - // safely access gui thread data. - - Q_D(QQuickPathItem); - if (d->renderer) { - if (!node) - node = d->createNode(); - d->renderer->updateNode(); - } - return node; -} - -// the renderer object lives on the gui thread -void QQuickPathItemPrivate::createRenderer() -{ - Q_Q(QQuickPathItem); - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; - } else { - rendererType = QQuickPathItem::GeometryRenderer; - renderer = new QQuickPathItemGenericRenderer(q); - } - break; -#endif - case QSGRendererInterface::Software: - rendererType = QQuickPathItem::SoftwareRenderer; - renderer = new QQuickPathItemSoftwareRenderer; - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } -} - -// the node lives on the render thread -QSGNode *QQuickPathItemPrivate::createNode() -{ - Q_Q(QQuickPathItem); - QSGNode *node = nullptr; - if (!q->window()) - return node; - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return node; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode; - static_cast(renderer)->setNode( - static_cast(node)); - } else { - node = new QQuickPathItemGenericNode; - static_cast(renderer)->setRootNode( - static_cast(node)); - } - break; -#endif - case QSGRendererInterface::Software: - node = new QQuickPathItemSoftwareRenderNode(q); - static_cast(renderer)->setNode( - static_cast(node)); - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } - - return node; -} - -static void q_asyncPathItemReady(void *data) -{ - QQuickPathItemPrivate *self = static_cast(data); - self->setStatus(QQuickPathItem::Ready); -} - -void QQuickPathItemPrivate::sync() -{ - const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); - if (useAsync) { - setStatus(QQuickPathItem::Processing); - renderer->setAsyncCallback(q_asyncPathItemReady, this); - } - - if (!jsData.isValid()) { - // Standard route: The path and stroke/fill parameters are provided via - // VisualPath and Path. - const int count = qmlData.vp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickVisualPath *p = qmlData.vp[i]; - int &dirty(QQuickVisualPathPrivate::get(p)->dirty); - - if (dirty & QQuickVisualPathPrivate::DirtyPath) - renderer->setPath(i, p->path()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) - renderer->setStrokeColor(i, p->strokeColor()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) - renderer->setStrokeWidth(i, p->strokeWidth()); - if (dirty & QQuickVisualPathPrivate::DirtyFillColor) - renderer->setFillColor(i, p->fillColor()); - if (dirty & QQuickVisualPathPrivate::DirtyFillRule) - renderer->setFillRule(i, p->fillRule()); - if (dirty & QQuickVisualPathPrivate::DirtyStyle) { - renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); - renderer->setCapStyle(i, p->capStyle()); - } - if (dirty & QQuickVisualPathPrivate::DirtyDash) - renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); - if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) - renderer->setFillGradient(i, p->fillGradient()); - - dirty = 0; - } - - renderer->endSync(useAsync); - } else { - // Path and stroke/fill params provided from JavaScript. This avoids - // QObjects at the expense of not supporting changes afterwards. - const int count = jsData.paths.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - renderer->setJSPath(i, jsData.paths[i]); - const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); - renderer->setStrokeColor(i, sfp.strokeColor); - renderer->setStrokeWidth(i, sfp.strokeWidth); - renderer->setFillColor(i, sfp.fillColor); - renderer->setFillRule(i, sfp.fillRule); - renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); - renderer->setCapStyle(i, sfp.capStyle); - renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); - renderer->setFillGradient(i, sfp.fillGradient); - } - - renderer->endSync(useAsync); - } - - if (!useAsync) - setStatus(QQuickPathItem::Ready); -} - -// ***** gradient support ***** - -/*! - \qmltype PathGradientStop - \instantiates QQuickPathGradientStop - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickPathGradientStop::position() const -{ - return m_position; -} - -void QQuickPathGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickPathGradientStop::color() const -{ - return m_color; -} - -void QQuickPathGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmltype PathGradient - \instantiates QQuickPathGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Base type of PathItem fill gradients - \since 5.10 - - This is an abstract base class for gradients like PathLinearGradient and - cannot be created directly. - */ - -QQuickPathGradient::QQuickPathGradient(QObject *parent) - : QObject(parent), - m_spread(PadSpread) -{ -} - -int QQuickPathGradient::countStops(QQmlListProperty *list) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickPathGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); - return; - } - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - -/*! - \qmlproperty list QtQuick::PathGradient::stops - \default - - The list of PathGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickPathGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickPathGradient::appendStop, - &QQuickPathGradient::countStops, - &QQuickPathGradient::atStop, - nullptr); -} - -QGradientStops QQuickPathGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickPathGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick::PathGradient::spred - - Specifies how the area outside the gradient area should be filled. The - default value is PathGradient.PadSpread. - - \list - \li PathGradient.PadSpread - The area is filled with the closest stop color. - \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. - \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. - \endlist - */ - -QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const -{ - return m_spread; -} - -void QQuickPathGradient::setSpread(SpreadMode mode) -{ - if (m_spread != mode) { - m_spread = mode; - emit spreadChanged(); - emit updated(); - } -} - -/*! - \qmltype PathLinearGradient - \instantiates QQuickPathLinearGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits PathGradient - \brief Linear gradient - \since 5.10 - - Linear gradients interpolate colors between start and end points. Outside - these points the gradient is either padded, reflected or repeated depending - on the spread type. - - \sa QLinearGradient - */ - -QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) - : QQuickPathGradient(parent) -{ -} - -/*! - \qmlproperty real QtQuick::PathLinearGradient::x1 - \qmlproperty real QtQuick::PathLinearGradient::y1 - \qmlproperty real QtQuick::PathLinearGradient::x2 - \qmlproperty real QtQuick::PathLinearGradient::y2 - - These properties define the start and end points between which color - interpolation occurs. By default both the stard and end points are set to - (0, 0). - */ - -qreal QQuickPathLinearGradient::x1() const -{ - return m_start.x(); -} - -void QQuickPathLinearGradient::setX1(qreal v) -{ - if (m_start.x() != v) { - m_start.setX(v); - emit x1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y1() const -{ - return m_start.y(); -} - -void QQuickPathLinearGradient::setY1(qreal v) -{ - if (m_start.y() != v) { - m_start.setY(v); - emit y1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::x2() const -{ - return m_end.x(); -} - -void QQuickPathLinearGradient::setX2(qreal v) -{ - if (m_end.x() != v) { - m_end.setX(v); - emit x2Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y2() const -{ - return m_end.y(); -} - -void QQuickPathLinearGradient::setY2(qreal v) -{ - if (m_end.y() != v) { - m_end.setY(v); - emit y2Changed(); - emit updated(); - } -} - -#ifndef QT_NO_OPENGL - -// contexts sharing with each other get the same cache instance -class QQuickPathItemGradientCacheWrapper -{ -public: - QQuickPathItemGradientCache *get(QOpenGLContext *context) - { - return m_resource.value(context); - } - -private: - QOpenGLMultiGroupSharedResource m_resource; -}; - -QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() -{ - static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; - return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); -} - -// let QOpenGLContext manage the lifetime of the cached textures -QQuickPathItemGradientCache::~QQuickPathItemGradientCache() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::invalidateResource() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) -{ - qDeleteAll(m_cache); - m_cache.clear(); -} - -static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, - uint *colorTable, int size, float opacity) -{ - int pos = 0; - const QGradientStops &s = gradient.stops; - const bool colorInterpolation = true; - - uint alpha = qRound(opacity * 256); - uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); - qreal incr = 1.0 / qreal(size); - qreal fpos = 1.5 * incr; - colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); - - while (fpos <= s.first().first) { - colorTable[pos] = colorTable[pos - 1]; - pos++; - fpos += incr; - } - - if (colorInterpolation) - current_color = qPremultiply(current_color); - - const int sLast = s.size() - 1; - for (int i = 0; i < sLast; ++i) { - qreal delta = 1/(s[i+1].first - s[i].first); - uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); - if (colorInterpolation) - next_color = qPremultiply(next_color); - - while (fpos < s[i+1].first && pos < size) { - int dist = int(256 * ((fpos - s[i].first) * delta)); - int idist = 256 - dist; - if (colorInterpolation) - colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); - else - colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); - ++pos; - fpos += incr; - } - current_color = next_color; - } - - Q_ASSERT(s.size() > 0); - - uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); - for ( ; pos < size; ++pos) - colorTable[pos] = last_color; - - colorTable[size-1] = last_color; -} - -QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) -{ - QSGPlainTexture *tx = m_cache[grad]; - if (!tx) { - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - GLuint id; - f->glGenTextures(1, &id); - f->glBindTexture(GL_TEXTURE_2D, id); - static const uint W = 1024; // texture size is 1024x1 - uint buf[W]; - generateGradientColorTable(grad, buf, W, 1.0f); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - tx = new QSGPlainTexture; - tx->setTextureId(id); - switch (grad.spread) { - case QQuickPathGradient::PadSpread: - tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); - tx->setVerticalWrapMode(QSGTexture::ClampToEdge); - break; - case QQuickPathGradient::RepeatSpread: - tx->setHorizontalWrapMode(QSGTexture::Repeat); - tx->setVerticalWrapMode(QSGTexture::Repeat); - break; - case QQuickPathGradient::ReflectSpread: - tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); - tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); - break; - default: - qWarning("Unknown gradient spread mode %d", grad.spread); - break; - } - m_cache[grad] = tx; - } - return tx; -} - -#endif // QT_NO_OPENGL - -// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** - -class QQuickPathItemJSEngineData : public QV8Engine::Deletable -{ -public: - QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); - - QV4::PersistentValue pathProto; - QV4::PersistentValue strokeFillParamsProto; -}; - -V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) - -namespace QV4 { -namespace Heap { - -struct QQuickPathItemJSPathPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSPath : Object { - void init() { Object::init(); } - QQuickPathItemPathObject *obj; -}; - -struct QQuickPathItemJSStrokeFillParamsPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSStrokeFillParams : Object { - void init() { Object::init(); } - QQuickPathItemStrokeFillParamsObject *obj; -}; - -} // namespace Heap -} // namespace QV4 - -struct QQuickPathItemJSPathPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); - o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); - o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); - o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); - o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); - -struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); - -struct QQuickPathItemJSPath : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); - -struct QQuickPathItemJSStrokeFillParams : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) - - static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); - -void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::MoveTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::LineTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 4) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::QuadTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // cx - p->path.coords.append(v[1].toNumber()); // cy - p->path.coords.append(v[2].toNumber()); // x - p->path.coords.append(v[3].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 6) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::CubicTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // c1x - p->path.coords.append(v[1].toNumber()); // c1y - p->path.coords.append(v[2].toNumber()); // c2x - p->path.coords.append(v[3].toNumber()); // c2y - p->path.coords.append(v[4].toNumber()); // x - p->path.coords.append(v[5].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 7) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::ArcTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // radiusX - p->path.coords.append(v[1].toNumber()); // radiusY - p->path.coords.append(v[2].toNumber()); // xAxisRotation - p->path.coords.append(v[3].toNumber()); // x - p->path.coords.append(v[4].toNumber()); // y - p->path.coords.append(v[5].toNumber()); // sweepFlag - p->path.coords.append(v[6].toNumber()); // largeArc - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -extern QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp - -static inline QString qt_color_string(const QColor &color) -{ - if (color.alpha() == 255) - return color.name(); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.strokeColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.strokeWidth = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.fillColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); -} - -void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.miterLimit = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.dashOffset = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - a->arrayReserve(p->sfp.dashPattern.count()); - QV4::ScopedValue v(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) - a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); - a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); - - scope.result = a.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isObject()) { - QV4::Scoped ao(scope, value); - if (!!ao) { - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - p->sfp.dashPattern.resize(ao->getLength()); - QV4::ScopedValue val(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { - val = ao->getIndexed(i); - p->sfp.dashPattern[i] = val->toNumber(); - } - } - } - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = r->d()->obj->v4fillGradient.value(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - QV4::Scoped qobjectWrapper(scope, value); - if (!!qobjectWrapper) { - if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { - r->d()->obj->v4fillGradient.set(scope.engine, value); - r->d()->obj->sfp.fillGradient = grad; - } - } else { - r->d()->obj->v4fillGradient.set(scope.engine, nullptr); - r->d()->obj->sfp.fillGradient = nullptr; - } - - scope.result = QV4::Encode::undefined(); -} - -QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); - pathProto = proto; - - proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); - - proto->defineAccessorProperty(QStringLiteral("strokeColor"), - QQuickPathItemJSStrokeFillParams::method_get_strokeColor, - QQuickPathItemJSStrokeFillParams::method_set_strokeColor); - proto->defineAccessorProperty(QStringLiteral("strokeWidth"), - QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, - QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); - proto->defineAccessorProperty(QStringLiteral("fillColor"), - QQuickPathItemJSStrokeFillParams::method_get_fillColor, - QQuickPathItemJSStrokeFillParams::method_set_fillColor); - proto->defineAccessorProperty(QStringLiteral("fillRule"), - QQuickPathItemJSStrokeFillParams::method_get_fillRule, - QQuickPathItemJSStrokeFillParams::method_set_fillRule); - proto->defineAccessorProperty(QStringLiteral("joinStyle"), - QQuickPathItemJSStrokeFillParams::method_get_joinStyle, - QQuickPathItemJSStrokeFillParams::method_set_joinStyle); - proto->defineAccessorProperty(QStringLiteral("miterLimit"), - QQuickPathItemJSStrokeFillParams::method_get_miterLimit, - QQuickPathItemJSStrokeFillParams::method_set_miterLimit); - proto->defineAccessorProperty(QStringLiteral("capStyle"), - QQuickPathItemJSStrokeFillParams::method_get_capStyle, - QQuickPathItemJSStrokeFillParams::method_set_capStyle); - proto->defineAccessorProperty(QStringLiteral("strokeStyle"), - QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, - QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); - proto->defineAccessorProperty(QStringLiteral("dashOffset"), - QQuickPathItemJSStrokeFillParams::method_get_dashOffset, - QQuickPathItemJSStrokeFillParams::method_set_dashOffset); - proto->defineAccessorProperty(QStringLiteral("dashPattern"), - QQuickPathItemJSStrokeFillParams::method_get_dashPattern, - QQuickPathItemJSStrokeFillParams::method_set_dashPattern); - proto->defineAccessorProperty(QStringLiteral("fillGradient"), - QQuickPathItemJSStrokeFillParams::method_get_fillGradient, - QQuickPathItemJSStrokeFillParams::method_set_fillGradient); - - strokeFillParamsProto = proto; -} - -void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->pathProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmltype JSPath - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes a path via a JavaScript API - */ - -/*! - \qmlmethod void QtQuick::JSPath::moveTo(x, y) - - Moves the path's position to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::lineTo(x, y) - - Defines a straight line to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) - - Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an - end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) - - Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and - (\a c2x, \a c2y), and an end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) - - Defines an elliptical arc, following the elliptical arc command in SVG. See - \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the - SVG path specification} for details on the parameters. - */ - -/*! - \qmlmethod void QtQuick::JSPath::clear() - - Clears the path object removing all path elements. This is more lightweight - than creating a new JSPath object. - */ - -void QQuickPathItemPathObject::clear() -{ - path = QQuickPathItemPath(); -} - -/*! - \qmltype StrokeFillParams - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes stroke and fill parameters via a JavaScript API - - The properties of StrokeFillParams objects correspond 1:1 to VisualPath - properties. The possible values for enumerations are the same as well, for - example: - - \code - sfp.strokeStyle = VisualPath.DashLine; - sfp.capStyle = VisualPath.RoundCap; - \endcode - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::strokeColor - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::strokeWidth - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::fillColor - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle - */ - -/*! - \qmlproperty int QtQuick::StrokeFillParams::miterLimit - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::dashOffset - */ - -/*! - \qmlproperty list QtQuick::StrokeFillParams::dashPattern - - The dash pattern can be specified using JavaScript arrays. - - \code - sfp.dashPattern = [ 4, 2 ]; - \endcode - */ - -/*! - \qmlproperty object QtQuick::StrokeFillParams::fillGradient - - Sets the fill gradient. The default value is null. Gradients cannot be - created from JavaScript. Instead, reference a PathLinearGradient or other - item by id. - - \code - PathLinearGradient { id: grad; ... } - ... - sfp.fillGradient = grad; - \endcode - */ - -/*! - \qmlmethod void QtQuick::StrokeFillParams::clear() - - Resets all values to their defaults. This is more lightweight than creating - a new StrokeFillParams object. - */ - -void QQuickPathItemStrokeFillParamsObject::clear() -{ - sfp = QQuickPathItemStrokeFillParams(); - if (!v4fillGradient.isNullOrUndefined()) - v4fillGradient.set(v4fillGradient.engine(), nullptr); -} - -void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmlmethod JSPath QtQuick::PathItem::newPath() - - Creates and returns a new object that describes a path and offers a - JavaScript API. Paired with a stroke-fill parameter object it is - equivalent to a VisualPath. - - The following two snippets are equivalent when it comes to the end result: - - \code - var p = pathItem.newPath(); - var sfp = pathItem.newStrokeFillParams(); - sfp.fillColor = "white"; - sfp.strokeColor = "black"; - sfp.strokeWidth = 0.172; - p.moveTo(-122.304, 84.285); - p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); - pathItem.appendVisualPath(p, sfp); - \endcode - - \code - PathItem { - VisualPath { - fillColor: "white" - strokeColor: "black" - strokeWidth: 0.172 - Path { - startX: -122.304; startY: 84.285 - PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } - } - } - } - \endcode - - The latter offers a full declarative API, with the possibility to binding - to and animating properties, while the former uses less resources due to - greatly reducing the number of QObject instances created. -*/ - -void QQuickPathItem::newPath(QQmlV4Function *args) -{ - QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() - - Creates and returns a new object that describes stroke and fill parameters - and offers a JavaScript API. Paired with a path object it is equivalent to - a VisualPath. - */ - -void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) -{ - QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod void QtQuick::PathItem::clearVisualPaths() - - Clears the list of visual paths. - - \note This applies only to path and stroke-fill parameter objects registered - via appendVisualPaths(). - */ - -void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->jsData.paths.clear(); - d->jsData.sfp.clear(); -} - -/*! - \qmlmethod void QtQuick::PathItem::commitVisualPaths() - - Updates the PathItem. - - In order to avoid rendering a half-prepared PathItem, calling - PathItem.appendVisualPath() does not trigger any processing. Instead, - applications must call this function when all path and stroke-fill - parameter objects are registered. - */ - -void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->_q_visualPathChanged(); -} - -/*! - \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) - - Adds the visual path compoes of \a path and \a strokeFillParams into the - PathItem. - - \note The declarative and imprative (JavaScript) APIs of PathItem use - independent data structures. Calling this function has no effect on the - PathItem.elements property and vice versa. Once this function is called, - the PathItem will only consider the data registered via this function and - will ignore the declarative elements property. - */ - -void QQuickPathItem::appendVisualPath(QQmlV4Function *args) -{ - if (args->length() < 2) - return; - - Q_D(QQuickPathItem); - QV4::Scope scope(args->v4engine()); - QV4::Scoped jsp(scope, (*args)[0]); - QV4::Scoped jssfp(scope, (*args)[1]); - - const QQuickPathItemPath &path(jsp->d()->obj->path); - const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); - - d->jsData.paths.append(path); - d->jsData.sfp.append(sfp); -} - -QT_END_NAMESPACE - -#include "moc_qquickpathitem_p.cpp" diff --git a/src/quick/items/qquickpathitem_p.h b/src/quick/items/qquickpathitem_p.h deleted file mode 100644 index 37b23dee6f..0000000000 --- a/src/quick/items/qquickpathitem_p.h +++ /dev/null @@ -1,335 +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 QQUICKPATHITEM_P_H -#define QQUICKPATHITEM_P_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 "qquickitem.h" - -#include -#include -#include -#include - -QT_REQUIRE_CONFIG(quick_path); - -QT_BEGIN_NAMESPACE - -class QQuickVisualPathPrivate; -class QQuickPathItemPrivate; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathGradientStop : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickPathGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) - Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - enum SpreadMode { - PadSpread, - RepeatSpread, - ReflectSpread - }; - Q_ENUM(SpreadMode) - - QQuickPathGradient(QObject *parent = nullptr); - - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - - SpreadMode spread() const; - void setSpread(SpreadMode mode); - -signals: - void updated(); - void spreadChanged(); - -private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; - SpreadMode m_spread; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathLinearGradient : public QQuickPathGradient -{ - Q_OBJECT - Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) - Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) - Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) - Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - QQuickPathLinearGradient(QObject *parent = nullptr); - - qreal x1() const; - void setX1(qreal v); - qreal y1() const; - void setY1(qreal v); - qreal x2() const; - void setX2(qreal v); - qreal y2() const; - void setY2(qreal v); - -signals: - void x1Changed(); - void y1Changed(); - void x2Changed(); - void y2Changed(); - -private: - QPointF m_start; - QPointF m_end; -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickVisualPath : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) - Q_CLASSINFO("DefaultProperty", "path") - - Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) - Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) - Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) - Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) - Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) - Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) - Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) - Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) - Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) - Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) - Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) - -public: - enum FillRule { - OddEvenFill = Qt::OddEvenFill, - WindingFill = Qt::WindingFill - }; - Q_ENUM(FillRule) - - enum JoinStyle { - MiterJoin = Qt::MiterJoin, - BevelJoin = Qt::BevelJoin, - RoundJoin = Qt::RoundJoin - }; - Q_ENUM(JoinStyle) - - enum CapStyle { - FlatCap = Qt::FlatCap, - SquareCap = Qt::SquareCap, - RoundCap = Qt::RoundCap - }; - Q_ENUM(CapStyle) - - enum StrokeStyle { - SolidLine = Qt::SolidLine, - DashLine = Qt::DashLine - }; - Q_ENUM(StrokeStyle) - - QQuickVisualPath(QObject *parent = nullptr); - ~QQuickVisualPath(); - - QQuickPath *path() const; - void setPath(QQuickPath *path); - - QColor strokeColor() const; - void setStrokeColor(const QColor &color); - - qreal strokeWidth() const; - void setStrokeWidth(qreal w); - - QColor fillColor() const; - void setFillColor(const QColor &color); - - FillRule fillRule() const; - void setFillRule(FillRule fillRule); - - JoinStyle joinStyle() const; - void setJoinStyle(JoinStyle style); - - int miterLimit() const; - void setMiterLimit(int limit); - - CapStyle capStyle() const; - void setCapStyle(CapStyle style); - - StrokeStyle strokeStyle() const; - void setStrokeStyle(StrokeStyle style); - - qreal dashOffset() const; - void setDashOffset(qreal offset); - - QVector dashPattern() const; - void setDashPattern(const QVector &array); - - QQuickPathGradient *fillGradient() const; - void setFillGradient(QQuickPathGradient *gradient); - void resetFillGradient(); - -Q_SIGNALS: - void changed(); - void pathChanged(); - void strokeColorChanged(); - void strokeWidthChanged(); - void fillColorChanged(); - void fillRuleChanged(); - void joinStyleChanged(); - void miterLimitChanged(); - void capStyleChanged(); - void strokeStyleChanged(); - void dashOffsetChanged(); - void dashPatternChanged(); - void fillGradientChanged(); - -private: - Q_DISABLE_COPY(QQuickVisualPath) - Q_DECLARE_PRIVATE(QQuickVisualPath) - Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) - Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickPathItem : public QQuickItem -{ - Q_OBJECT - Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) - Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") - -public: - enum RendererType { - UnknownRenderer, - GeometryRenderer, - NvprRenderer, - SoftwareRenderer - }; - Q_ENUM(RendererType) - - enum Status { - Null, - Ready, - Processing - }; - Q_ENUM(Status) - - QQuickPathItem(QQuickItem *parent = nullptr); - ~QQuickPathItem(); - - RendererType rendererType() const; - - bool asynchronous() const; - void setAsynchronous(bool async); - - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); - - Status status() const; - - QQmlListProperty elements(); - - Q_INVOKABLE void newPath(QQmlV4Function *args); - Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); - Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); - -protected: - QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; - void updatePolish() override; - void itemChange(ItemChange change, const ItemChangeData &data) override; - void componentComplete() override; - void classBegin() override; - -Q_SIGNALS: - void rendererChanged(); - void asynchronousChanged(); - void enableVendorExtensionsChanged(); - void statusChanged(); - -private: - Q_DISABLE_COPY(QQuickPathItem) - Q_DECLARE_PRIVATE(QQuickPathItem) - Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPathItem) - -#endif // QQUICKPATHITEM_P_H diff --git a/src/quick/items/qquickpathitem_p_p.h b/src/quick/items/qquickpathitem_p_p.h deleted file mode 100644 index c9a2904a25..0000000000 --- a/src/quick/items/qquickpathitem_p_p.h +++ /dev/null @@ -1,282 +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 QQUICKPATHITEM_P_P_H -#define QQUICKPATHITEM_P_P_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 "qquickpathitem_p.h" -#include "qquickitem_p.h" -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGPlainTexture; - -struct QQuickPathItemPath -{ - enum Command { - MoveTo, - LineTo, - QuadTo, - CubicTo, - ArcTo - }; - - QVector cmd; - QVector coords; - - QPainterPath toPainterPath() const; -}; - -struct QQuickPathItemStrokeFillParams -{ - QQuickPathItemStrokeFillParams(); - - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; -}; - -class QQuickAbstractPathRenderer -{ -public: - enum Flag { - SupportsAsync = 0x01 - }; - Q_DECLARE_FLAGS(Flags, Flag) - - virtual ~QQuickAbstractPathRenderer() { } - - // Gui thread - virtual void beginSync(int totalCount) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } - // - QML API - virtual void setPath(int index, const QQuickPath *path) = 0; - // - JS API - virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; - // - stroke/fill parameters - virtual void setStrokeColor(int index, const QColor &color) = 0; - virtual void setStrokeWidth(int index, qreal w) = 0; - virtual void setFillColor(int index, const QColor &color) = 0; - virtual void setFillRule(int index, QQuickVisualPath::FillRule fillRule) = 0; - virtual void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) = 0; - virtual void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) = 0; - virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) = 0; - virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - - // Render thread, with gui blocked - virtual void updateNode() = 0; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) - -class QQuickVisualPathPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQuickVisualPath) - -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStrokeColor = 0x02, - DirtyStrokeWidth = 0x04, - DirtyFillColor = 0x08, - DirtyFillRule = 0x10, - DirtyStyle = 0x20, - DirtyDash = 0x40, - DirtyFillGradient = 0x80, - - DirtyAll = 0xFF - }; - - QQuickVisualPathPrivate(); - - void _q_pathChanged(); - void _q_fillGradientChanged(); - - static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } - - QQuickPath *path; - int dirty; - QQuickPathItemStrokeFillParams sfp; -}; - -class QQuickPathItemPrivate : public QQuickItemPrivate -{ - Q_DECLARE_PUBLIC(QQuickPathItem) - -public: - QQuickPathItemPrivate(); - ~QQuickPathItemPrivate(); - - void createRenderer(); - QSGNode *createNode(); - void sync(); - - void _q_visualPathChanged(); - void setStatus(QQuickPathItem::Status newStatus); - - static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } - - bool componentComplete; - bool vpChanged; - QQuickPathItem::RendererType rendererType; - bool async; - QQuickPathItem::Status status; - QQuickAbstractPathRenderer *renderer; - - struct { - QVector vp; - } qmlData; - - struct { - bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } - QVector paths; - QVector sfp; - } jsData; - - bool enableVendorExts; -}; - -class QQuickPathItemPathObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemPath path; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -class QQuickPathItemStrokeFillParamsObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemStrokeFillParams sfp; - QV4::PersistentValue v4fillGradient; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemGradientCache : public QOpenGLSharedResource -{ -public: - struct GradientDesc { - QGradientStops stops; - QPointF start; - QPointF end; - QQuickPathGradient::SpreadMode spread; - bool operator==(const GradientDesc &other) const - { - return start == other.start && end == other.end && spread == other.spread - && stops == other.stops; - } - }; - - QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } - ~QQuickPathItemGradientCache(); - - void invalidateResource() override; - void freeResource(QOpenGLContext *) override; - - QSGTexture *get(const GradientDesc &grad); - - static QQuickPathItemGradientCache *currentCache(); - -private: - QHash m_cache; -}; - -inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) -{ - uint h = seed; - h += v.start.x() + v.end.y() + v.spread; - for (int i = 0; i < 3 && i < v.stops.count(); ++i) - h += v.stops[i].second.rgba(); - return h; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/items/qquickpathitemgenericrenderer.cpp b/src/quick/items/qquickpathitemgenericrenderer.cpp deleted file mode 100644 index 4e8fe55df2..0000000000 --- a/src/quick/items/qquickpathitemgenericrenderer.cpp +++ /dev/null @@ -1,775 +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 "qquickpathitemgenericrenderer_p.h" -#include -#include -#include - -#ifndef QT_NO_OPENGL -#include -#include -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -static const qreal TRI_SCALE = 1; - -struct ColoredVertex // must match QSGGeometry::ColoredPoint2D -{ - float x, y; - QQuickPathItemGenericRenderer::Color4ub color; - void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } -}; - -static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) -{ - QQuickPathItemGenericRenderer::Color4ub color = { - uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; -} - -QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), - m_window(window), - m_material(nullptr) -{ - setGeometry(m_geometry); - activateMaterial(MatSolidColor); -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("stroke-fill")); -#endif -} - -QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() -{ - delete m_geometry; -} - -void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -{ - switch (m) { - case MatSolidColor: - // Use vertexcolor material. Items with different colors remain batchable - // this way, at the expense of having to provide per-vertex color values. - if (!m_solidColorMaterial) - m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); - m_material = m_solidColorMaterial.data(); - break; - case MatLinearGradient: - if (!m_linearGradientMaterial) - m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); - m_material = m_linearGradientMaterial.data(); - break; - default: - qWarning("Unknown material %d", m); - return; - } - - if (material() != m_material) - setMaterial(m_material); -} - -static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) -{ - static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) { - static bool elementIndexUintChecked = false; - if (!elementIndexUintChecked) { - elementIndexUintChecked = true; - QOpenGLContext *context = QOpenGLContext::currentContext(); - QScopedPointer dummyContext; - QScopedPointer dummySurface; - bool ok = true; - if (!context) { - dummyContext.reset(new QOpenGLContext); - dummyContext->create(); - context = dummyContext.data(); - dummySurface.reset(new QOffscreenSurface); - dummySurface->setFormat(context->format()); - dummySurface->create(); - ok = context->makeCurrent(dummySurface.data()); - } - if (ok) { - elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( - QOpenGLExtensions::ElementIndexUint); - } - } - } -#else - Q_UNUSED(api); -#endif - return elementIndexUint; -} - -QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() -{ - for (VisualPathData &d : m_vp) { - if (d.pendingFill) - d.pendingFill->orphaned = true; - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - } -} - -// sync, and so triangulation too, happens on the gui thread -// - except when async is set, in which case triangulation is moved to worker threads - -void QQuickPathItemGenericRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } - for (VisualPathData &d : m_vp) - d.syncDirty = 0; -} - -void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.strokeColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.fillColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyFillGeom; -} - -void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathData &d(m_vp[index]); - d.pen.setStyle(Qt::PenStyle(strokeStyle)); - if (strokeStyle == QQuickVisualPath::DashLine) { - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - } - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.syncDirty |= DirtyFillGradient; -} - -void QQuickPathItemFillRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); - emit done(this); -} - -void QQuickPathItemStrokeRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); - emit done(this); -} - -void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) -{ - m_asyncCallback = callback; - m_asyncCallbackData = data; -} - -static QThreadPool *pathWorkThreadPool = nullptr; - -static void deletePathWorkThreadPool() -{ - delete pathWorkThreadPool; - pathWorkThreadPool = nullptr; -} - -void QQuickPathItemGenericRenderer::endSync(bool async) -{ - bool didKickOffAsync = false; - - for (int i = 0; i < m_vp.count(); ++i) { - VisualPathData &d(m_vp[i]); - if (!d.syncDirty) - continue; - - m_accDirty |= d.syncDirty; - - // Use a shadow dirty flag in order to avoid losing state in case there are - // multiple syncs with different dirty flags before we get to updateNode() - // on the render thread (with the gui thread blocked). For our purposes - // here syncDirty is still required since geometry regeneration must only - // happen when there was an actual change in this particular sync round. - d.effectiveDirty |= d.syncDirty; - - if (d.path.isEmpty()) { - d.fillVertices.clear(); - d.fillIndices.clear(); - d.strokeVertices.clear(); - continue; - } - - if (async && !pathWorkThreadPool) { - qAddPostRoutine(deletePathWorkThreadPool); - pathWorkThreadPool = new QThreadPool; - const int idealCount = QThread::idealThreadCount(); - pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - - if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (m_api == QSGRendererInterface::Unknown) - m_api = m_item->window()->rendererInterface()->graphicsApi(); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - // Bail out when orphaned (meaning either another run was - // started after this one, or the renderer got destroyed). - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.indexType = r->indexType; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyFillGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); - } - } - - if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyStrokeGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } - } - } - - if (!didKickOffAsync && async && m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() -{ - for (const VisualPathData &d : qAsConst(m_vp)) { - if (d.pendingFill || d.pendingStroke) - return; - } - m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; - m_item->update(); - if (m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -// the stroke/fill triangulation functions may be invoked either on the gui -// thread or some worker thread and must thus be self-contained. -void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); - const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - fillVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); - const qreal *vsrc = ts.vertices.constData(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); - - size_t indexByteSize; - if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - *indexType = QSGGeometry::UnsignedShortType; - // fillIndices is still QVector. Just resize to N/2 and pack - // the N quint16s into it. - fillIndices->resize(ts.indices.size() / 2); - indexByteSize = ts.indices.size() * sizeof(quint16); - } else { - *indexType = QSGGeometry::UnsignedIntType; - fillIndices->resize(ts.indices.size()); - indexByteSize = ts.indices.size() * sizeof(quint32); - } - memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); -} - -void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / TRI_SCALE; - - QTriangulatingStroker stroker; - stroker.setInvScale(inverseScale); - - if (pen.style() == Qt::SolidLine) { - stroker.process(vp, pen, clip, 0); - } else { - QDashedStrokeProcessor dashStroker; - dashStroker.setInvScale(inverseScale); - dashStroker.process(vp, pen, clip, 0); - QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), - dashStroker.elementTypes(), 0); - stroker.process(dashStroke, pen, clip, 0); - } - - if (!stroker.vertexCount()) { - strokeVertices->clear(); - return; - } - - const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - strokeVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); - const float *vsrc = stroker.vertices(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); -} - -void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) -{ - if (m_rootNode != node) { - m_rootNode = node; - m_accDirty |= DirtyList; - } -} - -// on the render thread with gui blocked -void QQuickPathItemGenericRenderer::updateNode() -{ - if (!m_rootNode || !m_accDirty) - return; - -// [ m_rootNode ] -// / / / -// #0 [ fill ] [ stroke ] [ next ] -// / / | -// #1 [ fill ] [ stroke ] [ next ] -// / / | -// #2 [ fill ] [ stroke ] [ next ] -// ... -// ... - - QQuickPathItemGenericNode **nodePtr = &m_rootNode; - QQuickPathItemGenericNode *prevNode = nullptr; - - for (VisualPathData &d : m_vp) { - if (!*nodePtr) { - *nodePtr = new QQuickPathItemGenericNode; - prevNode->m_next = *nodePtr; - prevNode->appendChildNode(*nodePtr); - } - - QQuickPathItemGenericNode *node = *nodePtr; - - if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - - if (!d.effectiveDirty) { - prevNode = node; - nodePtr = &node->m_next; - continue; - } - - if (d.fillColor.a == 0) { - delete node->m_fillNode; - node->m_fillNode = nullptr; - } else if (!node->m_fillNode) { - node->m_fillNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); - if (node->m_strokeNode) - node->removeChildNode(node->m_strokeNode); - node->appendChildNode(node->m_fillNode); - if (node->m_strokeNode) - node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyFillGeom; - } - - if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { - delete node->m_strokeNode; - node->m_strokeNode = nullptr; - } else if (!node->m_strokeNode) { - node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); - node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyStrokeGeom; - } - - updateFillNode(&d, node); - updateStrokeNode(&d, node); - - d.effectiveDirty = 0; - - prevNode = node; - nodePtr = &node->m_next; - } - - if (*nodePtr && prevNode) { - prevNode->removeChildNode(*nodePtr); - delete *nodePtr; - *nodePtr = nullptr; - } - - m_accDirty = 0; -} - -void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) -{ - if (d->fillGradientActive) { - if (d->effectiveDirty & DirtyFillGradient) - n->m_fillGradient = d->fillGradient; - } -} - -void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_fillNode) - return; - if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) - return; - - // Make a copy of the data that will be accessed by the material on - // the render thread. This must be done even when we bail out below. - QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; - updateShadowDataInNode(d, n); - - QSGGeometry *g = n->m_geometry; - if (d->fillVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - if (d->fillGradientActive) { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); - if (d->effectiveDirty & DirtyFillGradient) { - // Gradients are implemented via a texture-based material. - n->markDirty(QSGNode::DirtyMaterial); - // stop here if only the gradient changed; no need to touch the geometry - if (!(d->effectiveDirty & DirtyFillGeom)) - return; - } - } else { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); - // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); - n->markDirty(QSGNode::DirtyGeometry); - return; - } - } - - const int indexCount = d->indexType == QSGGeometry::UnsignedShortType - ? d->fillIndices.count() * 2 : d->fillIndices.count(); - if (g->indexType() != d->indexType) { - g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), - d->fillVertices.count(), indexCount, d->indexType); - n->setGeometry(g); - delete n->m_geometry; - n->m_geometry = g; - } else { - g->allocate(d->fillVertices.count(), indexCount); - } - g->setDrawingMode(QSGGeometry::DrawTriangles); - memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); - memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); - - n->markDirty(QSGNode::DirtyGeometry); -} - -void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_strokeNode) - return; - if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) - return; - - QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = n->m_geometry; - if (d->strokeVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - n->markDirty(QSGNode::DirtyGeometry); - - // Async loading runs update once, bails out above, then updates again once - // ready. Set the material dirty then. This is in-line with fill where the - // first activateMaterial() achieves the same. - if (!g->vertexCount()) - n->markDirty(QSGNode::DirtyMaterial); - - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); - return; - } - - g->allocate(d->strokeVertices.count(), 0); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QSGVertexColorMaterial; -#endif - - qWarning("Vertex-color material: Unsupported graphics API %d", api); - return nullptr; -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickPathItemGenericStrokeFillNode *node) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QQuickPathItemLinearGradientMaterial(node); -#endif - - qWarning("Linear gradient material: Unsupported graphics API %d", api); - return nullptr; -} - -#ifndef QT_NO_OPENGL - -QSGMaterialType QQuickPathItemLinearGradientShader::type; - -QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); -} - -void QQuickPathItemLinearGradientShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("opacity"); - m_matrixLoc = program()->uniformLocation("matrix"); - m_gradStartLoc = program()->uniformLocation("gradStart"); - m_gradEndLoc = program()->uniformLocation("gradEnd"); -} - -void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) -{ - QQuickPathItemLinearGradientMaterial *m = static_cast(mat); - - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - QQuickPathItemGenericStrokeFillNode *node = m->node(); - program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); - program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); - - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); - tx->bind(); -} - -char const *const *QQuickPathItemLinearGradientShader::attributeNames() const -{ - static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; - return attr; -} - -int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const -{ - Q_ASSERT(other && type() == other->type()); - const QQuickPathItemLinearGradientMaterial *m = static_cast(other); - - QQuickPathItemGenericStrokeFillNode *a = node(); - QQuickPathItemGenericStrokeFillNode *b = m->node(); - Q_ASSERT(a && b); - if (a == b) - return 0; - - const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; - const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; - - if (int d = ga->spread - gb->spread) - return d; - - if (int d = ga->start.x() - gb->start.x()) - return d; - if (int d = ga->start.y() - gb->start.y()) - return d; - if (int d = ga->end.x() - gb->end.x()) - return d; - if (int d = ga->end.y() - gb->end.y()) - return d; - - if (int d = ga->stops.count() - gb->stops.count()) - return d; - - for (int i = 0; i < ga->stops.count(); ++i) { - if (int d = ga->stops[i].first - gb->stops[i].first) - return d; - if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) - return d; - } - - return 0; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemgenericrenderer_p.h b/src/quick/items/qquickpathitemgenericrenderer_p.h deleted file mode 100644 index 70a9e88d2f..0000000000 --- a/src/quick/items/qquickpathitemgenericrenderer_p.h +++ /dev/null @@ -1,303 +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 QQUICKPATHITEMGENERICRENDERER_P_H -#define QQUICKPATHITEMGENERICRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemGenericNode; -class QQuickPathItemGenericStrokeFillNode; -class QQuickPathItemFillRunnable; -class QQuickPathItemStrokeRunnable; - -class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyFillGeom = 0x01, - DirtyStrokeGeom = 0x02, - DirtyColor = 0x04, - DirtyFillGradient = 0x08, - DirtyList = 0x10 // only for accDirty - }; - - QQuickPathItemGenericRenderer(QQuickItem *item) - : m_item(item), - m_api(QSGRendererInterface::Unknown), - m_rootNode(nullptr), - m_accDirty(0), - m_asyncCallback(nullptr) - { } - ~QQuickPathItemGenericRenderer(); - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - void setAsyncCallback(void (*)(void *), void *) override; - Flags flags() const override { return SupportsAsync; } - - void updateNode() override; - - void setRootNode(QQuickPathItemGenericNode *node); - - struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VertexContainerType; - typedef QVector IndexContainerType; - - static void triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint); - static void triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize); - -private: - void maybeUpdateAsyncItem(); - - struct VisualPathData { - float strokeWidth; - QPen pen; - Color4ub strokeColor; - Color4ub fillColor; - Qt::FillRule fillRule; - QPainterPath path; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - VertexContainerType fillVertices; - IndexContainerType fillIndices; - QSGGeometry::Type indexType; - VertexContainerType strokeVertices; - int syncDirty; - int effectiveDirty = 0; - QQuickPathItemFillRunnable *pendingFill = nullptr; - QQuickPathItemStrokeRunnable *pendingStroke = nullptr; - }; - - void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); - void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); - void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); - - QQuickItem *m_item; - QSGRendererInterface::GraphicsApi m_api; - QQuickPathItemGenericNode *m_rootNode; - QVector m_vp; - int m_accDirty; - void (*m_asyncCallback)(void *); - void *m_asyncCallbackData; -}; - -class QQuickPathItemFillRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QQuickPathItemGenericRenderer::Color4ub fillColor; - bool supportsElementIndexUint; - - // output - QQuickPathItemGenericRenderer::VertexContainerType fillVertices; - QQuickPathItemGenericRenderer::IndexContainerType fillIndices; - QSGGeometry::Type indexType; - -Q_SIGNALS: - void done(QQuickPathItemFillRunnable *self); -}; - -class QQuickPathItemStrokeRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QPen pen; - QQuickPathItemGenericRenderer::Color4ub strokeColor; - QSize clipSize; - - // output - QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; - -Q_SIGNALS: - void done(QQuickPathItemStrokeRunnable *self); -}; - -class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode -{ -public: - QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); - ~QQuickPathItemGenericStrokeFillNode(); - - enum Material { - MatSolidColor, - MatLinearGradient - }; - - void activateMaterial(Material m); - - QQuickWindow *window() const { return m_window; } - - // shadow data for custom materials - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - -private: - QSGGeometry *m_geometry; - QQuickWindow *m_window; - QSGMaterial *m_material; - QScopedPointer m_solidColorMaterial; - QScopedPointer m_linearGradientMaterial; - - friend class QQuickPathItemGenericRenderer; -}; - -class QQuickPathItemGenericNode : public QSGNode -{ -public: - QQuickPathItemGenericStrokeFillNode *m_fillNode = nullptr; - QQuickPathItemGenericStrokeFillNode *m_strokeNode = nullptr; - QQuickPathItemGenericNode *m_next = nullptr; -}; - -class QQuickPathItemGenericMaterialFactory -{ -public: - static QSGMaterial *createVertexColor(QQuickWindow *window); - static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericStrokeFillNode *node); -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemLinearGradientShader : public QSGMaterialShader -{ -public: - QQuickPathItemLinearGradientShader(); - - void initialize() override; - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; - char const *const *attributeNames() const override; - - static QSGMaterialType type; - -private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; -}; - -class QQuickPathItemLinearGradientMaterial : public QSGMaterial -{ -public: - QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) - : m_node(node) - { - // Passing RequiresFullMatrix is essential in order to prevent the - // batch renderer from baking in simple, translate-only transforms into - // the vertex data. The shader will rely on the fact that - // vertexCoord.xy is the PathItem-space coordinate and so no modifications - // are welcome. - setFlag(Blending | RequiresFullMatrix); - } - - QSGMaterialType *type() const override - { - return &QQuickPathItemLinearGradientShader::type; - } - - int compare(const QSGMaterial *other) const override; - - QSGMaterialShader *createShader() const override - { - return new QQuickPathItemLinearGradientShader; - } - - QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } - -private: - QQuickPathItemGenericStrokeFillNode *m_node; -}; - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/quick/items/qquickpathitemnvprrenderer.cpp b/src/quick/items/qquickpathitemnvprrenderer.cpp deleted file mode 100644 index f8504f9985..0000000000 --- a/src/quick/items/qquickpathitemnvprrenderer.cpp +++ /dev/null @@ -1,923 +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 "qquickpathitemnvprrenderer_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemNvprRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - convertPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - convertJSPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = fillRule; - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.joinStyle = joinStyle; - d.miterLimit = miterLimit; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.capStyle = capStyle; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - d.dashActive = strokeStyle == QQuickVisualPath::DashLine; - d.dashOffset = dashOffset; - d.dashPattern = dashPattern; - d.dirty |= DirtyDash; - m_accDirty |= DirtyDash; -} - -void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.dirty |= DirtyFillGradient; - m_accDirty |= DirtyFillGradient; -} - -void QQuickPathItemNvprRenderer::endSync(bool) -{ -} - -void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path) -{ - QDebugStateSaver saver(debug); - debug.space().noquote(); - if (!path.str.isEmpty()) { - debug << "Path with SVG string" << path.str; - return debug; - } - debug << "Path with" << path.cmd.count() << "commands"; - int ci = 0; - for (GLubyte cmd : path.cmd) { - static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { - { GL_MOVE_TO_NV, "moveTo", 2 }, - { GL_LINE_TO_NV, "lineTo", 2 }, - { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, - { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, - { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, - { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, - { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, - { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, - { GL_CLOSE_PATH_NV, "closePath", 0 } }; - for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { - if (nameTab[i].cmd == cmd) { - QByteArray cs; - for (int j = 0; j < nameTab[i].coordCount; ++j) { - cs.append(QByteArray::number(path.coord[ci++])); - cs.append(' '); - } - debug << "\n " << nameTab[i].s << " " << cs; - break; - } - } - } - return debug; -} - -static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) -{ - QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), - c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); - v->append(p.x()); - v->append(p.y()); - *pos = p; -} - -static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), - c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), - c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), - c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); - v->append(p.x()); - v->append(p.y()); -} - -void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (!path) - return; - - const QList &pp(QQuickPathPrivate::get(path)->_pathElements); - if (pp.isEmpty()) - return; - - QPointF startPos(path->startX(), path->startY()); - QPointF pos(startPos); - if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { - d->path.cmd.append(GL_MOVE_TO_NV); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - } - - for (QQuickPathElement *e : pp) { - if (QQuickPathMove *o = qobject_cast(e)) { - d->path.cmd.append(GL_MOVE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - startPos = pos; - } else if (QQuickPathLine *o = qobject_cast(e)) { - d->path.cmd.append(GL_LINE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathQuad *o = qobject_cast(e)) { - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - appendControlCoords(&d->path.coord, o, pos); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathCubic *o = qobject_cast(e)) { - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - appendControl1Coords(&d->path.coord, o, pos); - appendControl2Coords(&d->path.coord, o, pos); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathArc *o = qobject_cast(e)) { - const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo - GLenum cmd; - if (o->useLargeArc()) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(o->radiusX()); - d->path.coord.append(o->radiusY()); - d->path.coord.append(o->xAxisRotation()); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathSvg *o = qobject_cast(e)) { - // PathSvg cannot be combined with other elements. But take at - // least startX and startY into account. - if (d->path.str.isEmpty()) - d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); - d->path.str.append(o->path().toUtf8()); - } else { - qWarning() << "PathItem/NVPR: unsupported Path element" << e; - } - } - - // For compatibility with QTriangulatingStroker. SVG and others would not - // implicitly close the path when end_pos == start_pos (start_pos being the - // last moveTo pos); that would still need an explicit 'z' or similar. We - // don't have an explicit close command, so just fake a close when the - // positions match. - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (path.cmd.isEmpty()) - return; - - QPointF startPos(0, 0); - QPointF pos(startPos); - int coordIdx = 0; - - for (QQuickPathItemPath::Command cmd : path.cmd) { - switch (cmd) { - case QQuickPathItemPath::MoveTo: - d->path.cmd.append(GL_MOVE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - startPos = pos; - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - d->path.cmd.append(GL_LINE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - d->path.coord.append(path.coords[coordIdx + 2]); - d->path.coord.append(path.coords[coordIdx + 3]); - pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - { - const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); - const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); - GLenum cmd; - if (useLargeArc) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(path.coords[coordIdx]); // rx - d->path.coord.append(path.coords[coordIdx + 1]); // ry - d->path.coord.append(path.coords[coordIdx + 2]); // xrot - pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 7; - } - break; - default: - qWarning("Unknown JS path command: %d", cmd); - break; - } - } - - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) -{ - const float o = c.alphaF() * globalOpacity; - return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); -} - -void QQuickPathItemNvprRenderer::updateNode() -{ - // Called on the render thread with gui blocked -> update the node with its - // own copy of all relevant data. - - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemNvprRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - int dirty = src.dirty; - src.dirty = 0; - if (listChanged) - dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; - - // updateNode() can be called several times with different dirty - // states before render() gets invoked. So accumulate. - dst.dirty |= dirty; - - if (dirty & DirtyPath) - dst.source = src.path; - - if (dirty & DirtyStyle) { - dst.strokeWidth = src.strokeWidth; - dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); - dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); - switch (src.joinStyle) { - case QQuickVisualPath::MiterJoin: - dst.joinStyle = GL_MITER_TRUNCATE_NV; - break; - case QQuickVisualPath::BevelJoin: - dst.joinStyle = GL_BEVEL_NV; - break; - case QQuickVisualPath::RoundJoin: - dst.joinStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); - } - dst.miterLimit = src.miterLimit; - switch (src.capStyle) { - case QQuickVisualPath::FlatCap: - dst.capStyle = GL_FLAT; - break; - case QQuickVisualPath::SquareCap: - dst.capStyle = GL_SQUARE_NV; - break; - case QQuickVisualPath::RoundCap: - dst.capStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); - } - } - - if (dirty & DirtyFillRule) { - switch (src.fillRule) { - case QQuickVisualPath::OddEvenFill: - dst.fillRule = GL_INVERT; - break; - case QQuickVisualPath::WindingFill: - dst.fillRule = GL_COUNT_UP_NV; - break; - default: - Q_UNREACHABLE(); - } - } - - if (dirty & DirtyDash) { - dst.dashOffset = src.dashOffset; - if (src.dashActive) { - dst.dashPattern.resize(src.dashPattern.count()); - // Multiply by strokeWidth because the PathItem API follows QPen - // meaning the input dash pattern here is in width units. - for (int i = 0; i < src.dashPattern.count(); ++i) - dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; - } else { - dst.dashPattern.clear(); - } - } - - if (dirty & DirtyFillGradient) { - dst.fillGradientActive = src.fillGradientActive; - if (src.fillGradientActive) - dst.fillGradient = src.fillGradient; - } - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -bool QQuickPathItemNvprRenderNode::nvprInited = false; -QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; -QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; - -QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemNvprRenderNode::releaseResources() -{ - for (VisualPathRenderData &d : m_vp) { - if (d.path) { - nvpr.deletePaths(d.path, 1); - d.path = 0; - } - if (d.fallbackFbo) { - delete d.fallbackFbo; - d.fallbackFbo = nullptr; - } - } - - m_fallbackBlitter.destroy(); -} - -void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) -{ - m_nvpr = nvpr; -} - -void QQuickNvprMaterialManager::releaseResources() -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - for (MaterialDesc &mtl : m_materials) { - if (mtl.ppl) { - f->glDeleteProgramPipelines(1, &mtl.ppl); - mtl = MaterialDesc(); - } - } -} - -QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - MaterialDesc &mtl(m_materials[m]); - - if (!mtl.ppl) { - if (m == MatSolid) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "out vec4 fragColor;\n" - "uniform vec4 color;\n" - "uniform float opacity;\n" - "void main() {\n" - " fragColor = color * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for solid fill"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); - Q_ASSERT(mtl.uniLoc[0] >= 0); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - } else if (m == MatLinearGradient) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "layout(location = 0) in vec2 uv;" - "uniform float opacity;\n" - "uniform sampler2D gradTab;\n" - "uniform vec2 gradStart;\n" - "uniform vec2 gradEnd;\n" - "out vec4 fragColor;\n" - "void main() {\n" - " vec2 gradVec = gradEnd - gradStart;\n" - " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" - " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for linear gradient"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); - Q_ASSERT(mtl.uniLoc[2] >= 0); - mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); - Q_ASSERT(mtl.uniLoc[3] >= 0); - } else { - Q_UNREACHABLE(); - } - } - - f->glBindProgramPipeline(mtl.ppl); - - return &mtl; -} - -void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) -{ - if (d->dirty & QQuickPathItemNvprRenderer::DirtyPath) { - if (!d->path) { - d->path = nvpr.genPaths(1); - Q_ASSERT(d->path != 0); - } - if (d->source.str.isEmpty()) { - nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), - d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); - } else { - nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); - } - } - - if (d->dirty & QQuickPathItemNvprRenderer::DirtyStyle) { - nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); - nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); - nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); - nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); - nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); - } - - if (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { - nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); - // count == 0 -> no dash - nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); - } - - if (d->dirty) - d->fallbackValid = false; -} - -void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); -} - -void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; - if (d->fillGradientActive) { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(d->fillGradient); - tx->bind(); - // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) - // where x and y are in path coordinate space, which is just what - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); - } else { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); - } - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - const int writeMask = 0xFF; - nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); -} - -void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) -{ - if (d->fallbackValid && d->fallbackFbo) - return; - - GLfloat bb[4]; - nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); - QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); - d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); - d->fallbackTopLeft = QPointF(bb[0], bb[1]); - - if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { - delete d->fallbackFbo; - d->fallbackFbo = nullptr; - } - if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); - if (!d->fallbackFbo->bind()) - return; - - GLint prevViewport[4]; - f->glGetIntegerv(GL_VIEWPORT, prevViewport); - - f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); - f->glDisable(GL_DEPTH_TEST); - f->glClearColor(0, 0, 0, 0); - f->glClearStencil(0); - f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - QMatrix4x4 mv; - mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); - QMatrix4x4 proj; - proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); - - renderFill(d); - - d->fallbackFbo->release(); - f->glEnable(GL_DEPTH_TEST); - f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); - - d->fallbackValid = true; -} - -void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) -{ - if (!stencilClip) { - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - } else { - f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) - f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) - } -} - -void QQuickPathItemNvprRenderNode::render(const RenderState *state) -{ - f = QOpenGLContext::currentContext()->extraFunctions(); - - if (!nvprInited) { - if (!nvpr.create()) { - qWarning("NVPR init failed"); - return; - } - mtlmgr.create(&nvpr); - nvprInited = true; - } - - f->glUseProgram(0); - f->glStencilMask(~0); - f->glEnable(GL_STENCIL_TEST); - - const bool stencilClip = state->stencilEnabled(); - // when true, the stencil buffer already has a clip path with a ref value of sv - const int sv = state->stencilValue(); - const bool hasScissor = state->scissorEnabled(); - - if (hasScissor) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); - } - - // Depth test against the opaque batches rendered before. - f->glEnable(GL_DEPTH_TEST); - f->glDepthFunc(GL_LESS); - nvpr.pathCoverDepthFunc(GL_LESS); - nvpr.pathStencilDepthOffset(-0.05f, -1); - - bool reloadMatrices = true; - - for (VisualPathRenderData &d : m_vp) { - updatePath(&d); - - const bool hasFill = d.hasFill(); - const bool hasStroke = d.hasStroke(); - - if (hasFill && stencilClip) { - // Fall back to a texture when complex clipping is in use and we have - // to fill. Reconciling glStencilFillPath's and the scenegraph's clip - // stencil semantics has not succeeded so far... - if (hasScissor) - f->glDisable(GL_SCISSOR_TEST); - renderOffscreenFill(&d); - reloadMatrices = true; - if (hasScissor) - f->glEnable(GL_SCISSOR_TEST); - } - - if (reloadMatrices) { - reloadMatrices = false; - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - } - - // Fill! - if (hasFill) { - if (!stencilClip) { - setupStencilForCover(false, 0); - renderFill(&d); - } else { - if (!m_fallbackBlitter.isCreated()) - m_fallbackBlitter.create(); - f->glStencilFunc(GL_EQUAL, sv, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - QMatrix4x4 mv = *matrix(); - mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); - m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), mv, - inheritedOpacity()); - } - } - - // Stroke! - if (hasStroke) { - const int strokeStencilValue = 0x80; - const int writeMask = 0x80; - - setupStencilForCover(stencilClip, sv); - if (stencilClip) { - // for the stencil step (eff. read mask == 0xFF & ~writeMask) - nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); - // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. - // This assumes the clip stencil value is <= 127. - if (sv >= strokeStencilValue) - qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); - } - - renderStroke(&d, strokeStencilValue, writeMask); - } - - if (stencilClip) - nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); - - d.dirty = 0; - } - - f->glBindProgramPipeline(0); -} - -QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const -{ - return BlendState | StencilState | DepthState | ScissorState; -} - -QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const -{ - return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer -} - -bool QQuickPathItemNvprRenderNode::isSupported() -{ - static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; - return !nvprDisabled && QQuickNvprFunctions::isSupported(); -} - -bool QQuickNvprBlitter::create() -{ - if (isCreated()) - destroy(); - - m_program = new QOpenGLShaderProgram; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); - } else { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); - } - m_program->bindAttributeLocation("qt_Vertex", 0); - m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); - if (!m_program->link()) - return false; - - m_matrixLoc = m_program->uniformLocation("qt_Matrix"); - m_opacityLoc = m_program->uniformLocation("qt_Opacity"); - - m_buffer = new QOpenGLBuffer; - if (!m_buffer->create()) - return false; - m_buffer->bind(); - m_buffer->allocate(4 * sizeof(GLfloat) * 6); - m_buffer->release(); - - return true; -} - -void QQuickNvprBlitter::destroy() -{ - if (m_program) { - delete m_program; - m_program = nullptr; - } - if (m_buffer) { - delete m_buffer; - m_buffer = nullptr; - } -} - -void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - - m_program->bind(); - - QMatrix4x4 m = proj * modelview; - m_program->setUniformValue(m_matrixLoc, m); - m_program->setUniformValue(m_opacityLoc, opacity); - - m_buffer->bind(); - - if (size != m_prevSize) { - m_prevSize = size; - - QPointF p0(size.width() - 1, size.height() - 1); - QPointF p1(0, 0); - QPointF p2(0, size.height() - 1); - QPointF p3(size.width() - 1, 0); - - GLfloat vertices[6 * 4] = { - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, - - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - }; - - m_buffer->write(0, vertices, sizeof(vertices)); - } - - m_program->enableAttributeArray(0); - m_program->enableAttributeArray(1); - f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); - f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); - - f->glBindTexture(GL_TEXTURE_2D, textureId); - - f->glDrawArrays(GL_TRIANGLES, 0, 6); - - f->glBindTexture(GL_TEXTURE_2D, 0); - m_buffer->release(); - m_program->release(); -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemnvprrenderer_p.h b/src/quick/items/qquickpathitemnvprrenderer_p.h deleted file mode 100644 index deab9cf7f9..0000000000 --- a/src/quick/items/qquickpathitemnvprrenderer_p.h +++ /dev/null @@ -1,237 +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 QQUICKPATHITEMNVPRRENDERER_P_H -#define QQUICKPATHITEMNVPRRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -class QQuickPathItemNvprRenderNode; -class QOpenGLFramebufferObject; -class QOpenGLBuffer; -class QOpenGLExtraFunctions; - -class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStyle = 0x02, - DirtyFillRule = 0x04, - DirtyDash = 0x08, - DirtyFillGradient = 0x10, - DirtyList = 0x20 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemNvprRenderNode *node); - - struct NvprPath { - QVector cmd; - QVector coord; - QByteArray str; - }; - -private: - struct VisualPathGuiData { - int dirty = 0; - NvprPath path; - qreal strokeWidth; - QColor strokeColor; - QColor fillColor; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::FillRule fillRule; - bool dashActive; - qreal dashOffset; - QVector dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - }; - - void convertPath(const QQuickPath *path, VisualPathGuiData *d); - void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); - - QQuickPathItemNvprRenderNode *m_node = nullptr; - int m_accDirty = 0; - - QVector m_vp; -}; - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); - -class QQuickNvprMaterialManager -{ -public: - enum Material { - MatSolid, - MatLinearGradient, - - NMaterials - }; - - struct MaterialDesc { - GLuint ppl = 0; - GLuint prg = 0; - int uniLoc[4]; - }; - - void create(QQuickNvprFunctions *nvpr); - MaterialDesc *activateMaterial(Material m); - void releaseResources(); - -private: - QQuickNvprFunctions *m_nvpr; - MaterialDesc m_materials[NMaterials]; -}; - -class QQuickNvprBlitter -{ -public: - bool create(); - void destroy(); - bool isCreated() const { return m_program != nullptr; } - void texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity); - -private: - QOpenGLShaderProgram *m_program = nullptr; - QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; - QSize m_prevSize; -}; - -class QQuickPathItemNvprRenderNode : public QSGRenderNode -{ -public: - ~QQuickPathItemNvprRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - - static bool isSupported(); - -private: - struct VisualPathRenderData { - GLuint path = 0; - int dirty = 0; - QQuickPathItemNvprRenderer::NvprPath source; - GLfloat strokeWidth; - QVector4D strokeColor; - QVector4D fillColor; - GLenum joinStyle; - GLint miterLimit; - GLenum capStyle; - GLenum fillRule; - GLfloat dashOffset; - QVector dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - QOpenGLFramebufferObject *fallbackFbo = nullptr; - bool fallbackValid = false; - QSize fallbackSize; - QPointF fallbackTopLeft; - - bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } - bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } - }; - - void updatePath(VisualPathRenderData *d); - void renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask); - void renderFill(VisualPathRenderData *d); - void renderOffscreenFill(VisualPathRenderData *d); - void setupStencilForCover(bool stencilClip, int sv); - - static bool nvprInited; - static QQuickNvprFunctions nvpr; - static QQuickNvprMaterialManager mtlmgr; - - QQuickNvprBlitter m_fallbackBlitter; - QOpenGLExtraFunctions *f = nullptr; - - QVector m_vp; - - friend class QQuickPathItemNvprRenderer; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/quick/items/qquickpathitemsoftwarerenderer.cpp b/src/quick/items/qquickpathitemsoftwarerenderer.cpp deleted file mode 100644 index b7aa93bf65..0000000000 --- a/src/quick/items/qquickpathitemsoftwarerenderer.cpp +++ /dev/null @@ -1,277 +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 "qquickpathitemsoftwarerenderer_p.h" -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setColor(color); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.brush.setColor(color); - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - switch (strokeStyle) { - case QQuickVisualPath::SolidLine: - d.pen.setStyle(Qt::SolidLine); - break; - case QQuickVisualPath::DashLine: - d.pen.setStyle(Qt::CustomDashLine); - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - break; - default: - break; - } - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { - QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), - linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); - switch (gradient->spread()) { - case QQuickPathGradient::PadSpread: - painterGradient.setSpread(QGradient::PadSpread); - break; - case QQuickPathGradient::RepeatSpread: - painterGradient.setSpread(QGradient::RepeatSpread); - break; - case QQuickPathGradient::ReflectSpread: - painterGradient.setSpread(QGradient::ReflectSpread); - break; - default: - break; - } - d.brush = QBrush(painterGradient); - } else { - d.brush = QBrush(d.fillColor); - } - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::endSync(bool) -{ -} - -void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::updateNode() -{ - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - m_node->m_boundingRect = QRectF(); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - if (listChanged || (src.dirty & DirtyPath)) { - dst.path = src.path; - dst.path.setFillRule(src.fillRule); - } - - if (listChanged || (src.dirty & DirtyFillRule)) - dst.path.setFillRule(src.fillRule); - - if (listChanged || (src.dirty & DirtyPen)) { - dst.pen = src.pen; - dst.strokeWidth = src.strokeWidth; - } - - if (listChanged || (src.dirty & DirtyBrush)) - dst.brush = src.brush; - - src.dirty = 0; - - QRectF br = dst.path.boundingRect(); - const float sw = qMax(1.0f, dst.strokeWidth); - br.adjust(-sw, -sw, sw, sw); - m_node->m_boundingRect |= br; - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - -QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemSoftwareRenderNode::releaseResources() -{ -} - -void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) -{ - if (m_vp.isEmpty()) - return; - - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); - Q_ASSERT(p); - - const QRegion *clipRegion = state->clipRegion(); - if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform - - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - - for (const VisualPathRenderData &d : qAsConst(m_vp)) { - p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); - p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); - p->drawPath(d.path); - } -} - -QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const -{ - return 0; -} - -QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const -{ - return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() -} - -QRectF QQuickPathItemSoftwareRenderNode::rect() const -{ - return m_boundingRect; -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickpathitemsoftwarerenderer_p.h b/src/quick/items/qquickpathitemsoftwarerenderer_p.h deleted file mode 100644 index e76590bdfe..0000000000 --- a/src/quick/items/qquickpathitemsoftwarerenderer_p.h +++ /dev/null @@ -1,136 +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 QQUICKPATHITEMSOFTWARERENDERER_P_H -#define QQUICKPATHITEMSOFTWARERENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemSoftwareRenderNode; - -class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyPen = 0x02, - DirtyFillRule = 0x04, - DirtyBrush = 0x08, - DirtyList = 0x10 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemSoftwareRenderNode *node); - -private: - QQuickPathItemSoftwareRenderNode *m_node = nullptr; - int m_accDirty = 0; - struct VisualPathGuiData { - int dirty = 0; - QPainterPath path; - QPen pen; - float strokeWidth; - QColor fillColor; - QBrush brush; - Qt::FillRule fillRule; - }; - QVector m_vp; -}; - -class QQuickPathItemSoftwareRenderNode : public QSGRenderNode -{ -public: - QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); - ~QQuickPathItemSoftwareRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - QRectF rect() const override; - -private: - QQuickPathItem *m_item; - - struct VisualPathRenderData { - QPainterPath path; - QPen pen; - float strokeWidth; - QBrush brush; - }; - QVector m_vp; - QRectF m_boundingRect; - - friend class QQuickPathItemSoftwareRenderer; -}; - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/quick/util/qquicknvprfunctions.cpp b/src/quick/util/qquicknvprfunctions.cpp deleted file mode 100644 index 40eb2bb932..0000000000 --- a/src/quick/util/qquicknvprfunctions.cpp +++ /dev/null @@ -1,284 +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 "qquicknvprfunctions_p.h" - -#ifndef QT_NO_OPENGL - -#include -#include -#include -#include "qquicknvprfunctions_p_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QQuickNvprFunctions - - \brief Function resolvers and other helpers for GL_NV_path_rendering - for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner - that does not distract builds that do not have NVPR support either at - compile or run time. - - \internal - */ - -QQuickNvprFunctions::QQuickNvprFunctions() - : d(new QQuickNvprFunctionsPrivate(this)) -{ -} - -QQuickNvprFunctions::~QQuickNvprFunctions() -{ - delete d; -} - -/*! - \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top - of OpenGL 4.3 or OpenGL ES 3.1. - */ -QSurfaceFormat QQuickNvprFunctions::format() -{ - QSurfaceFormat fmt; - fmt.setDepthBufferSize(24); - fmt.setStencilBufferSize(8); - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - fmt.setVersion(3, 1); - } - return fmt; -} - -/*! - \return true if GL_NV_path_rendering is supported with the current OpenGL - context. - - When there is no current context, a temporary dummy one will be created and - made current. - */ -bool QQuickNvprFunctions::isSupported() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - QScopedPointer tempContext; - QScopedPointer tempSurface; - if (!ctx) { - tempContext.reset(new QOpenGLContext); - if (!tempContext->create()) - return false; - ctx = tempContext.data(); - tempSurface.reset(new QOffscreenSurface); - tempSurface->setFormat(ctx->format()); - tempSurface->create(); - if (!ctx->makeCurrent(tempSurface.data())) - return false; - } - - if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) - return false; - - // Do not check for DSA as the string may not be exposed on ES - // drivers, yet the functions we need are resolvable. -#if 0 - if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { - qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); - return false; - } -#endif - - return true; -} - -/*! - Initializes using the current OpenGL context. - - \return true when GL_NV_path_rendering is supported and initialization was - successful. - */ -bool QQuickNvprFunctions::create() -{ - return isSupported() && d->resolve(); -} - -/*! - Creates a program pipeline consisting of a separable fragment shader program. - - This is essential for using NVPR with OpenGL ES 3.1+ since normal, - GLES2-style programs would not work without a vertex shader. - - \note \a fragmentShaderSource should be a \c{version 310 es} shader since - this works both on desktop and embedded NVIDIA drivers, thus avoiding the - need to fight GLSL and GLSL ES differences. - - The pipeline object is stored into \a pipeline, the fragment shader program - into \a program. - - Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. - - \return \c false on failure in which case the error log is printed on the - debug output. \c true on success. - */ -bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (!ctx) - return false; - - QOpenGLExtraFunctions *f = ctx->extraFunctions(); - *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); - GLint status = 0; - f->glGetProgramiv(*program, GL_LINK_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); - qWarning("Failed to create separable shader program:\n%s", s.constData()); - } - return false; - } - - f->glGenProgramPipelines(1, pipeline); - f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); - f->glActiveShaderProgram(*pipeline, *program); - - f->glValidateProgramPipeline(*pipeline); - status = 0; - f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); - qWarning("Program pipeline validation failed:\n%s", s.constData()); - } - return false; - } - - return true; -} - -#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) - -bool QQuickNvprFunctionsPrivate::resolve() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - - q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); - q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); - q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); - q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); - q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); - q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); - q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); - q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); - q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); - q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); - q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); - q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); - q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); - q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); - q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); - q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); - q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); - q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); - q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); - q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); - q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); - q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); - q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); - q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); - q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); - q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); - q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); - q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); - q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); - q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); - q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); - q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); - q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); - q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); - q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); - q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); - q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); - q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); - q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); - q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); - q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); - q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); - q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); - q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); - q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); - q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); - q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); - q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); - q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); - q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); - q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); - q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); - q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); - q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); - q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); - q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); - q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); - q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); - q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); - q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); - q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); - q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); - q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); - q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); - - q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); - q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); - - return q->genPaths != nullptr // base path rendering ext - && q->programPathFragmentInputGen != nullptr // updated path rendering ext - && q->matrixLoadf != nullptr // direct state access ext - && q->matrixLoadIdentity != nullptr; -} - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL diff --git a/src/quick/util/qquicknvprfunctions_p.h b/src/quick/util/qquicknvprfunctions_p.h deleted file mode 100644 index 7900388305..0000000000 --- a/src/quick/util/qquicknvprfunctions_p.h +++ /dev/null @@ -1,406 +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 QQUICKNVPRFUNCTIONS_P_H -#define QQUICKNVPRFUNCTIONS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -#ifndef GL_NV_path_rendering -#define GL_PATH_FORMAT_SVG_NV 0x9070 -#define GL_PATH_FORMAT_PS_NV 0x9071 -#define GL_STANDARD_FONT_NAME_NV 0x9072 -#define GL_SYSTEM_FONT_NAME_NV 0x9073 -#define GL_FILE_NAME_NV 0x9074 -#define GL_PATH_STROKE_WIDTH_NV 0x9075 -#define GL_PATH_END_CAPS_NV 0x9076 -#define GL_PATH_INITIAL_END_CAP_NV 0x9077 -#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 -#define GL_PATH_JOIN_STYLE_NV 0x9079 -#define GL_PATH_MITER_LIMIT_NV 0x907A -#define GL_PATH_DASH_CAPS_NV 0x907B -#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C -#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D -#define GL_PATH_DASH_OFFSET_NV 0x907E -#define GL_PATH_CLIENT_LENGTH_NV 0x907F -#define GL_PATH_FILL_MODE_NV 0x9080 -#define GL_PATH_FILL_MASK_NV 0x9081 -#define GL_PATH_FILL_COVER_MODE_NV 0x9082 -#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 -#define GL_PATH_STROKE_MASK_NV 0x9084 -#define GL_COUNT_UP_NV 0x9088 -#define GL_COUNT_DOWN_NV 0x9089 -#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A -#define GL_CONVEX_HULL_NV 0x908B -#define GL_BOUNDING_BOX_NV 0x908D -#define GL_TRANSLATE_X_NV 0x908E -#define GL_TRANSLATE_Y_NV 0x908F -#define GL_TRANSLATE_2D_NV 0x9090 -#define GL_TRANSLATE_3D_NV 0x9091 -#define GL_AFFINE_2D_NV 0x9092 -#define GL_AFFINE_3D_NV 0x9094 -#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 -#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 -#define GL_UTF8_NV 0x909A -#define GL_UTF16_NV 0x909B -#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C -#define GL_PATH_COMMAND_COUNT_NV 0x909D -#define GL_PATH_COORD_COUNT_NV 0x909E -#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F -#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 -#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 -#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 -#define GL_SQUARE_NV 0x90A3 -#define GL_ROUND_NV 0x90A4 -#define GL_TRIANGULAR_NV 0x90A5 -#define GL_BEVEL_NV 0x90A6 -#define GL_MITER_REVERT_NV 0x90A7 -#define GL_MITER_TRUNCATE_NV 0x90A8 -#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 -#define GL_USE_MISSING_GLYPH_NV 0x90AA -#define GL_PATH_ERROR_POSITION_NV 0x90AB -#define GL_PATH_FOG_GEN_MODE_NV 0x90AC -#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD -#define GL_ADJACENT_PAIRS_NV 0x90AE -#define GL_FIRST_TO_REST_NV 0x90AF -#define GL_PATH_GEN_MODE_NV 0x90B0 -#define GL_PATH_GEN_COEFF_NV 0x90B1 -#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 -#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 -#define GL_PATH_STENCIL_FUNC_NV 0x90B7 -#define GL_PATH_STENCIL_REF_NV 0x90B8 -#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 -#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD -#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE -#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF -#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 -#define GL_MOVE_TO_RESETS_NV 0x90B5 -#define GL_MOVE_TO_CONTINUES_NV 0x90B6 -#define GL_CLOSE_PATH_NV 0x00 -#define GL_MOVE_TO_NV 0x02 -#define GL_RELATIVE_MOVE_TO_NV 0x03 -#define GL_LINE_TO_NV 0x04 -#define GL_RELATIVE_LINE_TO_NV 0x05 -#define GL_HORIZONTAL_LINE_TO_NV 0x06 -#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 -#define GL_VERTICAL_LINE_TO_NV 0x08 -#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 -#define GL_QUADRATIC_CURVE_TO_NV 0x0A -#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B -#define GL_CUBIC_CURVE_TO_NV 0x0C -#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D -#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E -#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F -#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 -#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 -#define GL_SMALL_CCW_ARC_TO_NV 0x12 -#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 -#define GL_SMALL_CW_ARC_TO_NV 0x14 -#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 -#define GL_LARGE_CCW_ARC_TO_NV 0x16 -#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 -#define GL_LARGE_CW_ARC_TO_NV 0x18 -#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 -#define GL_RESTART_PATH_NV 0xF0 -#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 -#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 -#define GL_RECT_NV 0xF6 -#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 -#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA -#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC -#define GL_ARC_TO_NV 0xFE -#define GL_RELATIVE_ARC_TO_NV 0xFF -#define GL_BOLD_BIT_NV 0x01 -#define GL_ITALIC_BIT_NV 0x02 -#define GL_GLYPH_WIDTH_BIT_NV 0x01 -#define GL_GLYPH_HEIGHT_BIT_NV 0x02 -#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 -#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 -#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 -#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 -#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 -#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 -#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 -#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 -#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 -#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 -#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 -#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 -#define GL_FONT_ASCENDER_BIT_NV 0x00200000 -#define GL_FONT_DESCENDER_BIT_NV 0x00400000 -#define GL_FONT_HEIGHT_BIT_NV 0x00800000 -#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 -#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 -#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 -#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 -#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 -#define GL_PRIMARY_COLOR_NV 0x852C -#define GL_SECONDARY_COLOR_NV 0x852D -#define GL_ROUNDED_RECT_NV 0xE8 -#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 -#define GL_ROUNDED_RECT2_NV 0xEA -#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB -#define GL_ROUNDED_RECT4_NV 0xEC -#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED -#define GL_ROUNDED_RECT8_NV 0xEE -#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF -#define GL_RELATIVE_RECT_NV 0xF7 -#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 -#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 -#define GL_FONT_UNAVAILABLE_NV 0x936A -#define GL_FONT_UNINTELLIGIBLE_NV 0x936B -#define GL_CONIC_CURVE_TO_NV 0x1A -#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B -#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 -#define GL_STANDARD_FONT_FORMAT_NV 0x936C -#define GL_2_BYTES_NV 0x1407 -#define GL_3_BYTES_NV 0x1408 -#define GL_4_BYTES_NV 0x1409 -#define GL_EYE_LINEAR_NV 0x2400 -#define GL_OBJECT_LINEAR_NV 0x2401 -#define GL_CONSTANT_NV 0x8576 -#define GL_PATH_PROJECTION_NV 0x1701 -#define GL_PATH_MODELVIEW_NV 0x1700 -#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 -#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 -#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 -#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 -#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 -#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 -#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 -#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 -#define GL_FRAGMENT_INPUT_NV 0x936D - -typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); -typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); -typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); -typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); -#endif - -#ifndef GL_FLAT -#define GL_FLAT 0x1D00 -#endif - -#ifndef GL_INVERT -#define GL_INVERT 0x150A -#endif - -#ifndef GL_EXT_direct_state_access -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); -#endif - -// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR -// code path even though it's never used. Keep it compiling by defining the -// necessary ES 3.1 separable program constants. -#ifndef GL_FRAGMENT_SHADER_BIT -#define GL_FRAGMENT_SHADER_BIT 0x00000002 -#endif -#ifndef GL_UNIFORM -#define GL_UNIFORM 0x92E1 -#endif - -class QQuickNvprFunctionsPrivate; - -class QQuickNvprFunctions -{ -public: - QQuickNvprFunctions(); - ~QQuickNvprFunctions(); - - static QSurfaceFormat format(); - static bool isSupported(); - - bool create(); - - bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); - - PFNGLGENPATHSNVPROC genPaths = nullptr; - PFNGLDELETEPATHSNVPROC deletePaths = nullptr; - PFNGLISPATHNVPROC isPath = nullptr; - PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; - PFNGLPATHCOORDSNVPROC pathCoords = nullptr; - PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; - PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; - PFNGLPATHSTRINGNVPROC pathString = nullptr; - PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; - PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; - PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; - PFNGLCOPYPATHNVPROC copyPath = nullptr; - PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; - PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; - PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; - PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; - PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; - PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; - PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; - PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; - PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; - PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; - PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; - PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; - PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; - PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; - PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; - PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; - PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; - PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; - PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; - PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; - PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; - PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; - PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; - PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; - PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; - PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; - PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; - PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; - PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; - PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; - PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; - PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; - PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; - PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; - PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; - PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; - PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; - PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; - PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; - PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; - PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; - PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; - PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; - PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; - PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; - PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; - PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; - PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; - - PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; - PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; - -private: - QQuickNvprFunctionsPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/quick/util/qquicknvprfunctions_p_p.h b/src/quick/util/qquicknvprfunctions_p_p.h deleted file mode 100644 index 6df20566af..0000000000 --- a/src/quick/util/qquicknvprfunctions_p_p.h +++ /dev/null @@ -1,70 +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 QQUICKNVPRFUNCTIONS_P_P_H -#define QQUICKNVPRFUNCTIONS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquicknvprfunctions_p.h" - -QT_BEGIN_NAMESPACE - -class QQuickNvprFunctionsPrivate -{ -public: - QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } - - bool resolve(); - - QQuickNvprFunctions *q; -}; - -QT_END_NAMESPACE - -#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h index 44b0d1b6dd..1777b99bf4 100644 --- a/src/quick/util/qquicksvgparser_p.h +++ b/src/quick/util/qquicksvgparser_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include #include #include @@ -59,9 +60,9 @@ QT_BEGIN_NAMESPACE namespace QQuickSvgParser { bool parsePathDataFast(const QString &dataStr, QPainterPath &path); - void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, - int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, - qreal cury); + Q_QUICK_PRIVATE_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, + int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, + qreal cury); } QT_END_NAMESPACE diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 56eb8ea3b7..b53b132cce 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -79,11 +79,4 @@ qtConfig(quick-path) { $$PWD/qquickpath_p.h \ $$PWD/qquickpath_p_p.h \ $$PWD/qquickpathinterpolator_p.h - qtConfig(opengl) { - SOURCES += \ - $$PWD/qquicknvprfunctions.cpp - HEADERS += \ - $$PWD/qquicknvprfunctions_p.h \ - $$PWD/qquicknvprfunctions_p_p.h - } } -- cgit v1.2.3 From 5519ba509b7d5f56bab099ad2d414d50d5d14fc9 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 25 Apr 2017 17:55:27 +0200 Subject: Remove redundant entries Sort the entries to ease lookup Change-Id: I306ca508e8b382b9a60fe737f28f5fa2c9471dc1 Reviewed-by: Shawn Rutledge --- src/quick/handlers/handlers.pri | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri index 749e4cc4e0..17066bb33a 100644 --- a/src/quick/handlers/handlers.pri +++ b/src/quick/handlers/handlers.pri @@ -1,22 +1,20 @@ HEADERS += \ $$PWD/qquickdraghandler_p.h \ - $$PWD/qquickpointerhandler_p.h \ - $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickhandlersmodule_p.h \ $$PWD/qquickmultipointerhandler_p.h \ $$PWD/qquickpinchhandler_p.h \ - $$PWD/qquickpointersinglehandler_p.h \ - $$PWD/qquickhandlersmodule_p.h \ $$PWD/qquickpointerdevicehandler_p.h \ + $$PWD/qquickpointerhandler_p.h \ + $$PWD/qquickpointersinglehandler_p.h \ $$PWD/qquicktaphandler_p.h \ - $$PWD/qquickhandlersmodule_p.h \ SOURCES += \ $$PWD/qquickdraghandler.cpp \ - $$PWD/qquickpointerhandler.cpp \ - $$PWD/qquickpointerdevicehandler.cpp \ + $$PWD/qquickhandlersmodule.cpp \ $$PWD/qquickmultipointerhandler.cpp \ $$PWD/qquickpinchhandler.cpp \ + $$PWD/qquickpointerdevicehandler.cpp \ + $$PWD/qquickpointerhandler.cpp \ $$PWD/qquickpointersinglehandler.cpp \ $$PWD/qquicktaphandler.cpp \ - $$PWD/qquickhandlersmodule.cpp \ -- cgit v1.2.3 From 09fe06675c6add8506462b114f31ab6e4d45c458 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 25 Apr 2017 14:14:42 +0200 Subject: PointerHandler: add wants/declines logging to qt.quick.handler.dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and remove the similar "delivering to" log from QQuickItem. It's more useful to know whether delivery will continue rather than simply that we made an attempt. Change-Id: I58c37fb50d4d0c99ef6aa68662ab304194b6d128 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 6 +++++- src/quick/items/qquickitem.cpp | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 8f113ddd56..8f539d4678 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -198,7 +198,11 @@ QQuickItem *QQuickPointerHandler::target() const void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) { - if (wantsPointerEvent(event)) { + bool wants = wantsPointerEvent(event); + qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName() + << "on" << parentItem()->metaObject()->className() << parentItem()->objectName() + << (wants ? "WANTS" : "DECLINES") << event; + if (wants) { handlePointerEventImpl(event); } else { setActive(false); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index ef22af965a..ad3c46a8c9 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -87,7 +87,6 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET) Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) -Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch) void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) { @@ -5103,12 +5102,10 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event) */ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber) { - Q_Q(QQuickItem); bool delivered = false; QVector &eventDeliveryTargets = event->device()->eventDeliveryTargets(); if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { - qCDebug(lcPointerHandlerDispatch) << " delivering" << event << "to" << handler << "on" << q; if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { handler->handlePointerEvent(event); delivered = true; -- cgit v1.2.3 From 45cfee52e4dd488adc1050dd50fbe1dbecee9a4d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 25 Apr 2017 15:10:30 +0200 Subject: QQuickMultiPointerHandler::wantsPointerEvent ignores grabs on press MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A PinchHandler for example has strict requirements, so when they are finally met, it needs to be willing to take over the grab from something else. This occurs when a point is pressed or released (in which case a grabbing PointerHandler might probably give up its grab anyway, if its requirements are not met anymore). MPTA can typically handle a variable number of points though, so it will not give up its grabs, and PinchHandler must be able to steal from it. The first step in stealing is for the handler to say that it wants the points despite any existing grabs. Change-Id: Ic3a2586d43e4cfe1877f90ce2e2862a9abc4b81b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index d1394e9516..1a5684d80e 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -69,12 +69,18 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) const int pCount = event->pointCount(); int pointCandidateCount = 0; - // points that are grabbed by other handlers are not candidates for this handler - for (int i = 0; i < pCount; ++i) { - QQuickEventPoint *pt = event->point(i); - QObject *exclusiveGrabber = pt->exclusiveGrabber(); - if (!exclusiveGrabber || exclusiveGrabber == this) - ++pointCandidateCount; + // If one or more points are newly pressed, all points are candidates for this handler. + // In other cases however, do not steal the grab: that is, if a point has a grabber, + // it's not a candidate for this handler. + if (event->isPressEvent()) { + pointCandidateCount = pCount; + } else { + for (int i = 0; i < pCount; ++i) { + QQuickEventPoint *pt = event->point(i); + QObject *exclusiveGrabber = pt->exclusiveGrabber(); + if (!exclusiveGrabber || exclusiveGrabber == this) + ++pointCandidateCount; + } } if (pointCandidateCount < m_requiredPointCount) -- cgit v1.2.3 From b7f9f342480c669780a244c237642edaf5b57bc3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 27 Apr 2017 12:16:20 +0200 Subject: QQMultiPointerHandler: don't steal grab if keepMouseGrab/keepTouchGrab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An Item (such as MPTA with onGestureStarted: gesture.grab()) may set these flags, traditionally to prevent Flickable from stealing the grab. QQuickMultiPointerHandler (and thus PinchHandler) now respects these flags too. Change-Id: Iac3ab796c5aa410be45639d679ecf82b7c44a442 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 15 ++++++++++++--- src/quick/handlers/qquickmultipointerhandler_p.h | 2 +- src/quick/handlers/qquickpinchhandler.cpp | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 1a5684d80e..016210beff 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -238,10 +238,19 @@ void QQuickMultiPointerHandler::acceptPoints(const QVector & point->setAccepted(); } -void QQuickMultiPointerHandler::grabPoints(QVector points) +bool QQuickMultiPointerHandler::grabPoints(QVector points) { - for (QQuickEventPoint* point : points) - setExclusiveGrab(point); + bool canGrab = true; + for (QQuickEventPoint* point : points) { + auto grabber = point->grabberItem(); + if (grabber && (grabber->keepMouseGrab() || grabber->keepTouchGrab())) + canGrab = false; + } + if (canGrab) { + for (QQuickEventPoint* point : points) + setExclusiveGrab(point); + } + return canGrab; } QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index be92a39631..ce014d658c 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -98,7 +98,7 @@ protected: QVector angles(const QPointF &ref) const; static qreal averageAngleDelta(const QVector &old, const QVector &newAngles); void acceptPoints(const QVector &points); - void grabPoints(QVector points); + bool grabPoints(QVector points); protected: QVector m_currentPoints; diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 67d32cdb18..3af77593e2 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -232,8 +232,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) // Verify that least one of the points have moved beyond threshold needed to activate the handler for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { if (QQuickWindowPrivate::dragOverThreshold(point)) { - grabPoints(m_currentPoints); - setActive(true); + if (grabPoints(m_currentPoints)) + setActive(true); break; } } -- cgit v1.2.3 From 926d69a61c50bf970c0f6cc5dddf735e996f502e Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Apr 2017 12:58:48 +0200 Subject: QQPSingleHandler: store m_scenePressPos separately MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scene-relative position at which an EventPoint was pressed does not change during a drag. The target item is moving, so we cannot convert a point relative to the target item back to a scene position and expect it to stay constant. Change-Id: I07389993c6b15316b686824c37dbad83b4b10861 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 2 ++ src/quick/handlers/qquickpointersinglehandler_p.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 5ad60291dd..dca98f6a50 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -145,6 +145,7 @@ void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *even switch (currentPoint->state()) { case QQuickEventPoint::Pressed: m_pressPos = currentPoint->pos(); + m_scenePressPos = currentPoint->scenePos(); setPressedButtons(event->buttons()); emit pointIdChanged(); break; @@ -223,6 +224,7 @@ void QQuickPointerSingleHandler::reset() m_uniquePointId = QPointingDeviceUniqueId(); m_pos = QPointF(); m_pressPos = QPointF(); + m_scenePressPos = QPointF(); m_sceneGrabPos = QPointF(); m_velocity = QVector2D(); m_rotation = 0; diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 466de1a1eb..70aa26801f 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -80,7 +80,7 @@ public: Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } void setAcceptedButtons(Qt::MouseButtons buttons); QPointF pressPos() const { return m_pressPos; } - QPointF scenePressPos() const { return parentItem()->mapToScene(m_pressPos); } + QPointF scenePressPos() const { return m_scenePressPos; } QPointF sceneGrabPos() const { return m_sceneGrabPos; } QPointF pos() const { return m_pos; } QPointF scenePos() const { return parentItem()->mapToScene(m_pos); } @@ -118,6 +118,7 @@ private: Qt::MouseButtons m_pressedButtons; QPointF m_pos; QPointF m_pressPos; + QPointF m_scenePressPos; QPointF m_sceneGrabPos; QVector2D m_velocity; qreal m_rotation; -- cgit v1.2.3 From 822e39df38b20763d2933d7a6d2d6ddf7b9b2639 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 31 Mar 2017 13:11:09 +0200 Subject: Start over with event delivery when touchpoint presses occur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was part of the intended design for pointer handlers; omitting this feature has until now prevented some opportunities for handoff from one handler to another. Now if PinchHandler has grabbed, pressing one more point will let something else have a chance again, for example. But this is incomplete in that if we release a point, we should do the same thing, actually. Change-Id: I10f567e7e4388bf0caab54c261178f19db20b14a Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 25 +++++++++++++++++++------ src/quick/items/qquickwindow.cpp | 14 ++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index ba93a4195e..a2d8ea3ad6 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1224,7 +1224,12 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point \internal Make a new QTouchEvent, giving it a subset of the original touch points. - Returns a nullptr if all points are stationary or there are no points inside the item. + Returns a nullptr if all points are stationary, or there are no points inside the item, + or none of the points were pressed inside and the item was not grabbing any of them + and isFiltering is false. When isFiltering is true, it is assumed that the item + cares about all points which are inside its bounds, because most filtering items + need to monitor eventpoint movements until a drag threshold is exceeded or the + requirements for a gesture to be recognized are met in some other way. */ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const { @@ -1234,6 +1239,8 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity + bool anyPressInside = false; + bool anyGrabber = false; QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); for (int i = 0; i < m_pointCount; ++i) { auto p = m_touchPoints.at(i); @@ -1241,8 +1248,10 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i continue; // include points where item is the grabber bool isGrabber = p->exclusiveGrabber() == item; - // include newly pressed points inside the bounds - bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos())); + if (isGrabber) + anyGrabber = true; + // include points inside the bounds + bool isInside = item->contains(item->mapFromScene(p->scenePos())); // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item bool grabberIsChild = false; @@ -1256,9 +1265,11 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i } bool filterRelevant = isFiltering && grabberIsChild; - if (!(isGrabber || isPressInside || filterRelevant)) + if (!(isGrabber || isInside || filterRelevant)) continue; - + bool isPress = p->state() == QQuickEventPoint::Pressed; + if (isPress && isInside) + anyPressInside = true; const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); if (tp) { eventStates |= tp->state(); @@ -1272,7 +1283,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i } } - if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty()) + // Now touchPoints will have only points which are inside the item. + // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressInside && !anyGrabber && !isFiltering)) return nullptr; // if all points have the same state, set the event type accordingly diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 359763bfb4..9c9764005d 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -754,6 +754,9 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId); if (point) { + auto originalEvent = point->pointerEvent()->device()->pointerEvent(); + for (int i = 0; i < originalEvent->pointCount(); ++i) + originalEvent->point(i)->cancelExclusiveGrab(); point->setGrabberItem(grabber); for (auto handler : point->passiveGrabbers()) point->cancelPassiveGrab(handler); @@ -2362,11 +2365,13 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve // Deliver newly pressed touch points bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) { - const QVector points = event->unacceptedPressedPointScenePositions(); + int pointCount = event->pointCount(); QVector targetItems; bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); - for (QPointF point: points) { - QVector targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent); + for (int i = 0; i < pointCount; ++i) { + auto point = event->point(i); + point->setAccepted(false); // because otherwise touchEventForItem will ignore it + QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2458,10 +2463,11 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. + bool isPress = pointerEvent->isPressEvent(); for (auto point: qAsConst(touchEvent->touchPoints())) { auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); - if (point.state() == Qt::TouchPointPressed) + if (isPress) pointerEventPoint->setGrabberItem(item); } } else { -- cgit v1.2.3 From 47479c0e969a3b3d220c77b8bb7cb8f2d58c07ae Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Apr 2017 08:51:47 +0200 Subject: DragHandler: allow parent to be different from target The most obvious way to implement a Slider is to allow dragging the knob - as on a real-world physical sliding potentiometer. But to make it easier on a touchscreen, it should be possible to touch anywhere along the slider's travel, as on a QtQuick.Controls 2 Slider. For that purpose, we need to respond to events within the bounds of one Item while actually dragging a different Item (the knob). It's similar to the way that PinchHandler can handle pinch gestures within one Item while transforming another (which may be too small to get both fingers inside). Change-Id: Iac9a5f11a7a45e22d93fe52bf62d157c48d72d3d Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler.cpp | 37 ++++++++++++++++++++++++++------ src/quick/handlers/qquickdraghandler_p.h | 4 +++- 2 files changed, 34 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 73ad97712e..b4006ab5c7 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -75,17 +75,25 @@ bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { + if (grabber == this && stateChange == QQuickEventPoint::GrabExclusive) + // In case the grab got handled over from another grabber, we might not get the Press + initializeTargetStartPos(point); enforceConstraints(); QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); } +void QQuickDragHandler::onActiveChanged() +{ + if (!active()) + m_targetStartPos = QPointF(); +} + void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) { point->setAccepted(); switch (point->state()) { case QQuickEventPoint::Pressed: - if (target() && target()->parentItem()) - m_startPos = target()->parentItem()->mapToScene(target()->position()); + initializeTargetStartPos(point); setPassiveGrab(point); break; case QQuickEventPoint::Updated: { @@ -97,7 +105,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) if (active()) { setTranslation(delta); if (target() && target()->parentItem()) { - QPointF pos = target()->parentItem()->mapFromScene(m_startPos + delta); + QPointF pos = target()->parentItem()->mapFromScene(m_targetStartPos + delta); enforceAxisConstraints(&pos); target()->setPosition(pos); } @@ -105,13 +113,13 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)))) { setExclusiveGrab(point); - if (target()) { + if (auto parent = parentItem()) { if (point->pointerEvent()->asPointerTouchEvent()) - target()->setKeepTouchGrab(true); + parent->setKeepTouchGrab(true); // tablet and mouse are treated the same by Item's legacy event handling, and // touch becomes synth-mouse for Flickable, so we need to prevent stealing // mouse grab too, whenever dragging occurs in an enabled direction - target()->setKeepMouseGrab(true); + parent->setKeepMouseGrab(true); } } } break; @@ -139,6 +147,23 @@ void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos) localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum())); } +void QQuickDragHandler::initializeTargetStartPos(QQuickEventPoint *point) +{ + if (target() && target()->parentItem() && m_targetStartPos.isNull()) { // prefer the m_targetStartPos we got when it got Pressed. + m_targetStartPos = target()->parentItem()->mapToScene(target()->position()); + if (!target()->contains(point->pos())) { + // If pressed outside of target item, move the target item so that the touchpoint is in its center, + // while still respecting the axis constraints. + const QPointF center = target()->parentItem()->mapFromScene(point->scenePos()); + const QPointF pointCenteredInItemPos = target()->parentItem()->mapToScene(center - QPointF(target()->width(), target()->height())/2); + if (m_xAxis.enabled()) + m_targetStartPos.setX(pointCenteredInItemPos.x()); + if (m_yAxis.enabled()) + m_targetStartPos.setY(pointCenteredInItemPos.y()); + } + } +} + void QQuickDragHandler::setTranslation(const QPointF &trans) { if (trans == m_translation) // fuzzy compare? diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h index a75ea6f2c7..54fef697f7 100644 --- a/src/quick/handlers/qquickdraghandler_p.h +++ b/src/quick/handlers/qquickdraghandler_p.h @@ -112,14 +112,16 @@ Q_SIGNALS: protected: bool wantsEventPoint(QQuickEventPoint *point) override; + void onActiveChanged() override; void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; private: void ungrab(); void enforceAxisConstraints(QPointF *localPos); + void initializeTargetStartPos(QQuickEventPoint *point); private: - QPointF m_startPos; + QPointF m_targetStartPos; QPointF m_translation; QQuickDragAxis m_xAxis; QQuickDragAxis m_yAxis; -- cgit v1.2.3 From eab5437d3d5a2a80a1b583f3345370036e26dc44 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 28 Apr 2017 10:30:59 +0200 Subject: TapHandler: CancelGrabPassive => setPressed(false) When a TapHandler's Item is inside a Flickable, the user has pressed the mouse button over the TapHandler, and then the Flickable takes the mouse grab during dragging from there, if the TapHandler has only a passive grab, it is cancelled. In this case the TapHandler should no longer be pressed, because it's being dragged instead. Change-Id: I129f44cc9b8d8e99b00e23cd5943dd57d4ae5d16 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquicktaphandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 14aeb07864..bf9a72c5ab 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -305,7 +305,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); - if (grabber == this && stateChange == QQuickEventPoint::CancelGrabExclusive) + if (grabber == this && (stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive)) setPressed(false, true, point); } -- cgit v1.2.3 From 8082698fff3ee87d0dfc75cdba0ae21ec523a3fe Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Apr 2017 12:49:49 +0200 Subject: QQPSingleHandler: accept only left mouse button by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It makes as much sense for DragHandler as it does for TapHandler. If you want to allow right-button dragging, you need to enable it in QML. In general, UIs have always relied on the left button for normal interaction, while the other buttons are for special cases. Change-Id: I708d5d080832c32ef581ca333c9be06e987ef007 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointersinglehandler.cpp | 2 +- src/quick/handlers/qquicktaphandler.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index dca98f6a50..4e89d71eab 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -55,7 +55,7 @@ QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) , m_pointId(0) , m_rotation(0) , m_pressure(0) - , m_acceptedButtons(Qt::AllButtons) + , m_acceptedButtons(Qt::LeftButton) , m_ignoreAdditionalPoints(false) { } diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index bf9a72c5ab..c0a0dd8379 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -85,7 +85,6 @@ QQuickTapHandler::QQuickTapHandler(QObject *parent) , m_longPressThreshold(-1) , m_lastTapTimestamp(0.0) { - setAcceptedButtons(Qt::LeftButton); if (m_mouseMultiClickDistanceSquared < 0) { m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0; m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()-> -- cgit v1.2.3 From 93563959cd9e64087599a829fc7d6afe22d3b487 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 24 Apr 2017 12:53:58 +0200 Subject: Add and use QQuickPointerSingleHandler::moveTarget() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now, DragHandler::pos() was not staying constant during a drag, because we update m_pos from the EventPoint, then move the Item. scenePos() also returns mapToScene(m_pos). If the Item is being dragged to follow the finger or the mouse cursor exactly, unconstrained, then pos() should always be the same as the parent-relative position where the press occurred. Change-Id: Ia02738c0cf458e039cf90371f9c8a7becb75a035 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickdraghandler.cpp | 2 +- src/quick/handlers/qquickpointersinglehandler.cpp | 6 ++++++ src/quick/handlers/qquickpointersinglehandler_p.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index b4006ab5c7..27edffd65a 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -107,7 +107,7 @@ void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point) if (target() && target()->parentItem()) { QPointF pos = target()->parentItem()->mapFromScene(m_targetStartPos + delta); enforceAxisConstraints(&pos); - target()->setPosition(pos); + moveTarget(pos, point); } } else if (!point->exclusiveGrabber() && ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) || diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 4e89d71eab..80632303f1 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -199,6 +199,12 @@ void QQuickPointerSingleHandler::setIgnoreAdditionalPoints(bool v) m_ignoreAdditionalPoints = v; } +void QQuickPointerSingleHandler::moveTarget(QPointF pos, QQuickEventPoint *point) +{ + target()->setPosition(pos); + m_pos = target()->mapFromScene(point->scenePos()); +} + void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) { if (buttons != m_pressedButtons) { diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 70aa26801f..06b8ec45dc 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -108,6 +108,8 @@ protected: void setIgnoreAdditionalPoints(bool v = true); + void moveTarget(QPointF pos, QQuickEventPoint *point); + private: void setPressedButtons(Qt::MouseButtons buttons); void reset(); -- cgit v1.2.3 From d84af8b81543e200db3041f27eadc2df5c62055e Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Thu, 4 May 2017 14:05:13 +0200 Subject: Only send filtered events to the parent of the receiver If receiver is nullptr, all filteringParentItems are candidates for filtering. Change-Id: I9c388a9a2e83c1f5815c99c059f0e507e0bc1040 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b536ae3804..289e1eabec 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2710,6 +2710,8 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ QVarLengthArray, 32> passiveGrabsToCancel; for (QPair itemAndParent : filteringParentItems) { QQuickItem *item = receiver ? receiver : itemAndParent.first; + if (item != itemAndParent.first) + continue; if (!item->acceptTouchEvents() && !item->acceptedMouseButtons()) continue; // if this item won't accept, parents don't need to filter the touch for it QQuickItem *filteringParent = itemAndParent.second; -- cgit v1.2.3 From ab91e7fa02a562d80fd0747f28a60e00c3b45a01 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 2 May 2017 11:34:19 +0200 Subject: Default QQuickItem::acceptTouchEvents to true until Qt 6 This is a partial revert of 1457df74f4c1d770e1e820de8cd082be1bd2489e to avoid making a mandatory API change so soon. Change-Id: I05040579fa36d3dc5ef7616861f6d17adf500d2c Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 1 + src/quick/items/qquickitem.cpp | 4 ++++ src/quick/items/qquickmousearea.cpp | 1 + src/quick/items/qquickrectangle.cpp | 3 +++ src/quick/items/qquickwindow.cpp | 10 ++++++++-- 5 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index a631512877..ce584cd283 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -270,6 +270,7 @@ void QQuickFlickablePrivate::init() qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()), q, QQuickFlickable, SLOT(velocityTimelineCompleted())) q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch q->setFiltersChildMouseEvents(true); QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem); viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index bdf60e7ea0..4cb7ef3e22 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3171,7 +3171,11 @@ QQuickItemPrivate::QQuickItemPrivate() , antialiasingValid(false) , isTabFence(false) , replayingPressEvent(false) +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + , touchEnabled(true) +#else , touchEnabled(false) +#endif , dirtyAttributes(0) , nextDirtyItem(0) , prevDirtyItem(0) diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index d8bad7d793..96f34ef276 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -85,6 +85,7 @@ void QQuickMouseAreaPrivate::init() { Q_Q(QQuickMouseArea); q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch q->setFiltersChildMouseEvents(true); if (qmlVisualTouchDebugging()) { q->setFlag(QQuickItem::ItemHasContents); diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index 0c532bcd4c..5ecca3c91d 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -324,6 +324,9 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent) : QQuickItem(*(new QQuickRectanglePrivate), parent) { setFlag(ItemHasContents); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + setAcceptTouchEvents(false); +#endif } /*! diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 289e1eabec..9c889d54f5 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2668,9 +2668,15 @@ void QQuickWindowPrivate::updateFilteringParentItems(const QVector } filteringParentItems.clear(); for (QQuickItem *item : targetItems) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool acceptsTouchEvents = item->acceptTouchEvents(); +#else + // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. + bool acceptsTouchEvents = false; +#endif QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); - // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant - if (!item->acceptedMouseButtons() && !item->acceptTouchEvents() && + // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant. + if (!item->acceptedMouseButtons() && !acceptsTouchEvents && !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty())) continue; QQuickItem *parent = item->parentItem(); -- cgit v1.2.3 From c67ed188abf80cb59f9d70aa38a57721b99091f0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 9 May 2017 07:09:55 +0200 Subject: QQuickPointerEvent debug operator: add timestamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It can help to distinguish the time period when we are still delivering the same event. Change-Id: Id738d3e210431f1803cad9ace000c0f702e930fc Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index a2d8ea3ad6..e7de663f30 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1347,7 +1347,9 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice * Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) { QDebugStateSaver saver(dbg); dbg.nospace(); - dbg << "QQuickPointerEvent(dev:"; + dbg << "QQuickPointerEvent("; + dbg << event->timestamp(); + dbg << " dev:"; QtDebugUtils::formatQEnum(dbg, event->device()->type()); if (event->buttons() != Qt::NoButton) { dbg << " buttons:"; -- cgit v1.2.3 From 2e570e52deb75d6293a144bd5b3d51171d757b92 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 11 May 2017 13:46:28 +0200 Subject: QQuickPointerEvent debug operator: don't crash if original event null QQuickPointerEvent is a wrapper for a QInputEvent. Between event deliveries, the object exists but it doesn't have a current event to which to refer. qDebug() should still work OK anyway in that case. Change-Id: I5aa9584c5d7988bb748befa90785efe8bd24678a Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index e7de663f30..0f613d300b 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1348,7 +1348,10 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e QDebugStateSaver saver(dbg); dbg.nospace(); dbg << "QQuickPointerEvent("; - dbg << event->timestamp(); + if (event->isValid()) + dbg << event->timestamp(); + else + dbg << "invalid"; dbg << " dev:"; QtDebugUtils::formatQEnum(dbg, event->device()->type()); if (event->buttons() != Qt::NoButton) { -- cgit v1.2.3 From 92269400ed1876b0ea1e87571cfa6281edcad7d3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 10 May 2017 22:06:33 +0200 Subject: Don't allow one handler to cancel another's grab, unless it is stealing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is required to keep the tst_flickableinterop touchDragSlider test passing after the change to TapHandler to never give up passive grab. And QQuickEventPoint::setGrabberPointerHandler(nullptr, true) will just go ahead and allow TapHandler to cancel the grab which the DragHandler currently has at that time, when you have pressed the TapHandler but started dragging so that the drag threshold is exceeded. (Perhaps this points to an API problem.) For now, we at least can enforce the rule in QQuickPointerHandler::setExclusiveGrab that if grab is false, it won't nullify the grab of a different handler. Change-Id: I7c93188cfdce51b3b5a17c13e5efc7fcbd123d4b Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 8f539d4678..3774c2d24c 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -138,6 +138,9 @@ void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab) { // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab qCDebug(lcPointerHandlerDispatch) << point << grab; + // Don't allow one handler to cancel another's grab, unless it is stealing it for itself + if (!grab && point->grabberPointerHandler() != this) + return; point->setGrabberPointerHandler(grab ? this : nullptr, true); } -- cgit v1.2.3 From 2c6d20245cfaaa8d62874df98beb49c99d0067bc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 10 May 2017 21:08:37 +0200 Subject: TapHandler: don't give up passive grab on setPressed(false) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a button with a TapHandler with the DragThreshold policy is inside a Flickable, and you press on the button and start dragging, when you exceed the drag threshold, TapHandler will not "want" the EventPoint anymore. That triggers it to call setPressed(false). Flickable does not have a grab because it was relying on filtering the children's events; and QQuickWindow does not deliver touch updates to non-grabber items. So when TapHandler gives up its passive grab, Flickable does not get a chance to filter TapHandler's touch update events anymore. Thus, we cannot do that. When the touchpoint is actually released though, passive grabs are ungrabbed anyway. This is required to get the new tst_FlickableInterop::touchDragFlickableBehindButton() autotest working. Change-Id: I55a175ae358f75b9d7ab64f0b8124d91b6a9e1d6 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquicktaphandler.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index c0a0dd8379..f79dbb266a 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -291,12 +291,9 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi } } emit pressedChanged(); - if (!press) { + if (!press && m_gesturePolicy != DragThreshold) { // on release, ungrab after emitting changed signals - if (m_gesturePolicy == DragThreshold) - setPassiveGrab(point, press); - else - setExclusiveGrab(point, press); + setExclusiveGrab(point, press); } } } -- cgit v1.2.3 From 48011c2dfeb83b4fe717034d4b3c353714fead48 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 3 Apr 2017 11:37:03 +0200 Subject: Start over with event delivery when touchpoint releases occur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new rule is that when the number of touchpoints changes, we start over with event delivery as if the touch had just begun, to give more opportunities to hand off processing from one item or handler to another. And MultiPointTouchArea can now handle the handoff: for example in tests/manual/pointer/pinchDragFlingMPTA.qml when the user is pressing three fingers, the PinchHandler is active; when the user then lifts one finger, the MPTA can resume handling the two remaining touchpoints as if they were just pressed. The change in QQuickMultiPointerHandler::wantsPointerEvent is both a behavior change and an optimization: released points aren't eligible; but if some points are released, then pressed, updated and stationary points are all eligible. And, figure this out without looping over the points twice. Change-Id: I26b7593de8e72b471adfec4a4482dd87a8288442 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickmultipointerhandler.cpp | 36 +++++++++------------- src/quick/handlers/qquickmultipointerhandler_p.h | 2 +- src/quick/handlers/qquickpinchhandler.cpp | 6 ++-- src/quick/handlers/qquickpointerhandler.cpp | 1 + src/quick/handlers/qquicktaphandler.cpp | 7 +++-- src/quick/items/qquickevents.cpp | 36 +++++++++++++++++----- src/quick/items/qquickevents_p_p.h | 9 +++++- src/quick/items/qquickitem.cpp | 1 - src/quick/items/qquickwindow.cpp | 38 +++++++++++++++--------- src/quick/items/qquickwindow_p.h | 4 +-- 10 files changed, 87 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp index 016210beff..a605b3f12e 100644 --- a/src/quick/handlers/qquickmultipointerhandler.cpp +++ b/src/quick/handlers/qquickmultipointerhandler.cpp @@ -66,44 +66,34 @@ bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) if (!QQuickPointerDeviceHandler::wantsPointerEvent(event)) return false; - const int pCount = event->pointCount(); - int pointCandidateCount = 0; - - // If one or more points are newly pressed, all points are candidates for this handler. - // In other cases however, do not steal the grab: that is, if a point has a grabber, - // it's not a candidate for this handler. - if (event->isPressEvent()) { - pointCandidateCount = pCount; - } else { - for (int i = 0; i < pCount; ++i) { - QQuickEventPoint *pt = event->point(i); - QObject *exclusiveGrabber = pt->exclusiveGrabber(); - if (!exclusiveGrabber || exclusiveGrabber == this) - ++pointCandidateCount; - } - } - - if (pointCandidateCount < m_requiredPointCount) - return false; if (sameAsCurrentPoints(event)) return true; - const QVector candidatePoints = pointsInsideOrNearTarget(event); + const QVector candidatePoints = eligiblePoints(event); const bool ret = (candidatePoints.size() == m_requiredPointCount); if (ret) m_currentPoints = candidatePoints; return ret; } -QVector QQuickMultiPointerHandler::pointsInsideOrNearTarget(QQuickPointerEvent *event) +QVector QQuickMultiPointerHandler::eligiblePoints(QQuickPointerEvent *event) { QVector ret; int c = event->pointCount(); QRectF targetBounds = target()->mapRectToScene(target()->boundingRect()) .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold)); + // If one or more points are newly pressed or released, all non-released points are candidates for this handler. + // In other cases however, do not steal the grab: that is, if a point has a grabber, + // it's not a candidate for this handler. + bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent(); for (int i = 0; i < c; ++i) { QQuickEventPoint *p = event->point(i); - if (targetBounds.contains(p->scenePos())) + if (!stealingAllowed) { + QObject *exclusiveGrabber = p->exclusiveGrabber(); + if (exclusiveGrabber && exclusiveGrabber != this) + continue; + } + if (p->state() != QQuickEventPoint::Released && targetBounds.contains(p->scenePos())) ret << p; } return ret; @@ -136,6 +126,8 @@ bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event) // TODO optimize: either ensure the points are sorted, // or use std::equal with a predicate for (int i = 0; ret && i < c; ++i) { + if (event->point(i)->state() == QQuickEventPoint::Released) + return false; bool found = false; int pointId = event->point(i)->pointId(); for (QQuickEventPoint *o : qAsConst(m_currentPoints)) diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index ce014d658c..1f2d213e56 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -88,7 +88,7 @@ protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; bool sameAsCurrentPoints(QQuickPointerEvent *event); - QVector pointsInsideOrNearTarget(QQuickPointerEvent *event); + QVector eligiblePoints(QQuickPointerEvent *event); QPointF touchPointCentroid(); QVector2D touchPointCentroidVelocity(); qreal averageTouchPointDistance(const QPointF &ref); diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 3af77593e2..465a49a6fd 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -228,7 +228,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos(); } - if (!active()) { + bool containsReleasedPoints = event->isReleaseEvent(); + if (!active() && !containsReleasedPoints) { // Verify that least one of the points have moved beyond threshold needed to activate the handler for (QQuickEventPoint *point : qAsConst(m_currentPoints)) { if (QQuickWindowPrivate::dragOverThreshold(point)) { @@ -302,7 +303,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) << ", rotation" << rotation; } - acceptPoints(m_currentPoints); + if (!containsReleasedPoints) + acceptPoints(m_currentPoints); emit updated(); } diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 3774c2d24c..1ed80c0a5b 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -216,6 +216,7 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event) pt->cancelExclusiveGrab(); } } + event->device()->eventDeliveryTargets().append(this); } bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event) diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index f79dbb266a..d73e9fd100 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -270,7 +270,7 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi else setExclusiveGrab(point, press); } - if (!cancel && !press) { + if (!cancel && !press && parentContains(point)) { if (point->timeHeld() < longPressThreshold()) { // Assuming here that pointerEvent()->timestamp() is in ms. qreal ts = point->pointerEvent()->timestamp() / 1000.0; @@ -301,8 +301,9 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) { QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point); - if (grabber == this && (stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive)) - setPressed(false, true, point); + bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive; + if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released)) + setPressed(false, isCanceled, point); } void QQuickTapHandler::connectPreRenderSignal(bool conn) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 0f613d300b..bde271882c 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -1055,6 +1055,18 @@ bool QQuickPointerMouseEvent::isPressEvent() const (me->buttons() & me->button()) == me->buttons()); } +bool QQuickPointerMouseEvent::isUpdateEvent() const +{ + auto me = static_cast(m_event); + return me->type() == QEvent::MouseMove; +} + +bool QQuickPointerMouseEvent::isReleaseEvent() const +{ + auto me = static_cast(m_event); + return me->type() == QEvent::MouseButtonRelease; +} + bool QQuickPointerTouchEvent::allPointsAccepted() const { for (int i = 0; i < m_pointCount; ++i) { if (!m_touchPoints.at(i)->isAccepted()) @@ -1124,6 +1136,16 @@ bool QQuickPointerTouchEvent::isPressEvent() const return static_cast(m_event)->touchPointStates() & Qt::TouchPointPressed; } +bool QQuickPointerTouchEvent::isUpdateEvent() const +{ + return static_cast(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary); +} + +bool QQuickPointerTouchEvent::isReleaseEvent() const +{ + return static_cast(m_event)->touchPointStates() & Qt::TouchPointReleased; +} + QVector QQuickPointerEvent::unacceptedPressedPointScenePositions() const { QVector points; @@ -1239,7 +1261,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity - bool anyPressInside = false; + bool anyPressOrReleaseInside = false; bool anyGrabber = false; QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform()); for (int i = 0; i < m_pointCount; ++i) { @@ -1250,8 +1272,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i bool isGrabber = p->exclusiveGrabber() == item; if (isGrabber) anyGrabber = true; - // include points inside the bounds + // include points inside the bounds if no other item is the grabber or if the item is filtering bool isInside = item->contains(item->mapFromScene(p->scenePos())); + bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item; // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item bool grabberIsChild = false; @@ -1265,11 +1288,10 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i } bool filterRelevant = isFiltering && grabberIsChild; - if (!(isGrabber || isInside || filterRelevant)) + if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant)) continue; - bool isPress = p->state() == QQuickEventPoint::Pressed; - if (isPress && isInside) - anyPressInside = true; + if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside) + anyPressOrReleaseInside = true; const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); if (tp) { eventStates |= tp->state(); @@ -1285,7 +1307,7 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i // Now touchPoints will have only points which are inside the item. // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway. - if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressInside && !anyGrabber && !isFiltering)) + if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) return nullptr; // if all points have the same state, set the event type accordingly diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 72d5be54c9..4b3587f18c 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -272,7 +272,8 @@ public: Released = Qt::TouchPointReleased // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe }; - Q_ENUM(State) + Q_DECLARE_FLAGS(States, State) + Q_FLAG(States) enum GrabState { GrabPassive = 0, @@ -406,6 +407,8 @@ public: // helpers for C++ only (during event delivery) virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; + virtual bool isUpdateEvent() const = 0; + virtual bool isReleaseEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; } virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; } @@ -448,6 +451,8 @@ public: QQuickPointerEvent *reset(QEvent *) override; void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; } int pointCount() const override { return 1; } @@ -481,6 +486,8 @@ public: QQuickPointerEvent *reset(QEvent *) override; void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isUpdateEvent() const override; + bool isReleaseEvent() const override; QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; } const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; } int pointCount() const override { return m_pointCount; } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 4cb7ef3e22..e959b51bc3 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -5113,7 +5113,6 @@ bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoid if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) { handler->handlePointerEvent(event); delivered = true; - eventDeliveryTargets.append(handler); } } } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9c889d54f5..e4eed8454c 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1703,18 +1703,17 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven bool delivered = false; if (pointerEvent->isPressEvent()) { // send initial press - delivered = deliverPressEvent(pointerEvent); + delivered = deliverPressOrReleaseEvent(pointerEvent); } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { // if this is an update or release from an actual mouse, // and the point wasn't grabbed, deliver only to PointerHandlers: // passive grabbers first, then the rest - QVector &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + const QVector &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); for (auto handler : point->passiveGrabbers()) { // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) handler->handlePointerEvent(pointerEvent); - eventDeliveryTargets.append(handler); } } // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order @@ -2266,9 +2265,11 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); if (event->isPressEvent()) - deliverPressEvent(event); + deliverPressOrReleaseEvent(event); if (!event->allUpdatedPointsAccepted()) deliverUpdatedTouchPoints(event); + if (event->isReleaseEvent()) + deliverPressOrReleaseEvent(event, true); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2322,7 +2323,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve int pointCount = event->pointCount(); // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) - QVector &eventDeliveryTargets = event->device()->eventDeliveryTargets(); + const QVector &eventDeliveryTargets = event->device()->eventDeliveryTargets(); for (int i = 0; i < pointCount; ++i) { QQuickEventPoint *point = event->point(i); for (auto handler : point->passiveGrabbers()) { @@ -2330,7 +2331,6 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve if (sendFilteredPointerEvent(event, handler->parentItem())) return; handler->handlePointerEvent(event); - eventDeliveryTargets.append(handler); } } } @@ -2363,8 +2363,8 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve } } -// Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) +// Deliver an event containing newly pressed or released touch points +bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) { int pointCount = event->pointCount(); QVector targetItems; @@ -2372,6 +2372,8 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) for (int i = 0; i < pointCount; ++i) { auto point = event->point(i); point->setAccepted(false); // because otherwise touchEventForItem will ignore it + if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) + point->setGrabberPointerHandler(nullptr, true); QVector targetItemsForPoint = pointerTargets(contentItem, point->scenePos(), !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); @@ -2380,14 +2382,14 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) } } - if (allowChildEventFiltering) { + if (allowChildEventFiltering && !handlersOnly) { updateFilteringParentItems(targetItems); if (sendFilteredPointerEvent(event, nullptr)) return true; } for (QQuickItem *item: targetItems) { - deliverMatchingPointsToItem(item, event); + deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) break; } @@ -2395,7 +2397,7 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event) return event->allPointsAccepted(); } -void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) { Q_Q(QQuickWindow); QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -2403,7 +2405,15 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo // Let the Item's handlers (if any) have the event first. itemPrivate->handlePointerEvent(pointerEvent); - if (pointerEvent->allPointsAccepted()) + if (handlersOnly) + return; + if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) + return; + + // If all points are released and the item is not the grabber, it doesn't get the event. + // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. + if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() + && !pointerEvent->exclusiveGrabbers().contains(item)) return; // TODO: unite this mouse point delivery with the synthetic mouse event below @@ -2464,11 +2474,11 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. - bool isPress = pointerEvent->isPressEvent(); + bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); for (auto point: qAsConst(touchEvent->touchPoints())) { auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); - if (isPress) + if (isPressOrRelease) pointerEventPoint->setGrabberItem(item); } } else { diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index f66809cc8a..fa39eca9a6 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -168,9 +168,9 @@ public: void deliverPointerEvent(QQuickPointerEvent *); void deliverTouchEvent(QQuickPointerTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); - bool deliverPressEvent(QQuickPointerEvent *); + bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false); void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event); - void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent); + void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false); QVector pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const; QVector mergePointerTargets(const QVector &list1, const QVector &list2) const; -- cgit v1.2.3 From aa1830fc1706a45e2b6721da85f6484fec821484 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Mon, 22 May 2017 11:43:06 +0200 Subject: Change enum values to not start from 0 And allow potential combination of passive and exclusive grabbers Change-Id: I0bd2d6863305c3db7c91b064c0a58cd5e167470e Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 4b3587f18c..9ab6ea9955 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -276,13 +276,13 @@ public: Q_FLAG(States) enum GrabState { - GrabPassive = 0, - GrabExclusive, - UngrabPassive, - UngrabExclusive, - CancelGrabPassive, - CancelGrabExclusive, - OverrideGrabPassive + GrabPassive = 0x01, + UngrabPassive = 0x02, + CancelGrabPassive = 0x03, + OverrideGrabPassive = 0x04, + GrabExclusive = 0x10, + UngrabExclusive = 0x20, + CancelGrabExclusive = 0x30, }; Q_ENUM(GrabState) -- cgit v1.2.3 From 5d4f488bf30f5650051d6cc226a75dbd17cd9a70 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 24 Feb 2017 16:42:47 +0100 Subject: Move properties into grouped "point" property Change-Id: I80000110a2e0ca69210322a0fcc587d86158358e Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickdraghandler.cpp | 2 +- src/quick/handlers/qquickpointersinglehandler.cpp | 95 +++++++++++----------- src/quick/handlers/qquickpointersinglehandler_p.h | 99 +++++++++++++---------- src/quick/handlers/qquicktaphandler.cpp | 4 +- 4 files changed, 109 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp index 27edffd65a..b41e1f6c9d 100644 --- a/src/quick/handlers/qquickdraghandler.cpp +++ b/src/quick/handlers/qquickdraghandler.cpp @@ -69,7 +69,7 @@ QQuickDragHandler::~QQuickDragHandler() bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point) { // If we've already been interested in a point, stay interested, even if it has strayed outside bounds. - return ((point->state() != QQuickEventPoint::Pressed && pointId() == point->pointId()) + return ((point->state() != QQuickEventPoint::Pressed && this->point().id() == point->pointId()) || QQuickPointerSingleHandler::wantsEventPoint(point)); } diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 80632303f1..40a99b0e99 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -52,9 +52,6 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET) QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent) : QQuickPointerDeviceHandler(parent) - , m_pointId(0) - , m_rotation(0) - , m_pressure(0) , m_acceptedButtons(Qt::LeftButton) , m_ignoreAdditionalPoints(false) { @@ -68,7 +65,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0) return false; - if (m_pointId) { + if (m_pointInfo.m_id) { // We already know which one we want, so check whether it's there. // It's expected to be an update or a release. // If we no longer want it, cancel the grab. @@ -79,7 +76,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) QQuickEventPoint *p = event->point(i); if (wantsEventPoint(p)) { ++candidatePointCount; - if (p->pointId() == m_pointId) + if (p->pointId() == m_pointInfo.m_id) point = p; } } @@ -91,7 +88,7 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) point->cancelAllGrabs(this); } } else { - qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointId + qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id << "is missing from current event, but was neither canceled nor released"; return false; } @@ -109,56 +106,56 @@ bool QQuickPointerSingleHandler::wantsPointerEvent(QQuickPointerEvent *event) } } if (chosen && candidatePointCount == 1) { - m_pointId = chosen->pointId(); + m_pointInfo.m_id = chosen->pointId(); chosen->setAccepted(); } } - return m_pointId; + return m_pointInfo.m_id; } void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event) { QQuickPointerDeviceHandler::handlePointerEventImpl(event); - QQuickEventPoint *currentPoint = event->pointById(m_pointId); + QQuickEventPoint *currentPoint = event->pointById(m_pointInfo.m_id); Q_ASSERT(currentPoint); - if (!m_pointId || !currentPoint->isAccepted()) { + if (!m_pointInfo.m_id || !currentPoint->isAccepted()) { reset(); } else { if (event->asPointerTouchEvent()) { QQuickEventTouchPoint *tp = static_cast(currentPoint); - m_uniquePointId = tp->uniqueId(); - m_rotation = tp->rotation(); - m_pressure = tp->pressure(); - m_ellipseDiameters = tp->ellipseDiameters(); + m_pointInfo.m_uniqueId = tp->uniqueId(); + m_pointInfo.m_rotation = tp->rotation(); + m_pointInfo.m_pressure = tp->pressure(); + m_pointInfo.m_ellipseDiameters = tp->ellipseDiameters(); } else if (event->asPointerTabletEvent()) { // TODO } else { - m_uniquePointId = event->device()->uniqueId(); - m_rotation = 0; - m_pressure = event->buttons() ? 1 : 0; - m_ellipseDiameters = QSizeF(); + m_pointInfo.m_uniqueId = event->device()->uniqueId(); + m_pointInfo.m_rotation = 0; + m_pointInfo.m_pressure = event->buttons() ? 1 : 0; + m_pointInfo.m_ellipseDiameters = QSizeF(); } - m_pos = currentPoint->pos(); + m_pointInfo.m_position = currentPoint->pos(); + m_pointInfo.m_scenePosition = currentPoint->scenePos(); if (currentPoint->state() == QQuickEventPoint::Updated) - m_velocity = currentPoint->velocity(); + m_pointInfo.m_velocity = currentPoint->velocity(); handleEventPoint(currentPoint); switch (currentPoint->state()) { case QQuickEventPoint::Pressed: - m_pressPos = currentPoint->pos(); - m_scenePressPos = currentPoint->scenePos(); - setPressedButtons(event->buttons()); - emit pointIdChanged(); + m_pointInfo.m_pressPosition = currentPoint->pos(); + m_pointInfo.m_scenePressPosition = currentPoint->scenePos(); + m_pointInfo.m_pressedButtons = event->buttons(); break; case QQuickEventPoint::Released: setExclusiveGrab(currentPoint, false); reset(); break; default: - setPressedButtons(event->buttons()); + m_pointInfo.m_pressedButtons = event->buttons(); break; } + emit pointChanged(); } - emit eventPointHandled(); } bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point) @@ -172,12 +169,12 @@ void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQ return; switch (stateChange) { case QQuickEventPoint::GrabExclusive: - m_sceneGrabPos = point->sceneGrabPos(); + m_pointInfo.m_sceneGrabPosition = point->sceneGrabPos(); setActive(true); QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); break; case QQuickEventPoint::GrabPassive: - m_sceneGrabPos = point->sceneGrabPos(); + m_pointInfo.m_sceneGrabPosition = point->sceneGrabPos(); QQuickPointerHandler::onGrabChanged(grabber, stateChange, point); break; case QQuickEventPoint::OverrideGrabPassive: @@ -202,15 +199,8 @@ void QQuickPointerSingleHandler::setIgnoreAdditionalPoints(bool v) void QQuickPointerSingleHandler::moveTarget(QPointF pos, QQuickEventPoint *point) { target()->setPosition(pos); - m_pos = target()->mapFromScene(point->scenePos()); -} - -void QQuickPointerSingleHandler::setPressedButtons(Qt::MouseButtons buttons) -{ - if (buttons != m_pressedButtons) { - m_pressedButtons = buttons; - emit pressedButtonsChanged(); - } + m_pointInfo.m_scenePosition = point->scenePos(); + m_pointInfo.m_position = target()->mapFromScene(m_pointInfo.m_scenePosition); } void QQuickPointerSingleHandler::setAcceptedButtons(Qt::MouseButtons buttons) @@ -225,20 +215,31 @@ void QQuickPointerSingleHandler::setAcceptedButtons(Qt::MouseButtons buttons) void QQuickPointerSingleHandler::reset() { setActive(false); - bool pointIdChange = m_pointId != 0; - m_pointId = 0; - m_uniquePointId = QPointingDeviceUniqueId(); - m_pos = QPointF(); - m_pressPos = QPointF(); - m_scenePressPos = QPointF(); - m_sceneGrabPos = QPointF(); + m_pointInfo.reset(); +} + +QQuickHandlerPoint::QQuickHandlerPoint() + : m_id(0) + , m_rotation(0) + , m_pressure(0) +{} + +void QQuickHandlerPoint::reset() +{ + m_id = 0; + m_uniqueId = QPointingDeviceUniqueId(); + m_position = QPointF(); + m_scenePosition = QPointF(); + m_pressPosition = QPointF(); + m_scenePressPosition = QPointF(); + m_sceneGrabPosition = QPointF(); m_velocity = QVector2D(); m_rotation = 0; m_pressure = 0; m_ellipseDiameters = QSizeF(); - setPressedButtons(Qt::NoButton); - if (pointIdChange) - emit pointIdChanged(); + m_pressedButtons = Qt::NoButton; } +int g_metaTypeId = qRegisterMetaType(); + QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 06b8ec45dc..635c9e0a77 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -55,47 +55,74 @@ QT_BEGIN_NAMESPACE +class QQuickPointerSingleHandler; + +class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint { + Q_PROPERTY(int id READ id) + Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) + Q_PROPERTY(QPointF position READ position) + Q_PROPERTY(QPointF scenePosition READ scenePosition) + Q_PROPERTY(QPointF pressPosition READ pressPosition) + Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition) + Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition) + Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons) + Q_PROPERTY(QVector2D velocity READ velocity) + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(qreal pressure READ pressure) + Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) + Q_GADGET + +public: + QQuickHandlerPoint(); + + int id() const { return m_id; } + Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } + QPointF pressPosition() const { return m_pressPosition; } + QPointF scenePressPosition() const { return m_scenePressPosition; } + QPointF sceneGrabPosition() const { return m_sceneGrabPosition; } + QPointF position() const { return m_position; } + QPointF scenePosition() const { return m_scenePosition; } + QVector2D velocity() const { return m_velocity; } + qreal rotation() const { return m_rotation; } + qreal pressure() const { return m_pressure; } + QSizeF ellipseDiameters() const { return m_ellipseDiameters; } + QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; } + +private: + void reset(); + int m_id; + QPointingDeviceUniqueId m_uniqueId; + Qt::MouseButtons m_pressedButtons; + QPointF m_position; + QPointF m_scenePosition; + QPointF m_pressPosition; + QPointF m_scenePressPosition; + QPointF m_sceneGrabPosition; + QVector2D m_velocity; + qreal m_rotation; + qreal m_pressure; + QSizeF m_ellipseDiameters; + friend class QQuickPointerSingleHandler; +}; + class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : public QQuickPointerDeviceHandler { Q_OBJECT - Q_PROPERTY(int pointId READ pointId NOTIFY pointIdChanged) - Q_PROPERTY(QPointingDeviceUniqueId uniquePointId READ uniquePointId NOTIFY pointIdChanged) - Q_PROPERTY(QPointF pos READ pos NOTIFY eventPointHandled) - Q_PROPERTY(QPointF scenePos READ scenePos NOTIFY eventPointHandled) - Q_PROPERTY(QPointF pressPos READ pressPos NOTIFY pressedButtonsChanged) - Q_PROPERTY(QPointF scenePressPos READ scenePressPos NOTIFY pressedButtonsChanged) - Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos NOTIFY singlePointGrabChanged) - Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedButtonsChanged) - Q_PROPERTY(QVector2D velocity READ velocity NOTIFY eventPointHandled) - Q_PROPERTY(qreal rotation READ rotation NOTIFY eventPointHandled) - Q_PROPERTY(qreal pressure READ pressure NOTIFY eventPointHandled) - Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters NOTIFY eventPointHandled) Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) + Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged) public: explicit QQuickPointerSingleHandler(QObject *parent = 0); virtual ~QQuickPointerSingleHandler() { } - int pointId() const { return m_pointId; } - Qt::MouseButtons pressedButtons() const { return m_pressedButtons; } Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; } void setAcceptedButtons(Qt::MouseButtons buttons); - QPointF pressPos() const { return m_pressPos; } - QPointF scenePressPos() const { return m_scenePressPos; } - QPointF sceneGrabPos() const { return m_sceneGrabPos; } - QPointF pos() const { return m_pos; } - QPointF scenePos() const { return parentItem()->mapToScene(m_pos); } - QVector2D velocity() const { return m_velocity; } - qreal rotation() const { return m_rotation; } - qreal pressure() const { return m_pressure; } - QSizeF ellipseDiameters() const { return m_ellipseDiameters; } - QPointingDeviceUniqueId uniquePointId() const { return m_uniquePointId; } + + QQuickHandlerPoint point() const { return m_pointInfo; } Q_SIGNALS: - void pointIdChanged(); - void pressedButtonsChanged(); - void acceptedButtonsChanged(); + void pointChanged(); void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here - void eventPointHandled(); + void acceptedButtonsChanged(); protected: bool wantsPointerEvent(QQuickPointerEvent *event) override; @@ -103,7 +130,7 @@ protected: void handlePointerEventImpl(QQuickPointerEvent *event) override; virtual void handleEventPoint(QQuickEventPoint *point) = 0; - QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointId); } + QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointInfo.m_id); } void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override; void setIgnoreAdditionalPoints(bool v = true); @@ -111,27 +138,17 @@ protected: void moveTarget(QPointF pos, QQuickEventPoint *point); private: - void setPressedButtons(Qt::MouseButtons buttons); void reset(); private: - int m_pointId; - QPointingDeviceUniqueId m_uniquePointId; - Qt::MouseButtons m_pressedButtons; - QPointF m_pos; - QPointF m_pressPos; - QPointF m_scenePressPos; - QPointF m_sceneGrabPos; - QVector2D m_velocity; - qreal m_rotation; - qreal m_pressure; - QSizeF m_ellipseDiameters; + QQuickHandlerPoint m_pointInfo; Qt::MouseButtons m_acceptedButtons; bool m_ignoreAdditionalPoints : 1; }; QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickHandlerPoint) QML_DECLARE_TYPE(QQuickPointerSingleHandler) #endif // QQUICKPOINTERSINGLEHANDLER_H diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index d73e9fd100..272c6de000 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -128,7 +128,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) ret = parentContains(point); break; case ReleaseWithinBounds: - ret = point->pointId() == pointId(); + ret = point->pointId() == this->point().id(); break; } break; @@ -143,7 +143,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point) // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called. // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but // we still don't want to be pressed anymore. - if (!ret && point->pointId() == pointId() && point->state() != QQuickEventPoint::Stationary) + if (!ret && point->pointId() == this->point().id() && point->state() != QQuickEventPoint::Stationary) setPressed(false, true, point); return ret; } -- cgit v1.2.3 From 0e3adb65b0e9c44fa6e202630ff57c907ecf0820 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Mon, 22 May 2017 15:34:18 +0200 Subject: Fix some problems with MouseDblClick handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sequence of events delivered from a double click is: [Press,Release,Press,DblClick,Release] The problem was that a DblClick was delivered just like a press event, so it would clear the passive grabber that was established because of the former Press event. When the Release event then got processed, there was therefore a risk that the Release event was not delivered to the passive grabber. The fix is to not deliver DblClick events at all to handlers, and to not deliver DblClick to items if the former Press event was accepted by a handler. Change-Id: I49c0e32ef4e33f7b6014d35dc065da2527b94779 Reviewed-by: Shawn Rutledge Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 9 ++++++++- src/quick/items/qquickevents_p_p.h | 2 ++ src/quick/items/qquickwindow.cpp | 10 ++++++++-- src/quick/items/qquickwindow_p.h | 1 + 4 files changed, 19 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index bde271882c..830adc50b7 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -879,8 +879,9 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event) Qt::TouchPointState state = Qt::TouchPointStationary; switch (ev->type()) { case QEvent::MouseButtonPress: - case QEvent::MouseButtonDblClick: m_mousePoint->clearPassiveGrabbers(); + Q_FALLTHROUGH(); + case QEvent::MouseButtonDblClick: state = Qt::TouchPointPressed; break; case QEvent::MouseButtonRelease: @@ -1055,6 +1056,12 @@ bool QQuickPointerMouseEvent::isPressEvent() const (me->buttons() & me->button()) == me->buttons()); } +bool QQuickPointerMouseEvent::isDoubleClickEvent() const +{ + auto me = static_cast(m_event); + return (me->type() == QEvent::MouseButtonDblClick); +} + bool QQuickPointerMouseEvent::isUpdateEvent() const { auto me = static_cast(m_event); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 9ab6ea9955..19b2a6221e 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -407,6 +407,7 @@ public: // helpers for C++ only (during event delivery) virtual void localize(QQuickItem *target) = 0; virtual bool isPressEvent() const = 0; + virtual bool isDoubleClickEvent() const { return false; } virtual bool isUpdateEvent() const = 0; virtual bool isReleaseEvent() const = 0; virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; } @@ -451,6 +452,7 @@ public: QQuickPointerEvent *reset(QEvent *) override; void localize(QQuickItem *target) override; bool isPressEvent() const override; + bool isDoubleClickEvent() const override; bool isUpdateEvent() const override; bool isReleaseEvent() const override; QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e4eed8454c..713df4c500 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -503,6 +503,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , lastWheelEventAccepted(false) , componentCompleted(true) , allowChildEventFiltering(true) + , allowDoubleClick(true) , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) @@ -2092,7 +2093,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - deliverPointerEvent(pointerEventInstance(event)); + if (allowDoubleClick) + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2404,7 +2406,11 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo pointerEvent->localize(item); // Let the Item's handlers (if any) have the event first. - itemPrivate->handlePointerEvent(pointerEvent); + // However, double click should never be delivered to handlers. + if (!pointerEvent->isDoubleClickEvent()) { + itemPrivate->handlePointerEvent(pointerEvent); + allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); + } if (handlersOnly) return; if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index fa39eca9a6..a40301bcfa 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -262,6 +262,7 @@ public: bool componentCompleted : 1; bool allowChildEventFiltering : 1; + bool allowDoubleClick : 1; Qt::FocusReason lastFocusReason; -- cgit v1.2.3 From 08dc25c023af53f96a909bda5f822ea0905461ad Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 16 May 2017 10:21:01 +0200 Subject: Add tst_multipointtoucharea_interop autotest to test interoperability of PointerHandlers with conventional touch- handling Items (with MultiPointTouchArea being the prototypical instance) Change-Id: Id19f312b17b70df072d66cd91816d2b19250a500 Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickmultipointerhandler_p.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h index 1f2d213e56..1bbc2f2fa3 100644 --- a/src/quick/handlers/qquickmultipointerhandler_p.h +++ b/src/quick/handlers/qquickmultipointerhandler_p.h @@ -54,7 +54,6 @@ #include "qquickitem.h" #include "qevent.h" #include "qquickpointerdevicehandler_p.h" -#include "../items/qquickmultipointtoucharea_p.h" QT_BEGIN_NAMESPACE -- cgit v1.2.3 From 11f5478fd320aff0ca1e31e4784fefed3d88dcb1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 30 May 2017 11:06:30 +0200 Subject: PointerHandlers plugin: fix the build on iOS The version suffix isn't necessary, and causes a link error when building applications. Change-Id: I4725b9190e0ea1fb4b2c4c5b752e40d5be2fedc3 Reviewed-by: Richard Moe Gustavsen --- src/imports/handlers/plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/handlers/plugin.cpp b/src/imports/handlers/plugin.cpp index 21b9764611..bc1ae244d3 100644 --- a/src/imports/handlers/plugin.cpp +++ b/src/imports/handlers/plugin.cpp @@ -44,7 +44,7 @@ static void initResources() { #ifdef QT_STATIC - Q_INIT_RESOURCE(qmake_Qt_labs_handlers_1); + Q_INIT_RESOURCE(qmake_Qt_labs_handlers); #endif } -- cgit v1.2.3 From 961da5273e17655e73ec0975c6de446b88d7f5ca Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 31 May 2017 12:42:09 +0200 Subject: Rename to Shape/ShapePath and remove public JS API Change-Id: I299354da0632fb0b8487cfb13748ed58b97d75fd Reviewed-by: Andy Nichols --- src/imports/imports.pro | 2 +- src/imports/pathitem/pathitem.pro | 31 - src/imports/pathitem/plugin.cpp | 74 - src/imports/pathitem/plugins.qmltypes | 312 --- src/imports/pathitem/qmldir | 4 - src/imports/pathitem/qquicknvprfunctions.cpp | 284 --- src/imports/pathitem/qquicknvprfunctions_p.h | 406 ---- src/imports/pathitem/qquicknvprfunctions_p_p.h | 70 - src/imports/pathitem/qquickpathitem.cpp | 2258 -------------------- src/imports/pathitem/qquickpathitem_p.h | 333 --- src/imports/pathitem/qquickpathitem_p_p.h | 282 --- .../pathitem/qquickpathitemgenericrenderer.cpp | 775 ------- .../pathitem/qquickpathitemgenericrenderer_p.h | 303 --- .../pathitem/qquickpathitemnvprrenderer.cpp | 923 -------- .../pathitem/qquickpathitemnvprrenderer_p.h | 237 -- .../pathitem/qquickpathitemsoftwarerenderer.cpp | 277 --- .../pathitem/qquickpathitemsoftwarerenderer_p.h | 136 -- src/imports/shapes/plugin.cpp | 74 + src/imports/shapes/plugins.qmltypes | 292 +++ src/imports/shapes/qmldir | 4 + src/imports/shapes/qquicknvprfunctions.cpp | 277 +++ src/imports/shapes/qquicknvprfunctions_p.h | 400 ++++ src/imports/shapes/qquicknvprfunctions_p_p.h | 70 + src/imports/shapes/qquickshape.cpp | 1485 +++++++++++++ src/imports/shapes/qquickshape_p.h | 327 +++ src/imports/shapes/qquickshape_p_p.h | 282 +++ src/imports/shapes/qquickshapegenericrenderer.cpp | 775 +++++++ src/imports/shapes/qquickshapegenericrenderer_p.h | 303 +++ src/imports/shapes/qquickshapenvprrenderer.cpp | 923 ++++++++ src/imports/shapes/qquickshapenvprrenderer_p.h | 237 ++ src/imports/shapes/qquickshapesoftwarerenderer.cpp | 277 +++ src/imports/shapes/qquickshapesoftwarerenderer_p.h | 136 ++ src/imports/shapes/shapes.pro | 31 + src/quick/util/qquickpath.cpp | 16 +- 34 files changed, 5902 insertions(+), 6714 deletions(-) delete mode 100644 src/imports/pathitem/pathitem.pro delete mode 100644 src/imports/pathitem/plugin.cpp delete mode 100644 src/imports/pathitem/plugins.qmltypes delete mode 100644 src/imports/pathitem/qmldir delete mode 100644 src/imports/pathitem/qquicknvprfunctions.cpp delete mode 100644 src/imports/pathitem/qquicknvprfunctions_p.h delete mode 100644 src/imports/pathitem/qquicknvprfunctions_p_p.h delete mode 100644 src/imports/pathitem/qquickpathitem.cpp delete mode 100644 src/imports/pathitem/qquickpathitem_p.h delete mode 100644 src/imports/pathitem/qquickpathitem_p_p.h delete mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemgenericrenderer_p.h delete mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemnvprrenderer_p.h delete mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp delete mode 100644 src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h create mode 100644 src/imports/shapes/plugin.cpp create mode 100644 src/imports/shapes/plugins.qmltypes create mode 100644 src/imports/shapes/qmldir create mode 100644 src/imports/shapes/qquicknvprfunctions.cpp create mode 100644 src/imports/shapes/qquicknvprfunctions_p.h create mode 100644 src/imports/shapes/qquicknvprfunctions_p_p.h create mode 100644 src/imports/shapes/qquickshape.cpp create mode 100644 src/imports/shapes/qquickshape_p.h create mode 100644 src/imports/shapes/qquickshape_p_p.h create mode 100644 src/imports/shapes/qquickshapegenericrenderer.cpp create mode 100644 src/imports/shapes/qquickshapegenericrenderer_p.h create mode 100644 src/imports/shapes/qquickshapenvprrenderer.cpp create mode 100644 src/imports/shapes/qquickshapenvprrenderer_p.h create mode 100644 src/imports/shapes/qquickshapesoftwarerenderer.cpp create mode 100644 src/imports/shapes/qquickshapesoftwarerenderer_p.h create mode 100644 src/imports/shapes/shapes.pro (limited to 'src') diff --git a/src/imports/imports.pro b/src/imports/imports.pro index df0ad01c06..3c7f96eff9 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -23,7 +23,7 @@ qtHaveModule(quick) { qtConfig(quick-particles): \ SUBDIRS += particles - SUBDIRS += pathitem + SUBDIRS += shapes } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/imports/pathitem/pathitem.pro b/src/imports/pathitem/pathitem.pro deleted file mode 100644 index d70bb6f203..0000000000 --- a/src/imports/pathitem/pathitem.pro +++ /dev/null @@ -1,31 +0,0 @@ -CXX_MODULE = qml -TARGET = qmlpathitemplugin -TARGETPATH = Qt/labs/pathitem -IMPORT_VERSION = 1.0 - -QT = core gui qml quick quick-private - -HEADERS += \ - qquickpathitem_p.h \ - qquickpathitem_p_p.h \ - qquickpathitemgenericrenderer_p.h \ - qquickpathitemsoftwarerenderer_p.h - -SOURCES += \ - plugin.cpp \ - qquickpathitem.cpp \ - qquickpathitemgenericrenderer.cpp \ - qquickpathitemsoftwarerenderer.cpp - -qtConfig(opengl) { - HEADERS += \ - qquicknvprfunctions_p.h \ - qquicknvprfunctions_p_p.h \ - qquickpathitemnvprrenderer_p.h - - SOURCES += \ - qquicknvprfunctions.cpp \ - qquickpathitemnvprrenderer.cpp -} - -load(qml_plugin) diff --git a/src/imports/pathitem/plugin.cpp b/src/imports/pathitem/plugin.cpp deleted file mode 100644 index 6b43e8f398..0000000000 --- a/src/imports/pathitem/plugin.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 -#include - -#include "qquickpathitem_p.h" - -static void initResources() -{ -#ifdef QT_STATIC - Q_INIT_RESOURCE(qmake_Qt_labs_pathitem); -#endif -} - -QT_BEGIN_NAMESPACE - -class QmlPathItemPlugin : public QQmlExtensionPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) - -public: - QmlPathItemPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } - void registerTypes(const char *uri) Q_DECL_OVERRIDE - { - Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.pathitem")); - qmlRegisterType(uri, 1, 0, "PathItem"); - qmlRegisterType(uri, 1, 0, "VisualPath"); - qmlRegisterType(uri, 1, 0, "PathGradientStop"); - qmlRegisterUncreatableType(uri, 1, 0, "PathGradient", QQuickPathGradient::tr("PathGradient is an abstract base class")); - qmlRegisterType(uri, 1, 0, "PathLinearGradient"); - } -}; - -QT_END_NAMESPACE - -#include "plugin.moc" diff --git a/src/imports/pathitem/plugins.qmltypes b/src/imports/pathitem/plugins.qmltypes deleted file mode 100644 index 03f26e243c..0000000000 --- a/src/imports/pathitem/plugins.qmltypes +++ /dev/null @@ -1,312 +0,0 @@ -import QtQuick.tooling 1.2 - -// This file describes the plugin-supplied types contained in the library. -// It is used for QML tooling purposes only. -// -// This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.pathitem 1.0' - -Module { - dependencies: [] - Component { - name: "QQuickItem" - defaultProperty: "data" - prototype: "QObject" - Enum { - name: "TransformOrigin" - values: { - "TopLeft": 0, - "Top": 1, - "TopRight": 2, - "Left": 3, - "Center": 4, - "Right": 5, - "BottomLeft": 6, - "Bottom": 7, - "BottomRight": 8 - } - } - Property { name: "parent"; type: "QQuickItem"; isPointer: true } - Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "x"; type: "double" } - Property { name: "y"; type: "double" } - Property { name: "z"; type: "double" } - Property { name: "width"; type: "double" } - Property { name: "height"; type: "double" } - Property { name: "opacity"; type: "double" } - Property { name: "enabled"; type: "bool" } - Property { name: "visible"; type: "bool" } - Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } - Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } - Property { name: "state"; type: "string" } - Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } - Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } - Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baselineOffset"; type: "double" } - Property { name: "clip"; type: "bool" } - Property { name: "focus"; type: "bool" } - Property { name: "activeFocus"; type: "bool"; isReadonly: true } - Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } - Property { name: "rotation"; type: "double" } - Property { name: "scale"; type: "double" } - Property { name: "transformOrigin"; type: "TransformOrigin" } - Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } - Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } - Property { name: "smooth"; type: "bool" } - Property { name: "antialiasing"; type: "bool" } - Property { name: "implicitWidth"; type: "double" } - Property { name: "implicitHeight"; type: "double" } - Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } - Signal { - name: "childrenRectChanged" - Parameter { type: "QRectF" } - } - Signal { - name: "baselineOffsetChanged" - Parameter { type: "double" } - } - Signal { - name: "stateChanged" - Parameter { type: "string" } - } - Signal { - name: "focusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusOnTabChanged" - revision: 1 - Parameter { type: "bool" } - } - Signal { - name: "parentChanged" - Parameter { type: "QQuickItem"; isPointer: true } - } - Signal { - name: "transformOriginChanged" - Parameter { type: "TransformOrigin" } - } - Signal { - name: "smoothChanged" - Parameter { type: "bool" } - } - Signal { - name: "antialiasingChanged" - Parameter { type: "bool" } - } - Signal { - name: "clipChanged" - Parameter { type: "bool" } - } - Signal { - name: "windowChanged" - revision: 1 - Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } - } - Method { name: "update" } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - Parameter { name: "targetSize"; type: "QSize" } - } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - } - Method { - name: "contains" - type: "bool" - Parameter { name: "point"; type: "QPointF" } - } - Method { - name: "mapFromItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapFromGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { name: "forceActiveFocus" } - Method { - name: "forceActiveFocus" - Parameter { name: "reason"; type: "Qt::FocusReason" } - } - Method { - name: "nextItemInFocusChain" - revision: 1 - type: "QQuickItem*" - Parameter { name: "forward"; type: "bool" } - } - Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } - Method { - name: "childAt" - type: "QQuickItem*" - Parameter { name: "x"; type: "double" } - Parameter { name: "y"; type: "double" } - } - } - Component { - name: "QQuickPathGradient" - defaultProperty: "stops" - prototype: "QObject" - exports: ["Qt.labs.pathitem/PathGradient 1.0"] - isCreatable: false - exportMetaObjectRevisions: [0] - Enum { - name: "SpreadMode" - values: { - "PadSpread": 0, - "RepeatSpread": 1, - "ReflectSpread": 2 - } - } - Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "spread"; type: "SpreadMode" } - Signal { name: "updated" } - } - Component { - name: "QQuickPathGradientStop" - prototype: "QObject" - exports: ["Qt.labs.pathitem/PathGradientStop 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "position"; type: "double" } - Property { name: "color"; type: "QColor" } - } - Component { - name: "QQuickPathItem" - defaultProperty: "elements" - prototype: "QQuickItem" - exports: ["Qt.labs.pathitem/PathItem 1.0"] - exportMetaObjectRevisions: [0] - Enum { - name: "RendererType" - values: { - "UnknownRenderer": 0, - "GeometryRenderer": 1, - "NvprRenderer": 2, - "SoftwareRenderer": 3 - } - } - Enum { - name: "Status" - values: { - "Null": 0, - "Ready": 1, - "Processing": 2 - } - } - Property { name: "renderer"; type: "RendererType"; isReadonly: true } - Property { name: "asynchronous"; type: "bool" } - Property { name: "enableVendorExtensions"; type: "bool" } - Property { name: "status"; type: "Status"; isReadonly: true } - Property { name: "elements"; type: "QQuickVisualPath"; isList: true; isReadonly: true } - Method { - name: "newPath" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "newStrokeFillParams" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "clearVisualPaths" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "commitVisualPaths" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "appendVisualPath" - Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } - } - } - Component { - name: "QQuickPathLinearGradient" - defaultProperty: "stops" - prototype: "QQuickPathGradient" - exports: ["Qt.labs.pathitem/PathLinearGradient 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "x1"; type: "double" } - Property { name: "y1"; type: "double" } - Property { name: "x2"; type: "double" } - Property { name: "y2"; type: "double" } - } - Component { - name: "QQuickVisualPath" - defaultProperty: "path" - prototype: "QObject" - exports: ["Qt.labs.pathitem/VisualPath 1.0"] - exportMetaObjectRevisions: [0] - Enum { - name: "FillRule" - values: { - "OddEvenFill": 0, - "WindingFill": 1 - } - } - Enum { - name: "JoinStyle" - values: { - "MiterJoin": 0, - "BevelJoin": 64, - "RoundJoin": 128 - } - } - Enum { - name: "CapStyle" - values: { - "FlatCap": 0, - "SquareCap": 16, - "RoundCap": 32 - } - } - Enum { - name: "StrokeStyle" - values: { - "SolidLine": 1, - "DashLine": 2 - } - } - Property { name: "path"; type: "QQuickPath"; isPointer: true } - Property { name: "strokeColor"; type: "QColor" } - Property { name: "strokeWidth"; type: "double" } - Property { name: "fillColor"; type: "QColor" } - Property { name: "fillRule"; type: "FillRule" } - Property { name: "joinStyle"; type: "JoinStyle" } - Property { name: "miterLimit"; type: "int" } - Property { name: "capStyle"; type: "CapStyle" } - Property { name: "strokeStyle"; type: "StrokeStyle" } - Property { name: "dashOffset"; type: "double" } - Property { name: "dashPattern"; type: "QVector" } - Property { name: "fillGradient"; type: "QQuickPathGradient"; isPointer: true } - Signal { name: "changed" } - } -} diff --git a/src/imports/pathitem/qmldir b/src/imports/pathitem/qmldir deleted file mode 100644 index 277b8a199b..0000000000 --- a/src/imports/pathitem/qmldir +++ /dev/null @@ -1,4 +0,0 @@ -module Qt.labs.pathitem -plugin qmlpathitemplugin -classname QmlPathItemPlugin -typeinfo plugins.qmltypes diff --git a/src/imports/pathitem/qquicknvprfunctions.cpp b/src/imports/pathitem/qquicknvprfunctions.cpp deleted file mode 100644 index 40eb2bb932..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions.cpp +++ /dev/null @@ -1,284 +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 "qquicknvprfunctions_p.h" - -#ifndef QT_NO_OPENGL - -#include -#include -#include -#include "qquicknvprfunctions_p_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QQuickNvprFunctions - - \brief Function resolvers and other helpers for GL_NV_path_rendering - for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner - that does not distract builds that do not have NVPR support either at - compile or run time. - - \internal - */ - -QQuickNvprFunctions::QQuickNvprFunctions() - : d(new QQuickNvprFunctionsPrivate(this)) -{ -} - -QQuickNvprFunctions::~QQuickNvprFunctions() -{ - delete d; -} - -/*! - \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top - of OpenGL 4.3 or OpenGL ES 3.1. - */ -QSurfaceFormat QQuickNvprFunctions::format() -{ - QSurfaceFormat fmt; - fmt.setDepthBufferSize(24); - fmt.setStencilBufferSize(8); - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - fmt.setVersion(3, 1); - } - return fmt; -} - -/*! - \return true if GL_NV_path_rendering is supported with the current OpenGL - context. - - When there is no current context, a temporary dummy one will be created and - made current. - */ -bool QQuickNvprFunctions::isSupported() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - QScopedPointer tempContext; - QScopedPointer tempSurface; - if (!ctx) { - tempContext.reset(new QOpenGLContext); - if (!tempContext->create()) - return false; - ctx = tempContext.data(); - tempSurface.reset(new QOffscreenSurface); - tempSurface->setFormat(ctx->format()); - tempSurface->create(); - if (!ctx->makeCurrent(tempSurface.data())) - return false; - } - - if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) - return false; - - // Do not check for DSA as the string may not be exposed on ES - // drivers, yet the functions we need are resolvable. -#if 0 - if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { - qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); - return false; - } -#endif - - return true; -} - -/*! - Initializes using the current OpenGL context. - - \return true when GL_NV_path_rendering is supported and initialization was - successful. - */ -bool QQuickNvprFunctions::create() -{ - return isSupported() && d->resolve(); -} - -/*! - Creates a program pipeline consisting of a separable fragment shader program. - - This is essential for using NVPR with OpenGL ES 3.1+ since normal, - GLES2-style programs would not work without a vertex shader. - - \note \a fragmentShaderSource should be a \c{version 310 es} shader since - this works both on desktop and embedded NVIDIA drivers, thus avoiding the - need to fight GLSL and GLSL ES differences. - - The pipeline object is stored into \a pipeline, the fragment shader program - into \a program. - - Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. - - \return \c false on failure in which case the error log is printed on the - debug output. \c true on success. - */ -bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - if (!ctx) - return false; - - QOpenGLExtraFunctions *f = ctx->extraFunctions(); - *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); - GLint status = 0; - f->glGetProgramiv(*program, GL_LINK_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); - qWarning("Failed to create separable shader program:\n%s", s.constData()); - } - return false; - } - - f->glGenProgramPipelines(1, pipeline); - f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); - f->glActiveShaderProgram(*pipeline, *program); - - f->glValidateProgramPipeline(*pipeline); - status = 0; - f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); - if (!status) { - GLint len = 0; - f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); - if (len) { - QByteArray s; - s.resize(len); - f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); - qWarning("Program pipeline validation failed:\n%s", s.constData()); - } - return false; - } - - return true; -} - -#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) - -bool QQuickNvprFunctionsPrivate::resolve() -{ - QOpenGLContext *ctx = QOpenGLContext::currentContext(); - - q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); - q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); - q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); - q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); - q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); - q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); - q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); - q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); - q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); - q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); - q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); - q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); - q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); - q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); - q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); - q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); - q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); - q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); - q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); - q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); - q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); - q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); - q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); - q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); - q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); - q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); - q->pathColorGen = PROC(PFNGLPATHCOLORGENNVPROC, glPathColorGenNV); - q->pathTexGen = PROC(PFNGLPATHTEXGENNVPROC, glPathTexGenNV); - q->pathFogGen = PROC(PFNGLPATHFOGGENNVPROC, glPathFogGenNV); - q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); - q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); - q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); - q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); - q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); - q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); - q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); - q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); - q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); - q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); - q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); - q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); - q->getPathColorgeniv = PROC(PFNGLGETPATHCOLORGENIVNVPROC, glGetPathColorGenivNV); - q->getPathColorgenfv = PROC(PFNGLGETPATHCOLORGENFVNVPROC, glGetPathColorGenfvNV); - q->getPathTexGeniv = PROC(PFNGLGETPATHTEXGENIVNVPROC, glGetPathTexGenivNV); - q->getPathTexGenfv = PROC(PFNGLGETPATHTEXGENFVNVPROC, glGetPathTexGenfvNV); - q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); - q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); - q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); - q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); - q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); - q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); - q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); - q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); - q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); - q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); - q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); - q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); - q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); - q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); - q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); - q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); - q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); - q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); - q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); - - q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); - q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); - - return q->genPaths != nullptr // base path rendering ext - && q->programPathFragmentInputGen != nullptr // updated path rendering ext - && q->matrixLoadf != nullptr // direct state access ext - && q->matrixLoadIdentity != nullptr; -} - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL diff --git a/src/imports/pathitem/qquicknvprfunctions_p.h b/src/imports/pathitem/qquicknvprfunctions_p.h deleted file mode 100644 index 7900388305..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions_p.h +++ /dev/null @@ -1,406 +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 QQUICKNVPRFUNCTIONS_P_H -#define QQUICKNVPRFUNCTIONS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -#ifndef GL_NV_path_rendering -#define GL_PATH_FORMAT_SVG_NV 0x9070 -#define GL_PATH_FORMAT_PS_NV 0x9071 -#define GL_STANDARD_FONT_NAME_NV 0x9072 -#define GL_SYSTEM_FONT_NAME_NV 0x9073 -#define GL_FILE_NAME_NV 0x9074 -#define GL_PATH_STROKE_WIDTH_NV 0x9075 -#define GL_PATH_END_CAPS_NV 0x9076 -#define GL_PATH_INITIAL_END_CAP_NV 0x9077 -#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 -#define GL_PATH_JOIN_STYLE_NV 0x9079 -#define GL_PATH_MITER_LIMIT_NV 0x907A -#define GL_PATH_DASH_CAPS_NV 0x907B -#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C -#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D -#define GL_PATH_DASH_OFFSET_NV 0x907E -#define GL_PATH_CLIENT_LENGTH_NV 0x907F -#define GL_PATH_FILL_MODE_NV 0x9080 -#define GL_PATH_FILL_MASK_NV 0x9081 -#define GL_PATH_FILL_COVER_MODE_NV 0x9082 -#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 -#define GL_PATH_STROKE_MASK_NV 0x9084 -#define GL_COUNT_UP_NV 0x9088 -#define GL_COUNT_DOWN_NV 0x9089 -#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A -#define GL_CONVEX_HULL_NV 0x908B -#define GL_BOUNDING_BOX_NV 0x908D -#define GL_TRANSLATE_X_NV 0x908E -#define GL_TRANSLATE_Y_NV 0x908F -#define GL_TRANSLATE_2D_NV 0x9090 -#define GL_TRANSLATE_3D_NV 0x9091 -#define GL_AFFINE_2D_NV 0x9092 -#define GL_AFFINE_3D_NV 0x9094 -#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 -#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 -#define GL_UTF8_NV 0x909A -#define GL_UTF16_NV 0x909B -#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C -#define GL_PATH_COMMAND_COUNT_NV 0x909D -#define GL_PATH_COORD_COUNT_NV 0x909E -#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F -#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 -#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 -#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 -#define GL_SQUARE_NV 0x90A3 -#define GL_ROUND_NV 0x90A4 -#define GL_TRIANGULAR_NV 0x90A5 -#define GL_BEVEL_NV 0x90A6 -#define GL_MITER_REVERT_NV 0x90A7 -#define GL_MITER_TRUNCATE_NV 0x90A8 -#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 -#define GL_USE_MISSING_GLYPH_NV 0x90AA -#define GL_PATH_ERROR_POSITION_NV 0x90AB -#define GL_PATH_FOG_GEN_MODE_NV 0x90AC -#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD -#define GL_ADJACENT_PAIRS_NV 0x90AE -#define GL_FIRST_TO_REST_NV 0x90AF -#define GL_PATH_GEN_MODE_NV 0x90B0 -#define GL_PATH_GEN_COEFF_NV 0x90B1 -#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 -#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 -#define GL_PATH_STENCIL_FUNC_NV 0x90B7 -#define GL_PATH_STENCIL_REF_NV 0x90B8 -#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 -#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD -#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE -#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF -#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 -#define GL_MOVE_TO_RESETS_NV 0x90B5 -#define GL_MOVE_TO_CONTINUES_NV 0x90B6 -#define GL_CLOSE_PATH_NV 0x00 -#define GL_MOVE_TO_NV 0x02 -#define GL_RELATIVE_MOVE_TO_NV 0x03 -#define GL_LINE_TO_NV 0x04 -#define GL_RELATIVE_LINE_TO_NV 0x05 -#define GL_HORIZONTAL_LINE_TO_NV 0x06 -#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 -#define GL_VERTICAL_LINE_TO_NV 0x08 -#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 -#define GL_QUADRATIC_CURVE_TO_NV 0x0A -#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B -#define GL_CUBIC_CURVE_TO_NV 0x0C -#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D -#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E -#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F -#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 -#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 -#define GL_SMALL_CCW_ARC_TO_NV 0x12 -#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 -#define GL_SMALL_CW_ARC_TO_NV 0x14 -#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 -#define GL_LARGE_CCW_ARC_TO_NV 0x16 -#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 -#define GL_LARGE_CW_ARC_TO_NV 0x18 -#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 -#define GL_RESTART_PATH_NV 0xF0 -#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 -#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 -#define GL_RECT_NV 0xF6 -#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 -#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA -#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC -#define GL_ARC_TO_NV 0xFE -#define GL_RELATIVE_ARC_TO_NV 0xFF -#define GL_BOLD_BIT_NV 0x01 -#define GL_ITALIC_BIT_NV 0x02 -#define GL_GLYPH_WIDTH_BIT_NV 0x01 -#define GL_GLYPH_HEIGHT_BIT_NV 0x02 -#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 -#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 -#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 -#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 -#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 -#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 -#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 -#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 -#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 -#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 -#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 -#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 -#define GL_FONT_ASCENDER_BIT_NV 0x00200000 -#define GL_FONT_DESCENDER_BIT_NV 0x00400000 -#define GL_FONT_HEIGHT_BIT_NV 0x00800000 -#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 -#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 -#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 -#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 -#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 -#define GL_PRIMARY_COLOR_NV 0x852C -#define GL_SECONDARY_COLOR_NV 0x852D -#define GL_ROUNDED_RECT_NV 0xE8 -#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 -#define GL_ROUNDED_RECT2_NV 0xEA -#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB -#define GL_ROUNDED_RECT4_NV 0xEC -#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED -#define GL_ROUNDED_RECT8_NV 0xEE -#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF -#define GL_RELATIVE_RECT_NV 0xF7 -#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 -#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 -#define GL_FONT_UNAVAILABLE_NV 0x936A -#define GL_FONT_UNINTELLIGIBLE_NV 0x936B -#define GL_CONIC_CURVE_TO_NV 0x1A -#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B -#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 -#define GL_STANDARD_FONT_FORMAT_NV 0x936C -#define GL_2_BYTES_NV 0x1407 -#define GL_3_BYTES_NV 0x1408 -#define GL_4_BYTES_NV 0x1409 -#define GL_EYE_LINEAR_NV 0x2400 -#define GL_OBJECT_LINEAR_NV 0x2401 -#define GL_CONSTANT_NV 0x8576 -#define GL_PATH_PROJECTION_NV 0x1701 -#define GL_PATH_MODELVIEW_NV 0x1700 -#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 -#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 -#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 -#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 -#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 -#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 -#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 -#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 -#define GL_FRAGMENT_INPUT_NV 0x936D - -typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); -typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); -typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); -typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); -typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); -typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); -typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); -typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); -typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); -#endif - -#ifndef GL_FLAT -#define GL_FLAT 0x1D00 -#endif - -#ifndef GL_INVERT -#define GL_INVERT 0x150A -#endif - -#ifndef GL_EXT_direct_state_access -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); -typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); -#endif - -// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR -// code path even though it's never used. Keep it compiling by defining the -// necessary ES 3.1 separable program constants. -#ifndef GL_FRAGMENT_SHADER_BIT -#define GL_FRAGMENT_SHADER_BIT 0x00000002 -#endif -#ifndef GL_UNIFORM -#define GL_UNIFORM 0x92E1 -#endif - -class QQuickNvprFunctionsPrivate; - -class QQuickNvprFunctions -{ -public: - QQuickNvprFunctions(); - ~QQuickNvprFunctions(); - - static QSurfaceFormat format(); - static bool isSupported(); - - bool create(); - - bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); - - PFNGLGENPATHSNVPROC genPaths = nullptr; - PFNGLDELETEPATHSNVPROC deletePaths = nullptr; - PFNGLISPATHNVPROC isPath = nullptr; - PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; - PFNGLPATHCOORDSNVPROC pathCoords = nullptr; - PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; - PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; - PFNGLPATHSTRINGNVPROC pathString = nullptr; - PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; - PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; - PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; - PFNGLCOPYPATHNVPROC copyPath = nullptr; - PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; - PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; - PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; - PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; - PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; - PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; - PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; - PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; - PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; - PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; - PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; - PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; - PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; - PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; - PFNGLPATHCOLORGENNVPROC pathColorGen = nullptr; - PFNGLPATHTEXGENNVPROC pathTexGen = nullptr; - PFNGLPATHFOGGENNVPROC pathFogGen = nullptr; - PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; - PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; - PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; - PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; - PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; - PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; - PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; - PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; - PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; - PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; - PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; - PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; - PFNGLGETPATHCOLORGENIVNVPROC getPathColorgeniv = nullptr; - PFNGLGETPATHCOLORGENFVNVPROC getPathColorgenfv = nullptr; - PFNGLGETPATHTEXGENIVNVPROC getPathTexGeniv = nullptr; - PFNGLGETPATHTEXGENFVNVPROC getPathTexGenfv = nullptr; - PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; - PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; - PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; - PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; - PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; - PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; - PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; - PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; - PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; - PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; - PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; - PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; - PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; - PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; - PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; - PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; - PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; - - PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; - PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; - -private: - QQuickNvprFunctionsPrivate *d; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/pathitem/qquicknvprfunctions_p_p.h b/src/imports/pathitem/qquicknvprfunctions_p_p.h deleted file mode 100644 index 6df20566af..0000000000 --- a/src/imports/pathitem/qquicknvprfunctions_p_p.h +++ /dev/null @@ -1,70 +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 QQUICKNVPRFUNCTIONS_P_P_H -#define QQUICKNVPRFUNCTIONS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquicknvprfunctions_p.h" - -QT_BEGIN_NAMESPACE - -class QQuickNvprFunctionsPrivate -{ -public: - QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } - - bool resolve(); - - QQuickNvprFunctions *q; -}; - -QT_END_NAMESPACE - -#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/pathitem/qquickpathitem.cpp b/src/imports/pathitem/qquickpathitem.cpp deleted file mode 100644 index 5255a55798..0000000000 --- a/src/imports/pathitem/qquickpathitem.cpp +++ /dev/null @@ -1,2258 +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 "qquickpathitem_p.h" -#include "qquickpathitem_p_p.h" -#include "qquickpathitemgenericrenderer_p.h" -#include "qquickpathitemnvprrenderer_p.h" -#include "qquickpathitemsoftwarerenderer_p.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QQuickPathItemStrokeFillParams::QQuickPathItemStrokeFillParams() - : strokeColor(Qt::white), - strokeWidth(1), - fillColor(Qt::white), - fillRule(QQuickVisualPath::OddEvenFill), - joinStyle(QQuickVisualPath::BevelJoin), - miterLimit(2), - capStyle(QQuickVisualPath::SquareCap), - strokeStyle(QQuickVisualPath::SolidLine), - dashOffset(0), - fillGradient(nullptr) -{ - dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space -} - -QPainterPath QQuickPathItemPath::toPainterPath() const -{ - QPainterPath p; - int coordIdx = 0; - for (int i = 0; i < cmd.count(); ++i) { - switch (cmd[i]) { - case QQuickPathItemPath::MoveTo: - p.moveTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - p.lineTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - p.quadTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3]); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - p.cubicTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3], - coords[coordIdx + 4], coords[coordIdx + 5]); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser - QQuickSvgParser::pathArc(p, - coords[coordIdx], coords[coordIdx + 1], // radius - coords[coordIdx + 2], // xAxisRotation - !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc - !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag - coords[coordIdx + 3], coords[coordIdx + 4], // end - p.currentPosition().x(), p.currentPosition().y()); - coordIdx += 7; - break; - default: - qWarning("Unknown JS path command: %d", cmd[i]); - break; - } - } - return p; -} - -/*! - \qmltype VisualPath - \instantiates QQuickVisualPath - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Describes a Path and associated properties for stroking and filling - \since 5.10 - - A PathItem contains one or more VisualPath elements. At least one - VisualPath is necessary in order to have a PathItem output anything - visible. A VisualPath in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. - - Any property changes in these data sets will be bubble up and change the - output of the PathItem. This means that it is simple and easy to change, or - even animate, the starting and ending position, control points, or any - stroke or fill parameters using the usual QML bindings and animation types - like NumberAnimation. - - In the following example the line join style changes automatically based on - the value of joinStyleIndex: - - \code - VisualPath { - strokeColor: "black" - strokeWidth: 16 - fillColor: "transparent" - capStyle: VisualPath.RoundCap - - property int joinStyleIndex: 0 - property variant styles: [ VisualPath.BevelJoin, VisualPath.MiterJoin, VisualPath.RoundJoin ] - - joinStyle: styles[joinStyleIndex] - - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } - } - \endcode - - Once associated with a PathItem, here is the output with a joinStyleIndex - of 2 (VisualPath.RoundJoin): - - \image visualpath-code-example.png - */ - -QQuickVisualPathPrivate::QQuickVisualPathPrivate() - : path(nullptr), - dirty(DirtyAll) -{ -} - -QQuickVisualPath::QQuickVisualPath(QObject *parent) - : QObject(*(new QQuickVisualPathPrivate), parent) -{ -} - -QQuickVisualPath::~QQuickVisualPath() -{ -} - -/*! - \qmlproperty Path QtQuick::VisualPath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickVisualPath::path() const -{ - Q_D(const QQuickVisualPath); - return d->path; -} - -void QQuickVisualPath::setPath(QQuickPath *path) -{ - Q_D(QQuickVisualPath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickVisualPath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickVisualPathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickVisualPathPrivate::_q_pathChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyPath; - emit q->changed(); -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeColor - - This property holds the stroking color. - - When set to \c transparent, no stroking occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::strokeColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeColor; -} - -void QQuickVisualPath::setStrokeColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeColor != color) { - d->sfp.strokeColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeColor; - emit strokeColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::strokeWidth - - This property holds the stroke width. - - When set to a negative value, no stroking occurs. - - The default value is 1. - */ - -qreal QQuickVisualPath::strokeWidth() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeWidth; -} - -void QQuickVisualPath::setStrokeWidth(qreal w) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeWidth != w) { - d->sfp.strokeWidth = w; - d->dirty |= QQuickVisualPathPrivate::DirtyStrokeWidth; - emit strokeWidthChanged(); - emit changed(); - } -} - -/*! - \qmlproperty color QtQuick::VisualPath::fillColor - - This property holds the fill color. - - When set to \c transparent, no filling occurs. - - The default value is \c white. - */ - -QColor QQuickVisualPath::fillColor() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillColor; -} - -void QQuickVisualPath::setFillColor(const QColor &color) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillColor != color) { - d->sfp.fillColor = color; - d->dirty |= QQuickVisualPathPrivate::DirtyFillColor; - emit fillColorChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::fillRule - - This property holds the fill rule. The default value is - VisualPath.OddEvenFill. For an example on fill rules, see - QPainterPath::setFillRule(). - - \list - \li VisualPath.OddEvenFill - \li VisualPath.WindingFill - \endlist - */ - -QQuickVisualPath::FillRule QQuickVisualPath::fillRule() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillRule; -} - -void QQuickVisualPath::setFillRule(FillRule fillRule) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillRule != fillRule) { - d->sfp.fillRule = fillRule; - d->dirty |= QQuickVisualPathPrivate::DirtyFillRule; - emit fillRuleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::joinStyle - - This property defines how joins between two connected lines are drawn. The - default value is VisualPath.BevelJoin. - - \list - \li VisualPath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. - \li VisualPath.BevelJoin - The triangular notch between the two lines is filled. - \li VisualPath.RoundJoin - A circular arc between the two lines is filled. - \endlist - */ - -QQuickVisualPath::JoinStyle QQuickVisualPath::joinStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.joinStyle; -} - -void QQuickVisualPath::setJoinStyle(JoinStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.joinStyle != style) { - d->sfp.joinStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit joinStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty int QtQuick::VisualPath::miterLimit - - When VisualPath.joinStyle is set to VisualPath.MiterJoin, this property - specifies how far the miter join can extend from the join point. - - The default value is 2. - */ - -int QQuickVisualPath::miterLimit() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.miterLimit; -} - -void QQuickVisualPath::setMiterLimit(int limit) -{ - Q_D(QQuickVisualPath); - if (d->sfp.miterLimit != limit) { - d->sfp.miterLimit = limit; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit miterLimitChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::capStyle - - This property defines how the end points of lines are drawn. The - default value is VisualPath.SquareCap. - - \list - \li VisualPath.FlatCap - A square line end that does not cover the end point of the line. - \li VisualPath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. - \li VisualPath.RoundCap - A rounded line end. - \endlist - */ - -QQuickVisualPath::CapStyle QQuickVisualPath::capStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.capStyle; -} - -void QQuickVisualPath::setCapStyle(CapStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.capStyle != style) { - d->sfp.capStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyStyle; - emit capStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty enumeration QtQuick::VisualPath::strokeStyle - - This property defines the style of stroking. The default value is - VisualPath.SolidLine. - - \list - \li VisualPath.SolidLine - A plain line. - \li VisualPath.DashLine - Dashes separated by a few pixels. - \endlist - */ - -QQuickVisualPath::StrokeStyle QQuickVisualPath::strokeStyle() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.strokeStyle; -} - -void QQuickVisualPath::setStrokeStyle(StrokeStyle style) -{ - Q_D(QQuickVisualPath); - if (d->sfp.strokeStyle != style) { - d->sfp.strokeStyle = style; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit strokeStyleChanged(); - emit changed(); - } -} - -/*! - \qmlproperty real QtQuick::VisualPath::dashOffset - - This property defines the starting point on the dash pattern, measured in - units used to specify the dash pattern. - - The default value is 0. - - \sa QPen::setDashOffset() - */ - -qreal QQuickVisualPath::dashOffset() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashOffset; -} - -void QQuickVisualPath::setDashOffset(qreal offset) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashOffset != offset) { - d->sfp.dashOffset = offset; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashOffsetChanged(); - emit changed(); - } -} - -/*! - \qmlproperty list QtQuick::VisualPath::dashPattern - - This property defines the dash pattern when VisualPath.strokeStyle is set - to VisualPath.DashLine. The pattern must be specified as an even number of - positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... - are the spaces. The pattern is specified in units of the pen's width. - - The default value is (4, 2), meaning a dash of 4 * VisualPath.strokeWidth - pixels followed by a space of 2 * VisualPath.strokeWidth pixels. - - \sa QPen::setDashPattern() - */ - -QVector QQuickVisualPath::dashPattern() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.dashPattern; -} - -void QQuickVisualPath::setDashPattern(const QVector &array) -{ - Q_D(QQuickVisualPath); - if (d->sfp.dashPattern != array) { - d->sfp.dashPattern = array; - d->dirty |= QQuickVisualPathPrivate::DirtyDash; - emit dashPatternChanged(); - emit changed(); - } -} - -/*! - \qmlproperty PathGradient QtQuick::VisualPath::fillGradient - - This property defines the fill gradient. By default no gradient is enabled - and the value is \c null. In this case the fill uses a solid color based on - the value of VisuaLPath.fillColor. - - When set, VisualPath.fillColor is ignored and filling is done using one of - the PathGradient subtypes. - */ - -QQuickPathGradient *QQuickVisualPath::fillGradient() const -{ - Q_D(const QQuickVisualPath); - return d->sfp.fillGradient; -} - -void QQuickVisualPath::setFillGradient(QQuickPathGradient *gradient) -{ - Q_D(QQuickVisualPath); - if (d->sfp.fillGradient != gradient) { - if (d->sfp.fillGradient) - qmlobject_disconnect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->sfp.fillGradient = gradient; - if (d->sfp.fillGradient) - qmlobject_connect(d->sfp.fillGradient, QQuickPathGradient, SIGNAL(updated()), - this, QQuickVisualPath, SLOT(_q_fillGradientChanged())); - d->dirty |= QQuickVisualPathPrivate::DirtyFillGradient; - emit changed(); - } -} - -void QQuickVisualPathPrivate::_q_fillGradientChanged() -{ - Q_Q(QQuickVisualPath); - dirty |= DirtyFillGradient; - emit q->changed(); -} - -void QQuickVisualPath::resetFillGradient() -{ - setFillGradient(nullptr); -} - -/*! - \qmltype PathItem - \instantiates QQuickPathItem - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Item - \brief Renders a path - \since 5.10 - - Renders a path either by generating geometry via QPainterPath and manual - triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}. - - This approach is different from rendering shapes via QQuickPaintedItem or - the 2D Canvas because the path never gets rasterized in software. Therefore - PathItem is suitable for creating shapes spreading over larger areas of the - screen, avoiding the performance penalty for texture uploads or framebuffer - blits. In addition, the declarative API allows manipulating, binding to, - and even animating the path element properties like starting and ending - position, the control points, etc. - - The types for specifying path elements are shared between \l PathView and - PathItem. However, not all PathItem implementations support all path - element types, while some may not make sense for PathView. PathItem's - currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, - PathArc, PathSvg. - - See \l Path for a detailed overview of the supported path elements. - - \code - PathItem { - width: 200 - height: 150 - anchors.centerIn: parent - VisualPath { - strokeWidth: 4 - strokeColor: "red" - fillGradient: PathLinearGradient { - x1: 20; y1: 20 - x2: 180; y2: 130 - PathGradientStop { position: 0; color: "blue" } - PathGradientStop { position: 0.2; color: "green" } - PathGradientStop { position: 0.4; color: "red" } - PathGradientStop { position: 0.6; color: "yellow" } - PathGradientStop { position: 1; color: "cyan" } - } - strokeStyle: VisualPath.DashLine - dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } - } - } - \endcode - - \image pathitem-code-example.png - - \note It is important to be aware of performance implications, in - particular when the application is running on the generic PathItem - implementation due to not having support for accelerated path rendering. - The geometry generation happens entirely on the CPU in this case, and this - is potentially expensive. Changing the set of path elements, changing the - properties of these elements, or changing certain properties of the - PathItem itself all lead to retriangulation on every change. Therefore, - applying animation to such properties can heavily affect performance on - less powerful systems. If animating properties other than stroke and fill - colors is a must, it is recommended to target systems providing - \c{GL_NV_path_rendering} where the cost of path property changes is much - smaller. - - The following list summarizes the available PathItem rendering approaches: - - \list - - \li When running with the default, OpenGL backend of Qt Quick, both the - generic, triangulation-based and the NVIDIA-specific - \c{GL_NV_path_rendering} methods are available. The choice is made at - runtime, depending on the graphics driver's capabilities. When this is not - desired, applications can force using the generic method by setting the - PathItem.enableVendorExtensions property to \c false. - - \li The \c software backend is fully supported. The path is rendered via - QPainter::strokePath() and QPainter::fillPath() in this case. - - \li The Direct 3D 12 backend is not currently supported. - - \li The OpenVG backend is not currently supported. - - \endlist - - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg -*/ - -QQuickPathItemPrivate::QQuickPathItemPrivate() - : componentComplete(true), - vpChanged(false), - rendererType(QQuickPathItem::UnknownRenderer), - async(false), - status(QQuickPathItem::Null), - renderer(nullptr), - enableVendorExts(true) -{ -} - -QQuickPathItemPrivate::~QQuickPathItemPrivate() -{ - delete renderer; -} - -void QQuickPathItemPrivate::_q_visualPathChanged() -{ - Q_Q(QQuickPathItem); - vpChanged = true; - q->polish(); -} - -void QQuickPathItemPrivate::setStatus(QQuickPathItem::Status newStatus) -{ - Q_Q(QQuickPathItem); - if (status != newStatus) { - status = newStatus; - emit q->statusChanged(); - } -} - -QQuickPathItem::QQuickPathItem(QQuickItem *parent) - : QQuickItem(*(new QQuickPathItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -QQuickPathItem::~QQuickPathItem() -{ -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::rendererType - - This property determines which path rendering backend is active. - - \list - - \li PathItem.UnknownRenderer - The renderer is unknown. - - \li PathItem.GeometryRenderer - The generic, driver independent solution - for OpenGL. Uses the same CPU-based triangulation approach as QPainter's - OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the - default, OpenGL Qt Quick scenegraph backend is in use. - - \li PathItem.NvprRenderer - Path items are rendered by performing OpenGL - calls using the \c{GL_NV_path_rendering} extension. This is the default on - NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in - use. - - \li PathItem.SoftwareRenderer - Pure QPainter drawing using the raster - paint engine. This is the default, and only, option when the Qt Quick - scenegraph is running with the \c software backend. - - \endlist -*/ - -QQuickPathItem::RendererType QQuickPathItem::rendererType() const -{ - Q_D(const QQuickPathItem); - return d->rendererType; -} - -/*! - \qmlproperty bool QtQuick::PathItem::asynchronous - - When PathItem.rendererType is PathItem.GeometryRenderer, the input path is - triangulated on the CPU during the polishing phase of the PathItem. This is - potentially expensive. To offload this work to separate worker threads, set - this property to \c true. - - When enabled, making a PathItem visible will not wait for the content to - become available. Instead, the gui/main thread is not blocked and the - results of the path rendering are shown only when all the asynchronous work - has been finished. - - The default value is \c false. - */ - -bool QQuickPathItem::asynchronous() const -{ - Q_D(const QQuickPathItem); - return d->async; -} - -void QQuickPathItem::setAsynchronous(bool async) -{ - Q_D(QQuickPathItem); - if (d->async != async) { - d->async = async; - emit asynchronousChanged(); - if (d->componentComplete) - d->_q_visualPathChanged(); - } -} - -/*! - \qmlproperty bool QtQuick::PathItem::enableVendorExtensions - - This property controls the usage of non-standard OpenGL extensions like - GL_NV_path_rendering. To disable PathItem.NvprRenderer and force a uniform - behavior regardless of the graphics card and drivers, set this property to - \c false. - - The default value is \c true. - */ - -bool QQuickPathItem::enableVendorExtensions() const -{ - Q_D(const QQuickPathItem); - return d->enableVendorExts; -} - -void QQuickPathItem::setEnableVendorExtensions(bool enable) -{ - Q_D(QQuickPathItem); - if (d->enableVendorExts != enable) { - d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick::PathItem::status - - This property determines the status of the PathItem and is relevant when - PathItem.asynchronous is set to \c true. - - \list - - \li PathItem.Null - Not yet initialized. - - \li PathItem.Ready - The PathItem has finished processing. - - \li PathItem.Processing - The path is being processed. - - \endlist - */ - -QQuickPathItem::Status QQuickPathItem::status() const -{ - Q_D(const QQuickPathItem); - return d->status; -} - -static QQuickVisualPath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickVisualPath *obj) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - d->qmlData.vp.append(obj); - - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - d->_q_visualPathChanged(); - } -} - -static int vpe_count(QQmlListProperty *property) -{ - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(static_cast(property->object)); - return d->qmlData.vp.count(); -} - -static void vpe_clear(QQmlListProperty *property) -{ - QQuickPathItem *item = static_cast(property->object); - QQuickPathItemPrivate *d = QQuickPathItemPrivate::get(item); - - for (QQuickVisualPath *p : d->qmlData.vp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_visualPathChanged())); - - d->qmlData.vp.clear(); - - if (d->componentComplete) - d->_q_visualPathChanged(); -} - -/*! - \qmlproperty list QtQuick::PathItem::elements - - This property holds the VisualPath objects that define the contents of the - PathItem. - - \default - */ - -QQmlListProperty QQuickPathItem::elements() -{ - return QQmlListProperty(this, - nullptr, - vpe_append, - vpe_count, - vpe_at, - vpe_clear); -} - -void QQuickPathItem::classBegin() -{ - Q_D(QQuickPathItem); - d->componentComplete = false; -} - -void QQuickPathItem::componentComplete() -{ - Q_D(QQuickPathItem); - d->componentComplete = true; - - for (QQuickVisualPath *p : d->qmlData.vp) - connect(p, SIGNAL(changed()), this, SLOT(_q_visualPathChanged())); - - d->_q_visualPathChanged(); -} - -void QQuickPathItem::updatePolish() -{ - Q_D(QQuickPathItem); - - if (!d->vpChanged) - return; - - d->vpChanged = false; - - if (!d->renderer) { - d->createRenderer(); - if (!d->renderer) - return; - emit rendererChanged(); - } - - // endSync() is where expensive calculations may happen (or get kicked off - // on worker threads), depending on the backend. Therefore do this only - // when the item is visible. - if (isVisible()) - d->sync(); - - update(); -} - -void QQuickPathItem::itemChange(ItemChange change, const ItemChangeData &data) -{ - Q_D(QQuickPathItem); - - // sync may have been deferred; do it now if the item became visible - if (change == ItemVisibleHasChanged && data.boolValue) - d->_q_visualPathChanged(); - - QQuickItem::itemChange(change, data); -} - -QSGNode *QQuickPathItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) -{ - // Called on the render thread, with the gui thread blocked. We can now - // safely access gui thread data. - - Q_D(QQuickPathItem); - if (d->renderer) { - if (!node) - node = d->createNode(); - d->renderer->updateNode(); - } - return node; -} - -// the renderer object lives on the gui thread -void QQuickPathItemPrivate::createRenderer() -{ - Q_Q(QQuickPathItem); - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - rendererType = QQuickPathItem::NvprRenderer; - renderer = new QQuickPathItemNvprRenderer; - } else { - rendererType = QQuickPathItem::GeometryRenderer; - renderer = new QQuickPathItemGenericRenderer(q); - } - break; -#endif - case QSGRendererInterface::Software: - rendererType = QQuickPathItem::SoftwareRenderer; - renderer = new QQuickPathItemSoftwareRenderer; - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } -} - -// the node lives on the render thread -QSGNode *QQuickPathItemPrivate::createNode() -{ - Q_Q(QQuickPathItem); - QSGNode *node = nullptr; - if (!q->window()) - return node; - QSGRendererInterface *ri = q->window()->rendererInterface(); - if (!ri) - return node; - - switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL - case QSGRendererInterface::OpenGL: - if (enableVendorExts && QQuickPathItemNvprRenderNode::isSupported()) { - node = new QQuickPathItemNvprRenderNode; - static_cast(renderer)->setNode( - static_cast(node)); - } else { - node = new QQuickPathItemGenericNode; - static_cast(renderer)->setRootNode( - static_cast(node)); - } - break; -#endif - case QSGRendererInterface::Software: - node = new QQuickPathItemSoftwareRenderNode(q); - static_cast(renderer)->setNode( - static_cast(node)); - break; - default: - qWarning("No path backend for this graphics API yet"); - break; - } - - return node; -} - -static void q_asyncPathItemReady(void *data) -{ - QQuickPathItemPrivate *self = static_cast(data); - self->setStatus(QQuickPathItem::Ready); -} - -void QQuickPathItemPrivate::sync() -{ - const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); - if (useAsync) { - setStatus(QQuickPathItem::Processing); - renderer->setAsyncCallback(q_asyncPathItemReady, this); - } - - if (!jsData.isValid()) { - // Standard route: The path and stroke/fill parameters are provided via - // VisualPath and Path. - const int count = qmlData.vp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickVisualPath *p = qmlData.vp[i]; - int &dirty(QQuickVisualPathPrivate::get(p)->dirty); - - if (dirty & QQuickVisualPathPrivate::DirtyPath) - renderer->setPath(i, p->path()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeColor) - renderer->setStrokeColor(i, p->strokeColor()); - if (dirty & QQuickVisualPathPrivate::DirtyStrokeWidth) - renderer->setStrokeWidth(i, p->strokeWidth()); - if (dirty & QQuickVisualPathPrivate::DirtyFillColor) - renderer->setFillColor(i, p->fillColor()); - if (dirty & QQuickVisualPathPrivate::DirtyFillRule) - renderer->setFillRule(i, p->fillRule()); - if (dirty & QQuickVisualPathPrivate::DirtyStyle) { - renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); - renderer->setCapStyle(i, p->capStyle()); - } - if (dirty & QQuickVisualPathPrivate::DirtyDash) - renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); - if (dirty & QQuickVisualPathPrivate::DirtyFillGradient) - renderer->setFillGradient(i, p->fillGradient()); - - dirty = 0; - } - - renderer->endSync(useAsync); - } else { - // Path and stroke/fill params provided from JavaScript. This avoids - // QObjects at the expense of not supporting changes afterwards. - const int count = jsData.paths.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - renderer->setJSPath(i, jsData.paths[i]); - const QQuickPathItemStrokeFillParams sfp(jsData.sfp[i]); - renderer->setStrokeColor(i, sfp.strokeColor); - renderer->setStrokeWidth(i, sfp.strokeWidth); - renderer->setFillColor(i, sfp.fillColor); - renderer->setFillRule(i, sfp.fillRule); - renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); - renderer->setCapStyle(i, sfp.capStyle); - renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); - renderer->setFillGradient(i, sfp.fillGradient); - } - - renderer->endSync(useAsync); - } - - if (!useAsync) - setStatus(QQuickPathItem::Ready); -} - -// ***** gradient support ***** - -/*! - \qmltype PathGradientStop - \instantiates QQuickPathGradientStop - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickPathGradientStop::QQuickPathGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickPathGradientStop::position() const -{ - return m_position; -} - -void QQuickPathGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick::PathGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickPathGradientStop::color() const -{ - return m_color; -} - -void QQuickPathGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickPathGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmltype PathGradient - \instantiates QQuickPathGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Base type of PathItem fill gradients - \since 5.10 - - This is an abstract base class for gradients like PathLinearGradient and - cannot be created directly. - */ - -QQuickPathGradient::QQuickPathGradient(QObject *parent) - : QObject(parent), - m_spread(PadSpread) -{ -} - -int QQuickPathGradient::countStops(QQmlListProperty *list) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickPathGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickPathGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickPathGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickPathGradientStop elements"); - return; - } - QQuickPathGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - -/*! - \qmlproperty list QtQuick::PathGradient::stops - \default - - The list of PathGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickPathGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickPathGradient::appendStop, - &QQuickPathGradient::countStops, - &QQuickPathGradient::atStop, - nullptr); -} - -QGradientStops QQuickPathGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickPathGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick::PathGradient::spred - - Specifies how the area outside the gradient area should be filled. The - default value is PathGradient.PadSpread. - - \list - \li PathGradient.PadSpread - The area is filled with the closest stop color. - \li PathGradient.RepeatSpread - The gradient is repeated outside the gradient area. - \li PathGradient.ReflectSpread - The gradient is reflected outside the gradient area. - \endlist - */ - -QQuickPathGradient::SpreadMode QQuickPathGradient::spread() const -{ - return m_spread; -} - -void QQuickPathGradient::setSpread(SpreadMode mode) -{ - if (m_spread != mode) { - m_spread = mode; - emit spreadChanged(); - emit updated(); - } -} - -/*! - \qmltype PathLinearGradient - \instantiates QQuickPathLinearGradient - \inqmlmodule QtQuick - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits PathGradient - \brief Linear gradient - \since 5.10 - - Linear gradients interpolate colors between start and end points. Outside - these points the gradient is either padded, reflected or repeated depending - on the spread type. - - \sa QLinearGradient - */ - -QQuickPathLinearGradient::QQuickPathLinearGradient(QObject *parent) - : QQuickPathGradient(parent) -{ -} - -/*! - \qmlproperty real QtQuick::PathLinearGradient::x1 - \qmlproperty real QtQuick::PathLinearGradient::y1 - \qmlproperty real QtQuick::PathLinearGradient::x2 - \qmlproperty real QtQuick::PathLinearGradient::y2 - - These properties define the start and end points between which color - interpolation occurs. By default both the stard and end points are set to - (0, 0). - */ - -qreal QQuickPathLinearGradient::x1() const -{ - return m_start.x(); -} - -void QQuickPathLinearGradient::setX1(qreal v) -{ - if (m_start.x() != v) { - m_start.setX(v); - emit x1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y1() const -{ - return m_start.y(); -} - -void QQuickPathLinearGradient::setY1(qreal v) -{ - if (m_start.y() != v) { - m_start.setY(v); - emit y1Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::x2() const -{ - return m_end.x(); -} - -void QQuickPathLinearGradient::setX2(qreal v) -{ - if (m_end.x() != v) { - m_end.setX(v); - emit x2Changed(); - emit updated(); - } -} - -qreal QQuickPathLinearGradient::y2() const -{ - return m_end.y(); -} - -void QQuickPathLinearGradient::setY2(qreal v) -{ - if (m_end.y() != v) { - m_end.setY(v); - emit y2Changed(); - emit updated(); - } -} - -#ifndef QT_NO_OPENGL - -// contexts sharing with each other get the same cache instance -class QQuickPathItemGradientCacheWrapper -{ -public: - QQuickPathItemGradientCache *get(QOpenGLContext *context) - { - return m_resource.value(context); - } - -private: - QOpenGLMultiGroupSharedResource m_resource; -}; - -QQuickPathItemGradientCache *QQuickPathItemGradientCache::currentCache() -{ - static QQuickPathItemGradientCacheWrapper qt_path_gradient_caches; - return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); -} - -// let QOpenGLContext manage the lifetime of the cached textures -QQuickPathItemGradientCache::~QQuickPathItemGradientCache() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::invalidateResource() -{ - m_cache.clear(); -} - -void QQuickPathItemGradientCache::freeResource(QOpenGLContext *) -{ - qDeleteAll(m_cache); - m_cache.clear(); -} - -static void generateGradientColorTable(const QQuickPathItemGradientCache::GradientDesc &gradient, - uint *colorTable, int size, float opacity) -{ - int pos = 0; - const QGradientStops &s = gradient.stops; - const bool colorInterpolation = true; - - uint alpha = qRound(opacity * 256); - uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); - qreal incr = 1.0 / qreal(size); - qreal fpos = 1.5 * incr; - colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); - - while (fpos <= s.first().first) { - colorTable[pos] = colorTable[pos - 1]; - pos++; - fpos += incr; - } - - if (colorInterpolation) - current_color = qPremultiply(current_color); - - const int sLast = s.size() - 1; - for (int i = 0; i < sLast; ++i) { - qreal delta = 1/(s[i+1].first - s[i].first); - uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); - if (colorInterpolation) - next_color = qPremultiply(next_color); - - while (fpos < s[i+1].first && pos < size) { - int dist = int(256 * ((fpos - s[i].first) * delta)); - int idist = 256 - dist; - if (colorInterpolation) - colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); - else - colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); - ++pos; - fpos += incr; - } - current_color = next_color; - } - - Q_ASSERT(s.size() > 0); - - uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); - for ( ; pos < size; ++pos) - colorTable[pos] = last_color; - - colorTable[size-1] = last_color; -} - -QSGTexture *QQuickPathItemGradientCache::get(const GradientDesc &grad) -{ - QSGPlainTexture *tx = m_cache[grad]; - if (!tx) { - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - GLuint id; - f->glGenTextures(1, &id); - f->glBindTexture(GL_TEXTURE_2D, id); - static const uint W = 1024; // texture size is 1024x1 - uint buf[W]; - generateGradientColorTable(grad, buf, W, 1.0f); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - tx = new QSGPlainTexture; - tx->setTextureId(id); - switch (grad.spread) { - case QQuickPathGradient::PadSpread: - tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); - tx->setVerticalWrapMode(QSGTexture::ClampToEdge); - break; - case QQuickPathGradient::RepeatSpread: - tx->setHorizontalWrapMode(QSGTexture::Repeat); - tx->setVerticalWrapMode(QSGTexture::Repeat); - break; - case QQuickPathGradient::ReflectSpread: - tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); - tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); - break; - default: - qWarning("Unknown gradient spread mode %d", grad.spread); - break; - } - m_cache[grad] = tx; - } - return tx; -} - -#endif // QT_NO_OPENGL - -// ***** JS-based alternative for creating static paths, (mostly) without QObjects ***** - -class QQuickPathItemJSEngineData : public QV8Engine::Deletable -{ -public: - QQuickPathItemJSEngineData(QV4::ExecutionEngine *engine); - - QV4::PersistentValue pathProto; - QV4::PersistentValue strokeFillParamsProto; -}; - -V4_DEFINE_EXTENSION(QQuickPathItemJSEngineData, engineData) - -namespace QV4 { -namespace Heap { - -struct QQuickPathItemJSPathPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSPath : Object { - void init() { Object::init(); } - QQuickPathItemPathObject *obj; -}; - -struct QQuickPathItemJSStrokeFillParamsPrototype : Object { - void init() { Object::init(); } -}; - -struct QQuickPathItemJSStrokeFillParams : Object { - void init() { Object::init(); } - QQuickPathItemStrokeFillParamsObject *obj; -}; - -} // namespace Heap -} // namespace QV4 - -struct QQuickPathItemJSPathPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPathPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSPathPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - o->defineDefaultProperty(QStringLiteral("moveTo"), method_moveTo, 0); - o->defineDefaultProperty(QStringLiteral("lineTo"), method_lineTo, 0); - o->defineDefaultProperty(QStringLiteral("quadTo"), method_quadTo, 0); - o->defineDefaultProperty(QStringLiteral("cubicTo"), method_cubicTo, 0); - o->defineDefaultProperty(QStringLiteral("arcTo"), method_arcTo, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPathPrototype); - -struct QQuickPathItemJSStrokeFillParamsPrototype : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParamsPrototype, QV4::Object) -public: - static QV4::Heap::QQuickPathItemJSStrokeFillParamsPrototype *create(QV4::ExecutionEngine *engine) - { - QV4::Scope scope(engine); - auto obj = engine->memoryManager->allocObject(); - QV4::Scoped o(scope, obj); - - o->defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); - - return o->d(); - } - - static void method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParamsPrototype); - -struct QQuickPathItemJSPath : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSPath, QV4::Object) -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSPath); - -struct QQuickPathItemJSStrokeFillParams : public QV4::Object -{ - V4_OBJECT2(QQuickPathItemJSStrokeFillParams, QV4::Object) - - static void method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); -}; - -DEFINE_OBJECT_VTABLE(QQuickPathItemJSStrokeFillParams); - -void QQuickPathItemJSPathPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_moveTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::MoveTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_lineTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 2) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::LineTo); - p->path.coords.append(callData->args[0].toNumber()); - p->path.coords.append(callData->args[1].toNumber()); - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_quadTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 4) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::QuadTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // cx - p->path.coords.append(v[1].toNumber()); // cy - p->path.coords.append(v[2].toNumber()); // x - p->path.coords.append(v[3].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_cubicTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 6) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::CubicTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // c1x - p->path.coords.append(v[1].toNumber()); // c1y - p->path.coords.append(v[2].toNumber()); // c2x - p->path.coords.append(v[3].toNumber()); // c2y - p->path.coords.append(v[4].toNumber()); // x - p->path.coords.append(v[5].toNumber()); // y - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSPathPrototype::method_arcTo(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - if (callData->argc >= 7) { - QQuickPathItemPathObject *p = r->d()->obj; - p->path.cmd.append(QQuickPathItemPath::ArcTo); - const QV4::Value *v = callData->args; - p->path.coords.append(v[0].toNumber()); // radiusX - p->path.coords.append(v[1].toNumber()); // radiusY - p->path.coords.append(v[2].toNumber()); // xAxisRotation - p->path.coords.append(v[3].toNumber()); // x - p->path.coords.append(v[4].toNumber()); // y - p->path.coords.append(v[5].toNumber()); // sweepFlag - p->path.coords.append(v[6].toNumber()); // largeArc - } - - scope.result = callData->thisObject.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParamsPrototype::method_clear(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - r->d()->obj->clear(); - - scope.result = callData->thisObject.asReturnedValue(); -} - -Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name); // qquickcontext2d.cpp - -static inline QString qt_color_string(const QColor &color) -{ - if (color.alpha() == 255) - return color.name(); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.strokeColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.strokeColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeWidth); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.strokeWidth = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = QV4::Encode(scope.engine->newString(qt_color_string(r->d()->obj->sfp.fillColor))); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillColor(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isString()) - r->d()->obj->sfp.fillColor = qt_color_from_string(value); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.fillRule); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillRule(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.fillRule = QQuickVisualPath::FillRule(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.joinStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_joinStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.joinStyle = QQuickVisualPath::JoinStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.miterLimit); -} - -void QQuickPathItemJSStrokeFillParams::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.miterLimit = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.capStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_capStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.capStyle = QQuickVisualPath::CapStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.strokeStyle); -} - -void QQuickPathItemJSStrokeFillParams::method_set_strokeStyle(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isInt32()) - r->d()->obj->sfp.strokeStyle = QQuickVisualPath::StrokeStyle(value->integerValue()); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = scope.engine->fromVariant(r->d()->obj->sfp.dashOffset); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashOffset(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - r->d()->obj->sfp.dashOffset = value->toNumber(); - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedArrayObject a(scope, scope.engine->newArrayObject()); - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - a->arrayReserve(p->sfp.dashPattern.count()); - QV4::ScopedValue v(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) - a->arrayPut(i, (v = scope.engine->fromVariant(p->sfp.dashPattern[i]))); - a->setArrayLengthUnchecked(p->sfp.dashPattern.count()); - - scope.result = a.asReturnedValue(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_dashPattern(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - if (value->isObject()) { - QV4::Scoped ao(scope, value); - if (!!ao) { - QQuickPathItemStrokeFillParamsObject *p = r->d()->obj; - p->sfp.dashPattern.resize(ao->getLength()); - QV4::ScopedValue val(scope); - for (int i = 0; i < p->sfp.dashPattern.count(); ++i) { - val = ao->getIndexed(i); - p->sfp.dashPattern[i] = val->toNumber(); - } - } - } - - scope.result = QV4::Encode::undefined(); -} - -void QQuickPathItemJSStrokeFillParams::method_get_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - scope.result = r->d()->obj->v4fillGradient.value(); -} - -void QQuickPathItemJSStrokeFillParams::method_set_fillGradient(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) -{ - QV4::Scoped r(scope, callData->thisObject.as()); - - QV4::ScopedValue value(scope, callData->argument(0)); - QV4::Scoped qobjectWrapper(scope, value); - if (!!qobjectWrapper) { - if (QQuickPathGradient *grad = qobject_cast(qobjectWrapper->object())) { - r->d()->obj->v4fillGradient.set(scope.engine, value); - r->d()->obj->sfp.fillGradient = grad; - } - } else { - r->d()->obj->v4fillGradient.set(scope.engine, nullptr); - r->d()->obj->sfp.fillGradient = nullptr; - } - - scope.result = QV4::Encode::undefined(); -} - -QQuickPathItemJSEngineData::QQuickPathItemJSEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, QQuickPathItemJSPathPrototype::create(v4)); - pathProto = proto; - - proto = QV4::ScopedObject(scope, QQuickPathItemJSStrokeFillParamsPrototype::create(v4)); - - proto->defineAccessorProperty(QStringLiteral("strokeColor"), - QQuickPathItemJSStrokeFillParams::method_get_strokeColor, - QQuickPathItemJSStrokeFillParams::method_set_strokeColor); - proto->defineAccessorProperty(QStringLiteral("strokeWidth"), - QQuickPathItemJSStrokeFillParams::method_get_strokeWidth, - QQuickPathItemJSStrokeFillParams::method_set_strokeWidth); - proto->defineAccessorProperty(QStringLiteral("fillColor"), - QQuickPathItemJSStrokeFillParams::method_get_fillColor, - QQuickPathItemJSStrokeFillParams::method_set_fillColor); - proto->defineAccessorProperty(QStringLiteral("fillRule"), - QQuickPathItemJSStrokeFillParams::method_get_fillRule, - QQuickPathItemJSStrokeFillParams::method_set_fillRule); - proto->defineAccessorProperty(QStringLiteral("joinStyle"), - QQuickPathItemJSStrokeFillParams::method_get_joinStyle, - QQuickPathItemJSStrokeFillParams::method_set_joinStyle); - proto->defineAccessorProperty(QStringLiteral("miterLimit"), - QQuickPathItemJSStrokeFillParams::method_get_miterLimit, - QQuickPathItemJSStrokeFillParams::method_set_miterLimit); - proto->defineAccessorProperty(QStringLiteral("capStyle"), - QQuickPathItemJSStrokeFillParams::method_get_capStyle, - QQuickPathItemJSStrokeFillParams::method_set_capStyle); - proto->defineAccessorProperty(QStringLiteral("strokeStyle"), - QQuickPathItemJSStrokeFillParams::method_get_strokeStyle, - QQuickPathItemJSStrokeFillParams::method_set_strokeStyle); - proto->defineAccessorProperty(QStringLiteral("dashOffset"), - QQuickPathItemJSStrokeFillParams::method_get_dashOffset, - QQuickPathItemJSStrokeFillParams::method_set_dashOffset); - proto->defineAccessorProperty(QStringLiteral("dashPattern"), - QQuickPathItemJSStrokeFillParams::method_get_dashPattern, - QQuickPathItemJSStrokeFillParams::method_set_dashPattern); - proto->defineAccessorProperty(QStringLiteral("fillGradient"), - QQuickPathItemJSStrokeFillParams::method_get_fillGradient, - QQuickPathItemJSStrokeFillParams::method_set_fillGradient); - - strokeFillParamsProto = proto; -} - -void QQuickPathItemPathObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->pathProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmltype JSPath - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes a path via a JavaScript API - */ - -/*! - \qmlmethod void QtQuick::JSPath::moveTo(x, y) - - Moves the path's position to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::lineTo(x, y) - - Defines a straight line to the absolute position specified by (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::quadTo(cx, cy, x, y) - - Defines a quadratic Bezier curve with a control point (\a cx, \a cy) and an - end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::cubicTo(c1x, c1y, c2x, c2y, x, y) - - Defines a cubic Bezier curve with two control points (\a c1x, \a c1y) and - (\a c2x, \a c2y), and an end point of (\a x, \a y). - */ - -/*! - \qmlmethod void QtQuick::JSPath::arcTo(radiusX, radiusY, xAxisRotation, x, y, sweepFlag, largeArc) - - Defines an elliptical arc, following the elliptical arc command in SVG. See - \l{https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands}{the - SVG path specification} for details on the parameters. - */ - -/*! - \qmlmethod void QtQuick::JSPath::clear() - - Clears the path object removing all path elements. This is more lightweight - than creating a new JSPath object. - */ - -void QQuickPathItemPathObject::clear() -{ - path = QQuickPathItemPath(); -} - -/*! - \qmltype StrokeFillParams - \inqmlmodule QtQuick - \ingroup qtquick-path - \brief Describes stroke and fill parameters via a JavaScript API - - The properties of StrokeFillParams objects correspond 1:1 to VisualPath - properties. The possible values for enumerations are the same as well, for - example: - - \code - sfp.strokeStyle = VisualPath.DashLine; - sfp.capStyle = VisualPath.RoundCap; - \endcode - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::strokeColor - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::strokeWidth - */ - -/*! - \qmlproperty color QtQuick::StrokeFillParams::fillColor - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::fillRule - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::joinStyle - */ - -/*! - \qmlproperty int QtQuick::StrokeFillParams::miterLimit - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::capStyle - */ - -/*! - \qmlproperty enumeration QtQuick::StrokeFillParams::strokeStyle - */ - -/*! - \qmlproperty real QtQuick::StrokeFillParams::dashOffset - */ - -/*! - \qmlproperty list QtQuick::StrokeFillParams::dashPattern - - The dash pattern can be specified using JavaScript arrays. - - \code - sfp.dashPattern = [ 4, 2 ]; - \endcode - */ - -/*! - \qmlproperty object QtQuick::StrokeFillParams::fillGradient - - Sets the fill gradient. The default value is null. Gradients cannot be - created from JavaScript. Instead, reference a PathLinearGradient or other - item by id. - - \code - PathLinearGradient { id: grad; ... } - ... - sfp.fillGradient = grad; - \endcode - */ - -/*! - \qmlmethod void QtQuick::StrokeFillParams::clear() - - Resets all values to their defaults. This is more lightweight than creating - a new StrokeFillParams object. - */ - -void QQuickPathItemStrokeFillParamsObject::clear() -{ - sfp = QQuickPathItemStrokeFillParams(); - if (!v4fillGradient.isNullOrUndefined()) - v4fillGradient.set(v4fillGradient.engine(), nullptr); -} - -void QQuickPathItemStrokeFillParamsObject::setV4Engine(QV4::ExecutionEngine *engine) -{ - QQuickPathItemJSEngineData *ed = engineData(engine); - QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, engine->memoryManager->allocObject()); - QV4::ScopedObject p(scope, ed->strokeFillParamsProto.value()); - wrapper->setPrototype(p); - wrapper->d()->obj = this; - m_v4value = wrapper; -} - -/*! - \qmlmethod JSPath QtQuick::PathItem::newPath() - - Creates and returns a new object that describes a path and offers a - JavaScript API. Paired with a stroke-fill parameter object it is - equivalent to a VisualPath. - - The following two snippets are equivalent when it comes to the end result: - - \code - var p = pathItem.newPath(); - var sfp = pathItem.newStrokeFillParams(); - sfp.fillColor = "white"; - sfp.strokeColor = "black"; - sfp.strokeWidth = 0.172; - p.moveTo(-122.304, 84.285); - p.cubicTo(-122.304, 84.285, -122.203, 86.179, -123.027, 86.16); - pathItem.appendVisualPath(p, sfp); - \endcode - - \code - PathItem { - VisualPath { - fillColor: "white" - strokeColor: "black" - strokeWidth: 0.172 - Path { - startX: -122.304; startY: 84.285 - PathCubic { control1X: -122.304; control1Y: 84.285; control2X: -122.203; control2Y: 86.179; x: -123.027; y: 86.16 } - } - } - } - \endcode - - The latter offers a full declarative API, with the possibility to binding - to and animating properties, while the former uses less resources due to - greatly reducing the number of QObject instances created. -*/ - -void QQuickPathItem::newPath(QQmlV4Function *args) -{ - QQuickPathItemPathObject *obj = new QQuickPathItemPathObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod StrokeFillParams QtQuick::PathItem::newStrokeFillParams() - - Creates and returns a new object that describes stroke and fill parameters - and offers a JavaScript API. Paired with a path object it is equivalent to - a VisualPath. - */ - -void QQuickPathItem::newStrokeFillParams(QQmlV4Function *args) -{ - QQuickPathItemStrokeFillParamsObject *obj = new QQuickPathItemStrokeFillParamsObject(this); - obj->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); - args->setReturnValue(obj->v4value()); -} - -/*! - \qmlmethod void QtQuick::PathItem::clearVisualPaths() - - Clears the list of visual paths. - - \note This applies only to path and stroke-fill parameter objects registered - via appendVisualPaths(). - */ - -void QQuickPathItem::clearVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->jsData.paths.clear(); - d->jsData.sfp.clear(); -} - -/*! - \qmlmethod void QtQuick::PathItem::commitVisualPaths() - - Updates the PathItem. - - In order to avoid rendering a half-prepared PathItem, calling - PathItem.appendVisualPath() does not trigger any processing. Instead, - applications must call this function when all path and stroke-fill - parameter objects are registered. - */ - -void QQuickPathItem::commitVisualPaths(QQmlV4Function *args) -{ - Q_UNUSED(args); - Q_D(QQuickPathItem); - d->_q_visualPathChanged(); -} - -/*! - \qmlmethod void QtQuick::PathItem::appendVisualPath(object path, object strokeFillParams) - - Adds the visual path compoes of \a path and \a strokeFillParams into the - PathItem. - - \note The declarative and imprative (JavaScript) APIs of PathItem use - independent data structures. Calling this function has no effect on the - PathItem.elements property and vice versa. Once this function is called, - the PathItem will only consider the data registered via this function and - will ignore the declarative elements property. - */ - -void QQuickPathItem::appendVisualPath(QQmlV4Function *args) -{ - if (args->length() < 2) - return; - - Q_D(QQuickPathItem); - QV4::Scope scope(args->v4engine()); - QV4::Scoped jsp(scope, (*args)[0]); - QV4::Scoped jssfp(scope, (*args)[1]); - - const QQuickPathItemPath &path(jsp->d()->obj->path); - const QQuickPathItemStrokeFillParams &sfp(jssfp->d()->obj->sfp); - - d->jsData.paths.append(path); - d->jsData.sfp.append(sfp); -} - -QT_END_NAMESPACE - -#include "moc_qquickpathitem_p.cpp" diff --git a/src/imports/pathitem/qquickpathitem_p.h b/src/imports/pathitem/qquickpathitem_p.h deleted file mode 100644 index c7c56fd5d8..0000000000 --- a/src/imports/pathitem/qquickpathitem_p.h +++ /dev/null @@ -1,333 +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 QQUICKPATHITEM_P_H -#define QQUICKPATHITEM_P_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 "qquickitem.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickVisualPathPrivate; -class QQuickPathItemPrivate; - -class QQuickPathGradientStop : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickPathGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class QQuickPathGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) - Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - enum SpreadMode { - PadSpread, - RepeatSpread, - ReflectSpread - }; - Q_ENUM(SpreadMode) - - QQuickPathGradient(QObject *parent = nullptr); - - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - - SpreadMode spread() const; - void setSpread(SpreadMode mode); - -signals: - void updated(); - void spreadChanged(); - -private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; - SpreadMode m_spread; -}; - -class QQuickPathLinearGradient : public QQuickPathGradient -{ - Q_OBJECT - Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) - Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) - Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) - Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - QQuickPathLinearGradient(QObject *parent = nullptr); - - qreal x1() const; - void setX1(qreal v); - qreal y1() const; - void setY1(qreal v); - qreal x2() const; - void setX2(qreal v); - qreal y2() const; - void setY2(qreal v); - -signals: - void x1Changed(); - void y1Changed(); - void x2Changed(); - void y2Changed(); - -private: - QPointF m_start; - QPointF m_end; -}; - -class QQuickVisualPath : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) - Q_CLASSINFO("DefaultProperty", "path") - - Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) - Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) - Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) - Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) - Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) - Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) - Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) - Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) - Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) - Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) - Q_PROPERTY(QQuickPathGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) - -public: - enum FillRule { - OddEvenFill = Qt::OddEvenFill, - WindingFill = Qt::WindingFill - }; - Q_ENUM(FillRule) - - enum JoinStyle { - MiterJoin = Qt::MiterJoin, - BevelJoin = Qt::BevelJoin, - RoundJoin = Qt::RoundJoin - }; - Q_ENUM(JoinStyle) - - enum CapStyle { - FlatCap = Qt::FlatCap, - SquareCap = Qt::SquareCap, - RoundCap = Qt::RoundCap - }; - Q_ENUM(CapStyle) - - enum StrokeStyle { - SolidLine = Qt::SolidLine, - DashLine = Qt::DashLine - }; - Q_ENUM(StrokeStyle) - - QQuickVisualPath(QObject *parent = nullptr); - ~QQuickVisualPath(); - - QQuickPath *path() const; - void setPath(QQuickPath *path); - - QColor strokeColor() const; - void setStrokeColor(const QColor &color); - - qreal strokeWidth() const; - void setStrokeWidth(qreal w); - - QColor fillColor() const; - void setFillColor(const QColor &color); - - FillRule fillRule() const; - void setFillRule(FillRule fillRule); - - JoinStyle joinStyle() const; - void setJoinStyle(JoinStyle style); - - int miterLimit() const; - void setMiterLimit(int limit); - - CapStyle capStyle() const; - void setCapStyle(CapStyle style); - - StrokeStyle strokeStyle() const; - void setStrokeStyle(StrokeStyle style); - - qreal dashOffset() const; - void setDashOffset(qreal offset); - - QVector dashPattern() const; - void setDashPattern(const QVector &array); - - QQuickPathGradient *fillGradient() const; - void setFillGradient(QQuickPathGradient *gradient); - void resetFillGradient(); - -Q_SIGNALS: - void changed(); - void pathChanged(); - void strokeColorChanged(); - void strokeWidthChanged(); - void fillColorChanged(); - void fillRuleChanged(); - void joinStyleChanged(); - void miterLimitChanged(); - void capStyleChanged(); - void strokeStyleChanged(); - void dashOffsetChanged(); - void dashPatternChanged(); - void fillGradientChanged(); - -private: - Q_DISABLE_COPY(QQuickVisualPath) - Q_DECLARE_PRIVATE(QQuickVisualPath) - Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) - Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) -}; - -class QQuickPathItem : public QQuickItem -{ - Q_OBJECT - Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) - Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") - -public: - enum RendererType { - UnknownRenderer, - GeometryRenderer, - NvprRenderer, - SoftwareRenderer - }; - Q_ENUM(RendererType) - - enum Status { - Null, - Ready, - Processing - }; - Q_ENUM(Status) - - QQuickPathItem(QQuickItem *parent = nullptr); - ~QQuickPathItem(); - - RendererType rendererType() const; - - bool asynchronous() const; - void setAsynchronous(bool async); - - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); - - Status status() const; - - QQmlListProperty elements(); - - Q_INVOKABLE void newPath(QQmlV4Function *args); - Q_INVOKABLE void newStrokeFillParams(QQmlV4Function *args); - Q_INVOKABLE void clearVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void commitVisualPaths(QQmlV4Function *args); - Q_INVOKABLE void appendVisualPath(QQmlV4Function *args); - -protected: - QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; - void updatePolish() override; - void itemChange(ItemChange change, const ItemChangeData &data) override; - void componentComplete() override; - void classBegin() override; - -Q_SIGNALS: - void rendererChanged(); - void asynchronousChanged(); - void enableVendorExtensionsChanged(); - void statusChanged(); - -private: - Q_DISABLE_COPY(QQuickPathItem) - Q_DECLARE_PRIVATE(QQuickPathItem) - Q_PRIVATE_SLOT(d_func(), void _q_visualPathChanged()) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPathItem) - -#endif // QQUICKPATHITEM_P_H diff --git a/src/imports/pathitem/qquickpathitem_p_p.h b/src/imports/pathitem/qquickpathitem_p_p.h deleted file mode 100644 index 6dde314a30..0000000000 --- a/src/imports/pathitem/qquickpathitem_p_p.h +++ /dev/null @@ -1,282 +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 QQUICKPATHITEM_P_P_H -#define QQUICKPATHITEM_P_P_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 "qquickpathitem_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGPlainTexture; - -struct QQuickPathItemPath -{ - enum Command { - MoveTo, - LineTo, - QuadTo, - CubicTo, - ArcTo - }; - - QVector cmd; - QVector coords; - - QPainterPath toPainterPath() const; -}; - -struct QQuickPathItemStrokeFillParams -{ - QQuickPathItemStrokeFillParams(); - - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickVisualPath::FillRule fillRule; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickPathGradient *fillGradient; -}; - -class QQuickAbstractPathRenderer -{ -public: - enum Flag { - SupportsAsync = 0x01 - }; - Q_DECLARE_FLAGS(Flags, Flag) - - virtual ~QQuickAbstractPathRenderer() { } - - // Gui thread - virtual void beginSync(int totalCount) = 0; - virtual void endSync(bool async) = 0; - virtual void setAsyncCallback(void (*)(void *), void *) { } - virtual Flags flags() const { return 0; } - // - QML API - virtual void setPath(int index, const QQuickPath *path) = 0; - // - JS API - virtual void setJSPath(int index, const QQuickPathItemPath &path) = 0; - // - stroke/fill parameters - virtual void setStrokeColor(int index, const QColor &color) = 0; - virtual void setStrokeWidth(int index, qreal w) = 0; - virtual void setFillColor(int index, const QColor &color) = 0; - virtual void setFillRule(int index, QQuickVisualPath::FillRule fillRule) = 0; - virtual void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) = 0; - virtual void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) = 0; - virtual void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) = 0; - virtual void setFillGradient(int index, QQuickPathGradient *gradient) = 0; - - // Render thread, with gui blocked - virtual void updateNode() = 0; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) - -class QQuickVisualPathPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQuickVisualPath) - -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStrokeColor = 0x02, - DirtyStrokeWidth = 0x04, - DirtyFillColor = 0x08, - DirtyFillRule = 0x10, - DirtyStyle = 0x20, - DirtyDash = 0x40, - DirtyFillGradient = 0x80, - - DirtyAll = 0xFF - }; - - QQuickVisualPathPrivate(); - - void _q_pathChanged(); - void _q_fillGradientChanged(); - - static QQuickVisualPathPrivate *get(QQuickVisualPath *p) { return p->d_func(); } - - QQuickPath *path; - int dirty; - QQuickPathItemStrokeFillParams sfp; -}; - -class QQuickPathItemPrivate : public QQuickItemPrivate -{ - Q_DECLARE_PUBLIC(QQuickPathItem) - -public: - QQuickPathItemPrivate(); - ~QQuickPathItemPrivate(); - - void createRenderer(); - QSGNode *createNode(); - void sync(); - - void _q_visualPathChanged(); - void setStatus(QQuickPathItem::Status newStatus); - - static QQuickPathItemPrivate *get(QQuickPathItem *item) { return item->d_func(); } - - bool componentComplete; - bool vpChanged; - QQuickPathItem::RendererType rendererType; - bool async; - QQuickPathItem::Status status; - QQuickAbstractPathRenderer *renderer; - - struct { - QVector vp; - } qmlData; - - struct { - bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } - QVector paths; - QVector sfp; - } jsData; - - bool enableVendorExts; -}; - -class QQuickPathItemPathObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemPathObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemPath path; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -class QQuickPathItemStrokeFillParamsObject : public QObject -{ - Q_OBJECT - -public: - QQuickPathItemStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickPathItemStrokeFillParams sfp; - QV4::PersistentValue v4fillGradient; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemGradientCache : public QOpenGLSharedResource -{ -public: - struct GradientDesc { - QGradientStops stops; - QPointF start; - QPointF end; - QQuickPathGradient::SpreadMode spread; - bool operator==(const GradientDesc &other) const - { - return start == other.start && end == other.end && spread == other.spread - && stops == other.stops; - } - }; - - QQuickPathItemGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } - ~QQuickPathItemGradientCache(); - - void invalidateResource() override; - void freeResource(QOpenGLContext *) override; - - QSGTexture *get(const GradientDesc &grad); - - static QQuickPathItemGradientCache *currentCache(); - -private: - QHash m_cache; -}; - -inline uint qHash(const QQuickPathItemGradientCache::GradientDesc &v, uint seed = 0) -{ - uint h = seed; - h += v.start.x() + v.end.y() + v.spread; - for (int i = 0; i < 3 && i < v.stops.count(); ++i) - h += v.stops[i].second.rgba(); - return h; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp b/src/imports/pathitem/qquickpathitemgenericrenderer.cpp deleted file mode 100644 index 4e8fe55df2..0000000000 --- a/src/imports/pathitem/qquickpathitemgenericrenderer.cpp +++ /dev/null @@ -1,775 +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 "qquickpathitemgenericrenderer_p.h" -#include -#include -#include - -#ifndef QT_NO_OPENGL -#include -#include -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -static const qreal TRI_SCALE = 1; - -struct ColoredVertex // must match QSGGeometry::ColoredPoint2D -{ - float x, y; - QQuickPathItemGenericRenderer::Color4ub color; - void set(float nx, float ny, QQuickPathItemGenericRenderer::Color4ub ncolor) - { - x = nx; y = ny; color = ncolor; - } -}; - -static inline QQuickPathItemGenericRenderer::Color4ub colorToColor4ub(const QColor &c) -{ - QQuickPathItemGenericRenderer::Color4ub color = { - uchar(qRound(c.redF() * c.alphaF() * 255)), - uchar(qRound(c.greenF() * c.alphaF() * 255)), - uchar(qRound(c.blueF() * c.alphaF() * 255)), - uchar(qRound(c.alphaF() * 255)) - }; - return color; -} - -QQuickPathItemGenericStrokeFillNode::QQuickPathItemGenericStrokeFillNode(QQuickWindow *window) - : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), - m_window(window), - m_material(nullptr) -{ - setGeometry(m_geometry); - activateMaterial(MatSolidColor); -#ifdef QSG_RUNTIME_DESCRIPTION - qsgnode_set_description(this, QLatin1String("stroke-fill")); -#endif -} - -QQuickPathItemGenericStrokeFillNode::~QQuickPathItemGenericStrokeFillNode() -{ - delete m_geometry; -} - -void QQuickPathItemGenericStrokeFillNode::activateMaterial(Material m) -{ - switch (m) { - case MatSolidColor: - // Use vertexcolor material. Items with different colors remain batchable - // this way, at the expense of having to provide per-vertex color values. - if (!m_solidColorMaterial) - m_solidColorMaterial.reset(QQuickPathItemGenericMaterialFactory::createVertexColor(m_window)); - m_material = m_solidColorMaterial.data(); - break; - case MatLinearGradient: - if (!m_linearGradientMaterial) - m_linearGradientMaterial.reset(QQuickPathItemGenericMaterialFactory::createLinearGradient(m_window, this)); - m_material = m_linearGradientMaterial.data(); - break; - default: - qWarning("Unknown material %d", m); - return; - } - - if (material() != m_material) - setMaterial(m_material); -} - -static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) -{ - static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) { - static bool elementIndexUintChecked = false; - if (!elementIndexUintChecked) { - elementIndexUintChecked = true; - QOpenGLContext *context = QOpenGLContext::currentContext(); - QScopedPointer dummyContext; - QScopedPointer dummySurface; - bool ok = true; - if (!context) { - dummyContext.reset(new QOpenGLContext); - dummyContext->create(); - context = dummyContext.data(); - dummySurface.reset(new QOffscreenSurface); - dummySurface->setFormat(context->format()); - dummySurface->create(); - ok = context->makeCurrent(dummySurface.data()); - } - if (ok) { - elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( - QOpenGLExtensions::ElementIndexUint); - } - } - } -#else - Q_UNUSED(api); -#endif - return elementIndexUint; -} - -QQuickPathItemGenericRenderer::~QQuickPathItemGenericRenderer() -{ - for (VisualPathData &d : m_vp) { - if (d.pendingFill) - d.pendingFill->orphaned = true; - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - } -} - -// sync, and so triangulation too, happens on the gui thread -// - except when async is set, in which case triangulation is moved to worker threads - -void QQuickPathItemGenericRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } - for (VisualPathData &d : m_vp) - d.syncDirty = 0; -} - -void QQuickPathItemGenericRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.strokeColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathData &d(m_vp[index]); - d.fillColor = colorToColor4ub(color); - d.syncDirty |= DirtyColor; -} - -void QQuickPathItemGenericRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.syncDirty |= DirtyFillGeom; -} - -void QQuickPathItemGenericRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathData &d(m_vp[index]); - d.pen.setStyle(Qt::PenStyle(strokeStyle)); - if (strokeStyle == QQuickVisualPath::DashLine) { - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - } - d.syncDirty |= DirtyStrokeGeom; -} - -void QQuickPathItemGenericRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.syncDirty |= DirtyFillGradient; -} - -void QQuickPathItemFillRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); - emit done(this); -} - -void QQuickPathItemStrokeRunnable::run() -{ - QQuickPathItemGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); - emit done(this); -} - -void QQuickPathItemGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) -{ - m_asyncCallback = callback; - m_asyncCallbackData = data; -} - -static QThreadPool *pathWorkThreadPool = nullptr; - -static void deletePathWorkThreadPool() -{ - delete pathWorkThreadPool; - pathWorkThreadPool = nullptr; -} - -void QQuickPathItemGenericRenderer::endSync(bool async) -{ - bool didKickOffAsync = false; - - for (int i = 0; i < m_vp.count(); ++i) { - VisualPathData &d(m_vp[i]); - if (!d.syncDirty) - continue; - - m_accDirty |= d.syncDirty; - - // Use a shadow dirty flag in order to avoid losing state in case there are - // multiple syncs with different dirty flags before we get to updateNode() - // on the render thread (with the gui thread blocked). For our purposes - // here syncDirty is still required since geometry regeneration must only - // happen when there was an actual change in this particular sync round. - d.effectiveDirty |= d.syncDirty; - - if (d.path.isEmpty()) { - d.fillVertices.clear(); - d.fillIndices.clear(); - d.strokeVertices.clear(); - continue; - } - - if (async && !pathWorkThreadPool) { - qAddPostRoutine(deletePathWorkThreadPool); - pathWorkThreadPool = new QThreadPool; - const int idealCount = QThread::idealThreadCount(); - pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); - } - - if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { - d.path.setFillRule(d.fillRule); - if (m_api == QSGRendererInterface::Unknown) - m_api = m_item->window()->rendererInterface()->graphicsApi(); - if (async) { - QQuickPathItemFillRunnable *r = new QQuickPathItemFillRunnable; - r->setAutoDelete(false); - if (d.pendingFill) - d.pendingFill->orphaned = true; - d.pendingFill = r; - r->path = d.path; - r->fillColor = d.fillColor; - r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); - // Unlikely in practice but in theory m_vp could be - // resized. Therefore, capture 'i' instead of 'd'. - QObject::connect(r, &QQuickPathItemFillRunnable::done, qApp, [this, i](QQuickPathItemFillRunnable *r) { - // Bail out when orphaned (meaning either another run was - // started after this one, or the renderer got destroyed). - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.fillVertices = r->fillVertices; - d.fillIndices = r->fillIndices; - d.indexType = r->indexType; - d.pendingFill = nullptr; - d.effectiveDirty |= DirtyFillGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); - } - } - - if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { - if (async) { - QQuickPathItemStrokeRunnable *r = new QQuickPathItemStrokeRunnable; - r->setAutoDelete(false); - if (d.pendingStroke) - d.pendingStroke->orphaned = true; - d.pendingStroke = r; - r->path = d.path; - r->pen = d.pen; - r->strokeColor = d.strokeColor; - r->clipSize = QSize(m_item->width(), m_item->height()); - QObject::connect(r, &QQuickPathItemStrokeRunnable::done, qApp, [this, i](QQuickPathItemStrokeRunnable *r) { - if (!r->orphaned && i < m_vp.count()) { - VisualPathData &d(m_vp[i]); - d.strokeVertices = r->strokeVertices; - d.pendingStroke = nullptr; - d.effectiveDirty |= DirtyStrokeGeom; - maybeUpdateAsyncItem(); - } - r->deleteLater(); - }); - didKickOffAsync = true; - pathWorkThreadPool->start(r); - } else { - triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, - QSize(m_item->width(), m_item->height())); - } - } - } - - if (!didKickOffAsync && async && m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -void QQuickPathItemGenericRenderer::maybeUpdateAsyncItem() -{ - for (const VisualPathData &d : qAsConst(m_vp)) { - if (d.pendingFill || d.pendingStroke) - return; - } - m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; - m_item->update(); - if (m_asyncCallback) - m_asyncCallback(m_asyncCallbackData); -} - -// the stroke/fill triangulation functions may be invoked either on the gui -// thread or some worker thread and must thus be self-contained. -void QQuickPathItemGenericRenderer::triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - - QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); - const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 - fillVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); - const qreal *vsrc = ts.vertices.constData(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); - - size_t indexByteSize; - if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { - *indexType = QSGGeometry::UnsignedShortType; - // fillIndices is still QVector. Just resize to N/2 and pack - // the N quint16s into it. - fillIndices->resize(ts.indices.size() / 2); - indexByteSize = ts.indices.size() * sizeof(quint16); - } else { - *indexType = QSGGeometry::UnsignedIntType; - fillIndices->resize(ts.indices.size()); - indexByteSize = ts.indices.size() * sizeof(quint32); - } - memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); -} - -void QQuickPathItemGenericRenderer::triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize) -{ - const QVectorPath &vp = qtVectorPathForPath(path); - const QRectF clip(QPointF(0, 0), clipSize); - const qreal inverseScale = 1.0 / TRI_SCALE; - - QTriangulatingStroker stroker; - stroker.setInvScale(inverseScale); - - if (pen.style() == Qt::SolidLine) { - stroker.process(vp, pen, clip, 0); - } else { - QDashedStrokeProcessor dashStroker; - dashStroker.setInvScale(inverseScale); - dashStroker.process(vp, pen, clip, 0); - QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), - dashStroker.elementTypes(), 0); - stroker.process(dashStroke, pen, clip, 0); - } - - if (!stroker.vertexCount()) { - strokeVertices->clear(); - return; - } - - const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 - strokeVertices->resize(vertexCount); - ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); - const float *vsrc = stroker.vertices(); - for (int i = 0; i < vertexCount; ++i) - vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); -} - -void QQuickPathItemGenericRenderer::setRootNode(QQuickPathItemGenericNode *node) -{ - if (m_rootNode != node) { - m_rootNode = node; - m_accDirty |= DirtyList; - } -} - -// on the render thread with gui blocked -void QQuickPathItemGenericRenderer::updateNode() -{ - if (!m_rootNode || !m_accDirty) - return; - -// [ m_rootNode ] -// / / / -// #0 [ fill ] [ stroke ] [ next ] -// / / | -// #1 [ fill ] [ stroke ] [ next ] -// / / | -// #2 [ fill ] [ stroke ] [ next ] -// ... -// ... - - QQuickPathItemGenericNode **nodePtr = &m_rootNode; - QQuickPathItemGenericNode *prevNode = nullptr; - - for (VisualPathData &d : m_vp) { - if (!*nodePtr) { - *nodePtr = new QQuickPathItemGenericNode; - prevNode->m_next = *nodePtr; - prevNode->appendChildNode(*nodePtr); - } - - QQuickPathItemGenericNode *node = *nodePtr; - - if (m_accDirty & DirtyList) - d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; - - if (!d.effectiveDirty) { - prevNode = node; - nodePtr = &node->m_next; - continue; - } - - if (d.fillColor.a == 0) { - delete node->m_fillNode; - node->m_fillNode = nullptr; - } else if (!node->m_fillNode) { - node->m_fillNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); - if (node->m_strokeNode) - node->removeChildNode(node->m_strokeNode); - node->appendChildNode(node->m_fillNode); - if (node->m_strokeNode) - node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyFillGeom; - } - - if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { - delete node->m_strokeNode; - node->m_strokeNode = nullptr; - } else if (!node->m_strokeNode) { - node->m_strokeNode = new QQuickPathItemGenericStrokeFillNode(m_item->window()); - node->appendChildNode(node->m_strokeNode); - d.effectiveDirty |= DirtyStrokeGeom; - } - - updateFillNode(&d, node); - updateStrokeNode(&d, node); - - d.effectiveDirty = 0; - - prevNode = node; - nodePtr = &node->m_next; - } - - if (*nodePtr && prevNode) { - prevNode->removeChildNode(*nodePtr); - delete *nodePtr; - *nodePtr = nullptr; - } - - m_accDirty = 0; -} - -void QQuickPathItemGenericRenderer::updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n) -{ - if (d->fillGradientActive) { - if (d->effectiveDirty & DirtyFillGradient) - n->m_fillGradient = d->fillGradient; - } -} - -void QQuickPathItemGenericRenderer::updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_fillNode) - return; - if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) - return; - - // Make a copy of the data that will be accessed by the material on - // the render thread. This must be done even when we bail out below. - QQuickPathItemGenericStrokeFillNode *n = node->m_fillNode; - updateShadowDataInNode(d, n); - - QSGGeometry *g = n->m_geometry; - if (d->fillVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - if (d->fillGradientActive) { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatLinearGradient); - if (d->effectiveDirty & DirtyFillGradient) { - // Gradients are implemented via a texture-based material. - n->markDirty(QSGNode::DirtyMaterial); - // stop here if only the gradient changed; no need to touch the geometry - if (!(d->effectiveDirty & DirtyFillGeom)) - return; - } - } else { - n->activateMaterial(QQuickPathItemGenericStrokeFillNode::MatSolidColor); - // fast path for updating only color values when no change in vertex positions - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); - n->markDirty(QSGNode::DirtyGeometry); - return; - } - } - - const int indexCount = d->indexType == QSGGeometry::UnsignedShortType - ? d->fillIndices.count() * 2 : d->fillIndices.count(); - if (g->indexType() != d->indexType) { - g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), - d->fillVertices.count(), indexCount, d->indexType); - n->setGeometry(g); - delete n->m_geometry; - n->m_geometry = g; - } else { - g->allocate(d->fillVertices.count(), indexCount); - } - g->setDrawingMode(QSGGeometry::DrawTriangles); - memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); - memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); - - n->markDirty(QSGNode::DirtyGeometry); -} - -void QQuickPathItemGenericRenderer::updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node) -{ - if (!node->m_strokeNode) - return; - if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) - return; - - QQuickPathItemGenericStrokeFillNode *n = node->m_strokeNode; - QSGGeometry *g = n->m_geometry; - if (d->strokeVertices.isEmpty()) { - if (g->vertexCount() || g->indexCount()) { - g->allocate(0, 0); - n->markDirty(QSGNode::DirtyGeometry); - } - return; - } - - n->markDirty(QSGNode::DirtyGeometry); - - // Async loading runs update once, bails out above, then updates again once - // ready. Set the material dirty then. This is in-line with fill where the - // first activateMaterial() achieves the same. - if (!g->vertexCount()) - n->markDirty(QSGNode::DirtyMaterial); - - if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { - ColoredVertex *vdst = reinterpret_cast(g->vertexData()); - for (int i = 0; i < g->vertexCount(); ++i) - vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); - return; - } - - g->allocate(d->strokeVertices.count(), 0); - g->setDrawingMode(QSGGeometry::DrawTriangleStrip); - memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createVertexColor(QQuickWindow *window) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QSGVertexColorMaterial; -#endif - - qWarning("Vertex-color material: Unsupported graphics API %d", api); - return nullptr; -} - -QSGMaterial *QQuickPathItemGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickPathItemGenericStrokeFillNode *node) -{ - QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); - -#ifndef QT_NO_OPENGL - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... - return new QQuickPathItemLinearGradientMaterial(node); -#endif - - qWarning("Linear gradient material: Unsupported graphics API %d", api); - return nullptr; -} - -#ifndef QT_NO_OPENGL - -QSGMaterialType QQuickPathItemLinearGradientShader::type; - -QQuickPathItemLinearGradientShader::QQuickPathItemLinearGradientShader() -{ - setShaderSourceFile(QOpenGLShader::Vertex, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); - setShaderSourceFile(QOpenGLShader::Fragment, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); -} - -void QQuickPathItemLinearGradientShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("opacity"); - m_matrixLoc = program()->uniformLocation("matrix"); - m_gradStartLoc = program()->uniformLocation("gradStart"); - m_gradEndLoc = program()->uniformLocation("gradEnd"); -} - -void QQuickPathItemLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) -{ - QQuickPathItemLinearGradientMaterial *m = static_cast(mat); - - if (state.isOpacityDirty()) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - - QQuickPathItemGenericStrokeFillNode *node = m->node(); - program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); - program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); - - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(node->m_fillGradient); - tx->bind(); -} - -char const *const *QQuickPathItemLinearGradientShader::attributeNames() const -{ - static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; - return attr; -} - -int QQuickPathItemLinearGradientMaterial::compare(const QSGMaterial *other) const -{ - Q_ASSERT(other && type() == other->type()); - const QQuickPathItemLinearGradientMaterial *m = static_cast(other); - - QQuickPathItemGenericStrokeFillNode *a = node(); - QQuickPathItemGenericStrokeFillNode *b = m->node(); - Q_ASSERT(a && b); - if (a == b) - return 0; - - const QQuickPathItemGradientCache::GradientDesc *ga = &a->m_fillGradient; - const QQuickPathItemGradientCache::GradientDesc *gb = &b->m_fillGradient; - - if (int d = ga->spread - gb->spread) - return d; - - if (int d = ga->start.x() - gb->start.x()) - return d; - if (int d = ga->start.y() - gb->start.y()) - return d; - if (int d = ga->end.x() - gb->end.x()) - return d; - if (int d = ga->end.y() - gb->end.y()) - return d; - - if (int d = ga->stops.count() - gb->stops.count()) - return d; - - for (int i = 0; i < ga->stops.count(); ++i) { - if (int d = ga->stops[i].first - gb->stops[i].first) - return d; - if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) - return d; - } - - return 0; -} - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h b/src/imports/pathitem/qquickpathitemgenericrenderer_p.h deleted file mode 100644 index 70a9e88d2f..0000000000 --- a/src/imports/pathitem/qquickpathitemgenericrenderer_p.h +++ /dev/null @@ -1,303 +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 QQUICKPATHITEMGENERICRENDERER_P_H -#define QQUICKPATHITEMGENERICRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemGenericNode; -class QQuickPathItemGenericStrokeFillNode; -class QQuickPathItemFillRunnable; -class QQuickPathItemStrokeRunnable; - -class QQuickPathItemGenericRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyFillGeom = 0x01, - DirtyStrokeGeom = 0x02, - DirtyColor = 0x04, - DirtyFillGradient = 0x08, - DirtyList = 0x10 // only for accDirty - }; - - QQuickPathItemGenericRenderer(QQuickItem *item) - : m_item(item), - m_api(QSGRendererInterface::Unknown), - m_rootNode(nullptr), - m_accDirty(0), - m_asyncCallback(nullptr) - { } - ~QQuickPathItemGenericRenderer(); - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - void setAsyncCallback(void (*)(void *), void *) override; - Flags flags() const override { return SupportsAsync; } - - void updateNode() override; - - void setRootNode(QQuickPathItemGenericNode *node); - - struct Color4ub { unsigned char r, g, b, a; }; - typedef QVector VertexContainerType; - typedef QVector IndexContainerType; - - static void triangulateFill(const QPainterPath &path, - const Color4ub &fillColor, - VertexContainerType *fillVertices, - IndexContainerType *fillIndices, - QSGGeometry::Type *indexType, - bool supportsElementIndexUint); - static void triangulateStroke(const QPainterPath &path, - const QPen &pen, - const Color4ub &strokeColor, - VertexContainerType *strokeVertices, - const QSize &clipSize); - -private: - void maybeUpdateAsyncItem(); - - struct VisualPathData { - float strokeWidth; - QPen pen; - Color4ub strokeColor; - Color4ub fillColor; - Qt::FillRule fillRule; - QPainterPath path; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - VertexContainerType fillVertices; - IndexContainerType fillIndices; - QSGGeometry::Type indexType; - VertexContainerType strokeVertices; - int syncDirty; - int effectiveDirty = 0; - QQuickPathItemFillRunnable *pendingFill = nullptr; - QQuickPathItemStrokeRunnable *pendingStroke = nullptr; - }; - - void updateShadowDataInNode(VisualPathData *d, QQuickPathItemGenericStrokeFillNode *n); - void updateFillNode(VisualPathData *d, QQuickPathItemGenericNode *node); - void updateStrokeNode(VisualPathData *d, QQuickPathItemGenericNode *node); - - QQuickItem *m_item; - QSGRendererInterface::GraphicsApi m_api; - QQuickPathItemGenericNode *m_rootNode; - QVector m_vp; - int m_accDirty; - void (*m_asyncCallback)(void *); - void *m_asyncCallbackData; -}; - -class QQuickPathItemFillRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QQuickPathItemGenericRenderer::Color4ub fillColor; - bool supportsElementIndexUint; - - // output - QQuickPathItemGenericRenderer::VertexContainerType fillVertices; - QQuickPathItemGenericRenderer::IndexContainerType fillIndices; - QSGGeometry::Type indexType; - -Q_SIGNALS: - void done(QQuickPathItemFillRunnable *self); -}; - -class QQuickPathItemStrokeRunnable : public QObject, public QRunnable -{ - Q_OBJECT - -public: - void run() override; - - bool orphaned = false; - - // input - QPainterPath path; - QPen pen; - QQuickPathItemGenericRenderer::Color4ub strokeColor; - QSize clipSize; - - // output - QQuickPathItemGenericRenderer::VertexContainerType strokeVertices; - -Q_SIGNALS: - void done(QQuickPathItemStrokeRunnable *self); -}; - -class QQuickPathItemGenericStrokeFillNode : public QSGGeometryNode -{ -public: - QQuickPathItemGenericStrokeFillNode(QQuickWindow *window); - ~QQuickPathItemGenericStrokeFillNode(); - - enum Material { - MatSolidColor, - MatLinearGradient - }; - - void activateMaterial(Material m); - - QQuickWindow *window() const { return m_window; } - - // shadow data for custom materials - QQuickPathItemGradientCache::GradientDesc m_fillGradient; - -private: - QSGGeometry *m_geometry; - QQuickWindow *m_window; - QSGMaterial *m_material; - QScopedPointer m_solidColorMaterial; - QScopedPointer m_linearGradientMaterial; - - friend class QQuickPathItemGenericRenderer; -}; - -class QQuickPathItemGenericNode : public QSGNode -{ -public: - QQuickPathItemGenericStrokeFillNode *m_fillNode = nullptr; - QQuickPathItemGenericStrokeFillNode *m_strokeNode = nullptr; - QQuickPathItemGenericNode *m_next = nullptr; -}; - -class QQuickPathItemGenericMaterialFactory -{ -public: - static QSGMaterial *createVertexColor(QQuickWindow *window); - static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickPathItemGenericStrokeFillNode *node); -}; - -#ifndef QT_NO_OPENGL - -class QQuickPathItemLinearGradientShader : public QSGMaterialShader -{ -public: - QQuickPathItemLinearGradientShader(); - - void initialize() override; - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; - char const *const *attributeNames() const override; - - static QSGMaterialType type; - -private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; -}; - -class QQuickPathItemLinearGradientMaterial : public QSGMaterial -{ -public: - QQuickPathItemLinearGradientMaterial(QQuickPathItemGenericStrokeFillNode *node) - : m_node(node) - { - // Passing RequiresFullMatrix is essential in order to prevent the - // batch renderer from baking in simple, translate-only transforms into - // the vertex data. The shader will rely on the fact that - // vertexCoord.xy is the PathItem-space coordinate and so no modifications - // are welcome. - setFlag(Blending | RequiresFullMatrix); - } - - QSGMaterialType *type() const override - { - return &QQuickPathItemLinearGradientShader::type; - } - - int compare(const QSGMaterial *other) const override; - - QSGMaterialShader *createShader() const override - { - return new QQuickPathItemLinearGradientShader; - } - - QQuickPathItemGenericStrokeFillNode *node() const { return m_node; } - -private: - QQuickPathItemGenericStrokeFillNode *m_node; -}; - -#endif // QT_NO_OPENGL - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMGENERICRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp b/src/imports/pathitem/qquickpathitemnvprrenderer.cpp deleted file mode 100644 index f8504f9985..0000000000 --- a/src/imports/pathitem/qquickpathitemnvprrenderer.cpp +++ /dev/null @@ -1,923 +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 "qquickpathitemnvprrenderer_p.h" -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemNvprRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemNvprRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - convertPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - convertJSPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemNvprRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = fillRule; - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemNvprRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.joinStyle = joinStyle; - d.miterLimit = miterLimit; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.capStyle = capStyle; - d.dirty |= DirtyStyle; - m_accDirty |= DirtyStyle; -} - -void QQuickPathItemNvprRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - d.dashActive = strokeStyle == QQuickVisualPath::DashLine; - d.dashOffset = dashOffset; - d.dashPattern = dashPattern; - d.dirty |= DirtyDash; - m_accDirty |= DirtyDash; -} - -void QQuickPathItemNvprRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillGradientActive = gradient != nullptr; - if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); - d.fillGradient.spread = gradient->spread(); - if (QQuickPathLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); - } else { - Q_UNREACHABLE(); - } - } - d.dirty |= DirtyFillGradient; - m_accDirty |= DirtyFillGradient; -} - -void QQuickPathItemNvprRenderer::endSync(bool) -{ -} - -void QQuickPathItemNvprRenderer::setNode(QQuickPathItemNvprRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path) -{ - QDebugStateSaver saver(debug); - debug.space().noquote(); - if (!path.str.isEmpty()) { - debug << "Path with SVG string" << path.str; - return debug; - } - debug << "Path with" << path.cmd.count() << "commands"; - int ci = 0; - for (GLubyte cmd : path.cmd) { - static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { - { GL_MOVE_TO_NV, "moveTo", 2 }, - { GL_LINE_TO_NV, "lineTo", 2 }, - { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, - { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, - { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, - { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, - { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, - { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, - { GL_CLOSE_PATH_NV, "closePath", 0 } }; - for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { - if (nameTab[i].cmd == cmd) { - QByteArray cs; - for (int j = 0; j < nameTab[i].coordCount; ++j) { - cs.append(QByteArray::number(path.coord[ci++])); - cs.append(' '); - } - debug << "\n " << nameTab[i].s << " " << cs; - break; - } - } - } - return debug; -} - -static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) -{ - QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), - c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); - v->append(p.x()); - v->append(p.y()); - *pos = p; -} - -static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), - c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), - c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); - v->append(p.x()); - v->append(p.y()); -} - -static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) -{ - QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), - c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); - v->append(p.x()); - v->append(p.y()); -} - -void QQuickPathItemNvprRenderer::convertPath(const QQuickPath *path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (!path) - return; - - const QList &pp(QQuickPathPrivate::get(path)->_pathElements); - if (pp.isEmpty()) - return; - - QPointF startPos(path->startX(), path->startY()); - QPointF pos(startPos); - if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { - d->path.cmd.append(GL_MOVE_TO_NV); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - } - - for (QQuickPathElement *e : pp) { - if (QQuickPathMove *o = qobject_cast(e)) { - d->path.cmd.append(GL_MOVE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - startPos = pos; - } else if (QQuickPathLine *o = qobject_cast(e)) { - d->path.cmd.append(GL_LINE_TO_NV); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathQuad *o = qobject_cast(e)) { - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - appendControlCoords(&d->path.coord, o, pos); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathCubic *o = qobject_cast(e)) { - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - appendControl1Coords(&d->path.coord, o, pos); - appendControl2Coords(&d->path.coord, o, pos); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathArc *o = qobject_cast(e)) { - const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo - GLenum cmd; - if (o->useLargeArc()) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(o->radiusX()); - d->path.coord.append(o->radiusY()); - d->path.coord.append(o->xAxisRotation()); - appendCoords(&d->path.coord, o, &pos); - } else if (QQuickPathSvg *o = qobject_cast(e)) { - // PathSvg cannot be combined with other elements. But take at - // least startX and startY into account. - if (d->path.str.isEmpty()) - d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); - d->path.str.append(o->path().toUtf8()); - } else { - qWarning() << "PathItem/NVPR: unsupported Path element" << e; - } - } - - // For compatibility with QTriangulatingStroker. SVG and others would not - // implicitly close the path when end_pos == start_pos (start_pos being the - // last moveTo pos); that would still need an explicit 'z' or similar. We - // don't have an explicit close command, so just fake a close when the - // positions match. - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -void QQuickPathItemNvprRenderer::convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d) -{ - d->path = NvprPath(); - if (path.cmd.isEmpty()) - return; - - QPointF startPos(0, 0); - QPointF pos(startPos); - int coordIdx = 0; - - for (QQuickPathItemPath::Command cmd : path.cmd) { - switch (cmd) { - case QQuickPathItemPath::MoveTo: - d->path.cmd.append(GL_MOVE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - startPos = pos; - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::LineTo: - d->path.cmd.append(GL_LINE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickPathItemPath::QuadTo: - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 4; - break; - case QQuickPathItemPath::CubicTo: - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - d->path.coord.append(path.coords[coordIdx + 2]); - d->path.coord.append(path.coords[coordIdx + 3]); - pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 6; - break; - case QQuickPathItemPath::ArcTo: - { - const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); - const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); - GLenum cmd; - if (useLargeArc) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(path.coords[coordIdx]); // rx - d->path.coord.append(path.coords[coordIdx + 1]); // ry - d->path.coord.append(path.coords[coordIdx + 2]); // xrot - pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 7; - } - break; - default: - qWarning("Unknown JS path command: %d", cmd); - break; - } - } - - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - -static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) -{ - const float o = c.alphaF() * globalOpacity; - return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); -} - -void QQuickPathItemNvprRenderer::updateNode() -{ - // Called on the render thread with gui blocked -> update the node with its - // own copy of all relevant data. - - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemNvprRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - int dirty = src.dirty; - src.dirty = 0; - if (listChanged) - dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; - - // updateNode() can be called several times with different dirty - // states before render() gets invoked. So accumulate. - dst.dirty |= dirty; - - if (dirty & DirtyPath) - dst.source = src.path; - - if (dirty & DirtyStyle) { - dst.strokeWidth = src.strokeWidth; - dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); - dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); - switch (src.joinStyle) { - case QQuickVisualPath::MiterJoin: - dst.joinStyle = GL_MITER_TRUNCATE_NV; - break; - case QQuickVisualPath::BevelJoin: - dst.joinStyle = GL_BEVEL_NV; - break; - case QQuickVisualPath::RoundJoin: - dst.joinStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); - } - dst.miterLimit = src.miterLimit; - switch (src.capStyle) { - case QQuickVisualPath::FlatCap: - dst.capStyle = GL_FLAT; - break; - case QQuickVisualPath::SquareCap: - dst.capStyle = GL_SQUARE_NV; - break; - case QQuickVisualPath::RoundCap: - dst.capStyle = GL_ROUND_NV; - break; - default: - Q_UNREACHABLE(); - } - } - - if (dirty & DirtyFillRule) { - switch (src.fillRule) { - case QQuickVisualPath::OddEvenFill: - dst.fillRule = GL_INVERT; - break; - case QQuickVisualPath::WindingFill: - dst.fillRule = GL_COUNT_UP_NV; - break; - default: - Q_UNREACHABLE(); - } - } - - if (dirty & DirtyDash) { - dst.dashOffset = src.dashOffset; - if (src.dashActive) { - dst.dashPattern.resize(src.dashPattern.count()); - // Multiply by strokeWidth because the PathItem API follows QPen - // meaning the input dash pattern here is in width units. - for (int i = 0; i < src.dashPattern.count(); ++i) - dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; - } else { - dst.dashPattern.clear(); - } - } - - if (dirty & DirtyFillGradient) { - dst.fillGradientActive = src.fillGradientActive; - if (src.fillGradientActive) - dst.fillGradient = src.fillGradient; - } - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -bool QQuickPathItemNvprRenderNode::nvprInited = false; -QQuickNvprFunctions QQuickPathItemNvprRenderNode::nvpr; -QQuickNvprMaterialManager QQuickPathItemNvprRenderNode::mtlmgr; - -QQuickPathItemNvprRenderNode::~QQuickPathItemNvprRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemNvprRenderNode::releaseResources() -{ - for (VisualPathRenderData &d : m_vp) { - if (d.path) { - nvpr.deletePaths(d.path, 1); - d.path = 0; - } - if (d.fallbackFbo) { - delete d.fallbackFbo; - d.fallbackFbo = nullptr; - } - } - - m_fallbackBlitter.destroy(); -} - -void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) -{ - m_nvpr = nvpr; -} - -void QQuickNvprMaterialManager::releaseResources() -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - for (MaterialDesc &mtl : m_materials) { - if (mtl.ppl) { - f->glDeleteProgramPipelines(1, &mtl.ppl); - mtl = MaterialDesc(); - } - } -} - -QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - MaterialDesc &mtl(m_materials[m]); - - if (!mtl.ppl) { - if (m == MatSolid) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "out vec4 fragColor;\n" - "uniform vec4 color;\n" - "uniform float opacity;\n" - "void main() {\n" - " fragColor = color * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for solid fill"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); - Q_ASSERT(mtl.uniLoc[0] >= 0); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - } else if (m == MatLinearGradient) { - static const char *fragSrc = - "#version 310 es\n" - "precision highp float;\n" - "layout(location = 0) in vec2 uv;" - "uniform float opacity;\n" - "uniform sampler2D gradTab;\n" - "uniform vec2 gradStart;\n" - "uniform vec2 gradEnd;\n" - "out vec4 fragColor;\n" - "void main() {\n" - " vec2 gradVec = gradEnd - gradStart;\n" - " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" - " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" - "}\n"; - if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { - qWarning("NVPR: Failed to create shader pipeline for linear gradient"); - return nullptr; - } - Q_ASSERT(mtl.ppl && mtl.prg); - mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); - Q_ASSERT(mtl.uniLoc[1] >= 0); - mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); - Q_ASSERT(mtl.uniLoc[2] >= 0); - mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); - Q_ASSERT(mtl.uniLoc[3] >= 0); - } else { - Q_UNREACHABLE(); - } - } - - f->glBindProgramPipeline(mtl.ppl); - - return &mtl; -} - -void QQuickPathItemNvprRenderNode::updatePath(VisualPathRenderData *d) -{ - if (d->dirty & QQuickPathItemNvprRenderer::DirtyPath) { - if (!d->path) { - d->path = nvpr.genPaths(1); - Q_ASSERT(d->path != 0); - } - if (d->source.str.isEmpty()) { - nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), - d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); - } else { - nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); - } - } - - if (d->dirty & QQuickPathItemNvprRenderer::DirtyStyle) { - nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); - nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); - nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); - nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); - nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); - } - - if (d->dirty & QQuickPathItemNvprRenderer::DirtyDash) { - nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); - // count == 0 -> no dash - nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); - } - - if (d->dirty) - d->fallbackValid = false; -} - -void QQuickPathItemNvprRenderNode::renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); -} - -void QQuickPathItemNvprRenderNode::renderFill(VisualPathRenderData *d) -{ - QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; - if (d->fillGradientActive) { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - QSGTexture *tx = QQuickPathItemGradientCache::currentCache()->get(d->fillGradient); - tx->bind(); - // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) - // where x and y are in path coordinate space, which is just what - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); - } else { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); - f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], - d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); - } - f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); - - const int writeMask = 0xFF; - nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); -} - -void QQuickPathItemNvprRenderNode::renderOffscreenFill(VisualPathRenderData *d) -{ - if (d->fallbackValid && d->fallbackFbo) - return; - - GLfloat bb[4]; - nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); - QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); - d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); - d->fallbackTopLeft = QPointF(bb[0], bb[1]); - - if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { - delete d->fallbackFbo; - d->fallbackFbo = nullptr; - } - if (!d->fallbackFbo) - d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); - if (!d->fallbackFbo->bind()) - return; - - GLint prevViewport[4]; - f->glGetIntegerv(GL_VIEWPORT, prevViewport); - - f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); - f->glDisable(GL_DEPTH_TEST); - f->glClearColor(0, 0, 0, 0); - f->glClearStencil(0); - f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - QMatrix4x4 mv; - mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); - QMatrix4x4 proj; - proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); - - renderFill(d); - - d->fallbackFbo->release(); - f->glEnable(GL_DEPTH_TEST); - f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); - - d->fallbackValid = true; -} - -void QQuickPathItemNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) -{ - if (!stencilClip) { - // Assume stencil buffer is cleared to 0 for each frame. - // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. - f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - } else { - f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) - f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) - } -} - -void QQuickPathItemNvprRenderNode::render(const RenderState *state) -{ - f = QOpenGLContext::currentContext()->extraFunctions(); - - if (!nvprInited) { - if (!nvpr.create()) { - qWarning("NVPR init failed"); - return; - } - mtlmgr.create(&nvpr); - nvprInited = true; - } - - f->glUseProgram(0); - f->glStencilMask(~0); - f->glEnable(GL_STENCIL_TEST); - - const bool stencilClip = state->stencilEnabled(); - // when true, the stencil buffer already has a clip path with a ref value of sv - const int sv = state->stencilValue(); - const bool hasScissor = state->scissorEnabled(); - - if (hasScissor) { - // scissor rect is already set, just enable scissoring - f->glEnable(GL_SCISSOR_TEST); - } - - // Depth test against the opaque batches rendered before. - f->glEnable(GL_DEPTH_TEST); - f->glDepthFunc(GL_LESS); - nvpr.pathCoverDepthFunc(GL_LESS); - nvpr.pathStencilDepthOffset(-0.05f, -1); - - bool reloadMatrices = true; - - for (VisualPathRenderData &d : m_vp) { - updatePath(&d); - - const bool hasFill = d.hasFill(); - const bool hasStroke = d.hasStroke(); - - if (hasFill && stencilClip) { - // Fall back to a texture when complex clipping is in use and we have - // to fill. Reconciling glStencilFillPath's and the scenegraph's clip - // stencil semantics has not succeeded so far... - if (hasScissor) - f->glDisable(GL_SCISSOR_TEST); - renderOffscreenFill(&d); - reloadMatrices = true; - if (hasScissor) - f->glEnable(GL_SCISSOR_TEST); - } - - if (reloadMatrices) { - reloadMatrices = false; - nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); - nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); - } - - // Fill! - if (hasFill) { - if (!stencilClip) { - setupStencilForCover(false, 0); - renderFill(&d); - } else { - if (!m_fallbackBlitter.isCreated()) - m_fallbackBlitter.create(); - f->glStencilFunc(GL_EQUAL, sv, 0xFF); - f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - QMatrix4x4 mv = *matrix(); - mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); - m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), - *state->projectionMatrix(), mv, - inheritedOpacity()); - } - } - - // Stroke! - if (hasStroke) { - const int strokeStencilValue = 0x80; - const int writeMask = 0x80; - - setupStencilForCover(stencilClip, sv); - if (stencilClip) { - // for the stencil step (eff. read mask == 0xFF & ~writeMask) - nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); - // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. - // This assumes the clip stencil value is <= 127. - if (sv >= strokeStencilValue) - qWarning("PathItem/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); - } - - renderStroke(&d, strokeStencilValue, writeMask); - } - - if (stencilClip) - nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); - - d.dirty = 0; - } - - f->glBindProgramPipeline(0); -} - -QSGRenderNode::StateFlags QQuickPathItemNvprRenderNode::changedStates() const -{ - return BlendState | StencilState | DepthState | ScissorState; -} - -QSGRenderNode::RenderingFlags QQuickPathItemNvprRenderNode::flags() const -{ - return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer -} - -bool QQuickPathItemNvprRenderNode::isSupported() -{ - static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; - return !nvprDisabled && QQuickNvprFunctions::isSupported(); -} - -bool QQuickNvprBlitter::create() -{ - if (isCreated()) - destroy(); - - m_program = new QOpenGLShaderProgram; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); - } else { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); - } - m_program->bindAttributeLocation("qt_Vertex", 0); - m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); - if (!m_program->link()) - return false; - - m_matrixLoc = m_program->uniformLocation("qt_Matrix"); - m_opacityLoc = m_program->uniformLocation("qt_Opacity"); - - m_buffer = new QOpenGLBuffer; - if (!m_buffer->create()) - return false; - m_buffer->bind(); - m_buffer->allocate(4 * sizeof(GLfloat) * 6); - m_buffer->release(); - - return true; -} - -void QQuickNvprBlitter::destroy() -{ - if (m_program) { - delete m_program; - m_program = nullptr; - } - if (m_buffer) { - delete m_buffer; - m_buffer = nullptr; - } -} - -void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity) -{ - QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); - - m_program->bind(); - - QMatrix4x4 m = proj * modelview; - m_program->setUniformValue(m_matrixLoc, m); - m_program->setUniformValue(m_opacityLoc, opacity); - - m_buffer->bind(); - - if (size != m_prevSize) { - m_prevSize = size; - - QPointF p0(size.width() - 1, size.height() - 1); - QPointF p1(0, 0); - QPointF p2(0, size.height() - 1); - QPointF p3(size.width() - 1, 0); - - GLfloat vertices[6 * 4] = { - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, - - GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, - GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, - GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, - }; - - m_buffer->write(0, vertices, sizeof(vertices)); - } - - m_program->enableAttributeArray(0); - m_program->enableAttributeArray(1); - f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); - f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); - - f->glBindTexture(GL_TEXTURE_2D, textureId); - - f->glDrawArrays(GL_TRIANGLES, 0, 6); - - f->glBindTexture(GL_TEXTURE_2D, 0); - m_buffer->release(); - m_program->release(); -} - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h b/src/imports/pathitem/qquickpathitemnvprrenderer_p.h deleted file mode 100644 index cfe1c7eab9..0000000000 --- a/src/imports/pathitem/qquickpathitemnvprrenderer_p.h +++ /dev/null @@ -1,237 +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 QQUICKPATHITEMNVPRRENDERER_P_H -#define QQUICKPATHITEMNVPRRENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include "qquicknvprfunctions_p.h" -#include -#include -#include -#include - -#ifndef QT_NO_OPENGL - -QT_BEGIN_NAMESPACE - -class QQuickPathItemNvprRenderNode; -class QOpenGLFramebufferObject; -class QOpenGLBuffer; -class QOpenGLExtraFunctions; - -class QQuickPathItemNvprRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyStyle = 0x02, - DirtyFillRule = 0x04, - DirtyDash = 0x08, - DirtyFillGradient = 0x10, - DirtyList = 0x20 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemNvprRenderNode *node); - - struct NvprPath { - QVector cmd; - QVector coord; - QByteArray str; - }; - -private: - struct VisualPathGuiData { - int dirty = 0; - NvprPath path; - qreal strokeWidth; - QColor strokeColor; - QColor fillColor; - QQuickVisualPath::JoinStyle joinStyle; - int miterLimit; - QQuickVisualPath::CapStyle capStyle; - QQuickVisualPath::FillRule fillRule; - bool dashActive; - qreal dashOffset; - QVector dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - }; - - void convertPath(const QQuickPath *path, VisualPathGuiData *d); - void convertJSPath(const QQuickPathItemPath &path, VisualPathGuiData *d); - - QQuickPathItemNvprRenderNode *m_node = nullptr; - int m_accDirty = 0; - - QVector m_vp; -}; - -QDebug operator<<(QDebug debug, const QQuickPathItemNvprRenderer::NvprPath &path); - -class QQuickNvprMaterialManager -{ -public: - enum Material { - MatSolid, - MatLinearGradient, - - NMaterials - }; - - struct MaterialDesc { - GLuint ppl = 0; - GLuint prg = 0; - int uniLoc[4]; - }; - - void create(QQuickNvprFunctions *nvpr); - MaterialDesc *activateMaterial(Material m); - void releaseResources(); - -private: - QQuickNvprFunctions *m_nvpr; - MaterialDesc m_materials[NMaterials]; -}; - -class QQuickNvprBlitter -{ -public: - bool create(); - void destroy(); - bool isCreated() const { return m_program != nullptr; } - void texturedQuad(GLuint textureId, const QSize &size, - const QMatrix4x4 &proj, const QMatrix4x4 &modelview, - float opacity); - -private: - QOpenGLShaderProgram *m_program = nullptr; - QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; - QSize m_prevSize; -}; - -class QQuickPathItemNvprRenderNode : public QSGRenderNode -{ -public: - ~QQuickPathItemNvprRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - - static bool isSupported(); - -private: - struct VisualPathRenderData { - GLuint path = 0; - int dirty = 0; - QQuickPathItemNvprRenderer::NvprPath source; - GLfloat strokeWidth; - QVector4D strokeColor; - QVector4D fillColor; - GLenum joinStyle; - GLint miterLimit; - GLenum capStyle; - GLenum fillRule; - GLfloat dashOffset; - QVector dashPattern; - bool fillGradientActive; - QQuickPathItemGradientCache::GradientDesc fillGradient; - QOpenGLFramebufferObject *fallbackFbo = nullptr; - bool fallbackValid = false; - QSize fallbackSize; - QPointF fallbackTopLeft; - - bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } - bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } - }; - - void updatePath(VisualPathRenderData *d); - void renderStroke(VisualPathRenderData *d, int strokeStencilValue, int writeMask); - void renderFill(VisualPathRenderData *d); - void renderOffscreenFill(VisualPathRenderData *d); - void setupStencilForCover(bool stencilClip, int sv); - - static bool nvprInited; - static QQuickNvprFunctions nvpr; - static QQuickNvprMaterialManager mtlmgr; - - QQuickNvprBlitter m_fallbackBlitter; - QOpenGLExtraFunctions *f = nullptr; - - QVector m_vp; - - friend class QQuickPathItemNvprRenderer; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_OPENGL - -#endif // QQUICKPATHITEMNVPRRENDERER_P_H diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp b/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp deleted file mode 100644 index b7aa93bf65..0000000000 --- a/src/imports/pathitem/qquickpathitemsoftwarerenderer.cpp +++ /dev/null @@ -1,277 +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 "qquickpathitemsoftwarerenderer_p.h" -#include - -QT_BEGIN_NAMESPACE - -void QQuickPathItemSoftwareRenderer::beginSync(int totalCount) -{ - if (m_vp.count() != totalCount) { - m_vp.resize(totalCount); - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::setPath(int index, const QQuickPath *path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path ? path->path() : QPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setJSPath(int index, const QQuickPathItemPath &path) -{ - VisualPathGuiData &d(m_vp[index]); - d.path = path.toPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - -void QQuickPathItemSoftwareRenderer::setStrokeColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setColor(color); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeWidth(int index, qreal w) -{ - VisualPathGuiData &d(m_vp[index]); - d.strokeWidth = w; - if (w >= 0.0f) - d.pen.setWidthF(w); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillColor(int index, const QColor &color) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillColor = color; - d.brush.setColor(color); - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::setFillRule(int index, QQuickVisualPath::FillRule fillRule) -{ - VisualPathGuiData &d(m_vp[index]); - d.fillRule = Qt::FillRule(fillRule); - d.dirty |= DirtyFillRule; - m_accDirty |= DirtyFillRule; -} - -void QQuickPathItemSoftwareRenderer::setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); - d.pen.setMiterLimit(miterLimit); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) -{ - VisualPathGuiData &d(m_vp[index]); - d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) -{ - VisualPathGuiData &d(m_vp[index]); - switch (strokeStyle) { - case QQuickVisualPath::SolidLine: - d.pen.setStyle(Qt::SolidLine); - break; - case QQuickVisualPath::DashLine: - d.pen.setStyle(Qt::CustomDashLine); - d.pen.setDashPattern(dashPattern); - d.pen.setDashOffset(dashOffset); - break; - default: - break; - } - d.dirty |= DirtyPen; - m_accDirty |= DirtyPen; -} - -void QQuickPathItemSoftwareRenderer::setFillGradient(int index, QQuickPathGradient *gradient) -{ - VisualPathGuiData &d(m_vp[index]); - if (QQuickPathLinearGradient *linearGradient = qobject_cast(gradient)) { - QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), - linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); - switch (gradient->spread()) { - case QQuickPathGradient::PadSpread: - painterGradient.setSpread(QGradient::PadSpread); - break; - case QQuickPathGradient::RepeatSpread: - painterGradient.setSpread(QGradient::RepeatSpread); - break; - case QQuickPathGradient::ReflectSpread: - painterGradient.setSpread(QGradient::ReflectSpread); - break; - default: - break; - } - d.brush = QBrush(painterGradient); - } else { - d.brush = QBrush(d.fillColor); - } - d.dirty |= DirtyBrush; - m_accDirty |= DirtyBrush; -} - -void QQuickPathItemSoftwareRenderer::endSync(bool) -{ -} - -void QQuickPathItemSoftwareRenderer::setNode(QQuickPathItemSoftwareRenderNode *node) -{ - if (m_node != node) { - m_node = node; - m_accDirty |= DirtyList; - } -} - -void QQuickPathItemSoftwareRenderer::updateNode() -{ - if (!m_accDirty) - return; - - const int count = m_vp.count(); - const bool listChanged = m_accDirty & DirtyList; - if (listChanged) - m_node->m_vp.resize(count); - - m_node->m_boundingRect = QRectF(); - - for (int i = 0; i < count; ++i) { - VisualPathGuiData &src(m_vp[i]); - QQuickPathItemSoftwareRenderNode::VisualPathRenderData &dst(m_node->m_vp[i]); - - if (listChanged || (src.dirty & DirtyPath)) { - dst.path = src.path; - dst.path.setFillRule(src.fillRule); - } - - if (listChanged || (src.dirty & DirtyFillRule)) - dst.path.setFillRule(src.fillRule); - - if (listChanged || (src.dirty & DirtyPen)) { - dst.pen = src.pen; - dst.strokeWidth = src.strokeWidth; - } - - if (listChanged || (src.dirty & DirtyBrush)) - dst.brush = src.brush; - - src.dirty = 0; - - QRectF br = dst.path.boundingRect(); - const float sw = qMax(1.0f, dst.strokeWidth); - br.adjust(-sw, -sw, sw, sw); - m_node->m_boundingRect |= br; - } - - m_node->markDirty(QSGNode::DirtyMaterial); - m_accDirty = 0; -} - -QQuickPathItemSoftwareRenderNode::QQuickPathItemSoftwareRenderNode(QQuickPathItem *item) - : m_item(item) -{ -} - -QQuickPathItemSoftwareRenderNode::~QQuickPathItemSoftwareRenderNode() -{ - releaseResources(); -} - -void QQuickPathItemSoftwareRenderNode::releaseResources() -{ -} - -void QQuickPathItemSoftwareRenderNode::render(const RenderState *state) -{ - if (m_vp.isEmpty()) - return; - - QSGRendererInterface *rif = m_item->window()->rendererInterface(); - QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); - Q_ASSERT(p); - - const QRegion *clipRegion = state->clipRegion(); - if (clipRegion && !clipRegion->isEmpty()) - p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform - - p->setTransform(matrix()->toTransform()); - p->setOpacity(inheritedOpacity()); - - for (const VisualPathRenderData &d : qAsConst(m_vp)) { - p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); - p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); - p->drawPath(d.path); - } -} - -QSGRenderNode::StateFlags QQuickPathItemSoftwareRenderNode::changedStates() const -{ - return 0; -} - -QSGRenderNode::RenderingFlags QQuickPathItemSoftwareRenderNode::flags() const -{ - return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() -} - -QRectF QQuickPathItemSoftwareRenderNode::rect() const -{ - return m_boundingRect; -} - -QT_END_NAMESPACE diff --git a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h b/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h deleted file mode 100644 index e76590bdfe..0000000000 --- a/src/imports/pathitem/qquickpathitemsoftwarerenderer_p.h +++ /dev/null @@ -1,136 +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 QQUICKPATHITEMSOFTWARERENDERER_P_H -#define QQUICKPATHITEMSOFTWARERENDERER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of a number of Qt sources files. This header file may change from -// version to version without notice, or even be removed. -// -// We mean it. -// - -#include "qquickpathitem_p_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickPathItemSoftwareRenderNode; - -class QQuickPathItemSoftwareRenderer : public QQuickAbstractPathRenderer -{ -public: - enum Dirty { - DirtyPath = 0x01, - DirtyPen = 0x02, - DirtyFillRule = 0x04, - DirtyBrush = 0x08, - DirtyList = 0x10 - }; - - void beginSync(int totalCount) override; - void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickPathItemPath &path) override; - void setStrokeColor(int index, const QColor &color) override; - void setStrokeWidth(int index, qreal w) override; - void setFillColor(int index, const QColor &color) override; - void setFillRule(int index, QQuickVisualPath::FillRule fillRule) override; - void setJoinStyle(int index, QQuickVisualPath::JoinStyle joinStyle, int miterLimit) override; - void setCapStyle(int index, QQuickVisualPath::CapStyle capStyle) override; - void setStrokeStyle(int index, QQuickVisualPath::StrokeStyle strokeStyle, - qreal dashOffset, const QVector &dashPattern) override; - void setFillGradient(int index, QQuickPathGradient *gradient) override; - void endSync(bool async) override; - - void updateNode() override; - - void setNode(QQuickPathItemSoftwareRenderNode *node); - -private: - QQuickPathItemSoftwareRenderNode *m_node = nullptr; - int m_accDirty = 0; - struct VisualPathGuiData { - int dirty = 0; - QPainterPath path; - QPen pen; - float strokeWidth; - QColor fillColor; - QBrush brush; - Qt::FillRule fillRule; - }; - QVector m_vp; -}; - -class QQuickPathItemSoftwareRenderNode : public QSGRenderNode -{ -public: - QQuickPathItemSoftwareRenderNode(QQuickPathItem *item); - ~QQuickPathItemSoftwareRenderNode(); - - void render(const RenderState *state) override; - void releaseResources() override; - StateFlags changedStates() const override; - RenderingFlags flags() const override; - QRectF rect() const override; - -private: - QQuickPathItem *m_item; - - struct VisualPathRenderData { - QPainterPath path; - QPen pen; - float strokeWidth; - QBrush brush; - }; - QVector m_vp; - QRectF m_boundingRect; - - friend class QQuickPathItemSoftwareRenderer; -}; - -QT_END_NAMESPACE - -#endif // QQUICKPATHITEMSOFTWARERENDERER_P_H diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp new file mode 100644 index 0000000000..2f2f8c74d3 --- /dev/null +++ b/src/imports/shapes/plugin.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 +#include + +#include "qquickshape_p.h" + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtQuick_Shapes); +#endif +} + +QT_BEGIN_NAMESPACE + +class QmlShapesPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QmlShapesPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes")); + qmlRegisterType(uri, 1, 0, "Shape"); + qmlRegisterType(uri, 1, 0, "ShapePath"); + qmlRegisterType(uri, 1, 0, "ShapeGradientStop"); + qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); + qmlRegisterType(uri, 1, 0, "ShapeLinearGradient"); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes new file mode 100644 index 0000000000..28d8dd1f12 --- /dev/null +++ b/src/imports/shapes/plugins.qmltypes @@ -0,0 +1,292 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.Shapes 1.0' + +Module { + dependencies: [] + Component { + name: "QQuickItem" + defaultProperty: "data" + prototype: "QObject" + Enum { + name: "TransformOrigin" + values: { + "TopLeft": 0, + "Top": 1, + "TopRight": 2, + "Left": 3, + "Center": 4, + "Right": 5, + "BottomLeft": 6, + "Bottom": 7, + "BottomRight": 8 + } + } + Property { name: "parent"; type: "QQuickItem"; isPointer: true } + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "x"; type: "double" } + Property { name: "y"; type: "double" } + Property { name: "z"; type: "double" } + Property { name: "width"; type: "double" } + Property { name: "height"; type: "double" } + Property { name: "opacity"; type: "double" } + Property { name: "enabled"; type: "bool" } + Property { name: "visible"; type: "bool" } + Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } + Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } + Property { name: "state"; type: "string" } + Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } + Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } + Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } + Property { name: "baselineOffset"; type: "double" } + Property { name: "clip"; type: "bool" } + Property { name: "focus"; type: "bool" } + Property { name: "activeFocus"; type: "bool"; isReadonly: true } + Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } + Property { name: "rotation"; type: "double" } + Property { name: "scale"; type: "double" } + Property { name: "transformOrigin"; type: "TransformOrigin" } + Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } + Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } + Property { name: "smooth"; type: "bool" } + Property { name: "antialiasing"; type: "bool" } + Property { name: "implicitWidth"; type: "double" } + Property { name: "implicitHeight"; type: "double" } + Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } + Signal { + name: "childrenRectChanged" + Parameter { type: "QRectF" } + } + Signal { + name: "baselineOffsetChanged" + Parameter { type: "double" } + } + Signal { + name: "stateChanged" + Parameter { type: "string" } + } + Signal { + name: "focusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusChanged" + Parameter { type: "bool" } + } + Signal { + name: "activeFocusOnTabChanged" + revision: 1 + Parameter { type: "bool" } + } + Signal { + name: "parentChanged" + Parameter { type: "QQuickItem"; isPointer: true } + } + Signal { + name: "transformOriginChanged" + Parameter { type: "TransformOrigin" } + } + Signal { + name: "smoothChanged" + Parameter { type: "bool" } + } + Signal { + name: "antialiasingChanged" + Parameter { type: "bool" } + } + Signal { + name: "clipChanged" + Parameter { type: "bool" } + } + Signal { + name: "windowChanged" + revision: 1 + Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } + } + Method { name: "update" } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + Parameter { name: "targetSize"; type: "QSize" } + } + Method { + name: "grabToImage" + revision: 2 + type: "bool" + Parameter { name: "callback"; type: "QJSValue" } + } + Method { + name: "contains" + type: "bool" + Parameter { name: "point"; type: "QPointF" } + } + Method { + name: "mapFromItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToItem" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapFromGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "mapToGlobal" + revision: 7 + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { name: "forceActiveFocus" } + Method { + name: "forceActiveFocus" + Parameter { name: "reason"; type: "Qt::FocusReason" } + } + Method { + name: "nextItemInFocusChain" + revision: 1 + type: "QQuickItem*" + Parameter { name: "forward"; type: "bool" } + } + Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } + Method { + name: "childAt" + type: "QQuickItem*" + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + } + Component { + name: "QQuickShapeGradient" + defaultProperty: "stops" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapeGradient 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "SpreadMode" + values: { + "PadSpread": 0, + "RepeatSpread": 1, + "ReflectSpread": 2 + } + } + Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "spread"; type: "SpreadMode" } + Signal { name: "updated" } + } + Component { + name: "QQuickShapeGradientStop" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapeGradientStop 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "position"; type: "double" } + Property { name: "color"; type: "QColor" } + } + Component { + name: "QQuickShape" + defaultProperty: "elements" + prototype: "QQuickItem" + exports: ["QtQuick.Shapes/Shape 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "RendererType" + values: { + "UnknownRenderer": 0, + "GeometryRenderer": 1, + "NvprRenderer": 2, + "SoftwareRenderer": 3 + } + } + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Processing": 2 + } + } + Property { name: "renderer"; type: "RendererType"; isReadonly: true } + Property { name: "asynchronous"; type: "bool" } + Property { name: "enableVendorExtensions"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "elements"; type: "QQuickShapePath"; isList: true; isReadonly: true } + } + Component { + name: "QQuickShapeLinearGradient" + defaultProperty: "stops" + prototype: "QQuickShapeGradient" + exports: ["QtQuick.Shapes/ShapeLinearGradient 1.0"] + exportMetaObjectRevisions: [0] + Property { name: "x1"; type: "double" } + Property { name: "y1"; type: "double" } + Property { name: "x2"; type: "double" } + Property { name: "y2"; type: "double" } + } + Component { + name: "QQuickShapePath" + defaultProperty: "path" + prototype: "QObject" + exports: ["QtQuick.Shapes/ShapePath 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "FillRule" + values: { + "OddEvenFill": 0, + "WindingFill": 1 + } + } + Enum { + name: "JoinStyle" + values: { + "MiterJoin": 0, + "BevelJoin": 64, + "RoundJoin": 128 + } + } + Enum { + name: "CapStyle" + values: { + "FlatCap": 0, + "SquareCap": 16, + "RoundCap": 32 + } + } + Enum { + name: "StrokeStyle" + values: { + "SolidLine": 1, + "DashLine": 2 + } + } + Property { name: "path"; type: "QQuickPath"; isPointer: true } + Property { name: "strokeColor"; type: "QColor" } + Property { name: "strokeWidth"; type: "double" } + Property { name: "fillColor"; type: "QColor" } + Property { name: "fillRule"; type: "FillRule" } + Property { name: "joinStyle"; type: "JoinStyle" } + Property { name: "miterLimit"; type: "int" } + Property { name: "capStyle"; type: "CapStyle" } + Property { name: "strokeStyle"; type: "StrokeStyle" } + Property { name: "dashOffset"; type: "double" } + Property { name: "dashPattern"; type: "QVector" } + Property { name: "fillGradient"; type: "QQuickShapeGradient"; isPointer: true } + Signal { name: "changed" } + } +} diff --git a/src/imports/shapes/qmldir b/src/imports/shapes/qmldir new file mode 100644 index 0000000000..306ad1ecd7 --- /dev/null +++ b/src/imports/shapes/qmldir @@ -0,0 +1,4 @@ +module QtQuick.Shapes +plugin qmlshapesplugin +classname QmlShapesPlugin +typeinfo plugins.qmltypes diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp new file mode 100644 index 0000000000..e9b93cf4a1 --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 "qquicknvprfunctions_p.h" + +#ifndef QT_NO_OPENGL + +#include +#include +#include +#include "qquicknvprfunctions_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickNvprFunctions + + \brief Function resolvers and other helpers for GL_NV_path_rendering + for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner + that does not distract builds that do not have NVPR support either at + compile or run time. + + \internal + */ + +QQuickNvprFunctions::QQuickNvprFunctions() + : d(new QQuickNvprFunctionsPrivate(this)) +{ +} + +QQuickNvprFunctions::~QQuickNvprFunctions() +{ + delete d; +} + +/*! + \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top + of OpenGL 4.3 or OpenGL ES 3.1. + */ +QSurfaceFormat QQuickNvprFunctions::format() +{ + QSurfaceFormat fmt; + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { + fmt.setVersion(3, 1); + } + return fmt; +} + +/*! + \return true if GL_NV_path_rendering is supported with the current OpenGL + context. + + When there is no current context, a temporary dummy one will be created and + made current. + */ +bool QQuickNvprFunctions::isSupported() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QScopedPointer tempContext; + QScopedPointer tempSurface; + if (!ctx) { + tempContext.reset(new QOpenGLContext); + if (!tempContext->create()) + return false; + ctx = tempContext.data(); + tempSurface.reset(new QOffscreenSurface); + tempSurface->setFormat(ctx->format()); + tempSurface->create(); + if (!ctx->makeCurrent(tempSurface.data())) + return false; + } + + if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) + return false; + + // Do not check for DSA as the string may not be exposed on ES + // drivers, yet the functions we need are resolvable. +#if 0 + if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) { + qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported"); + return false; + } +#endif + + return true; +} + +/*! + Initializes using the current OpenGL context. + + \return true when GL_NV_path_rendering is supported and initialization was + successful. + */ +bool QQuickNvprFunctions::create() +{ + return isSupported() && d->resolve(); +} + +/*! + Creates a program pipeline consisting of a separable fragment shader program. + + This is essential for using NVPR with OpenGL ES 3.1+ since normal, + GLES2-style programs would not work without a vertex shader. + + \note \a fragmentShaderSource should be a \c{version 310 es} shader since + this works both on desktop and embedded NVIDIA drivers, thus avoiding the + need to fight GLSL and GLSL ES differences. + + The pipeline object is stored into \a pipeline, the fragment shader program + into \a program. + + Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc. + + \return \c false on failure in which case the error log is printed on the + debug output. \c true on success. + */ +bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + if (!ctx) + return false; + + QOpenGLExtraFunctions *f = ctx->extraFunctions(); + *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource); + GLint status = 0; + f->glGetProgramiv(*program, GL_LINK_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data()); + qWarning("Failed to create separable shader program:\n%s", s.constData()); + } + return false; + } + + f->glGenProgramPipelines(1, pipeline); + f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program); + f->glActiveShaderProgram(*pipeline, *program); + + f->glValidateProgramPipeline(*pipeline); + status = 0; + f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status); + if (!status) { + GLint len = 0; + f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len); + if (len) { + QByteArray s; + s.resize(len); + f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data()); + qWarning("Program pipeline validation failed:\n%s", s.constData()); + } + return false; + } + + return true; +} + +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + +bool QQuickNvprFunctionsPrivate::resolve() +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + + q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV); + q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV); + q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV); + q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV); + q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV); + q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV); + q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV); + q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV); + q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV); + q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV); + q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV); + q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV); + q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV); + q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV); + q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV); + q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV); + q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV); + q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV); + q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV); + q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV); + q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV); + q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV); + q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV); + q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV); + q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV); + q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV); + q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV); + q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV); + q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV); + q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV); + q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV); + q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV); + q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV); + q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV); + q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV); + q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV); + q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV); + q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV); + q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV); + q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV); + q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV); + q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV); + q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV); + q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV); + q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV); + q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV); + q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV); + q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV); + q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV); + q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV); + q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV); + q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV); + q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV); + q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV); + q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV); + q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV); + q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV); + + q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT); + q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT); + + return q->genPaths != nullptr // base path rendering ext + && q->programPathFragmentInputGen != nullptr // updated path rendering ext + && q->matrixLoadf != nullptr // direct state access ext + && q->matrixLoadIdentity != nullptr; +} + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/imports/shapes/qquicknvprfunctions_p.h new file mode 100644 index 0000000000..dd45dd7daa --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions_p.h @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_H +#define QQUICKNVPRFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +// note: fixed pipeline specific functions are removed - modern ES ext +// headers have all this, but not the fixed stuff + +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D + +typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +#endif + +#ifndef GL_FLAT +#define GL_FLAT 0x1D00 +#endif + +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif + +// this one originates from fixed pipeline so may not be in GLES ext headers, but we need it still +#ifndef GL_OBJECT_LINEAR_NV +#define GL_OBJECT_LINEAR_NV 0x2401 +#endif + +#ifndef GL_EXT_direct_state_access +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +#endif + +// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR +// code path even though it's never used. Keep it compiling by defining the +// necessary ES 3.1 separable program constants. +#ifndef GL_FRAGMENT_SHADER_BIT +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#endif +#ifndef GL_UNIFORM +#define GL_UNIFORM 0x92E1 +#endif + +class QQuickNvprFunctionsPrivate; + +class QQuickNvprFunctions +{ +public: + QQuickNvprFunctions(); + ~QQuickNvprFunctions(); + + static QSurfaceFormat format(); + static bool isSupported(); + + bool create(); + + bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program); + + PFNGLGENPATHSNVPROC genPaths = nullptr; + PFNGLDELETEPATHSNVPROC deletePaths = nullptr; + PFNGLISPATHNVPROC isPath = nullptr; + PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr; + PFNGLPATHCOORDSNVPROC pathCoords = nullptr; + PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr; + PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr; + PFNGLPATHSTRINGNVPROC pathString = nullptr; + PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr; + PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr; + PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr; + PFNGLCOPYPATHNVPROC copyPath = nullptr; + PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr; + PFNGLTRANSFORMPATHNVPROC transformPath = nullptr; + PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr; + PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr; + PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr; + PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr; + PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr; + PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr; + PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr; + PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr; + PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr; + PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr; + PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr; + PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr; + PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr; + PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr; + PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr; + PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr; + PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr; + PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr; + PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr; + PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr; + PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr; + PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr; + PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr; + PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr; + PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr; + PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr; + PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr; + PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr; + PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr; + PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr; + PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr; + PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr; + PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr; + PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr; + PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr; + PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr; + PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr; + PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr; + PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr; + PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr; + PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr; + + PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr; + PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr; + +private: + QQuickNvprFunctionsPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/shapes/qquicknvprfunctions_p_p.h b/src/imports/shapes/qquicknvprfunctions_p_p.h new file mode 100644 index 0000000000..6df20566af --- /dev/null +++ b/src/imports/shapes/qquicknvprfunctions_p_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QQUICKNVPRFUNCTIONS_P_P_H +#define QQUICKNVPRFUNCTIONS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicknvprfunctions_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickNvprFunctionsPrivate +{ +public: + QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { } + + bool resolve(); + + QQuickNvprFunctions *q; +}; + +QT_END_NAMESPACE + +#endif // QQUICKNVPRFUNCTIONS_P_P_H diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp new file mode 100644 index 0000000000..e37addba07 --- /dev/null +++ b/src/imports/shapes/qquickshape.cpp @@ -0,0 +1,1485 @@ +/**************************************************************************** +** +** 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 "qquickshape_p.h" +#include "qquickshape_p_p.h" +#include "qquickshapegenericrenderer_p.h" +#include "qquickshapenvprrenderer_p.h" +#include "qquickshapesoftwarerenderer_p.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQuick.Shapes 1.0 + \title Qt Quick Shapes QML Types + \ingroup qmlmodules + \brief Provides QML types for drawing stroked and filled shapes + + To use the types in this module, import the module with the following line: + + \code + import QtQuick.Shapes 1.0 + \endcode +*/ + +QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams() + : strokeColor(Qt::white), + strokeWidth(1), + fillColor(Qt::white), + fillRule(QQuickShapePath::OddEvenFill), + joinStyle(QQuickShapePath::BevelJoin), + miterLimit(2), + capStyle(QQuickShapePath::SquareCap), + strokeStyle(QQuickShapePath::SolidLine), + dashOffset(0), + fillGradient(nullptr) +{ + dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space +} + +QPainterPath QQuickShapePathCommands::toPainterPath() const +{ + QPainterPath p; + int coordIdx = 0; + for (int i = 0; i < cmd.count(); ++i) { + switch (cmd[i]) { + case QQuickShapePathCommands::MoveTo: + p.moveTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickShapePathCommands::LineTo: + p.lineTo(coords[coordIdx], coords[coordIdx + 1]); + coordIdx += 2; + break; + case QQuickShapePathCommands::QuadTo: + p.quadTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3]); + coordIdx += 4; + break; + case QQuickShapePathCommands::CubicTo: + p.cubicTo(coords[coordIdx], coords[coordIdx + 1], + coords[coordIdx + 2], coords[coordIdx + 3], + coords[coordIdx + 4], coords[coordIdx + 5]); + coordIdx += 6; + break; + case QQuickShapePathCommands::ArcTo: + // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser + QQuickSvgParser::pathArc(p, + coords[coordIdx], coords[coordIdx + 1], // radius + coords[coordIdx + 2], // xAxisRotation + !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc + !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag + coords[coordIdx + 3], coords[coordIdx + 4], // end + p.currentPosition().x(), p.currentPosition().y()); + coordIdx += 7; + break; + default: + qWarning("Unknown JS path command: %d", cmd[i]); + break; + } + } + return p; +} + +/*! + \qmltype ShapePath + \instantiates QQuickShapePath + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Describes a Path and associated properties for stroking and filling + \since 5.10 + + A Shape contains one or more ShapePath elements. At least one + ShapePath is necessary in order to have a Shape output anything + visible. A ShapeItem in turn contains a Path and properties describing the + stroking and filling parameters, such as the stroke width and color, the + fill color or gradient, join and cap styles, and so on. Finally, the Path + object contains a list of path elements like PathMove, PathLine, PathCubic, + PathQuad, PathArc. + + Any property changes in these data sets will be bubble up and change the + output of the Shape. This means that it is simple and easy to change, or + even animate, the starting and ending position, control points, or any + stroke or fill parameters using the usual QML bindings and animation types + like NumberAnimation. + + In the following example the line join style changes automatically based on + the value of joinStyleIndex: + + \code + ShapePath { + strokeColor: "black" + strokeWidth: 16 + fillColor: "transparent" + capStyle: ShapePath.RoundCap + + property int joinStyleIndex: 0 + property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ] + + joinStyle: styles[joinStyleIndex] + + Path { + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } + } + } + \endcode + + Once associated with a Shape, here is the output with a joinStyleIndex + of 2 (ShapePath.RoundJoin): + + \image visualpath-code-example.png + */ + +QQuickShapePathPrivate::QQuickShapePathPrivate() + : path(nullptr), + dirty(DirtyAll) +{ +} + +QQuickShapePath::QQuickShapePath(QObject *parent) + : QObject(*(new QQuickShapePathPrivate), parent) +{ +} + +QQuickShapePath::~QQuickShapePath() +{ +} + +/*! + \qmlproperty Path QtQuick.Shapes::ShapePath::path + + This property holds the Path object. + + \default + */ + +QQuickPath *QQuickShapePath::path() const +{ + Q_D(const QQuickShapePath); + return d->path; +} + +void QQuickShapePath::setPath(QQuickPath *path) +{ + Q_D(QQuickShapePath); + if (d->path == path) + return; + + if (d->path) + qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickShapePath, SLOT(_q_pathChanged())); + d->path = path; + qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), + this, QQuickShapePath, SLOT(_q_pathChanged())); + + d->dirty |= QQuickShapePathPrivate::DirtyPath; + emit pathChanged(); + emit changed(); +} + +void QQuickShapePathPrivate::_q_pathChanged() +{ + Q_Q(QQuickShapePath); + dirty |= DirtyPath; + emit q->changed(); +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor + + This property holds the stroking color. + + When set to \c transparent, no stroking occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::strokeColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeColor; +} + +void QQuickShapePath::setStrokeColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeColor != color) { + d->sfp.strokeColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor; + emit strokeColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::strokeWidth + + This property holds the stroke width. + + When set to a negative value, no stroking occurs. + + The default value is 1. + */ + +qreal QQuickShapePath::strokeWidth() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeWidth; +} + +void QQuickShapePath::setStrokeWidth(qreal w) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeWidth != w) { + d->sfp.strokeWidth = w; + d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth; + emit strokeWidthChanged(); + emit changed(); + } +} + +/*! + \qmlproperty color QtQuick.Shapes::ShapePath::fillColor + + This property holds the fill color. + + When set to \c transparent, no filling occurs. + + The default value is \c white. + */ + +QColor QQuickShapePath::fillColor() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillColor; +} + +void QQuickShapePath::setFillColor(const QColor &color) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillColor != color) { + d->sfp.fillColor = color; + d->dirty |= QQuickShapePathPrivate::DirtyFillColor; + emit fillColorChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule + + This property holds the fill rule. The default value is + ShapePath.OddEvenFill. For an example on fill rules, see + QPainterPath::setFillRule(). + + \list + \li ShapePath.OddEvenFill + \li ShapePath.WindingFill + \endlist + */ + +QQuickShapePath::FillRule QQuickShapePath::fillRule() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillRule; +} + +void QQuickShapePath::setFillRule(FillRule fillRule) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillRule != fillRule) { + d->sfp.fillRule = fillRule; + d->dirty |= QQuickShapePathPrivate::DirtyFillRule; + emit fillRuleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle + + This property defines how joins between two connected lines are drawn. The + default value is ShapePath.BevelJoin. + + \list + \li ShapePath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled. + \li ShapePath.BevelJoin - The triangular notch between the two lines is filled. + \li ShapePath.RoundJoin - A circular arc between the two lines is filled. + \endlist + */ + +QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.joinStyle; +} + +void QQuickShapePath::setJoinStyle(JoinStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.joinStyle != style) { + d->sfp.joinStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit joinStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit + + When ShapePath.joinStyle is set to ShapePath.MiterJoin, this property + specifies how far the miter join can extend from the join point. + + The default value is 2. + */ + +int QQuickShapePath::miterLimit() const +{ + Q_D(const QQuickShapePath); + return d->sfp.miterLimit; +} + +void QQuickShapePath::setMiterLimit(int limit) +{ + Q_D(QQuickShapePath); + if (d->sfp.miterLimit != limit) { + d->sfp.miterLimit = limit; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit miterLimitChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle + + This property defines how the end points of lines are drawn. The + default value is ShapePath.SquareCap. + + \list + \li ShapePath.FlatCap - A square line end that does not cover the end point of the line. + \li ShapePath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width. + \li ShapePath.RoundCap - A rounded line end. + \endlist + */ + +QQuickShapePath::CapStyle QQuickShapePath::capStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.capStyle; +} + +void QQuickShapePath::setCapStyle(CapStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.capStyle != style) { + d->sfp.capStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyStyle; + emit capStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle + + This property defines the style of stroking. The default value is + ShapePath.SolidLine. + + \list + \li ShapePath.SolidLine - A plain line. + \li ShapePath.DashLine - Dashes separated by a few pixels. + \endlist + */ + +QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const +{ + Q_D(const QQuickShapePath); + return d->sfp.strokeStyle; +} + +void QQuickShapePath::setStrokeStyle(StrokeStyle style) +{ + Q_D(QQuickShapePath); + if (d->sfp.strokeStyle != style) { + d->sfp.strokeStyle = style; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit strokeStyleChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset + + This property defines the starting point on the dash pattern, measured in + units used to specify the dash pattern. + + The default value is 0. + + \sa QPen::setDashOffset() + */ + +qreal QQuickShapePath::dashOffset() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashOffset; +} + +void QQuickShapePath::setDashOffset(qreal offset) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashOffset != offset) { + d->sfp.dashOffset = offset; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashOffsetChanged(); + emit changed(); + } +} + +/*! + \qmlproperty list QtQuick.Shapes::ShapePath::dashPattern + + This property defines the dash pattern when ShapePath.strokeStyle is set + to ShapePath.DashLine. The pattern must be specified as an even number of + positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6... + are the spaces. The pattern is specified in units of the pen's width. + + The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth + pixels followed by a space of 2 * ShapePath.strokeWidth pixels. + + \sa QPen::setDashPattern() + */ + +QVector QQuickShapePath::dashPattern() const +{ + Q_D(const QQuickShapePath); + return d->sfp.dashPattern; +} + +void QQuickShapePath::setDashPattern(const QVector &array) +{ + Q_D(QQuickShapePath); + if (d->sfp.dashPattern != array) { + d->sfp.dashPattern = array; + d->dirty |= QQuickShapePathPrivate::DirtyDash; + emit dashPatternChanged(); + emit changed(); + } +} + +/*! + \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient + + This property defines the fill gradient. By default no gradient is enabled + and the value is \c null. In this case the fill uses a solid color based on + the value of ShapePath.fillColor. + + When set, ShapePath.fillColor is ignored and filling is done using one of + the ShapeGradient subtypes. + */ + +QQuickShapeGradient *QQuickShapePath::fillGradient() const +{ + Q_D(const QQuickShapePath); + return d->sfp.fillGradient; +} + +void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient) +{ + Q_D(QQuickShapePath); + if (d->sfp.fillGradient != gradient) { + if (d->sfp.fillGradient) + qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->sfp.fillGradient = gradient; + if (d->sfp.fillGradient) + qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), + this, QQuickShapePath, SLOT(_q_fillGradientChanged())); + d->dirty |= QQuickShapePathPrivate::DirtyFillGradient; + emit changed(); + } +} + +void QQuickShapePathPrivate::_q_fillGradientChanged() +{ + Q_Q(QQuickShapePath); + dirty |= DirtyFillGradient; + emit q->changed(); +} + +void QQuickShapePath::resetFillGradient() +{ + setFillGradient(nullptr); +} + +/*! + \qmltype Shape + \instantiates QQuickShape + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Item + \brief Renders a path + \since 5.10 + + Renders a path either by generating geometry via QPainterPath and manual + triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}. + + This approach is different from rendering shapes via QQuickPaintedItem or + the 2D Canvas because the path never gets rasterized in software. Therefore + Shape is suitable for creating shapes spreading over larger areas of the + screen, avoiding the performance penalty for texture uploads or framebuffer + blits. In addition, the declarative API allows manipulating, binding to, + and even animating the path element properties like starting and ending + position, the control points, etc. + + The types for specifying path elements are shared between \l PathView and + Shape. However, not all Shape implementations support all path + element types, while some may not make sense for PathView. Shape's + currently supported subset is: PathMove, PathLine, PathQuad, PathCubic, + PathArc, PathSvg. + + See \l Path for a detailed overview of the supported path elements. + + \code + Shape { + width: 200 + height: 150 + anchors.centerIn: parent + ShapePath { + strokeWidth: 4 + strokeColor: "red" + fillGradient: ShapeLinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + ShapeGradientStop { position: 0; color: "blue" } + ShapeGradientStop { position: 0.2; color: "green" } + ShapeGradientStop { position: 0.4; color: "red" } + ShapeGradientStop { position: 0.6; color: "yellow" } + ShapeGradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + Path { + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } + } + } + } + \endcode + + \image pathitem-code-example.png + + \note It is important to be aware of performance implications, in particular + when the application is running on the generic Shape implementation due to + not having support for accelerated path rendering. The geometry generation + happens entirely on the CPU in this case, and this is potentially + expensive. Changing the set of path elements, changing the properties of + these elements, or changing certain properties of the Shape itself all lead + to retriangulation of the affected elements on every change. Therefore, + applying animation to such properties can affect performance on less + powerful systems. If animating properties other than stroke and fill colors + is a must, it is recommended to target systems providing + \c{GL_NV_path_rendering} where the cost of path property changes is much + smaller. + + \note However, the data-driven, declarative nature of the Shape API often + means better cacheability for the underlying CPU and GPU resources. A + property change in one ShapePath will only lead to reprocessing the affected + ShapePath, leaving other parts of the Shape unchanged. Therefore, a heavily + changing (for example, animating) property can often result in a lower + overall system load than with imperative painting approaches (for example, + QPainter). + + The following list summarizes the available Shape rendering approaches: + + \list + + \li When running with the default, OpenGL backend of Qt Quick, both the + generic, triangulation-based and the NVIDIA-specific + \c{GL_NV_path_rendering} methods are available. The choice is made at + runtime, depending on the graphics driver's capabilities. When this is not + desired, applications can force using the generic method by setting the + Shape.enableVendorExtensions property to \c false. + + \li The \c software backend is fully supported. The path is rendered via + QPainter::strokePath() and QPainter::fillPath() in this case. + + \li The Direct 3D 12 backend is not currently supported. + + \li The OpenVG backend is not currently supported. + + \endlist + + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg +*/ + +QQuickShapePrivate::QQuickShapePrivate() + : componentComplete(true), + spChanged(false), + rendererType(QQuickShape::UnknownRenderer), + async(false), + status(QQuickShape::Null), + renderer(nullptr), + enableVendorExts(true) +{ +} + +QQuickShapePrivate::~QQuickShapePrivate() +{ + delete renderer; +} + +void QQuickShapePrivate::_q_shapePathChanged() +{ + Q_Q(QQuickShape); + spChanged = true; + q->polish(); +} + +void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus) +{ + Q_Q(QQuickShape); + if (status != newStatus) { + status = newStatus; + emit q->statusChanged(); + } +} + +QQuickShape::QQuickShape(QQuickItem *parent) + : QQuickItem(*(new QQuickShapePrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickShape::~QQuickShape() +{ +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType + + This property determines which path rendering backend is active. + + \list + + \li Shape.UnknownRenderer - The renderer is unknown. + + \li Shape.GeometryRenderer - The generic, driver independent solution + for OpenGL. Uses the same CPU-based triangulation approach as QPainter's + OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the + default, OpenGL Qt Quick scenegraph backend is in use. + + \li Shape.NvprRenderer - Path items are rendered by performing OpenGL + calls using the \c{GL_NV_path_rendering} extension. This is the default on + NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in + use. + + \li Shape.SoftwareRenderer - Pure QPainter drawing using the raster + paint engine. This is the default, and only, option when the Qt Quick + scenegraph is running with the \c software backend. + + \endlist +*/ + +QQuickShape::RendererType QQuickShape::rendererType() const +{ + Q_D(const QQuickShape); + return d->rendererType; +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::asynchronous + + When Shape.rendererType is Shape.GeometryRenderer, the input path is + triangulated on the CPU during the polishing phase of the Shape. This is + potentially expensive. To offload this work to separate worker threads, set + this property to \c true. + + When enabled, making a Shape visible will not wait for the content to + become available. Instead, the gui/main thread is not blocked and the + results of the path rendering are shown only when all the asynchronous work + has been finished. + + The default value is \c false. + */ + +bool QQuickShape::asynchronous() const +{ + Q_D(const QQuickShape); + return d->async; +} + +void QQuickShape::setAsynchronous(bool async) +{ + Q_D(QQuickShape); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + if (d->componentComplete) + d->_q_shapePathChanged(); + } +} + +/*! + \qmlproperty bool QtQuick.Shapes::Shape::enableVendorExtensions + + This property controls the usage of non-standard OpenGL extensions like + GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform + behavior regardless of the graphics card and drivers, set this property to + \c false. + + The default value is \c true. + */ + +bool QQuickShape::enableVendorExtensions() const +{ + Q_D(const QQuickShape); + return d->enableVendorExts; +} + +void QQuickShape::setEnableVendorExtensions(bool enable) +{ + Q_D(QQuickShape); + if (d->enableVendorExts != enable) { + d->enableVendorExts = enable; + emit enableVendorExtensionsChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::status + + This property determines the status of the Shape and is relevant when + Shape.asynchronous is set to \c true. + + \list + + \li Shape.Null - Not yet initialized. + + \li Shape.Ready - The Shape has finished processing. + + \li Shape.Processing - The path is being processed. + + \endlist + */ + +QQuickShape::Status QQuickShape::status() const +{ + Q_D(const QQuickShape); + return d->status; +} + +static QQuickShapePath *vpe_at(QQmlListProperty *property, int index) +{ + QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); + return d->qmlData.sp.at(index); +} + +static void vpe_append(QQmlListProperty *property, QQuickShapePath *obj) +{ + QQuickShape *item = static_cast(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + d->qmlData.sp.append(obj); + + if (d->componentComplete) { + QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + d->_q_shapePathChanged(); + } +} + +static int vpe_count(QQmlListProperty *property) +{ + QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); + return d->qmlData.sp.count(); +} + +static void vpe_clear(QQmlListProperty *property) +{ + QQuickShape *item = static_cast(property->object); + QQuickShapePrivate *d = QQuickShapePrivate::get(item); + + for (QQuickShapePath *p : d->qmlData.sp) + QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + + d->qmlData.sp.clear(); + + if (d->componentComplete) + d->_q_shapePathChanged(); +} + +/*! + \qmlproperty list QtQuick.Shapes::Shape::elements + + This property holds the ShapePath objects that define the contents of the + Shape. + + \default + */ + +QQmlListProperty QQuickShape::elements() +{ + return QQmlListProperty(this, + nullptr, + vpe_append, + vpe_count, + vpe_at, + vpe_clear); +} + +void QQuickShape::classBegin() +{ + Q_D(QQuickShape); + d->componentComplete = false; +} + +void QQuickShape::componentComplete() +{ + Q_D(QQuickShape); + d->componentComplete = true; + + for (QQuickShapePath *p : d->qmlData.sp) + connect(p, SIGNAL(changed()), this, SLOT(_q_shapePathChanged())); + + d->_q_shapePathChanged(); +} + +void QQuickShape::updatePolish() +{ + Q_D(QQuickShape); + + if (!d->spChanged) + return; + + d->spChanged = false; + + if (!d->renderer) { + d->createRenderer(); + if (!d->renderer) + return; + emit rendererChanged(); + } + + // endSync() is where expensive calculations may happen (or get kicked off + // on worker threads), depending on the backend. Therefore do this only + // when the item is visible. + if (isVisible()) + d->sync(); + + update(); +} + +void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data) +{ + Q_D(QQuickShape); + + // sync may have been deferred; do it now if the item became visible + if (change == ItemVisibleHasChanged && data.boolValue) + d->_q_shapePathChanged(); + + QQuickItem::itemChange(change, data); +} + +QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + // Called on the render thread, with the gui thread blocked. We can now + // safely access gui thread data. + + Q_D(QQuickShape); + if (d->renderer) { + if (!node) + node = d->createNode(); + d->renderer->updateNode(); + } + return node; +} + +// the renderer object lives on the gui thread +void QQuickShapePrivate::createRenderer() +{ + Q_Q(QQuickShape); + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + rendererType = QQuickShape::NvprRenderer; + renderer = new QQuickShapeNvprRenderer; + } else { + rendererType = QQuickShape::GeometryRenderer; + renderer = new QQuickShapeGenericRenderer(q); + } + break; +#endif + case QSGRendererInterface::Software: + rendererType = QQuickShape::SoftwareRenderer; + renderer = new QQuickShapeSoftwareRenderer; + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } +} + +// the node lives on the render thread +QSGNode *QQuickShapePrivate::createNode() +{ + Q_Q(QQuickShape); + QSGNode *node = nullptr; + if (!q->window()) + return node; + QSGRendererInterface *ri = q->window()->rendererInterface(); + if (!ri) + return node; + + switch (ri->graphicsApi()) { +#ifndef QT_NO_OPENGL + case QSGRendererInterface::OpenGL: + if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { + node = new QQuickShapeNvprRenderNode; + static_cast(renderer)->setNode( + static_cast(node)); + } else { + node = new QQuickShapeGenericNode; + static_cast(renderer)->setRootNode( + static_cast(node)); + } + break; +#endif + case QSGRendererInterface::Software: + node = new QQuickShapeSoftwareRenderNode(q); + static_cast(renderer)->setNode( + static_cast(node)); + break; + default: + qWarning("No path backend for this graphics API yet"); + break; + } + + return node; +} + +static void q_asyncShapeReady(void *data) +{ + QQuickShapePrivate *self = static_cast(data); + self->setStatus(QQuickShape::Ready); +} + +void QQuickShapePrivate::sync() +{ + const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync); + if (useAsync) { + setStatus(QQuickShape::Processing); + renderer->setAsyncCallback(q_asyncShapeReady, this); + } + + if (!jsData.isValid()) { + // Standard route: The path and stroke/fill parameters are provided via + // QML elements. + const int count = qmlData.sp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickShapePath *p = qmlData.sp[i]; + int &dirty(QQuickShapePathPrivate::get(p)->dirty); + + if (dirty & QQuickShapePathPrivate::DirtyPath) + renderer->setPath(i, p->path()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickShapePathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickShapePathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickShapePathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); + } + if (dirty & QQuickShapePathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickShapePathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); + + dirty = 0; + } + + renderer->endSync(useAsync); + } else { + + // ### there is no public API to reach this code path atm + Q_UNREACHABLE(); + + // Path and stroke/fill params provided from JavaScript. This avoids + // QObjects at the expense of not supporting changes afterwards. + const int count = jsData.paths.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + renderer->setJSPath(i, jsData.paths[i]); + const QQuickShapeStrokeFillParams sfp(jsData.sfp[i]); + renderer->setStrokeColor(i, sfp.strokeColor); + renderer->setStrokeWidth(i, sfp.strokeWidth); + renderer->setFillColor(i, sfp.fillColor); + renderer->setFillRule(i, sfp.fillRule); + renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); + renderer->setCapStyle(i, sfp.capStyle); + renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); + renderer->setFillGradient(i, sfp.fillGradient); + } + + renderer->endSync(useAsync); + } + + if (!useAsync) + setStatus(QQuickShape::Ready); +} + +// ***** gradient support ***** + +/*! + \qmltype ShapeGradientStop + \instantiates QQuickShapeGradientStop + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Defines a color at a position in a gradient + \since 5.10 + */ + +QQuickShapeGradientStop::QQuickShapeGradientStop(QObject *parent) + : QObject(parent), + m_position(0), + m_color(Qt::black) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeGradientStop::position + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is 0. + */ + +qreal QQuickShapeGradientStop::position() const +{ + return m_position; +} + +void QQuickShapeGradientStop::setPosition(qreal position) +{ + if (m_position != position) { + m_position = position; + if (QQuickShapeGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeGradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default value is \c black. + */ + +QColor QQuickShapeGradientStop::color() const +{ + return m_color; +} + +void QQuickShapeGradientStop::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + if (QQuickShapeGradient *grad = qobject_cast(parent())) + emit grad->updated(); + } +} + +/*! + \qmltype ShapeGradient + \instantiates QQuickShapeGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits Object + \brief Base type of Shape fill gradients + \since 5.10 + + This is an abstract base class for gradients like ShapeLinearGradient and + cannot be created directly. + */ + +QQuickShapeGradient::QQuickShapeGradient(QObject *parent) + : QObject(parent), + m_spread(PadSpread) +{ +} + +int QQuickShapeGradient::countStops(QQmlListProperty *list) +{ + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.count(); +} + +QObject *QQuickShapeGradient::atStop(QQmlListProperty *list, int index) +{ + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + return grad->m_stops.at(index); +} + +void QQuickShapeGradient::appendStop(QQmlListProperty *list, QObject *stop) +{ + QQuickShapeGradientStop *sstop = qobject_cast(stop); + if (!sstop) { + qWarning("Gradient stop list only supports QQuickShapeGradientStop elements"); + return; + } + QQuickShapeGradient *grad = qobject_cast(list->object); + Q_ASSERT(grad); + sstop->setParent(grad); + grad->m_stops.append(sstop); +} + +/*! + \qmlproperty list QtQuick.Shapes::ShapeGradient::stops + \default + + The list of ShapeGradientStop objects defining the colors at given positions + in the gradient. + */ + +QQmlListProperty QQuickShapeGradient::stops() +{ + return QQmlListProperty(this, nullptr, + &QQuickShapeGradient::appendStop, + &QQuickShapeGradient::countStops, + &QQuickShapeGradient::atStop, + nullptr); +} + +QGradientStops QQuickShapeGradient::sortedGradientStops() const +{ + QGradientStops result; + for (int i = 0; i < m_stops.count(); ++i) { + QQuickShapeGradientStop *s = static_cast(m_stops[i]); + int j = 0; + while (j < result.count() && result[j].first < s->position()) + ++j; + result.insert(j, QGradientStop(s->position(), s->color())); + } + return result; +} + +/*! + \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spred + + Specifies how the area outside the gradient area should be filled. The + default value is ShapeGradient.PadSpread. + + \list + \li ShapeGradient.PadSpread - The area is filled with the closest stop color. + \li ShapeGradient.RepeatSpread - The gradient is repeated outside the gradient area. + \li ShapeGradient.ReflectSpread - The gradient is reflected outside the gradient area. + \endlist + */ + +QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const +{ + return m_spread; +} + +void QQuickShapeGradient::setSpread(SpreadMode mode) +{ + if (m_spread != mode) { + m_spread = mode; + emit spreadChanged(); + emit updated(); + } +} + +/*! + \qmltype ShapeLinearGradient + \instantiates QQuickShapeLinearGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Linear gradient + \since 5.10 + + Linear gradients interpolate colors between start and end points. Outside + these points the gradient is either padded, reflected or repeated depending + on the spread type. + + \sa QLinearGradient + */ + +QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x1 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y1 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x2 + \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y2 + + These properties define the start and end points between which color + interpolation occurs. By default both the stard and end points are set to + (0, 0). + */ + +qreal QQuickShapeLinearGradient::x1() const +{ + return m_start.x(); +} + +void QQuickShapeLinearGradient::setX1(qreal v) +{ + if (m_start.x() != v) { + m_start.setX(v); + emit x1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y1() const +{ + return m_start.y(); +} + +void QQuickShapeLinearGradient::setY1(qreal v) +{ + if (m_start.y() != v) { + m_start.setY(v); + emit y1Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::x2() const +{ + return m_end.x(); +} + +void QQuickShapeLinearGradient::setX2(qreal v) +{ + if (m_end.x() != v) { + m_end.setX(v); + emit x2Changed(); + emit updated(); + } +} + +qreal QQuickShapeLinearGradient::y2() const +{ + return m_end.y(); +} + +void QQuickShapeLinearGradient::setY2(qreal v) +{ + if (m_end.y() != v) { + m_end.setY(v); + emit y2Changed(); + emit updated(); + } +} + +#ifndef QT_NO_OPENGL + +// contexts sharing with each other get the same cache instance +class QQuickShapeGradientCacheWrapper +{ +public: + QQuickShapeGradientCache *get(QOpenGLContext *context) + { + return m_resource.value(context); + } + +private: + QOpenGLMultiGroupSharedResource m_resource; +}; + +QQuickShapeGradientCache *QQuickShapeGradientCache::currentCache() +{ + static QQuickShapeGradientCacheWrapper qt_path_gradient_caches; + return qt_path_gradient_caches.get(QOpenGLContext::currentContext()); +} + +// let QOpenGLContext manage the lifetime of the cached textures +QQuickShapeGradientCache::~QQuickShapeGradientCache() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::invalidateResource() +{ + m_cache.clear(); +} + +void QQuickShapeGradientCache::freeResource(QOpenGLContext *) +{ + qDeleteAll(m_cache); + m_cache.clear(); +} + +static void generateGradientColorTable(const QQuickShapeGradientCache::GradientDesc &gradient, + uint *colorTable, int size, float opacity) +{ + int pos = 0; + const QGradientStops &s = gradient.stops; + const bool colorInterpolation = true; + + uint alpha = qRound(opacity * 256); + uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha); + qreal incr = 1.0 / qreal(size); + qreal fpos = 1.5 * incr; + colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color)); + + while (fpos <= s.first().first) { + colorTable[pos] = colorTable[pos - 1]; + pos++; + fpos += incr; + } + + if (colorInterpolation) + current_color = qPremultiply(current_color); + + const int sLast = s.size() - 1; + for (int i = 0; i < sLast; ++i) { + qreal delta = 1/(s[i+1].first - s[i].first); + uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha); + if (colorInterpolation) + next_color = qPremultiply(next_color); + + while (fpos < s[i+1].first && pos < size) { + int dist = int(256 * ((fpos - s[i].first) * delta)); + int idist = 256 - dist; + if (colorInterpolation) + colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)); + else + colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist))); + ++pos; + fpos += incr; + } + current_color = next_color; + } + + Q_ASSERT(s.size() > 0); + + uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); + for ( ; pos < size; ++pos) + colorTable[pos] = last_color; + + colorTable[size-1] = last_color; +} + +QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) +{ + QSGPlainTexture *tx = m_cache[grad]; + if (!tx) { + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + GLuint id; + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + static const uint W = 1024; // texture size is 1024x1 + uint buf[W]; + generateGradientColorTable(grad, buf, W, 1.0f); + f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); + tx = new QSGPlainTexture; + tx->setTextureId(id); + switch (grad.spread) { + case QQuickShapeGradient::PadSpread: + tx->setHorizontalWrapMode(QSGTexture::ClampToEdge); + tx->setVerticalWrapMode(QSGTexture::ClampToEdge); + break; + case QQuickShapeGradient::RepeatSpread: + tx->setHorizontalWrapMode(QSGTexture::Repeat); + tx->setVerticalWrapMode(QSGTexture::Repeat); + break; + case QQuickShapeGradient::ReflectSpread: + tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat); + tx->setVerticalWrapMode(QSGTexture::MirroredRepeat); + break; + default: + qWarning("Unknown gradient spread mode %d", grad.spread); + break; + } + m_cache[grad] = tx; + } + return tx; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#include "moc_qquickshape_p.cpp" diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h new file mode 100644 index 0000000000..5b3580dd1b --- /dev/null +++ b/src/imports/shapes/qquickshape_p.h @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** 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 QQUICKSHAPE_P_H +#define QQUICKSHAPE_P_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 "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapePathPrivate; +class QQuickShapePrivate; + +class QQuickShapeGradientStop : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickShapeGradientStop(QObject *parent = nullptr); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + qreal m_position; + QColor m_color; +}; + +class QQuickShapeGradient : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty stops READ stops) + Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + enum SpreadMode { + PadSpread, + RepeatSpread, + ReflectSpread + }; + Q_ENUM(SpreadMode) + + QQuickShapeGradient(QObject *parent = nullptr); + + QQmlListProperty stops(); + + QGradientStops sortedGradientStops() const; + + SpreadMode spread() const; + void setSpread(SpreadMode mode); + +signals: + void updated(); + void spreadChanged(); + +private: + static int countStops(QQmlListProperty *list); + static QObject *atStop(QQmlListProperty *list, int index); + static void appendStop(QQmlListProperty *list, QObject *stop); + + QVector m_stops; + SpreadMode m_spread; +}; + +class QQuickShapeLinearGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed) + Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed) + Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed) + Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeLinearGradient(QObject *parent = nullptr); + + qreal x1() const; + void setX1(qreal v); + qreal y1() const; + void setY1(qreal v); + qreal x2() const; + void setX2(qreal v); + qreal y2() const; + void setY2(qreal v); + +signals: + void x1Changed(); + void y1Changed(); + void x2Changed(); + void y2Changed(); + +private: + QPointF m_start; + QPointF m_end; +}; + +class QQuickShapePath : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) + Q_CLASSINFO("DefaultProperty", "path") + + Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) + Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged) + Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged) + Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged) + Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged) + Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged) + Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged) + Q_PROPERTY(QVector dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged) + Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient) + +public: + enum FillRule { + OddEvenFill = Qt::OddEvenFill, + WindingFill = Qt::WindingFill + }; + Q_ENUM(FillRule) + + enum JoinStyle { + MiterJoin = Qt::MiterJoin, + BevelJoin = Qt::BevelJoin, + RoundJoin = Qt::RoundJoin + }; + Q_ENUM(JoinStyle) + + enum CapStyle { + FlatCap = Qt::FlatCap, + SquareCap = Qt::SquareCap, + RoundCap = Qt::RoundCap + }; + Q_ENUM(CapStyle) + + enum StrokeStyle { + SolidLine = Qt::SolidLine, + DashLine = Qt::DashLine + }; + Q_ENUM(StrokeStyle) + + QQuickShapePath(QObject *parent = nullptr); + ~QQuickShapePath(); + + QQuickPath *path() const; + void setPath(QQuickPath *path); + + QColor strokeColor() const; + void setStrokeColor(const QColor &color); + + qreal strokeWidth() const; + void setStrokeWidth(qreal w); + + QColor fillColor() const; + void setFillColor(const QColor &color); + + FillRule fillRule() const; + void setFillRule(FillRule fillRule); + + JoinStyle joinStyle() const; + void setJoinStyle(JoinStyle style); + + int miterLimit() const; + void setMiterLimit(int limit); + + CapStyle capStyle() const; + void setCapStyle(CapStyle style); + + StrokeStyle strokeStyle() const; + void setStrokeStyle(StrokeStyle style); + + qreal dashOffset() const; + void setDashOffset(qreal offset); + + QVector dashPattern() const; + void setDashPattern(const QVector &array); + + QQuickShapeGradient *fillGradient() const; + void setFillGradient(QQuickShapeGradient *gradient); + void resetFillGradient(); + +Q_SIGNALS: + void changed(); + void pathChanged(); + void strokeColorChanged(); + void strokeWidthChanged(); + void fillColorChanged(); + void fillRuleChanged(); + void joinStyleChanged(); + void miterLimitChanged(); + void capStyleChanged(); + void strokeStyleChanged(); + void dashOffsetChanged(); + void dashPatternChanged(); + void fillGradientChanged(); + +private: + Q_DISABLE_COPY(QQuickShapePath) + Q_DECLARE_PRIVATE(QQuickShapePath) + Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) +}; + +class QQuickShape : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QQmlListProperty elements READ elements) + Q_CLASSINFO("DefaultProperty", "elements") + +public: + enum RendererType { + UnknownRenderer, + GeometryRenderer, + NvprRenderer, + SoftwareRenderer + }; + Q_ENUM(RendererType) + + enum Status { + Null, + Ready, + Processing + }; + Q_ENUM(Status) + + QQuickShape(QQuickItem *parent = nullptr); + ~QQuickShape(); + + RendererType rendererType() const; + + bool asynchronous() const; + void setAsynchronous(bool async); + + bool enableVendorExtensions() const; + void setEnableVendorExtensions(bool enable); + + Status status() const; + + QQmlListProperty elements(); + +protected: + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; + void updatePolish() override; + void itemChange(ItemChange change, const ItemChangeData &data) override; + void componentComplete() override; + void classBegin() override; + +Q_SIGNALS: + void rendererChanged(); + void asynchronousChanged(); + void enableVendorExtensionsChanged(); + void statusChanged(); + +private: + Q_DISABLE_COPY(QQuickShape) + Q_DECLARE_PRIVATE(QQuickShape) + Q_PRIVATE_SLOT(d_func(), void _q_shapePathChanged()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickShape) + +#endif // QQUICKSHAPE_P_H diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h new file mode 100644 index 0000000000..7a503e36a9 --- /dev/null +++ b/src/imports/shapes/qquickshape_p_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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 QQUICKSHAPE_P_P_H +#define QQUICKSHAPE_P_P_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 "qquickshape_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPlainTexture; + +struct QQuickShapePathCommands +{ + enum Command { + MoveTo, + LineTo, + QuadTo, + CubicTo, + ArcTo + }; + + QVector cmd; + QVector coords; + + QPainterPath toPainterPath() const; +}; + +struct QQuickShapeStrokeFillParams +{ + QQuickShapeStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickShapePath::FillRule fillRule; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickShapeGradient *fillGradient; +}; + +class QQuickAbstractPathRenderer +{ +public: + enum Flag { + SupportsAsync = 0x01 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + virtual ~QQuickAbstractPathRenderer() { } + + // Gui thread + virtual void beginSync(int totalCount) = 0; + virtual void endSync(bool async) = 0; + virtual void setAsyncCallback(void (*)(void *), void *) { } + virtual Flags flags() const { return 0; } + // - QML API + virtual void setPath(int index, const QQuickPath *path) = 0; + // - JS API + virtual void setJSPath(int index, const QQuickShapePathCommands &path) = 0; + // - stroke/fill parameters + virtual void setStrokeColor(int index, const QColor &color) = 0; + virtual void setStrokeWidth(int index, qreal w) = 0; + virtual void setFillColor(int index, const QColor &color) = 0; + virtual void setFillRule(int index, QQuickShapePath::FillRule fillRule) = 0; + virtual void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) = 0; + virtual void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) = 0; + virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) = 0; + virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0; + + // Render thread, with gui blocked + virtual void updateNode() = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) + +class QQuickShapePathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickShapePath) + +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStrokeColor = 0x02, + DirtyStrokeWidth = 0x04, + DirtyFillColor = 0x08, + DirtyFillRule = 0x10, + DirtyStyle = 0x20, + DirtyDash = 0x40, + DirtyFillGradient = 0x80, + + DirtyAll = 0xFF + }; + + QQuickShapePathPrivate(); + + void _q_pathChanged(); + void _q_fillGradientChanged(); + + static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); } + + QQuickPath *path; + int dirty; + QQuickShapeStrokeFillParams sfp; +}; + +class QQuickShapePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickShape) + +public: + QQuickShapePrivate(); + ~QQuickShapePrivate(); + + void createRenderer(); + QSGNode *createNode(); + void sync(); + + void _q_shapePathChanged(); + void setStatus(QQuickShape::Status newStatus); + + static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); } + + bool componentComplete; + bool spChanged; + QQuickShape::RendererType rendererType; + bool async; + QQuickShape::Status status; + QQuickAbstractPathRenderer *renderer; + + struct { + QVector sp; + } qmlData; + + struct { + bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } + QVector paths; + QVector sfp; + } jsData; + + bool enableVendorExts; +}; + +class QQuickShapePathObject : public QObject +{ + Q_OBJECT + +public: + QQuickShapePathObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickShapePath path; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +class QQuickShapeStrokeFillParamsObject : public QObject +{ + Q_OBJECT + +public: + QQuickShapeStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } + + void setV4Engine(QV4::ExecutionEngine *engine); + QV4::ReturnedValue v4value() const { return m_v4value.value(); } + + QQuickShapeStrokeFillParams sfp; + QV4::PersistentValue v4fillGradient; + + void clear(); + +private: + QV4::PersistentValue m_v4value; +}; + +#ifndef QT_NO_OPENGL + +class QQuickShapeGradientCache : public QOpenGLSharedResource +{ +public: + struct GradientDesc { + QGradientStops stops; + QPointF start; + QPointF end; + QQuickShapeGradient::SpreadMode spread; + bool operator==(const GradientDesc &other) const + { + return start == other.start && end == other.end && spread == other.spread + && stops == other.stops; + } + }; + + QQuickShapeGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { } + ~QQuickShapeGradientCache(); + + void invalidateResource() override; + void freeResource(QOpenGLContext *) override; + + QSGTexture *get(const GradientDesc &grad); + + static QQuickShapeGradientCache *currentCache(); + +private: + QHash m_cache; +}; + +inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0) +{ + uint h = seed; + h += v.start.x() + v.end.y() + v.spread; + for (int i = 0; i < 3 && i < v.stops.count(); ++i) + h += v.stops[i].second.rgba(); + return h; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp new file mode 100644 index 0000000000..ff226959eb --- /dev/null +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -0,0 +1,775 @@ +/**************************************************************************** +** +** 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 "qquickshapegenericrenderer_p.h" +#include +#include +#include + +#ifndef QT_NO_OPENGL +#include +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +static const qreal TRI_SCALE = 1; + +struct ColoredVertex // must match QSGGeometry::ColoredPoint2D +{ + float x, y; + QQuickShapeGenericRenderer::Color4ub color; + void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor) + { + x = nx; y = ny; color = ncolor; + } +}; + +static inline QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c) +{ + QQuickShapeGenericRenderer::Color4ub color = { + uchar(qRound(c.redF() * c.alphaF() * 255)), + uchar(qRound(c.greenF() * c.alphaF() * 255)), + uchar(qRound(c.blueF() * c.alphaF() * 255)), + uchar(qRound(c.alphaF() * 255)) + }; + return color; +} + +QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window) + : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)), + m_window(window), + m_material(nullptr) +{ + setGeometry(m_geometry); + activateMaterial(MatSolidColor); +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("stroke-fill")); +#endif +} + +QQuickShapeGenericStrokeFillNode::~QQuickShapeGenericStrokeFillNode() +{ + delete m_geometry; +} + +void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) +{ + switch (m) { + case MatSolidColor: + // Use vertexcolor material. Items with different colors remain batchable + // this way, at the expense of having to provide per-vertex color values. + if (!m_solidColorMaterial) + m_solidColorMaterial.reset(QQuickShapeGenericMaterialFactory::createVertexColor(m_window)); + m_material = m_solidColorMaterial.data(); + break; + case MatLinearGradient: + if (!m_linearGradientMaterial) + m_linearGradientMaterial.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(m_window, this)); + m_material = m_linearGradientMaterial.data(); + break; + default: + qWarning("Unknown material %d", m); + return; + } + + if (material() != m_material) + setMaterial(m_material); +} + +static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) +{ + static bool elementIndexUint = true; +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) { + static bool elementIndexUintChecked = false; + if (!elementIndexUintChecked) { + elementIndexUintChecked = true; + QOpenGLContext *context = QOpenGLContext::currentContext(); + QScopedPointer dummyContext; + QScopedPointer dummySurface; + bool ok = true; + if (!context) { + dummyContext.reset(new QOpenGLContext); + dummyContext->create(); + context = dummyContext.data(); + dummySurface.reset(new QOffscreenSurface); + dummySurface->setFormat(context->format()); + dummySurface->create(); + ok = context->makeCurrent(dummySurface.data()); + } + if (ok) { + elementIndexUint = static_cast(context->functions())->hasOpenGLExtension( + QOpenGLExtensions::ElementIndexUint); + } + } + } +#else + Q_UNUSED(api); +#endif + return elementIndexUint; +} + +QQuickShapeGenericRenderer::~QQuickShapeGenericRenderer() +{ + for (ShapePathData &d : m_sp) { + if (d.pendingFill) + d.pendingFill->orphaned = true; + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + } +} + +// sync, and so triangulation too, happens on the gui thread +// - except when async is set, in which case triangulation is moved to worker threads + +void QQuickShapeGenericRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } + for (ShapePathData &d : m_sp) + d.syncDirty = 0; +} + +void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathData &d(m_sp[index]); + d.path = path.toPainterPath(); + d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.strokeColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathData &d(m_sp[index]); + d.fillColor = colorToColor4ub(color); + d.syncDirty |= DirtyColor; +} + +void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.syncDirty |= DirtyFillGeom; +} + +void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathData &d(m_sp[index]); + d.pen.setStyle(Qt::PenStyle(strokeStyle)); + if (strokeStyle == QQuickShapePath::DashLine) { + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + } + d.syncDirty |= DirtyStrokeGeom; +} + +void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathData &d(m_sp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.syncDirty |= DirtyFillGradient; +} + +void QQuickShapeFillRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint); + emit done(this); +} + +void QQuickShapeStrokeRunnable::run() +{ + QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize); + emit done(this); +} + +void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data) +{ + m_asyncCallback = callback; + m_asyncCallbackData = data; +} + +static QThreadPool *pathWorkThreadPool = nullptr; + +static void deletePathWorkThreadPool() +{ + delete pathWorkThreadPool; + pathWorkThreadPool = nullptr; +} + +void QQuickShapeGenericRenderer::endSync(bool async) +{ + bool didKickOffAsync = false; + + for (int i = 0; i < m_sp.count(); ++i) { + ShapePathData &d(m_sp[i]); + if (!d.syncDirty) + continue; + + m_accDirty |= d.syncDirty; + + // Use a shadow dirty flag in order to avoid losing state in case there are + // multiple syncs with different dirty flags before we get to updateNode() + // on the render thread (with the gui thread blocked). For our purposes + // here syncDirty is still required since geometry regeneration must only + // happen when there was an actual change in this particular sync round. + d.effectiveDirty |= d.syncDirty; + + if (d.path.isEmpty()) { + d.fillVertices.clear(); + d.fillIndices.clear(); + d.strokeVertices.clear(); + continue; + } + + if (async && !pathWorkThreadPool) { + qAddPostRoutine(deletePathWorkThreadPool); + pathWorkThreadPool = new QThreadPool; + const int idealCount = QThread::idealThreadCount(); + pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4); + } + + if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) { + d.path.setFillRule(d.fillRule); + if (m_api == QSGRendererInterface::Unknown) + m_api = m_item->window()->rendererInterface()->graphicsApi(); + if (async) { + QQuickShapeFillRunnable *r = new QQuickShapeFillRunnable; + r->setAutoDelete(false); + if (d.pendingFill) + d.pendingFill->orphaned = true; + d.pendingFill = r; + r->path = d.path; + r->fillColor = d.fillColor; + r->supportsElementIndexUint = q_supportsElementIndexUint(m_api); + // Unlikely in practice but in theory m_sp could be + // resized. Therefore, capture 'i' instead of 'd'. + QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) { + // Bail out when orphaned (meaning either another run was + // started after this one, or the renderer got destroyed). + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.fillVertices = r->fillVertices; + d.fillIndices = r->fillIndices; + d.indexType = r->indexType; + d.pendingFill = nullptr; + d.effectiveDirty |= DirtyFillGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api)); + } + } + + if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) { + if (async) { + QQuickShapeStrokeRunnable *r = new QQuickShapeStrokeRunnable; + r->setAutoDelete(false); + if (d.pendingStroke) + d.pendingStroke->orphaned = true; + d.pendingStroke = r; + r->path = d.path; + r->pen = d.pen; + r->strokeColor = d.strokeColor; + r->clipSize = QSize(m_item->width(), m_item->height()); + QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) { + if (!r->orphaned && i < m_sp.count()) { + ShapePathData &d(m_sp[i]); + d.strokeVertices = r->strokeVertices; + d.pendingStroke = nullptr; + d.effectiveDirty |= DirtyStrokeGeom; + maybeUpdateAsyncItem(); + } + r->deleteLater(); + }); + didKickOffAsync = true; + pathWorkThreadPool->start(r); + } else { + triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices, + QSize(m_item->width(), m_item->height())); + } + } + } + + if (!didKickOffAsync && async && m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +void QQuickShapeGenericRenderer::maybeUpdateAsyncItem() +{ + for (const ShapePathData &d : qAsConst(m_sp)) { + if (d.pendingFill || d.pendingStroke) + return; + } + m_accDirty |= DirtyFillGeom | DirtyStrokeGeom; + m_item->update(); + if (m_asyncCallback) + m_asyncCallback(m_asyncCallbackData); +} + +// the stroke/fill triangulation functions may be invoked either on the gui +// thread or some worker thread and must thus be self-contained. +void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + + QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint); + const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2 + fillVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(fillVertices->data()); + const qreal *vsrc = ts.vertices.constData(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor); + + size_t indexByteSize; + if (ts.indices.type() == QVertexIndexVector::UnsignedShort) { + *indexType = QSGGeometry::UnsignedShortType; + // fillIndices is still QVector. Just resize to N/2 and pack + // the N quint16s into it. + fillIndices->resize(ts.indices.size() / 2); + indexByteSize = ts.indices.size() * sizeof(quint16); + } else { + *indexType = QSGGeometry::UnsignedIntType; + fillIndices->resize(ts.indices.size()); + indexByteSize = ts.indices.size() * sizeof(quint32); + } + memcpy(fillIndices->data(), ts.indices.data(), indexByteSize); +} + +void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize) +{ + const QVectorPath &vp = qtVectorPathForPath(path); + const QRectF clip(QPointF(0, 0), clipSize); + const qreal inverseScale = 1.0 / TRI_SCALE; + + QTriangulatingStroker stroker; + stroker.setInvScale(inverseScale); + + if (pen.style() == Qt::SolidLine) { + stroker.process(vp, pen, clip, 0); + } else { + QDashedStrokeProcessor dashStroker; + dashStroker.setInvScale(inverseScale); + dashStroker.process(vp, pen, clip, 0); + QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(), + dashStroker.elementTypes(), 0); + stroker.process(dashStroke, pen, clip, 0); + } + + if (!stroker.vertexCount()) { + strokeVertices->clear(); + return; + } + + const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2 + strokeVertices->resize(vertexCount); + ColoredVertex *vdst = reinterpret_cast(strokeVertices->data()); + const float *vsrc = stroker.vertices(); + for (int i = 0; i < vertexCount; ++i) + vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor); +} + +void QQuickShapeGenericRenderer::setRootNode(QQuickShapeGenericNode *node) +{ + if (m_rootNode != node) { + m_rootNode = node; + m_accDirty |= DirtyList; + } +} + +// on the render thread with gui blocked +void QQuickShapeGenericRenderer::updateNode() +{ + if (!m_rootNode || !m_accDirty) + return; + +// [ m_rootNode ] +// / / / +// #0 [ fill ] [ stroke ] [ next ] +// / / | +// #1 [ fill ] [ stroke ] [ next ] +// / / | +// #2 [ fill ] [ stroke ] [ next ] +// ... +// ... + + QQuickShapeGenericNode **nodePtr = &m_rootNode; + QQuickShapeGenericNode *prevNode = nullptr; + + for (ShapePathData &d : m_sp) { + if (!*nodePtr) { + *nodePtr = new QQuickShapeGenericNode; + prevNode->m_next = *nodePtr; + prevNode->appendChildNode(*nodePtr); + } + + QQuickShapeGenericNode *node = *nodePtr; + + if (m_accDirty & DirtyList) + d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient; + + if (!d.effectiveDirty) { + prevNode = node; + nodePtr = &node->m_next; + continue; + } + + if (d.fillColor.a == 0) { + delete node->m_fillNode; + node->m_fillNode = nullptr; + } else if (!node->m_fillNode) { + node->m_fillNode = new QQuickShapeGenericStrokeFillNode(m_item->window()); + if (node->m_strokeNode) + node->removeChildNode(node->m_strokeNode); + node->appendChildNode(node->m_fillNode); + if (node->m_strokeNode) + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyFillGeom; + } + + if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) { + delete node->m_strokeNode; + node->m_strokeNode = nullptr; + } else if (!node->m_strokeNode) { + node->m_strokeNode = new QQuickShapeGenericStrokeFillNode(m_item->window()); + node->appendChildNode(node->m_strokeNode); + d.effectiveDirty |= DirtyStrokeGeom; + } + + updateFillNode(&d, node); + updateStrokeNode(&d, node); + + d.effectiveDirty = 0; + + prevNode = node; + nodePtr = &node->m_next; + } + + if (*nodePtr && prevNode) { + prevNode->removeChildNode(*nodePtr); + delete *nodePtr; + *nodePtr = nullptr; + } + + m_accDirty = 0; +} + +void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) +{ + if (d->fillGradientActive) { + if (d->effectiveDirty & DirtyFillGradient) + n->m_fillGradient = d->fillGradient; + } +} + +void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_fillNode) + return; + if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient))) + return; + + // Make a copy of the data that will be accessed by the material on + // the render thread. This must be done even when we bail out below. + QQuickShapeGenericStrokeFillNode *n = node->m_fillNode; + updateShadowDataInNode(d, n); + + QSGGeometry *g = n->m_geometry; + if (d->fillVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + if (d->fillGradientActive) { + n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatLinearGradient); + if (d->effectiveDirty & DirtyFillGradient) { + // Gradients are implemented via a texture-based material. + n->markDirty(QSGNode::DirtyMaterial); + // stop here if only the gradient changed; no need to touch the geometry + if (!(d->effectiveDirty & DirtyFillGeom)) + return; + } + } else { + n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatSolidColor); + // fast path for updating only color values when no change in vertex positions + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor); + n->markDirty(QSGNode::DirtyGeometry); + return; + } + } + + const int indexCount = d->indexType == QSGGeometry::UnsignedShortType + ? d->fillIndices.count() * 2 : d->fillIndices.count(); + if (g->indexType() != d->indexType) { + g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), + d->fillVertices.count(), indexCount, d->indexType); + n->setGeometry(g); + delete n->m_geometry; + n->m_geometry = g; + } else { + g->allocate(d->fillVertices.count(), indexCount); + } + g->setDrawingMode(QSGGeometry::DrawTriangles); + memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex()); + memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex()); + + n->markDirty(QSGNode::DirtyGeometry); +} + +void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node) +{ + if (!node->m_strokeNode) + return; + if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor))) + return; + + QQuickShapeGenericStrokeFillNode *n = node->m_strokeNode; + QSGGeometry *g = n->m_geometry; + if (d->strokeVertices.isEmpty()) { + if (g->vertexCount() || g->indexCount()) { + g->allocate(0, 0); + n->markDirty(QSGNode::DirtyGeometry); + } + return; + } + + n->markDirty(QSGNode::DirtyGeometry); + + // Async loading runs update once, bails out above, then updates again once + // ready. Set the material dirty then. This is in-line with fill where the + // first activateMaterial() achieves the same. + if (!g->vertexCount()) + n->markDirty(QSGNode::DirtyMaterial); + + if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) { + ColoredVertex *vdst = reinterpret_cast(g->vertexData()); + for (int i = 0; i < g->vertexCount(); ++i) + vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor); + return; + } + + g->allocate(d->strokeVertices.count(), 0); + g->setDrawingMode(QSGGeometry::DrawTriangleStrip); + memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex()); +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QSGVertexColorMaterial; +#endif + + qWarning("Vertex-color material: Unsupported graphics API %d", api); + return nullptr; +} + +QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#ifndef QT_NO_OPENGL + if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + return new QQuickShapeLinearGradientMaterial(node); +#endif + + qWarning("Linear gradient material: Unsupported graphics API %d", api); + return nullptr; +} + +#ifndef QT_NO_OPENGL + +QSGMaterialType QQuickShapeLinearGradientShader::type; + +QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); +} + +void QQuickShapeLinearGradientShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("opacity"); + m_matrixLoc = program()->uniformLocation("matrix"); + m_gradStartLoc = program()->uniformLocation("gradStart"); + m_gradEndLoc = program()->uniformLocation("gradEnd"); +} + +void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeLinearGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(node->m_fillGradient); + tx->bind(); +} + +char const *const *QQuickShapeLinearGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeLinearGradientMaterial *m = static_cast(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickShapeGradientCache::GradientDesc *ga = &a->m_fillGradient; + const QQuickShapeGradientCache::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->start.x() - gb->start.x()) + return d; + if (int d = ga->start.y() - gb->start.y()) + return d; + if (int d = ga->end.x() - gb->end.x()) + return d; + if (int d = ga->end.y() - gb->end.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h new file mode 100644 index 0000000000..ba50f50309 --- /dev/null +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** 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 QQUICKSHAPEGENERICRENDERER_P_H +#define QQUICKSHAPEGENERICRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapeGenericNode; +class QQuickShapeGenericStrokeFillNode; +class QQuickShapeFillRunnable; +class QQuickShapeStrokeRunnable; + +class QQuickShapeGenericRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyFillGeom = 0x01, + DirtyStrokeGeom = 0x02, + DirtyColor = 0x04, + DirtyFillGradient = 0x08, + DirtyList = 0x10 // only for accDirty + }; + + QQuickShapeGenericRenderer(QQuickItem *item) + : m_item(item), + m_api(QSGRendererInterface::Unknown), + m_rootNode(nullptr), + m_accDirty(0), + m_asyncCallback(nullptr) + { } + ~QQuickShapeGenericRenderer(); + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + void setAsyncCallback(void (*)(void *), void *) override; + Flags flags() const override { return SupportsAsync; } + + void updateNode() override; + + void setRootNode(QQuickShapeGenericNode *node); + + struct Color4ub { unsigned char r, g, b, a; }; + typedef QVector VertexContainerType; + typedef QVector IndexContainerType; + + static void triangulateFill(const QPainterPath &path, + const Color4ub &fillColor, + VertexContainerType *fillVertices, + IndexContainerType *fillIndices, + QSGGeometry::Type *indexType, + bool supportsElementIndexUint); + static void triangulateStroke(const QPainterPath &path, + const QPen &pen, + const Color4ub &strokeColor, + VertexContainerType *strokeVertices, + const QSize &clipSize); + +private: + void maybeUpdateAsyncItem(); + + struct ShapePathData { + float strokeWidth; + QPen pen; + Color4ub strokeColor; + Color4ub fillColor; + Qt::FillRule fillRule; + QPainterPath path; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + VertexContainerType fillVertices; + IndexContainerType fillIndices; + QSGGeometry::Type indexType; + VertexContainerType strokeVertices; + int syncDirty; + int effectiveDirty = 0; + QQuickShapeFillRunnable *pendingFill = nullptr; + QQuickShapeStrokeRunnable *pendingStroke = nullptr; + }; + + void updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n); + void updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node); + void updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node); + + QQuickItem *m_item; + QSGRendererInterface::GraphicsApi m_api; + QQuickShapeGenericNode *m_rootNode; + QVector m_sp; + int m_accDirty; + void (*m_asyncCallback)(void *); + void *m_asyncCallbackData; +}; + +class QQuickShapeFillRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QQuickShapeGenericRenderer::Color4ub fillColor; + bool supportsElementIndexUint; + + // output + QQuickShapeGenericRenderer::VertexContainerType fillVertices; + QQuickShapeGenericRenderer::IndexContainerType fillIndices; + QSGGeometry::Type indexType; + +Q_SIGNALS: + void done(QQuickShapeFillRunnable *self); +}; + +class QQuickShapeStrokeRunnable : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void run() override; + + bool orphaned = false; + + // input + QPainterPath path; + QPen pen; + QQuickShapeGenericRenderer::Color4ub strokeColor; + QSize clipSize; + + // output + QQuickShapeGenericRenderer::VertexContainerType strokeVertices; + +Q_SIGNALS: + void done(QQuickShapeStrokeRunnable *self); +}; + +class QQuickShapeGenericStrokeFillNode : public QSGGeometryNode +{ +public: + QQuickShapeGenericStrokeFillNode(QQuickWindow *window); + ~QQuickShapeGenericStrokeFillNode(); + + enum Material { + MatSolidColor, + MatLinearGradient + }; + + void activateMaterial(Material m); + + QQuickWindow *window() const { return m_window; } + + // shadow data for custom materials + QQuickShapeGradientCache::GradientDesc m_fillGradient; + +private: + QSGGeometry *m_geometry; + QQuickWindow *m_window; + QSGMaterial *m_material; + QScopedPointer m_solidColorMaterial; + QScopedPointer m_linearGradientMaterial; + + friend class QQuickShapeGenericRenderer; +}; + +class QQuickShapeGenericNode : public QSGNode +{ +public: + QQuickShapeGenericStrokeFillNode *m_fillNode = nullptr; + QQuickShapeGenericStrokeFillNode *m_strokeNode = nullptr; + QQuickShapeGenericNode *m_next = nullptr; +}; + +class QQuickShapeGenericMaterialFactory +{ +public: + static QSGMaterial *createVertexColor(QQuickWindow *window); + static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); +}; + +#ifndef QT_NO_OPENGL + +class QQuickShapeLinearGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeLinearGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc; + int m_matrixLoc; + int m_gradStartLoc; + int m_gradEndLoc; +}; + +class QQuickShapeLinearGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeLinearGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeLinearGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + +#endif // QT_NO_OPENGL + +QT_END_NAMESPACE + +#endif // QQUICKSHAPEGENERICRENDERER_P_H diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp new file mode 100644 index 0000000000..a3e9d31be5 --- /dev/null +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** 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 "qquickshapenvprrenderer_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQuickShapeNvprRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeNvprRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + convertPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeNvprRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathGuiData &d(m_sp[index]); + convertJSPath(path, &d); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeNvprRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = fillRule; + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeNvprRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.joinStyle = joinStyle; + d.miterLimit = miterLimit; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.capStyle = capStyle; + d.dirty |= DirtyStyle; + m_accDirty |= DirtyStyle; +} + +void QQuickShapeNvprRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + d.dashActive = strokeStyle == QQuickShapePath::DashLine; + d.dashOffset = dashOffset; + d.dashPattern = dashPattern; + d.dirty |= DirtyDash; + m_accDirty |= DirtyDash; +} + +void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillGradientActive = gradient != nullptr; + if (gradient) { + d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.spread = gradient->spread(); + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + d.fillGradient.start = QPointF(g->x1(), g->y1()); + d.fillGradient.end = QPointF(g->x2(), g->y2()); + } else { + Q_UNREACHABLE(); + } + } + d.dirty |= DirtyFillGradient; + m_accDirty |= DirtyFillGradient; +} + +void QQuickShapeNvprRenderer::endSync(bool) +{ +} + +void QQuickShapeNvprRenderer::setNode(QQuickShapeNvprRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path) +{ + QDebugStateSaver saver(debug); + debug.space().noquote(); + if (!path.str.isEmpty()) { + debug << "Path with SVG string" << path.str; + return debug; + } + debug << "Path with" << path.cmd.count() << "commands"; + int ci = 0; + for (GLubyte cmd : path.cmd) { + static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = { + { GL_MOVE_TO_NV, "moveTo", 2 }, + { GL_LINE_TO_NV, "lineTo", 2 }, + { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 }, + { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 }, + { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 }, + { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 }, + { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 }, + { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 }, + { GL_CLOSE_PATH_NV, "closePath", 0 } }; + for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) { + if (nameTab[i].cmd == cmd) { + QByteArray cs; + for (int j = 0; j < nameTab[i].coordCount; ++j) { + cs.append(QByteArray::number(path.coord[ci++])); + cs.append(' '); + } + debug << "\n " << nameTab[i].s << " " << cs; + break; + } + } + } + return debug; +} + +static inline void appendCoords(QVector *v, QQuickCurve *c, QPointF *pos) +{ + QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(), + c->hasRelativeY() ? pos->y() + c->relativeY() : c->y()); + v->append(p.x()); + v->append(p.y()); + *pos = p; +} + +static inline void appendControlCoords(QVector *v, QQuickPathQuad *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(), + c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl1Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(), + c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y()); + v->append(p.x()); + v->append(p.y()); +} + +static inline void appendControl2Coords(QVector *v, QQuickPathCubic *c, const QPointF &pos) +{ + QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(), + c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y()); + v->append(p.x()); + v->append(p.y()); +} + +void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiData *d) +{ + d->path = NvprPath(); + if (!path) + return; + + const QList &pp(QQuickPathPrivate::get(path)->_pathElements); + if (pp.isEmpty()) + return; + + QPointF startPos(path->startX(), path->startY()); + QPointF pos(startPos); + if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) { + d->path.cmd.append(GL_MOVE_TO_NV); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + } + + for (QQuickPathElement *e : pp) { + if (QQuickPathMove *o = qobject_cast(e)) { + d->path.cmd.append(GL_MOVE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + startPos = pos; + } else if (QQuickPathLine *o = qobject_cast(e)) { + d->path.cmd.append(GL_LINE_TO_NV); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathQuad *o = qobject_cast(e)) { + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + appendControlCoords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathCubic *o = qobject_cast(e)) { + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + appendControl1Coords(&d->path.coord, o, pos); + appendControl2Coords(&d->path.coord, o, pos); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathArc *o = qobject_cast(e)) { + const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo + GLenum cmd; + if (o->useLargeArc()) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(o->radiusX()); + d->path.coord.append(o->radiusY()); + d->path.coord.append(o->xAxisRotation()); + appendCoords(&d->path.coord, o, &pos); + } else if (QQuickPathSvg *o = qobject_cast(e)) { + // PathSvg cannot be combined with other elements. But take at + // least startX and startY into account. + if (d->path.str.isEmpty()) + d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8(); + d->path.str.append(o->path().toUtf8()); + } else { + qWarning() << "Shape/NVPR: unsupported Path element" << e; + } + } + + // For compatibility with QTriangulatingStroker. SVG and others would not + // implicitly close the path when end_pos == start_pos (start_pos being the + // last moveTo pos); that would still need an explicit 'z' or similar. We + // don't have an explicit close command, so just fake a close when the + // positions match. + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +void QQuickShapeNvprRenderer::convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d) +{ + d->path = NvprPath(); + if (path.cmd.isEmpty()) + return; + + QPointF startPos(0, 0); + QPointF pos(startPos); + int coordIdx = 0; + + for (QQuickShapePathCommands::Command cmd : path.cmd) { + switch (cmd) { + case QQuickShapePathCommands::MoveTo: + d->path.cmd.append(GL_MOVE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + startPos = pos; + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickShapePathCommands::LineTo: + d->path.cmd.append(GL_LINE_TO_NV); + pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 2; + break; + case QQuickShapePathCommands::QuadTo: + d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 4; + break; + case QQuickShapePathCommands::CubicTo: + d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); + d->path.coord.append(path.coords[coordIdx]); + d->path.coord.append(path.coords[coordIdx + 1]); + d->path.coord.append(path.coords[coordIdx + 2]); + d->path.coord.append(path.coords[coordIdx + 3]); + pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 6; + break; + case QQuickShapePathCommands::ArcTo: + { + const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); + const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); + GLenum cmd; + if (useLargeArc) + cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; + else + cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; + d->path.cmd.append(cmd); + d->path.coord.append(path.coords[coordIdx]); // rx + d->path.coord.append(path.coords[coordIdx + 1]); // ry + d->path.coord.append(path.coords[coordIdx + 2]); // xrot + pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); + d->path.coord.append(pos.x()); + d->path.coord.append(pos.y()); + coordIdx += 7; + } + break; + default: + qWarning("Unknown JS path command: %d", cmd); + break; + } + } + + if (pos == startPos) + d->path.cmd.append(GL_CLOSE_PATH_NV); +} + +static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) +{ + const float o = c.alphaF() * globalOpacity; + return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o); +} + +void QQuickShapeNvprRenderer::updateNode() +{ + // Called on the render thread with gui blocked -> update the node with its + // own copy of all relevant data. + + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeNvprRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]); + + int dirty = src.dirty; + src.dirty = 0; + if (listChanged) + dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient; + + // updateNode() can be called several times with different dirty + // states before render() gets invoked. So accumulate. + dst.dirty |= dirty; + + if (dirty & DirtyPath) + dst.source = src.path; + + if (dirty & DirtyStyle) { + dst.strokeWidth = src.strokeWidth; + dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f); + dst.fillColor = qsg_premultiply(src.fillColor, 1.0f); + switch (src.joinStyle) { + case QQuickShapePath::MiterJoin: + dst.joinStyle = GL_MITER_TRUNCATE_NV; + break; + case QQuickShapePath::BevelJoin: + dst.joinStyle = GL_BEVEL_NV; + break; + case QQuickShapePath::RoundJoin: + dst.joinStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + dst.miterLimit = src.miterLimit; + switch (src.capStyle) { + case QQuickShapePath::FlatCap: + dst.capStyle = GL_FLAT; + break; + case QQuickShapePath::SquareCap: + dst.capStyle = GL_SQUARE_NV; + break; + case QQuickShapePath::RoundCap: + dst.capStyle = GL_ROUND_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyFillRule) { + switch (src.fillRule) { + case QQuickShapePath::OddEvenFill: + dst.fillRule = GL_INVERT; + break; + case QQuickShapePath::WindingFill: + dst.fillRule = GL_COUNT_UP_NV; + break; + default: + Q_UNREACHABLE(); + } + } + + if (dirty & DirtyDash) { + dst.dashOffset = src.dashOffset; + if (src.dashActive) { + dst.dashPattern.resize(src.dashPattern.count()); + // Multiply by strokeWidth because the Shape API follows QPen + // meaning the input dash pattern here is in width units. + for (int i = 0; i < src.dashPattern.count(); ++i) + dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + } else { + dst.dashPattern.clear(); + } + } + + if (dirty & DirtyFillGradient) { + dst.fillGradientActive = src.fillGradientActive; + if (src.fillGradientActive) + dst.fillGradient = src.fillGradient; + } + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +bool QQuickShapeNvprRenderNode::nvprInited = false; +QQuickNvprFunctions QQuickShapeNvprRenderNode::nvpr; +QQuickNvprMaterialManager QQuickShapeNvprRenderNode::mtlmgr; + +QQuickShapeNvprRenderNode::~QQuickShapeNvprRenderNode() +{ + releaseResources(); +} + +void QQuickShapeNvprRenderNode::releaseResources() +{ + for (ShapePathRenderData &d : m_sp) { + if (d.path) { + nvpr.deletePaths(d.path, 1); + d.path = 0; + } + if (d.fallbackFbo) { + delete d.fallbackFbo; + d.fallbackFbo = nullptr; + } + } + + m_fallbackBlitter.destroy(); +} + +void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr) +{ + m_nvpr = nvpr; +} + +void QQuickNvprMaterialManager::releaseResources() +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + for (MaterialDesc &mtl : m_materials) { + if (mtl.ppl) { + f->glDeleteProgramPipelines(1, &mtl.ppl); + mtl = MaterialDesc(); + } + } +} + +QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + MaterialDesc &mtl(m_materials[m]); + + if (!mtl.ppl) { + if (m == MatSolid) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "out vec4 fragColor;\n" + "uniform vec4 color;\n" + "uniform float opacity;\n" + "void main() {\n" + " fragColor = color * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for solid fill"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color"); + Q_ASSERT(mtl.uniLoc[0] >= 0); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + } else if (m == MatLinearGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "layout(location = 0) in vec2 uv;" + "uniform float opacity;\n" + "uniform sampler2D gradTab;\n" + "uniform vec2 gradStart;\n" + "uniform vec2 gradEnd;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 gradVec = gradEnd - gradStart;\n" + " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n" + " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for linear gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + } else { + Q_UNREACHABLE(); + } + } + + f->glBindProgramPipeline(mtl.ppl); + + return &mtl; +} + +void QQuickShapeNvprRenderNode::updatePath(ShapePathRenderData *d) +{ + if (d->dirty & QQuickShapeNvprRenderer::DirtyPath) { + if (!d->path) { + d->path = nvpr.genPaths(1); + Q_ASSERT(d->path != 0); + } + if (d->source.str.isEmpty()) { + nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(), + d->source.coord.count(), GL_FLOAT, d->source.coord.constData()); + } else { + nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData()); + } + } + + if (d->dirty & QQuickShapeNvprRenderer::DirtyStyle) { + nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth); + nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle); + nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit); + nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle); + nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle); + } + + if (d->dirty & QQuickShapeNvprRenderer::DirtyDash) { + nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset); + // count == 0 -> no dash + nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData()); + } + + if (d->dirty) + d->fallbackValid = false; +} + +void QQuickShapeNvprRenderNode::renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV); +} + +void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) +{ + QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; + if (d->fillGradientActive) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(d->fillGradient); + tx->bind(); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); + } else { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); + f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], + d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w()); + } + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity()); + + const int writeMask = 0xFF; + nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV); +} + +void QQuickShapeNvprRenderNode::renderOffscreenFill(ShapePathRenderData *d) +{ + if (d->fallbackValid && d->fallbackFbo) + return; + + GLfloat bb[4]; + nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb); + QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize(); + d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height())); + d->fallbackTopLeft = QPointF(bb[0], bb[1]); + + if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) { + delete d->fallbackFbo; + d->fallbackFbo = nullptr; + } + if (!d->fallbackFbo) + d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil); + if (!d->fallbackFbo->bind()) + return; + + GLint prevViewport[4]; + f->glGetIntegerv(GL_VIEWPORT, prevViewport); + + f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height()); + f->glDisable(GL_DEPTH_TEST); + f->glClearColor(0, 0, 0, 0); + f->glClearStencil(0); + f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + QMatrix4x4 mv; + mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y()); + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData()); + QMatrix4x4 proj; + proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData()); + + renderFill(d); + + d->fallbackFbo->release(); + f->glEnable(GL_DEPTH_TEST); + f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); + + d->fallbackValid = true; +} + +void QQuickShapeNvprRenderNode::setupStencilForCover(bool stencilClip, int sv) +{ + if (!stencilClip) { + // Assume stencil buffer is cleared to 0 for each frame. + // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear. + f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + } else { + f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF) + f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value) + } +} + +void QQuickShapeNvprRenderNode::render(const RenderState *state) +{ + f = QOpenGLContext::currentContext()->extraFunctions(); + + if (!nvprInited) { + if (!nvpr.create()) { + qWarning("NVPR init failed"); + return; + } + mtlmgr.create(&nvpr); + nvprInited = true; + } + + f->glUseProgram(0); + f->glStencilMask(~0); + f->glEnable(GL_STENCIL_TEST); + + const bool stencilClip = state->stencilEnabled(); + // when true, the stencil buffer already has a clip path with a ref value of sv + const int sv = state->stencilValue(); + const bool hasScissor = state->scissorEnabled(); + + if (hasScissor) { + // scissor rect is already set, just enable scissoring + f->glEnable(GL_SCISSOR_TEST); + } + + // Depth test against the opaque batches rendered before. + f->glEnable(GL_DEPTH_TEST); + f->glDepthFunc(GL_LESS); + nvpr.pathCoverDepthFunc(GL_LESS); + nvpr.pathStencilDepthOffset(-0.05f, -1); + + bool reloadMatrices = true; + + for (ShapePathRenderData &d : m_sp) { + updatePath(&d); + + const bool hasFill = d.hasFill(); + const bool hasStroke = d.hasStroke(); + + if (hasFill && stencilClip) { + // Fall back to a texture when complex clipping is in use and we have + // to fill. Reconciling glStencilFillPath's and the scenegraph's clip + // stencil semantics has not succeeded so far... + if (hasScissor) + f->glDisable(GL_SCISSOR_TEST); + renderOffscreenFill(&d); + reloadMatrices = true; + if (hasScissor) + f->glEnable(GL_SCISSOR_TEST); + } + + if (reloadMatrices) { + reloadMatrices = false; + nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData()); + nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData()); + } + + // Fill! + if (hasFill) { + if (!stencilClip) { + setupStencilForCover(false, 0); + renderFill(&d); + } else { + if (!m_fallbackBlitter.isCreated()) + m_fallbackBlitter.create(); + f->glStencilFunc(GL_EQUAL, sv, 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + QMatrix4x4 mv = *matrix(); + mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y()); + m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(), + *state->projectionMatrix(), mv, + inheritedOpacity()); + } + } + + // Stroke! + if (hasStroke) { + const int strokeStencilValue = 0x80; + const int writeMask = 0x80; + + setupStencilForCover(stencilClip, sv); + if (stencilClip) { + // for the stencil step (eff. read mask == 0xFF & ~writeMask) + nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF); + // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F. + // This assumes the clip stencil value is <= 127. + if (sv >= strokeStencilValue) + qWarning("Shape/NVPR: stencil clip ref value %d too large; expect rendering errors", sv); + } + + renderStroke(&d, strokeStencilValue, writeMask); + } + + if (stencilClip) + nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0); + + d.dirty = 0; + } + + f->glBindProgramPipeline(0); +} + +QSGRenderNode::StateFlags QQuickShapeNvprRenderNode::changedStates() const +{ + return BlendState | StencilState | DepthState | ScissorState; +} + +QSGRenderNode::RenderingFlags QQuickShapeNvprRenderNode::flags() const +{ + return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer +} + +bool QQuickShapeNvprRenderNode::isSupported() +{ + static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0; + return !nvprDisabled && QQuickNvprFunctions::isSupported(); +} + +bool QQuickNvprBlitter::create() +{ + if (isCreated()) + destroy(); + + m_program = new QOpenGLShaderProgram; + if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + } else { + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + } + m_program->bindAttributeLocation("qt_Vertex", 0); + m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); + if (!m_program->link()) + return false; + + m_matrixLoc = m_program->uniformLocation("qt_Matrix"); + m_opacityLoc = m_program->uniformLocation("qt_Opacity"); + + m_buffer = new QOpenGLBuffer; + if (!m_buffer->create()) + return false; + m_buffer->bind(); + m_buffer->allocate(4 * sizeof(GLfloat) * 6); + m_buffer->release(); + + return true; +} + +void QQuickNvprBlitter::destroy() +{ + if (m_program) { + delete m_program; + m_program = nullptr; + } + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } +} + +void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity) +{ + QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions(); + + m_program->bind(); + + QMatrix4x4 m = proj * modelview; + m_program->setUniformValue(m_matrixLoc, m); + m_program->setUniformValue(m_opacityLoc, opacity); + + m_buffer->bind(); + + if (size != m_prevSize) { + m_prevSize = size; + + QPointF p0(size.width() - 1, size.height() - 1); + QPointF p1(0, 0); + QPointF p2(0, size.height() - 1); + QPointF p3(size.width() - 1, 0); + + GLfloat vertices[6 * 4] = { + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + GLfloat(p2.x()), GLfloat(p2.y()), 0, 0, + + GLfloat(p0.x()), GLfloat(p0.y()), 1, 0, + GLfloat(p3.x()), GLfloat(p3.y()), 1, 1, + GLfloat(p1.x()), GLfloat(p1.y()), 0, 1, + }; + + m_buffer->write(0, vertices, sizeof(vertices)); + } + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat))); + + f->glBindTexture(GL_TEXTURE_2D, textureId); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + f->glBindTexture(GL_TEXTURE_2D, 0); + m_buffer->release(); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h new file mode 100644 index 0000000000..33007313d4 --- /dev/null +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** 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 QQUICKSHAPENVPRRENDERER_P_H +#define QQUICKSHAPENVPRRENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include "qquicknvprfunctions_p.h" +#include +#include +#include +#include + +#ifndef QT_NO_OPENGL + +QT_BEGIN_NAMESPACE + +class QQuickShapeNvprRenderNode; +class QOpenGLFramebufferObject; +class QOpenGLBuffer; +class QOpenGLExtraFunctions; + +class QQuickShapeNvprRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyStyle = 0x02, + DirtyFillRule = 0x04, + DirtyDash = 0x08, + DirtyFillGradient = 0x10, + DirtyList = 0x20 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeNvprRenderNode *node); + + struct NvprPath { + QVector cmd; + QVector coord; + QByteArray str; + }; + +private: + struct ShapePathGuiData { + int dirty = 0; + NvprPath path; + qreal strokeWidth; + QColor strokeColor; + QColor fillColor; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::FillRule fillRule; + bool dashActive; + qreal dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + }; + + void convertPath(const QQuickPath *path, ShapePathGuiData *d); + void convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d); + + QQuickShapeNvprRenderNode *m_node = nullptr; + int m_accDirty = 0; + + QVector m_sp; +}; + +QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path); + +class QQuickNvprMaterialManager +{ +public: + enum Material { + MatSolid, + MatLinearGradient, + + NMaterials + }; + + struct MaterialDesc { + GLuint ppl = 0; + GLuint prg = 0; + int uniLoc[4]; + }; + + void create(QQuickNvprFunctions *nvpr); + MaterialDesc *activateMaterial(Material m); + void releaseResources(); + +private: + QQuickNvprFunctions *m_nvpr; + MaterialDesc m_materials[NMaterials]; +}; + +class QQuickNvprBlitter +{ +public: + bool create(); + void destroy(); + bool isCreated() const { return m_program != nullptr; } + void texturedQuad(GLuint textureId, const QSize &size, + const QMatrix4x4 &proj, const QMatrix4x4 &modelview, + float opacity); + +private: + QOpenGLShaderProgram *m_program = nullptr; + QOpenGLBuffer *m_buffer = nullptr; + int m_matrixLoc; + int m_opacityLoc; + QSize m_prevSize; +}; + +class QQuickShapeNvprRenderNode : public QSGRenderNode +{ +public: + ~QQuickShapeNvprRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + + static bool isSupported(); + +private: + struct ShapePathRenderData { + GLuint path = 0; + int dirty = 0; + QQuickShapeNvprRenderer::NvprPath source; + GLfloat strokeWidth; + QVector4D strokeColor; + QVector4D fillColor; + GLenum joinStyle; + GLint miterLimit; + GLenum capStyle; + GLenum fillRule; + GLfloat dashOffset; + QVector dashPattern; + bool fillGradientActive; + QQuickShapeGradientCache::GradientDesc fillGradient; + QOpenGLFramebufferObject *fallbackFbo = nullptr; + bool fallbackValid = false; + QSize fallbackSize; + QPointF fallbackTopLeft; + + bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; } + bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); } + }; + + void updatePath(ShapePathRenderData *d); + void renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask); + void renderFill(ShapePathRenderData *d); + void renderOffscreenFill(ShapePathRenderData *d); + void setupStencilForCover(bool stencilClip, int sv); + + static bool nvprInited; + static QQuickNvprFunctions nvpr; + static QQuickNvprMaterialManager mtlmgr; + + QQuickNvprBlitter m_fallbackBlitter; + QOpenGLExtraFunctions *f = nullptr; + + QVector m_sp; + + friend class QQuickShapeNvprRenderer; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_OPENGL + +#endif // QQUICKSHAPENVPRRENDERER_P_H diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp new file mode 100644 index 0000000000..33d80be22c --- /dev/null +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 "qquickshapesoftwarerenderer_p.h" +#include + +QT_BEGIN_NAMESPACE + +void QQuickShapeSoftwareRenderer::beginSync(int totalCount) +{ + if (m_sp.count() != totalCount) { + m_sp.resize(totalCount); + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::setPath(int index, const QQuickPath *path) +{ + ShapePathGuiData &d(m_sp[index]); + d.path = path ? path->path() : QPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeSoftwareRenderer::setJSPath(int index, const QQuickShapePathCommands &path) +{ + ShapePathGuiData &d(m_sp[index]); + d.path = path.toPainterPath(); + d.dirty |= DirtyPath; + m_accDirty |= DirtyPath; +} + +void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setColor(color); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeWidth(int index, qreal w) +{ + ShapePathGuiData &d(m_sp[index]); + d.strokeWidth = w; + if (w >= 0.0f) + d.pen.setWidthF(w); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setFillColor(int index, const QColor &color) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillColor = color; + d.brush.setColor(color); + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule) +{ + ShapePathGuiData &d(m_sp[index]); + d.fillRule = Qt::FillRule(fillRule); + d.dirty |= DirtyFillRule; + m_accDirty |= DirtyFillRule; +} + +void QQuickShapeSoftwareRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle)); + d.pen.setMiterLimit(miterLimit); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle) +{ + ShapePathGuiData &d(m_sp[index]); + d.pen.setCapStyle(Qt::PenCapStyle(capStyle)); + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) +{ + ShapePathGuiData &d(m_sp[index]); + switch (strokeStyle) { + case QQuickShapePath::SolidLine: + d.pen.setStyle(Qt::SolidLine); + break; + case QQuickShapePath::DashLine: + d.pen.setStyle(Qt::CustomDashLine); + d.pen.setDashPattern(dashPattern); + d.pen.setDashOffset(dashOffset); + break; + default: + break; + } + d.dirty |= DirtyPen; + m_accDirty |= DirtyPen; +} + +void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) +{ + ShapePathGuiData &d(m_sp[index]); + if (QQuickShapeLinearGradient *linearGradient = qobject_cast(gradient)) { + QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), + linearGradient->x2(), linearGradient->y2()); + painterGradient.setStops(linearGradient->sortedGradientStops()); + switch (gradient->spread()) { + case QQuickShapeGradient::PadSpread: + painterGradient.setSpread(QGradient::PadSpread); + break; + case QQuickShapeGradient::RepeatSpread: + painterGradient.setSpread(QGradient::RepeatSpread); + break; + case QQuickShapeGradient::ReflectSpread: + painterGradient.setSpread(QGradient::ReflectSpread); + break; + default: + break; + } + d.brush = QBrush(painterGradient); + } else { + d.brush = QBrush(d.fillColor); + } + d.dirty |= DirtyBrush; + m_accDirty |= DirtyBrush; +} + +void QQuickShapeSoftwareRenderer::endSync(bool) +{ +} + +void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node) +{ + if (m_node != node) { + m_node = node; + m_accDirty |= DirtyList; + } +} + +void QQuickShapeSoftwareRenderer::updateNode() +{ + if (!m_accDirty) + return; + + const int count = m_sp.count(); + const bool listChanged = m_accDirty & DirtyList; + if (listChanged) + m_node->m_sp.resize(count); + + m_node->m_boundingRect = QRectF(); + + for (int i = 0; i < count; ++i) { + ShapePathGuiData &src(m_sp[i]); + QQuickShapeSoftwareRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]); + + if (listChanged || (src.dirty & DirtyPath)) { + dst.path = src.path; + dst.path.setFillRule(src.fillRule); + } + + if (listChanged || (src.dirty & DirtyFillRule)) + dst.path.setFillRule(src.fillRule); + + if (listChanged || (src.dirty & DirtyPen)) { + dst.pen = src.pen; + dst.strokeWidth = src.strokeWidth; + } + + if (listChanged || (src.dirty & DirtyBrush)) + dst.brush = src.brush; + + src.dirty = 0; + + QRectF br = dst.path.boundingRect(); + const float sw = qMax(1.0f, dst.strokeWidth); + br.adjust(-sw, -sw, sw, sw); + m_node->m_boundingRect |= br; + } + + m_node->markDirty(QSGNode::DirtyMaterial); + m_accDirty = 0; +} + +QQuickShapeSoftwareRenderNode::QQuickShapeSoftwareRenderNode(QQuickShape *item) + : m_item(item) +{ +} + +QQuickShapeSoftwareRenderNode::~QQuickShapeSoftwareRenderNode() +{ + releaseResources(); +} + +void QQuickShapeSoftwareRenderNode::releaseResources() +{ +} + +void QQuickShapeSoftwareRenderNode::render(const RenderState *state) +{ + if (m_sp.isEmpty()) + return; + + QSGRendererInterface *rif = m_item->window()->rendererInterface(); + QPainter *p = static_cast(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource)); + Q_ASSERT(p); + + const QRegion *clipRegion = state->clipRegion(); + if (clipRegion && !clipRegion->isEmpty()) + p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform + + p->setTransform(matrix()->toTransform()); + p->setOpacity(inheritedOpacity()); + + for (const ShapePathRenderData &d : qAsConst(m_sp)) { + p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen); + p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush); + p->drawPath(d.path); + } +} + +QSGRenderNode::StateFlags QQuickShapeSoftwareRenderNode::changedStates() const +{ + return 0; +} + +QSGRenderNode::RenderingFlags QQuickShapeSoftwareRenderNode::flags() const +{ + return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect() +} + +QRectF QQuickShapeSoftwareRenderNode::rect() const +{ + return m_boundingRect; +} + +QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapesoftwarerenderer_p.h b/src/imports/shapes/qquickshapesoftwarerenderer_p.h new file mode 100644 index 0000000000..53982ce347 --- /dev/null +++ b/src/imports/shapes/qquickshapesoftwarerenderer_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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 QQUICKSHAPESOFTWARERENDERER_P_H +#define QQUICKSHAPESOFTWARERENDERER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickshape_p_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickShapeSoftwareRenderNode; + +class QQuickShapeSoftwareRenderer : public QQuickAbstractPathRenderer +{ +public: + enum Dirty { + DirtyPath = 0x01, + DirtyPen = 0x02, + DirtyFillRule = 0x04, + DirtyBrush = 0x08, + DirtyList = 0x10 + }; + + void beginSync(int totalCount) override; + void setPath(int index, const QQuickPath *path) override; + void setJSPath(int index, const QQuickShapePathCommands &path) override; + void setStrokeColor(int index, const QColor &color) override; + void setStrokeWidth(int index, qreal w) override; + void setFillColor(int index, const QColor &color) override; + void setFillRule(int index, QQuickShapePath::FillRule fillRule) override; + void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override; + void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override; + void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle, + qreal dashOffset, const QVector &dashPattern) override; + void setFillGradient(int index, QQuickShapeGradient *gradient) override; + void endSync(bool async) override; + + void updateNode() override; + + void setNode(QQuickShapeSoftwareRenderNode *node); + +private: + QQuickShapeSoftwareRenderNode *m_node = nullptr; + int m_accDirty = 0; + struct ShapePathGuiData { + int dirty = 0; + QPainterPath path; + QPen pen; + float strokeWidth; + QColor fillColor; + QBrush brush; + Qt::FillRule fillRule; + }; + QVector m_sp; +}; + +class QQuickShapeSoftwareRenderNode : public QSGRenderNode +{ +public: + QQuickShapeSoftwareRenderNode(QQuickShape *item); + ~QQuickShapeSoftwareRenderNode(); + + void render(const RenderState *state) override; + void releaseResources() override; + StateFlags changedStates() const override; + RenderingFlags flags() const override; + QRectF rect() const override; + +private: + QQuickShape *m_item; + + struct ShapePathRenderData { + QPainterPath path; + QPen pen; + float strokeWidth; + QBrush brush; + }; + QVector m_sp; + QRectF m_boundingRect; + + friend class QQuickShapeSoftwareRenderer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHAPESOFTWARERENDERER_P_H diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro new file mode 100644 index 0000000000..80e6a22142 --- /dev/null +++ b/src/imports/shapes/shapes.pro @@ -0,0 +1,31 @@ +CXX_MODULE = qml +TARGET = qmlshapesplugin +TARGETPATH = QtQuick/Shapes +IMPORT_VERSION = 1.0 + +QT = core gui qml quick quick-private + +HEADERS += \ + qquickshape_p.h \ + qquickshape_p_p.h \ + qquickshapegenericrenderer_p.h \ + qquickshapesoftwarerenderer_p.h + +SOURCES += \ + plugin.cpp \ + qquickshape.cpp \ + qquickshapegenericrenderer.cpp \ + qquickshapesoftwarerenderer.cpp + +qtConfig(opengl) { + HEADERS += \ + qquicknvprfunctions_p.h \ + qquicknvprfunctions_p_p.h \ + qquickshapenvprrenderer_p.h + + SOURCES += \ + qquicknvprfunctions.cpp \ + qquickshapenvprrenderer.cpp +} + +load(qml_plugin) diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 84441e308f..085963be6c 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE \instantiates QQuickPath \inqmlmodule QtQuick \ingroup qtquick-animation-paths - \brief Defines a path for use by \l PathView and \l PathItem + \brief Defines a path for use by \l PathView and \l Shape A Path is composed of one or more path segments - PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg. @@ -80,16 +80,16 @@ QT_BEGIN_NAMESPACE along the path. Path and the other types for specifying path elements are shared between - \l PathView and \l PathItem. The following table provides an overview of the + \l PathView and \l Shape. The following table provides an overview of the applicability of the various path elements: \table \header \li Element \li PathView - \li PathItem - \li PathItem, GL_NV_path_rendering - \li PathItem, software + \li Shape + \li Shape, GL_NV_path_rendering + \li Shape, software \row \li PathMove \li N/A @@ -146,7 +146,7 @@ QT_BEGIN_NAMESPACE \li No \endtable - \sa PathView, PathItem, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg + \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg */ QQuickPath::QQuickPath(QObject *parent) : QObject(*(new QQuickPathPrivate), parent) @@ -1119,7 +1119,7 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) \ingroup qtquick-animation-paths \brief Moves the Path's position - While not relevant with PathView, for Path elements used with PathItem it + While not relevant with PathView, for Path elements used with Shape it is important to distinguish between the operations of drawing a straight line and moving the path position without drawing anything. @@ -1927,7 +1927,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) \note Mixing PathSvg with other type of elements is not always supported, therefore it is strongly recommended to avoid this. For example, when - \l PathItem is backed by \c{GL_NV_path_rendering}, a Path can contain one or + \l Shape is backed by \c{GL_NV_path_rendering}, a Path can contain one or more PathSvg elements, or one or more other type of elements, but not both. \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve -- cgit v1.2.3 From 5af4c9b237a23ce12ca7c56eb6c9ecda17743228 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 2 Jun 2017 15:12:05 +0200 Subject: Reduce objects: Make ShapePath inherit Path Shape { ShapePath { Path { ... } } } simply becomes Shape { ShapePath { ... } } Change-Id: Ie57936cd7953c8a8d6c67e78b9d73bdbe2a05316 Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 122 +++++++++++++---------------------- src/imports/shapes/qquickshape_p.h | 14 +--- src/imports/shapes/qquickshape_p_p.h | 3 +- src/quick/util/qquickpath.cpp | 5 ++ src/quick/util/qquickpath_p.h | 1 + src/quick/util/qquickpath_p_p.h | 2 +- 6 files changed, 55 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index e37addba07..81736fcd82 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -133,17 +133,17 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths \ingroup qtquick-views - \inherits Object + \inherits Path \brief Describes a Path and associated properties for stroking and filling \since 5.10 - A Shape contains one or more ShapePath elements. At least one - ShapePath is necessary in order to have a Shape output anything - visible. A ShapeItem in turn contains a Path and properties describing the - stroking and filling parameters, such as the stroke width and color, the - fill color or gradient, join and cap styles, and so on. Finally, the Path - object contains a list of path elements like PathMove, PathLine, PathCubic, - PathQuad, PathArc. + A Shape contains one or more ShapePath elements. At least one ShapePath is + necessary in order to have a Shape output anything visible. A ShapePath + itself is a \l Path with additional properties describing the stroking and + filling parameters, such as the stroke width and color, the fill color or + gradient, join and cap styles, and so on. As with ordinary \l Path objects, + ShapePath also contains a list of path elements like \l PathMove, \l PathLine, + \l PathCubic, \l PathQuad, \l PathArc, together with a starting position. Any property changes in these data sets will be bubble up and change the output of the Shape. This means that it is simple and easy to change, or @@ -166,12 +166,10 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const joinStyle: styles[joinStyleIndex] - Path { - startX: 30 - startY: 30 - PathLine { x: 100; y: 100 } - PathLine { x: 30; y: 100 } - } + startX: 30 + startY: 30 + PathLine { x: 100; y: 100 } + PathLine { x: 30; y: 100 } } \endcode @@ -182,57 +180,27 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const */ QQuickShapePathPrivate::QQuickShapePathPrivate() - : path(nullptr), - dirty(DirtyAll) + : dirty(DirtyAll) { } QQuickShapePath::QQuickShapePath(QObject *parent) - : QObject(*(new QQuickShapePathPrivate), parent) + : QQuickPath(*(new QQuickShapePathPrivate), parent) { -} + // The inherited changed() and the shapePathChanged() signals remain + // distinct, and this is intentional. Combining the two is not possible due + // to the difference in semantics and the need to act (see dirty flag + // below) differently on QQuickPath-related changes. -QQuickShapePath::~QQuickShapePath() -{ + connect(this, &QQuickPath::changed, [this]() { + Q_D(QQuickShapePath); + d->dirty |= QQuickShapePathPrivate::DirtyPath; + emit shapePathChanged(); + }); } -/*! - \qmlproperty Path QtQuick.Shapes::ShapePath::path - - This property holds the Path object. - - \default - */ - -QQuickPath *QQuickShapePath::path() const -{ - Q_D(const QQuickShapePath); - return d->path; -} - -void QQuickShapePath::setPath(QQuickPath *path) -{ - Q_D(QQuickShapePath); - if (d->path == path) - return; - - if (d->path) - qmlobject_disconnect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickShapePath, SLOT(_q_pathChanged())); - d->path = path; - qmlobject_connect(d->path, QQuickPath, SIGNAL(changed()), - this, QQuickShapePath, SLOT(_q_pathChanged())); - - d->dirty |= QQuickShapePathPrivate::DirtyPath; - emit pathChanged(); - emit changed(); -} - -void QQuickShapePathPrivate::_q_pathChanged() +QQuickShapePath::~QQuickShapePath() { - Q_Q(QQuickShapePath); - dirty |= DirtyPath; - emit q->changed(); } /*! @@ -258,7 +226,7 @@ void QQuickShapePath::setStrokeColor(const QColor &color) d->sfp.strokeColor = color; d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor; emit strokeColorChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -285,7 +253,7 @@ void QQuickShapePath::setStrokeWidth(qreal w) d->sfp.strokeWidth = w; d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth; emit strokeWidthChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -312,7 +280,7 @@ void QQuickShapePath::setFillColor(const QColor &color) d->sfp.fillColor = color; d->dirty |= QQuickShapePathPrivate::DirtyFillColor; emit fillColorChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -342,7 +310,7 @@ void QQuickShapePath::setFillRule(FillRule fillRule) d->sfp.fillRule = fillRule; d->dirty |= QQuickShapePathPrivate::DirtyFillRule; emit fillRuleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -372,7 +340,7 @@ void QQuickShapePath::setJoinStyle(JoinStyle style) d->sfp.joinStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit joinStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -398,7 +366,7 @@ void QQuickShapePath::setMiterLimit(int limit) d->sfp.miterLimit = limit; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit miterLimitChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -428,7 +396,7 @@ void QQuickShapePath::setCapStyle(CapStyle style) d->sfp.capStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyStyle; emit capStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -457,7 +425,7 @@ void QQuickShapePath::setStrokeStyle(StrokeStyle style) d->sfp.strokeStyle = style; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit strokeStyleChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -485,7 +453,7 @@ void QQuickShapePath::setDashOffset(qreal offset) d->sfp.dashOffset = offset; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit dashOffsetChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -516,7 +484,7 @@ void QQuickShapePath::setDashPattern(const QVector &array) d->sfp.dashPattern = array; d->dirty |= QQuickShapePathPrivate::DirtyDash; emit dashPatternChanged(); - emit changed(); + emit shapePathChanged(); } } @@ -549,7 +517,7 @@ void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient) qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()), this, QQuickShapePath, SLOT(_q_fillGradientChanged())); d->dirty |= QQuickShapePathPrivate::DirtyFillGradient; - emit changed(); + emit shapePathChanged(); } } @@ -557,7 +525,7 @@ void QQuickShapePathPrivate::_q_fillGradientChanged() { Q_Q(QQuickShapePath); dirty |= DirtyFillGradient; - emit q->changed(); + emit q->shapePathChanged(); } void QQuickShapePath::resetFillGradient() @@ -613,12 +581,10 @@ void QQuickShapePath::resetFillGradient() } strokeStyle: ShapePath.DashLine dashPattern: [ 1, 4 ] - Path { - startX: 20; startY: 20 - PathLine { x: 180; y: 130 } - PathLine { x: 20; y: 130 } - PathLine { x: 20; y: 20 } - } + startX: 20; startY: 20 + PathLine { x: 180; y: 130 } + PathLine { x: 20; y: 130 } + PathLine { x: 20; y: 20 } } } \endcode @@ -838,7 +804,7 @@ static void vpe_append(QQmlListProperty *property, QQuickShapeP d->qmlData.sp.append(obj); if (d->componentComplete) { - QObject::connect(obj, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + QObject::connect(obj, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } } @@ -855,7 +821,7 @@ static void vpe_clear(QQmlListProperty *property) QQuickShapePrivate *d = QQuickShapePrivate::get(item); for (QQuickShapePath *p : d->qmlData.sp) - QObject::disconnect(p, SIGNAL(changed()), item, SLOT(_q_shapePathChanged())); + QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->qmlData.sp.clear(); @@ -894,7 +860,7 @@ void QQuickShape::componentComplete() d->componentComplete = true; for (QQuickShapePath *p : d->qmlData.sp) - connect(p, SIGNAL(changed()), this, SLOT(_q_shapePathChanged())); + connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } @@ -1042,7 +1008,7 @@ void QQuickShapePrivate::sync() int &dirty(QQuickShapePathPrivate::get(p)->dirty); if (dirty & QQuickShapePathPrivate::DirtyPath) - renderer->setPath(i, p->path()); + renderer->setPath(i, p); if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) renderer->setStrokeColor(i, p->strokeColor()); if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 5b3580dd1b..a01c36e971 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -54,7 +54,7 @@ #include "qquickitem.h" #include -#include +#include #include #include @@ -152,13 +152,10 @@ private: QPointF m_end; }; -class QQuickShapePath : public QObject +class QQuickShapePath : public QQuickPath { Q_OBJECT - Q_PROPERTY(QQuickPath *path READ path WRITE setPath NOTIFY pathChanged) - Q_CLASSINFO("DefaultProperty", "path") - Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged) Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged) Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) @@ -201,9 +198,6 @@ public: QQuickShapePath(QObject *parent = nullptr); ~QQuickShapePath(); - QQuickPath *path() const; - void setPath(QQuickPath *path); - QColor strokeColor() const; void setStrokeColor(const QColor &color); @@ -239,8 +233,7 @@ public: void resetFillGradient(); Q_SIGNALS: - void changed(); - void pathChanged(); + void shapePathChanged(); void strokeColorChanged(); void strokeWidthChanged(); void fillColorChanged(); @@ -256,7 +249,6 @@ Q_SIGNALS: private: Q_DISABLE_COPY(QQuickShapePath) Q_DECLARE_PRIVATE(QQuickShapePath) - Q_PRIVATE_SLOT(d_func(), void _q_pathChanged()) Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged()) }; diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index 7a503e36a9..a8a5675ccb 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -131,7 +131,7 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) -class QQuickShapePathPrivate : public QObjectPrivate +class QQuickShapePathPrivate : public QQuickPathPrivate { Q_DECLARE_PUBLIC(QQuickShapePath) @@ -156,7 +156,6 @@ public: static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); } - QQuickPath *path; int dirty; QQuickShapeStrokeFillParams sfp; }; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 085963be6c..0103f66814 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -153,6 +153,11 @@ QQuickPath::QQuickPath(QObject *parent) { } +QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + QQuickPath::~QQuickPath() { } diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 1c639db9ce..b7fde5c272 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -419,6 +419,7 @@ Q_SIGNALS: void startYChanged(); protected: + QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr); void componentComplete() override; void classBegin() override; void disconnectPathElements(); diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h index 1dc3c1c47a..8ce85dbf0f 100644 --- a/src/quick/util/qquickpath_p_p.h +++ b/src/quick/util/qquickpath_p_p.h @@ -64,7 +64,7 @@ QT_REQUIRE_CONFIG(quick_path); QT_BEGIN_NAMESPACE -class QQuickPathPrivate : public QObjectPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickPathPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickPath) -- cgit v1.2.3 From f4f89858cffa1107af5139dfb1e1d7b16ca3a1a0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Jun 2017 14:54:37 +0200 Subject: Use GradientStop and rename ShapeLinearGradient to LinearGradient Thus application code becomes: ShapePath { ... fillGradient: LinearGradient { ... GradientStop { ... } } } which is even more clean and readable. The duplication for stops is now avoided. Change-Id: I50ae2f388e21683a37dc4787763dc71e16eef4f5 Reviewed-by: Mitch Curtis Reviewed-by: J-P Nurmi --- src/imports/shapes/plugin.cpp | 3 +- src/imports/shapes/plugins.qmltypes | 10 +- src/imports/shapes/qquickshape.cpp | 166 +++------------------ src/imports/shapes/qquickshape_p.h | 36 +---- src/imports/shapes/qquickshapegenericrenderer.cpp | 2 +- src/imports/shapes/qquickshapenvprrenderer.cpp | 2 +- src/imports/shapes/qquickshapesoftwarerenderer.cpp | 2 +- 7 files changed, 30 insertions(+), 191 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index 2f2f8c74d3..ae0d02da93 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -63,9 +63,8 @@ public: Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes")); qmlRegisterType(uri, 1, 0, "Shape"); qmlRegisterType(uri, 1, 0, "ShapePath"); - qmlRegisterType(uri, 1, 0, "ShapeGradientStop"); qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); - qmlRegisterType(uri, 1, 0, "ShapeLinearGradient"); + qmlRegisterType(uri, 1, 0, "LinearGradient"); } }; diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes index 28d8dd1f12..00d0050085 100644 --- a/src/imports/shapes/plugins.qmltypes +++ b/src/imports/shapes/plugins.qmltypes @@ -191,14 +191,6 @@ Module { Property { name: "spread"; type: "SpreadMode" } Signal { name: "updated" } } - Component { - name: "QQuickShapeGradientStop" - prototype: "QObject" - exports: ["QtQuick.Shapes/ShapeGradientStop 1.0"] - exportMetaObjectRevisions: [0] - Property { name: "position"; type: "double" } - Property { name: "color"; type: "QColor" } - } Component { name: "QQuickShape" defaultProperty: "elements" @@ -232,7 +224,7 @@ Module { name: "QQuickShapeLinearGradient" defaultProperty: "stops" prototype: "QQuickShapeGradient" - exports: ["QtQuick.Shapes/ShapeLinearGradient 1.0"] + exports: ["QtQuick.Shapes/LinearGradient 1.0"] exportMetaObjectRevisions: [0] Property { name: "x1"; type: "double" } Property { name: "y1"; type: "double" } diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 81736fcd82..3c253eabd4 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -47,12 +47,6 @@ #include #include -#include -#include -#include -#include -#include - QT_BEGIN_NAMESPACE /*! @@ -497,6 +491,9 @@ void QQuickShapePath::setDashPattern(const QVector &array) When set, ShapePath.fillColor is ignored and filling is done using one of the ShapeGradient subtypes. + + \note The Gradient type cannot be used here. Rather, prefer using one of the + advanced subtypes, like LinearGradient. */ QQuickShapeGradient *QQuickShapePath::fillGradient() const @@ -570,14 +567,14 @@ void QQuickShapePath::resetFillGradient() ShapePath { strokeWidth: 4 strokeColor: "red" - fillGradient: ShapeLinearGradient { + fillGradient: LinearGradient { x1: 20; y1: 20 x2: 180; y2: 130 - ShapeGradientStop { position: 0; color: "blue" } - ShapeGradientStop { position: 0.2; color: "green" } - ShapeGradientStop { position: 0.4; color: "red" } - ShapeGradientStop { position: 0.6; color: "yellow" } - ShapeGradientStop { position: 1; color: "cyan" } + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } } strokeStyle: ShapePath.DashLine dashPattern: [ 1, 4 ] @@ -1062,149 +1059,29 @@ void QQuickShapePrivate::sync() // ***** gradient support ***** -/*! - \qmltype ShapeGradientStop - \instantiates QQuickShapeGradientStop - \inqmlmodule QtQuick.Shapes - \ingroup qtquick-paths - \ingroup qtquick-views - \inherits Object - \brief Defines a color at a position in a gradient - \since 5.10 - */ - -QQuickShapeGradientStop::QQuickShapeGradientStop(QObject *parent) - : QObject(parent), - m_position(0), - m_color(Qt::black) -{ -} - -/*! - \qmlproperty real QtQuick.Shapes::ShapeGradientStop::position - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is 0. - */ - -qreal QQuickShapeGradientStop::position() const -{ - return m_position; -} - -void QQuickShapeGradientStop::setPosition(qreal position) -{ - if (m_position != position) { - m_position = position; - if (QQuickShapeGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - -/*! - \qmlproperty real QtQuick.Shapes::ShapeGradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default value is \c black. - */ - -QColor QQuickShapeGradientStop::color() const -{ - return m_color; -} - -void QQuickShapeGradientStop::setColor(const QColor &color) -{ - if (m_color != color) { - m_color = color; - if (QQuickShapeGradient *grad = qobject_cast(parent())) - emit grad->updated(); - } -} - /*! \qmltype ShapeGradient \instantiates QQuickShapeGradient \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths \ingroup qtquick-views - \inherits Object + \inherits Gradient \brief Base type of Shape fill gradients \since 5.10 - This is an abstract base class for gradients like ShapeLinearGradient and - cannot be created directly. + This is an abstract base class for gradients like LinearGradient and + cannot be created directly. It extends \l Gradient with properties like the + spread mode. */ QQuickShapeGradient::QQuickShapeGradient(QObject *parent) - : QObject(parent), + : QQuickGradient(parent), m_spread(PadSpread) { } -int QQuickShapeGradient::countStops(QQmlListProperty *list) -{ - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.count(); -} - -QObject *QQuickShapeGradient::atStop(QQmlListProperty *list, int index) -{ - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - return grad->m_stops.at(index); -} - -void QQuickShapeGradient::appendStop(QQmlListProperty *list, QObject *stop) -{ - QQuickShapeGradientStop *sstop = qobject_cast(stop); - if (!sstop) { - qWarning("Gradient stop list only supports QQuickShapeGradientStop elements"); - return; - } - QQuickShapeGradient *grad = qobject_cast(list->object); - Q_ASSERT(grad); - sstop->setParent(grad); - grad->m_stops.append(sstop); -} - /*! - \qmlproperty list QtQuick.Shapes::ShapeGradient::stops - \default - - The list of ShapeGradientStop objects defining the colors at given positions - in the gradient. - */ - -QQmlListProperty QQuickShapeGradient::stops() -{ - return QQmlListProperty(this, nullptr, - &QQuickShapeGradient::appendStop, - &QQuickShapeGradient::countStops, - &QQuickShapeGradient::atStop, - nullptr); -} - -QGradientStops QQuickShapeGradient::sortedGradientStops() const -{ - QGradientStops result; - for (int i = 0; i < m_stops.count(); ++i) { - QQuickShapeGradientStop *s = static_cast(m_stops[i]); - int j = 0; - while (j < result.count() && result[j].first < s->position()) - ++j; - result.insert(j, QGradientStop(s->position(), s->color())); - } - return result; -} - -/*! - \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spred + \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread Specifies how the area outside the gradient area should be filled. The default value is ShapeGradient.PadSpread. @@ -1231,7 +1108,7 @@ void QQuickShapeGradient::setSpread(SpreadMode mode) } /*! - \qmltype ShapeLinearGradient + \qmltype LinearGradient \instantiates QQuickShapeLinearGradient \inqmlmodule QtQuick.Shapes \ingroup qtquick-paths @@ -1244,6 +1121,9 @@ void QQuickShapeGradient::setSpread(SpreadMode mode) these points the gradient is either padded, reflected or repeated depending on the spread type. + \note LinearGradient is not compatible with Rectangle items that only + support Gradient. This type is to be used with Shape. + \sa QLinearGradient */ @@ -1253,10 +1133,10 @@ QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent) } /*! - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x1 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y1 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::x2 - \qmlproperty real QtQuick.Shapes::ShapeLinearGradient::y2 + \qmlproperty real QtQuick.Shapes::LinearGradient::x1 + \qmlproperty real QtQuick.Shapes::LinearGradient::y1 + \qmlproperty real QtQuick.Shapes::LinearGradient::x2 + \qmlproperty real QtQuick.Shapes::LinearGradient::y2 These properties define the start and end points between which color interpolation occurs. By default both the stard and end points are set to diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index a01c36e971..50b242e492 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -55,38 +55,16 @@ #include #include -#include -#include +#include QT_BEGIN_NAMESPACE class QQuickShapePathPrivate; class QQuickShapePrivate; -class QQuickShapeGradientStop : public QObject +class QQuickShapeGradient : public QQuickGradient { Q_OBJECT - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QQuickShapeGradientStop(QObject *parent = nullptr); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - qreal m_position; - QColor m_color; -}; - -class QQuickShapeGradient : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlListProperty stops READ stops) Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged) Q_CLASSINFO("DefaultProperty", "stops") @@ -100,23 +78,13 @@ public: QQuickShapeGradient(QObject *parent = nullptr); - QQmlListProperty stops(); - - QGradientStops sortedGradientStops() const; - SpreadMode spread() const; void setSpread(SpreadMode mode); signals: - void updated(); void spreadChanged(); private: - static int countStops(QQmlListProperty *list); - static QObject *atStop(QQmlListProperty *list, int index); - static void appendStop(QQmlListProperty *list, QObject *stop); - - QVector m_stops; SpreadMode m_spread; }; diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index ff226959eb..47203698d5 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -247,7 +247,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient ShapePathData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { d.fillGradient.start = QPointF(g->x1(), g->y1()); diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index a3e9d31be5..f3f8d807ec 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -135,7 +135,7 @@ void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gr ShapePathGuiData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; if (gradient) { - d.fillGradient.stops = gradient->sortedGradientStops(); + d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { d.fillGradient.start = QPointF(g->x1(), g->y1()); diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp index 33d80be22c..b3373106af 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -144,7 +144,7 @@ void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient if (QQuickShapeLinearGradient *linearGradient = qobject_cast(gradient)) { QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->sortedGradientStops()); + painterGradient.setStops(linearGradient->gradientStops()); // sorted switch (gradient->spread()) { case QQuickShapeGradient::PadSpread: painterGradient.setSpread(QGradient::PadSpread); -- cgit v1.2.3 From 123f698c5bbf23ad816bf2274620ab4f0a82ed2f Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Jun 2017 15:51:14 +0200 Subject: Allow freely mixing non-ShapePath objects in Shape The own list property must be kept. However, we can reuse QQuickItemPrivate's data accessors in order to start supporting code like: Shape { .. ShapePath { ... } ShapePath { ... } Rectangle { ... } Image { ... } // any other visual type // or non-visual: Timer { ... } } Change-Id: I6d502d697cae37bf16857770273a749cee1b3aa3 Reviewed-by: J-P Nurmi --- src/imports/shapes/qquickshape.cpp | 52 +++++++++++++++++++------------------- src/imports/shapes/qquickshape_p.h | 6 ++--- 2 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 3c253eabd4..0d060242b4 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -588,6 +588,11 @@ void QQuickShapePath::resetFillGradient() \image pathitem-code-example.png + Like \l Item, Shape also allows any visual or non-visual objects to be + declared as children. ShapePath objects are handled specially. This is + useful since it allows adding visual items, like \l Rectangle or \l Image, + and non-visual objects, like \l Timer directly as children of Shape. + \note It is important to be aware of performance implications, in particular when the application is running on the generic Shape implementation due to not having support for accelerated path rendering. The geometry generation @@ -788,31 +793,23 @@ QQuickShape::Status QQuickShape::status() const return d->status; } -static QQuickShapePath *vpe_at(QQmlListProperty *property, int index) -{ - QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); - return d->qmlData.sp.at(index); -} - -static void vpe_append(QQmlListProperty *property, QQuickShapePath *obj) +static void vpe_append(QQmlListProperty *property, QObject *obj) { QQuickShape *item = static_cast(property->object); QQuickShapePrivate *d = QQuickShapePrivate::get(item); - d->qmlData.sp.append(obj); + QQuickShapePath *path = qobject_cast(obj); + if (path) + d->qmlData.sp.append(path); + + QQuickItemPrivate::data_append(property, obj); - if (d->componentComplete) { - QObject::connect(obj, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); + if (path && d->componentComplete) { + QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); } } -static int vpe_count(QQmlListProperty *property) -{ - QQuickShapePrivate *d = QQuickShapePrivate::get(static_cast(property->object)); - return d->qmlData.sp.count(); -} - -static void vpe_clear(QQmlListProperty *property) +static void vpe_clear(QQmlListProperty *property) { QQuickShape *item = static_cast(property->object); QQuickShapePrivate *d = QQuickShapePrivate::get(item); @@ -822,27 +819,30 @@ static void vpe_clear(QQmlListProperty *property) d->qmlData.sp.clear(); + QQuickItemPrivate::data_clear(property); + if (d->componentComplete) d->_q_shapePathChanged(); } /*! - \qmlproperty list QtQuick.Shapes::Shape::elements + \qmlproperty list QtQuick.Shapes::Shape::data This property holds the ShapePath objects that define the contents of the - Shape. + Shape. It can also contain any other type of objects, since Shape, like Item, + allows adding any visual or non-visual objects as children. \default */ -QQmlListProperty QQuickShape::elements() +QQmlListProperty QQuickShape::data() { - return QQmlListProperty(this, - nullptr, - vpe_append, - vpe_count, - vpe_at, - vpe_clear); + return QQmlListProperty(this, + nullptr, + vpe_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + vpe_clear); } void QQuickShape::classBegin() diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 50b242e492..c5096be229 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -227,8 +227,8 @@ class QQuickShape : public QQuickItem Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QQmlListProperty elements READ elements) - Q_CLASSINFO("DefaultProperty", "elements") + Q_PROPERTY(QQmlListProperty data READ data) + Q_CLASSINFO("DefaultProperty", "data") public: enum RendererType { @@ -259,7 +259,7 @@ public: Status status() const; - QQmlListProperty elements(); + QQmlListProperty data(); protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override; -- cgit v1.2.3 From eeb320bbd8763f3e72f79369cc3908e999a0da3c Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Thu, 2 Mar 2017 15:25:07 +0100 Subject: Delay the deletion of QSGTextures until all windows are synchronized With the 'basic' and the 'windows' render loop the scene graph context is shared. Because of this we cannot start deleting textures after the first window is synchronized as it may contain textures needed by the another window, which is not yet synchronized. QWindowPrivate::syncSceneGraph() is not calling endSync() anymore as it doesn't know whether it is the last window or not. Instead the renderloop is now responsible for calling endSync() once this is safe to do. Change-Id: Icb50ebfb447c928e38b41df7e26f3bfafdb4a811 Reviewed-by: Robert Griebl Reviewed-by: Gunnar Sletta --- src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp | 1 + src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp | 2 ++ src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp | 1 + src/quick/items/qquickrendercontrol.cpp | 3 +++ src/quick/items/qquickwindow.cpp | 2 -- .../adaptations/software/qsgsoftwarerenderloop.cpp | 1 + .../adaptations/software/qsgsoftwarethreadedrenderloop.cpp | 2 ++ src/quick/scenegraph/qsgrenderloop.cpp | 12 ++++++++++++ src/quick/scenegraph/qsgthreadedrenderloop.cpp | 1 + src/quick/scenegraph/qsgwindowsrenderloop.cpp | 10 ++++++++++ 10 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 60b76deb2e..0d3f78d95d 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -461,6 +461,7 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) data.rc->initialize(nullptr); wd->syncSceneGraph(); + data.rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 11cc257103..120a84566f 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -410,6 +410,7 @@ bool QSGD3D12RenderThread::event(QEvent *e) QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); wd->renderSceneGraph(wme->window->size()); *wme->image = engine->executeAndWaitReadbackRenderTarget(); } @@ -545,6 +546,7 @@ void QSGD3D12RenderThread::sync(bool inExpose) rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); if (!hadRenderer && wd->renderer) { if (Q_UNLIKELY(debug_loop())) diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp index f7aa704095..d31156f0eb 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp @@ -205,6 +205,7 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) emit window->afterAnimating(); cd->syncSceneGraph(); + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index f2828bbedd..7e995936af 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -284,6 +284,7 @@ bool QQuickRenderControl::sync() QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); cd->syncSceneGraph(); + d->rc->endSync(); // TODO: find out if the sync actually caused a scenegraph update. return true; @@ -383,6 +384,7 @@ QImage QQuickRenderControl::grab() QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); cd->polishItems(); cd->syncSceneGraph(); + d->rc->endSync(); render(); grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false); if (QQuickRenderControl::renderWindowFor(d->window)) { @@ -402,6 +404,7 @@ QImage QQuickRenderControl::grab() softwareRenderer->markDirty(); cd->polishItems(); cd->syncSceneGraph(); + d->rc->endSync(); render(); softwareRenderer->setCurrentPaintDevice(prevDev); } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index d30f1c3f78..433c5b25b1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -432,10 +432,8 @@ void QQuickWindowPrivate::syncSceneGraph() emit q->afterSynchronizing(); runAndClearJobs(&afterSynchronizingJobs); - context->endSync(); } - void QQuickWindowPrivate::renderSceneGraph(const QSize &size) { QML_MEMORY_SCOPE_STRING("SceneGraph"); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 962db20cbc..3f0d1383b9 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -149,6 +149,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) emit window->afterAnimating(); cd->syncSceneGraph(); + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp index 71db35377e..19f16a79fb 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -330,6 +330,7 @@ bool QSGSoftwareRenderThread::event(QEvent *e) softwareRenderer->setBackingStore(backingStore); rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); wd->renderSceneGraph(wme->window->size()); *wme->image = backingStore->handle()->toImage(); } @@ -443,6 +444,7 @@ void QSGSoftwareRenderThread::sync(bool inExpose) rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); if (!hadRenderer && wd->renderer) { qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer"); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index c27700cf84..bc65dc1bc3 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -380,6 +380,16 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) bool alsoSwap = data.updatePending; data.updatePending = false; + bool lastDirtyWindow = true; + auto i = m_windows.constBegin(); + while (i != m_windows.constEnd()) { + if (i.value().updatePending) { + lastDirtyWindow = false; + break; + } + i++; + } + if (!current) return; @@ -407,6 +417,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) emit window->afterAnimating(); cd->syncSceneGraph(); + if (lastDirtyWindow) + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 2364fb714c..3a8e673c0d 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -425,6 +425,7 @@ bool QSGRenderThread::event(QEvent *e) qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph"; QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window); d->syncSceneGraph(); + sgrc->endSync(); qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph"; QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size()); diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index e16f7ea966..e10e52d95e 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -445,6 +445,14 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) } } + bool lastDirtyWindow = true; + for (int i=0; iflushFrameSynchronousEvents(); // Event delivery or processing has caused the window to stop rendering. if (!windowData(window)) @@ -464,6 +472,8 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) RLDEBUG(" - syncing"); d->syncSceneGraph(); + if (lastDirtyWindow) + m_rc->endSync(); QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_synced, QQuickProfiler::SceneGraphRenderLoopSync); -- cgit v1.2.3 From 5935488d46d2e78d72b4ff61a56bc5e7448c2002 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Thu, 15 Jun 2017 14:36:56 +0200 Subject: Doc: Avoid copyright text in the codeblock by using \snippet Change-Id: Ib6f1a1e796a085d0f274c7e87d4ed1314e958a06 Reviewed-by: Nico Vertriest Reviewed-by: Mitch Curtis --- src/qml/doc/snippets/code/backend/backend.cpp | 3 ++- src/qml/doc/snippets/code/backend/backend.h | 3 ++- src/qml/doc/snippets/code/backend/main.cpp | 3 ++- src/qml/doc/snippets/code/backend/main.qml | 4 ++-- src/qml/doc/src/cppintegration/topic.qdoc | 8 ++++---- 5 files changed, 12 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/qml/doc/snippets/code/backend/backend.cpp b/src/qml/doc/snippets/code/backend/backend.cpp index 4a7ee89cec..58f5a15e2a 100644 --- a/src/qml/doc/snippets/code/backend/backend.cpp +++ b/src/qml/doc/snippets/code/backend/backend.cpp @@ -47,7 +47,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +//! [backend_cpp] #include "backend.h" BackEnd::BackEnd(QObject *parent) : @@ -68,3 +68,4 @@ void BackEnd::setUserName(const QString &userName) m_userName = userName; emit userNameChanged(); } +//! [backend_cpp] diff --git a/src/qml/doc/snippets/code/backend/backend.h b/src/qml/doc/snippets/code/backend/backend.h index 91bb766e1f..fa7ce9eb86 100644 --- a/src/qml/doc/snippets/code/backend/backend.h +++ b/src/qml/doc/snippets/code/backend/backend.h @@ -47,7 +47,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +//! [backend_header] #ifndef BACKEND_H #define BACKEND_H @@ -73,3 +73,4 @@ private: }; #endif // BACKEND_H +//! [backend_header] diff --git a/src/qml/doc/snippets/code/backend/main.cpp b/src/qml/doc/snippets/code/backend/main.cpp index d7a1bcbd4f..91a012dfda 100644 --- a/src/qml/doc/snippets/code/backend/main.cpp +++ b/src/qml/doc/snippets/code/backend/main.cpp @@ -47,7 +47,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +//! [main_cpp] #include #include @@ -64,3 +64,4 @@ int main(int argc, char *argv[]) return app.exec(); } +//! [main_cpp] diff --git a/src/qml/doc/snippets/code/backend/main.qml b/src/qml/doc/snippets/code/backend/main.qml index 3720da8412..fadc9cd768 100644 --- a/src/qml/doc/snippets/code/backend/main.qml +++ b/src/qml/doc/snippets/code/backend/main.qml @@ -47,7 +47,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - +//! [main_qml] import QtQuick 2.6 import QtQuick.Controls 2.0 //![import] @@ -76,4 +76,4 @@ ApplicationWindow { } //![username_input] } - +//! [main_qml] diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index 22115395b1..183af25297 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -46,13 +46,13 @@ BackEnd, in a QML application: \li Add a new C++ class called \c BackEnd to the project and replace its header file contents with: -\quotefile code/backend/backend.h +\snippet code/backend/backend.h backend_header The \c Q_PROPERTY macro declares a property that could be accessed from QML. \li Replace its C++ file contents with: -\quotefile code/backend/backend.cpp +\snippet code/backend/backend.cpp backend_cpp The \c setUserName function emits the \c userNameChanged signal every time \c m_userName value changes. The signal can be handled from QML using the @@ -61,14 +61,14 @@ The \c setUserName function emits the \c userNameChanged signal every time \li Include \c "backend.h" in \c main.cpp and register the class as a QML type under a import URL as shown below: -\quotefile code/backend/main.cpp +\snippet code/backend/main.cpp main_cpp The BackEnd class is registered as a type, which is accessible from QML by importing the URL, "\c{io.qt.examples.backend 1.0}". \li Replace the contents of \c main.qml with the following code: -\quotefile code/backend/main.qml +\snippet code/backend/main.qml main_qml The \c BackEnd instance lets you access the \c userName property, which is updated when the TextField's \c text property changes. -- cgit v1.2.3 From b66bc399aed8b307a27781c97585a09888d8692f Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Thu, 15 Jun 2017 13:38:43 +0200 Subject: testlib: print out milliseconds for datetime Task-number: QTBUG-32555 Change-Id: I9219c8a7199d4db27c3d160de2544f0a7cb320a6 Reviewed-by: Mitch Curtis Reviewed-by: Edward Welbourne --- src/qmltest/quicktestresult.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index c4fb2b0f5f..8c62196128 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -518,6 +518,12 @@ void QuickTestResult::stringify(QQmlV4Function *args) result = QString::fromLatin1("Qt.url(%1)").arg(url.toString()); break; } + case QVariant::DateTime: + { + QDateTime dt = v.value(); + result = dt.toString(Qt::ISODateWithMs); + break; + } default: result = v.toString(); } -- cgit v1.2.3 From 3fc212f7cad26e310e3f560a9416ad4dfbcaca00 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 16 Jun 2017 10:12:59 +0200 Subject: Doc: Enclose regular expressions with \badcode Without them, qdoc tries to parse the backslashes as qdoc commands: src/quick/util/qquickvalidator.cpp:231: warning: Unknown command '\d' Change-Id: I36322586c477822f7efbae8b80adaee177c7ca44 Reviewed-by: Venugopal Shivashankar --- src/quick/util/qquickvalidator.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp index 93f414fe80..c3ce149dcf 100644 --- a/src/quick/util/qquickvalidator.cpp +++ b/src/quick/util/qquickvalidator.cpp @@ -228,9 +228,15 @@ void QQuickDoubleValidator::resetLocaleName() \list \li A list of numbers with one to three positions separated by a comma: + \badcode /\d{1,3}(?:,\d{1,3})+$/ + \endcode + \li An amount consisting of up to 3 numbers before the decimal point, and - 1 to 2 after the decimal point: \li /(\d{1,3})([.,]\d{1,2})?$/ + 1 to 2 after the decimal point: + \badcode + /(\d{1,3})([.,]\d{1,2})?$/ + \endcode \endlist */ -- cgit v1.2.3 From 1e0685136d0debb2a3b62d9f4650c95afe41913b Mon Sep 17 00:00:00 2001 From: Colin Ogilvie Date: Fri, 2 Jun 2017 11:37:09 +0100 Subject: Don't leak QQmlJavaScriptExpression errors Ensure any error is deleted when the expression is Change-Id: Ibbfd28f50279d4c66830b40c5c917eb8d98f266e Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlexpression.cpp | 1 - src/qml/qml/qqmljavascriptexpression.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index b70db5ed86..1e1fbcf448 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -202,7 +202,6 @@ QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, */ QQmlExpression::~QQmlExpression() { - clearError(); } /*! diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 17cccc0bbd..9d4e46e254 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -111,6 +111,7 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() clearActiveGuards(); clearPermanentGuards(); + clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; -- cgit v1.2.3 From 4199572d64d46bfa2efdcf7c910e81e5b8fb5547 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Fri, 16 Jun 2017 22:51:57 +0300 Subject: Fix QML compiler crashes on big endian systems Commit be491913c036b148 changed QV4::CompiledData::Unit to use LEUInt32 structures internally, rather than native uints, however the generators were not updated at that time and still wrote native uints. Also initialize constants field of CompilationUnit to prevent crashes in unlink() where operator delete[] is called. Change-Id: Id6c6e6ad519c9927ba6027479689ecfde9ea86de Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 8 ++++---- src/qml/compiler/qv4compileddata.cpp | 2 ++ src/qml/compiler/qv4compiler.cpp | 10 +++++----- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 57cb4c607c..03a71768d8 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1425,7 +1425,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: } // write objects - quint32 *objectTable = reinterpret_cast(data + qmlUnit->offsetToObjects); + QV4::CompiledData::LEUInt32 *objectTable = reinterpret_cast(data + qmlUnit->offsetToObjects); char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; for (int i = 0; i < output.objects.count(); ++i) { const Object *o = output.objects.at(i); @@ -1467,7 +1467,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: objectToWrite->offsetToNamedObjectsInComponent = nextOffset; nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); - quint32 *functionsTable = reinterpret_cast(objectPtr + objectToWrite->offsetToFunctions); + QV4::CompiledData::LEUInt32 *functionsTable = reinterpret_cast(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = o->runtimeFunctionIndices.at(f->index); @@ -1493,7 +1493,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias); Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount())); - quint32 *signalOffsetTable = reinterpret_cast(objectPtr + objectToWrite->offsetToSignals); + QV4::CompiledData::LEUInt32 *signalOffsetTable = reinterpret_cast(objectPtr + objectToWrite->offsetToSignals); quint32 signalTableSize = 0; char *signalPtr = objectPtr + nextOffset; for (const Signal *s = o->firstSignal(); s; s = s->next) { @@ -1513,7 +1513,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: signalPtr += size; } - quint32 *namedObjectInComponentPtr = reinterpret_cast(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); + QV4::CompiledData::LEUInt32 *namedObjectInComponentPtr = reinterpret_cast(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 485a5e6fb7..db707061fe 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -99,6 +99,7 @@ CompilationUnit::CompilationUnit() , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) + , constants(nullptr) , totalBindingsCount(0) , totalParserStatusCount(0) , totalObjectCount(0) @@ -239,6 +240,7 @@ void CompilationUnit::unlink() runtimeFunctions.clear(); #if Q_BYTE_ORDER == Q_BIG_ENDIAN delete [] constants; + constants = nullptr; #endif } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index e32749bbf7..de1b835edb 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -335,29 +335,29 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->codeSize = 0; // write formals - quint32 *formals = (quint32 *)(f + function->formalsOffset); + CompiledData::LEUInt32 *formals = (CompiledData::LEUInt32 *)(f + function->formalsOffset); for (int i = 0; i < irFunction->formals.size(); ++i) formals[i] = getStringId(*irFunction->formals.at(i)); // write locals - quint32 *locals = (quint32 *)(f + function->localsOffset); + CompiledData::LEUInt32 *locals = (CompiledData::LEUInt32 *)(f + function->localsOffset); for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(*irFunction->locals.at(i)); // write QML dependencies - quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset); + CompiledData::LEUInt32 *writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingIdObjectsOffset); for (int id : irFunction->idObjectDependencies) { Q_ASSERT(id >= 0); *writtenDeps++ = static_cast(id); } - writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset); + writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingContextPropertiesOffset); for (auto property : irFunction->contextObjectPropertyDependencies) { *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } - writtenDeps = (quint32 *)(f + function->dependingScopePropertiesOffset); + writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingScopePropertiesOffset); for (auto property : irFunction->scopeObjectPropertyDependencies) { *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index -- cgit v1.2.3 From 22b7ac33a10a2a9767664efece2e74a70d26c798 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 16 Jun 2017 08:03:45 +0200 Subject: Fix excessive recursion in renderer Change-Id: Iffee781932773fe22c7d946b532ba74492e1e2df Task-number: QTBUG-59789 Reviewed-by: Robin Burchell --- src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 78f2c86f6c..edee29584c 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -969,9 +969,10 @@ bool Renderer::changeBatchRoot(Node *node, Node *root) void Renderer::nodeChangedBatchRoot(Node *node, Node *root) { if (node->type() == QSGNode::ClipNodeType || node->isBatchRoot) { - if (!changeBatchRoot(node, root)) - return; - node = root; + // When we reach a batchroot, we only need to update it. Its subtree + // is relative to that root, so no need to recurse further. + changeBatchRoot(node, root); + return; } else if (node->type() == QSGNode::GeometryNodeType) { // Only need to change the root as nodeChanged anyway flags a full update. Element *e = node->element(); -- cgit v1.2.3 From df52c3e6a1489e68805f6ea025dc952ca4e21714 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 16 Jun 2017 11:32:51 +0200 Subject: Rename to vendorExtensionsEnable Fits existing Quick item naming patterns better. Change-Id: Id6d8f3653b33b1c1963bda4a2bcc212761e74caa Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 10 +++++----- src/imports/shapes/qquickshape_p.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 0d060242b4..4e6896621e 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -623,7 +623,7 @@ void QQuickShapePath::resetFillGradient() \c{GL_NV_path_rendering} methods are available. The choice is made at runtime, depending on the graphics driver's capabilities. When this is not desired, applications can force using the generic method by setting the - Shape.enableVendorExtensions property to \c false. + Shape.vendorExtensionsEnabled property to \c false. \li The \c software backend is fully supported. The path is rendered via QPainter::strokePath() and QPainter::fillPath() in this case. @@ -745,7 +745,7 @@ void QQuickShape::setAsynchronous(bool async) } /*! - \qmlproperty bool QtQuick.Shapes::Shape::enableVendorExtensions + \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled This property controls the usage of non-standard OpenGL extensions like GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform @@ -755,18 +755,18 @@ void QQuickShape::setAsynchronous(bool async) The default value is \c true. */ -bool QQuickShape::enableVendorExtensions() const +bool QQuickShape::vendorExtensionsEnabled() const { Q_D(const QQuickShape); return d->enableVendorExts; } -void QQuickShape::setEnableVendorExtensions(bool enable) +void QQuickShape::setVendorExtensionsEnabled(bool enable) { Q_D(QQuickShape); if (d->enableVendorExts != enable) { d->enableVendorExts = enable; - emit enableVendorExtensionsChanged(); + emit vendorExtensionsEnabledChanged(); } } diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index c5096be229..db0b449c6c 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -225,7 +225,7 @@ class QQuickShape : public QQuickItem Q_OBJECT Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool enableVendorExtensions READ enableVendorExtensions WRITE setEnableVendorExtensions NOTIFY enableVendorExtensionsChanged) + Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QQmlListProperty data READ data) Q_CLASSINFO("DefaultProperty", "data") @@ -254,8 +254,8 @@ public: bool asynchronous() const; void setAsynchronous(bool async); - bool enableVendorExtensions() const; - void setEnableVendorExtensions(bool enable); + bool vendorExtensionsEnabled() const; + void setVendorExtensionsEnabled(bool enable); Status status() const; @@ -271,7 +271,7 @@ protected: Q_SIGNALS: void rendererChanged(); void asynchronousChanged(); - void enableVendorExtensionsChanged(); + void vendorExtensionsEnabledChanged(); void statusChanged(); private: -- cgit v1.2.3 From ee076afedccbe1d37306a7972051f84eb036d655 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 16 Jun 2017 11:36:39 +0200 Subject: Path and PathMove doc fixes Change-Id: Ife5f290d6860d2e0a41b61da1f7cc2bc995a4079 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/util/qquickpath.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 0103f66814..15defdc01b 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -124,7 +124,7 @@ QT_BEGIN_NAMESPACE \li PathSvg \li Yes \li Yes - \li Yes, with limitations + \li Yes \li Yes \row \li PathAttribute @@ -1124,13 +1124,6 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) \ingroup qtquick-animation-paths \brief Moves the Path's position - While not relevant with PathView, for Path elements used with Shape it - is important to distinguish between the operations of drawing a straight - line and moving the path position without drawing anything. - - \note PathMove should not be used in a Path associated with a PathView. Use - PathLine instead. - The example below creates a path consisting of two horizontal lines with some empty space between them. All three segments have a width of 100: @@ -1143,6 +1136,11 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data) } \endqml + \note PathMove should not be used in a Path associated with a PathView. Use + PathLine instead. For ShapePath however it is important to distinguish + between the operations of drawing a straight line and moving the path + position without drawing anything. + \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine */ @@ -1930,10 +1928,10 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data) \endqml \endtable - \note Mixing PathSvg with other type of elements is not always supported, - therefore it is strongly recommended to avoid this. For example, when - \l Shape is backed by \c{GL_NV_path_rendering}, a Path can contain one or - more PathSvg elements, or one or more other type of elements, but not both. + \note Mixing PathSvg with other type of elements is not always supported. + For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a + ShapePath can contain one or more PathSvg elements, or one or more other + type of elements, but not both. \sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve */ -- cgit v1.2.3 From 9ac29fcc88a49ef2ffa4639664c906c5d73b57c9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 12 Jun 2017 17:43:54 +0200 Subject: Silence -Wuninitialized warning Task-number: QTBUG-61089 Change-Id: I8b1fb03d040b04b3b14f371bf1a5ba8c2318054f Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira (cherry picked from commit 784ea8c09d448a418b3128be8bee14d9535e36c9) --- src/qml/compiler/qv4instr_moth_p.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index dabda7bae8..5f46e90ec7 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -897,6 +897,8 @@ template struct InstrMeta { }; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wuninitialized") #define MOTH_INSTR_META_TEMPLATE(I, FMT) \ template<> struct InstrMeta<(int)Instr::I> { \ enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ @@ -910,6 +912,7 @@ struct InstrMeta { }; FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); #undef MOTH_INSTR_META_TEMPLATE +QT_WARNING_POP template class InstrData : public InstrMeta::DataType -- cgit v1.2.3 From 0e73e58f7e8afc4a31c02fe74ddbb3a6781d8144 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 16 Jun 2017 15:06:27 +0200 Subject: Doc: Fix link to Window.screen src/qml/qml/qqmlengine.cpp:455: warning: Can't link to 'QtQuick::Window::screen' Change-Id: I3f662ff574673d86ca048aec709948b236c17fd9 Reviewed-by: Nico Vertriest --- src/qml/qml/qqmlengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f0564e7b33..ba22bfde76 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -590,7 +590,7 @@ The following functions are also on the Qt object. \li application.font \endlist - \sa Screen, Window, {QtQuick::Window::screen}{Window.screen} + \sa Screen, Window, {QtQuick.Window::Window::screen}{Window.screen} */ /*! -- cgit v1.2.3 From f2e44812a739b9f481213c67e7683b99676f2593 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Jun 2017 11:38:35 +0200 Subject: Prospective build fix for Integrity OS If ExecutionEngine is forward declared and the compiler tries to instantiate the Value/Array/etc. templates early on, it may not be able to map ExecutionEngine to EngineBase. That is what the error message suggests. Since we don't need ExecutionEngine let's try EngineBase. Change-Id: Idd18dd431705cce8df79180e7ac08574bbe1170c Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata_p.h | 12 ++++++------ src/qml/jsruntime/qv4memberdata_p.h | 4 ++-- src/qml/memory/qv4writebarrier_p.h | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 1d895c90c5..e1de2e82e6 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -107,7 +107,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { Heap::ArrayData *arrayData; uint index; - void set(ExecutionEngine *e, Value newVal) { + void set(EngineBase *e, Value newVal) { arrayData->values.set(e, index, newVal); } const Value *operator->() const { return &arrayData->values[index]; } @@ -123,7 +123,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { return vtable()->get(this, i); } inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs); - inline void setProperty(ExecutionEngine *e, uint index, const Property *p); + inline void setProperty(EngineBase *e, uint index, const Property *p); inline Index getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; @@ -135,7 +135,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { return vtable()->length(this); } - void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + void setArrayData(EngineBase *e, uint index, Value newVal) { values.set(e, index, newVal); } @@ -146,7 +146,7 @@ V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { uint mappedIndex(uint index) const { return (index + offset) % values.alloc; } const Value &data(uint index) const { return values[mappedIndex(index)]; } - void setData(ExecutionEngine *e, uint index, Value newVal) { + void setData(EngineBase *e, uint index, Value newVal) { values.set(e, mappedIndex(index), newVal); } @@ -197,7 +197,7 @@ struct Q_QML_EXPORT ArrayData : public Managed PropertyAttributes *attrs() const { return d()->attrs; } void setAttrs(PropertyAttributes *a) { d()->attrs = a; } const Value *arrayData() const { return d()->values.data(); } - void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + void setArrayData(EngineBase *e, uint index, Value newVal) { d()->setArrayData(e, index, newVal); } @@ -310,7 +310,7 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) return true; } -void ArrayData::setProperty(QV4::ExecutionEngine *e, uint index, const Property *p) +void ArrayData::setProperty(QV4::EngineBase *e, uint index, const Property *p) { uint mapped = mappedIndex(index); Q_ASSERT(mapped != UINT_MAX); diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 9d333011fc..bae524d088 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -88,8 +88,8 @@ struct MemberData : Managed const Value &operator[] (uint idx) const { return d()->values[idx]; } const Value *data() const { return d()->values.data(); } - void set(ExecutionEngine *e, uint index, Value v) { d()->values.set(e, index, v); } - void set(ExecutionEngine *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); } + void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); } + void set(EngineBase *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); } inline uint size() const { return d()->values.size; } diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h index 3182fd2aa9..86fd28000d 100644 --- a/src/qml/memory/qv4writebarrier_p.h +++ b/src/qml/memory/qv4writebarrier_p.h @@ -123,7 +123,7 @@ struct Pointer { return base; } - void set(ExecutionEngine *e, T newVal) { + void set(EngineBase *e, T newVal) { WriteBarrier::write(e, base(), reinterpret_cast(&ptr), reinterpret_cast(newVal)); } @@ -146,10 +146,10 @@ struct HeapValue : Value { return base; } - void set(ExecutionEngine *e, const Value &newVal) { + void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), this, newVal); } - void set(ExecutionEngine *e, Heap::Base *b) { + void set(EngineBase *e, Heap::Base *b) { WriteBarrier::write(e, base(), this, b); } }; @@ -166,10 +166,10 @@ struct ValueArray { return base; } - void set(ExecutionEngine *e, uint index, Value v) { + void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values + index, v); } - void set(ExecutionEngine *e, uint index, Heap::Base *b) { + void set(EngineBase *e, uint index, Heap::Base *b) { WriteBarrier::write(e, base(), values + index, b); } inline const Value &operator[] (uint index) const { @@ -180,13 +180,13 @@ struct ValueArray { return values; } - void insertData(ExecutionEngine *e, uint index, Value v) { + void insertData(EngineBase *e, uint index, Value v) { for (uint i = size - 1; i > index; --i) { values[i] = values[i - 1]; } set(e, index, v); } - void removeData(ExecutionEngine *e, uint index, int n = 1) { + void removeData(EngineBase *e, uint index, int n = 1) { Q_UNUSED(e); for (uint i = index; i < size - n; ++i) { values[i] = values[i + n]; -- cgit v1.2.3 From 87d9baa677edd7aee5c3c6544b4fc1770f7e5ac8 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 1 Jun 2017 14:52:23 +0200 Subject: QQuickImplicitSizeItem: don't redefine implicit size change signals f7ada9b9 redefined QQuickItem's implicitWidthChanged and implicitHeightChanged signals in order to avoid warnings. 2ca187ca makes it possible to reuse the base class' signals as long as they don't have parameters. This change should be transparent to users. [ChangeLog][QtQuick][Important Behavior Change] The implicitWidthChanged2 and implicitHeightChanged2 signals of QQuickImplicitSizeItem have been removed. Although these were undocumented and hence private API, any applications currently using them should use the public implicitWidthChanged and implicitHeightChanged signals. Task-number: QTCREATORBUG-15000 Change-Id: I35cfdefc6c992f4529b60c979265650c91ba8549 Reviewed-by: Albert Astals Cid Reviewed-by: J-P Nurmi Reviewed-by: Robin Burchell --- src/quick/items/qquickimplicitsizeitem.cpp | 26 ++++---------------------- src/quick/items/qquickimplicitsizeitem_p.h | 8 ++------ src/quick/items/qquickimplicitsizeitem_p_p.h | 3 --- 3 files changed, 6 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp index 1996fb9489..2569b2a224 100644 --- a/src/quick/items/qquickimplicitsizeitem.cpp +++ b/src/quick/items/qquickimplicitsizeitem.cpp @@ -45,29 +45,11 @@ QT_BEGIN_NAMESPACE /*! \internal - The purpose of QQuickImplicitSizeItem is not immediately clear, as both - the implicit size properties and signals exist on QQuickItem. However, - for some items - where the implicit size has an underlying meaning (such as - Image, where the implicit size represents the real size of the image) - having implicit size writable is an undesirable thing. - - QQuickImplicitSizeItem redefines the properties as being readonly. - Unfortunately, this also means they need to redefine the change signals. - See QTBUG-30258 for more information. + QQuickImplicitSizeItem redefines the implicitWidth and implicitHeight + properties as readonly, as some items (e.g. Image, where the implicit size + represents the real size of the image) should not be able to have their + implicit size modified. */ -void QQuickImplicitSizeItemPrivate::implicitWidthChanged() -{ - Q_Q(QQuickImplicitSizeItem); - QQuickItemPrivate::implicitWidthChanged(); - emit q->implicitWidthChanged2(); -} - -void QQuickImplicitSizeItemPrivate::implicitHeightChanged() -{ - Q_Q(QQuickImplicitSizeItem); - QQuickItemPrivate::implicitHeightChanged(); - emit q->implicitHeightChanged2(); -} QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent) : QQuickItem(dd, parent) diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h index 75b04449f8..8ae8f9f447 100644 --- a/src/quick/items/qquickimplicitsizeitem_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p.h @@ -60,16 +60,12 @@ class QQuickImplicitSizeItemPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem { Q_OBJECT - Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged2) - Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged2) + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) protected: QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent); -Q_SIGNALS: - Q_REVISION(1) void implicitWidthChanged2(); - Q_REVISION(1) void implicitHeightChanged2(); - private: Q_DISABLE_COPY(QQuickImplicitSizeItem) Q_DECLARE_PRIVATE(QQuickImplicitSizeItem) diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h index 2c42fa62f2..0495cf87e1 100644 --- a/src/quick/items/qquickimplicitsizeitem_p_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p_p.h @@ -65,9 +65,6 @@ public: QQuickImplicitSizeItemPrivate() { } - - void implicitWidthChanged() Q_DECL_OVERRIDE; - void implicitHeightChanged() Q_DECL_OVERRIDE; }; QT_END_NAMESPACE -- cgit v1.2.3 From 463a1ed22a10044b5e719a84abc47c8270b70efd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Jun 2017 14:25:53 +0200 Subject: Fix lookup of formals in QML signal handlers with AOT Partially revert commit 38221427bc21a11b96de7fa7666264c34298c0c0 to allow for the lookup of formals by name even when using the simple call context. Task-number: QTBUG-61531 Change-Id: Ic5b235b62949ce050817ef2937bd4a35dd64aa6a Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4context.cpp | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index ba72a9e43b..6807c835b0 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -237,16 +237,14 @@ bool ExecutionContext::deleteProperty(String *name) return global->deleteProperty(name); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); ScopedObject qml(scope, c->activation); if (qml && qml->hasProperty(name)) return qml->deleteProperty(name); @@ -418,26 +416,21 @@ ReturnedValue ExecutionContext::getProperty(String *name) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); + name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } - if (c->v4Function->isNamedExpression()) { - if (c->function && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); - } - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -445,6 +438,12 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) return v->asReturnedValue(); } + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -498,25 +497,21 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: { - Heap::CallContext *c = static_cast(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast(ctx->d()); + name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - if (c->v4Function->isNamedExpression()) { - if (c->function && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } - Q_FALLTHROUGH(); - } - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast(ctx->d()); + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -524,6 +519,12 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (hasProperty) return v->asReturnedValue(); } + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { -- cgit v1.2.3 From aa048e7a6c4c1b72839cea95c386c6ccc12be420 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 2 Jun 2017 08:01:18 +0200 Subject: Update plugins.qmltypes Task-number: QTCREATORBUG-15000 Change-Id: Ia184b442d2adff9a92878f80355b01e79114132b Reviewed-by: J-P Nurmi --- src/imports/qtquick2/plugins.qmltypes | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index 834b4bfac2..73d6d8ec68 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.9' +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.10' Module { dependencies: [] @@ -1517,8 +1517,12 @@ Module { name: "QQuickFlickable" defaultProperty: "flickableData" prototype: "QQuickItem" - exports: ["QtQuick/Flickable 2.0", "QtQuick/Flickable 2.9"] - exportMetaObjectRevisions: [0, 9] + exports: [ + "QtQuick/Flickable 2.0", + "QtQuick/Flickable 2.10", + "QtQuick/Flickable 2.9" + ] + exportMetaObjectRevisions: [0, 10, 9] Enum { name: "BoundsBehavior" values: { @@ -1528,6 +1532,12 @@ Module { "DragAndOvershootBounds": 3 } } + Enum { + name: "BoundsMovement" + values: { + "FollowBoundsBehavior": 1 + } + } Enum { name: "FlickableDirection" values: { @@ -1552,6 +1562,7 @@ Module { Property { name: "horizontalVelocity"; type: "double"; isReadonly: true } Property { name: "verticalVelocity"; type: "double"; isReadonly: true } Property { name: "boundsBehavior"; type: "BoundsBehavior" } + Property { name: "boundsMovement"; revision: 10; type: "BoundsMovement" } Property { name: "rebound"; type: "QQuickTransition"; isPointer: true } Property { name: "maximumFlickVelocity"; type: "double" } Property { name: "flickDeceleration"; type: "double" } @@ -1583,6 +1594,7 @@ Module { Property { name: "flickableData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "flickableChildren"; type: "QQuickItem"; isList: true; isReadonly: true } Signal { name: "isAtBoundaryChanged" } + Signal { name: "boundsMovementChanged"; revision: 10 } Signal { name: "movementStarted" } Signal { name: "movementEnded" } Signal { name: "flickStarted" } @@ -1811,7 +1823,6 @@ Module { exports: ["QtQuick/Gradient 2.0"] exportMetaObjectRevisions: [0] Property { name: "stops"; type: "QQuickGradientStop"; isList: true; isReadonly: true } - Signal { name: "updated" } } Component { name: "QQuickGradientStop" @@ -2088,8 +2099,6 @@ Module { prototype: "QQuickItem" Property { name: "implicitWidth"; type: "double"; isReadonly: true } Property { name: "implicitHeight"; type: "double"; isReadonly: true } - Signal { name: "implicitWidthChanged2"; revision: 1 } - Signal { name: "implicitHeightChanged2"; revision: 1 } } Component { name: "QQuickIntValidator" @@ -2300,6 +2309,7 @@ Module { Property { name: "samplerName"; type: "QByteArray" } Property { name: "effect"; type: "QQmlComponent"; isPointer: true } Property { name: "textureMirroring"; type: "QQuickShaderEffectSource::TextureMirroring" } + Property { name: "samples"; type: "int" } Signal { name: "enabledChanged" Parameter { name: "enabled"; type: "bool" } @@ -2340,6 +2350,10 @@ Module { name: "textureMirroringChanged" Parameter { name: "mirroring"; type: "QQuickShaderEffectSource::TextureMirroring" } } + Signal { + name: "samplesChanged" + Parameter { name: "count"; type: "int" } + } } Component { name: "QQuickItemView" @@ -3594,9 +3608,10 @@ Module { prototype: "QQuickItem" exports: [ "QtQuick/ShaderEffectSource 2.0", - "QtQuick/ShaderEffectSource 2.6" + "QtQuick/ShaderEffectSource 2.6", + "QtQuick/ShaderEffectSource 2.9" ] - exportMetaObjectRevisions: [0, 1] + exportMetaObjectRevisions: [0, 1, 2] Enum { name: "WrapMode" values: { @@ -3632,6 +3647,7 @@ Module { Property { name: "mipmap"; type: "bool" } Property { name: "recursive"; type: "bool" } Property { name: "textureMirroring"; revision: 1; type: "TextureMirroring" } + Property { name: "samples"; revision: 2; type: "int" } Signal { name: "scheduledUpdateCompleted" } Method { name: "scheduleUpdate" } } @@ -4084,6 +4100,7 @@ Module { Property { name: "rightPadding"; revision: 6; type: "double" } Property { name: "bottomPadding"; revision: 6; type: "double" } Property { name: "fontInfo"; revision: 9; type: "QJSValue"; isReadonly: true } + Property { name: "advance"; revision: 10; type: "QSizeF"; isReadonly: true } Signal { name: "textChanged" Parameter { name: "text"; type: "string" } -- cgit v1.2.3 From 03c2661b1243cc529fc3d8cfa65073f1da420307 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Jun 2017 13:34:09 +0200 Subject: Fix alignment issue on ARMv7 As analyzed in the bug report, it appears that we may get QV4::CompiledData::Function pointers for writing that are not aligned for the 64-bit fields at the beginning. [ChangeLog][QtQml] Fix crash due to misaligned data structures on ARMv7 Task-number: QTBUG-61552 Change-Id: I6b2c166b725496150c8850475577628ccd811d65 Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4compiler.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index e32749bbf7..c32e1685a0 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -406,6 +406,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); + nextOffset = (nextOffset + 7) & ~quint32(0x7); + for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *f = irModule->functions.at(i); functionOffsets[i] = nextOffset; -- cgit v1.2.3 From 8635fb9e0ca9a8a2bb5d42785c53ac4b4eb363b1 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 21 Jun 2017 11:20:14 +0200 Subject: Fix typo in QQuickWindow::setRenderTarget warning Change-Id: Idc4521e142603ee37a71acdae63ec750fa970d71 Reviewed-by: Laszlo Agocs --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 433c5b25b1..31f367ed96 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3428,7 +3428,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo) { Q_D(QQuickWindow); if (d->context && QThread::currentThread() != d->context->thread()) { - qWarning("QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread"); + qWarning("QQuickWindow::setRenderTarget: Cannot set render target from outside the rendering thread"); return; } -- cgit v1.2.3 From dfce0a8feceeb7156eba6ac5d8d3521e3009a583 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 21 Jun 2017 09:03:22 +0200 Subject: Support non-integer pixel-ratio in QQuickWidget Non-integer pixel-ratios always resulted in blurry rendering when QQuickWidget was used, but not with QQuickWindow. Fixed by reading qreal accessor of devicePixelRatio instead. Change-Id: I49f5efcf2da2efc090c00017e68c99c857cd84ef Task-number: QTBUG-61502 Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 2e8623f508..2c3c72d5f1 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -913,9 +913,9 @@ void QQuickWidget::createFramebufferObject() d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height()); if (d->useSoftwareRenderer) { - const QSize imageSize = size() * devicePixelRatio(); + const QSize imageSize = size() * devicePixelRatioF(); d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); - d->softwareImage.setDevicePixelRatio(devicePixelRatio()); + d->softwareImage.setDevicePixelRatio(devicePixelRatioF()); return; } @@ -960,7 +960,7 @@ void QQuickWidget::createFramebufferObject() format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT); #endif - const QSize fboSize = size() * devicePixelRatio(); + const QSize fboSize = size() * devicePixelRatioF(); // Could be a simple hide - show, in which case the previous fbo is just fine. if (!d->fbo || d->fbo->size() != fboSize) { @@ -1181,7 +1181,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) // Software Renderer if (d->useSoftwareRenderer) { needsSync = true; - if (d->softwareImage.size() != size() * devicePixelRatio()) { + if (d->softwareImage.size() != size() * devicePixelRatioF()) { createFramebufferObject(); } } else { @@ -1191,7 +1191,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) // during hide - resize - show sequences and also during application exit. if (!d->fbo && !d->offscreenWindow->openglContext()) return; - if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) { + if (!d->fbo || d->fbo->size() != size() * devicePixelRatioF()) { needsSync = true; createFramebufferObject(); } @@ -1607,10 +1607,12 @@ void QQuickWidget::paintEvent(QPaintEvent *event) //Paint everything painter.drawImage(rect(), d->softwareImage); } else { + QTransform transform; + transform.scale(devicePixelRatioF(), devicePixelRatioF()); //Paint only the updated areas const auto rects = d->updateRegion.rects(); for (auto targetRect : rects) { - auto sourceRect = QRect(targetRect.topLeft() * devicePixelRatio(), targetRect.size() * devicePixelRatio()); + auto sourceRect = transform.mapRect(QRectF(targetRect)); painter.drawImage(targetRect, d->softwareImage, sourceRect); } d->updateRegion = QRegion(); -- cgit v1.2.3 From 717618d0b05b751ab155c55a6c2b03c56cffc99b Mon Sep 17 00:00:00 2001 From: Yevhen Kyriukha Date: Sun, 21 May 2017 23:02:37 +0300 Subject: Support external textures (samplerExternalOES) in ShaderEffect samplerExternalOES type defined in OES_EGL_image_external extension is used on embedded platforms to achieve zero-copy of textures. [ChangeLog][Qt Quick] Added support for samplerExternalOES sampler type in ShaderEffect Task-number: QTBUG-59462 Change-Id: Ib1e864f2e1321949b0a6539d37b92d2b62541ea8 Reviewed-by: Laszlo Agocs --- src/quick/items/qquickopenglshadereffect.cpp | 28 ++++++++++++++---------- src/quick/items/qquickopenglshadereffectnode.cpp | 15 +++++++++---- src/quick/items/qquickopenglshadereffectnode_p.h | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 4fcfe04b55..c3e0ba05bd 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -230,7 +230,7 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, auto mapper = signalMappers[shaderType].at(i); void *a = mapper; QObjectPrivate::disconnect(item, mapper->signalIndex(), &a); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) @@ -272,7 +272,7 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData()); } - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) @@ -335,6 +335,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Q_ASSERT(decl == UniformQualifier); const int sampLen = sizeof("sampler2D") - 1; + const int sampExtLen = sizeof("samplerExternalOES") - 1; const int opLen = sizeof("qt_Opacity") - 1; const int matLen = sizeof("qt_Matrix") - 1; const int srLen = sizeof("qt_SubRect_") - 1; @@ -357,8 +358,12 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, mapper = new QtPrivate::MappedSlotObject([this, mappedId](){ this->mappedPropertyChanged(mappedId); }); - bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; - d.specialType = sampler ? UniformData::Sampler : UniformData::None; + if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) + d.specialType = UniformData::Sampler; + else if (typeLength == sampExtLen && qstrncmp("samplerExternalOES", s + typeIndex, sampExtLen) == 0) + d.specialType = UniformData::SamplerExternal; + else + d.specialType = UniformData::None; d.setValueFromProperty(item, itemMetaObject); } uniformData[shaderType].append(d); @@ -451,7 +456,8 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode int textureProviderCount = 0; for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { - if (uniformData[shaderType].at(i).specialType == UniformData::Sampler) + if (uniformData[shaderType].at(i).specialType == UniformData::Sampler || + uniformData[shaderType].at(i).specialType == UniformData::SamplerExternal) ++textureProviderCount; } material->uniforms[shaderType] = uniformData[shaderType]; @@ -474,7 +480,7 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType != UniformData::Sampler) + if (d.specialType != UniformData::Sampler && d.specialType != UniformData::SamplerExternal) continue; QSGTextureProvider *oldProvider = material->textureProviders.at(index); QSGTextureProvider *newProvider = 0; @@ -513,7 +519,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) QQuickItemPrivate::get(source)->refWindow(window); @@ -524,7 +530,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { const UniformData &d = uniformData[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) QQuickItemPrivate::get(source)->derefWindow(); @@ -539,7 +545,7 @@ void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object) for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { UniformData &d = uniformData[shaderType][i]; - if (d.specialType == UniformData::Sampler && d.value.canConvert()) { + if ((d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) && d.value.canConvert()) { if (qvariant_cast(d.value) == object) d.value = QVariant(); } @@ -554,7 +560,7 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector(d.value) == source) + if ((d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler || d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::SamplerExternal) && qvariant_cast(d.value) == source) return false; } } @@ -568,7 +574,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16); int index = mappedId & 0xffff; UniformData &d = uniformData[shaderType][index]; - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { if (item->window()) diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index e1ea98641d..5dbfee73cb 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -47,6 +47,10 @@ #include #include +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + QT_BEGIN_NAMESPACE static bool hasAtlasTexture(const QVector &textureProviders) @@ -124,7 +128,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { const UniformData &d = material->uniforms[shaderType].at(i); QByteArray name = d.name; - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { program()->setUniformValue(d.name.constData(), textureProviderIndex++); // We don't need to store the sampler uniform locations, since their values // only need to be set once. Look for the "qt_SubRect_" uniforms instead. @@ -143,7 +147,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { const UniformData &d = material->uniforms[shaderType].at(i); int loc = m_uniformLocs[shaderType].at(i); - if (d.specialType == UniformData::Sampler) { + if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { int idx = textureProviderIndex++; functions->glActiveTexture(GL_TEXTURE0 + idx); if (QSGTextureProvider *provider = material->textureProviders.at(idx)) { @@ -164,7 +168,10 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri continue; } } - functions->glBindTexture(GL_TEXTURE_2D, 0); + if (d.specialType == UniformData::Sampler) + functions->glBindTexture(GL_TEXTURE_2D, 0); + else if (d.specialType == UniformData::SamplerExternal) + functions->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); } else if (d.specialType == UniformData::Opacity) { program()->setUniformValue(loc, state.opacity()); } else if (d.specialType == UniformData::Matrix) { @@ -396,7 +403,7 @@ bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformDa if (name != other.name) return false; - if (specialType == UniformData::Sampler) { + if (specialType == UniformData::Sampler || specialType == UniformData::SamplerExternal) { // We can't check the source objects as these live in the GUI thread, // so return true here and rely on the textureProvider check for // equality of these.. diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index aea28e6612..784294d9eb 100644 --- a/src/quick/items/qquickopenglshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -90,7 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMateri public: struct UniformData { - enum SpecialType { None, Sampler, SubRect, Opacity, Matrix }; + enum SpecialType { None, Sampler, SamplerExternal, SubRect, Opacity, Matrix }; QByteArray name; QVariant value; -- cgit v1.2.3 From d5b3f5da9cfa90fc43f29f3bdeec01884a47d6ca Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Fri, 16 Jun 2017 12:06:03 +0200 Subject: Do not crash on Windows It seems that moving the registration to the block where the rest of the type registration was done avoids the crash. According to the documentation, a Q_GADGET doesn't have to be explicitly registered into the meta type system, but I never managed to get that working (and our existing Q_GADGETS seems to require explicit registration too) Change-Id: Iab1c258dbe40bd1c8062fab2c4b68fce208f5144 Reviewed-by: J-P Nurmi --- src/quick/handlers/qquickhandlersmodule.cpp | 1 + src/quick/handlers/qquickpointersinglehandler.cpp | 2 -- src/quick/handlers/qquickpointersinglehandler_p.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp index 4bfdc020d8..4a3a1f6aa2 100644 --- a/src/quick/handlers/qquickhandlersmodule.cpp +++ b/src/quick/handlers/qquickhandlersmodule.cpp @@ -82,6 +82,7 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor) QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler")); qmlRegisterType(uri,major,minor,"PinchHandler"); qmlRegisterType(uri,major,minor,"TapHandler"); + qRegisterMetaType(); } void QQuickHandlersModule::defineModule() diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp index 40a99b0e99..0b0f482d8d 100644 --- a/src/quick/handlers/qquickpointersinglehandler.cpp +++ b/src/quick/handlers/qquickpointersinglehandler.cpp @@ -240,6 +240,4 @@ void QQuickHandlerPoint::reset() m_pressedButtons = Qt::NoButton; } -int g_metaTypeId = qRegisterMetaType(); - QT_END_NAMESPACE diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h index 635c9e0a77..8898a1600b 100644 --- a/src/quick/handlers/qquickpointersinglehandler_p.h +++ b/src/quick/handlers/qquickpointersinglehandler_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE class QQuickPointerSingleHandler; class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint { + Q_GADGET Q_PROPERTY(int id READ id) Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId) Q_PROPERTY(QPointF position READ position) @@ -70,7 +71,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint { Q_PROPERTY(qreal rotation READ rotation) Q_PROPERTY(qreal pressure READ pressure) Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters) - Q_GADGET public: QQuickHandlerPoint(); -- cgit v1.2.3 From 768c4b4526b1e6340587d72b985005da786a8494 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 23 Jun 2017 14:20:50 +0200 Subject: YarrOp: Initialize members Fix GCC 7 warnings: 3rdparty\masm\yarr\YarrJIT.cpp:455:12: error: '.JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)1>::YarrOp::m_alternative' may be used uninitialized in this function [-Werror=maybe-uninitialized] 3rdparty\masm\yarr\YarrJIT.cpp:455:12: error: '.JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)1>::YarrOp::m_previousOp' may be used uninitialized in this function [-Werror=maybe-uninitialized] 3rdparty\masm\yarr\YarrJIT.cpp:455:12: error: '.JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)1>::YarrOp::m_nextOp' may be used uninitialized in this function [-Werror=maybe-uninitialized] 3rdparty\masm\yarr\YarrJIT.cpp:455:12: error: '.JSC::Yarr::YarrGenerator<(JSC::Yarr::YarrJITCompileMode)1>::YarrOp::m_term' may be used uninitialized in this function [-Werror=maybe-uninitialized] Task-number: QTBUG-61558 Change-Id: I661f5455dd4cd57797d09edde9e097e17fb98dae Reviewed-by: Simon Hausmann --- src/3rdparty/masm/yarr/YarrJIT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp index e4f2d97759..71123b7be7 100644 --- a/src/3rdparty/masm/yarr/YarrJIT.cpp +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -468,16 +468,16 @@ class YarrGenerator : private DefaultMacroAssembler { // The operation, as a YarrOpCode, and also a reference to the PatternTerm. YarrOpCode m_op; - PatternTerm* m_term; + PatternTerm* m_term = nullptr; // For alternatives, this holds the PatternAlternative and doubly linked // references to this alternative's siblings. In the case of the // OpBodyAlternativeEnd node at the end of a section of repeating nodes, // m_nextOp will reference the OpBodyAlternativeBegin node of the first // repeating alternative. - PatternAlternative* m_alternative; - size_t m_previousOp; - size_t m_nextOp; + PatternAlternative* m_alternative = nullptr; + size_t m_previousOp = 0; + size_t m_nextOp = 0; // Used to record a set of Jumps out of the generated code, typically // used for jumps out to backtracking code, and a single reentry back -- cgit v1.2.3 From 45420ef4b06b6039ac09f813c964c1b037a485f1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 23 Jun 2017 14:31:01 +0200 Subject: Yarr: Add Q_FALLTHROUGH to fallthroughs detected by GCC 7 Change-Id: I99dcca18155eeef1fdaec8d7693a6a415a68b55b Reviewed-by: Simon Hausmann --- src/3rdparty/masm/yarr/YarrParser.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/yarr/YarrParser.h b/src/3rdparty/masm/yarr/YarrParser.h index 8c5d71b5fe..13ffd3a1d6 100644 --- a/src/3rdparty/masm/yarr/YarrParser.h +++ b/src/3rdparty/masm/yarr/YarrParser.h @@ -118,7 +118,7 @@ private: m_state = AfterCharacterClassHyphen; return; } - // Otherwise just fall through - cached character so treat this as Empty. + Q_FALLTHROUGH(); // cached character, so treat this as Empty. case Empty: m_character = ch; @@ -168,6 +168,7 @@ private: case CachedCharacter: // Flush the currently cached character, then fall through. m_delegate.atomCharacterClassAtom(m_character); + Q_FALLTHROUGH(); case Empty: case AfterCharacterClass: @@ -347,9 +348,8 @@ private: delegate.atomPatternCharacter('\\'); break; } - - // Fall-through to handle this as an octal escape. } + Q_FALLTHROUGH(); // Handle this as an octal escape. // Octal escape case '0': @@ -656,7 +656,8 @@ private: } restoreState(state); - } // if we did not find a complete quantifer, fall through to the default case. + } + Q_FALLTHROUGH(); // if we did not find a complete quantifer, fall through to the default case. default: m_delegate.atomPatternCharacter(consume()); -- cgit v1.2.3 From e3cf025bc041dab43f0432b15adb9f46b4ec8991 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:29:45 +0200 Subject: Initialize pointer *** CID 181282: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer_p.h: 85 in QQuickShapeGenericRenderer::QQuickShapeGenericRenderer(QQuickItem *)() 79 QQuickShapeGenericRenderer(QQuickItem *item) 80 : m_item(item), 81 m_api(QSGRendererInterface::Unknown), 82 m_rootNode(nullptr), 83 m_accDirty(0), 84 m_asyncCallback(nullptr) >>> CID 181282: Uninitialized members (UNINIT_CTOR) >>> Non-static class member "m_asyncCallbackData" is not initialized in this constructor nor in any functions that it calls. 85 { } 86 ~QQuickShapeGenericRenderer(); 87 88 void beginSync(int totalCount) override; 89 void setPath(int index, const QQuickPath *path) override; 90 void setJSPath(int index, const QQuickShapePathCommands &path) override; Change-Id: Ifce4e20c8372b392900b77f45ae14d2abfa1657d Coverity-Id: 181282 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer_p.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index ba50f50309..430a95465b 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -81,7 +81,8 @@ public: m_api(QSGRendererInterface::Unknown), m_rootNode(nullptr), m_accDirty(0), - m_asyncCallback(nullptr) + m_asyncCallback(nullptr), + m_asyncCallbackData(nullptr) { } ~QQuickShapeGenericRenderer(); -- cgit v1.2.3 From 47b59f9001ea6c185e44f243a1fb697b1859215b Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:27:21 +0200 Subject: Initialize pointer *** CID 181281: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapenvprrenderer_p.h: 156 in QQuickNvprMaterialManager::QQuickNvprMaterialManager()() 150 151 void create(QQuickNvprFunctions *nvpr); 152 MaterialDesc *activateMaterial(Material m); 153 void releaseResources(); 154 155 private: >>> CID 181281: Uninitialized members (UNINIT_CTOR) >>> The compiler-generated constructor for this class does not initialize "m_nvpr". 156 QQuickNvprFunctions *m_nvpr; 157 MaterialDesc m_materials[NMaterials]; 158 }; 159 160 class QQuickNvprBlitter 161 { Change-Id: Ia502683b55022ac3eafd4ebd015d44273185bdfc Coverity-Id: 181281 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index 33007313d4..d90357a40d 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -153,7 +153,7 @@ public: void releaseResources(); private: - QQuickNvprFunctions *m_nvpr; + QQuickNvprFunctions *m_nvpr = nullptr; MaterialDesc m_materials[NMaterials]; }; -- cgit v1.2.3 From 1fa071b2b70ee6314fa12312da2d930f3983a7cc Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:21:33 +0200 Subject: Initialize variables *** CID 181280: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer.cpp: 700 in QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader()() 694 QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() 695 { 696 setShaderSourceFile(QOpenGLShader::Vertex, 697 QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); 698 setShaderSourceFile(QOpenGLShader::Fragment, 699 QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); >>> CID 181280: Uninitialized members (UNINIT_CTOR) >>> Non-static class member "m_gradEndLoc" is not initialized in this constructor nor in any functions that it calls. 700 } 701 702 void QQuickShapeLinearGradientShader::initialize() 703 { 704 m_opacityLoc = program()->uniformLocation("opacity"); 705 m_matrixLoc = program()->uniformLocation("matrix"); Change-Id: Ic9435039a1409ade63c7592a4a55b6c7306d03c2 Coverity-Id: 181280 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer_p.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index 430a95465b..ad2427f00a 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -259,10 +259,10 @@ public: static QSGMaterialType type; private: - int m_opacityLoc; - int m_matrixLoc; - int m_gradStartLoc; - int m_gradEndLoc; + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_gradStartLoc = -1; + int m_gradEndLoc = -1; }; class QQuickShapeLinearGradientMaterial : public QSGMaterial -- cgit v1.2.3 From 31611f32bb89564dc6933d47eeb4ca4e45ac5563 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 11:16:17 +0200 Subject: Initialize uniform location variables *** CID 181276: Uninitialized members (UNINIT_CTOR) /qtdeclarative/src/imports/shapes/qquickshapenvprrenderer_p.h: 174 in QQuickNvprBlitter::QQuickNvprBlitter()() 168 float opacity); 169 170 private: 171 QOpenGLShaderProgram *m_program = nullptr; 172 QOpenGLBuffer *m_buffer = nullptr; 173 int m_matrixLoc; >>> CID 181276: Uninitialized members (UNINIT_CTOR) >>> The compiler-generated constructor for this class does not initialize "m_opacityLoc". 174 int m_opacityLoc; 175 QSize m_prevSize; 176 }; 177 178 class QQuickShapeNvprRenderNode : public QSGRenderNode 179 { Change-Id: I4176981fe79c175db974ff428a5116d82420f7a6 Coverity-Id: 181276 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer_p.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index d90357a40d..159bf79493 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -170,8 +170,8 @@ public: private: QOpenGLShaderProgram *m_program = nullptr; QOpenGLBuffer *m_buffer = nullptr; - int m_matrixLoc; - int m_opacityLoc; + int m_matrixLoc = -1; + int m_opacityLoc = -1; QSize m_prevSize; }; -- cgit v1.2.3 From 21fd17d7c1fca77e802ba2d3fd5aeebcdef157e4 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Sun, 25 Jun 2017 10:37:49 +0200 Subject: Add Q_ASSERT to test prevNode To make Coverity happy: *** CID 181275: Null pointer dereferences (FORWARD_NULL) /qtdeclarative/src/imports/shapes/qquickshapegenericrenderer.cpp: 503 in QQuickShapeGenericRenderer::updateNode()() 497 QQuickShapeGenericNode **nodePtr = &m_rootNode; 498 QQuickShapeGenericNode *prevNode = nullptr; 499 500 for (ShapePathData &d : m_sp) { 501 if (!*nodePtr) { 502 *nodePtr = new QQuickShapeGenericNode; >>> CID 181275: Null pointer dereferences (FORWARD_NULL) >>> Dereferencing null pointer "prevNode". 503 prevNode->m_next = *nodePtr; 504 prevNode->appendChildNode(*nodePtr); 505 } 506 507 QQuickShapeGenericNode *node = *nodePtr; 508 Change-Id: I76e79ef6fe94aa8b0e77f79724101b4682dab6d3 Coverity-Id: 181275 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 47203698d5..4a4176793f 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -499,6 +499,7 @@ void QQuickShapeGenericRenderer::updateNode() for (ShapePathData &d : m_sp) { if (!*nodePtr) { + Q_ASSERT(prevNode); *nodePtr = new QQuickShapeGenericNode; prevNode->m_next = *nodePtr; prevNode->appendChildNode(*nodePtr); -- cgit v1.2.3 From 4beee1a6dcc1be57aa6fb2a175dadc6ff298545d Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Mon, 26 Jun 2017 09:23:11 +0200 Subject: shapes: Convert QT_NO_OPENGL to QT_CONFIG(opengl) Also guard fillGradient for now. Task-number: QTBUG-61632 Change-Id: I5fa2607cc1ede0922519258afd455bee4d0187c7 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquicknvprfunctions.cpp | 4 ++-- src/imports/shapes/qquicknvprfunctions_p.h | 4 ++-- src/imports/shapes/qquickshape.cpp | 8 ++++---- src/imports/shapes/qquickshape_p_p.h | 4 ++-- src/imports/shapes/qquickshapegenericrenderer.cpp | 20 ++++++++++++++------ src/imports/shapes/qquickshapegenericrenderer_p.h | 8 ++++++-- src/imports/shapes/qquickshapenvprrenderer_p.h | 4 ++-- 7 files changed, 32 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp index e9b93cf4a1..0b92d4b4c0 100644 --- a/src/imports/shapes/qquicknvprfunctions.cpp +++ b/src/imports/shapes/qquicknvprfunctions.cpp @@ -39,7 +39,7 @@ #include "qquicknvprfunctions_p.h" -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) #include #include @@ -274,4 +274,4 @@ bool QQuickNvprFunctionsPrivate::resolve() QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/imports/shapes/qquicknvprfunctions_p.h index dd45dd7daa..342e92cbc3 100644 --- a/src/imports/shapes/qquicknvprfunctions_p.h +++ b/src/imports/shapes/qquicknvprfunctions_p.h @@ -54,7 +54,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QT_BEGIN_NAMESPACE @@ -395,6 +395,6 @@ private: QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) #endif // QQUICKNVPRFUNCTIONS_P_H diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 4e6896621e..1054af30de 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -921,7 +921,7 @@ void QQuickShapePrivate::createRenderer() return; switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) case QSGRendererInterface::OpenGL: if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { rendererType = QQuickShape::NvprRenderer; @@ -954,7 +954,7 @@ QSGNode *QQuickShapePrivate::createNode() return node; switch (ri->graphicsApi()) { -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) case QSGRendererInterface::OpenGL: if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) { node = new QQuickShapeNvprRenderNode; @@ -1199,7 +1199,7 @@ void QQuickShapeLinearGradient::setY2(qreal v) } } -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) // contexts sharing with each other get the same cache instance class QQuickShapeGradientCacheWrapper @@ -1324,7 +1324,7 @@ QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) return tx; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index a8a5675ccb..888488efcd 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -234,7 +234,7 @@ private: QV4::PersistentValue m_v4value; }; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) class QQuickShapeGradientCache : public QOpenGLSharedResource { @@ -274,7 +274,7 @@ inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0 return h; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 4a4176793f..ca5f2a0e91 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -42,7 +42,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) #include #include #include @@ -118,7 +118,7 @@ void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) { static bool elementIndexUint = true; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) { static bool elementIndexUintChecked = false; if (!elementIndexUintChecked) { @@ -246,6 +246,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient { ShapePathData &d(m_sp[index]); d.fillGradientActive = gradient != nullptr; +#if QT_CONFIG(opengl) if (gradient) { d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); @@ -256,6 +257,7 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient Q_UNREACHABLE(); } } +#endif d.syncDirty |= DirtyFillGradient; } @@ -559,8 +561,12 @@ void QQuickShapeGenericRenderer::updateNode() void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) { if (d->fillGradientActive) { +#if QT_CONFIG(opengl) if (d->effectiveDirty & DirtyFillGradient) n->m_fillGradient = d->fillGradient; +#else + Q_UNUSED(n); +#endif } } @@ -665,7 +671,7 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow * { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... return new QSGVertexColorMaterial; #endif @@ -679,16 +685,18 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindo { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... return new QQuickShapeLinearGradientMaterial(node); +#else + Q_UNUSED(node); #endif qWarning("Linear gradient material: Unsupported graphics API %d", api); return nullptr; } -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QSGMaterialType QQuickShapeLinearGradientShader::type; @@ -771,6 +779,6 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const return 0; } -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index ad2427f00a..1f36e3decf 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -133,7 +133,9 @@ private: Qt::FillRule fillRule; QPainterPath path; bool fillGradientActive; +#if QT_CONFIG(opengl) QQuickShapeGradientCache::GradientDesc fillGradient; +#endif VertexContainerType fillVertices; IndexContainerType fillIndices; QSGGeometry::Type indexType; @@ -218,7 +220,9 @@ public: QQuickWindow *window() const { return m_window; } // shadow data for custom materials +#if QT_CONFIG(opengl) QQuickShapeGradientCache::GradientDesc m_fillGradient; +#endif private: QSGGeometry *m_geometry; @@ -245,7 +249,7 @@ public: static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); }; -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) class QQuickShapeLinearGradientShader : public QSGMaterialShader { @@ -297,7 +301,7 @@ private: QQuickShapeGenericStrokeFillNode *m_node; }; -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index 159bf79493..ec7ba498f9 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -58,7 +58,7 @@ #include #include -#ifndef QT_NO_OPENGL +#if QT_CONFIG(opengl) QT_BEGIN_NAMESPACE @@ -232,6 +232,6 @@ private: QT_END_NAMESPACE -#endif // QT_NO_OPENGL +#endif // QT_CONFIG(opengl) #endif // QQUICKSHAPENVPRRENDERER_P_H -- cgit v1.2.3 From 5c80b29e6d77f0ebc63832473159f5a39babe21b Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Fri, 23 Jun 2017 23:26:35 +0200 Subject: QQmlXMLHttpRequest: support sending ArrayBuffer data The XMLHttpRequest.send() method should be able to send arbitrary binary data, and not just UTF-8 text: with this change we first attempt to use the parameter to the send() method as an ArrayBuffer, and fall back to a QString if that fails. [ChangeLog][QtQml] Allow sending binary data, encoded as ArrayBuffer objects, via XMLHttpRequest's send() method. Task-number: QTBUG-61599 Change-Id: I25781969ee39b4d168e5c76315ed9853092b322b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlxmlhttprequest.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 9e8735cbc6..113ef0c412 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1823,8 +1823,13 @@ void QQmlXMLHttpRequestCtor::method_send(const QV4::BuiltinFunction *, QV4::Scop THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state"); QByteArray data; - if (callData->argc > 0) - data = callData->args[0].toQStringNoThrow().toUtf8(); + if (callData->argc > 0) { + if (const ArrayBuffer *buffer = callData->args[0].as()) { + data = buffer->asByteArray(); + } else { + data = callData->args[0].toQStringNoThrow().toUtf8(); + } + } scope.result = r->send(w, scope.engine->callingQmlContext(), data); } -- cgit v1.2.3 From 2969c92c82e67beb592542cb34a3cbd65ed2fb16 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 27 Jun 2017 15:21:53 +0200 Subject: Do not access the qle_bitfield internals directly This make is impossible to make it private. The uses here also set the internal value multiple times as each part of the union shares the internal. Change-Id: I5db5bf6dd930c09b2aa169371b8d989acfcc00e5 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata_p.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index f4ba257cf5..ff0d597b92 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -129,11 +129,12 @@ struct TableIterator struct Location { union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 20> line; QJsonPrivate::qle_bitfield<20, 12> column; }; - Location() { line.val = 0; column.val = 0; } + Location() : _dummy(0) { } inline bool operator<(const Location &other) const { return line < other.line || @@ -149,11 +150,12 @@ struct RegExp RegExp_Multiline = 0x04 }; union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 4> flags; QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - RegExp() { flags.val = 0; stringIndex.val = 0; } + RegExp() : _dummy(0) { } }; struct Lookup @@ -167,21 +169,23 @@ struct Lookup }; union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 4> type_and_flags; QJsonPrivate::qle_bitfield<4, 28> nameIndex; }; - Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } + Lookup() : _dummy(0) { } }; struct JSClassMember { union { + quint32 _dummy; QJsonPrivate::qle_bitfield<0, 31> nameOffset; QJsonPrivate::qle_bitfield<31, 1> isAccessor; }; - JSClassMember() { nameOffset = 0; isAccessor = 0; } + JSClassMember() : _dummy(0) { } }; struct JSClass -- cgit v1.2.3 From a813d8a4d87e22a87722fada10710711b1d4306f Mon Sep 17 00:00:00 2001 From: Marco Benelli Date: Thu, 8 Jun 2017 16:18:08 +0200 Subject: QtQml: restore models definitions in qmltypes Regenerated plugins.qmltypes with the -noforceqtquick option added to qmlplugindump. In this way some definitions (eg ObjectModel) are restored. Change-Id: I294ab673b395fc50d8851614fd5801ed121d5b13 Reviewed-by: J-P Nurmi Reviewed-by: Thomas Hartmann --- src/imports/models/plugins.qmltypes | 468 +++++++++++++++++++++++++++++++++++- 1 file changed, 466 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/models/plugins.qmltypes b/src/imports/models/plugins.qmltypes index aa06a2a709..e6d09b76d6 100644 --- a/src/imports/models/plugins.qmltypes +++ b/src/imports/models/plugins.qmltypes @@ -4,10 +4,275 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtQml.Models 2.3' +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml.Models 2.3' Module { - dependencies: ["QtQuick 2.8"] + dependencies: [] + Component { + name: "QAbstractItemModel" + prototype: "QObject" + Enum { + name: "LayoutChangeHint" + values: { + "NoLayoutChangeHint": 0, + "VerticalSortHint": 1, + "HorizontalSortHint": 2 + } + } + Signal { + name: "dataChanged" + Parameter { name: "topLeft"; type: "QModelIndex" } + Parameter { name: "bottomRight"; type: "QModelIndex" } + Parameter { name: "roles"; type: "QVector" } + } + Signal { + name: "dataChanged" + Parameter { name: "topLeft"; type: "QModelIndex" } + Parameter { name: "bottomRight"; type: "QModelIndex" } + } + Signal { + name: "headerDataChanged" + Parameter { name: "orientation"; type: "Qt::Orientation" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "layoutChanged" + Parameter { name: "parents"; type: "QList" } + Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" } + } + Signal { + name: "layoutChanged" + Parameter { name: "parents"; type: "QList" } + } + Signal { name: "layoutChanged" } + Signal { + name: "layoutAboutToBeChanged" + Parameter { name: "parents"; type: "QList" } + Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" } + } + Signal { + name: "layoutAboutToBeChanged" + Parameter { name: "parents"; type: "QList" } + } + Signal { name: "layoutAboutToBeChanged" } + Signal { + name: "rowsAboutToBeInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsAboutToBeRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "rowsRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsAboutToBeInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsInserted" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsAboutToBeRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { + name: "columnsRemoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "first"; type: "int" } + Parameter { name: "last"; type: "int" } + } + Signal { name: "modelAboutToBeReset" } + Signal { name: "modelReset" } + Signal { + name: "rowsAboutToBeMoved" + Parameter { name: "sourceParent"; type: "QModelIndex" } + Parameter { name: "sourceStart"; type: "int" } + Parameter { name: "sourceEnd"; type: "int" } + Parameter { name: "destinationParent"; type: "QModelIndex" } + Parameter { name: "destinationRow"; type: "int" } + } + Signal { + name: "rowsMoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "start"; type: "int" } + Parameter { name: "end"; type: "int" } + Parameter { name: "destination"; type: "QModelIndex" } + Parameter { name: "row"; type: "int" } + } + Signal { + name: "columnsAboutToBeMoved" + Parameter { name: "sourceParent"; type: "QModelIndex" } + Parameter { name: "sourceStart"; type: "int" } + Parameter { name: "sourceEnd"; type: "int" } + Parameter { name: "destinationParent"; type: "QModelIndex" } + Parameter { name: "destinationColumn"; type: "int" } + } + Signal { + name: "columnsMoved" + Parameter { name: "parent"; type: "QModelIndex" } + Parameter { name: "start"; type: "int" } + Parameter { name: "end"; type: "int" } + Parameter { name: "destination"; type: "QModelIndex" } + Parameter { name: "column"; type: "int" } + } + Method { name: "submit"; type: "bool" } + Method { name: "revert" } + Method { + name: "hasIndex" + type: "bool" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "hasIndex" + type: "bool" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + } + Method { + name: "index" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "index" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + } + Method { + name: "parent" + type: "QModelIndex" + Parameter { name: "child"; type: "QModelIndex" } + } + Method { + name: "sibling" + type: "QModelIndex" + Parameter { name: "row"; type: "int" } + Parameter { name: "column"; type: "int" } + Parameter { name: "idx"; type: "QModelIndex" } + } + Method { + name: "rowCount" + type: "int" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "rowCount"; type: "int" } + Method { + name: "columnCount" + type: "int" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "columnCount"; type: "int" } + Method { + name: "hasChildren" + type: "bool" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { name: "hasChildren"; type: "bool" } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "QModelIndex" } + } + Method { + name: "setData" + type: "bool" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "setData" + type: "bool" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "value"; type: "QVariant" } + } + Method { + name: "headerData" + type: "QVariant" + Parameter { name: "section"; type: "int" } + Parameter { name: "orientation"; type: "Qt::Orientation" } + Parameter { name: "role"; type: "int" } + } + Method { + name: "headerData" + type: "QVariant" + Parameter { name: "section"; type: "int" } + Parameter { name: "orientation"; type: "Qt::Orientation" } + } + Method { + name: "fetchMore" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "canFetchMore" + type: "bool" + Parameter { name: "parent"; type: "QModelIndex" } + } + Method { + name: "flags" + type: "Qt::ItemFlags" + Parameter { name: "index"; type: "QModelIndex" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "hits"; type: "int" } + Parameter { name: "flags"; type: "Qt::MatchFlags" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + Parameter { name: "hits"; type: "int" } + } + Method { + name: "match" + type: "QModelIndexList" + Parameter { name: "start"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + Parameter { name: "value"; type: "QVariant" } + } + } + Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" } Component { name: "QItemSelectionModel" prototype: "QObject" @@ -119,4 +384,203 @@ Module { } Method { name: "selectedColumns"; type: "QModelIndexList" } } + Component { + name: "QQmlDelegateModel" + defaultProperty: "delegate" + prototype: "QQmlInstanceModel" + exports: ["QtQml.Models/DelegateModel 2.1"] + exportMetaObjectRevisions: [0] + attachedType: "QQmlDelegateModelAttached" + Property { name: "model"; type: "QVariant" } + Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } + Property { name: "filterOnGroup"; type: "string" } + Property { name: "items"; type: "QQmlDelegateModelGroup"; isReadonly: true; isPointer: true } + Property { + name: "persistedItems" + type: "QQmlDelegateModelGroup" + isReadonly: true + isPointer: true + } + Property { name: "groups"; type: "QQmlDelegateModelGroup"; isList: true; isReadonly: true } + Property { name: "parts"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "rootIndex"; type: "QVariant" } + Signal { name: "filterGroupChanged" } + Signal { name: "defaultGroupsChanged" } + Method { + name: "modelIndex" + type: "QVariant" + Parameter { name: "idx"; type: "int" } + } + Method { name: "parentModelIndex"; type: "QVariant" } + } + Component { + name: "QQmlDelegateModelAttached" + prototype: "QObject" + Property { name: "model"; type: "QQmlDelegateModel"; isReadonly: true; isPointer: true } + Property { name: "groups"; type: "QStringList" } + Property { name: "isUnresolved"; type: "bool"; isReadonly: true } + Signal { name: "unresolvedChanged" } + } + Component { + name: "QQmlDelegateModelGroup" + prototype: "QObject" + exports: ["QtQml.Models/DelegateModelGroup 2.1"] + exportMetaObjectRevisions: [0] + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "name"; type: "string" } + Property { name: "includeByDefault"; type: "bool" } + Signal { name: "defaultIncludeChanged" } + Signal { + name: "changed" + Parameter { name: "removed"; type: "QQmlV4Handle" } + Parameter { name: "inserted"; type: "QQmlV4Handle" } + } + Method { + name: "insert" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "create" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "resolve" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "remove" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "addGroups" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "removeGroups" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "setGroups" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "move" + Parameter { type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "get" + type: "QQmlV4Handle" + Parameter { name: "index"; type: "int" } + } + } + Component { name: "QQmlDelegateModelParts"; prototype: "QObject" } + Component { + name: "QQmlListElement" + prototype: "QObject" + exports: ["QtQml.Models/ListElement 2.1"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QQmlListModel" + prototype: "QAbstractListModel" + exports: ["QtQml.Models/ListModel 2.1"] + exportMetaObjectRevisions: [0] + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "dynamicRoles"; type: "bool" } + Method { name: "clear" } + Method { + name: "remove" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "append" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "insert" + Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true } + } + Method { + name: "get" + type: "QQmlV4Handle" + Parameter { name: "index"; type: "int" } + } + Method { + name: "set" + Parameter { name: "index"; type: "int" } + Parameter { type: "QQmlV4Handle" } + } + Method { + name: "setProperty" + Parameter { name: "index"; type: "int" } + Parameter { name: "property"; type: "string" } + Parameter { name: "value"; type: "QVariant" } + } + Method { + name: "move" + Parameter { name: "from"; type: "int" } + Parameter { name: "to"; type: "int" } + Parameter { name: "count"; type: "int" } + } + Method { name: "sync" } + } + Component { + name: "QQmlObjectModel" + defaultProperty: "children" + prototype: "QQmlInstanceModel" + exports: [ + "QtQml.Models/ObjectModel 2.1", + "QtQml.Models/ObjectModel 2.3" + ] + exportMetaObjectRevisions: [0, 3] + attachedType: "QQmlObjectModelAttached" + Property { name: "children"; type: "QObject"; isList: true; isReadonly: true } + Method { name: "clear"; revision: 3 } + Method { + name: "get" + revision: 3 + type: "QObject*" + Parameter { name: "index"; type: "int" } + } + Method { + name: "append" + revision: 3 + Parameter { name: "object"; type: "QObject"; isPointer: true } + } + Method { + name: "insert" + revision: 3 + Parameter { name: "index"; type: "int" } + Parameter { name: "object"; type: "QObject"; isPointer: true } + } + Method { + name: "move" + revision: 3 + Parameter { name: "from"; type: "int" } + Parameter { name: "to"; type: "int" } + Parameter { name: "n"; type: "int" } + } + Method { + name: "move" + revision: 3 + Parameter { name: "from"; type: "int" } + Parameter { name: "to"; type: "int" } + } + Method { + name: "remove" + revision: 3 + Parameter { name: "index"; type: "int" } + Parameter { name: "n"; type: "int" } + } + Method { + name: "remove" + revision: 3 + Parameter { name: "index"; type: "int" } + } + } + Component { + name: "QQmlObjectModelAttached" + prototype: "QObject" + Property { name: "index"; type: "int"; isReadonly: true } + } } -- cgit v1.2.3 From 95f6dcbae17b72c8dfebc9f97c77310151b63ceb Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 17 May 2017 22:28:13 +0200 Subject: Fix ObjectModel::move() to mark the changes as moves QQmlObjectModel::move() created a QQmlChangeSet with moveId -1, which made item views and controls see the changes as removals and insertions, because QQmlChangeSet::Change::isMove() returned false. Consequently, item views did not update the current index when the current item was moved. Task-number: QTBUG-60894 Change-Id: I4a64b7670c1fae12337995627437cc83efb9f1ef Reviewed-by: Michael Brasser Reviewed-by: Robin Burchell --- src/qml/types/qqmlobjectmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp index 2814b9d38f..64d0169f6b 100644 --- a/src/qml/types/qqmlobjectmodel.cpp +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -129,7 +129,7 @@ public: } QQmlChangeSet changeSet; - changeSet.move(from, to, n, -1); + changeSet.move(from, to, n, 0); emit q->modelUpdated(changeSet, false); emit q->childrenChanged(); } -- cgit v1.2.3 From 5c221826025276aea5ee19275f350a28e02db254 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 30 Jun 2017 11:53:30 +0200 Subject: doc: Add font.hintingPreference to font type documentation The docs for this was added to the font property documentation in Text/TextEdit/TextInput, but not to the main font QML type documentation. Change-Id: I579706bea77b6fcd3972921c34b7693bf686ba31 Reviewed-by: Simon Hausmann --- src/quick/doc/src/qmltypereference.qdoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src') diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 2406722dbc..b0aa143505 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -170,6 +170,7 @@ available when you import \c QtQuick. \li \l enumeration \c font.capitalization \li \l real \c font.letterSpacing \li \l real \c font.wordSpacing + \li \l enumeration \c font.hintingPreference \endlist Example: @@ -236,6 +237,19 @@ available when you import \c QtQuick. \li Alters the text to be rendered with the first character of each word as an uppercase character. \endtable + Setting the hinting preference only has an effect when using the "NativeRendering" render type. + The property supports the following values: + + \list + \value Font.PreferDefaultHinting - Use the default hinting level for the target platform. + \value Font.PreferNoHinting - If possible, render text without hinting the outlines + of the glyphs. + \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting, + but align glyphs to the pixel grid in the vertical direction. + \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and + vertical directions. + \endlist + \sa {QML Basic Types} */ -- cgit v1.2.3 From 441e0b41bf5b700cdaa3b0ba2393c487ed4b9de5 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 30 Jun 2017 10:07:29 +0200 Subject: Expose "kerning" property for fonts For text where the content is known, it can be handy to be able to disable the kerning feature in OpenType to improve performance. [ChangeLog][Qt Quick][Text] Added "kerning" property to the font type to support disabling kerning on text. Task-number: QTBUG-56728 Change-Id: I2e447587a066a7e12c5d38967e0845eaad021014 Reviewed-by: Simon Hausmann --- src/quick/doc/src/qmltypereference.qdoc | 1 + src/quick/items/qquicktext.cpp | 13 +++++++++++++ src/quick/items/qquicktextedit.cpp | 13 +++++++++++++ src/quick/items/qquicktextinput.cpp | 13 +++++++++++++ src/quick/util/qquickglobal.cpp | 5 +++++ src/quick/util/qquickvaluetypes.cpp | 10 ++++++++++ src/quick/util/qquickvaluetypes_p.h | 4 ++++ 7 files changed, 59 insertions(+) (limited to 'src') diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 2406722dbc..cbcf945a11 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -170,6 +170,7 @@ available when you import \c QtQuick. \li \l enumeration \c font.capitalization \li \l real \c font.letterSpacing \li \l real \c font.wordSpacing + \li \l bool \c font.kerning \endlist Example: diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 080cc9412e..1bcfbd41f7 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1499,6 +1499,19 @@ QQuickText::~QQuickText() Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting } \endqml */ + +/*! + \qmlproperty bool QtQuick::Text::font.kerning + \since 5.10 + + Enables or disables the kerning OpenType feature when shaping the text. This may improve performance + when creating or changing the text, at the expense of some cosmetic features. The default value + is true. + + \qml + Text { text: "OATS FLAVOUR WAY"; font.kerning: false } + \endqml +*/ QFont QQuickText::font() const { Q_D(const QQuickText); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 61d610520f..aad380ca30 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -355,6 +355,19 @@ QString QQuickTextEdit::text() const \endqml */ +/*! + \qmlproperty bool QtQuick::TextEdit::font.kerning + \since 5.10 + + Enables or disables the kerning OpenType feature when shaping the text. This may improve performance + when creating or changing the text, at the expense of some cosmetic features. The default value + is true. + + \qml + Text { text: "OATS FLAVOUR WAY"; kerning: font.false } + \endqml +*/ + /*! \qmlproperty string QtQuick::TextEdit::text diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index a378359c95..d485083820 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -378,6 +378,19 @@ QString QQuickTextInputPrivate::realText() const TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting } \endqml */ + +/*! + \qmlproperty bool QtQuick::TextInput::font.kerning + \since 5.10 + + Enables or disables the kerning OpenType feature when shaping the text. This may improve performance + when creating or changing the text, at the expense of some cosmetic features. The default value + is true. + + \qml + Text { text: "OATS FLAVOUR WAY"; font.kerning: false } + \endqml +*/ QFont QQuickTextInput::font() const { Q_D(const QQuickTextInput); diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp index 1d2f3de1df..6df23cdff5 100644 --- a/src/quick/util/qquickglobal.cpp +++ b/src/quick/util/qquickglobal.cpp @@ -302,6 +302,7 @@ public: QV4::ScopedValue vweight(scope, obj->get((s = v4->newString(QStringLiteral("weight"))))); QV4::ScopedValue vwspac(scope, obj->get((s = v4->newString(QStringLiteral("wordSpacing"))))); QV4::ScopedValue vhint(scope, obj->get((s = v4->newString(QStringLiteral("hintingPreference"))))); + QV4::ScopedValue vkerning(scope, obj->get((s = v4->newString(QStringLiteral("kerning"))))); // pull out the values, set ok to true if at least one valid field is given. if (vbold->isBoolean()) { @@ -356,6 +357,10 @@ public: retn.setHintingPreference(static_cast(vhint->integerValue())); if (ok) *ok = true; } + if (vkerning->isBoolean()) { + retn.setKerning(vkerning->booleanValue()); + if (ok) *ok = true; + } return retn; } diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp index 4d34c6d661..bc4a72b6ea 100644 --- a/src/quick/util/qquickvaluetypes.cpp +++ b/src/quick/util/qquickvaluetypes.cpp @@ -757,6 +757,16 @@ void QQuickFontValueType::setHintingPreference(QQuickFontValueType::HintingPrefe v.setHintingPreference(QFont::HintingPreference(hintingPreference)); } +bool QQuickFontValueType::kerning() const +{ + return v.kerning(); +} + +void QQuickFontValueType::setKerning(bool b) +{ + v.setKerning(b); +} + QT_END_NAMESPACE #include "moc_qquickvaluetypes_p.cpp" diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h index 4a1598ec5c..a3f35a84ec 100644 --- a/src/quick/util/qquickvaluetypes_p.h +++ b/src/quick/util/qquickvaluetypes_p.h @@ -323,6 +323,7 @@ class QQuickFontValueType Q_PROPERTY(qreal letterSpacing READ letterSpacing WRITE setLetterSpacing FINAL) Q_PROPERTY(qreal wordSpacing READ wordSpacing WRITE setWordSpacing FINAL) Q_PROPERTY(HintingPreference hintingPreference READ hintingPreference WRITE setHintingPreference FINAL) + Q_PROPERTY(bool kerning READ kerning WRITE setKerning FINAL) public: enum FontWeight { Thin = QFont::Thin, @@ -393,6 +394,9 @@ public: HintingPreference hintingPreference() const; void setHintingPreference(HintingPreference); + + bool kerning() const; + void setKerning(bool b); }; QT_END_NAMESPACE -- cgit v1.2.3 From 98a71c7739a91be03b73312253dd1291e0a1d96d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 1 Jul 2017 14:11:34 -0700 Subject: Suppress warning about QSignalMapper being deprecated It is since qtbase commit 29bcbeab90210da80234529905d17280374f9684. Change-Id: I8d96dea9955d4c749b99fffd14cd512a8ff88a74 Reviewed-by: Simon Hausmann --- src/quick/items/qquickgenericshadereffect.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp index b366071962..305ef7e778 100644 --- a/src/quick/items/qquickgenericshadereffect.cpp +++ b/src/quick/items/qquickgenericshadereffect.cpp @@ -546,7 +546,10 @@ void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType) // Have a QSignalMapper that emits mapped() with an index+type on each property change notify signal. auto &sm(m_signalMappers[shaderType][i]); if (!sm.mapper) { +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED sm.mapper = new QSignalMapper; +QT_WARNING_POP sm.mapper->setMapping(m_item, i | (shaderType << 16)); } sm.active = true; -- cgit v1.2.3 From f6cc21f08441f1aa6fa0c4acd9e41efedb4e3823 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 20 Jun 2017 11:56:58 +0200 Subject: Doc: Add \keyword for "Grouped" and "Attached" property topics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables searching the index list for the \keyword in Qt Creator Change-Id: Ic8fde82def48c4d0f4cbf0e75bc862e00ca3ca65 Reviewed-by: Topi Reiniö --- src/qml/doc/src/cppintegration/definetypes.qdoc | 4 ++-- src/qml/doc/src/cppintegration/exposecppattributes.qdoc | 1 + src/qml/doc/src/qmlfunctions.qdoc | 2 +- src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 32084bd308..1ce00c6ad0 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -346,8 +346,8 @@ demonstrates a usage of extension objects. \section1 Defining QML-Specific Types and Attributes - -\section2 Providing Attached Objects for Data Annotations +\section2 Providing Attached Properties +\keyword Integrating QML and C++ - Attached Properties In the QML language syntax, there is a notion of \l{Attached properties and attached signal handlers}{\e {attached properties} and \e {attached signal diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index c4c58c2821..2121c1f291 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -301,6 +301,7 @@ Note that the template class type for the QQmlListProperty — in this case, \section2 Grouped Properties +\keyword Integrating QML and C++ - Grouped Properties Any read-only object-type property is accessible from QML code as a \e {grouped property}. This can be used to expose a group of related diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 834684fe6d..e73f1cb59c 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -354,7 +354,7 @@ Returns 0 if type \e T is not a valid attaching type, or if \a create is false and no attachment object instance has previously been created for \a attachee. - \sa {Providing Attached Objects for Data Annotations} + \sa {Providing Attached Properties} */ diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 33f58dc1b9..df65a0942f 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -856,8 +856,7 @@ are otherwise unavailable to the object. In particular, they allow objects to access properties or signals that are specifically relevant to the individual object. -A QML type implementation may choose to \l {Providing Attached Objects for -Data Annotations}{create an \e {attaching type} in C++} with +A QML type implementation may choose to \l {Providing Attached Properties}{create an \e {attaching type} in C++} with particular properties and signals. Instances of this type can then be created and \e attached to specific objects at run time, allowing those objects to access the properties and signals of the attaching type. These are accessed by -- cgit v1.2.3 From fd295c5047095806db2015a978ed593a140ff373 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 4 Jul 2017 10:59:28 +0200 Subject: doc: Use correct class in docs for font.kerning Copy-pasted the docs for Text.font.kerning into TextEdit and TextInput and forgot to change the class name in the example. Amends 441e0b41bf5b700cdaa3b0ba2393c487ed4b9de5. Task-number: QTBUG-56728 Change-Id: Ieab27efb51fa702d83b891e3c7b7aeb5e4795fc5 Reviewed-by: Simon Hausmann --- src/quick/items/qquicktextedit.cpp | 2 +- src/quick/items/qquicktextinput.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index aad380ca30..aec8666dc4 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -364,7 +364,7 @@ QString QQuickTextEdit::text() const is true. \qml - Text { text: "OATS FLAVOUR WAY"; kerning: font.false } + TextEdit { text: "OATS FLAVOUR WAY"; kerning: font.false } \endqml */ diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index d485083820..49f574156a 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -388,7 +388,7 @@ QString QQuickTextInputPrivate::realText() const is true. \qml - Text { text: "OATS FLAVOUR WAY"; font.kerning: false } + TextInput { text: "OATS FLAVOUR WAY"; font.kerning: false } \endqml */ QFont QQuickTextInput::font() const -- cgit v1.2.3 From 759d8bb3c2c5e10381534bcb652900afaf7aca96 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 6 Jul 2017 09:18:20 +0200 Subject: Update QtQuick 2 plugins.qmltypes Change-Id: Ic86891dbd65acc9db7a467960884c036abd0f987 Reviewed-by: Marco Benelli --- src/imports/qtquick2/plugins.qmltypes | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index 73d6d8ec68..d23d6cc311 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -1807,6 +1807,7 @@ Module { Property { name: "letterSpacing"; type: "double" } Property { name: "wordSpacing"; type: "double" } Property { name: "hintingPreference"; type: "HintingPreference" } + Property { name: "kerning"; type: "bool" } Method { name: "toString"; type: "string" } } Component { @@ -1823,6 +1824,7 @@ Module { exports: ["QtQuick/Gradient 2.0"] exportMetaObjectRevisions: [0] Property { name: "stops"; type: "QQuickGradientStop"; isList: true; isReadonly: true } + Signal { name: "updated" } } Component { name: "QQuickGradientStop" @@ -3079,8 +3081,8 @@ Module { Component { name: "QQuickPathArc" prototype: "QQuickCurve" - exports: ["QtQuick/PathArc 2.0"] - exportMetaObjectRevisions: [0] + exports: ["QtQuick/PathArc 2.0", "QtQuick/PathArc 2.9"] + exportMetaObjectRevisions: [0, 2] Enum { name: "ArcDirection" values: { @@ -3092,6 +3094,8 @@ Module { Property { name: "radiusY"; type: "double" } Property { name: "useLargeArc"; type: "bool" } Property { name: "direction"; type: "ArcDirection" } + Property { name: "xAxisRotation"; revision: 2; type: "double" } + Signal { name: "xAxisRotationChanged"; revision: 2 } } Component { name: "QQuickPathAttribute" @@ -3143,6 +3147,12 @@ Module { exports: ["QtQuick/PathLine 2.0"] exportMetaObjectRevisions: [0] } + Component { + name: "QQuickPathMove" + prototype: "QQuickCurve" + exports: ["QtQuick/PathMove 2.9"] + exportMetaObjectRevisions: [0] + } Component { name: "QQuickPathPercent" prototype: "QQuickPathElement" -- cgit v1.2.3 From 96ba4222f9654cae43f40f8cabfa689725bb2b1b Mon Sep 17 00:00:00 2001 From: Marco Benelli Date: Thu, 6 Jul 2017 08:53:14 +0200 Subject: Update plugins.qmltypes for Shapes Update qmltypes forcing the dependency to QtQuick in order to avoid the dumping of duplicated QtQuick's components. Change-Id: Ie16f21518076d0af1c744e420d689122fafb485e Reviewed-by: Thomas Hartmann --- src/imports/shapes/plugins.qmltypes | 217 +++++------------------------------- 1 file changed, 25 insertions(+), 192 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes index 00d0050085..c7c079991d 100644 --- a/src/imports/shapes/plugins.qmltypes +++ b/src/imports/shapes/plugins.qmltypes @@ -4,196 +4,13 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.Shapes 1.0' +// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.0' Module { - dependencies: [] - Component { - name: "QQuickItem" - defaultProperty: "data" - prototype: "QObject" - Enum { - name: "TransformOrigin" - values: { - "TopLeft": 0, - "Top": 1, - "TopRight": 2, - "Left": 3, - "Center": 4, - "Right": 5, - "BottomLeft": 6, - "Bottom": 7, - "BottomRight": 8 - } - } - Property { name: "parent"; type: "QQuickItem"; isPointer: true } - Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "x"; type: "double" } - Property { name: "y"; type: "double" } - Property { name: "z"; type: "double" } - Property { name: "width"; type: "double" } - Property { name: "height"; type: "double" } - Property { name: "opacity"; type: "double" } - Property { name: "enabled"; type: "bool" } - Property { name: "visible"; type: "bool" } - Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true } - Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true } - Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true } - Property { name: "state"; type: "string" } - Property { name: "childrenRect"; type: "QRectF"; isReadonly: true } - Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true } - Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true } - Property { name: "baselineOffset"; type: "double" } - Property { name: "clip"; type: "bool" } - Property { name: "focus"; type: "bool" } - Property { name: "activeFocus"; type: "bool"; isReadonly: true } - Property { name: "activeFocusOnTab"; revision: 1; type: "bool" } - Property { name: "rotation"; type: "double" } - Property { name: "scale"; type: "double" } - Property { name: "transformOrigin"; type: "TransformOrigin" } - Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true } - Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true } - Property { name: "smooth"; type: "bool" } - Property { name: "antialiasing"; type: "bool" } - Property { name: "implicitWidth"; type: "double" } - Property { name: "implicitHeight"; type: "double" } - Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } - Signal { - name: "childrenRectChanged" - Parameter { type: "QRectF" } - } - Signal { - name: "baselineOffsetChanged" - Parameter { type: "double" } - } - Signal { - name: "stateChanged" - Parameter { type: "string" } - } - Signal { - name: "focusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusChanged" - Parameter { type: "bool" } - } - Signal { - name: "activeFocusOnTabChanged" - revision: 1 - Parameter { type: "bool" } - } - Signal { - name: "parentChanged" - Parameter { type: "QQuickItem"; isPointer: true } - } - Signal { - name: "transformOriginChanged" - Parameter { type: "TransformOrigin" } - } - Signal { - name: "smoothChanged" - Parameter { type: "bool" } - } - Signal { - name: "antialiasingChanged" - Parameter { type: "bool" } - } - Signal { - name: "clipChanged" - Parameter { type: "bool" } - } - Signal { - name: "windowChanged" - revision: 1 - Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } - } - Method { name: "update" } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - Parameter { name: "targetSize"; type: "QSize" } - } - Method { - name: "grabToImage" - revision: 2 - type: "bool" - Parameter { name: "callback"; type: "QJSValue" } - } - Method { - name: "contains" - type: "bool" - Parameter { name: "point"; type: "QPointF" } - } - Method { - name: "mapFromItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToItem" - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapFromGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { - name: "mapToGlobal" - revision: 7 - Parameter { type: "QQmlV4Function"; isPointer: true } - } - Method { name: "forceActiveFocus" } - Method { - name: "forceActiveFocus" - Parameter { name: "reason"; type: "Qt::FocusReason" } - } - Method { - name: "nextItemInFocusChain" - revision: 1 - type: "QQuickItem*" - Parameter { name: "forward"; type: "bool" } - } - Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" } - Method { - name: "childAt" - type: "QQuickItem*" - Parameter { name: "x"; type: "double" } - Parameter { name: "y"; type: "double" } - } - } - Component { - name: "QQuickShapeGradient" - defaultProperty: "stops" - prototype: "QObject" - exports: ["QtQuick.Shapes/ShapeGradient 1.0"] - isCreatable: false - exportMetaObjectRevisions: [0] - Enum { - name: "SpreadMode" - values: { - "PadSpread": 0, - "RepeatSpread": 1, - "ReflectSpread": 2 - } - } - Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true } - Property { name: "spread"; type: "SpreadMode" } - Signal { name: "updated" } - } + dependencies: ["QtQuick 2.8"] Component { name: "QQuickShape" - defaultProperty: "elements" + defaultProperty: "data" prototype: "QQuickItem" exports: ["QtQuick.Shapes/Shape 1.0"] exportMetaObjectRevisions: [0] @@ -216,9 +33,26 @@ Module { } Property { name: "renderer"; type: "RendererType"; isReadonly: true } Property { name: "asynchronous"; type: "bool" } - Property { name: "enableVendorExtensions"; type: "bool" } + Property { name: "vendorExtensionsEnabled"; type: "bool" } Property { name: "status"; type: "Status"; isReadonly: true } - Property { name: "elements"; type: "QQuickShapePath"; isList: true; isReadonly: true } + Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } + } + Component { + name: "QQuickShapeGradient" + defaultProperty: "stops" + prototype: "QQuickGradient" + exports: ["QtQuick.Shapes/ShapeGradient 1.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "SpreadMode" + values: { + "PadSpread": 0, + "RepeatSpread": 1, + "ReflectSpread": 2 + } + } + Property { name: "spread"; type: "SpreadMode" } } Component { name: "QQuickShapeLinearGradient" @@ -233,8 +67,8 @@ Module { } Component { name: "QQuickShapePath" - defaultProperty: "path" - prototype: "QObject" + defaultProperty: "pathElements" + prototype: "QQuickPath" exports: ["QtQuick.Shapes/ShapePath 1.0"] exportMetaObjectRevisions: [0] Enum { @@ -267,7 +101,6 @@ Module { "DashLine": 2 } } - Property { name: "path"; type: "QQuickPath"; isPointer: true } Property { name: "strokeColor"; type: "QColor" } Property { name: "strokeWidth"; type: "double" } Property { name: "fillColor"; type: "QColor" } @@ -279,6 +112,6 @@ Module { Property { name: "dashOffset"; type: "double" } Property { name: "dashPattern"; type: "QVector" } Property { name: "fillGradient"; type: "QQuickShapeGradient"; isPointer: true } - Signal { name: "changed" } + Signal { name: "shapePathChanged" } } } -- cgit v1.2.3 From df26542db1eddbcb8a92cce8a38eaa6dc9fa5990 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 6 Jul 2017 09:18:00 +0200 Subject: shapes: Remove componentComplete from QQuickShapePrivate QQuickItemPrivate already has the member componentComplete. Duplicating this breaks QQuickItem::isComponentComplete(), which is used in Qt Quick Designer. For this reason QQuickShape was never completed in Qt Quick Designer. This patch fixes the issue. Change-Id: I30201ff5fb17282dab99a8c84182c6fb6e183134 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshape.cpp | 3 +-- src/imports/shapes/qquickshape_p_p.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 1054af30de..666ed4e595 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -638,8 +638,7 @@ void QQuickShapePath::resetFillGradient() */ QQuickShapePrivate::QQuickShapePrivate() - : componentComplete(true), - spChanged(false), + : spChanged(false), rendererType(QQuickShape::UnknownRenderer), async(false), status(QQuickShape::Null), diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index 888488efcd..dc62994af2 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -177,7 +177,6 @@ public: static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); } - bool componentComplete; bool spChanged; QQuickShape::RendererType rendererType; bool async; -- cgit v1.2.3 From 6d471d6ec79c10e564f2c05bfac7ffab3560f54d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 4 Jul 2017 12:27:24 +0200 Subject: QQuickFBO: Keep devicePixelRatio qreal as long as possible Task-number: QTBUG-61686 Change-Id: I9637be13f701d32d87a42fc4ae0f013b8843503e Reviewed-by: Andy Shaw Reviewed-by: Allan Sandfeld Jensen --- src/quick/items/qquickframebufferobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp index 52b19d994c..3c00e956cc 100644 --- a/src/quick/items/qquickframebufferobject.cpp +++ b/src/quick/items/qquickframebufferobject.cpp @@ -260,7 +260,7 @@ public: bool renderPending; bool invalidatePending; - int devicePixelRatio; + qreal devicePixelRatio; }; static inline bool isOpenGL(QSGRenderContext *rc) -- cgit v1.2.3 From 8ee40ea18185f9030c7596a3e2c20dd1c3aeeb48 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 6 Jul 2017 10:53:02 +0200 Subject: shape: Call base impl for componentComplete ...and classBegin(). Change-Id: Ie6b4a53c32044d17ce1beb5415a830683c2b513f Reviewed-by: J-P Nurmi --- src/imports/shapes/qquickshape.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 666ed4e595..d81f560315 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -846,14 +846,14 @@ QQmlListProperty QQuickShape::data() void QQuickShape::classBegin() { - Q_D(QQuickShape); - d->componentComplete = false; + QQuickItem::classBegin(); } void QQuickShape::componentComplete() { Q_D(QQuickShape); - d->componentComplete = true; + + QQuickItem::componentComplete(); for (QQuickShapePath *p : d->qmlData.sp) connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged())); -- cgit v1.2.3 From 336ecf47667a020376c72a192f195c277931d75a Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 28 Jun 2017 12:51:33 +0200 Subject: Refactor le integer types from qjson_p.h to qendian_p.h Change-Id: Ibb24b0a55dd94e03fea3104e8af5ddb266004300 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 12 +- src/qml/compiler/qv4compileddata.cpp | 4 +- src/qml/compiler/qv4compileddata_p.h | 257 +++++++++++++++---------------- src/qml/compiler/qv4compiler.cpp | 22 +-- src/qml/compiler/qv4compiler_p.h | 4 +- src/qml/jsruntime/qv4function.cpp | 6 +- src/qml/qml/qqmljavascriptexpression.cpp | 6 +- src/qml/qml/qqmlobjectcreator.cpp | 2 +- 8 files changed, 153 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index e80b9d8ac4..df615e6804 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1425,7 +1425,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: } // write objects - QV4::CompiledData::LEUInt32 *objectTable = reinterpret_cast(data + qmlUnit->offsetToObjects); + quint32_le *objectTable = reinterpret_cast(data + qmlUnit->offsetToObjects); char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; for (int i = 0; i < output.objects.count(); ++i) { const Object *o = output.objects.at(i); @@ -1467,7 +1467,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: objectToWrite->offsetToNamedObjectsInComponent = nextOffset; nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); - QV4::CompiledData::LEUInt32 *functionsTable = reinterpret_cast(objectPtr + objectToWrite->offsetToFunctions); + quint32_le *functionsTable = reinterpret_cast(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = o->runtimeFunctionIndices.at(f->index); @@ -1493,7 +1493,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias); Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount())); - QV4::CompiledData::LEUInt32 *signalOffsetTable = reinterpret_cast(objectPtr + objectToWrite->offsetToSignals); + quint32_le *signalOffsetTable = reinterpret_cast(objectPtr + objectToWrite->offsetToSignals); quint32 signalTableSize = 0; char *signalPtr = objectPtr + nextOffset; for (const Signal *s = o->firstSignal(); s; s = s->next) { @@ -1513,7 +1513,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: signalPtr += size; } - QV4::CompiledData::LEUInt32 *namedObjectInComponentPtr = reinterpret_cast(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); + quint32_le *namedObjectInComponentPtr = reinterpret_cast(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } @@ -2225,7 +2225,7 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; - const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); + const quint32_le *functionIdx = serializedObject->functionOffsetTable(); for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { QmlIR::Function *f = pool->New(); const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); @@ -2236,7 +2236,7 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO f->nameIndex = compiledFunction->nameIndex; QQmlJS::AST::FormalParameterList *paramList = 0; - const QV4::CompiledData::LEUInt32 *formalNameIdx = compiledFunction->formalsTable(); + const quint32_le *formalNameIdx = compiledFunction->formalsTable(); for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) { const QString formal = unit->stringAt(*formalNameIdx); QStringRef paramNameRef = jsParserEngine->newStringRef(formal); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 94abf416a1..be7429df41 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -187,7 +187,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) #if Q_BYTE_ORDER == Q_BIG_ENDIAN Value *bigEndianConstants = new Value[data->constantTableSize]; - const LEUInt64 *littleEndianConstants = data->constants(); + const quint64_le *littleEndianConstants = data->constants(); for (uint i = 0; i < data->constantTableSize; ++i) bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); constants = bigEndianConstants; @@ -271,7 +271,7 @@ IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjec if (it == namedObjectsPerComponentCache.end()) { IdentifierHash namedObjectCache(engine); const CompiledData::Object *component = data->objectAt(componentObjectIndex); - const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index ff0d597b92..876244437e 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -62,7 +62,7 @@ #include #include #include -#include +#include #ifndef V4_BOOTSTRAP #include #include @@ -96,13 +96,6 @@ class CompilationUnitMapper; namespace CompiledData { -typedef QJsonPrivate::q_littleendian LEInt16; -typedef QJsonPrivate::q_littleendian LEUInt16; -typedef QJsonPrivate::q_littleendian LEUInt32; -typedef QJsonPrivate::q_littleendian LEInt32; -typedef QJsonPrivate::q_littleendian LEUInt64; -typedef QJsonPrivate::q_littleendian LEInt64; - struct String; struct Function; struct Lookup; @@ -130,8 +123,8 @@ struct Location { union { quint32 _dummy; - QJsonPrivate::qle_bitfield<0, 20> line; - QJsonPrivate::qle_bitfield<20, 12> column; + quint32_le_bitfield<0, 20> line; + quint32_le_bitfield<20, 12> column; }; Location() : _dummy(0) { } @@ -151,8 +144,8 @@ struct RegExp }; union { quint32 _dummy; - QJsonPrivate::qle_bitfield<0, 4> flags; - QJsonPrivate::qle_bitfield<4, 28> stringIndex; + quint32_le_bitfield<0, 4> flags; + quint32_le_bitfield<4, 28> stringIndex; }; RegExp() : _dummy(0) { } @@ -170,8 +163,8 @@ struct Lookup union { quint32 _dummy; - QJsonPrivate::qle_bitfield<0, 4> type_and_flags; - QJsonPrivate::qle_bitfield<4, 28> nameIndex; + quint32_le_bitfield<0, 4> type_and_flags; + quint32_le_bitfield<4, 28> nameIndex; }; Lookup() : _dummy(0) { } @@ -181,8 +174,8 @@ struct JSClassMember { union { quint32 _dummy; - QJsonPrivate::qle_bitfield<0, 31> nameOffset; - QJsonPrivate::qle_bitfield<31, 1> isAccessor; + quint32_le_bitfield<0, 31> nameOffset; + quint32_le_bitfield<31, 1> isAccessor; }; JSClassMember() : _dummy(0) { } @@ -190,7 +183,7 @@ struct JSClassMember struct JSClass { - LEUInt32 nMembers; + quint32_le nMembers; // JSClassMember[nMembers] static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } @@ -198,7 +191,7 @@ struct JSClass struct String { - LEInt32 size; + qint32_le size; // uint16 strdata[] static int calculateSize(const QString &str) { @@ -221,24 +214,24 @@ struct Function // Absolute offset into file where the code for this function is located. Only used when the function // is serialized. - LEUInt64 codeOffset; - LEUInt64 codeSize; - - LEUInt32 nameIndex; - LEUInt32 nFormals; - LEUInt32 formalsOffset; - LEUInt32 nLocals; - LEUInt32 localsOffset; - LEUInt32 nInnerFunctions; + quint64_le codeOffset; + quint64_le codeSize; + + quint32_le nameIndex; + quint32_le nFormals; + quint32_le formalsOffset; + quint32_le nLocals; + quint32_le localsOffset; + quint32_le nInnerFunctions; Location location; // Qml Extensions Begin - LEUInt32 nDependingIdObjects; - LEUInt32 dependingIdObjectsOffset; // Array of resolved ID objects - LEUInt32 nDependingContextProperties; - LEUInt32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) - LEUInt32 nDependingScopeProperties; - LEUInt32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) + quint32_le nDependingIdObjects; + quint32_le dependingIdObjectsOffset; // Array of resolved ID objects + quint32_le nDependingContextProperties; + quint32_le dependingContextPropertiesOffset; // Array of int pairs (property index and notify index) + quint32_le nDependingScopeProperties; + quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index) // Qml Extensions End // quint32 formalsIndex[nFormals] @@ -249,15 +242,15 @@ struct Function // Keep all unaligned data at the end quint8 flags; - const LEUInt32 *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } - const LEUInt32 *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } - const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset); } - const LEUInt32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset); } - const LEUInt32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset); } + const quint32_le *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } + const quint32_le *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } + const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset); } + const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset); } + const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset); } // --- QQmlPropertyCacheCreator interface - const LEUInt32 *formalsBegin() const { return formalsTable(); } - const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; } + const quint32_le *formalsBegin() const { return formalsTable(); } + const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } @@ -270,13 +263,13 @@ struct Function // Qml data structures struct Q_QML_EXPORT TranslationData { - LEUInt32 commentIndex; - LEInt32 number; + quint32_le commentIndex; + qint32_le number; }; struct Q_QML_PRIVATE_EXPORT Binding { - LEUInt32 propertyNameIndex; + quint32_le propertyNameIndex; enum ValueType : unsigned int { Type_Invalid, @@ -304,17 +297,17 @@ struct Q_QML_PRIVATE_EXPORT Binding }; union { - QJsonPrivate::qle_bitfield<0, 16> flags; - QJsonPrivate::qle_bitfield<16, 16> type; + quint32_le_bitfield<0, 16> flags; + quint32_le_bitfield<16, 16> type; }; union { bool b; quint64 doubleValue; // do not access directly, needs endian protected access - LEUInt32 compiledScriptIndex; // used when Type_Script - LEUInt32 objectIndex; + quint32_le compiledScriptIndex; // used when Type_Script + quint32_le objectIndex; TranslationData translationData; // used when Type_Translation } value; - LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) + quint32_le stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) Location location; Location valueLocation; @@ -399,16 +392,16 @@ struct Q_QML_PRIVATE_EXPORT Binding struct Parameter { - LEUInt32 nameIndex; - LEUInt32 type; - LEUInt32 customTypeNameIndex; + quint32_le nameIndex; + quint32_le type; + quint32_le customTypeNameIndex; Location location; }; struct Signal { - LEUInt32 nameIndex; - LEUInt32 nParameters; + quint32_le nameIndex; + quint32_le nParameters; Location location; // Parameter parameters[1]; @@ -440,12 +433,12 @@ struct Property IsReadOnly = 0x1 }; - LEUInt32 nameIndex; + quint32_le nameIndex; union { - QJsonPrivate::qle_bitfield<0, 31> type; - QJsonPrivate::qle_bitfield<31, 1> flags; // readonly + quint32_le_bitfield<0, 31> type; + quint32_le_bitfield<31, 1> flags; // readonly }; - LEUInt32 customTypeNameIndex; // If type >= Custom + quint32_le customTypeNameIndex; // If type >= Custom Location location; }; @@ -456,18 +449,18 @@ struct Alias { AliasPointsToPointerObject = 0x4 }; union { - QJsonPrivate::qle_bitfield<0, 29> nameIndex; - QJsonPrivate::qle_bitfield<29, 3> flags; + quint32_le_bitfield<0, 29> nameIndex; + quint32_le_bitfield<29, 3> flags; }; union { - LEUInt32 idIndex; // string index - QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) - QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias; + quint32_le idIndex; // string index + quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues) + quint32_le_bitfield<31, 1> aliasToLocalAlias; }; union { - LEUInt32 propertyNameIndex; // string index - LEInt32 encodedMetaPropertyIndex; - LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) + quint32_le propertyNameIndex; // string index + qint32_le encodedMetaPropertyIndex; + quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId) }; Location location; Location referenceLocation; @@ -490,26 +483,26 @@ struct Object // Depending on the use, this may be the type name to instantiate before instantiating this // object. For grouped properties the type name will be empty and for attached properties // it will be the name of the attached type. - LEUInt32 inheritedTypeNameIndex; - LEUInt32 idNameIndex; + quint32_le inheritedTypeNameIndex; + quint32_le idNameIndex; union { - QJsonPrivate::qle_bitfield<0, 15> flags; - QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias; - QJsonPrivate::qle_signedbitfield<16, 16> id; + quint32_le_bitfield<0, 15> flags; + quint32_le_bitfield<15, 1> defaultPropertyIsAlias; + qint32_le_bitfield<16, 16> id; }; - LEInt32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object - LEUInt32 nFunctions; - LEUInt32 offsetToFunctions; - LEUInt32 nProperties; - LEUInt32 offsetToProperties; - LEUInt32 nAliases; - LEUInt32 offsetToAliases; - LEUInt32 nSignals; - LEUInt32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects - LEUInt32 nBindings; - LEUInt32 offsetToBindings; - LEUInt32 nNamedObjectsInComponent; - LEUInt32 offsetToNamedObjectsInComponent; + qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object + quint32_le nFunctions; + quint32_le offsetToFunctions; + quint32_le nProperties; + quint32_le offsetToProperties; + quint32_le nAliases; + quint32_le offsetToAliases; + quint32_le nSignals; + quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects + quint32_le nBindings; + quint32_le offsetToBindings; + quint32_le nNamedObjectsInComponent; + quint32_le offsetToNamedObjectsInComponent; Location location; Location locationOfIdProperty; // Function[] @@ -530,9 +523,9 @@ struct Object ) & ~0x7; } - const LEUInt32 *functionOffsetTable() const + const quint32_le *functionOffsetTable() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToFunctions); + return reinterpret_cast(reinterpret_cast(this) + offsetToFunctions); } const Property *propertyTable() const @@ -552,14 +545,14 @@ struct Object const Signal *signalAt(int idx) const { - const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); - const LEUInt32 offset = offsetTable[idx]; + const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); + const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } - const LEUInt32 *namedObjectsInComponentTable() const + const quint32_le *namedObjectsInComponentTable() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); + return reinterpret_cast(reinterpret_cast(this) + offsetToNamedObjectsInComponent); } // --- QQmlPropertyCacheCreator interface @@ -592,13 +585,13 @@ struct Import ImportFile = 0x2, ImportScript = 0x3 }; - LEUInt32 type; + quint32_le type; - LEUInt32 uriIndex; - LEUInt32 qualifierIndex; + quint32_le uriIndex; + quint32_le qualifierIndex; - LEInt32 majorVersion; - LEInt32 minorVersion; + qint32_le majorVersion; + qint32_le minorVersion; Location location; @@ -611,17 +604,17 @@ struct Unit { // DO NOT CHANGE THESE FIELDS EVER char magic[8]; - LEUInt32 version; - LEUInt32 qtVersion; - LEInt64 sourceTimeStamp; - LEUInt32 unitSize; // Size of the Unit and any depending data. + quint32_le version; + quint32_le qtVersion; + qint64_le sourceTimeStamp; + quint32_le unitSize; // Size of the Unit and any depending data. // END DO NOT CHANGE THESE FIELDS EVER char md5Checksum[16]; // checksum of all bytes following this field. void generateChecksum(); - LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi() - LEUInt32 codeGeneratorIndex; + quint32_le architectureIndex; // string index to QSysInfo::buildAbi() + quint32_le codeGeneratorIndex; char dependencyMD5Checksum[16]; enum : unsigned int { @@ -633,36 +626,36 @@ struct Unit ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation }; - LEUInt32 flags; - LEUInt32 stringTableSize; - LEUInt32 offsetToStringTable; - LEUInt32 functionTableSize; - LEUInt32 offsetToFunctionTable; - LEUInt32 lookupTableSize; - LEUInt32 offsetToLookupTable; - LEUInt32 regexpTableSize; - LEUInt32 offsetToRegexpTable; - LEUInt32 constantTableSize; - LEUInt32 offsetToConstantTable; - LEUInt32 jsClassTableSize; - LEUInt32 offsetToJSClassTable; - LEInt32 indexOfRootFunction; - LEUInt32 sourceFileIndex; + quint32_le flags; + quint32_le stringTableSize; + quint32_le offsetToStringTable; + quint32_le functionTableSize; + quint32_le offsetToFunctionTable; + quint32_le lookupTableSize; + quint32_le offsetToLookupTable; + quint32_le regexpTableSize; + quint32_le offsetToRegexpTable; + quint32_le constantTableSize; + quint32_le offsetToConstantTable; + quint32_le jsClassTableSize; + quint32_le offsetToJSClassTable; + qint32_le indexOfRootFunction; + quint32_le sourceFileIndex; /* QML specific fields */ - LEUInt32 nImports; - LEUInt32 offsetToImports; - LEUInt32 nObjects; - LEUInt32 offsetToObjects; - LEUInt32 indexOfRootObject; + quint32_le nImports; + quint32_le offsetToImports; + quint32_le nObjects; + quint32_le offsetToObjects; + quint32_le indexOfRootObject; const Import *importAt(int idx) const { return reinterpret_cast((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } const Object *objectAt(int idx) const { - const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToObjects); - const LEUInt32 offset = offsetTable[idx]; + const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToObjects); + const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } @@ -672,8 +665,8 @@ struct Unit /* end QML specific fields*/ QString stringAt(int idx) const { - const LEUInt32 *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToStringTable); - const LEUInt32 offset = offsetTable[idx]; + const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToStringTable); + const quint32_le offset = offsetTable[idx]; const String *str = reinterpret_cast(reinterpret_cast(this) + offset); if (str->size == 0) return QString(); @@ -685,7 +678,7 @@ struct Unit // return QString::fromRawData(characters, str->size); return QString(characters, str->size); #else - const LEUInt16 *characters = reinterpret_cast(str + 1); + const quint16_le *characters = reinterpret_cast(str + 1); QString qstr(str->size, Qt::Uninitialized); QChar *ch = qstr.data(); for (int i = 0; i < str->size; ++i) @@ -694,11 +687,11 @@ struct Unit #endif } - const LEUInt32 *functionOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToFunctionTable); } + const quint32_le *functionOffsetTable() const { return reinterpret_cast((reinterpret_cast(this)) + offsetToFunctionTable); } const Function *functionAt(int idx) const { - const LEUInt32 *offsetTable = functionOffsetTable(); - const LEUInt32 offset = offsetTable[idx]; + const quint32_le *offsetTable = functionOffsetTable(); + const quint32_le offset = offsetTable[idx]; return reinterpret_cast(reinterpret_cast(this) + offset); } @@ -706,13 +699,13 @@ struct Unit const RegExp *regexpAt(int index) const { return reinterpret_cast(reinterpret_cast(this) + offsetToRegexpTable + index * sizeof(RegExp)); } - const LEUInt64 *constants() const { - return reinterpret_cast(reinterpret_cast(this) + offsetToConstantTable); + const quint64_le *constants() const { + return reinterpret_cast(reinterpret_cast(this) + offsetToConstantTable); } const JSClassMember *jsClassAt(int idx, int *nMembers) const { - const LEUInt32 *offsetTable = reinterpret_cast(reinterpret_cast(this) + offsetToJSClassTable); - const LEUInt32 offset = offsetTable[idx]; + const quint32_le *offsetTable = reinterpret_cast(reinterpret_cast(this) + offsetToJSClassTable); + const quint32_le offset = offsetTable[idx]; const char *ptr = reinterpret_cast(this) + offset; const JSClass *klass = reinterpret_cast(ptr); *nMembers = klass->nMembers; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index a49388846d..35825587b1 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -78,7 +78,7 @@ void QV4::Compiler::StringTableGenerator::clear() void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast(unit); - CompiledData::LEUInt32 *stringTable = reinterpret_cast(dataStart + unit->offsetToStringTable); + quint32_le *stringTable = reinterpret_cast(dataStart + unit->offsetToStringTable); char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint); for (int i = 0; i < strings.size(); ++i) { stringTable[i] = stringData - dataStart; @@ -220,7 +220,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(*f->locals.at(i)); } - Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, irModule->functions.size() * sizeof(CompiledData::LEUInt32)); + Q_ALLOCA_VAR(quint32_le, functionOffsets, irModule->functions.size() * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; @@ -233,7 +233,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(unit, &tempHeader, sizeof(tempHeader)); } - memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32)); + memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le)); for (int i = 0; i < irModule->functions.size(); ++i) { QV4::IR::Function *function = irModule->functions.at(i); @@ -254,7 +254,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO ReturnedValue *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); #else - CompiledData::LEUInt64 *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); + quint64_le *constantTable = reinterpret_cast(dataPtr + unit->offsetToConstantTable); for (int i = 0; i < constants.count(); ++i) constantTable[i] = constants.at(i); #endif @@ -263,7 +263,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); // write js classes and js class lookup table - CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast(dataPtr + unit->offsetToJSClassTable); + quint32_le *jsClassOffsetTable = reinterpret_cast(dataPtr + unit->offsetToJSClassTable); for (int i = 0; i < jsClassOffsets.count(); ++i) jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i); } @@ -337,36 +337,36 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->codeSize = 0; // write formals - CompiledData::LEUInt32 *formals = (CompiledData::LEUInt32 *)(f + function->formalsOffset); + quint32_le *formals = (quint32_le *)(f + function->formalsOffset); for (int i = 0; i < irFunction->formals.size(); ++i) formals[i] = getStringId(*irFunction->formals.at(i)); // write locals - CompiledData::LEUInt32 *locals = (CompiledData::LEUInt32 *)(f + function->localsOffset); + quint32_le *locals = (quint32_le *)(f + function->localsOffset); for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(*irFunction->locals.at(i)); // write QML dependencies - CompiledData::LEUInt32 *writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingIdObjectsOffset); + quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset); for (int id : irFunction->idObjectDependencies) { Q_ASSERT(id >= 0); *writtenDeps++ = static_cast(id); } - writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingContextPropertiesOffset); + writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset); for (auto property : irFunction->contextObjectPropertyDependencies) { *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } - writtenDeps = (CompiledData::LEUInt32 *)(f + function->dependingScopePropertiesOffset); + writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset); for (auto property : irFunction->scopeObjectPropertyDependencies) { *writtenDeps++ = property.key(); // property index *writtenDeps++ = property.value(); // notify index } } -QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset) +QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset) { CompiledData::Unit unit; memset(&unit, 0, sizeof(unit)); diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 49b8664513..9c51a44bad 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -52,7 +52,7 @@ #include #include "qv4jsir_p.h" -#include +#include QT_BEGIN_NAMESPACE @@ -120,7 +120,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { StringTableGenerator stringTable; QString codeGeneratorName; private: - CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian *functionOffsets, uint *jsClassDataOffset); + CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset); IR::Module *irModule; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 4c8117527c..d1bdab1b3c 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -60,7 +60,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, Q_UNUSED(engine); internalClass = engine->internalClasses[EngineBase::Class_Empty]; - const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); + const quint32_le *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); ScopedString arg(scope); @@ -79,7 +79,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } nFormals = compiledFunction->nFormals; - const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); + const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); @@ -111,7 +111,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QListlocalsTable(); + const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9d4e46e254..9587b3961f 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -350,7 +350,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct QV4::Scoped context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); - const QV4::CompiledData::LEUInt32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); + const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); @@ -359,7 +359,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct } Q_ASSERT(qmlContext->contextObject); - const QV4::CompiledData::LEUInt32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); + const quint32_le *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; for (int i = 0; i < contextPropertyDependencyCount; ++i) { const int propertyIndex = *contextPropertyDependency++; @@ -369,7 +369,7 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct } QObject *scopeObject = context->qmlScope(); - const QV4::CompiledData::LEUInt32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); + const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; for (int i = 0; i < scopePropertyDependencyCount; ++i) { const int propertyIndex = *scopePropertyDependency++; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 2cbcfbbfb6..b73cbaa563 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -996,7 +996,7 @@ void QQmlObjectCreator::setupFunctions() QV4::ScopedValue function(scope); QV4::ScopedContext qmlContext(scope, currentQmlContext()); - const QV4::CompiledData::LEUInt32 *functionIdx = _compiledObject->functionOffsetTable(); + const quint32_le *functionIdx = _compiledObject->functionOffsetTable(); for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx]; const QString name = runtimeFunction->name()->toQString(); -- cgit v1.2.3 From b9767f03bdfcc06dae49661150fbeb0f19a8547b Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 13 Jun 2017 16:34:07 +0200 Subject: Doc: explain ListView default Z values in detail This should save users some time when they are trying to figure out why the delegate items are rendered underneath the header when headerPositioning is set to ListView.OverlayHeader, for example. Task-number: QTBUG-61346 Change-Id: I490250f2a64a8bbda463b3a31be6f820d0cfe881 Reviewed-by: J-P Nurmi Reviewed-by: Robin Burchell --- src/quick/items/qquicklistview.cpp | 62 +++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 18f9b8512d..979a3557a1 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -1835,6 +1835,38 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte \snippet qml/listview/listview.qml flickBothDirections + \section1 Stacking Order in ListView + + The \l {QQuickItem::z}{Z value} of items determines whether they are + rendered above or below other items. ListView uses several different + default Z values, depending on what type of item is being created: + + \table + \header + \li Property + \li Default Z value + \row + \li \l delegate + \li 1 + \row + \li \l footer + \li 1 + \row + \li \l header + \li 1 + \row + \li \l highlight + \li 0 + \row + \li \l section.delegate + \li 2 + \endtable + + These default values are set if the Z value of the item is \c 0, so setting + the Z value of these items to \c 0 has no effect. Note that the Z value is + of type \l [QML] {real}, so it is possible to set fractional + values like \c 0.1. + \sa {QML Data Models}, GridView, PathView, {Qt Quick Examples - Views} */ QQuickListView::QQuickListView(QQuickItem *parent) @@ -1963,6 +1995,8 @@ QQuickListView::~QQuickListView() \note Delegates are instantiated as needed and may be destroyed at any time. They are parented to ListView's \l {Flickable::contentItem}{contentItem}, not to the view itself. State should \e never be stored in a delegate. + + \sa {Stacking Order in ListView} */ /*! \qmlproperty int QtQuick::ListView::currentIndex @@ -1990,7 +2024,7 @@ QQuickListView::~QQuickListView() The default \l {QQuickItem::z}{stacking order} of the highlight item is \c 0. - \sa highlight, highlightFollowsCurrentItem + \sa highlight, highlightFollowsCurrentItem, {Stacking Order in ListView} */ /*! @@ -2009,7 +2043,8 @@ QQuickListView::~QQuickListView() highlight item is \c 0. \sa highlightItem, highlightFollowsCurrentItem, - {Qt Quick Examples - Views#Highlight}{ListView highlight example} + {Qt Quick Examples - Views#Highlight}{ListView highlight example}, + {Stacking Order in ListView} */ /*! @@ -2352,7 +2387,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation) differing sections will result in a section header being created even if that section exists elsewhere. - \sa {Qt Quick Examples - Views}{ListView examples} + \sa {Qt Quick Examples - Views}{ListView examples}, + {Stacking Order in ListView} */ QQuickViewSection *QQuickListView::sectionCriteria() { @@ -2503,7 +2539,7 @@ void QQuickListView::setSnapMode(SnapMode mode) footer is positioned at the end of the view, after any items. The default \l {QQuickItem::z}{stacking order} of the footer is \c 1. - \sa header, footerItem + \sa header, footerItem, {Stacking Order in ListView} */ @@ -2515,7 +2551,7 @@ void QQuickListView::setSnapMode(SnapMode mode) header is positioned at the beginning of the view, before any items. The default \l {QQuickItem::z}{stacking order} of the header is \c 1. - \sa footer, headerItem + \sa footer, headerItem, {Stacking Order in ListView} */ /*! @@ -2526,7 +2562,7 @@ void QQuickListView::setSnapMode(SnapMode mode) header is positioned at the beginning of the view, before any items. The default \l {QQuickItem::z}{stacking order} of the header is \c 1. - \sa header, footerItem + \sa header, footerItem, {Stacking Order in ListView} */ /*! @@ -2537,7 +2573,7 @@ void QQuickListView::setSnapMode(SnapMode mode) footer is positioned at the end of the view, after any items. The default \l {QQuickItem::z}{stacking order} of the footer is \c 1. - \sa footer, headerItem + \sa footer, headerItem, {Stacking Order in ListView} */ /*! @@ -2555,6 +2591,12 @@ void QQuickListView::setSnapMode(SnapMode mode) The header can be pushed away by moving the content forwards, and pulled back by moving the content backwards. \endlist + + \note This property has no effect on the \l {QQuickItem::z}{stacking order} + of the header. For example, if the header should be shown above the + \l delegate items when using \c ListView.OverlayHeader, its Z value + should be set to a value higher than that of the delegates. For more + information, see \l {Stacking Order in ListView}. */ QQuickListView::HeaderPositioning QQuickListView::headerPositioning() const { @@ -2592,6 +2634,12 @@ void QQuickListView::setHeaderPositioning(QQuickListView::HeaderPositioning posi The footer can be pushed away by moving the content backwards, and pulled back by moving the content forwards. \endlist + + \note This property has no effect on the \l {QQuickItem::z}{stacking order} + of the footer. For example, if the footer should be shown above the + \l delegate items when using \c ListView.OverlayFooter, its Z value + should be set to a value higher than that of the delegates. For more + information, see \l {Stacking Order in ListView}. */ QQuickListView::FooterPositioning QQuickListView::footerPositioning() const { -- cgit v1.2.3 From 9d546614a6b080baad66b4cddcb2afa83352e348 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 6 Jul 2017 14:31:07 +0200 Subject: shape: Revise performance notes in the docs Clear up some sentences and add some more recommendations. Change-Id: Iecfd90c63411aa6d17a9218122bada92b06f1cd3 Reviewed-by: J-P Nurmi Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 59 ++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index d81f560315..648c9df1ce 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -131,7 +131,7 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const \brief Describes a Path and associated properties for stroking and filling \since 5.10 - A Shape contains one or more ShapePath elements. At least one ShapePath is + A \l Shape contains one or more ShapePath elements. At least one ShapePath is necessary in order to have a Shape output anything visible. A ShapePath itself is a \l Path with additional properties describing the stroking and filling parameters, such as the stroke width and color, the fill color or @@ -593,27 +593,6 @@ void QQuickShapePath::resetFillGradient() useful since it allows adding visual items, like \l Rectangle or \l Image, and non-visual objects, like \l Timer directly as children of Shape. - \note It is important to be aware of performance implications, in particular - when the application is running on the generic Shape implementation due to - not having support for accelerated path rendering. The geometry generation - happens entirely on the CPU in this case, and this is potentially - expensive. Changing the set of path elements, changing the properties of - these elements, or changing certain properties of the Shape itself all lead - to retriangulation of the affected elements on every change. Therefore, - applying animation to such properties can affect performance on less - powerful systems. If animating properties other than stroke and fill colors - is a must, it is recommended to target systems providing - \c{GL_NV_path_rendering} where the cost of path property changes is much - smaller. - - \note However, the data-driven, declarative nature of the Shape API often - means better cacheability for the underlying CPU and GPU resources. A - property change in one ShapePath will only lead to reprocessing the affected - ShapePath, leaving other parts of the Shape unchanged. Therefore, a heavily - changing (for example, animating) property can often result in a lower - overall system load than with imperative painting approaches (for example, - QPainter). - The following list summarizes the available Shape rendering approaches: \list @@ -634,6 +613,42 @@ void QQuickShapePath::resetFillGradient() \endlist + When using Shape, it is important to be aware of potential performance + implications: + + \li When the application is running with the generic, triangulation-based + Shape implementation, the geometry generation happens entirely on the + CPU. This is potentially expensive. Changing the set of path elements, + changing the properties of these elements, or changing certain properties of + the Shape itself all lead to retriangulation of the affected paths on every + change. Therefore, applying animation to such properties can affect + performance on less powerful systems. + + \li However, the data-driven, declarative nature of the Shape API often + means better cacheability for the underlying CPU and GPU resources. A + property change in one ShapePath will only lead to reprocessing the affected + ShapePath, leaving other parts of the Shape unchanged. Therefore, a + frequently changing property can still result in a lower overall system load + than with imperative painting approaches (for example, QPainter). + + \li If animating properties other than stroke and fill colors is a must, it + is recommended to target systems providing \c{GL_NV_path_rendering} where + the cost of property changes is smaller. + + \li At the same time, attention must be paid to the number of Shape elements + in the scene, in particular when using this special accelerated approach for + \c{GL_NV_path_rendering}. The way such a Shape item is represented in the + scene graph is different from an ordinary geometry-based item, and incurs a + certain cost when it comes to OpenGL state changes. + + \li As a general rule, scenes should avoid using separate Shape items when + it is not absolutely necessary. Prefer using one Shape item with multiple + ShapePath elements over multiple Shape items. Scenes that cannot avoid using + a large number of individual Shape items should consider setting + Shape.vendorExtensionsEnabled to \c false. + + \endlist + \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg */ -- cgit v1.2.3 From a670d760e351987ad23b445bfb08f6add1fb067b Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 6 Jul 2017 14:59:06 +0200 Subject: shapes: Add example links Change-Id: I9826058b6f721a6d3a85878ab872864e0fc494ac Reviewed-by: J-P Nurmi --- src/imports/shapes/qquickshape.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 648c9df1ce..3d90ca0c1f 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -171,6 +171,8 @@ QPainterPath QQuickShapePathCommands::toPainterPath() const of 2 (ShapePath.RoundJoin): \image visualpath-code-example.png + + \sa {Qt Quick Examples - Shapes}, Shape */ QQuickShapePathPrivate::QQuickShapePathPrivate() @@ -649,7 +651,7 @@ void QQuickShapePath::resetFillGradient() \endlist - \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg + \sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg */ QQuickShapePrivate::QQuickShapePrivate() -- cgit v1.2.3 From 65ef4bab4ad0ef4a45ff56de3e143a588deac364 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 14 Jun 2017 13:15:45 +0200 Subject: QQuickText: don't clear the text formats on every layout In order to fix QTBUG-21919, 6ff9ba0 added a QTextLayout::clearFormats() call to QQuickTextPrivate::updateLayout(). This patch moves that logic to clearFormats(), called from setText() and setTextFormat() in order to avoid clearing the formats on every text layout update. This allows Qt Quick Controls 2 to extend QQuickText with support for mnenonics by adding a text format range to underline the appropriate piece of text in the internal QTextLayout. Task-number: QTBUG-61422 Change-Id: I646d53f0feeeaa3c106db94f187c7accabdc6a61 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktext.cpp | 15 +++++++++++---- src/quick/items/qquicktext_p_p.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 1bcfbd41f7..2e66367e85 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -269,9 +269,6 @@ void QQuickTextPrivate::updateLayout() formatModifiesFontSize = fontSizeModified; multilengthEos = -1; } else { - layout.clearFormats(); - if (elideLayout) - elideLayout->clearFormats(); QString tmp = text; multilengthEos = tmp.indexOf(QLatin1Char('\x9c')); if (multilengthEos != -1) @@ -632,6 +629,13 @@ QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QT } } +void QQuickTextPrivate::clearFormats() +{ + layout.clearFormats(); + if (elideLayout) + elideLayout->clearFormats(); +} + /*! Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText. @@ -1060,7 +1064,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) elideLayout = new QTextLayout; elideLayout->setCacheEnabled(true); } - if (styledText) { + QTextEngine *engine = layout.engine(); + if (engine && engine->hasFormats()) { QVector formats; switch (elideMode) { case QQuickText::ElideRight: @@ -1612,6 +1617,7 @@ void QQuickText::setText(const QString &n) d->extra->doc->setText(n); d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft(); } else { + d->clearFormats(); d->rightToLeftText = d->text.isRightToLeft(); } d->determineHorizontalAlignment(); @@ -2102,6 +2108,7 @@ void QQuickText::setTextFormat(TextFormat format) d->extra->doc->setText(d->text); d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft(); } else { + d->clearFormats(); d->rightToLeftText = d->text.isRightToLeft(); d->textHasChanged = true; } diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index fde07eaf2e..957641ec0a 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -85,6 +85,7 @@ public: int lineHeightOffset() const; QString elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine = 0) const; void elideFormats(int start, int length, int offset, QVector *elidedFormats); + void clearFormats(); void processHoverEvent(QHoverEvent *event); -- cgit v1.2.3 From 286f14f1e29e7f4e2db4517d087dd5c92606f971 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 5 Jul 2017 12:53:01 +0200 Subject: Add QQuickItem::ItemEnabledHasChanged The itemChange() method has been very useful for Qt Quick Controls 2 to efficiently react to various item changes, but a notification for the enabled state was missing, so it always had to be handled as a special case using signals and slots. This change allows QQC2 to handle enabled state changes the same way e.g. visibility changes are handled. It's also nice to be able to update a control's internal state before the actual notifier signal is emitted. [ChangeLog][QtQuick][QQuickItem] Added a ItemEnabledHasChanged value to the ItemChange enum. QQuickItem::itemChange(ItemEnabledHasChanged) gets called when the item's effective enabled state has changed. The new enabled state is stored in ItemChangeData::boolValue. Change-Id: Iae96ec21f2b94f453632282473decd1c66097a75 Reviewed-by: Mitch Curtis Reviewed-by: Robin Burchell --- src/quick/items/qquickitem.cpp | 16 ++++++++++++++++ src/quick/items/qquickitem.h | 3 ++- src/quick/items/qquickitem_p.h | 3 ++- src/quick/items/qquickitemchangelistener_p.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index a8a862bb2f..1e71c20ff3 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2138,6 +2138,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) \value ItemAntialiasingHasChanged The antialiasing has changed. The current (boolean) value can be found in QQuickItem::antialiasing. + + \value ItemEnabledHasChanged The item's enabled state has changed. + ItemChangeData::boolValue contains the new enabled state. (since Qt 5.10) */ /*! @@ -5927,6 +5930,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); } + itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable); emit q->enabledChanged(); } @@ -6099,6 +6103,18 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } break; } + case QQuickItem::ItemEnabledHasChanged: { + q->itemChange(change, data); + if (!changeListeners.isEmpty()) { + const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732) + for (const QQuickItemPrivate::ChangeListener &change : listeners) { + if (change.types & QQuickItemPrivate::Enabled) { + change.listener->itemEnabledChanged(q); + } + } + } + break; + } case QQuickItem::ItemParentHasChanged: { q->itemChange(change, data); if (!changeListeners.isEmpty()) { diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index f58946d01d..4a832bbf6f 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -173,7 +173,8 @@ public: ItemActiveFocusHasChanged, // value.boolValue ItemRotationHasChanged, // value.realValue ItemAntialiasingHasChanged, // value.boolValue - ItemDevicePixelRatioHasChanged // value.realValue + ItemDevicePixelRatioHasChanged, // value.realValue + ItemEnabledHasChanged // value.boolValue }; union ItemChangeData { diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index e56d839de9..74d70da2bc 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -321,7 +321,8 @@ public: Children = 0x40, Rotation = 0x80, ImplicitWidth = 0x100, - ImplicitHeight = 0x200 + ImplicitHeight = 0x200, + Enabled = 0x400, }; Q_DECLARE_FLAGS(ChangeTypes, ChangeType) diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h index 83c69a9330..cb0af75c4c 100644 --- a/src/quick/items/qquickitemchangelistener_p.h +++ b/src/quick/items/qquickitemchangelistener_p.h @@ -125,6 +125,7 @@ public: virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {} virtual void itemSiblingOrderChanged(QQuickItem *) {} virtual void itemVisibilityChanged(QQuickItem *) {} + virtual void itemEnabledChanged(QQuickItem *) {} virtual void itemOpacityChanged(QQuickItem *) {} virtual void itemDestroyed(QQuickItem *) {} virtual void itemChildAdded(QQuickItem *, QQuickItem * /* child */ ) {} -- cgit v1.2.3 From c7b22fd4d3bc8e5e4ea17b5f101d5d062d7f5d62 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 11 Jul 2017 11:42:10 +0200 Subject: =?UTF-8?q?Doc:=20finish=20incomplete=20sentence=20in=20Loader?= =?UTF-8?q?=E2=80=99s=20detailed=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-61889 Change-Id: Ib6adcabc79b75fe2ee9a31fc4808a2a5f303df74 Reviewed-by: Topi Reiniö --- src/quick/items/qquickloader.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 5d5934bbd2..2c8f854d4d 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -270,6 +270,7 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const In some cases you may wish to use a Loader within a view delegate to improve delegate loading performance. This works well in most cases, but there is one important issue to + be aware of related to the \l{QtQml::Component#Creation Context}{creation context} of a Component. In the following example, the \c index context property inserted by the ListView into \c delegateComponent's context will be inaccessible to Text, as the Loader will use the creation context of \c myComponent as the parent -- cgit v1.2.3 From 8b0c90be00887a99c638f6834c53ee31104e1217 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 12 Jul 2017 14:28:53 +0200 Subject: QDoc: Fix syntax error on the definition of the Q_GADGET Change-Id: I1a0ec09441e7791bcdaf34f16a71be9e535625f0 Reviewed-by: Simon Hausmann --- src/qml/doc/src/cppintegration/data.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 4523ee39d8..b5c5505628 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -352,7 +352,7 @@ properties: private: QString m_name; - } + }; Q_DECLARE_METATYPE(Actor) \endcode -- cgit v1.2.3 From f812edbb5f3edc8a2349ce386139ad08144e665d Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 13 Jul 2017 09:48:05 +0200 Subject: Doc: link to Qt::WindowFlags in Window's flag doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0d5f03bcdcf9154431ed38eac2b41b622ad0c3d6 Reviewed-by: Topi Reiniö --- src/quick/items/qquickwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 31f367ed96..c124150b8d 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4085,6 +4085,8 @@ void QQuickWindow::resetOpenGLState() The flags which you read from this property might differ from the ones that you set if the requested flags could not be fulfilled. + + \sa Qt::WindowFlags */ /*! -- cgit v1.2.3 From ab5d4c78224c9ec79165e8890e5f8b8e838e0709 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Mon, 10 Jul 2017 17:26:59 +0100 Subject: Rebuild QQmlData::propertyCache if deleted by another engine QQmlData is shared between engines, but the relevant QObjectWrapper is not. Since 749a7212e903d8e8c6f256edb1836b9449cc7fe1 when a QObjectWrapper is deleted it resets the shared QQmlData propertyCache. In most cases the propertyCache except when a property updated in an existing binding in the first engine, where it currently asserts. Task-number: QTBUG-61681 Change-Id: I6efdc506e5c7e30b95cda1be282afa9feb781cd2 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 62288a5845..325f752cd5 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -515,7 +515,12 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD Q_ASSERT(propertyData); QQmlData *data = QQmlData::get(*m_target, false); - Q_ASSERT(data && data->propertyCache); + Q_ASSERT(data); + + if (Q_UNLIKELY(!data->propertyCache)) { + data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); + data->propertyCache->addref(); + } *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); Q_ASSERT(*propertyData); -- cgit v1.2.3 From d0eb6f9e07156880c6d9de4b98b2e4abfc4a0bfb Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 27 Jun 2017 12:11:02 +0200 Subject: Do not break qtlocation when integrating Once pointerhandlers are merged it should be fixed in qtlocation, and then this should be removed. Change-Id: I8b6364fff762417ac45ea76bfe1c06836c7fdf15 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickevents_p_p.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b39e81d6e8..0d6e06ac41 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -306,6 +306,7 @@ public: void setExclusiveGrabber(QObject *exclusiveGrabber); QQuickItem *grabberItem() const; + Q_DECL_DEPRECATED QQuickItem *grabber() const { return grabberItem(); } void setGrabberItem(QQuickItem *exclusiveGrabber); QQuickPointerHandler *grabberPointerHandler() const; -- cgit v1.2.3 From 22a2cc43387ec3b9f74a6c01f8665378a4541147 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 20 Apr 2017 15:59:31 +0200 Subject: Add support for enum declarations in QML Enums can be declared with the following syntax: enum MyEnum { Value1, Value2 } Grammar changes done by Simon Hausmann. [ChangeLog][QtQml] Enums can now be declared directly in QML. Task-number: QTBUG-14861 Change-Id: Ic6b6e032651d01ee2ecf9d5ce5734976cb3ad7ab Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 94 +- src/qml/compiler/qqmlirbuilder_p.h | 26 + src/qml/compiler/qqmlpropertycachecreator_p.h | 19 +- src/qml/compiler/qv4compileddata_p.h | 47 +- .../qmllanguageref/syntax/objectattributes.qdoc | 38 + src/qml/jsruntime/qv4qmlcontext.cpp | 2 +- src/qml/parser/qqmljs.g | 34 + src/qml/parser/qqmljsast.cpp | 17 + src/qml/parser/qqmljsast_p.h | 67 +- src/qml/parser/qqmljsastfwd_p.h | 2 + src/qml/parser/qqmljsastvisitor_p.h | 4 + src/qml/parser/qqmljsgrammar.cpp | 2111 ++++++++++---------- src/qml/parser/qqmljsgrammar_p.h | 295 +-- src/qml/parser/qqmljskeywords_p.h | 2 +- src/qml/parser/qqmljslexer_p.h | 1 - src/qml/parser/qqmljsparser.cpp | 384 ++-- src/qml/parser/qqmljsparser_p.h | 5 +- src/qml/qml/qqmlimport.cpp | 36 +- src/qml/qml/qqmlimport_p.h | 13 +- src/qml/qml/qqmlmetatype.cpp | 151 +- src/qml/qml/qqmlmetatype_p.h | 9 +- src/qml/qml/qqmlpropertycache.cpp | 21 +- src/qml/qml/qqmlpropertycache_p.h | 33 +- src/qml/qml/qqmltypeloader.cpp | 4 + src/qml/qml/qqmltypenamecache.cpp | 4 +- src/qml/qml/qqmltypenamecache_p.h | 2 +- 26 files changed, 1916 insertions(+), 1505 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index df615e6804..e05c38e14a 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -86,6 +86,7 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons flags = QV4::CompiledData::Object::NoFlag; properties = pool->New >(); aliases = pool->New >(); + qmlEnums = pool->New>(); qmlSignals = pool->New >(); bindings = pool->New >(); functions = pool->New >(); @@ -118,6 +119,21 @@ QString Object::sanityCheckFunctionNames(const QSet &illegalNames, QQml return QString(); // no error } +QString Object::appendEnum(Enum *enumeration) +{ + Object *target = declarationsOverride; + if (!target) + target = this; + + for (Enum *e = qmlEnums->first; e; e = e->next) { + if (e->nameIndex == enumeration->nameIndex) + return tr("Duplicate scoped enum name"); + } + + target->qmlEnums->append(enumeration); + return QString(); // no error +} + QString Object::appendSignal(Signal *signal) { Object *target = declarationsOverride; @@ -709,6 +725,48 @@ static QStringList astNodeToStringList(QQmlJS::AST::Node *node) return QStringList(); } +bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) +{ + Enum *enumeration = New(); + QString enumName = node->name.toString(); + enumeration->nameIndex = registerString(enumName); + + if (enumName.at(0).isLower()) + COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter")); + + enumeration->location.line = node->enumToken.startLine; + enumeration->location.column = node->enumToken.startColumn; + + enumeration->enumValues = New>(); + + QQmlJS::AST::UiEnumMemberList *e = node->members; + int i = -1; + while (e) { + EnumValue *enumValue = New(); + QString member = e->member.toString(); + enumValue->nameIndex = registerString(member); + enumValue->value = ++i; + + if (member.at(0).isLower()) + COMPILE_EXCEPTION(e->memberToken, tr("Enum names must begin with an upper case letter")); + + enumValue->location.line = e->memberToken.startLine; + enumValue->location.column = e->memberToken.startColumn; + enumeration->enumValues->append(enumValue); + + e = e->next; + } + + QString error = _object->appendEnum(enumeration); + if (!error.isEmpty()) { + recordError(node->enumToken, error); + return false; + } + + return false; +} + + bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) { static const struct TypeNameToType { @@ -1375,13 +1433,19 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: int objectsSize = 0; for (Object *o : qAsConst(output.objects)) { objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); + objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count); objectsSize += signalTableSize; + + int enumTableSize = 0; + for (const Enum *e = o->firstEnum(); e; e = e->next) + enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count); + + objectsSize += enumTableSize; } const int totalSize = unitSize + importSize + objectOffsetTableSize + objectsSize + output.jsGenerator.stringTable.sizeOfTableAndData(); @@ -1455,6 +1519,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: objectToWrite->offsetToAliases = nextOffset; nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias); + objectToWrite->nEnums = o->enumCount(); + objectToWrite->offsetToEnums = nextOffset; + nextOffset += objectToWrite->nEnums * sizeof(quint32); + objectToWrite->nSignals = o->signalCount(); objectToWrite->offsetToSignals = nextOffset; nextOffset += objectToWrite->nSignals * sizeof(quint32); @@ -1512,14 +1580,36 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: signalTableSize += size; signalPtr += size; } + nextOffset += signalTableSize; + + quint32_le *enumOffsetTable = reinterpret_cast(objectPtr + objectToWrite->offsetToEnums); + quint32 enumTableSize = 0; + char *enumPtr = objectPtr + nextOffset; + for (const Enum *e = o->firstEnum(); e; e = e->next) { + *enumOffsetTable++ = enumPtr - objectPtr; + QV4::CompiledData::Enum *enumToWrite = reinterpret_cast(enumPtr); + + enumToWrite->nameIndex = e->nameIndex; + enumToWrite->location = e->location; + enumToWrite->nEnumValues = e->enumValues->count; + + QV4::CompiledData::EnumValue *enumValueToWrite = reinterpret_cast(enumPtr + sizeof(*enumToWrite)); + for (EnumValue *enumValue = e->enumValues->first; enumValue; enumValue = enumValue->next, ++enumValueToWrite) + *enumValueToWrite = *enumValue; + + int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count); + enumTableSize += size; + enumPtr += size; + } quint32_le *namedObjectInComponentPtr = reinterpret_cast(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); objectPtr += signalTableSize; + objectPtr += enumTableSize; } // enable flag if we encountered pragma Singleton diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 64bf111d9a..4c29e0b9f5 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -265,6 +265,25 @@ public: struct Object; +struct EnumValue : public QV4::CompiledData::EnumValue +{ + EnumValue *next; +}; + +struct Enum +{ + int nameIndex; + QV4::CompiledData::Location location; + PoolList *enumValues; + + int enumValueCount() const { return enumValues->count; } + PoolList::Iterator enumValuesBegin() const { return enumValues->begin(); } + PoolList::Iterator enumValuesEnd() const { return enumValues->end(); } + + Enum *next; +}; + + struct SignalParameter : public QV4::CompiledData::Parameter { SignalParameter *next; @@ -359,6 +378,8 @@ public: int propertyCount() const { return properties->count; } Alias *firstAlias() const { return aliases->first; } int aliasCount() const { return aliases->count; } + const Enum *firstEnum() const { return qmlEnums->first; } + int enumCount() const { return qmlEnums->count; } const Signal *firstSignal() const { return qmlSignals->first; } int signalCount() const { return qmlSignals->count; } Binding *firstBinding() const { return bindings->first; } @@ -372,6 +393,8 @@ public: PoolList::Iterator propertiesEnd() const { return properties->end(); } PoolList::Iterator aliasesBegin() const { return aliases->begin(); } PoolList::Iterator aliasesEnd() const { return aliases->end(); } + PoolList::Iterator enumsBegin() const { return qmlEnums->begin(); } + PoolList::Iterator enumsEnd() const { return qmlEnums->end(); } PoolList::Iterator signalsBegin() const { return qmlSignals->begin(); } PoolList::Iterator signalsEnd() const { return qmlSignals->end(); } PoolList::Iterator functionsBegin() const { return functions->begin(); } @@ -385,6 +408,7 @@ public: QString sanityCheckFunctionNames(const QSet &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); + QString appendEnum(Enum *enumeration); QString appendSignal(Signal *signal); QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); @@ -408,6 +432,7 @@ private: PoolList *properties; PoolList *aliases; + PoolList *qmlEnums; PoolList *qmlSignals; PoolList *bindings; PoolList *functions; @@ -482,6 +507,7 @@ public: bool visit(QQmlJS::AST::UiArrayBinding *ast) override; bool visit(QQmlJS::AST::UiObjectBinding *ast) override; bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QQmlJS::AST::UiEnumDeclaration *ast) override; bool visit(QQmlJS::AST::UiPublicMember *ast) override; bool visit(QQmlJS::AST::UiScriptBinding *ast) override; bool visit(QQmlJS::AST::UiSourceElement *ast) override; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 3c14abc019..5901e4e13e 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -116,7 +116,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObje { const CompiledObject *obj = objectContainer->objectAt(objectIndex); - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0; + bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0; if (!needVMEMetaObject) { auto binding = obj->bindingsBegin(); auto end = obj->bindingsEnd(); @@ -244,7 +244,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj QQmlRefPointer cache; cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount())); + obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount())); propertyCaches->set(objectIndex, cache); propertyCaches->setNeedsVMEMetaObject(objectIndex); @@ -370,6 +370,21 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); } + auto e = obj->enumsBegin(); + auto eend = obj->enumsEnd(); + for ( ; e != eend; ++e) { + const int enumValueCount = e->enumValueCount(); + QVector values; + values.reserve(enumValueCount); + + auto enumValue = e->enumValuesBegin(); + auto end = e->enumValuesEnd(); + for ( ; enumValue != end; ++enumValue) + values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value)); + + cache->appendEnum(stringAt(e->nameIndex), values); + } + // Dynamic signals auto s = obj->signalsBegin(); auto send = obj->signalsEnd(); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 876244437e..386f3ae922 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -390,6 +390,36 @@ struct Q_QML_PRIVATE_EXPORT Binding }; +struct EnumValue +{ + quint32_le nameIndex; + qint32_le value; + Location location; +}; + +struct Enum +{ + quint32_le nameIndex; + quint32_le nEnumValues; + Location location; + + const EnumValue *enumValueAt(int idx) const { + return reinterpret_cast(this + 1) + idx; + } + + static int calculateSize(int nEnumValues) { + return (sizeof(Enum) + + nEnumValues * sizeof(EnumValue) + + 7) & ~0x7; + } + + // --- QQmlPropertyCacheCreatorInterface + const EnumValue *enumValuesBegin() const { return enumValueAt(0); } + const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); } + int enumValueCount() const { return nEnumValues; } + // --- +}; + struct Parameter { quint32_le nameIndex; @@ -497,6 +527,8 @@ struct Object quint32_le offsetToProperties; quint32_le nAliases; quint32_le offsetToAliases; + quint32_le nEnums; + quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects quint32_le nSignals; quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects quint32_le nBindings; @@ -510,12 +542,13 @@ struct Object // Signal[] // Binding[] - static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent) + static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent) { return ( sizeof(Object) + nFunctions * sizeof(quint32) + nProperties * sizeof(Property) + nAliases * sizeof(Alias) + + nEnums * sizeof(quint32) + nSignals * sizeof(quint32) + nBindings * sizeof(Binding) + nNamedObjectsInComponent * sizeof(int) @@ -543,6 +576,13 @@ struct Object return reinterpret_cast(reinterpret_cast(this) + offsetToBindings); } + const Enum *enumAt(int idx) const + { + const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToEnums); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast(reinterpret_cast(this) + offset); + } + const Signal *signalAt(int idx) const { const quint32_le *offsetTable = reinterpret_cast((reinterpret_cast(this)) + offsetToSignals); @@ -558,6 +598,7 @@ struct Object // --- QQmlPropertyCacheCreator interface int propertyCount() const { return nProperties; } int aliasCount() const { return nAliases; } + int enumCount() const { return nEnums; } int signalCount() const { return nSignals; } int functionCount() const { return nFunctions; } @@ -570,6 +611,10 @@ struct Object const Alias *aliasesBegin() const { return aliasTable(); } const Alias *aliasesEnd() const { return aliasTable() + nAliases; } + typedef TableIterator EnumIterator; + EnumIterator enumsBegin() const { return EnumIterator(this, 0); } + EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); } + typedef TableIterator SignalIterator; SignalIterator signalsBegin() const { return SignalIterator(this, 0); } SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); } diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 33f58dc1b9..befe575fe1 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -50,6 +50,7 @@ The set of QML object-type attribute types is as follows: \li signal handler attributes \li method attributes \li attached properties and attached signal handler attributes +\li enumeration attributes \endlist These attributes are discussed in detail below. @@ -975,4 +976,41 @@ ListView { Now \c delegateItem.ListView.isCurrentItem correctly refers to the \c isCurrentItem attached property of the delegate. +\section2 Enumeration Attributes + +Enumerations provide a fixed set of named choices. They can be declared in QML using the \c enum keyword: + +\qml +// MyText.qml +Text { + enum TextType { + Normal, + Heading + } +} +\endqml + +As shown above, enumeration types (e.g. \c TextType) and values (e.g. \c Normal) must begin with an uppercase letter. + +Values are referred to via \c {..} or \c {.}. + +\qml +// MyText.qml +Text { + enum TextType { + Normal, + Heading + } + + property int textType: MyText.TextType.Normal + + font.bold: textType == MyText.TextType.Heading + font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12 +} +\endqml + +More information on enumeration usage in QML can be found in the \l {QML Basic Types} \l enumeration documentation. + +The ability to declare enumerations in QML was introduced in Qt 5.10. + */ diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 61d785066f..89770d269a 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -134,7 +134,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP if (context->imports && name->startsWithUpper()) { // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = context->imports->query(name); + QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); if (r.isValid()) { if (hasProperty) diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index ca84e0c157..395e62e657 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -77,6 +77,7 @@ %token T_MULTILINE_STRING_LITERAL "multiline string literal" %token T_COMMENT "comment" %token T_COMPATIBILITY_SEMICOLON +%token T_ENUM "enum" --- context keywords. %token T_PUBLIC "public" @@ -281,6 +282,7 @@ public: AST::UiArrayMemberList *UiArrayMemberList; AST::UiQualifiedId *UiQualifiedId; AST::UiQualifiedPragmaId *UiQualifiedPragmaId; + AST::UiEnumMemberList *UiEnumMemberList; }; public: @@ -1207,6 +1209,37 @@ case $rule_number: { } break; ./ +UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE; +/. +case $rule_number: { + AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); + enumDeclaration->enumToken = loc(1); + enumDeclaration->rbraceToken = loc(5); + sym(1).Node = enumDeclaration; + break; +} +./ + +EnumMemberList: T_IDENTIFIER; +/. +case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1)); + node->memberToken = loc(1); + sym(1).Node = node; + break; +} +./ + +EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER; +/. +case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); + node->memberToken = loc(3); + sym(1).Node = node; + break; +} +./ + JsIdentifier: T_IDENTIFIER; JsIdentifier: T_PROPERTY ; @@ -1602,6 +1635,7 @@ ReservedIdentifier: T_DEFAULT ; ReservedIdentifier: T_DELETE ; ReservedIdentifier: T_DO ; ReservedIdentifier: T_ELSE ; +ReservedIdentifier: T_ENUM ; ReservedIdentifier: T_FALSE ; ReservedIdentifier: T_FINALLY ; ReservedIdentifier: T_FOR ; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 7f8cecca8f..2433522f42 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -967,6 +967,23 @@ void UiSourceElement::accept0(Visitor *visitor) visitor->endVisit(this); } +void UiEnumDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiEnumMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + } } // namespace QQmlJS::AST QT_QML_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 0de419d697..d458b2cd35 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -218,7 +218,9 @@ public: Kind_UiQualifiedPragmaId, Kind_UiScriptBinding, Kind_UiSourceElement, - Kind_UiHeaderItemList + Kind_UiHeaderItemList, + Kind_UiEnumDeclaration, + Kind_UiEnumMemberList }; inline Node() @@ -2785,6 +2787,69 @@ public: SourceLocation rbracketToken; }; +class QML_PARSER_EXPORT UiEnumMemberList: public Node +{ + QQMLJS_DECLARE_AST_NODE(UiEnumMemberList) +public: + UiEnumMemberList(const QStringRef &member) + : next(this), member(member) + { kind = K; } + + UiEnumMemberList(UiEnumMemberList *previous, const QStringRef &member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + SourceLocation firstSourceLocation() const override + { return memberToken; } + + SourceLocation lastSourceLocation() const override + { return next ? next->lastSourceLocation() : memberToken; } + + void accept0(Visitor *visitor) override; + + UiEnumMemberList *finish() + { + UiEnumMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiEnumMemberList *next; + QStringRef member; + SourceLocation memberToken; +}; + +class QML_PARSER_EXPORT UiEnumDeclaration: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiEnumDeclaration) + + UiEnumDeclaration(const QStringRef &name, + UiEnumMemberList *members) + : name(name) + , members(members) + { kind = K; } + + SourceLocation firstSourceLocation() const override + { return enumToken; } + + SourceLocation lastSourceLocation() const override + { return rbraceToken; } + + void accept0(Visitor *visitor) override; + +// attributes + SourceLocation enumToken; + SourceLocation rbraceToken; + QStringRef name; + UiEnumMemberList *members; +}; + } } // namespace AST diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 189eb72a57..140a757e51 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -181,6 +181,8 @@ class UiArrayMemberList; class UiQualifiedId; class UiQualifiedPragmaId; class UiHeaderItemList; +class UiEnumDeclaration; +class UiEnumMemberList; } } // namespace AST diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index e582a8f6a7..13218f0e98 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -84,6 +84,8 @@ public: virtual bool visit(UiArrayMemberList *) { return true; } virtual bool visit(UiQualifiedId *) { return true; } virtual bool visit(UiQualifiedPragmaId *) { return true; } + virtual bool visit(UiEnumDeclaration *) { return true; } + virtual bool visit(UiEnumMemberList *) { return true; } virtual void endVisit(UiProgram *) {} virtual void endVisit(UiImport *) {} @@ -101,6 +103,8 @@ public: virtual void endVisit(UiArrayMemberList *) {} virtual void endVisit(UiQualifiedId *) {} virtual void endVisit(UiQualifiedPragmaId *) {} + virtual void endVisit(UiEnumDeclaration *) {} + virtual void endVisit(UiEnumMemberList *) { } // QQmlJS virtual bool visit(ThisExpression *) { return true; } diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp index ca5a4bbd85..8e47898e2f 100644 --- a/src/qml/parser/qqmljsgrammar.cpp +++ b/src/qml/parser/qqmljsgrammar.cpp @@ -43,1069 +43,1086 @@ QT_BEGIN_NAMESPACE const char *const QQmlJSGrammar::spell [] = { - "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue", - "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", - "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", - "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", - "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", - "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", - ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", - "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", - "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment", - 0, "public", "import", "pragma", "as", "on", "get", "set", 0, 0, - 0, 0, 0, 0, 0, 0, 0}; + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", + "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", + "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment", + 0, "enum", "public", "import", "pragma", "as", "on", "get", "set", 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; const short QQmlJSGrammar::lhs [] = { - 107, 107, 107, 107, 107, 107, 108, 114, 114, 117, - 117, 117, 117, 120, 122, 118, 118, 119, 119, 119, - 119, 119, 119, 119, 119, 123, 124, 116, 115, 127, - 127, 128, 128, 129, 129, 126, 112, 112, 112, 112, - 131, 131, 131, 131, 131, 131, 131, 112, 139, 139, - 139, 139, 140, 140, 141, 141, 112, 112, 112, 112, - 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, - 112, 112, 112, 112, 112, 112, 125, 125, 125, 125, - 125, 125, 125, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 130, 146, 146, 146, 146, 145, 145, 150, 150, - 150, 148, 148, 151, 151, 151, 151, 154, 154, 154, - 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, - 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, - 155, 121, 121, 121, 121, 121, 158, 158, 159, 159, - 159, 159, 157, 157, 160, 160, 161, 161, 162, 162, - 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, - 163, 164, 164, 164, 164, 165, 165, 165, 166, 166, - 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, - 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, - 170, 170, 170, 170, 170, 171, 171, 172, 172, 173, - 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, - 178, 179, 179, 180, 180, 181, 181, 182, 182, 149, - 149, 183, 183, 184, 184, 184, 184, 184, 184, 184, - 184, 184, 184, 184, 184, 110, 110, 185, 185, 186, - 186, 187, 187, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, 132, 196, - 196, 195, 195, 143, 143, 197, 197, 197, 198, 198, - 200, 200, 199, 201, 204, 202, 202, 205, 203, 203, - 133, 134, 134, 135, 135, 188, 188, 188, 188, 188, - 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, - 190, 191, 191, 136, 137, 206, 206, 209, 209, 207, - 207, 210, 208, 192, 193, 193, 138, 138, 138, 211, - 212, 194, 194, 213, 142, 156, 156, 214, 214, 153, - 153, 152, 152, 215, 113, 113, 216, 216, 111, 111, - 147, 147, 217}; + 108, 108, 108, 108, 108, 108, 109, 115, 115, 118, + 118, 118, 118, 121, 123, 119, 119, 120, 120, 120, + 120, 120, 120, 120, 120, 124, 125, 117, 116, 128, + 128, 129, 129, 130, 130, 127, 113, 113, 113, 113, + 132, 132, 132, 132, 132, 132, 132, 113, 140, 140, + 140, 140, 141, 141, 142, 142, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 145, 145, 126, + 126, 126, 126, 126, 126, 126, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 131, 148, 148, 148, 148, 147, + 147, 152, 152, 152, 150, 150, 153, 153, 153, 153, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 157, 157, 122, 122, 122, 122, 122, + 160, 160, 161, 161, 161, 161, 159, 159, 162, 162, + 163, 163, 164, 164, 164, 165, 165, 165, 165, 165, + 165, 165, 165, 165, 165, 166, 166, 166, 166, 167, + 167, 167, 168, 168, 168, 168, 169, 169, 169, 169, + 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, + 171, 171, 171, 171, 172, 172, 172, 172, 172, 173, + 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, + 178, 179, 179, 180, 180, 181, 181, 182, 182, 183, + 183, 184, 184, 151, 151, 185, 185, 186, 186, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 111, + 111, 187, 187, 188, 188, 189, 189, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 133, 198, 198, 197, 197, 144, 144, 199, + 199, 199, 200, 200, 202, 202, 201, 203, 206, 204, + 204, 207, 205, 205, 134, 135, 135, 136, 136, 190, + 190, 190, 190, 190, 190, 190, 190, 191, 191, 191, + 191, 192, 192, 192, 192, 193, 193, 137, 138, 208, + 208, 211, 211, 209, 209, 212, 210, 194, 195, 195, + 139, 139, 139, 213, 214, 196, 196, 215, 143, 158, + 158, 216, 216, 155, 155, 154, 154, 217, 114, 114, + 218, 218, 112, 112, 149, 149, 219 +}; const short QQmlJSGrammar::rhs [] = { - 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, - 1, 2, 2, 1, 1, 2, 2, 2, 2, 3, - 3, 5, 5, 4, 4, 2, 2, 0, 1, 1, - 2, 1, 3, 2, 3, 2, 1, 5, 4, 4, - 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, - 1, 3, 0, 1, 2, 4, 6, 6, 3, 3, - 7, 7, 4, 4, 5, 5, 8, 8, 5, 6, - 6, 10, 6, 7, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 3, 3, 4, 5, 3, 4, - 3, 1, 1, 2, 3, 4, 1, 2, 3, 7, - 8, 1, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 4, 3, 5, 1, 2, 4, 4, - 4, 3, 0, 1, 1, 3, 1, 1, 1, 2, - 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 1, 3, 3, 3, 1, 3, 3, 1, 3, - 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, - 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, - 1, 3, 3, 3, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 3, 1, 5, 1, 5, 1, - 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 3, 0, 1, 1, - 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, - 2, 0, 1, 3, 3, 1, 1, 1, 1, 3, - 1, 3, 2, 2, 2, 0, 1, 2, 0, 1, - 1, 2, 2, 7, 5, 7, 7, 7, 5, 9, - 10, 7, 8, 2, 2, 3, 3, 2, 2, 3, - 3, 3, 3, 5, 5, 3, 5, 1, 2, 0, - 1, 4, 3, 3, 3, 3, 3, 3, 4, 5, - 2, 2, 2, 1, 8, 8, 7, 1, 3, 0, - 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, - 0, 1, 2}; + 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 1, 2, 2, 1, 1, 2, 2, 2, 2, 3, + 3, 5, 5, 4, 4, 2, 2, 0, 1, 1, + 2, 1, 3, 2, 3, 2, 1, 5, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, + 1, 3, 0, 1, 2, 4, 6, 6, 3, 3, + 7, 7, 4, 4, 5, 5, 8, 8, 5, 6, + 6, 10, 6, 7, 1, 1, 5, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 3, 3, 4, + 5, 3, 4, 3, 1, 1, 2, 3, 4, 1, + 2, 3, 7, 8, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, 3, 5, + 1, 2, 4, 4, 4, 3, 0, 1, 1, 3, + 1, 1, 1, 2, 2, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 3, 3, 1, + 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, + 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, + 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 5, 1, 5, 1, 3, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 0, 1, 1, 3, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 2, 0, 1, 3, 3, 1, + 1, 1, 1, 3, 1, 3, 2, 2, 2, 0, + 1, 2, 0, 1, 1, 2, 2, 7, 5, 7, + 7, 7, 5, 9, 10, 7, 8, 2, 2, 3, + 3, 2, 2, 3, 3, 3, 3, 5, 5, 3, + 5, 1, 2, 0, 1, 4, 3, 3, 3, 3, + 3, 3, 4, 5, 2, 2, 2, 1, 8, 8, + 7, 1, 3, 0, 1, 0, 1, 1, 1, 1, + 1, 2, 1, 1, 0, 1, 2 +}; const short QQmlJSGrammar::action_default [] = { - 0, 0, 28, 0, 0, 0, 28, 0, 189, 256, - 220, 228, 224, 168, 240, 216, 3, 153, 85, 169, - 232, 236, 157, 186, 167, 172, 152, 206, 193, 0, - 92, 93, 88, 0, 82, 77, 361, 0, 0, 0, - 0, 90, 0, 0, 86, 89, 81, 0, 0, 78, - 80, 83, 79, 91, 84, 0, 87, 0, 0, 182, - 0, 0, 169, 188, 171, 170, 0, 0, 0, 184, - 185, 183, 187, 0, 217, 0, 0, 0, 0, 207, - 0, 0, 0, 0, 0, 0, 197, 0, 0, 0, - 191, 192, 190, 195, 199, 198, 196, 194, 209, 208, - 210, 0, 225, 0, 221, 0, 0, 163, 150, 162, - 151, 118, 119, 120, 145, 121, 147, 122, 123, 124, - 125, 126, 127, 128, 129, 130, 131, 132, 146, 133, - 134, 148, 135, 136, 137, 138, 139, 140, 141, 142, - 143, 144, 149, 0, 0, 161, 257, 164, 0, 165, - 0, 166, 160, 0, 253, 246, 244, 251, 252, 250, - 249, 255, 248, 247, 245, 254, 241, 0, 229, 0, - 0, 233, 0, 0, 237, 0, 0, 163, 155, 0, - 154, 0, 159, 173, 0, 350, 350, 351, 0, 348, - 0, 349, 0, 352, 264, 271, 270, 278, 266, 0, - 267, 0, 353, 0, 360, 268, 269, 85, 274, 272, - 357, 354, 359, 275, 0, 287, 0, 0, 0, 0, - 344, 0, 361, 286, 258, 301, 0, 0, 0, 288, - 0, 0, 276, 277, 0, 265, 273, 302, 303, 0, - 350, 0, 0, 352, 0, 345, 346, 0, 334, 358, - 0, 318, 319, 320, 321, 0, 314, 315, 316, 317, - 342, 343, 0, 0, 0, 0, 0, 306, 307, 308, - 262, 260, 222, 230, 226, 242, 218, 263, 0, 169, - 234, 238, 211, 200, 0, 0, 219, 0, 0, 0, - 0, 212, 0, 0, 0, 0, 0, 204, 202, 205, - 203, 201, 214, 213, 215, 0, 227, 0, 223, 0, - 261, 169, 0, 243, 258, 259, 0, 258, 0, 0, - 310, 0, 0, 0, 312, 0, 231, 0, 0, 235, - 0, 0, 239, 299, 0, 291, 300, 294, 0, 298, - 0, 258, 292, 0, 258, 0, 0, 311, 0, 0, - 0, 313, 0, 0, 0, 305, 0, 304, 85, 112, - 362, 0, 0, 117, 280, 283, 0, 118, 287, 121, - 147, 123, 124, 88, 128, 129, 82, 130, 286, 133, - 86, 89, 258, 83, 91, 136, 84, 138, 87, 140, - 141, 288, 143, 144, 149, 0, 114, 113, 116, 100, - 115, 99, 0, 109, 281, 279, 0, 0, 0, 352, - 0, 110, 157, 158, 163, 0, 156, 0, 322, 323, - 0, 350, 0, 0, 352, 0, 111, 0, 0, 0, - 325, 330, 328, 331, 0, 0, 329, 330, 0, 326, - 0, 327, 282, 333, 0, 282, 332, 0, 335, 336, - 0, 282, 337, 338, 0, 0, 339, 0, 0, 0, - 340, 341, 175, 174, 0, 0, 0, 309, 0, 0, - 0, 324, 296, 289, 0, 297, 293, 0, 295, 284, - 0, 285, 290, 0, 0, 352, 0, 347, 103, 0, - 0, 107, 94, 0, 96, 105, 0, 97, 106, 108, - 98, 104, 95, 0, 101, 179, 177, 181, 178, 176, - 180, 355, 6, 356, 4, 2, 75, 102, 0, 0, - 78, 80, 79, 37, 5, 0, 76, 0, 51, 50, - 49, 0, 0, 51, 0, 0, 0, 52, 0, 67, - 68, 0, 65, 0, 66, 41, 42, 43, 44, 46, - 47, 71, 45, 0, 51, 0, 0, 0, 0, 0, - 61, 0, 62, 0, 0, 32, 0, 0, 72, 33, - 0, 36, 34, 30, 0, 35, 31, 0, 63, 0, - 64, 157, 0, 69, 73, 0, 0, 0, 0, 157, - 282, 0, 70, 85, 118, 287, 121, 147, 123, 124, - 88, 128, 129, 130, 286, 133, 86, 89, 258, 91, - 136, 84, 138, 87, 140, 141, 288, 143, 144, 149, - 74, 0, 59, 53, 60, 54, 0, 0, 0, 0, - 56, 0, 57, 58, 55, 0, 0, 0, 0, 48, - 0, 38, 39, 0, 40, 8, 0, 0, 9, 0, - 11, 0, 10, 0, 1, 27, 15, 14, 26, 13, - 12, 29, 7, 0, 18, 0, 19, 0, 24, 25, - 0, 20, 21, 0, 22, 23, 16, 17, 363}; + 0, 0, 28, 0, 0, 0, 28, 0, 193, 260, + 224, 232, 228, 172, 244, 220, 3, 157, 88, 173, + 236, 240, 161, 190, 171, 176, 156, 210, 197, 0, + 95, 96, 91, 0, 85, 80, 365, 0, 0, 0, + 0, 93, 0, 0, 89, 92, 84, 0, 0, 81, + 83, 86, 82, 94, 87, 0, 90, 0, 0, 186, + 0, 0, 173, 192, 175, 174, 0, 0, 0, 188, + 189, 187, 191, 0, 221, 0, 0, 0, 0, 211, + 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, + 195, 196, 194, 199, 203, 202, 200, 198, 213, 212, + 214, 0, 229, 0, 225, 0, 0, 167, 154, 166, + 155, 121, 122, 123, 149, 124, 151, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 150, + 137, 138, 152, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 153, 0, 0, 165, 261, 168, 0, + 169, 0, 170, 164, 0, 257, 250, 248, 255, 256, + 254, 253, 259, 252, 251, 249, 258, 245, 0, 233, + 0, 0, 237, 0, 0, 241, 0, 0, 167, 159, + 0, 158, 0, 163, 177, 0, 354, 354, 355, 0, + 352, 0, 353, 0, 356, 268, 275, 274, 282, 270, + 0, 271, 0, 357, 0, 364, 272, 273, 88, 278, + 276, 361, 358, 363, 279, 0, 291, 0, 0, 0, + 0, 348, 0, 365, 290, 262, 305, 0, 0, 0, + 292, 0, 0, 280, 281, 0, 269, 277, 306, 307, + 0, 354, 0, 0, 356, 0, 349, 350, 0, 338, + 362, 0, 322, 323, 324, 325, 0, 318, 319, 320, + 321, 346, 347, 0, 0, 0, 0, 0, 310, 311, + 312, 266, 264, 226, 234, 230, 246, 222, 267, 0, + 173, 238, 242, 215, 204, 0, 0, 223, 0, 0, + 0, 0, 216, 0, 0, 0, 0, 0, 208, 206, + 209, 207, 205, 218, 217, 219, 0, 231, 0, 227, + 0, 265, 173, 0, 247, 262, 263, 0, 262, 0, + 0, 314, 0, 0, 0, 316, 0, 235, 0, 0, + 239, 0, 0, 243, 303, 0, 295, 304, 298, 0, + 302, 0, 262, 296, 0, 262, 0, 0, 315, 0, + 0, 0, 317, 0, 0, 0, 309, 0, 308, 88, + 115, 366, 0, 0, 120, 284, 287, 0, 121, 291, + 124, 151, 126, 127, 91, 132, 133, 85, 134, 290, + 137, 89, 92, 262, 86, 94, 140, 87, 142, 90, + 144, 145, 292, 147, 148, 153, 0, 117, 116, 119, + 103, 118, 102, 0, 112, 285, 283, 0, 0, 0, + 356, 0, 113, 161, 162, 167, 0, 160, 0, 326, + 327, 0, 354, 0, 0, 356, 0, 114, 0, 0, + 0, 329, 334, 332, 335, 0, 0, 333, 334, 0, + 330, 0, 331, 286, 337, 0, 286, 336, 0, 339, + 340, 0, 286, 341, 342, 0, 0, 343, 0, 0, + 0, 344, 345, 179, 178, 0, 0, 0, 313, 0, + 0, 0, 328, 300, 293, 0, 301, 297, 0, 299, + 288, 0, 289, 294, 0, 0, 356, 0, 351, 106, + 0, 0, 110, 97, 0, 99, 108, 0, 100, 109, + 111, 101, 107, 98, 0, 104, 183, 181, 185, 182, + 180, 184, 359, 6, 360, 4, 2, 75, 105, 0, + 0, 0, 81, 83, 82, 37, 5, 0, 76, 0, + 51, 50, 49, 0, 0, 51, 0, 0, 0, 52, + 0, 67, 68, 0, 65, 0, 66, 41, 42, 43, + 44, 46, 47, 71, 45, 0, 0, 0, 78, 0, + 77, 79, 0, 51, 0, 0, 0, 0, 0, 61, + 0, 62, 0, 0, 32, 0, 0, 72, 33, 0, + 36, 34, 30, 0, 35, 31, 0, 63, 0, 64, + 161, 0, 69, 73, 0, 0, 0, 0, 161, 286, + 0, 70, 88, 121, 291, 124, 151, 126, 127, 91, + 132, 133, 134, 290, 137, 89, 92, 262, 94, 140, + 87, 142, 90, 144, 145, 292, 147, 148, 153, 74, + 0, 59, 53, 60, 54, 0, 0, 0, 0, 56, + 0, 57, 58, 55, 0, 0, 0, 0, 48, 0, + 38, 39, 0, 40, 8, 0, 0, 9, 0, 11, + 0, 10, 0, 1, 27, 15, 14, 26, 13, 12, + 29, 7, 0, 18, 0, 19, 0, 24, 25, 0, + 20, 21, 0, 22, 23, 16, 17, 367 +}; const short QQmlJSGrammar::goto_default [] = { - 7, 654, 212, 199, 210, 524, 512, 649, 662, 511, - 648, 652, 650, 658, 22, 655, 653, 651, 18, 523, - 574, 564, 571, 566, 551, 194, 198, 200, 205, 236, - 213, 233, 555, 626, 625, 204, 235, 26, 490, 489, - 361, 360, 9, 359, 362, 203, 483, 363, 109, 17, - 148, 24, 13, 147, 19, 25, 59, 23, 8, 28, - 27, 282, 15, 276, 10, 272, 12, 274, 11, 273, - 20, 280, 21, 281, 14, 275, 271, 312, 417, 277, - 278, 206, 196, 195, 209, 208, 232, 197, 366, 365, - 234, 474, 473, 334, 335, 476, 337, 475, 336, 430, - 434, 437, 433, 432, 452, 453, 201, 187, 202, 211, - 0}; + 7, 663, 213, 200, 211, 526, 513, 658, 671, 512, + 657, 661, 659, 667, 22, 664, 662, 660, 18, 525, + 583, 573, 580, 575, 553, 195, 199, 201, 206, 237, + 214, 234, 564, 635, 634, 205, 236, 557, 26, 491, + 490, 362, 361, 9, 360, 363, 204, 484, 364, 109, + 17, 149, 24, 13, 148, 19, 25, 59, 23, 8, + 28, 27, 283, 15, 277, 10, 273, 12, 275, 11, + 274, 20, 281, 21, 282, 14, 276, 272, 313, 418, + 278, 279, 207, 197, 196, 210, 209, 233, 198, 367, + 366, 235, 475, 474, 335, 336, 477, 338, 476, 337, + 431, 435, 438, 434, 433, 453, 454, 202, 188, 203, + 212, 0 +}; const short QQmlJSGrammar::action_index [] = { - 308, 1392, 2787, 2787, 2890, 1102, 71, 6, 103, -107, - 10, -35, -64, 287, -107, 310, 11, -107, -107, 815, - 30, 112, 183, 214, -107, -107, -107, 463, 203, 1392, - -107, -107, -107, 536, -107, -107, 2478, 1786, 1392, 1392, - 1392, -107, 1005, 1392, -107, -107, -107, 1392, 1392, -107, - -107, -107, -107, -107, -107, 1392, -107, 1392, 1392, -107, - 1392, 1392, 75, 204, -107, -107, 1392, 1392, 1392, -107, - -107, -107, 221, 1392, 306, 1392, 1392, 1392, 1392, 463, - 1392, 1392, 1392, 1392, 1392, 1392, 200, 1392, 1392, 1392, - 149, 145, 108, 231, 241, 295, 379, 379, 463, 463, - 463, 1392, -70, 1392, 4, 2375, 1392, 1392, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, 105, 1392, -107, -107, -5, -58, -107, - 1392, -107, -107, 1392, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, 1392, -44, 1392, - 1392, 5, 7, 1392, -107, 2375, 1392, 1392, -107, 134, - -107, -43, -107, -107, -16, 541, 541, 15, -36, -107, - 462, -107, -4, 2787, -107, -107, -107, -107, -107, 213, - -107, 449, -107, -20, -107, -107, -107, 31, -107, -107, - -107, 2787, -107, -107, 616, -107, 711, 144, 2890, 21, - 42, 43, 3096, -107, 1392, -107, 62, 1392, 101, -107, - 102, 99, -107, -107, 417, -107, -107, -107, -107, 93, - 441, 56, 92, 2787, 34, -107, -107, 2890, -107, -107, - 118, -107, -107, -107, -107, 125, -107, -107, -107, -107, - -107, -107, -14, 33, 1392, 137, 193, -107, -107, -107, - 1488, -107, 44, -1, -42, -107, 316, -8, -60, 718, - 97, 87, 368, 222, 359, 1392, 313, 1392, 1392, 1392, - 1392, 342, 1392, 1392, 1392, 1392, 1392, 271, 270, 263, - 262, 225, 346, 352, 362, 1392, -51, 1392, 29, 1392, - -107, 815, 1392, -107, 1392, 28, -22, 1392, -19, 2890, - -107, 1392, 160, 2890, -107, 1392, 0, 1392, 1392, 97, - 45, 1392, -107, 37, 142, 25, -107, -107, 1392, -107, - 541, 1392, -107, 9, 1392, 12, 2890, -107, 1392, 128, - 2890, -107, 1392, 124, 2890, 61, 2890, -107, 60, -107, - 67, 26, 73, -107, -107, 2890, 49, 544, 80, 556, - 114, 1392, 2890, 85, 58, 482, 2581, 64, 88, 1005, - 90, 94, 1588, 2581, 96, 70, 197, 1392, 100, 76, - 1392, 104, 1392, 82, 84, 2684, -107, -107, -107, -107, - -107, -107, 1392, -107, -107, -107, 95, 63, 91, 2787, - 53, -107, 217, -107, 1392, 50, -107, 120, -107, -107, - 40, 372, 8, 27, 2787, 3, -107, 1392, 141, 20, - -107, 46, -107, 41, 147, 1392, -107, 39, 36, -107, - -15, -107, 2890, -107, 297, 2890, -107, 175, -107, -107, - 187, 2890, 14, -107, -3, -2, -107, 459, -34, -6, - -107, -107, -107, -107, 1392, 139, 2890, -107, 1392, 132, - 2890, -107, 1, -107, 251, -107, -107, 1392, -107, -107, - 541, -107, -107, -48, -23, 2787, -47, -107, -107, 113, - 1984, -107, -107, 1885, -107, -107, 1687, -107, -107, -107, - -107, -107, -107, 107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, 2787, -107, -107, -107, 131, -50, 910, - 243, -45, -7, -107, -107, 232, -107, 206, -12, -107, - -107, 633, 189, -107, 198, 13, 385, -107, 153, -107, - -107, 184, -107, 2080, -107, -107, -107, -107, -107, -107, - -107, -107, -107, 208, 18, 633, 219, 129, 353, 292, - -107, 48, -107, 910, 122, -107, 81, 910, -107, -107, - 1296, -107, -107, -107, 1199, -107, -107, 224, -107, 2080, - -107, 311, 81, -107, -107, 205, 633, 98, 2176, 304, - 2993, 69, -107, 89, 613, 86, 597, 109, 1392, 2890, - 83, 55, 467, 52, 79, 804, 78, 77, 1588, 66, - 47, 59, 1392, 57, 32, 1392, 54, 1392, 38, 35, - -107, 255, -107, 228, -107, 51, 2, 524, 195, 532, - -107, 133, -107, -107, -107, 2272, 910, 1786, 17, -107, - 152, -107, -107, 16, -107, -107, 910, 910, 119, 910, - -107, 302, -107, 148, -107, -107, 143, 140, -107, -107, - -107, -107, -107, 369, -107, 249, -107, 111, -107, -107, - 364, -107, -107, 65, -107, -107, -107, -107, -107, + 301, 1388, 2901, 2901, 2797, 1095, 106, 97, 99, -108, + 92, 88, 85, 364, -108, 338, 96, -108, -108, 805, + 100, 161, 281, 247, -108, -108, -108, 394, 274, 1388, + -108, -108, -108, 516, -108, -108, 2589, 1786, 1388, 1388, + 1388, -108, 997, 1388, -108, -108, -108, 1388, 1388, -108, + -108, -108, -108, -108, -108, 1388, -108, 1388, 1388, -108, + 1388, 1388, 116, 235, -108, -108, 1388, 1388, 1388, -108, + -108, -108, 220, 1388, 326, 1388, 1388, 1388, 1388, 384, + 1388, 1388, 1388, 1388, 1388, 1388, 193, 1388, 1388, 1388, + 115, 103, 102, 211, 227, 231, 257, 230, 424, 414, + 404, 1388, 85, 1388, 78, 2381, 1388, 1388, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, 157, 1388, -108, -108, 68, 61, + -108, 1388, -108, -108, 1388, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, 1388, 59, + 1388, 1388, 71, 60, 1388, -108, 2381, 1388, 1388, -108, + 271, -108, 54, -108, -108, 53, 428, 434, 58, 51, + -108, 437, -108, 50, 2901, -108, -108, -108, -108, -108, + 232, -108, 528, -108, 48, -108, -108, -108, 55, -108, + -108, -108, 2901, -108, -108, 634, -108, 637, 101, 2797, + 56, 90, 89, 3109, -108, 1388, -108, 86, 1388, 77, + -108, 84, 82, -108, -108, 425, -108, -108, -108, -108, + 81, 346, 75, 66, 2901, 74, -108, -108, 2797, -108, + -108, 121, -108, -108, -108, -108, 117, -108, -108, -108, + -108, -108, -108, 63, 69, 1388, 108, 160, -108, -108, + -108, 1586, -108, 80, 67, 72, -108, 324, 76, 73, + 711, 83, 122, 449, 308, 411, 1388, 331, 1388, 1388, + 1388, 1388, 449, 1388, 1388, 1388, 1388, 1388, 303, 299, + 298, 284, 280, 449, 357, 449, 1388, -20, 1388, 57, + 1388, -108, 805, 1388, -108, 1388, 70, 62, 1388, 64, + 2797, -108, 1388, 204, 2797, -108, 1388, 65, 1388, 1388, + 104, 105, 1388, -108, 91, 136, 171, -108, -108, 1388, + -108, 528, 1388, -108, -6, 1388, 87, 2797, -108, 1388, + 129, 2797, -108, 1388, 125, 2797, 93, 2797, -108, 79, + -108, 22, -36, 20, -108, -108, 2797, -34, 581, 15, + 594, 113, 1388, 2797, 16, -12, 508, 2485, -11, 11, + 997, 21, 24, 1489, 2485, 25, -2, 3, 1388, 94, + -32, 1388, -4, 1388, -30, -29, 2693, -108, -108, -108, + -108, -108, -108, 1388, -108, -108, -108, -19, -9, 37, + 2901, -1, -108, 285, -108, 1388, 5, -108, 134, -108, + -108, 39, 421, 40, 44, 2901, 41, -108, 1388, 141, + 47, -108, 52, -108, 36, 206, 1388, -108, 30, 29, + -108, -16, -108, 2797, -108, 123, 2797, -108, 270, -108, + -108, 132, 2797, 18, -108, 27, 28, -108, 528, -10, + 31, -108, -108, -108, -108, 1388, 133, 2797, -108, 1388, + 124, 2797, -108, 9, -108, 209, -108, -108, 1388, -108, + -108, 445, -108, -108, 0, -17, 2901, -51, -108, -108, + 111, 1986, -108, -108, 1686, -108, -108, 1886, -108, -108, + -108, -108, -108, -108, 120, -108, -108, -108, -108, -108, + -108, -108, -108, -108, 2901, -108, -108, -108, 112, -22, + 23, 901, 216, -26, 13, -108, -108, 218, -108, 210, + 45, -108, -108, 621, 201, -108, 162, 43, 401, -108, + 144, -108, -108, 219, -108, 2277, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -31, 17, 186, -108, 19, + -108, -108, 208, 34, 621, 200, 166, 528, 228, -108, + -5, -108, 901, 190, -108, -13, 794, -108, -108, 1291, + -108, -108, -108, 1193, -108, -108, 212, -108, 2277, -108, + 352, -13, -108, -108, 185, 521, 26, 2083, 319, 3005, + 4, -108, 1, 571, 2, 700, 146, 1388, 2797, -7, + -25, 511, -24, 6, 997, 7, 8, 1489, 46, 38, + 49, 1388, 94, 12, 1388, 42, 1388, 32, 33, -108, + 277, -108, 273, -108, -3, 35, 524, 233, 621, -108, + 98, -108, -108, -108, 2180, 901, 1786, 14, -108, 153, + -108, -108, 10, -108, -108, 901, 901, 110, 901, -108, + 300, -108, 95, -108, -108, 176, 158, -108, -108, -108, + -108, -108, 528, -108, 172, -108, 109, -108, -108, 528, + -108, -108, 126, -108, -108, -108, -108, -108, - -111, 55, 62, 77, 71, 279, -7, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -74, - -111, -111, -111, -111, -111, -111, -111, -111, -111, 70, - -111, -111, -111, -8, -111, -111, -6, -28, 12, 84, - 85, -111, 93, 100, -111, -111, -111, 101, 104, -111, - -111, -111, -111, -111, -111, 107, -111, 112, 118, -111, - 182, 184, -111, -111, -111, -111, 218, 215, 209, -111, - -111, -111, -111, 202, -111, 195, 193, 192, 191, -111, - 189, 183, 181, 175, 168, 155, -111, 170, 153, 150, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, 151, -111, 142, -111, 172, 30, -4, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -2, -111, -111, -111, -111, -111, - 0, -111, -111, 9, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, 125, -111, 122, - 10, -111, -111, 22, -111, 236, 46, 127, -111, -111, - -111, -111, -111, -111, -111, 37, 124, -111, -111, -111, - 39, -111, -111, 42, -111, -111, -111, -111, -111, -111, - -111, 44, -111, -111, -111, -111, -111, -111, -111, -111, - -111, 94, -111, -111, 47, -111, 48, -111, 128, -111, - 50, -111, 91, -111, -3, -111, -111, 66, 53, -111, - -111, -111, -111, -111, 57, -111, -111, -111, -111, -111, - 79, -111, -111, 78, -111, -111, -111, 82, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, 67, -111, -111, -111, -111, -111, - 61, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, 59, 258, -111, 259, 268, 269, - 272, -111, 60, 63, 73, 74, 75, -111, -111, -111, - -111, -111, -111, -111, -111, 252, -111, 242, -111, 233, - -111, -111, 232, -111, 87, -111, -111, 89, -111, 133, - -111, 51, -111, 135, -111, 231, -111, 223, 222, -111, - -111, 221, -111, -111, -111, -111, -111, -111, 219, -111, - 92, 102, -111, -111, 110, -111, 171, -111, 40, -111, - 173, -111, 38, -111, 176, -111, 179, -111, -111, -111, - -111, -111, -111, -111, -111, 180, -111, 19, -111, 18, - -111, 145, 185, -111, -111, 17, 166, -111, -111, 65, - -111, -111, 29, 177, -111, -111, -111, 25, -111, 5, - 159, -111, 164, -111, -111, 207, -111, -111, -111, -111, - -111, -111, -18, -111, -111, -111, -111, -111, -111, 212, - -111, -111, -111, -111, 216, -111, -111, -111, -111, -111, - -111, 213, -111, -111, 86, -111, -111, 16, -111, -111, - -111, -111, -111, -85, -111, 14, -111, -84, -111, -111, - -111, -111, 286, -111, -111, 287, -111, -111, -111, -111, - -111, 214, -94, -111, -111, -16, -111, -10, -111, -19, - -111, -111, -111, -111, 2, -111, 83, -111, 105, -111, - 81, -111, -111, -111, -111, -111, -111, -41, -111, -111, - 131, -111, -111, -111, -111, 76, -111, -111, -111, -111, - -35, -111, -111, 64, -111, -111, -29, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, 208, -111, -111, -111, -111, -111, 20, - -111, -111, -111, -111, -111, -111, -111, 13, -111, -111, - -111, 26, 15, -111, -111, -111, 32, -111, -111, -111, - -111, -111, -111, 329, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, 54, 56, -111, 58, -111, - -111, -111, -111, 68, -111, -111, -111, 72, -111, -111, - 330, -111, -111, -111, 327, -111, -111, -111, -111, 389, - -111, -111, 52, -111, -111, 31, 49, -111, 371, -111, - 134, 34, -111, -111, 43, -111, 41, -111, 108, 141, - -111, -111, 35, -111, -111, 97, -111, -111, 45, -111, - -111, -111, 36, -111, 21, 129, -111, 146, -111, -111, - -111, -111, -111, -1, -111, -111, -111, 11, -5, 7, - -111, -111, -111, -111, -111, 353, 311, 408, 4, -111, - -111, -111, -111, 1, -111, -111, 8, 6, 249, 248, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, 3, -111, -111, -111, -111, -111, -111, - -14, -111, -111, -111, -111, -111, -111, -111, -111}; + -112, -3, 65, 88, 78, 292, -4, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -70, + -112, -112, -112, -112, -112, -112, -112, -112, -112, 70, + -112, -112, -112, 10, -112, -112, 2, -24, 15, 96, + 99, -112, 130, 103, -112, -112, -112, 48, 62, -112, + -112, -112, -112, -112, -112, 57, -112, 82, 203, -112, + 187, 181, -112, -112, -112, -112, 169, 177, 178, -112, + -112, -112, -112, 189, -112, 196, 198, 206, 161, -112, + 107, 113, 135, 116, 117, 119, -112, 125, 128, 133, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, 139, -112, 145, -112, 180, 8, -42, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, 5, -112, -112, -112, -112, + -112, 31, -112, -112, 32, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, 86, -112, + 77, 17, -112, -112, 28, -112, 273, 37, 79, -112, + -112, -112, -112, -112, -112, -112, 61, 95, -112, -112, + -112, 46, -112, -112, 58, -112, -112, -112, -112, -112, + -112, -112, 45, -112, -112, -112, -112, -112, -112, -112, + -112, -112, 131, -112, -112, 29, -112, 33, -112, 84, + -112, 39, -112, 223, -112, 42, -112, -112, 35, 16, + -112, -112, -112, -112, -112, 49, -112, -112, -112, -112, + -112, 184, -112, -112, 194, -112, -112, -112, 199, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, 19, -112, -112, -112, -112, + -112, 76, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -11, 214, -112, 215, 221, + 224, 225, -112, 92, 90, 83, 74, 73, -112, -112, + -112, -112, -112, -112, -112, -112, 237, -112, 234, -112, + 243, -112, -112, 254, -112, 67, -112, -112, 85, -112, + 167, -112, 26, -112, 219, -112, 247, -112, 244, 241, + -112, -112, 233, -112, -112, -112, -112, -112, -112, 213, + -112, 108, 183, -112, -112, 193, -112, 202, -112, 106, + -112, 209, -112, 55, -112, 317, -112, 205, -112, -112, + -112, -112, -112, -112, -112, -112, 191, -112, 59, -112, + 60, -112, 134, 179, -112, -112, 44, 160, -112, -112, + 156, -112, -112, 41, 211, -112, -112, -112, 50, -112, + 36, 192, -112, 63, -112, -112, 72, -112, -112, -112, + -112, -112, -112, 23, -112, -112, -112, -112, -112, -112, + 75, -112, -112, -112, -112, 114, -112, -112, -112, -112, + -112, -112, 64, -112, -112, 69, -112, -112, 52, -112, + -112, -112, -112, -112, -50, -112, 56, -112, -45, -112, + -112, -112, -112, 293, -112, -112, 264, -112, -112, -112, + -112, -112, 89, -64, -112, -112, 24, -112, 25, -112, + 27, -112, -112, -112, -112, 34, -112, 122, -112, 47, + -112, 66, -112, -112, -112, -112, -112, -112, 38, -112, + -112, 220, -112, -112, -112, -112, 197, -112, -112, -112, + -112, 30, -112, -112, 120, -112, -112, 3, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, 195, -112, -112, -112, -112, -112, + -112, 51, -112, -112, -112, -112, -112, -112, -112, 40, + -112, -112, -112, -15, -28, -112, -112, -112, -12, -112, + -112, -112, -112, -112, -112, 314, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, 7, 0, -112, 21, -112, -112, + -112, -112, 147, -112, -112, -112, 236, -112, -112, 324, + -112, -112, -112, 424, -112, -112, -112, -112, 360, -112, + -112, 13, -112, -112, -1, 12, -112, 338, -112, 212, + 4, -112, -112, 9, -112, -6, -112, 208, 246, -112, + -112, 6, -112, -112, 71, -112, -112, 18, -112, -112, + -112, 14, -112, -10, 53, -112, 43, -112, -112, -112, + -112, -112, -30, -112, -112, -112, -5, -18, -2, -112, + -112, -112, -112, -112, 378, 81, 311, 1, -112, -112, + -112, -112, 11, -112, -112, 20, 22, 207, 80, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -8, -112, -112, -112, -112, -112, -112, -9, + -112, -112, -112, -112, -112, -112, -112, -112 +}; const short QQmlJSGrammar::action_info [] = { - 309, 314, 152, 150, 101, 73, 678, 167, 487, 103, - 485, 73, 484, 101, 173, 103, 527, 182, 477, 144, - 186, 585, 621, 190, 192, 532, 459, 451, 307, 193, - 285, 451, 167, 457, 455, 246, 144, 307, 247, 317, - 441, 319, 537, 442, 435, 285, 435, 305, 305, 570, - 570, 435, 331, 431, 338, 556, 348, 270, 426, 628, - 424, -142, 631, 263, -139, 451, -137, 247, 423, 264, - 344, 468, 346, -115, 464, 395, 421, 356, 185, 352, - 402, 401, 563, 427, -116, -134, -146, -145, 352, 245, - -126, 270, -126, -145, 270, -146, 247, -134, 427, 325, - 352, -116, 570, -115, 405, 588, 427, -139, 411, 451, - 416, -142, 0, 144, 570, 144, 242, 64, 464, 0, - 468, 493, 0, 408, 409, 243, 675, 674, 65, 240, - 567, 407, 144, 0, 451, 468, 144, 327, 464, 0, - 144, 328, 144, 60, 535, 144, 175, 144, 60, 144, - 340, 0, 0, 558, 61, 175, 0, 438, 175, 61, - 567, 145, 169, 646, 647, 176, 170, 504, 144, 494, - 261, 260, 669, 668, 176, 261, 260, 176, 568, 254, - 253, 419, 418, 144, 354, 60, 259, 258, 350, 60, - 180, 543, 470, 454, 633, 632, 61, 266, 175, 466, - 61, 429, 439, 341, -137, 261, 260, 455, 641, 677, - 676, 646, 647, 535, 540, 539, 66, 176, 533, 177, - 323, 144, 536, 175, 533, 87, 66, 88, 87, 0, - 88, 579, 175, 66, 533, 528, 449, 448, 89, 635, - 0, 89, 176, 0, 414, 544, 542, 87, 533, 88, - 87, 176, 88, 414, 269, 267, 87, 533, 88, 480, - 89, 67, 0, 89, 530, 570, 87, 68, 88, 89, - 530, 67, 554, 0, 238, 237, 529, 68, 67, 89, - 530, 530, 529, 268, 68, 580, 578, 87, 87, 88, - 88, 623, 529, 529, 530, 87, 87, 88, 88, 561, - 89, 89, 105, 530, 445, 144, 529, 0, 89, 89, - 672, 671, 481, 479, 0, 529, 624, 622, 530, 175, - 87, 106, 88, 107, 75, 76, 175, 636, 75, 76, - 529, 287, 288, 89, 287, 288, 0, -102, 176, 0, - 177, 0, 0, 670, -102, 176, 0, 177, 0, 665, - 0, 77, 78, 562, 560, 77, 78, 0, 289, 290, - 0, 289, 290, 666, 664, 292, 293, 0, 0, 292, - 293, 0, 0, 0, 294, 292, 293, 295, 294, 296, - 0, 295, 35, 296, 294, 292, 293, 295, 35, 296, - 0, 292, 293, 35, 294, 0, 663, 295, 35, 296, - 294, 35, 0, 295, 87, 296, 88, 6, 5, 4, - 1, 3, 2, 0, 35, 0, 0, 89, 0, 49, - 52, 50, 0, 0, 0, 49, 52, 50, 0, 0, - 49, 52, 50, 0, 0, 49, 52, 50, 49, 52, - 50, 0, 0, 0, 0, 0, 35, 0, 46, 34, - 51, 49, 52, 50, 46, 34, 51, 0, 0, 46, - 34, 51, 0, 0, 46, 34, 51, 46, 34, 51, - 35, 0, 0, 0, 0, 0, 0, 0, 35, 0, - 46, 34, 51, 49, 52, 50, 80, 81, 35, 0, - 0, 35, 0, 0, 82, 83, 35, 0, 84, 0, - 85, 0, 0, 185, 0, 0, 0, 49, 52, 50, - 0, 35, 46, 34, 51, 49, 52, 50, 185, 0, - 0, 0, 0, 0, 0, 49, 52, 50, 49, 52, - 50, 0, 0, 49, 52, 50, 46, 34, 51, 535, - 0, 0, 0, 0, 46, 34, 51, 535, 49, 52, - 50, 0, 0, 35, 46, 34, 51, 46, 34, 51, - 0, 35, 46, 34, 51, 35, 0, 0, 0, 0, - 35, 0, 185, 35, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, - 49, 52, 50, 0, 0, 0, 0, 0, 49, 52, - 50, 0, 49, 52, 50, 252, 251, 49, 52, 50, - 49, 52, 50, 0, 0, 0, 0, 257, 256, 46, - 34, 51, 49, 52, 50, 0, 35, 46, 34, 51, - 0, 46, 34, 51, 0, 0, 46, 34, 51, 46, - 34, 51, 35, 0, 0, 35, 0, 0, 535, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 257, 256, - 0, 0, 35, 49, 52, 50, 0, 0, 0, 0, - 0, 0, 0, 0, 252, 251, 0, 252, 251, 49, - 52, 50, 49, 52, 50, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 49, - 52, 50, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 154, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 155, 0, 0, 0, 156, 0, 0, 0, 0, - 35, 0, 0, 0, 157, 0, 158, 0, 0, 321, - 0, 0, 0, 0, 0, 0, 0, 159, 0, 160, - 64, 0, 0, 0, 0, 0, 0, 161, 0, 0, - 162, 65, 257, 256, 0, 0, 163, 49, 52, 50, - 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, - 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 30, 31, 154, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 155, 0, - 0, 0, 156, 35, 0, 0, 0, 36, 37, 0, - 38, 157, 0, 158, 0, 0, 0, 42, 0, 0, - 0, 45, 0, 0, 159, 0, 160, 64, 0, 0, - 0, 0, 0, 0, 161, 0, 0, 162, 65, 53, - 49, 52, 50, 163, 54, 0, 0, 0, 0, 164, - 0, 0, 0, 0, 0, 44, 56, 32, 0, 0, - 0, 0, 41, 0, 0, 165, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, - 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, - 0, 0, 0, 519, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, - 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 0, 0, 0, 0, 42, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 518, 0, 30, 31, 0, 0, 0, 0, - 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, - 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, - 0, 0, 0, 0, 0, 519, 0, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 53, 520, 522, - 521, 0, 54, 0, 0, 0, 0, 229, 0, 0, - 0, 0, 0, 44, 56, 32, 215, 223, 0, 0, - 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 518, - 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 220, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, - 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, - 0, 0, 0, 0, 575, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 53, 520, 522, 521, 0, 54, - 0, 0, 0, 0, 229, 0, 0, 0, 0, 0, - 44, 56, 32, 215, 223, 0, 0, 41, 0, 0, - 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 518, 0, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 0, 0, 0, 519, - 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, - 0, 572, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 520, 522, 521, 0, 54, 0, 0, 0, - 0, 229, 0, 0, 0, 0, 0, 44, 56, 32, - 215, 223, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, - 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, - 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, - 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, - 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, - 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, - 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, - 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, - 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, - 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, - 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, - 55, 0, 57, 284, 58, 0, 0, 0, 0, 44, - 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, - 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, -135, 0, 0, 0, 29, - 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, - 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, - 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, - 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, - 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, - 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 499, 0, 0, 29, 30, - 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, - 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, - 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 500, 0, 0, 0, 0, 0, 0, - 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, - 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, - 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 491, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, - 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 491, 0, 0, 29, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, - 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, - 0, 497, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, - 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 499, 0, 0, 29, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, - 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, - 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, - 502, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, - 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, - 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, - 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, - 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, - 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, - 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, - 54, 0, 55, 228, 57, 0, 58, 0, 231, 0, - 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 221, 0, 0, 590, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 225, 0, 0, - 0, 53, 49, 52, 50, 226, 54, 0, 55, 228, - 57, 0, 58, 0, 231, 0, 0, 44, 56, 32, - 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, - 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, - 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, - 0, 35, 221, 0, 0, 590, 637, 0, 38, 0, - 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, - 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, - 0, 0, 0, 225, 0, 0, 0, 53, 49, 52, - 50, 226, 54, 0, 55, 228, 57, 0, 58, 0, - 231, 0, 0, 44, 56, 32, 0, 0, 0, 0, - 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, - 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, - 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, - 0, 49, 52, 50, 133, 134, 135, 0, 137, 138, - 139, 140, 141, 142, 0, 0, 130, 136, 121, 114, - 128, 116, 131, 0, 0, 0, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, - 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, - 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, - 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 129, 0, 0, 0, 398, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, - 0, 0, 0, 400, 49, 52, 50, 133, 134, 135, - 0, 137, 138, 139, 140, 141, 142, 0, 0, 130, - 136, 121, 114, 128, 116, 131, 0, 0, 0, 0, - 0, 0, 0, 46, 376, 383, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, - 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, - 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, - 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 129, 0, 0, 0, 398, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 132, 0, 0, 0, 0, 0, 400, 49, 52, 50, - 133, 134, 135, 0, 137, 138, 139, 140, 141, 142, - 0, 0, 130, 136, 121, 114, 128, 116, 131, 0, - 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, - 113, 0, 0, 115, 117, 118, 0, 0, 119, 0, - 120, 0, 0, 0, 122, 123, 124, 0, 0, 0, - 0, 0, 0, 35, 125, 126, 127, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, - 0, 398, 0, 0, 0, 0, 0, 0, 0, 399, - 0, 0, 0, 132, 0, 0, 0, 0, 0, 400, - 49, 52, 50, 133, 134, 135, 0, 137, 138, 139, - 140, 141, 142, 0, 0, 130, 136, 121, 114, 128, - 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, - 376, 383, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 214, 0, 0, 0, 0, 216, 0, 29, 30, - 31, 218, 0, 0, 0, 0, 0, 0, 219, 220, - 0, 0, 0, 0, 0, 0, 35, 221, 0, 0, - 222, 37, 0, 38, 0, 0, 0, 39, 0, 40, - 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 0, 0, 0, 224, 0, 225, 0, - 0, 0, 53, 49, 52, 50, 226, 54, 227, 55, - 228, 57, 229, 58, 230, 231, 0, 0, 44, 56, - 32, 215, 223, 217, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 214, 0, 0, 0, 0, 216, - 0, 29, 30, 31, 218, 0, 0, 0, 0, 0, - 0, 219, 33, 0, 0, 0, 0, 0, 0, 35, - 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, - 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, - 0, 47, 0, 48, 0, 0, 0, 0, 0, 224, - 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, - 54, 227, 55, 228, 57, 229, 58, 230, 231, 0, - 0, 44, 56, 32, 215, 223, 217, 0, 41, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 594, 112, 113, - 0, 0, 596, 117, 598, 30, 31, 599, 0, 120, - 0, 0, 0, 122, 601, 602, 0, 0, 0, 0, - 0, 0, 35, 603, 126, 127, 222, 37, 0, 38, - 0, 0, 0, 39, 0, 40, 605, 43, 0, 0, - 607, 0, 0, 0, 47, 0, 48, 0, 0, 0, - 0, 0, 608, 0, 225, 0, 0, 0, 609, 49, - 52, 50, 610, 611, 612, 55, 614, 615, 616, 617, - 618, 619, 0, 0, 606, 613, 600, 595, 604, 597, - 131, 41, 0, 0, 0, 0, 0, 0, 46, 376, - 383, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 367, 112, 113, 0, 0, 369, 117, 371, 30, 31, - 372, 0, 120, 0, 0, 0, 122, 374, 375, 0, - 0, 0, 0, 0, 0, 35, 377, 126, 127, 222, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 379, - 43, 0, 0, 381, 0, 0, 0, 47, 0, 48, - 0, -282, 0, 0, 0, 382, 0, 225, 0, 0, - 0, 384, 49, 52, 50, 385, 386, 387, 55, 389, - 390, 391, 392, 393, 394, 0, 0, 380, 388, 373, - 368, 378, 370, 131, 41, 0, 0, 0, 0, 0, - 0, 46, 376, 383, 0, 0, 0, 0, 0, 0, - 0, 0, 0, + -130, 452, 556, -146, 488, 637, 465, 469, 248, -149, + -141, 271, 353, -150, -138, -119, 486, 408, -150, 402, + 579, 406, -149, -130, 271, 353, 478, 403, -138, 572, + 396, -119, -118, 597, 428, 436, 443, 579, 456, 442, + 594, 436, 630, 579, 529, 452, 558, 579, 561, -146, + 460, 409, 555, -118, 412, 345, -141, 436, 286, 308, + 485, 452, 248, 458, 452, 417, 191, 174, 465, 469, + 410, 565, 539, 168, 428, 422, 151, 425, 145, 73, + 432, 286, 534, 194, 310, 326, 248, 0, 0, 187, + 0, 0, 271, 73, 0, 640, 427, 687, 0, 244, + 424, -143, 168, 247, 145, 265, 326, 101, 339, 357, + 452, 193, 332, 306, 183, 306, 145, 241, 469, 494, + 465, 153, 428, 318, 320, 353, 186, 176, 145, 246, + 446, 145, 145, 145, 315, 243, 101, 145, 455, 60, + 264, 145, 60, 60, 341, 0, 177, 347, 0, 145, + 61, 308, 456, 61, 61, 60, 686, 685, 64, 642, + 641, 576, 262, 261, 103, 145, 61, 495, 267, 65, + 678, 677, 328, 176, 262, 261, 329, 537, 260, 259, + 505, 537, 255, 254, 471, 355, 538, 684, 683, 351, + 567, 176, 177, 467, 559, 420, 419, 342, 576, 655, + 656, 430, 349, 655, 656, 542, 541, 262, 261, 650, + 177, 170, 145, 146, 535, 171, 439, 481, 87, 588, + 88, 270, 268, 176, 0, 644, 545, 0, 0, 535, + 535, 89, 66, 681, 680, 570, 87, 0, 88, 530, + 145, 560, 177, 0, 415, 563, 577, 66, 0, 89, + 269, 579, 87, 0, 88, 87, 87, 88, 88, 66, + 532, 440, 535, 0, 324, 89, 0, 679, 89, 89, + 482, 480, 531, 589, 587, 532, 532, 67, 145, 145, + 546, 544, 87, 68, 88, 532, 0, 531, 531, 571, + 569, 532, 67, 239, 238, 89, 176, 531, 68, 87, + 176, 88, 535, 531, 67, 87, 0, 88, 532, 87, + 68, 88, 89, 632, 645, 177, 0, 178, 89, 177, + 531, 415, 89, 87, 87, 88, 88, 181, 87, 0, + 88, 450, 449, 87, 176, 88, 89, 89, 633, 631, + 0, 89, 288, 289, 75, 76, 89, 674, 532, 288, + 289, 0, -105, 177, 0, 178, 75, 76, 0, 0, + 531, 675, 673, 0, 0, 0, 0, 176, 0, 290, + 291, 77, 78, 0, 0, 35, 290, 291, 0, 105, + 293, 294, 0, 77, 78, -105, 177, 0, 178, 295, + 0, 0, 296, 0, 297, 672, 0, 0, 106, 0, + 107, 6, 5, 4, 1, 3, 2, 80, 81, 0, + 0, 0, 49, 52, 50, 82, 83, 80, 81, 84, + 0, 85, 0, 0, 0, 82, 83, 80, 81, 84, + 35, 85, 0, 0, 0, 82, 83, 80, 81, 84, + 35, 85, 46, 34, 51, 82, 83, 80, 81, 84, + 35, 85, 0, 0, 35, 82, 83, 35, 0, 84, + 0, 85, 0, 35, 0, 0, 35, 49, 52, 50, + 0, 0, 293, 294, 35, 0, 0, 49, 52, 50, + 0, 295, 0, 0, 296, 0, 297, 49, 52, 50, + 0, 49, 52, 50, 49, 52, 50, 46, 34, 51, + 49, 52, 50, 49, 52, 50, 0, 46, 34, 51, + 0, 49, 52, 50, 0, 0, 0, 46, 34, 51, + 0, 46, 34, 51, 46, 34, 51, 0, 0, 0, + 46, 34, 51, 46, 34, 51, 537, 35, 0, 537, + 35, 46, 34, 51, 186, 35, 0, 186, 0, 0, + 35, 0, 186, 35, 0, 0, 0, 35, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 49, 52, 50, 49, 52, 50, + 0, 0, 49, 52, 50, 0, 0, 49, 52, 50, + 49, 52, 50, 0, 49, 52, 50, 0, 0, 0, + 35, 0, 0, 0, 46, 34, 51, 46, 34, 51, + 35, 0, 46, 34, 51, 0, 0, 46, 34, 51, + 46, 34, 51, 35, 46, 34, 51, 0, 0, 0, + 0, 0, 253, 252, 0, 0, 537, 49, 52, 50, + 0, 0, 253, 252, 0, 0, 0, 49, 52, 50, + 35, 0, 0, 0, 0, 258, 257, 0, 0, 0, + 49, 52, 50, 35, 0, 0, 35, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 49, 52, 50, + 46, 34, 51, 0, 0, 253, 252, 0, 258, 257, + 49, 52, 50, 49, 52, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 155, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 156, 0, 0, 0, 157, 35, + 46, 34, 51, 46, 34, 51, 0, 158, 0, 159, + 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, + 160, 0, 161, 64, 0, 0, 0, 0, 0, 0, + 162, 258, 257, 163, 65, 0, 49, 52, 50, 164, + 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 166, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 30, 31, 155, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 156, 0, + 0, 0, 157, 35, 0, 0, 0, 36, 37, 0, + 38, 158, 0, 159, 0, 0, 0, 521, 0, 0, + 0, 45, 0, 0, 160, 0, 161, 64, 0, 0, + 0, 0, 0, 0, 162, 0, 0, 163, 65, 53, + 49, 52, 50, 164, 54, 0, 0, 0, 0, 165, + 0, 0, 0, 0, 0, 44, 56, 32, 0, 0, + 0, 0, 41, 0, 0, 166, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, + 0, 0, 0, 0, 521, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, + 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, + 42, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 53, 49, 52, 50, 0, 54, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 44, 56, + 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 519, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 0, 0, 0, 521, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 522, 524, 523, 0, 54, 0, 0, 0, 0, + 230, 0, 0, 0, 0, 0, 44, 56, 32, 216, + 224, 0, 0, 41, 0, 0, 520, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 519, 0, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, + 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, + 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 584, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 53, 522, + 524, 523, 0, 54, 0, 0, 0, 0, 230, 0, + 0, 0, 0, 0, 44, 56, 32, 216, 224, 0, + 0, 41, 0, 0, 520, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 519, 0, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, + 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, + 0, 0, 0, 0, 521, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 581, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 53, 522, 524, 523, + 0, 54, 0, 0, 0, 0, 230, 0, 0, 0, + 0, 0, 44, 56, 32, 216, 224, 0, 0, 41, + 0, 0, 520, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, + 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -139, 0, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, + 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, + 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, + 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, + 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 285, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 498, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 35, 222, 0, 0, 599, 37, 0, 38, + 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, + 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, + 0, 0, 0, 0, 226, 0, 0, 0, 53, 49, + 52, 50, 227, 54, 0, 55, 229, 57, 0, 58, + 0, 232, 0, 0, 44, 56, 32, 0, 0, 0, + 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 222, 0, 0, 599, 646, 0, 38, 0, 0, 0, + 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, + 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, + 0, 226, 0, 0, 0, 53, 49, 52, 50, 227, + 54, 0, 55, 229, 57, 0, 58, 0, 232, 0, + 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 35, 222, 0, 0, + 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 226, 0, + 0, 0, 53, 49, 52, 50, 227, 54, 0, 55, + 229, 57, 0, 58, 0, 232, 0, 0, 44, 56, + 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, + 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, + 0, 123, 124, 125, 0, 0, 0, 0, 0, 0, + 35, 126, 127, 128, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 130, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 133, 0, 0, 0, 0, 0, 0, 49, 52, 50, + 134, 135, 136, 0, 138, 139, 140, 141, 142, 143, + 0, 0, 131, 137, 122, 114, 129, 116, 132, 0, + 0, 0, 121, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, + 0, 120, 0, 0, 0, 123, 124, 125, 0, 0, + 0, 0, 0, 0, 35, 126, 127, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 130, 0, + 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 133, 0, 0, 0, 0, 0, + 401, 49, 52, 50, 134, 135, 136, 0, 138, 139, + 140, 141, 142, 143, 0, 0, 131, 137, 122, 114, + 129, 116, 132, 0, 0, 0, 121, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, + 118, 0, 0, 119, 0, 120, 0, 0, 0, 123, + 124, 125, 0, 0, 0, 0, 0, 0, 35, 126, + 127, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 130, 0, 0, 0, 399, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 133, 0, + 0, 0, 0, 0, 401, 49, 52, 50, 134, 135, + 136, 0, 138, 139, 140, 141, 142, 143, 0, 0, + 131, 137, 122, 114, 129, 116, 132, 0, 0, 0, + 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, + 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, + 0, 0, 0, 123, 124, 125, 0, 0, 0, 0, + 0, 0, 35, 126, 127, 128, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, + 399, 0, 0, 0, 0, 0, 0, 0, 400, 0, + 0, 0, 133, 0, 0, 0, 0, 0, 401, 49, + 52, 50, 134, 135, 136, 0, 138, 139, 140, 141, + 142, 143, 0, 0, 131, 137, 122, 114, 129, 116, + 132, 0, 0, 0, 121, 0, 0, 0, 0, 46, + 377, 384, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 215, 0, 0, 0, 0, 217, 0, 29, 30, + 31, 219, 0, 0, 0, 0, 0, 0, 220, 33, + 0, 0, 0, 0, 0, 0, 35, 222, 0, 0, + 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 225, 0, 226, 0, + 0, 0, 53, 49, 52, 50, 227, 54, 228, 55, + 229, 57, 230, 58, 231, 232, 0, 0, 44, 56, + 32, 216, 224, 218, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, + 217, 0, 29, 30, 31, 219, 0, 0, 0, 0, + 0, 0, 220, 221, 0, 0, 0, 0, 0, 0, + 35, 222, 0, 0, 223, 37, 0, 38, 0, 0, + 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, + 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, + 225, 0, 226, 0, 0, 0, 53, 49, 52, 50, + 227, 54, 228, 55, 229, 57, 230, 58, 231, 232, + 0, 0, 44, 56, 32, 216, 224, 218, 0, 41, + 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 603, + 112, 113, 0, 0, 605, 117, 607, 30, 31, 608, + 0, 120, 0, 0, 0, 123, 610, 611, 0, 0, + 0, 0, 0, 0, 35, 612, 127, 128, 223, 37, + 0, 38, 0, 0, 0, 39, 0, 40, 614, 43, + 0, 0, 616, 0, 0, 0, 47, 0, 48, 0, + 0, 0, 0, 0, 617, 0, 226, 0, 0, 0, + 618, 49, 52, 50, 619, 620, 621, 55, 623, 624, + 625, 626, 627, 628, 0, 0, 615, 622, 609, 604, + 613, 606, 132, 41, 0, 0, 121, 0, 0, 0, + 0, 46, 377, 384, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 368, 112, 113, 0, 0, 370, 117, + 372, 30, 31, 373, 0, 120, 0, 0, 0, 123, + 375, 376, 0, 0, 0, 0, 0, 0, 35, 378, + 127, 128, 223, 37, 0, 38, 0, 0, 0, 39, + 0, 40, 380, 43, 0, 0, 382, 0, 0, 0, + 47, 0, 48, 0, -286, 0, 0, 0, 383, 0, + 226, 0, 0, 0, 385, 49, 52, 50, 386, 387, + 388, 55, 390, 391, 392, 393, 394, 395, 0, 0, + 381, 389, 374, 369, 379, 371, 132, 41, 0, 0, + 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, + 0, 0, 0, 0, 0, 0, 0, - 315, 478, 645, 153, 673, 465, 460, 501, 458, 461, - 184, 456, 396, 498, 488, 503, 440, 444, 436, 428, - 657, 667, 656, 644, 403, 630, 642, 629, 447, 634, - 450, 627, 315, 143, 553, 184, 255, 250, 149, 447, - 146, 353, 151, 349, 541, 531, 450, 534, 315, 179, - 538, 166, 172, 184, 322, 189, 620, 191, 16, 255, - 207, 250, 239, 586, 174, 250, 255, 587, 184, 447, - 265, 0, 577, 515, 584, 472, 559, 333, 450, 412, - 207, 514, 517, 471, 248, 467, 517, 565, 557, 207, - 315, 569, 315, 364, 207, 207, 207, 189, 249, 207, - 207, 207, 496, 0, 207, 315, 495, 412, 469, 358, - 333, 412, 207, 315, 62, 279, 413, 62, 0, 297, - 283, 486, 298, 244, 62, 241, 183, 62, 62, 62, - 262, 425, 299, 300, 301, 320, 364, 324, 62, 62, - 505, 506, 189, 262, 413, 0, 207, 0, 413, 472, - 0, 207, 593, 207, 62, 62, 507, 508, 62, 207, - 509, 62, 62, 510, 183, 316, 62, 318, 462, 149, - 188, 513, 62, 347, 463, 351, 62, 181, 355, 62, - 343, 357, 404, 62, 396, 462, 342, 262, 345, 207, - 108, 207, 171, 168, 207, 396, 62, 207, 207, 62, - 62, 183, 463, 207, 62, 62, 104, 62, 92, 62, - 406, 91, 249, 62, 97, 462, 364, 102, 62, 110, - 463, 420, 62, 482, 62, 396, 207, 96, 90, 62, - 207, 189, 207, 0, 95, 62, 62, 62, 62, 63, - 94, 72, 93, 62, 0, 62, 62, 62, 86, 62, - 397, 100, 99, 98, 108, 79, 62, 410, 149, 422, - 660, 659, 517, 62, 74, 71, 415, 661, 0, 62, - 0, 70, 62, 311, 69, 311, 311, 62, 283, 0, - 283, 283, 283, 110, 178, 62, 311, 311, 364, 364, - 283, 283, 283, 517, 329, 339, 62, 332, 330, 0, - 326, 283, 525, 0, 207, 207, 62, 308, 313, 310, - 0, 283, 62, 62, 516, 526, 0, 283, 283, 306, - 291, 286, 62, 62, 0, 517, 62, 283, 283, 302, - 303, 283, 576, 304, 643, 573, 0, 0, 0, 0, - 0, 517, 0, 0, 517, 0, 0, 0, 0, 0, - 525, 0, 0, 525, 545, 546, 547, 548, 552, 549, - 550, 0, 516, 526, 0, 516, 526, 589, 0, 0, - 0, 0, 0, 0, 443, 446, 638, 639, 545, 546, - 547, 548, 552, 549, 550, 589, 0, 0, 0, 0, - 0, 0, 0, 0, 591, 592, 545, 546, 547, 548, - 552, 549, 550, 581, 0, 0, 0, 0, 0, 0, - 0, 0, 582, 583, 545, 546, 547, 548, 552, 549, - 550, 0, 581, 0, 0, 0, 0, 565, 0, 640, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 488, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0}; + 16, 150, 636, 543, 536, 654, 540, 334, 154, 682, + 676, 144, 256, 643, 638, 451, 639, 448, 504, 489, + 397, 316, 266, 651, 185, 586, 629, 251, 185, 323, + 596, 595, 566, 653, 665, 593, 666, 466, 448, 568, + 180, 451, 457, 459, 316, 316, 499, 251, 147, 462, + 470, 256, 461, 448, 437, 429, 441, 185, 354, 445, + 173, 451, 185, 240, 192, 562, 404, 473, 472, 0, + 316, 175, 533, 502, 152, 167, 208, 251, 256, 190, + 516, 479, 190, 208, 208, 413, 263, 208, 316, 0, + 397, 365, 515, 208, 518, 518, 208, 0, 62, 670, + 464, 0, 208, 62, 652, 509, 208, 208, 62, 350, + 463, 423, 62, 190, 511, 426, 398, 62, 62, 510, + 464, 411, 150, 414, 468, 62, 334, 184, 62, 62, + 182, 280, 62, 302, 301, 250, 284, 62, 62, 463, + 208, 62, 189, 300, 413, 62, 317, 62, 172, 208, + 299, 62, 298, 506, 62, 169, 507, 150, 62, 497, + 508, 518, 62, 496, 319, 416, 574, 86, 62, 321, + 413, 62, 62, 93, 62, 514, 95, 96, 397, 97, + 62, 263, 414, 62, 90, 208, 316, 91, 62, 62, + 62, 184, 92, 405, 62, 94, 316, 208, 108, 250, + 62, 249, 190, 343, 348, 407, 102, 358, 414, 208, + 104, 352, 208, 208, 365, 208, 62, 208, 669, 668, + 208, 325, 100, 208, 62, 365, 69, 208, 110, 397, + 602, 242, 62, 62, 70, 71, 62, 208, 473, 72, + 245, 359, 62, 487, 62, 63, 0, 62, 263, 463, + 518, 62, 74, 62, 0, 578, 421, 79, 62, 98, + 464, 62, 344, 62, 208, 184, 365, 99, 312, 62, + 62, 0, 346, 284, 284, 284, 62, 292, 287, 62, + 62, 284, 208, 303, 284, 284, 304, 305, 312, 62, + 340, 108, 62, 284, 284, 365, 312, 284, 312, 62, + 309, 284, 62, 284, 284, 307, 518, 284, 0, 312, + 333, 208, 0, 483, 284, 527, 330, 327, 331, 356, + 311, 110, 179, 0, 0, 590, 0, 517, 528, 582, + 574, 314, 649, 0, 0, 208, 0, 0, 518, 547, + 548, 549, 550, 554, 551, 552, 0, 527, 0, 0, + 0, 0, 598, 447, 489, 0, 0, 0, 0, 517, + 528, 600, 601, 547, 548, 549, 550, 554, 551, 552, + 0, 0, 0, 0, 590, 0, 0, 0, 0, 0, + 0, 0, 444, 591, 592, 547, 548, 549, 550, 554, + 551, 552, 598, 0, 0, 0, 0, 0, 0, 0, + 0, 647, 648, 547, 548, 549, 550, 554, 551, 552, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 585, + 0, 0, 0, 0, 0, 0, 0, 0, 518, 0, + 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 517, + 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; const short QQmlJSGrammar::action_check [] = { - 8, 61, 60, 8, 48, 1, 0, 2, 55, 79, - 33, 1, 60, 48, 7, 79, 66, 60, 17, 8, - 36, 66, 29, 8, 60, 37, 60, 33, 79, 33, - 1, 33, 2, 36, 20, 55, 8, 79, 7, 61, - 55, 60, 29, 7, 5, 1, 5, 48, 48, 33, - 33, 5, 7, 33, 17, 37, 31, 36, 55, 8, - 33, 7, 60, 77, 7, 33, 7, 7, 60, 36, - 61, 36, 60, 7, 36, 8, 36, 16, 36, 36, - 7, 55, 34, 36, 7, 7, 7, 7, 36, 55, - 7, 36, 7, 7, 36, 7, 7, 7, 36, 2, - 36, 7, 33, 7, 55, 7, 36, 7, 55, 33, - 60, 7, -1, 8, 33, 8, 60, 42, 36, -1, - 36, 8, -1, 60, 33, 33, 61, 62, 53, 36, - 8, 36, 8, -1, 33, 36, 8, 50, 36, -1, - 8, 54, 8, 40, 15, 8, 15, 8, 40, 8, - 8, -1, -1, 24, 51, 15, -1, 10, 15, 51, - 8, 56, 50, 92, 93, 34, 54, 60, 8, 56, - 61, 62, 61, 62, 34, 61, 62, 34, 56, 61, - 62, 61, 62, 8, 60, 40, 61, 62, 60, 40, - 56, 7, 60, 6, 61, 62, 51, 60, 15, 60, - 51, 60, 55, 61, 7, 61, 62, 20, 56, 61, - 62, 92, 93, 15, 61, 62, 12, 34, 29, 36, - 60, 8, 24, 15, 29, 25, 12, 27, 25, -1, - 27, 7, 15, 12, 29, 29, 61, 62, 38, 7, - -1, 38, 34, -1, 36, 61, 62, 25, 29, 27, - 25, 34, 27, 36, 61, 62, 25, 29, 27, 8, - 38, 57, -1, 38, 75, 33, 25, 63, 27, 38, - 75, 57, 29, -1, 61, 62, 87, 63, 57, 38, - 75, 75, 87, 90, 63, 61, 62, 25, 25, 27, - 27, 36, 87, 87, 75, 25, 25, 27, 27, 7, - 38, 38, 15, 75, 7, 8, 87, -1, 38, 38, - 61, 62, 61, 62, -1, 87, 61, 62, 75, 15, - 25, 34, 27, 36, 18, 19, 15, 95, 18, 19, - 87, 18, 19, 38, 18, 19, -1, 33, 34, -1, - 36, -1, -1, 94, 33, 34, -1, 36, -1, 47, - -1, 45, 46, 61, 62, 45, 46, -1, 45, 46, - -1, 45, 46, 61, 62, 23, 24, -1, -1, 23, - 24, -1, -1, -1, 32, 23, 24, 35, 32, 37, - -1, 35, 29, 37, 32, 23, 24, 35, 29, 37, - -1, 23, 24, 29, 32, -1, 94, 35, 29, 37, - 32, 29, -1, 35, 25, 37, 27, 99, 100, 101, - 102, 103, 104, -1, 29, -1, -1, 38, -1, 66, - 67, 68, -1, -1, -1, 66, 67, 68, -1, -1, - 66, 67, 68, -1, -1, 66, 67, 68, 66, 67, - 68, -1, -1, -1, -1, -1, 29, -1, 95, 96, - 97, 66, 67, 68, 95, 96, 97, -1, -1, 95, - 96, 97, -1, -1, 95, 96, 97, 95, 96, 97, - 29, -1, -1, -1, -1, -1, -1, -1, 29, -1, - 95, 96, 97, 66, 67, 68, 23, 24, 29, -1, - -1, 29, -1, -1, 31, 32, 29, -1, 35, -1, - 37, -1, -1, 36, -1, -1, -1, 66, 67, 68, - -1, 29, 95, 96, 97, 66, 67, 68, 36, -1, - -1, -1, -1, -1, -1, 66, 67, 68, 66, 67, - 68, -1, -1, 66, 67, 68, 95, 96, 97, 15, - -1, -1, -1, -1, 95, 96, 97, 15, 66, 67, - 68, -1, -1, 29, 95, 96, 97, 95, 96, 97, - -1, 29, 95, 96, 97, 29, -1, -1, -1, -1, - 29, -1, 36, 29, -1, -1, -1, 95, 96, 97, - -1, -1, -1, -1, -1, 29, -1, -1, -1, -1, - 66, 67, 68, -1, -1, -1, -1, -1, 66, 67, - 68, -1, 66, 67, 68, 61, 62, 66, 67, 68, - 66, 67, 68, -1, -1, -1, -1, 61, 62, 95, - 96, 97, 66, 67, 68, -1, 29, 95, 96, 97, - -1, 95, 96, 97, -1, -1, 95, 96, 97, 95, - 96, 97, 29, -1, -1, 29, -1, -1, 15, -1, - -1, 95, 96, 97, -1, -1, -1, -1, 61, 62, - -1, -1, 29, 66, 67, 68, -1, -1, -1, -1, - -1, -1, -1, -1, 61, 62, -1, 61, 62, 66, - 67, 68, 66, 67, 68, -1, -1, -1, -1, -1, - -1, -1, 95, 96, 97, -1, -1, -1, -1, 66, - 67, 68, -1, -1, -1, -1, -1, -1, 95, 96, - 97, 95, 96, 97, -1, -1, -1, -1, -1, -1, - -1, 3, -1, -1, -1, -1, -1, -1, 95, 96, - 97, 13, -1, -1, -1, 17, -1, -1, -1, -1, - 29, -1, -1, -1, 26, -1, 28, -1, -1, 31, - -1, -1, -1, -1, -1, -1, -1, 39, -1, 41, - 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, - 52, 53, 61, 62, -1, -1, 58, 66, 67, 68, - -1, -1, 64, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 80, -1, - -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, - -1, -1, -1, -1, -1, -1, 12, 13, 3, -1, - -1, -1, -1, -1, -1, -1, 22, -1, 13, -1, - -1, -1, 17, 29, -1, -1, -1, 33, 34, -1, - 36, 26, -1, 28, -1, -1, -1, 43, -1, -1, - -1, 47, -1, -1, 39, -1, 41, 42, -1, -1, - -1, -1, -1, -1, 49, -1, -1, 52, 53, 65, - 66, 67, 68, 58, 70, -1, -1, -1, -1, 64, - -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, -1, 88, -1, -1, 80, -1, -1, -1, 95, - 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 12, 13, -1, -1, -1, -1, -1, -1, - -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, - -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, - 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, - -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, - -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, - -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, - 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, - -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, - -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, - -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, - 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, - -1, -1, -1, 81, 82, 83, 84, 85, -1, -1, - 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, - -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, - -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, - -1, -1, -1, -1, 55, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, - 81, 82, 83, 84, 85, -1, -1, 88, -1, -1, - -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 10, -1, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, - -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, - -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, - -1, 75, -1, -1, -1, -1, -1, 81, 82, 83, - 84, 85, -1, -1, 88, -1, -1, -1, -1, -1, - -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, - -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, - -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, - -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, - -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, - 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, - -1, -1, -1, 81, 82, 83, -1, -1, -1, -1, - 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, - 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, - 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - 72, -1, 74, 75, 76, -1, -1, -1, -1, 81, - 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, - -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 7, -1, -1, -1, 11, - 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, - 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, - 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, - -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, - 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, - -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, - 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, - 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, - 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, - -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, - -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, - 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, - -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, - 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, - -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, - -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, - -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, - -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, - 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, - -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, - 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, - 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, -1, 88, -1, -1, -1, -1, -1, -1, 95, - 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, - -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, - 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, - 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, - -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, - -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, - 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, - -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, - -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, -1, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, - 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, - -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, - -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, - -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, - -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, - -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, - -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, - -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, - 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, - 78, -1, -1, 81, 82, 83, -1, -1, -1, -1, - 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, - 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, - -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, - -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, - -1, 66, 67, 68, 69, 70, 71, -1, 73, 74, - 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, - 85, 86, 87, -1, -1, -1, -1, -1, -1, -1, - 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, - -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, - 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, - 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 59, -1, -1, - -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, - -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, 86, 87, -1, -1, -1, -1, - -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, - 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, - -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, - 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, - 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, - -1, -1, 81, 82, 83, 84, 85, 86, 87, -1, - -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, - 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, - 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, - -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 43, -1, -1, - -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, - -1, -1, -1, 59, -1, -1, -1, -1, -1, 65, - 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, - 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, - 86, 87, -1, -1, -1, -1, -1, -1, -1, 95, - 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 4, -1, -1, -1, -1, 9, -1, 11, 12, - 13, 14, -1, -1, -1, -1, -1, -1, 21, 22, - -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, - 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, - 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, - 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, - -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, - 83, 84, 85, 86, -1, 88, -1, -1, -1, -1, - -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, -1, -1, -1, -1, 9, - -1, 11, 12, 13, 14, -1, -1, -1, -1, -1, - -1, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, - 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, - -1, 51, -1, 53, -1, -1, -1, -1, -1, 59, - -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, -1, 88, -1, - -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, - -1, -1, 9, 10, 11, 12, 13, 14, -1, 16, - -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, - -1, -1, 29, 30, 31, 32, 33, 34, -1, 36, - -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, - 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, - -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, - 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, - 87, 88, -1, -1, -1, -1, -1, -1, 95, 96, - 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, - 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, - -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, 55, -1, -1, -1, 59, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, - 84, 85, 86, 87, 88, -1, -1, -1, -1, -1, - -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, - -1, -1, -1, + 7, 33, 33, 7, 55, 8, 36, 36, 7, 7, + 7, 36, 36, 7, 7, 7, 33, 36, 7, 55, + 33, 55, 7, 7, 36, 36, 17, 7, 7, 34, + 8, 7, 7, 7, 36, 5, 7, 33, 20, 55, + 66, 5, 29, 33, 66, 33, 29, 33, 29, 7, + 60, 60, 29, 7, 55, 61, 7, 5, 1, 79, + 60, 33, 7, 36, 33, 60, 8, 7, 36, 36, + 33, 37, 29, 2, 36, 36, 8, 33, 8, 1, + 33, 1, 37, 33, 8, 2, 7, -1, -1, 36, + -1, -1, 36, 1, -1, 60, 55, 0, -1, 33, + 60, 7, 2, 55, 8, 36, 2, 48, 17, 16, + 33, 60, 7, 48, 60, 48, 8, 36, 36, 8, + 36, 60, 36, 61, 60, 36, 36, 15, 8, 55, + 7, 8, 8, 8, 61, 60, 48, 8, 6, 40, + 77, 8, 40, 40, 8, -1, 34, 60, -1, 8, + 51, 79, 20, 51, 51, 40, 61, 62, 42, 61, + 62, 8, 61, 62, 79, 8, 51, 56, 60, 53, + 61, 62, 50, 15, 61, 62, 54, 15, 61, 62, + 60, 15, 61, 62, 60, 60, 24, 61, 62, 60, + 24, 15, 34, 60, 8, 61, 62, 61, 8, 93, + 94, 60, 31, 93, 94, 61, 62, 61, 62, 56, + 34, 50, 8, 56, 29, 54, 10, 8, 25, 7, + 27, 61, 62, 15, -1, 7, 7, -1, -1, 29, + 29, 38, 12, 61, 62, 7, 25, -1, 27, 29, + 8, 55, 34, -1, 36, 29, 56, 12, -1, 38, + 90, 33, 25, -1, 27, 25, 25, 27, 27, 12, + 75, 55, 29, -1, 60, 38, -1, 95, 38, 38, + 61, 62, 87, 61, 62, 75, 75, 57, 8, 8, + 61, 62, 25, 63, 27, 75, -1, 87, 87, 61, + 62, 75, 57, 61, 62, 38, 15, 87, 63, 25, + 15, 27, 29, 87, 57, 25, -1, 27, 75, 25, + 63, 27, 38, 36, 96, 34, -1, 36, 38, 34, + 87, 36, 38, 25, 25, 27, 27, 56, 25, -1, + 27, 61, 62, 25, 15, 27, 38, 38, 61, 62, + -1, 38, 18, 19, 18, 19, 38, 47, 75, 18, + 19, -1, 33, 34, -1, 36, 18, 19, -1, -1, + 87, 61, 62, -1, -1, -1, -1, 15, -1, 45, + 46, 45, 46, -1, -1, 29, 45, 46, -1, 15, + 23, 24, -1, 45, 46, 33, 34, -1, 36, 32, + -1, -1, 35, -1, 37, 95, -1, -1, 34, -1, + 36, 100, 101, 102, 103, 104, 105, 23, 24, -1, + -1, -1, 66, 67, 68, 31, 32, 23, 24, 35, + -1, 37, -1, -1, -1, 31, 32, 23, 24, 35, + 29, 37, -1, -1, -1, 31, 32, 23, 24, 35, + 29, 37, 96, 97, 98, 31, 32, 23, 24, 35, + 29, 37, -1, -1, 29, 31, 32, 29, -1, 35, + -1, 37, -1, 29, -1, -1, 29, 66, 67, 68, + -1, -1, 23, 24, 29, -1, -1, 66, 67, 68, + -1, 32, -1, -1, 35, -1, 37, 66, 67, 68, + -1, 66, 67, 68, 66, 67, 68, 96, 97, 98, + 66, 67, 68, 66, 67, 68, -1, 96, 97, 98, + -1, 66, 67, 68, -1, -1, -1, 96, 97, 98, + -1, 96, 97, 98, 96, 97, 98, -1, -1, -1, + 96, 97, 98, 96, 97, 98, 15, 29, -1, 15, + 29, 96, 97, 98, 36, 29, -1, 36, -1, -1, + 29, -1, 36, 29, -1, -1, -1, 29, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 66, 67, 68, 66, 67, 68, + -1, -1, 66, 67, 68, -1, -1, 66, 67, 68, + 66, 67, 68, -1, 66, 67, 68, -1, -1, -1, + 29, -1, -1, -1, 96, 97, 98, 96, 97, 98, + 29, -1, 96, 97, 98, -1, -1, 96, 97, 98, + 96, 97, 98, 29, 96, 97, 98, -1, -1, -1, + -1, -1, 61, 62, -1, -1, 15, 66, 67, 68, + -1, -1, 61, 62, -1, -1, -1, 66, 67, 68, + 29, -1, -1, -1, -1, 61, 62, -1, -1, -1, + 66, 67, 68, 29, -1, -1, 29, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, 66, 67, 68, + 96, 97, 98, -1, -1, 61, 62, -1, 61, 62, + 66, 67, 68, 66, 67, 68, -1, -1, -1, -1, + -1, -1, -1, -1, 3, -1, -1, 96, 97, 98, + -1, -1, -1, -1, 13, -1, -1, -1, 17, 29, + 96, 97, 98, 96, 97, 98, -1, 26, -1, 28, + -1, -1, 31, -1, -1, -1, -1, -1, -1, -1, + 39, -1, 41, 42, -1, -1, -1, -1, -1, -1, + 49, 61, 62, 52, 53, -1, 66, 67, 68, 58, + -1, -1, -1, -1, -1, 64, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 80, -1, -1, -1, -1, 96, 97, 98, -1, + -1, -1, -1, -1, -1, -1, 12, 13, 3, -1, + -1, -1, -1, -1, -1, -1, 22, -1, 13, -1, + -1, -1, 17, 29, -1, -1, -1, 33, 34, -1, + 36, 26, -1, 28, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, 39, -1, 41, 42, -1, -1, + -1, -1, -1, -1, 49, -1, -1, 52, 53, 65, + 66, 67, 68, 58, 70, -1, -1, -1, -1, 64, + -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, -1, 88, -1, -1, 80, -1, -1, -1, -1, + 96, 97, 98, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, -1, 88, + -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, -1, -1, -1, + 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 65, 66, 67, 68, -1, 70, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, + -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 10, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + 75, -1, -1, -1, -1, -1, 81, 82, 83, 84, + 85, -1, -1, 88, -1, -1, 91, -1, -1, -1, + -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 10, -1, 12, 13, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, + -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, + 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, + 67, 68, -1, 70, -1, -1, -1, -1, 75, -1, + -1, -1, -1, -1, 81, 82, 83, 84, 85, -1, + -1, 88, -1, -1, 91, -1, -1, -1, -1, 96, + 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, -1, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, + -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, -1, -1, -1, 75, -1, -1, -1, + -1, -1, 81, 82, 83, 84, 85, -1, -1, 88, + -1, -1, 91, -1, -1, -1, -1, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, + -1, -1, -1, -1, 96, 97, 98, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, + 81, 82, 83, -1, -1, -1, -1, 88, -1, -1, + -1, -1, -1, -1, -1, 96, 97, 98, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, 75, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, 29, 30, -1, -1, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, + -1, -1, -1, -1, 61, -1, -1, -1, 65, 66, + 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, + -1, 78, -1, -1, 81, 82, 83, -1, -1, -1, + -1, 88, -1, -1, -1, -1, -1, -1, -1, 96, + 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, + -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, + -1, -1, -1, -1, -1, -1, 96, 97, 98, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, -1, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, + 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, + 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, + -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, + 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, + -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 59, -1, -1, -1, -1, -1, -1, 66, 67, 68, + 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, 86, 87, -1, + -1, -1, 91, -1, -1, -1, -1, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, 91, -1, -1, -1, + -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, + 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, + -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, 86, 87, -1, -1, -1, + 91, -1, -1, -1, -1, 96, 97, 98, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, + -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, + -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, + 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, + -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, + 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, + 87, -1, -1, -1, 91, -1, -1, -1, -1, 96, + 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, -1, -1, -1, -1, 9, -1, 11, 12, + 13, 14, -1, -1, -1, -1, -1, -1, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, -1, 88, -1, -1, -1, -1, + -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, + 9, -1, 11, 12, 13, 14, -1, -1, -1, -1, + -1, -1, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, 86, -1, 88, + -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, 12, 13, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, 59, -1, 61, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, 88, -1, -1, 91, -1, -1, -1, + -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, + 11, 12, 13, 14, -1, 16, -1, -1, -1, 20, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + 31, 32, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, 55, -1, -1, -1, 59, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, 86, 87, 88, -1, -1, + 91, -1, -1, -1, -1, 96, 97, 98, -1, -1, + -1, -1, -1, -1, -1, -1, -1, - 3, 42, 9, 77, 18, 3, 25, 42, 18, 25, - 18, 105, 18, 42, 42, 3, 100, 3, 103, 3, - 14, 18, 14, 22, 42, 18, 22, 32, 3, 18, - 25, 32, 3, 3, 14, 18, 18, 18, 42, 3, - 42, 3, 42, 3, 18, 32, 25, 32, 3, 3, - 18, 42, 42, 18, 3, 18, 22, 18, 3, 18, - 18, 18, 18, 32, 42, 18, 18, 18, 18, 3, - 3, -1, 18, 2, 22, 18, 18, 18, 25, 14, - 18, 4, 14, 2, 2, 2, 14, 19, 32, 18, - 3, 19, 3, 2, 18, 18, 18, 18, 4, 18, - 18, 18, 38, -1, 18, 3, 42, 14, 3, 18, - 18, 14, 18, 3, 54, 54, 51, 54, -1, 59, - 59, 45, 59, 45, 54, 46, 56, 54, 54, 54, - 2, 45, 59, 59, 59, 2, 2, 2, 54, 54, - 56, 56, 18, 2, 51, -1, 18, -1, 51, 18, - -1, 18, 18, 18, 54, 54, 56, 56, 54, 18, - 56, 54, 54, 56, 56, 78, 54, 78, 56, 42, - 46, 109, 54, 2, 56, 2, 54, 50, 2, 54, - 78, 2, 2, 54, 18, 56, 94, 2, 78, 18, - 18, 18, 70, 68, 18, 18, 54, 18, 18, 54, - 54, 56, 56, 18, 54, 54, 64, 54, 58, 54, - 44, 58, 4, 54, 59, 56, 2, 66, 54, 47, - 56, 44, 54, 92, 54, 18, 18, 59, 58, 54, - 18, 18, 18, -1, 59, 54, 54, 54, 54, 57, - 59, 57, 59, 54, -1, 54, 54, 54, 59, 54, - 43, 60, 60, 60, 18, 60, 54, 45, 42, 46, - 11, 12, 14, 54, 62, 56, 50, 19, -1, 54, - -1, 56, 54, 54, 56, 54, 54, 54, 59, -1, - 59, 59, 59, 47, 48, 54, 54, 54, 2, 2, - 59, 59, 59, 14, 71, 76, 54, 76, 76, -1, - 69, 59, 23, -1, 18, 18, 54, 65, 76, 76, - -1, 59, 54, 54, 35, 36, -1, 59, 59, 67, - 61, 63, 54, 54, -1, 14, 54, 59, 59, 61, - 61, 59, 5, 61, 23, 5, -1, -1, -1, -1, - -1, 14, -1, -1, 14, -1, -1, -1, -1, -1, - 23, -1, -1, 23, 25, 26, 27, 28, 29, 30, - 31, -1, 35, 36, -1, 35, 36, 14, -1, -1, - -1, -1, -1, -1, 88, 88, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 14, -1, -1, -1, -1, - -1, -1, -1, -1, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 14, -1, -1, -1, -1, -1, -1, - -1, -1, 23, 24, 25, 26, 27, 28, 29, 30, - 31, -1, 14, -1, -1, -1, -1, 19, -1, 21, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; + 3, 43, 32, 18, 32, 9, 18, 18, 78, 18, + 18, 3, 18, 18, 32, 25, 18, 3, 3, 43, + 18, 3, 3, 22, 18, 18, 22, 18, 18, 3, + 18, 32, 32, 22, 14, 22, 14, 3, 3, 18, + 3, 25, 106, 18, 3, 3, 43, 18, 43, 25, + 3, 18, 25, 3, 104, 3, 101, 18, 3, 3, + 43, 25, 18, 18, 18, 14, 43, 18, 2, -1, + 3, 43, 32, 43, 43, 43, 18, 18, 18, 18, + 2, 43, 18, 18, 18, 14, 2, 18, 3, -1, + 18, 2, 4, 18, 14, 14, 18, -1, 55, 19, + 57, -1, 18, 55, 23, 57, 18, 18, 55, 3, + 57, 47, 55, 18, 57, 46, 44, 55, 55, 57, + 57, 46, 43, 52, 2, 55, 18, 57, 55, 55, + 51, 55, 55, 60, 60, 4, 60, 55, 55, 57, + 18, 55, 47, 60, 14, 55, 79, 55, 71, 18, + 60, 55, 60, 57, 55, 69, 57, 43, 55, 39, + 57, 14, 55, 43, 79, 51, 19, 60, 55, 2, + 14, 55, 55, 60, 55, 110, 60, 60, 18, 60, + 55, 2, 52, 55, 59, 18, 3, 59, 55, 55, + 55, 57, 59, 2, 55, 60, 3, 18, 18, 4, + 55, 2, 18, 95, 2, 45, 67, 2, 52, 18, + 65, 2, 18, 18, 2, 18, 55, 18, 11, 12, + 18, 2, 61, 18, 55, 2, 57, 18, 48, 18, + 18, 47, 55, 55, 57, 57, 55, 18, 18, 58, + 46, 18, 55, 46, 55, 58, -1, 55, 2, 57, + 14, 55, 63, 55, -1, 19, 45, 61, 55, 61, + 57, 55, 79, 55, 18, 57, 2, 61, 55, 55, + 55, -1, 79, 60, 60, 60, 55, 62, 64, 55, + 55, 60, 18, 62, 60, 60, 62, 62, 55, 55, + 77, 18, 55, 60, 60, 2, 55, 60, 55, 55, + 66, 60, 55, 60, 60, 68, 14, 60, -1, 55, + 77, 18, -1, 93, 60, 23, 72, 70, 77, 2, + 77, 48, 49, -1, -1, 14, -1, 35, 36, 5, + 19, 77, 21, -1, -1, 18, -1, -1, 14, 25, + 26, 27, 28, 29, 30, 31, -1, 23, -1, -1, + -1, -1, 14, 89, 43, -1, -1, -1, -1, 35, + 36, 23, 24, 25, 26, 27, 28, 29, 30, 31, + -1, -1, -1, -1, 14, -1, -1, -1, -1, -1, + -1, -1, 89, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 14, -1, -1, -1, -1, -1, -1, -1, + -1, 23, 24, 25, 26, 27, 28, 29, 30, 31, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, + -1, -1, -1, -1, -1, -1, -1, -1, 14, -1, + -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1 +}; QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h index b4f762d28b..aa8450f218 100644 --- a/src/qml/parser/qqmljsgrammar_p.h +++ b/src/qml/parser/qqmljsgrammar_p.h @@ -59,153 +59,154 @@ QT_BEGIN_NAMESPACE class QQmlJSGrammar { public: - enum VariousConstants { - EOF_SYMBOL = 0, - REDUCE_HERE = 106, - SHIFT_THERE = 105, - T_AND = 1, - T_AND_AND = 2, - T_AND_EQ = 3, - T_AS = 94, - T_AUTOMATIC_SEMICOLON = 62, - T_BREAK = 4, - T_CASE = 5, - T_CATCH = 6, - T_COLON = 7, - T_COMMA = 8, - T_COMMENT = 89, - T_COMPATIBILITY_SEMICOLON = 90, - T_CONST = 84, - T_CONTINUE = 9, - T_DEBUGGER = 86, - T_DEFAULT = 10, - T_DELETE = 11, - T_DIVIDE_ = 12, - T_DIVIDE_EQ = 13, - T_DO = 14, - T_DOT = 15, - T_ELSE = 16, - T_EQ = 17, - T_EQ_EQ = 18, - T_EQ_EQ_EQ = 19, - T_ERROR = 98, - T_FALSE = 83, - T_FEED_JS_EXPRESSION = 102, - T_FEED_JS_PROGRAM = 104, - T_FEED_JS_SOURCE_ELEMENT = 103, - T_FEED_JS_STATEMENT = 101, - T_FEED_UI_OBJECT_MEMBER = 100, - T_FEED_UI_PROGRAM = 99, - T_FINALLY = 20, - T_FOR = 21, - T_FUNCTION = 22, - T_GE = 23, - T_GET = 96, - T_GT = 24, - T_GT_GT = 25, - T_GT_GT_EQ = 26, - T_GT_GT_GT = 27, - T_GT_GT_GT_EQ = 28, - T_IDENTIFIER = 29, - T_IF = 30, - T_IMPORT = 92, - T_IN = 31, - T_INSTANCEOF = 32, - T_LBRACE = 33, - T_LBRACKET = 34, - T_LE = 35, - T_LET = 85, - T_LPAREN = 36, - T_LT = 37, - T_LT_LT = 38, - T_LT_LT_EQ = 39, - T_MINUS = 40, - T_MINUS_EQ = 41, - T_MINUS_MINUS = 42, - T_MULTILINE_STRING_LITERAL = 88, - T_NEW = 43, - T_NOT = 44, - T_NOT_EQ = 45, - T_NOT_EQ_EQ = 46, - T_NULL = 81, - T_NUMERIC_LITERAL = 47, - T_ON = 95, - T_OR = 48, - T_OR_EQ = 49, - T_OR_OR = 50, - T_PLUS = 51, - T_PLUS_EQ = 52, - T_PLUS_PLUS = 53, - T_PRAGMA = 93, - T_PROPERTY = 66, - T_PUBLIC = 91, - T_QUESTION = 54, - T_RBRACE = 55, - T_RBRACKET = 56, - T_READONLY = 68, - T_REMAINDER = 57, - T_REMAINDER_EQ = 58, - T_RESERVED_WORD = 87, - T_RETURN = 59, - T_RPAREN = 60, - T_SEMICOLON = 61, - T_SET = 97, - T_SIGNAL = 67, - T_STAR = 63, - T_STAR_EQ = 64, - T_STRING_LITERAL = 65, - T_SWITCH = 69, - T_THIS = 70, - T_THROW = 71, - T_TILDE = 72, - T_TRUE = 82, - T_TRY = 73, - T_TYPEOF = 74, - T_VAR = 75, - T_VOID = 76, - T_WHILE = 77, - T_WITH = 78, - T_XOR = 79, - T_XOR_EQ = 80, - - ACCEPT_STATE = 678, - RULE_COUNT = 363, - STATE_COUNT = 679, - TERMINAL_COUNT = 107, - NON_TERMINAL_COUNT = 111, - - GOTO_INDEX_OFFSET = 679, - GOTO_INFO_OFFSET = 3203, - GOTO_CHECK_OFFSET = 3203 - }; - - static const char *const spell []; - static const short lhs []; - static const short rhs []; - static const short goto_default []; - static const short action_default []; - static const short action_index []; - static const short action_info []; - static const short action_check []; - - static inline int nt_action (int state, int nt) - { - const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; - if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) - return goto_default [nt]; - - return action_info [GOTO_INFO_OFFSET + yyn]; - } - - static inline int t_action (int state, int token) - { - const int yyn = action_index [state] + token; - - if (yyn < 0 || action_check [yyn] != token) - return - action_default [state]; - - return action_info [yyn]; - } + enum VariousConstants { + EOF_SYMBOL = 0, + REDUCE_HERE = 107, + SHIFT_THERE = 106, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AS = 95, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 89, + T_COMPATIBILITY_SEMICOLON = 90, + T_CONST = 84, + T_CONTINUE = 9, + T_DEBUGGER = 86, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_ENUM = 91, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_ERROR = 99, + T_FALSE = 83, + T_FEED_JS_EXPRESSION = 103, + T_FEED_JS_PROGRAM = 105, + T_FEED_JS_SOURCE_ELEMENT = 104, + T_FEED_JS_STATEMENT = 102, + T_FEED_UI_OBJECT_MEMBER = 101, + T_FEED_UI_PROGRAM = 100, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GET = 97, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 93, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LET = 85, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 88, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 81, + T_NUMERIC_LITERAL = 47, + T_ON = 96, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PRAGMA = 94, + T_PROPERTY = 66, + T_PUBLIC = 92, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 68, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 87, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SET = 98, + T_SIGNAL = 67, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 69, + T_THIS = 70, + T_THROW = 71, + T_TILDE = 72, + T_TRUE = 82, + T_TRY = 73, + T_TYPEOF = 74, + T_VAR = 75, + T_VOID = 76, + T_WHILE = 77, + T_WITH = 78, + T_XOR = 79, + T_XOR_EQ = 80, + + ACCEPT_STATE = 687, + RULE_COUNT = 367, + STATE_COUNT = 688, + TERMINAL_COUNT = 108, + NON_TERMINAL_COUNT = 112, + + GOTO_INDEX_OFFSET = 688, + GOTO_INFO_OFFSET = 3217, + GOTO_CHECK_OFFSET = 3217 + }; + + static const char *const spell[]; + static const short lhs[]; + static const short rhs[]; + static const short goto_default[]; + static const short action_default[]; + static const short action_index[]; + static const short action_info[]; + static const short action_check[]; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } }; diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 8b789526a5..20daf545a9 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -181,7 +181,7 @@ static inline int classify4(const QChar *s, bool qmlMode) { else if (s[1].unicode() == 'n') { if (s[2].unicode() == 'u') { if (s[3].unicode() == 'm') { - return Lexer::T_ENUM; + return qmlMode ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD); } } } diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index af5597b625..11d8081713 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -99,7 +99,6 @@ public: T_CHAR = T_RESERVED_WORD, T_CLASS = T_RESERVED_WORD, T_DOUBLE = T_RESERVED_WORD, - T_ENUM = T_RESERVED_WORD, T_EXPORT = T_RESERVED_WORD, T_EXTENDS = T_RESERVED_WORD, T_FINAL = T_RESERVED_WORD, diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index 636b959097..3844fe4473 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -656,49 +656,71 @@ case 75: { sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); } break; -case 83: { +case 76: { + AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); + enumDeclaration->enumToken = loc(1); + enumDeclaration->rbraceToken = loc(5); + sym(1).Node = enumDeclaration; + break; +} + +case 77: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1)); + node->memberToken = loc(1); + sym(1).Node = node; + break; +} + +case 78: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); + node->memberToken = loc(3); + sym(1).Node = node; + break; +} + +case 86: { AST::ThisExpression *node = new (pool) AST::ThisExpression(); node->thisToken = loc(1); sym(1).Node = node; } break; -case 84: { +case 87: { AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 85: { +case 88: { AST::NullExpression *node = new (pool) AST::NullExpression(); node->nullToken = loc(1); sym(1).Node = node; } break; -case 86: { +case 89: { AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); node->trueToken = loc(1); sym(1).Node = node; } break; -case 87: { +case 90: { AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); node->falseToken = loc(1); sym(1).Node = node; } break; -case 88: { +case 91: { AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); node->literalToken = loc(1); sym(1).Node = node; } break; -case 89: -case 90: { +case 92: +case 93: { AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); node->literalToken = loc(1); sym(1).Node = node; } break; -case 91: { +case 94: { bool rx = lexer->scanRegExp(Lexer::NoPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -714,7 +736,7 @@ case 91: { sym(1).Node = node; } break; -case 92: { +case 95: { bool rx = lexer->scanRegExp(Lexer::EqualPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -730,28 +752,28 @@ case 92: { sym(1).Node = node; } break; -case 93: { +case 96: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); node->lbracketToken = loc(1); node->rbracketToken = loc(2); sym(1).Node = node; } break; -case 94: { +case 97: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 95: { +case 98: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 96: { +case 99: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), (AST::Elision *) 0); node->lbracketToken = loc(1); @@ -760,7 +782,7 @@ case 96: { sym(1).Node = node; } break; -case 97: { +case 100: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), sym(4).Elision->finish()); node->lbracketToken = loc(1); @@ -769,7 +791,7 @@ case 97: { sym(1).Node = node; } break; -case 98: { +case 101: { AST::ObjectLiteral *node = 0; if (sym(2).Node) node = new (pool) AST::ObjectLiteral( @@ -781,7 +803,7 @@ case 98: { sym(1).Node = node; } break; -case 99: { +case 102: { AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( sym(2).PropertyAssignmentList->finish ()); node->lbraceToken = loc(1); @@ -789,14 +811,14 @@ case 99: { sym(1).Node = node; } break; -case 100: { +case 103: { AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); node->lparenToken = loc(1); node->rparenToken = loc(3); sym(1).Node = node; } break; -case 101: { +case 104: { if (AST::ArrayMemberExpression *mem = AST::cast(sym(1).Expression)) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, QLatin1String("Ignored annotation"))); @@ -816,48 +838,48 @@ case 101: { } } break; -case 102: { +case 105: { sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); } break; -case 103: { +case 106: { sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); } break; -case 104: { +case 107: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, (AST::Elision *) 0, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 105: { +case 108: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), sym(4).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 106: { +case 109: { AST::Elision *node = new (pool) AST::Elision(); node->commaToken = loc(1); sym(1).Node = node; } break; -case 107: { +case 110: { AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); node->commaToken = loc(2); sym(1).Node = node; } break; -case 108: { +case 111: { AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( sym(1).PropertyName, sym(3).Expression); node->colonToken = loc(2); sym(1).Node = node; } break; -case 109: { +case 112: { AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( sym(2).PropertyName, sym(6).FunctionBody); node->getSetToken = loc(1); @@ -868,7 +890,7 @@ case 109: { sym(1).Node = node; } break; -case 110: { +case 113: { AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); node->getSetToken = loc(1); @@ -879,56 +901,56 @@ case 110: { sym(1).Node = node; } break; -case 111: { +case 114: { sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); } break; -case 112: { +case 115: { AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); node->commaToken = loc(2); sym(1).Node = node; } break; -case 113: { +case 116: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 114: { +case 117: { AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 115: { +case 118: { AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 116: { +case 119: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 153: { +case 157: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 154: { +case 158: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 155: { +case 159: { AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); node->newToken = loc(1); node->lparenToken = loc(3); @@ -936,384 +958,384 @@ case 155: { sym(1).Node = node; } break; -case 157: { +case 161: { AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); node->newToken = loc(1); sym(1).Node = node; } break; -case 158: { +case 162: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 159: { +case 163: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 160: { +case 164: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 161: { +case 165: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 162: { +case 166: { sym(1).Node = 0; } break; -case 163: { +case 167: { sym(1).Node = sym(1).ArgumentList->finish(); } break; -case 164: { +case 168: { sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); } break; -case 165: { +case 169: { AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 169: { +case 173: { AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); node->incrementToken = loc(2); sym(1).Node = node; } break; -case 170: { +case 174: { AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); node->decrementToken = loc(2); sym(1).Node = node; } break; -case 172: { +case 176: { AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); node->deleteToken = loc(1); sym(1).Node = node; } break; -case 173: { +case 177: { AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); node->voidToken = loc(1); sym(1).Node = node; } break; -case 174: { +case 178: { AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); node->typeofToken = loc(1); sym(1).Node = node; } break; -case 175: { +case 179: { AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); node->incrementToken = loc(1); sym(1).Node = node; } break; -case 176: { +case 180: { AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); node->decrementToken = loc(1); sym(1).Node = node; } break; -case 177: { +case 181: { AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); node->plusToken = loc(1); sym(1).Node = node; } break; -case 178: { +case 182: { AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); node->minusToken = loc(1); sym(1).Node = node; } break; -case 179: { +case 183: { AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); node->tildeToken = loc(1); sym(1).Node = node; } break; -case 180: { +case 184: { AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); node->notToken = loc(1); sym(1).Node = node; } break; -case 182: { +case 186: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mul, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 183: { +case 187: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Div, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 184: { +case 188: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mod, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 186: { +case 190: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 187: { +case 191: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 189: { +case 193: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 190: { +case 194: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 191: { +case 195: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 193: { +case 197: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 194: { +case 198: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 195: { +case 199: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 196: { +case 200: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 197: { +case 201: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 198: { +case 202: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 200: { +case 204: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 201: { +case 205: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 202: { +case 206: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 203: { +case 207: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 204: { +case 208: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 206: { +case 210: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 207: { +case 211: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 208: { +case 212: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 209: { +case 213: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 211: { +case 215: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 212: { +case 216: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 213: { +case 217: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 214: { +case 218: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 216: { +case 220: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 218: { +case 222: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 220: { +case 224: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 222: { +case 226: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 224: { +case 228: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 226: { +case 230: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 228: { +case 232: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 230: { +case 234: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 232: { +case 236: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 234: { +case 238: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 236: { +case 240: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1321,7 +1343,7 @@ case 236: { sym(1).Node = node; } break; -case 238: { +case 242: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1329,112 +1351,112 @@ case 238: { sym(1).Node = node; } break; -case 240: { +case 244: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 242: { +case 246: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 243: { +case 247: { sym(1).ival = QSOperator::Assign; } break; -case 244: { +case 248: { sym(1).ival = QSOperator::InplaceMul; } break; -case 245: { +case 249: { sym(1).ival = QSOperator::InplaceDiv; } break; -case 246: { +case 250: { sym(1).ival = QSOperator::InplaceMod; } break; -case 247: { +case 251: { sym(1).ival = QSOperator::InplaceAdd; } break; -case 248: { +case 252: { sym(1).ival = QSOperator::InplaceSub; } break; -case 249: { +case 253: { sym(1).ival = QSOperator::InplaceLeftShift; } break; -case 250: { +case 254: { sym(1).ival = QSOperator::InplaceRightShift; } break; -case 251: { +case 255: { sym(1).ival = QSOperator::InplaceURightShift; } break; -case 252: { +case 256: { sym(1).ival = QSOperator::InplaceAnd; } break; -case 253: { +case 257: { sym(1).ival = QSOperator::InplaceXor; } break; -case 254: { +case 258: { sym(1).ival = QSOperator::InplaceOr; } break; -case 256: { +case 260: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 257: { +case 261: { sym(1).Node = 0; } break; -case 260: { +case 264: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 261: { +case 265: { sym(1).Node = 0; } break; -case 278: { +case 282: { AST::Block *node = new (pool) AST::Block(sym(2).StatementList); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 279: { +case 283: { sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); } break; -case 280: { +case 284: { sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); } break; -case 281: { +case 285: { sym(1).Node = 0; } break; -case 282: { +case 286: { sym(1).Node = sym(1).StatementList->finish (); } break; -case 284: { +case 288: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; if (sym(1).ival == T_LET) s = AST::VariableDeclaration::BlockScope; @@ -1447,82 +1469,82 @@ case 284: { sym(1).Node = node; } break; -case 285: { +case 289: { sym(1).ival = T_LET; } break; -case 286: { +case 290: { sym(1).ival = T_CONST; } break; -case 287: { +case 291: { sym(1).ival = T_VAR; } break; -case 288: { +case 292: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 289: { +case 293: { AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( sym(1).VariableDeclarationList, sym(3).VariableDeclaration); node->commaToken = loc(2); sym(1).Node = node; } break; -case 290: { +case 294: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 291: { +case 295: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); } break; -case 292: { +case 296: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 293: { +case 297: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 294: { +case 298: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 295: { +case 299: { sym(1).Node = 0; } break; -case 297: { +case 301: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 298: { +case 302: { sym(1).Node = 0; } break; -case 300: { +case 304: { AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); node->semicolonToken = loc(1); sym(1).Node = node; } break; -case 302: { +case 306: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 303: { +case 307: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1531,7 +1553,7 @@ case 303: { sym(1).Node = node; } break; -case 304: { +case 308: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1539,7 +1561,7 @@ case 304: { sym(1).Node = node; } break; -case 307: { +case 311: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); node->doToken = loc(1); node->whileToken = loc(3); @@ -1549,7 +1571,7 @@ case 307: { sym(1).Node = node; } break; -case 308: { +case 312: { AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); node->whileToken = loc(1); node->lparenToken = loc(2); @@ -1557,7 +1579,7 @@ case 308: { sym(1).Node = node; } break; -case 309: { +case 313: { AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); node->forToken = loc(1); @@ -1568,7 +1590,7 @@ case 309: { sym(1).Node = node; } break; -case 310: { +case 314: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( sym(4).VariableDeclarationList->finish(s), sym(6).Expression, @@ -1582,7 +1604,7 @@ case 310: { sym(1).Node = node; } break; -case 311: { +case 315: { AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); @@ -1592,7 +1614,7 @@ case 311: { sym(1).Node = node; } break; -case 312: { +case 316: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); node->forToken = loc(1); @@ -1603,14 +1625,14 @@ case 312: { sym(1).Node = node; } break; -case 314: { +case 318: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); node->continueToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 316: { +case 320: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); node->continueToken = loc(1); node->identifierToken = loc(2); @@ -1618,14 +1640,14 @@ case 316: { sym(1).Node = node; } break; -case 318: { +case 322: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); node->breakToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 320: { +case 324: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); node->breakToken = loc(1); node->identifierToken = loc(2); @@ -1633,14 +1655,14 @@ case 320: { sym(1).Node = node; } break; -case 322: { +case 326: { AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); node->returnToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 323: { +case 327: { AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); node->withToken = loc(1); node->lparenToken = loc(2); @@ -1648,7 +1670,7 @@ case 323: { sym(1).Node = node; } break; -case 324: { +case 328: { AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); node->switchToken = loc(1); node->lparenToken = loc(2); @@ -1656,83 +1678,83 @@ case 324: { sym(1).Node = node; } break; -case 325: { +case 329: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 326: { +case 330: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(5); sym(1).Node = node; } break; -case 327: { +case 331: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); } break; -case 328: { +case 332: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); } break; -case 329: { +case 333: { sym(1).Node = 0; } break; -case 330: { +case 334: { sym(1).Node = sym(1).CaseClauses->finish (); } break; -case 331: { +case 335: { AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); node->caseToken = loc(1); node->colonToken = loc(3); sym(1).Node = node; } break; -case 332: { +case 336: { AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); node->defaultToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 333: { +case 337: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); node->identifierToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 335: { +case 339: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); node->throwToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 336: { +case 340: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); node->tryToken = loc(1); sym(1).Node = node; } break; -case 337: { +case 341: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 338: { +case 342: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 339: { +case 343: { AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); node->catchToken = loc(1); node->lparenToken = loc(2); @@ -1741,20 +1763,20 @@ case 339: { sym(1).Node = node; } break; -case 340: { +case 344: { AST::Finally *node = new (pool) AST::Finally(sym(2).Block); node->finallyToken = loc(1); sym(1).Node = node; } break; -case 342: { +case 346: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); node->debuggerToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 344: { +case 348: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); node->identifierToken = loc(2); @@ -1765,7 +1787,7 @@ case 344: { sym(1).Node = node; } break; -case 345: { +case 349: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); if (! stringRef(2).isNull()) @@ -1777,7 +1799,7 @@ case 345: { sym(1).Node = node; } break; -case 346: { +case 350: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); node->functionToken = loc(1); node->lparenToken = loc(2); @@ -1787,56 +1809,56 @@ case 346: { sym(1).Node = node; } break; -case 347: { +case 351: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 348: { +case 352: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); node->commaToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 349: { +case 353: { sym(1).Node = 0; } break; -case 350: { +case 354: { sym(1).Node = sym(1).FormalParameterList->finish (); } break; -case 351: { +case 355: { sym(1).Node = 0; } break; -case 353: { +case 357: { sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); } break; -case 355: { +case 359: { sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); } break; -case 356: { +case 360: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); } break; -case 357: { +case 361: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); } break; -case 358: { +case 362: { sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); } break; -case 359: { +case 363: { sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); } break; -case 360: { +case 364: { sym(1).Node = 0; } break; diff --git a/src/qml/parser/qqmljsparser_p.h b/src/qml/parser/qqmljsparser_p.h index f382cd7563..6273ed48b3 100644 --- a/src/qml/parser/qqmljsparser_p.h +++ b/src/qml/parser/qqmljsparser_p.h @@ -125,6 +125,7 @@ public: AST::UiArrayMemberList *UiArrayMemberList; AST::UiQualifiedId *UiQualifiedId; AST::UiQualifiedPragmaId *UiQualifiedPragmaId; + AST::UiEnumMemberList *UiEnumMemberList; }; public: @@ -246,9 +247,9 @@ protected: -#define J_SCRIPT_REGEXPLITERAL_RULE1 91 +#define J_SCRIPT_REGEXPLITERAL_RULE1 94 -#define J_SCRIPT_REGEXPLITERAL_RULE2 92 +#define J_SCRIPT_REGEXPLITERAL_RULE2 95 QT_QML_END_NAMESPACE diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ee5b38717b..d2a7970a84 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -295,7 +295,8 @@ public: QList *errors); bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor, - QQmlType** type_return, QList *errors); + QQmlType** type_return, QList *errors, + QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion); QUrl baseUrl; QString base; @@ -620,7 +621,8 @@ QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version) */ bool QQmlImports::resolveType(const QHashedStringRef &type, QQmlType** type_return, int *vmaj, int *vmin, - QQmlImportNamespace** ns_return, QList *errors) const + QQmlImportNamespace** ns_return, QList *errors, + QQmlImport::RecursionRestriction recursionRestriction) const { QQmlImportNamespace* ns = d->findQualifiedNamespace(type); if (ns) { @@ -629,7 +631,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return true; } if (type_return) { - if (d->resolveType(type,vmaj,vmin,type_return, errors)) { + if (d->resolveType(type,vmaj,vmin,type_return, errors, recursionRestriction)) { if (qmlImportTrace()) { #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \ << ')' << "::resolveType: " << type.toString() << " => " @@ -712,7 +714,8 @@ bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &t bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QString *base, bool *typeRecursionDetected) const + QQmlType** type_return, QString *base, bool *typeRecursionDetected, + QQmlImport::RecursionRestriction recursionRestriction) const { if (majversion >= 0 && minversion >= 0) { QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion); @@ -747,7 +750,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, if (resolveLocalUrl(*base, c.fileName) != componentUrl) continue; // failed attempt to access an internal type } - if (*base == componentUrl) { + if (recursionRestriction == QQmlImport::PreventRecursion && *base == componentUrl) { if (typeRecursionDetected) *typeRecursionDetected = true; continue; // no recursion @@ -790,7 +793,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, } if (exists) { - if (base && (*base == qmlUrl)) { // no recursion + if (recursionRestriction == QQmlImport::PreventRecursion && base && (*base == qmlUrl)) { // no recursion if (typeRecursionDetected) *typeRecursionDetected = true; } else { @@ -806,7 +809,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, } bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QList *errors) + QQmlType** type_return, QList *errors, + QQmlImport::RecursionRestriction recursionRestriction) { QQmlImportNamespace *s = 0; int dot = type.indexOf(Dot); @@ -835,7 +839,7 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, } QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1); if (s) { - if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return, &base, errors)) + if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return, &base, errors, recursionRestriction)) return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url @@ -858,13 +862,14 @@ QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, int *vmajor, int *vminor, QQmlType** type_return, - QString *base, QList *errors) + QString *base, QList *errors, + QQmlImport::RecursionRestriction recursionRestriction) { bool typeRecursionDetected = false; for (int i=0; iresolveType(typeLoader, type, vmajor, vminor, type_return, - base, &typeRecursionDetected)) { + base, &typeRecursionDetected, recursionRestriction)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; j::const_iterator it = nameSpace->imports.constBegin(); + it != nameSpace->imports.constEnd(); ++it) { + if ((*it)->uri == importUri) + return true; + } + } + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); Q_ASSERT(inserted); diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 7c691a468c..1a50d5c16a 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -70,6 +70,10 @@ class QQmlImportDatabase; class QQmlTypeLoader; class QQmlTypeLoaderQmldirContent; +namespace QQmlImport { + enum RecursionRestriction { PreventRecursion, AllowRecursion }; +} + struct QQmlImportInstance { QString uri; // e.g. QtQuick @@ -87,7 +91,8 @@ struct QQmlImportInstance bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, bool *typeRecursionDetected = 0) const; + QString *base = 0, bool *typeRecursionDetected = 0, + QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const; }; class QQmlImportNamespace @@ -102,7 +107,8 @@ public: bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, QList *errors = 0); + QString *base = 0, QList *errors = 0, + QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion); // Prefix when used as a qualified import. Otherwise empty. QHashedString prefix; @@ -128,7 +134,8 @@ public: QQmlType** type_return, int *version_major, int *version_minor, QQmlImportNamespace** ns_return, - QList *errors = 0) const; + QList *errors = 0, + QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const; bool resolveType(QQmlImportNamespace*, const QHashedStringRef& type, QQmlType** type_return, int *version_major, int *version_minor) const; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 0672618225..b75508e1e5 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -160,8 +160,9 @@ public: ~QQmlTypePrivate(); void init() const; - void initEnums() const; + void initEnums(const QQmlPropertyCache *cache = 0) const; void insertEnums(const QMetaObject *metaObject) const; + void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; QQmlType::RegistrationType regType; @@ -500,75 +501,17 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const return QQmlMetaType::qmlType(mo); } -int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const +QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const { + // similar logic to resolveCompositeBaseType Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->enumValue(engine, name, ok); -} - -int QQmlType::resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumIndex(engine, name, ok); -} - -int QQmlType::resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumIndex(engine, name, ok); -} - - -int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumValue(engine, index, name, ok); -} - -int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumValue(engine, index, name, ok); -} - -int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedName, const QByteArray &name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumValue(engine, scopedName, name, ok); -} - -int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedName, const QStringRef &name, bool *ok) const -{ - Q_ASSERT(isComposite()); - *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->scopedEnumValue(engine, scopedName, name, ok); + if (!engine) + return 0; + QQmlRefPointer td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer::Adopt); + if (td.isNull() || !td->isComplete()) + return 0; + QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); + return compilationUnit->rootPropertyCache(); } static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, @@ -737,7 +680,7 @@ void QQmlTypePrivate::init() const lock.unlock(); } -void QQmlTypePrivate::initEnums() const +void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const { if (isEnumSetup) return; @@ -746,6 +689,8 @@ void QQmlTypePrivate::initEnums() const QMutexLocker lock(metaTypeDataLock()); if (isEnumSetup) return; + if (cache) + insertEnumsFromPropertyCache(cache); if (baseMetaObject) // could be singleton type without metaobject insertEnums(baseMetaObject); @@ -784,6 +729,31 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const } } +void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const +{ + const QMetaObject *cppMetaObject = cache->firstCppMetaObject(); + + while (cache && cache->metaObject() != cppMetaObject) { + QStringHash *scoped = new QStringHash(); + + int count = cache->qmlEnumCount(); + for (int ii = 0; ii < count; ++ii) { + QQmlEnumData *enumData = cache->qmlEnum(ii); + + for (int jj = 0; jj < enumData->values.count(); ++jj) { + const QQmlEnumValue &value = enumData->values.at(jj); + enums.insert(value.namedValue, value.value); + scoped->insert(value.namedValue, value.value); + } + scopedEnums << scoped; + scopedEnumIndex.insert(enumData->name, scopedEnums.count()-1); + } + cache = cache->parent(); + } + insertEnums(cppMetaObject); +} + + QByteArray QQmlType::typeName() const { if (d->regType == SingletonType || d->regType == CompositeSingletonType) @@ -1033,11 +1003,11 @@ QUrl QQmlType::sourceUrl() const int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name.toString(), ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; + *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->enums.value(name); if (rv) @@ -1050,11 +1020,11 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name.toUtf16(), ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; + *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->enums.value(name); if (rv) @@ -1067,11 +1037,10 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name->toQString(), ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->enums.value(name); if (rv) @@ -1084,11 +1053,10 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumIndex(engine, name, ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -1101,11 +1069,10 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumIndex(engine, name, ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -1117,9 +1084,8 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bo int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const { + Q_UNUSED(engine) Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumValue(engine, index, name, ok); *ok = true; Q_ASSERT(index > -1 && index < d->scopedEnums.count()); @@ -1133,9 +1099,8 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::S int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const { + Q_UNUSED(engine) Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumValue(engine, index, name, ok); *ok = true; Q_ASSERT(index > -1 && index < d->scopedEnums.count()); @@ -1150,11 +1115,10 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QStrin int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumValue(engine, scopedEnumName, name, ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); if (rv) { @@ -1172,11 +1136,10 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scope int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeScopedEnumValue(engine, scopedEnumName, name, ok); + const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : 0; *ok = true; - d->initEnums(); + d->initEnums(cache); int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); if (rv) { diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 4dd28bbd36..ead4f130ec 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -135,6 +135,7 @@ public: struct QQmlMetaTypeData; class QHashedCStringRef; +class QQmlPropertyCache; class Q_QML_PRIVATE_EXPORT QQmlType { public: @@ -227,13 +228,7 @@ public: private: QQmlType *superType() const; QQmlType *resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - int resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - int resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const; - int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const; - int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedName, const QByteArray &name, bool *ok) const; - int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedName, const QStringRef &name, bool *ok) const; + QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; friend class QQmlTypePrivate; friend struct QQmlMetaTypeData; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index d18159841c..15b9fe8312 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -321,12 +321,13 @@ QQmlPropertyCache *QQmlPropertyCache::copy() } QQmlPropertyCache *QQmlPropertyCache::copyAndReserve(int propertyCount, int methodCount, - int signalCount) + int signalCount, int enumCount) { QQmlPropertyCache *rv = copy(propertyCount + methodCount + signalCount); rv->propertyIndexCache.reserve(propertyCount); rv->methodIndexCache.reserve(methodCount); rv->signalHandlerIndexCache.reserve(signalCount); + rv->enumCache.reserve(enumCount); rv->_metaObject = 0; return rv; @@ -421,6 +422,14 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0)); } +void QQmlPropertyCache::appendEnum(const QString &name, const QVector &values) +{ + QQmlEnumData data; + data.name = name; + data.values = values; + enumCache.append(data); +} + // Returns this property cache's metaObject, creating it if necessary. const QMetaObject *QQmlPropertyCache::createMetaObject() { @@ -1245,6 +1254,16 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) method.setReturnType(returnType); } + for (int ii = 0; ii < enumCache.count(); ++ii) { + const QQmlEnumData &enumData = enumCache.at(ii); + QMetaEnumBuilder enumeration = builder.addEnumerator(enumData.name.toUtf8()); + enumeration.setIsScoped(true); + for (int jj = 0; jj < enumData.values.count(); ++jj) { + const QQmlEnumValue &value = enumData.values.at(jj); + enumeration.addKey(value.namedValue.toUtf8(), value.value); + } + } + if (!_defaultPropertyName.isEmpty()) { QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0); if (dp && dp->coreIndex() >= propertyIndexCacheStart) { diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 64be1cb206..392768c1b1 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -349,6 +349,20 @@ private: bool notFullyResolved() const { return _flags.notFullyResolved; } }; +struct QQmlEnumValue +{ + QQmlEnumValue() : value(-1) {} + QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {} + QString namedValue; + int value; +}; + +struct QQmlEnumData +{ + QString name; + QVector values; +}; + class QQmlPropertyCacheMethodArguments; class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount, public QQmlCleanup { @@ -374,13 +388,14 @@ public: QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags()); QQmlPropertyCache *copyAndReserve(int propertyCount, - int methodCount, int signalCount); + int methodCount, int signalCount, int enumCount); void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex, int propType, int notifyIndex); void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex, const int *types = 0, const QList &names = QList()); void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, const QList &names = QList()); + void appendEnum(const QString &, const QVector &); const QMetaObject *metaObject() const; const QMetaObject *createMetaObject(); @@ -395,6 +410,7 @@ public: QQmlPropertyData *property(int) const; QQmlPropertyData *method(int) const; QQmlPropertyData *signal(int index) const; + QQmlEnumData *qmlEnum(int) const; int methodIndexToSignalIndex(int) const; QString defaultPropertyName() const; @@ -434,6 +450,7 @@ public: inline int methodOffset() const; inline int signalCount() const; inline int signalOffset() const; + inline int qmlEnumCount() const; static bool isDynamicMetaObject(const QMetaObject *); @@ -507,6 +524,7 @@ private: IndexCache signalHandlerIndexCache; StringCache stringCache; AllowedRevisionCache allowedRevisionCache; + QVector enumCache; bool _hasPropertyOverrides : 1; bool _ownMetaObject : 1; @@ -747,6 +765,14 @@ inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const return ensureResolved(rv); } +inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const +{ + if (index < 0 || index >= enumCache.count()) + return 0; + + return const_cast(&enumCache.at(index)); +} + inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const { if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) @@ -817,6 +843,11 @@ int QQmlPropertyCache::signalOffset() const return signalHandlerIndexCacheStart; } +int QQmlPropertyCache::qmlEnumCount() const +{ + return enumCache.count(); +} + bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const { if (_jsFactoryMethodIndex != -1) { diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index e4293596d8..151a7cd883 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2651,6 +2651,10 @@ void QQmlTypeData::resolveTypes() m_resolvedTypes.insert(unresolvedRef.key(), ref); } + + // ### this allows enums to work without explicit import or instantiation of the type + if (!m_implicitImportLoaded) + loadImplicitImport(); } QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index c8e2b92c29..35ef8ada57 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -140,7 +140,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, return result; } -QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) const +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, QQmlImport::RecursionRestriction recursionRestriction) const { Result result = query(m_namedImports, name); @@ -156,7 +156,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons QQmlImportNamespace *typeNamespace = 0; QList errors; QQmlType *t = 0; - bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors); + bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors, recursionRestriction); if (typeFound) { return Result(t); } diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 7cdcbe91b6..f7ba2a91b7 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -90,7 +90,7 @@ public: }; Result query(const QHashedStringRef &) const; Result query(const QHashedStringRef &, const void *importNamespace) const; - Result query(const QV4::String *) const; + Result query(const QV4::String *, QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const; Result query(const QV4::String *, const void *importNamespace) const; private: -- cgit v1.2.3 From 61887379b0c823953b61120532055fcbd881aadd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 12 Jul 2017 14:39:45 +0200 Subject: Add support for QEvent::LanguageChange Respond to the language change event by refreshing all binding expressions. For constant string translation bindings we must now create special QQmlBinding instances instead of a one-time property write meta-call upon instantiation. Those however are more lightweight than an entire JavaScript expression. In addition this provides a slot to explicitly trigger a re-evaluation of bindings, to make it a little easier to discover for the developer. [ChangeLog][QtQml][QQmlEngine] Added retranslate() slot and QEvent::LanguageChange support to refresh bindings when changing the language at run-time. Task-number: QTBUG-15602 Change-Id: Ide174648e1d8a5738acb88e15495018d0869d7bc Reviewed-by: Michael Brasser --- src/qml/compiler/qv4compileddata_p.h | 3 ++- src/qml/qml/qqmlbinding.cpp | 46 +++++++++++++++++++++++++++++++- src/qml/qml/qqmlbinding_p.h | 2 ++ src/qml/qml/qqmlengine.cpp | 24 +++++++++++++++++ src/qml/qml/qqmlengine.h | 4 +++ src/qml/qml/qqmljavascriptexpression.cpp | 7 ++++- src/qml/qml/qqmljavascriptexpression_p.h | 2 ++ src/qml/qml/qqmlobjectcreator.cpp | 21 ++++++++------- src/qml/qml/qqmlobjectcreator_p.h | 2 +- 9 files changed, 97 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 386f3ae922..b28f77e6e6 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -361,7 +361,8 @@ struct Q_QML_PRIVATE_EXPORT Binding static QString escapedString(const QString &string); - bool evaluatesToString() const { return type == Type_String || type == Type_Translation || type == Type_TranslationById; } + bool containsTranslations() const { return type == Type_Translation || type == Type_TranslationById; } + bool evaluatesToString() const { return type == Type_String || containsTranslations(); } QString valueAsString(const Unit *unit) const; QString valueAsScriptString(const Unit *unit) const; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 62288a5845..0ccb4e8e4e 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -195,7 +195,7 @@ class QQmlNonbindingBinding: public QQmlBinding { protected: void doUpdate(const DeleteWatcher &watcher, - QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) Q_DECL_OVERRIDE Q_DECL_FINAL + QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) Q_DECL_OVERRIDE { auto ep = QQmlEnginePrivate::get(scope.engine); ep->referenceScarceResources(); @@ -296,6 +296,50 @@ protected: } }; +class QQmlTranslationBinding : public GenericBinding { +public: + QQmlTranslationBinding(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Binding *binding) + { + setCompilationUnit(compilationUnit); + m_binding = binding; + setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column)); + } + + void doUpdate(const DeleteWatcher &watcher, + QQmlPropertyData::WriteFlags flags, QV4::Scope &) Q_DECL_OVERRIDE Q_DECL_FINAL + { + if (watcher.wasDeleted()) + return; + + if (!isAddedToObject() || hasError()) + return; + + const QString result = m_binding->valueAsString(m_compilationUnit->data); + + Q_ASSERT(targetObject()); + + QQmlPropertyData *pd; + QQmlPropertyData vpd; + getPropertyData(&pd, &vpd); + Q_ASSERT(pd); + doStore(result, pd, flags); + } + +private: + const QV4::CompiledData::Binding *m_binding; +}; + +QQmlBinding *QQmlBinding::createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) +{ + QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding); + + b->setNotifyOnValueChanged(true); + b->QQmlJavaScriptExpression::setContext(ctxt); + b->setScopeObject(obj); + + return b; +} + Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const QV4::Value &result, diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 0f2fb329f5..1c894148e4 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -77,6 +77,8 @@ public: const QString &url = QString(), quint16 lineNumber = 0); static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function, QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope); + static QQmlBinding *createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, + QObject *obj, QQmlContextData *ctxt); ~QQmlBinding(); void setTarget(const QQmlProperty &); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index c0cabe4dd0..38a2978712 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1327,6 +1327,27 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) d->outputWarningsToMsgLog = enabled; } +/*! + Refreshes all binding expressions that use strings marked for translation. + + Call this function after you have installed a new translator with + QCoreApplication::installTranslator, to ensure that your user-interface + shows up-to-date translations. + + \note Due to a limitation in the implementation, this function + refreshes all the engine's bindings, not only those that use strings + marked for translation. + This may be optimized in a future release. + + \since 5.10 +*/ +void QQmlEngine::retranslate() +{ + Q_D(QQmlEngine); + if (QQmlContextData *firstChildContext = QQmlContextData::get(d->rootContext)->childContexts) + firstChildContext->refreshExpressions(); +} + /*! Returns the QQmlContext for the \a object, or 0 if no context has been set. @@ -1447,6 +1468,9 @@ bool QQmlEngine::event(QEvent *e) Q_D(QQmlEngine); if (e->type() == QEvent::User) d->doDeleteInEngineThread(); + else if (e->type() == QEvent::LanguageChange) { + retranslate(); + } return QJSEngine::event(e); } diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 8cada954fe..2bf4c0497b 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -144,6 +144,10 @@ public: bool outputWarningsToStandardError() const; void setOutputWarningsToStandardError(bool); +public Q_SLOTS: + void retranslate(); + +public: static QQmlContext *contextForObject(const QObject *); static void setContextForObject(QObject *, QQmlContext *); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9587b3961f..214cfff6b2 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -461,7 +461,12 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext, return; m_qmlScope.set(qmlContext->engine(), *qmlContext); m_v4Function = f; - m_compilationUnit = m_v4Function->compilationUnit; + setCompilationUnit(m_v4Function->compilationUnit); +} + +void QQmlJavaScriptExpression::setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit) +{ + m_compilationUnit = compilationUnit; } void QQmlJavaScriptExpression::clearActiveGuards() diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 646cc5ab3d..b540959331 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -160,11 +160,13 @@ protected: } void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); + void setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit); private: friend class QQmlContextData; friend class QQmlPropertyCapture; friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); + friend class QQmlTranslationBinding; QQmlDelayedError *m_error; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b73cbaa563..9206395c8f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -801,17 +801,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con && !_valueTypeProperty) QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); - if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - - QV4::Scope scope(v4); - QV4::Scoped qmlContext(scope, currentQmlContext()); - + if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, - context, _scopeObject, runtimeFunction, qmlContext); + context, _scopeObject, runtimeFunction, currentQmlContext()); bs->takeExpression(expr); } else { @@ -827,7 +823,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con prop = _valueTypeProperty; subprop = property; } - qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, qmlContext); + if (binding->containsTranslations()) { + qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); + } else { + QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, currentQmlContext()); + } qmlBinding->setTarget(_bindingTarget, *prop, subprop); sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); @@ -1026,12 +1027,12 @@ void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::O context->setIdProperty(object->id, instance); } -QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() +QV4::QmlContext *QQmlObjectCreator::currentQmlContext() { if (!_qmlContext->isManaged()) _qmlContext->setM(QV4::QmlContext::create(v4->rootContext(), context, _scopeObject)); - return _qmlContext->d(); + return _qmlContext; } QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 982324be3c..45c14f0963 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -122,7 +122,7 @@ private: void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const; - QV4::Heap::QmlContext *currentQmlContext(); + QV4::QmlContext *currentQmlContext(); enum Phase { Startup, -- cgit v1.2.3 From 5d072dbb583050fe9338ca65e1c2b159c3f77692 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Fri, 14 Jul 2017 12:16:03 +0200 Subject: Software Adaptation: Fix nested clipping logic Task-number: QTBUG-61939 Change-Id: Ibb7f242241df0a7a418ab4f268487e72d5595622 Reviewed-by: Eirik Aavitsland --- .../adaptations/software/qsgsoftwarerenderablenodeupdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index 4937565aa9..666f1d0616 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -83,7 +83,7 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *) bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) { // Make sure to translate the clip rect into world coordinates - if (m_clipState.count() == 1) { + if (m_clipState.count() == 0 || m_clipState.top().isNull()) { m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()))); m_hasClip = true; } else { @@ -97,7 +97,7 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *) { m_clipState.pop(); - if (m_clipState.count() == 1) + if (m_clipState.count() == 0 || m_clipState.top().isNull()) m_hasClip = false; } -- cgit v1.2.3 From fd1612d9b640fa80c49d3de17f8707318df6640c Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 17 Jul 2017 11:26:19 +0200 Subject: QQuickWindowQmlImpl: declare attached properties in the header QQuickWindowQmlImpl is inherited by QQuickApplicationWindow in Qt Quick Controls 2, which must register revisions (qmlRegisterRevision) in base classes to make revisioned base class members available in AppWindow. The fact that QQuickWindowQmlImpl provides attached properties must be declared in the header so that qmlRegisterRevision in Qt Quick Controls 2 does not lose the Window-attached properties. Task-number: QTBUG-61935 Change-Id: I634c8fe980b06279610953d9ded2c27d8627d5ea Reviewed-by: Simon Hausmann --- src/quick/items/qquickwindowmodule.cpp | 2 -- src/quick/items/qquickwindowmodule_p.h | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index a5234d4f77..c1cc02568c 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -204,6 +204,4 @@ void QQuickWindowModule::defineModule() QT_END_NAMESPACE -QML_DECLARE_TYPEINFO(QQuickWindowQmlImpl, QML_HAS_ATTACHED_PROPERTIES) - #include "moc_qquickwindowmodule_p.cpp" diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h index 16130bc8a0..869d5b9a8e 100644 --- a/src/quick/items/qquickwindowmodule_p.h +++ b/src/quick/items/qquickwindowmodule_p.h @@ -54,10 +54,10 @@ #include #include #include +#include QT_BEGIN_NAMESPACE -class QQuickWindowAttached; class QQuickWindowQmlImplPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus @@ -105,4 +105,7 @@ public: QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickWindowQmlImpl) +QML_DECLARE_TYPEINFO(QQuickWindowQmlImpl, QML_HAS_ATTACHED_PROPERTIES) + #endif -- cgit v1.2.3 From 9dc9424d1873b1bc3b5ed056d4dda7f6c76e15ae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 12 Jul 2017 09:44:05 +0200 Subject: Fix linking if apps using QQmlPropertyMap when using version scripts Avoid the use of the QObject constructor that takes a QObjectPrivate in the inline QQmlPropertyMap constructor. Task-number: QTBUG-46433 Change-Id: I62e3c80e28334ce5a4c3d2249abfada45a8dccb3 Reviewed-by: Thiago Macieira --- src/qml/util/qqmlpropertymap.cpp | 12 +++--------- src/qml/util/qqmlpropertymap.h | 6 ++---- 2 files changed, 5 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp index 6e6554f2c3..b54e8d901a 100644 --- a/src/qml/util/qqmlpropertymap.cpp +++ b/src/qml/util/qqmlpropertymap.cpp @@ -186,9 +186,8 @@ int QQmlPropertyMapMetaObject::createProperty(const char *name, const char *valu Constructs a bindable map with parent object \a parent. */ QQmlPropertyMap::QQmlPropertyMap(QObject *parent) -: QObject(*allocatePrivate(), parent) +: QQmlPropertyMap(&staticMetaObject, parent) { - init(metaObject()); } /*! @@ -339,18 +338,13 @@ QVariant QQmlPropertyMap::updateValue(const QString &key, const QVariant &input) } /*! \internal */ -void QQmlPropertyMap::init(const QMetaObject *staticMetaObject) +QQmlPropertyMap::QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *parent) + : QObject(*(new QQmlPropertyMapPrivate), parent) { Q_D(QQmlPropertyMap); d->mo = new QQmlPropertyMapMetaObject(this, d, staticMetaObject); } -/*! \internal */ -QObjectPrivate *QQmlPropertyMap::allocatePrivate() -{ - return new QQmlPropertyMapPrivate; -} - /*! \fn void QQmlPropertyMap::valueChanged(const QString &key, const QVariant &value) This signal is emitted whenever one of the values in the map is changed. \a key diff --git a/src/qml/util/qqmlpropertymap.h b/src/qml/util/qqmlpropertymap.h index 01048f3662..8c5ecce48e 100644 --- a/src/qml/util/qqmlpropertymap.h +++ b/src/qml/util/qqmlpropertymap.h @@ -80,15 +80,13 @@ protected: template QQmlPropertyMap(DerivedType *derived, QObject *parentObj) - : QObject(*allocatePrivate(), parentObj) + : QQmlPropertyMap(&DerivedType::staticMetaObject, parentObj) { Q_UNUSED(derived) - init(&DerivedType::staticMetaObject); } private: - void init(const QMetaObject *staticMetaObject); - static QObjectPrivate *allocatePrivate(); + QQmlPropertyMap(const QMetaObject *staticMetaObject, QObject *parent); Q_DECLARE_PRIVATE(QQmlPropertyMap) Q_DISABLE_COPY(QQmlPropertyMap) -- cgit v1.2.3 From 65e52acf4027c6c6731942e8a67f8e30e26c9655 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 18 Jul 2017 16:17:23 +0200 Subject: Export QQuickWindowAttached fd1612d9 broke the qqc2 build: error: undefined reference to 'QQuickWindowAttached::staticMetaObject' Change-Id: Ia52f0e124fa81fecdee0ed006837a93636aa9622 Reviewed-by: Simon Hausmann --- src/quick/items/qquickwindowattached_p.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h index 3212508fd8..191f22137c 100644 --- a/src/quick/items/qquickwindowattached_p.h +++ b/src/quick/items/qquickwindowattached_p.h @@ -51,6 +51,7 @@ // We mean it. // +#include #include #include @@ -59,7 +60,7 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQuickWindow; -class Q_AUTOTEST_EXPORT QQuickWindowAttached : public QObject +class Q_QUICK_PRIVATE_EXPORT QQuickWindowAttached : public QObject { Q_OBJECT -- cgit v1.2.3 From 6b8695dbdbb381566c28d5d10d9de14e8eb99205 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 22 Jul 2017 13:13:39 -0700 Subject: Use __builtin_trap in GCC too It has had that for longer than Clang has existed. Change-Id: I84e45059a888497fb55ffffd14d3c03160312537 Reviewed-by: Simon Hausmann --- src/3rdparty/masm/wtf/Assertions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h index af65f5325c..491e434498 100644 --- a/src/3rdparty/masm/wtf/Assertions.h +++ b/src/3rdparty/masm/wtf/Assertions.h @@ -167,7 +167,7 @@ WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); Signals are ignored by the crash reporter on OS X so we must do better. */ #ifndef CRASH -#if COMPILER(CLANG) +#if COMPILER(CLANG) || COMPILER(GCC) #define CRASH() \ (WTFReportBacktrace(), \ WTFInvokeCrashHook(), \ -- cgit v1.2.3 From a51e7963e9a5b2a35c982e77f636ac4ca9a8cbc4 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 24 Jul 2017 13:53:58 +0200 Subject: Ensure same glyph cache is used for same font at different sizes This was fixed in Qt 5.9.0 by 9921b48c83490b450241d6c172f1375ab4efb6b1, but refactoring caused this change to be lost. This is just porting the fix to the refactored class hierarchy. Task-number: QTBUG-60696 Change-Id: I4f95cc7bdd49580ae623a03a7f9cc5242599b6c2 Reviewed-by: Robin Burchell --- src/quick/scenegraph/qsgcontext_p.h | 2 +- src/quick/scenegraph/qsgdefaultrendercontext.cpp | 25 ++++++++++++++++++++++-- src/quick/scenegraph/qsgdefaultrendercontext_p.h | 2 ++ 3 files changed, 26 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 6ff8f4a76e..84a2523f26 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -194,7 +194,7 @@ protected: QMutex m_mutex; QHash m_textures; QSet m_texturesToDelete; - QHash m_glyphCaches; + QHash m_glyphCaches; QSet m_fontEnginesToClean; }; diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 29600ef0ca..34bb554db6 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -271,6 +271,26 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri } } +QString QSGDefaultRenderContext::fontKey(const QRawFont &font) +{ + QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; + if (!fe->faceId().filename.isEmpty()) { + QByteArray keyName = fe->faceId().filename; + if (font.style() != QFont::StyleNormal) + keyName += QByteArray(" I"); + if (font.weight() != QFont::Normal) + keyName += ' ' + QByteArray::number(font.weight()); + keyName += QByteArray(" DF"); + return QString::fromUtf8(keyName); + } else { + return QString::fromLatin1("%1_%2_%3_%4") + .arg(font.familyName()) + .arg(font.styleName()) + .arg(font.weight()) + .arg(font.style()); + } +} + void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader) { shader->program()->bind(); @@ -293,10 +313,11 @@ QT_END_NAMESPACE QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) { - QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(font, 0); + QString key = fontKey(font); + QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0); if (!cache) { cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font); - m_glyphCaches.insert(font, cache); + m_glyphCaches.insert(key, cache); } return cache; diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index 0aed46b658..2537a06988 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -96,6 +96,8 @@ public: int maxTextureSize() const override { return m_maxTextureSize; } protected: + static QString fontKey(const QRawFont &font); + QOpenGLContext *m_gl; QSGDepthStencilBufferManager *m_depthStencilManager; int m_maxTextureSize; -- cgit v1.2.3 From a2db480db51748b381f18987790b5f2de8c0c911 Mon Sep 17 00:00:00 2001 From: Berthold Krevert Date: Sun, 16 Jul 2017 21:43:05 +0200 Subject: Check for GL_NV_path_rendering extension If resolving of glProgramPathFragmentInput fails, the nvpr renderer should not report that it is supported, so that the shape item can fallback to the geometry renderer. Task-number: QTBUG-61913 Change-Id: I0795b1dedc330432884d5214ee2492c757055a54 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquicknvprfunctions.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp index 0b92d4b4c0..409a59be7f 100644 --- a/src/imports/shapes/qquicknvprfunctions.cpp +++ b/src/imports/shapes/qquicknvprfunctions.cpp @@ -87,6 +87,8 @@ QSurfaceFormat QQuickNvprFunctions::format() return fmt; } +#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) + /*! \return true if GL_NV_path_rendering is supported with the current OpenGL context. @@ -114,6 +116,10 @@ bool QQuickNvprFunctions::isSupported() if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering"))) return false; + // Check that GL_NV_Path_rendering extension is at least API revision 1.3 + if (!PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV)) + return false; + // Do not check for DSA as the string may not be exposed on ES // drivers, yet the functions we need are resolvable. #if 0 @@ -199,8 +205,6 @@ bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderS return true; } -#define PROC(type, name) reinterpret_cast(ctx->getProcAddress(#name)) - bool QQuickNvprFunctionsPrivate::resolve() { QOpenGLContext *ctx = QOpenGLContext::currentContext(); -- cgit v1.2.3 From 9c515a6de24bc8d5709136cc099ceeae8e3e642c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 25 Jul 2017 13:14:21 +0200 Subject: Do not (dis)connectNotify on dynamically created model item objects These item objects are direct subclasses of QObject, and cannot override connectNotify/disconnectNotify. This prevents the creation of the backing QMetaObject during disconnect, which happens during destruction, which in turn will call back into the model that is being destroyed. Task-number: QTBUG-59704 Change-Id: I7f997e5d2fda242b38e67b9147224d72aa4508ba Reviewed-by: Simon Hausmann --- src/qml/qml/qqmljavascriptexpression.cpp | 4 ++-- src/qml/qml/qqmljavascriptexpression_p.h | 2 +- src/qml/qml/qqmlnotifier.cpp | 9 ++++++--- src/qml/qml/qqmlnotifier_p.h | 10 ++++++---- src/qml/types/qqmllistmodel.cpp | 10 +++------- 5 files changed, 18 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9d4e46e254..eec5d1ae57 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -283,7 +283,7 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify) { if (watcher->wasDeleted()) return; @@ -319,7 +319,7 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur Q_ASSERT(g->isConnected(o, n)); } else { g = QQmlJavaScriptExpressionGuard::New(expression, engine); - g->connect(o, n, engine); + g->connect(o, n, engine, doNotify); } if (duration == Permanently) diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 646cc5ab3d..eeed272793 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -204,7 +204,7 @@ public: static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); - void captureProperty(QObject *, int, int, Duration duration = OnlyOnce); + void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); QQmlEngine *engine; QQmlJavaScriptExpression *expression; diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index 538ca822ee..938e2b77e2 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -117,7 +117,7 @@ void QQmlNotifier::emitNotify(QQmlNotifierEndpoint *endpoint, void **a) \a sourceSignal MUST be in the signal index range (see QObjectPrivate::signalIndex()). This is different from QMetaMethod::methodIndex(). */ -void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine *engine) +void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine *engine, bool doNotify) { disconnect(); @@ -142,8 +142,11 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine QQmlPropertyPrivate::flushSignal(source, sourceSignal); QQmlData *ddata = QQmlData::get(source, true); ddata->addNotify(sourceSignal, this); - QObjectPrivate * const priv = QObjectPrivate::get(source); - priv->connectNotify(QMetaObjectPrivate::signal(source->metaObject(), sourceSignal)); + if (doNotify) { + needsConnectNotify = doNotify; + QObjectPrivate * const priv = QObjectPrivate::get(source); + priv->connectNotify(QMetaObjectPrivate::signal(source->metaObject(), sourceSignal)); + } } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index dad79e0e55..6e91369793 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -100,7 +100,7 @@ public: inline bool isConnected(QObject *source, int sourceSignal) const; inline bool isConnected(QQmlNotifier *) const; - void connect(QObject *source, int sourceSignal, QQmlEngine *engine); + void connect(QObject *source, int sourceSignal, QQmlEngine *engine, bool doNotify = true); inline void connect(QQmlNotifier *); inline void disconnect(); @@ -121,9 +121,10 @@ private: inline QQmlNotifier *senderAsNotifier() const; Callback callback:4; + int needsConnectNotify:1; // The index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - signed int sourceSignal:28; + signed int sourceSignal:27; }; QQmlNotifier::QQmlNotifier() @@ -155,7 +156,7 @@ void QQmlNotifier::notify() } QQmlNotifierEndpoint::QQmlNotifierEndpoint(Callback callback) -: next(0), prev(0), senderPtr(0), callback(callback), sourceSignal(-1) +: next(0), prev(0), senderPtr(0), callback(callback), needsConnectNotify(false), sourceSignal(-1) { } @@ -205,7 +206,8 @@ void QQmlNotifierEndpoint::disconnect() if (sourceSignal != -1) { QObject * const obj = senderAsObject(); QObjectPrivate * const priv = QObjectPrivate::get(obj); - priv->disconnectNotify(QMetaObjectPrivate::signal(obj->metaObject(), sourceSignal)); + if (needsConnectNotify) + priv->disconnectNotify(QMetaObjectPrivate::signal(obj->metaObject(), sourceSignal)); } if (isNotifying()) *((qintptr *)(senderPtr & ~0x1)) = 0; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 4d8f213284..9d0f1afb32 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1361,13 +1361,9 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); - if (ep && ep->propertyCapture) { - QObjectPrivate *op = QObjectPrivate::get(that->object()); - // Temporarily hide the dynamic meta-object, to prevent it from being created when the capture - // triggers a QObject::connectNotify() by calling obj->metaObject(). - QScopedValueRollback metaObjectBlocker(op->metaObject, 0); - ep->propertyCapture->captureProperty(that->object(), -1, role->index); - } + if (ep && ep->propertyCapture) + ep->propertyCapture->captureProperty(that->object(), -1, role->index, + QQmlPropertyCapture::OnlyOnce, false); } const int elementIndex = that->d()->m_elementIndex; -- cgit v1.2.3 From 264989606420f940cdab14f3f39362b9c82c3885 Mon Sep 17 00:00:00 2001 From: Wieland Hagen Date: Tue, 25 Jul 2017 12:46:38 +0200 Subject: Fix QQuickFramebufferObject::Renderer::invalidateFramebufferObject() invalidateFramebufferObject() sets the invalidatePending flag, which should then trigger the deletion of the old FBO and allocation via Renderer::createFramebufferObject(). This does only happen though, if the size has changed. Instead, always create a new FBO if invalidateFramebufferObject() has been called, regardless of whether the size changes or not. Change-Id: I849cb858afac89038343457c6362233c34956d58 Task-number: QTBUG-54434 Reviewed-by: Laszlo Agocs --- src/quick/items/qquickframebufferobject.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp index 3c00e956cc..042ee21aec 100644 --- a/src/quick/items/qquickframebufferobject.cpp +++ b/src/quick/items/qquickframebufferobject.cpp @@ -312,14 +312,12 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode n->devicePixelRatio = window()->effectiveDevicePixelRatio(); desiredFboSize *= n->devicePixelRatio; - if (n->fbo && (d->followsItemSize || n->invalidatePending)) { - if (n->fbo->size() != desiredFboSize) { - delete n->fbo; - n->fbo = 0; - delete n->msDisplayFbo; - n->msDisplayFbo = 0; - n->invalidatePending = false; - } + if (n->fbo && ((d->followsItemSize && n->fbo->size() != desiredFboSize) || n->invalidatePending)) { + delete n->fbo; + n->fbo = 0; + delete n->msDisplayFbo; + n->msDisplayFbo = 0; + n->invalidatePending = false; } if (!n->fbo) { -- cgit v1.2.3 From 39061af50cc3092289cdd71d17802139590ecb59 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 24 Jul 2017 15:00:37 +0200 Subject: Fix memory leak in QSGAtlasTexture::Manager::create() Parent the Atlas to the manager. Task-number: QTBUG-61754 Change-Id: Ida8b0622d1dbcaafa622f72a1d210969fa61d5bf Reviewed-by: Laszlo Agocs --- src/quick/scenegraph/util/qsgatlastexture.cpp | 7 ++++--- src/quick/scenegraph/util/qsgatlastexture_p.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 22f0b13f46..d5f836a525 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -116,7 +116,7 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) Texture *t = 0; if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) { if (!m_atlas) - m_atlas = new Atlas(m_atlas_size); + m_atlas = new Atlas(m_atlas_size, this); // t may be null for atlas allocation failure t = m_atlas->create(image); if (t && !hasAlphaChannel && t->hasAlphaChannel()) @@ -125,8 +125,9 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) return t; } -Atlas::Atlas(const QSize &size) - : m_allocator(size) +Atlas::Atlas(const QSize &size, QObject *parent) + : QObject(parent) + , m_allocator(size) , m_texture_id(0) , m_size(size) , m_atlas_transient_image_threshold(0) diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h index 3dee539547..0bb07e8e89 100644 --- a/src/quick/scenegraph/util/qsgatlastexture_p.h +++ b/src/quick/scenegraph/util/qsgatlastexture_p.h @@ -88,7 +88,7 @@ private: class Atlas : public QObject { public: - Atlas(const QSize &size); + Atlas(const QSize &size, QObject *parent); ~Atlas(); void invalidate(); -- cgit v1.2.3 From 5952cb93cc93702124aaa5dec938581b0312761a Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 24 Jul 2017 12:56:48 +0200 Subject: Fix compilation with namespace I am not sure why this hasn't failed in CI, but the function is declared inside the namespace, so the definition needs to go in there as well. Seems to be a merge error all the way back to 5.8. Change-Id: Iedd25d3e9e756c55cc302da90bab11535bdc1b01 Reviewed-by: Simon Hausmann --- src/quick/scenegraph/qsgdefaultrendercontext.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index e42979934e..b343f89cc0 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -289,9 +289,6 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context) return qobject_cast(context->property(QSG_RENDERCONTEXT_PROPERTY).value()); } -QT_END_NAMESPACE - - QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) { if (!m_distanceFieldCacheManager) @@ -306,4 +303,6 @@ QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(con return cache; } +QT_END_NAMESPACE + #include "moc_qsgdefaultrendercontext_p.cpp" -- cgit v1.2.3 From ed90226834f8d4ef76149e995e2aecff68e4df43 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 26 Jul 2017 12:13:03 +0200 Subject: Fix loading of QML plugins with old IID in static builds After commit 709f6370884b110def2e4665df8fa7bbf5fae734 we required the use of QQmlExtensionInterface_iid in qml plugins for static linkage. This mean that plugins using the "/1.0" variant would also continue to load, but those not would fail to load. This is annoying when porting apps from older Qt versions. To make the upgrade path easier, let's just support both IIDs. [ChangeLog][Qml] Fix loading of static qml plugins using the old plugin interface id Change-Id: I1c662b1fedad3f32b7dea1eddc32838d2eb9f3be Reviewed-by: J-P Nurmi --- src/qml/qml/qqmlextensioninterface.h | 3 +++ src/qml/qml/qqmlimport.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlextensioninterface.h b/src/qml/qml/qqmlextensioninterface.h index ef56d5e312..62b9b26569 100644 --- a/src/qml/qml/qqmlextensioninterface.h +++ b/src/qml/qml/qqmlextensioninterface.h @@ -66,7 +66,10 @@ public: Q_DECLARE_INTERFACE(QQmlTypesExtensionInterface, "org.qt-project.Qt.QQmlTypesExtensionInterface/1.0") +// NOTE: When changing this to a new version and deciding to add backup code to +// continue to support the previous version, make sure to support both of these iids. #define QQmlExtensionInterface_iid "org.qt-project.Qt.QQmlExtensionInterface/1.0" +#define QQmlExtensionInterface_iid_old "org.qt-project.Qt.QQmlExtensionInterface" Q_DECLARE_INTERFACE(QQmlExtensionInterface, QQmlExtensionInterface_iid) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ee5b38717b..ec748d1ca9 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -966,8 +966,8 @@ static QVector makePlugins() // the list the first time called to only contain QML plugins: const auto staticPlugins = QPluginLoader::staticPlugins(); for (const QStaticPlugin &plugin : staticPlugins) { - if (plugin.metaData().value(QLatin1String("IID")).toString() - == QLatin1String(QQmlExtensionInterface_iid)) { + const QString iid = plugin.metaData().value(QLatin1String("IID")).toString(); + if (iid == QLatin1String(QQmlExtensionInterface_iid) || iid == QLatin1String(QQmlExtensionInterface_iid_old)) { plugins.append(plugin); } } -- cgit v1.2.3 From bb199cc8ad4111808260f6255312b08c7d81dcbc Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 4 Mar 2016 11:34:59 +0100 Subject: QQuickTextInput: Don't move the cursor after internalInsert() has done so internalInsert() will set the cursor to the right position which accounts for any input mask set on the control as well. Therefore it will already be placed at the next correct position and should not be changed again after that. Task-number: QTBUG-40943 Change-Id: Ic80193ee94d2aa002b5a14a88df719a5a2cf51b1 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktextinput.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index a378359c95..43899ba457 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -3407,10 +3407,10 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) if (!event->commitString().isEmpty()) { internalInsert(event->commitString()); cursorPositionChanged = true; + } else { + m_cursor = qBound(0, c, m_text.length()); } - m_cursor = qBound(0, c, m_text.length()); - for (int i = 0; i < event->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = event->attributes().at(i); if (a.type == QInputMethodEvent::Selection) { -- cgit v1.2.3 From 08569a0981308f2d3c164d89aa4a00f321fd8e21 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 27 Jul 2017 11:47:28 +0200 Subject: QJSEngine: document limitation of dynamic QObject properties Dynamic QObject properties can not be accessed through C++ (via QJSValue) nor JavaScript. Task-number: QTBUG-38181 Change-Id: I78bb9898fef615a647234ae8df444e8855870258 Reviewed-by: Simon Hausmann --- src/qml/doc/snippets/code/src_script_qjsengine.cpp | 13 +++++++++++++ src/qml/jsapi/qjsengine.cpp | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/doc/snippets/code/src_script_qjsengine.cpp b/src/qml/doc/snippets/code/src_script_qjsengine.cpp index c9bd7dfcd9..0305574d34 100644 --- a/src/qml/doc/snippets/code/src_script_qjsengine.cpp +++ b/src/qml/doc/snippets/code/src_script_qjsengine.cpp @@ -91,3 +91,16 @@ myEngine.evaluate("button.checkable = true"); qDebug() << scriptButton.property("checkable").toBool(); scriptButton.property("show").call(); // call the show() slot //! [5] + + +//! [6] +QJSEngine engine; + +QObject *myQObject = new QObject(); +myQObject->setProperty("dynamicProperty", 3); + +QJSValue myScriptQObject = engine.newQObject(myQObject); +engine.globalObject().setProperty("myObject", myScriptQObject); + +qDebug() << engine.evaluate("myObject.dynamicProperty").toInt(); +//! [6] diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index e4c150057a..c678f8037a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -175,9 +175,14 @@ Q_DECLARE_METATYPE(QList) called from the script to create a new QObject instance with JavaScriptOwnership. + \snippet code/src_script_qjsengine.cpp 5 + \section2 Dynamic QObject Properties - \snippet code/src_script_qjsengine.cpp 5 + Dynamic QObject properties are not supported. For example, the following code + will not work: + + \snippet code/src_script_qjsengine.cpp 6 \section1 Extensions -- cgit v1.2.3 From 50bc2ac834502b2051dccaaec22da010a3ef5e70 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Wed, 26 Jul 2017 17:28:43 +0200 Subject: Add QDebugStateSaver to operator<<(QDebug, const QSGRootNode) CID 54558 (#1 of 1): Not restoring ostream format (STREAM_FORMAT_STATE) 4. end_of_path: Changing format state of stream d for category basefield without later restoring it. Coverity-Id: 54558 Change-Id: Iad6e6103684c57d3ab98e78e7c91e23731632913 Reviewed-by: Simon Hausmann --- src/quick/scenegraph/coreapi/qsgnode.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index e400928d4e..264b30b897 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -1582,6 +1582,7 @@ QDebug operator<<(QDebug d, const QSGRootNode *n) d << "RootNode(null)"; return d; } + QDebugStateSaver saver(d); d << "RootNode" << hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); #ifdef QSG_RUNTIME_DESCRIPTION d << QSGNodePrivate::description(n); -- cgit v1.2.3 From ce5436a9a07b5593529afc0a259e55d5c716e41c Mon Sep 17 00:00:00 2001 From: Berthold Krevert Date: Thu, 27 Jul 2017 20:09:01 +0200 Subject: Rename property to rendererType This follows the documentation. Change-Id: I74d8d2b45546717c6a6b252af9370c6670ef1b78 Reviewed-by: Laszlo Agocs --- src/imports/shapes/plugins.qmltypes | 2 +- src/imports/shapes/qquickshape_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes index c7c079991d..b8a7c532e0 100644 --- a/src/imports/shapes/plugins.qmltypes +++ b/src/imports/shapes/plugins.qmltypes @@ -31,7 +31,7 @@ Module { "Processing": 2 } } - Property { name: "renderer"; type: "RendererType"; isReadonly: true } + Property { name: "rendererType"; type: "RendererType"; isReadonly: true } Property { name: "asynchronous"; type: "bool" } Property { name: "vendorExtensionsEnabled"; type: "bool" } Property { name: "status"; type: "Status"; isReadonly: true } diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index db0b449c6c..3a630aacbc 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -223,7 +223,7 @@ private: class QQuickShape : public QQuickItem { Q_OBJECT - Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged) + Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) -- cgit v1.2.3 From 5e8347fdbab98d721f031634a9f6c8b217072d34 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jul 2017 11:54:44 +0200 Subject: Move shapes-related shaders to under the shapes plugin Do not leave them in quick/items' qrc. Change-Id: I12360a54caa368219a7a80645f92af66aa9de9ba Reviewed-by: Mitch Curtis --- src/imports/shapes/plugin.cpp | 1 + src/imports/shapes/qquickshapegenericrenderer.cpp | 4 ++-- src/imports/shapes/qquickshapenvprrenderer.cpp | 8 ++++---- src/imports/shapes/shaders/blit.frag | 9 +++++++++ src/imports/shapes/shaders/blit.vert | 12 ++++++++++++ src/imports/shapes/shaders/blit_core.frag | 13 +++++++++++++ src/imports/shapes/shaders/blit_core.vert | 14 ++++++++++++++ src/imports/shapes/shaders/lineargradient.frag | 9 +++++++++ src/imports/shapes/shaders/lineargradient.vert | 15 +++++++++++++++ src/imports/shapes/shaders/lineargradient_core.frag | 12 ++++++++++++ src/imports/shapes/shaders/lineargradient_core.vert | 17 +++++++++++++++++ src/imports/shapes/shapes.pro | 2 ++ src/imports/shapes/shapes.qrc | 12 ++++++++++++ src/quick/items/items.qrc | 4 ---- src/quick/items/shaders/lineargradient.frag | 9 --------- src/quick/items/shaders/lineargradient.vert | 15 --------------- src/quick/items/shaders/lineargradient_core.frag | 12 ------------ src/quick/items/shaders/lineargradient_core.vert | 17 ----------------- 18 files changed, 122 insertions(+), 63 deletions(-) create mode 100644 src/imports/shapes/shaders/blit.frag create mode 100644 src/imports/shapes/shaders/blit.vert create mode 100644 src/imports/shapes/shaders/blit_core.frag create mode 100644 src/imports/shapes/shaders/blit_core.vert create mode 100644 src/imports/shapes/shaders/lineargradient.frag create mode 100644 src/imports/shapes/shaders/lineargradient.vert create mode 100644 src/imports/shapes/shaders/lineargradient_core.frag create mode 100644 src/imports/shapes/shaders/lineargradient_core.vert create mode 100644 src/imports/shapes/shapes.qrc delete mode 100644 src/quick/items/shaders/lineargradient.frag delete mode 100644 src/quick/items/shaders/lineargradient.vert delete mode 100644 src/quick/items/shaders/lineargradient_core.frag delete mode 100644 src/quick/items/shaders/lineargradient_core.vert (limited to 'src') diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index ae0d02da93..1729fc88b4 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -47,6 +47,7 @@ static void initResources() #ifdef QT_STATIC Q_INIT_RESOURCE(qmake_QtQuick_Shapes); #endif + Q_INIT_RESOURCE(shapes); } QT_BEGIN_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index ca5f2a0e91..41bab83582 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -703,9 +703,9 @@ QSGMaterialType QQuickShapeLinearGradientShader::type; QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader() { setShaderSourceFile(QOpenGLShader::Vertex, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert")); + QStringLiteral(":/qt-project.org/shapes/shaders/lineargradient.vert")); setShaderSourceFile(QOpenGLShader::Fragment, - QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag")); + QStringLiteral(":/qt-project.org/shapes/shaders/lineargradient.frag")); } void QQuickShapeLinearGradientShader::initialize() diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index f3f8d807ec..57306a4d53 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -835,11 +835,11 @@ bool QQuickNvprBlitter::create() m_program = new QOpenGLShaderProgram; if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/shapes/shaders/blit_core.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/shapes/shaders/blit_core.frag")); } else { - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert")); - m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/shapes/shaders/blit.vert")); + m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/shapes/shaders/blit.frag")); } m_program->bindAttributeLocation("qt_Vertex", 0); m_program->bindAttributeLocation("qt_MultiTexCoord0", 1); diff --git a/src/imports/shapes/shaders/blit.frag b/src/imports/shapes/shaders/blit.frag new file mode 100644 index 0000000000..505f0db179 --- /dev/null +++ b/src/imports/shapes/shaders/blit.frag @@ -0,0 +1,9 @@ +varying highp vec2 qt_TexCoord0; + +uniform sampler2D source; +uniform lowp float qt_Opacity; + +void main() +{ + gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; +} diff --git a/src/imports/shapes/shaders/blit.vert b/src/imports/shapes/shaders/blit.vert new file mode 100644 index 0000000000..f8306bd945 --- /dev/null +++ b/src/imports/shapes/shaders/blit.vert @@ -0,0 +1,12 @@ +uniform highp mat4 qt_Matrix; + +attribute highp vec4 qt_Vertex; +attribute highp vec2 qt_MultiTexCoord0; + +varying highp vec2 qt_TexCoord0; + +void main() +{ + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; +} diff --git a/src/imports/shapes/shaders/blit_core.frag b/src/imports/shapes/shaders/blit_core.frag new file mode 100644 index 0000000000..7073808fba --- /dev/null +++ b/src/imports/shapes/shaders/blit_core.frag @@ -0,0 +1,13 @@ +#version 150 core + +in vec2 qt_TexCoord0; + +out vec4 fragColor; + +uniform sampler2D source; +uniform float qt_Opacity; + +void main() +{ + fragColor = texture(source, qt_TexCoord0) * qt_Opacity; +} diff --git a/src/imports/shapes/shaders/blit_core.vert b/src/imports/shapes/shaders/blit_core.vert new file mode 100644 index 0000000000..5246441da3 --- /dev/null +++ b/src/imports/shapes/shaders/blit_core.vert @@ -0,0 +1,14 @@ +#version 150 core + +in vec4 qt_Vertex; +in vec2 qt_MultiTexCoord0; + +out vec2 qt_TexCoord0; + +uniform mat4 qt_Matrix; + +void main() +{ + qt_TexCoord0 = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; +} diff --git a/src/imports/shapes/shaders/lineargradient.frag b/src/imports/shapes/shaders/lineargradient.frag new file mode 100644 index 0000000000..7f4a739109 --- /dev/null +++ b/src/imports/shapes/shaders/lineargradient.frag @@ -0,0 +1,9 @@ +uniform sampler2D gradTabTexture; +uniform highp float opacity; + +varying highp float gradTabIndex; + +void main() +{ + gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/imports/shapes/shaders/lineargradient.vert b/src/imports/shapes/shaders/lineargradient.vert new file mode 100644 index 0000000000..eb21b8886b --- /dev/null +++ b/src/imports/shapes/shaders/lineargradient.vert @@ -0,0 +1,15 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +varying float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shaders/lineargradient_core.frag b/src/imports/shapes/shaders/lineargradient_core.frag new file mode 100644 index 0000000000..5908acfa67 --- /dev/null +++ b/src/imports/shapes/shaders/lineargradient_core.frag @@ -0,0 +1,12 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +in float gradTabIndex; +out vec4 fragColor; + +void main() +{ + fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; +} diff --git a/src/imports/shapes/shaders/lineargradient_core.vert b/src/imports/shapes/shaders/lineargradient_core.vert new file mode 100644 index 0000000000..60b56f38e3 --- /dev/null +++ b/src/imports/shapes/shaders/lineargradient_core.vert @@ -0,0 +1,17 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 gradStart; +uniform vec2 gradEnd; + +out float gradTabIndex; + +void main() +{ + vec2 gradVec = gradEnd - gradStart; + gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro index 80e6a22142..4406474c3f 100644 --- a/src/imports/shapes/shapes.pro +++ b/src/imports/shapes/shapes.pro @@ -28,4 +28,6 @@ qtConfig(opengl) { qquickshapenvprrenderer.cpp } +RESOURCES += shapes.qrc + load(qml_plugin) diff --git a/src/imports/shapes/shapes.qrc b/src/imports/shapes/shapes.qrc new file mode 100644 index 0000000000..65ee2007f9 --- /dev/null +++ b/src/imports/shapes/shapes.qrc @@ -0,0 +1,12 @@ + + + shaders/blit.vert + shaders/blit.frag + shaders/blit_core.frag + shaders/blit_core.vert + shaders/lineargradient.vert + shaders/lineargradient.frag + shaders/lineargradient_core.vert + shaders/lineargradient_core.frag + + diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc index da9bf0c828..6aaf757c29 100644 --- a/src/quick/items/items.qrc +++ b/src/quick/items/items.qrc @@ -8,9 +8,5 @@ shaders/shadereffect_core.vert shaders/shadereffectfallback_core.frag shaders/shadereffectfallback_core.vert - shaders/lineargradient.vert - shaders/lineargradient.frag - shaders/lineargradient_core.vert - shaders/lineargradient_core.frag diff --git a/src/quick/items/shaders/lineargradient.frag b/src/quick/items/shaders/lineargradient.frag deleted file mode 100644 index 7f4a739109..0000000000 --- a/src/quick/items/shaders/lineargradient.frag +++ /dev/null @@ -1,9 +0,0 @@ -uniform sampler2D gradTabTexture; -uniform highp float opacity; - -varying highp float gradTabIndex; - -void main() -{ - gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; -} diff --git a/src/quick/items/shaders/lineargradient.vert b/src/quick/items/shaders/lineargradient.vert deleted file mode 100644 index eb21b8886b..0000000000 --- a/src/quick/items/shaders/lineargradient.vert +++ /dev/null @@ -1,15 +0,0 @@ -attribute vec4 vertexCoord; -attribute vec4 vertexColor; - -uniform mat4 matrix; -uniform vec2 gradStart; -uniform vec2 gradEnd; - -varying float gradTabIndex; - -void main() -{ - vec2 gradVec = gradEnd - gradStart; - gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); - gl_Position = matrix * vertexCoord; -} diff --git a/src/quick/items/shaders/lineargradient_core.frag b/src/quick/items/shaders/lineargradient_core.frag deleted file mode 100644 index 5908acfa67..0000000000 --- a/src/quick/items/shaders/lineargradient_core.frag +++ /dev/null @@ -1,12 +0,0 @@ -#version 150 core - -uniform sampler2D gradTabTexture; -uniform float opacity; - -in float gradTabIndex; -out vec4 fragColor; - -void main() -{ - fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity; -} diff --git a/src/quick/items/shaders/lineargradient_core.vert b/src/quick/items/shaders/lineargradient_core.vert deleted file mode 100644 index 60b56f38e3..0000000000 --- a/src/quick/items/shaders/lineargradient_core.vert +++ /dev/null @@ -1,17 +0,0 @@ -#version 150 core - -in vec4 vertexCoord; -in vec4 vertexColor; - -uniform mat4 matrix; -uniform vec2 gradStart; -uniform vec2 gradEnd; - -out float gradTabIndex; - -void main() -{ - vec2 gradVec = gradEnd - gradStart; - gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y); - gl_Position = matrix * vertexCoord; -} -- cgit v1.2.3 From e26ac7911613cb9c572b81820e427fbd0a5874f6 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 31 Jul 2017 14:59:04 +0200 Subject: shapes: Rip out more JS API leftovers None of the removed code is ever hit in practice since the public JS API has been removed some time ago. Let's follow it up with removing the internal details since such an API is not going to come back in the near future. Change-Id: I721ab296a7a2acb3a5f61ce705da7aa66d3ad765 Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape.cpp | 134 +++++---------------- src/imports/shapes/qquickshape_p_p.h | 103 +++------------- src/imports/shapes/qquickshapegenericrenderer.cpp | 7 -- src/imports/shapes/qquickshapegenericrenderer_p.h | 1 - src/imports/shapes/qquickshapenvprrenderer.cpp | 84 ------------- src/imports/shapes/qquickshapenvprrenderer_p.h | 2 - src/imports/shapes/qquickshapesoftwarerenderer.cpp | 8 -- src/imports/shapes/qquickshapesoftwarerenderer_p.h | 1 - 8 files changed, 49 insertions(+), 291 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 3d90ca0c1f..b8a138507a 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -77,50 +77,6 @@ QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams() dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space } -QPainterPath QQuickShapePathCommands::toPainterPath() const -{ - QPainterPath p; - int coordIdx = 0; - for (int i = 0; i < cmd.count(); ++i) { - switch (cmd[i]) { - case QQuickShapePathCommands::MoveTo: - p.moveTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickShapePathCommands::LineTo: - p.lineTo(coords[coordIdx], coords[coordIdx + 1]); - coordIdx += 2; - break; - case QQuickShapePathCommands::QuadTo: - p.quadTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3]); - coordIdx += 4; - break; - case QQuickShapePathCommands::CubicTo: - p.cubicTo(coords[coordIdx], coords[coordIdx + 1], - coords[coordIdx + 2], coords[coordIdx + 3], - coords[coordIdx + 4], coords[coordIdx + 5]); - coordIdx += 6; - break; - case QQuickShapePathCommands::ArcTo: - // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser - QQuickSvgParser::pathArc(p, - coords[coordIdx], coords[coordIdx + 1], // radius - coords[coordIdx + 2], // xAxisRotation - !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc - !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag - coords[coordIdx + 3], coords[coordIdx + 4], // end - p.currentPosition().x(), p.currentPosition().y()); - coordIdx += 7; - break; - default: - qWarning("Unknown JS path command: %d", cmd[i]); - break; - } - } - return p; -} - /*! \qmltype ShapePath \instantiates QQuickShapePath @@ -815,7 +771,7 @@ static void vpe_append(QQmlListProperty *property, QObject *obj) QQuickShapePrivate *d = QQuickShapePrivate::get(item); QQuickShapePath *path = qobject_cast(obj); if (path) - d->qmlData.sp.append(path); + d->sp.append(path); QQuickItemPrivate::data_append(property, obj); @@ -830,10 +786,10 @@ static void vpe_clear(QQmlListProperty *property) QQuickShape *item = static_cast(property->object); QQuickShapePrivate *d = QQuickShapePrivate::get(item); - for (QQuickShapePath *p : d->qmlData.sp) + for (QQuickShapePath *p : d->sp) QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged())); - d->qmlData.sp.clear(); + d->sp.clear(); QQuickItemPrivate::data_clear(property); @@ -872,7 +828,7 @@ void QQuickShape::componentComplete() QQuickItem::componentComplete(); - for (QQuickShapePath *p : d->qmlData.sp) + for (QQuickShapePath *p : d->sp) connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged())); d->_q_shapePathChanged(); @@ -1010,65 +966,37 @@ void QQuickShapePrivate::sync() renderer->setAsyncCallback(q_asyncShapeReady, this); } - if (!jsData.isValid()) { - // Standard route: The path and stroke/fill parameters are provided via - // QML elements. - const int count = qmlData.sp.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - QQuickShapePath *p = qmlData.sp[i]; - int &dirty(QQuickShapePathPrivate::get(p)->dirty); - - if (dirty & QQuickShapePathPrivate::DirtyPath) - renderer->setPath(i, p); - if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) - renderer->setStrokeColor(i, p->strokeColor()); - if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) - renderer->setStrokeWidth(i, p->strokeWidth()); - if (dirty & QQuickShapePathPrivate::DirtyFillColor) - renderer->setFillColor(i, p->fillColor()); - if (dirty & QQuickShapePathPrivate::DirtyFillRule) - renderer->setFillRule(i, p->fillRule()); - if (dirty & QQuickShapePathPrivate::DirtyStyle) { - renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); - renderer->setCapStyle(i, p->capStyle()); - } - if (dirty & QQuickShapePathPrivate::DirtyDash) - renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); - if (dirty & QQuickShapePathPrivate::DirtyFillGradient) - renderer->setFillGradient(i, p->fillGradient()); - - dirty = 0; - } - - renderer->endSync(useAsync); - } else { - - // ### there is no public API to reach this code path atm - Q_UNREACHABLE(); - - // Path and stroke/fill params provided from JavaScript. This avoids - // QObjects at the expense of not supporting changes afterwards. - const int count = jsData.paths.count(); - renderer->beginSync(count); - - for (int i = 0; i < count; ++i) { - renderer->setJSPath(i, jsData.paths[i]); - const QQuickShapeStrokeFillParams sfp(jsData.sfp[i]); - renderer->setStrokeColor(i, sfp.strokeColor); - renderer->setStrokeWidth(i, sfp.strokeWidth); - renderer->setFillColor(i, sfp.fillColor); - renderer->setFillRule(i, sfp.fillRule); - renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit); - renderer->setCapStyle(i, sfp.capStyle); - renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern); - renderer->setFillGradient(i, sfp.fillGradient); + const int count = sp.count(); + renderer->beginSync(count); + + for (int i = 0; i < count; ++i) { + QQuickShapePath *p = sp[i]; + int &dirty(QQuickShapePathPrivate::get(p)->dirty); + + if (dirty & QQuickShapePathPrivate::DirtyPath) + renderer->setPath(i, p); + if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) + renderer->setStrokeColor(i, p->strokeColor()); + if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) + renderer->setStrokeWidth(i, p->strokeWidth()); + if (dirty & QQuickShapePathPrivate::DirtyFillColor) + renderer->setFillColor(i, p->fillColor()); + if (dirty & QQuickShapePathPrivate::DirtyFillRule) + renderer->setFillRule(i, p->fillRule()); + if (dirty & QQuickShapePathPrivate::DirtyStyle) { + renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit()); + renderer->setCapStyle(i, p->capStyle()); } + if (dirty & QQuickShapePathPrivate::DirtyDash) + renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern()); + if (dirty & QQuickShapePathPrivate::DirtyFillGradient) + renderer->setFillGradient(i, p->fillGradient()); - renderer->endSync(useAsync); + dirty = 0; } + renderer->endSync(useAsync); + if (!useAsync) setStatus(QQuickShape::Ready); } diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index dc62994af2..6ca752de56 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -62,39 +62,6 @@ QT_BEGIN_NAMESPACE class QSGPlainTexture; -struct QQuickShapePathCommands -{ - enum Command { - MoveTo, - LineTo, - QuadTo, - CubicTo, - ArcTo - }; - - QVector cmd; - QVector coords; - - QPainterPath toPainterPath() const; -}; - -struct QQuickShapeStrokeFillParams -{ - QQuickShapeStrokeFillParams(); - - QColor strokeColor; - qreal strokeWidth; - QColor fillColor; - QQuickShapePath::FillRule fillRule; - QQuickShapePath::JoinStyle joinStyle; - int miterLimit; - QQuickShapePath::CapStyle capStyle; - QQuickShapePath::StrokeStyle strokeStyle; - qreal dashOffset; - QVector dashPattern; - QQuickShapeGradient *fillGradient; -}; - class QQuickAbstractPathRenderer { public: @@ -110,11 +77,7 @@ public: virtual void endSync(bool async) = 0; virtual void setAsyncCallback(void (*)(void *), void *) { } virtual Flags flags() const { return 0; } - // - QML API virtual void setPath(int index, const QQuickPath *path) = 0; - // - JS API - virtual void setJSPath(int index, const QQuickShapePathCommands &path) = 0; - // - stroke/fill parameters virtual void setStrokeColor(int index, const QColor &color) = 0; virtual void setStrokeWidth(int index, qreal w) = 0; virtual void setFillColor(int index, const QColor &color) = 0; @@ -131,6 +94,23 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags) +struct QQuickShapeStrokeFillParams +{ + QQuickShapeStrokeFillParams(); + + QColor strokeColor; + qreal strokeWidth; + QColor fillColor; + QQuickShapePath::FillRule fillRule; + QQuickShapePath::JoinStyle joinStyle; + int miterLimit; + QQuickShapePath::CapStyle capStyle; + QQuickShapePath::StrokeStyle strokeStyle; + qreal dashOffset; + QVector dashPattern; + QQuickShapeGradient *fillGradient; +}; + class QQuickShapePathPrivate : public QQuickPathPrivate { Q_DECLARE_PUBLIC(QQuickShapePath) @@ -182,57 +162,10 @@ public: bool async; QQuickShape::Status status; QQuickAbstractPathRenderer *renderer; - - struct { - QVector sp; - } qmlData; - - struct { - bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); } - QVector paths; - QVector sfp; - } jsData; - + QVector sp; bool enableVendorExts; }; -class QQuickShapePathObject : public QObject -{ - Q_OBJECT - -public: - QQuickShapePathObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickShapePath path; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - -class QQuickShapeStrokeFillParamsObject : public QObject -{ - Q_OBJECT - -public: - QQuickShapeStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { } - - void setV4Engine(QV4::ExecutionEngine *engine); - QV4::ReturnedValue v4value() const { return m_v4value.value(); } - - QQuickShapeStrokeFillParams sfp; - QV4::PersistentValue v4fillGradient; - - void clear(); - -private: - QV4::PersistentValue m_v4value; -}; - #if QT_CONFIG(opengl) class QQuickShapeGradientCache : public QOpenGLSharedResource diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 41bab83582..398106af3d 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -178,13 +178,6 @@ void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path) d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; } -void QQuickShapeGenericRenderer::setJSPath(int index, const QQuickShapePathCommands &path) -{ - ShapePathData &d(m_sp[index]); - d.path = path.toPainterPath(); - d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom; -} - void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color) { ShapePathData &d(m_sp[index]); diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index 1f36e3decf..dadeba3467 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -88,7 +88,6 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickShapePathCommands &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index 57306a4d53..4f49bb5256 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -62,14 +62,6 @@ void QQuickShapeNvprRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } -void QQuickShapeNvprRenderer::setJSPath(int index, const QQuickShapePathCommands &path) -{ - ShapePathGuiData &d(m_sp[index]); - convertJSPath(path, &d); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - void QQuickShapeNvprRenderer::setStrokeColor(int index, const QColor &color) { ShapePathGuiData &d(m_sp[index]); @@ -296,82 +288,6 @@ void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiDa d->path.cmd.append(GL_CLOSE_PATH_NV); } -void QQuickShapeNvprRenderer::convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d) -{ - d->path = NvprPath(); - if (path.cmd.isEmpty()) - return; - - QPointF startPos(0, 0); - QPointF pos(startPos); - int coordIdx = 0; - - for (QQuickShapePathCommands::Command cmd : path.cmd) { - switch (cmd) { - case QQuickShapePathCommands::MoveTo: - d->path.cmd.append(GL_MOVE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - startPos = pos; - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickShapePathCommands::LineTo: - d->path.cmd.append(GL_LINE_TO_NV); - pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 2; - break; - case QQuickShapePathCommands::QuadTo: - d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 4; - break; - case QQuickShapePathCommands::CubicTo: - d->path.cmd.append(GL_CUBIC_CURVE_TO_NV); - d->path.coord.append(path.coords[coordIdx]); - d->path.coord.append(path.coords[coordIdx + 1]); - d->path.coord.append(path.coords[coordIdx + 2]); - d->path.coord.append(path.coords[coordIdx + 3]); - pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 6; - break; - case QQuickShapePathCommands::ArcTo: - { - const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]); - const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]); - GLenum cmd; - if (useLargeArc) - cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV; - else - cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV; - d->path.cmd.append(cmd); - d->path.coord.append(path.coords[coordIdx]); // rx - d->path.coord.append(path.coords[coordIdx + 1]); // ry - d->path.coord.append(path.coords[coordIdx + 2]); // xrot - pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]); - d->path.coord.append(pos.x()); - d->path.coord.append(pos.y()); - coordIdx += 7; - } - break; - default: - qWarning("Unknown JS path command: %d", cmd); - break; - } - } - - if (pos == startPos) - d->path.cmd.append(GL_CLOSE_PATH_NV); -} - static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity) { const float o = c.alphaF() * globalOpacity; diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index ec7ba498f9..7eb2924ab7 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -81,7 +81,6 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickShapePathCommands &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; @@ -122,7 +121,6 @@ private: }; void convertPath(const QQuickPath *path, ShapePathGuiData *d); - void convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d); QQuickShapeNvprRenderNode *m_node = nullptr; int m_accDirty = 0; diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp index b3373106af..4e6e758697 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -58,14 +58,6 @@ void QQuickShapeSoftwareRenderer::setPath(int index, const QQuickPath *path) m_accDirty |= DirtyPath; } -void QQuickShapeSoftwareRenderer::setJSPath(int index, const QQuickShapePathCommands &path) -{ - ShapePathGuiData &d(m_sp[index]); - d.path = path.toPainterPath(); - d.dirty |= DirtyPath; - m_accDirty |= DirtyPath; -} - void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color) { ShapePathGuiData &d(m_sp[index]); diff --git a/src/imports/shapes/qquickshapesoftwarerenderer_p.h b/src/imports/shapes/qquickshapesoftwarerenderer_p.h index 53982ce347..0abc2e37b0 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer_p.h +++ b/src/imports/shapes/qquickshapesoftwarerenderer_p.h @@ -73,7 +73,6 @@ public: void beginSync(int totalCount) override; void setPath(int index, const QQuickPath *path) override; - void setJSPath(int index, const QQuickShapePathCommands &path) override; void setStrokeColor(int index, const QColor &color) override; void setStrokeWidth(int index, qreal w) override; void setFillColor(int index, const QColor &color) override; -- cgit v1.2.3 From 2a79c2bf4d9525a31e180d87d3fa249e98f5683e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 10:40:55 +0200 Subject: Turn QQmlType into a refcounted value type Task-number: QTBUG-61536 Change-Id: If906af2bf3afd09c23a612aaac417a751b06eba4 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlmetatype.cpp | 195 +++++++++++++++++++++++++++++-------------- src/qml/qml/qqmlmetatype_p.h | 14 +++- 2 files changed, 142 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index bb9b69c479..1959146388 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -155,6 +155,7 @@ QQmlMetaTypeData::~QQmlMetaTypeData() class QQmlTypePrivate { + Q_DISABLE_COPY(QQmlTypePrivate) public: QQmlTypePrivate(QQmlType::RegistrationType type); ~QQmlTypePrivate(); @@ -163,6 +164,7 @@ public: void initEnums() const; void insertEnums(const QMetaObject *metaObject) const; + QAtomicInt refCount; QQmlType::RegistrationType regType; struct QQmlCppTypeData @@ -278,7 +280,7 @@ QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const QHash QQmlTypePrivate::attachedPropertyIds; QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) -: regType(type), iid(0), typeId(0), listId(0), revision(0), +: refCount(1), regType(type), iid(0), typeId(0), listId(0), revision(0), containsRevisionedAttributes(false), superType(0), baseMetaObject(0), index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false) { @@ -332,7 +334,7 @@ QQmlTypePrivate::~QQmlTypePrivate() } QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) -: d(new QQmlTypePrivate(InterfaceType)) + : d(new QQmlTypePrivate(InterfaceType)) { d->iid = interface.iid; d->typeId = interface.typeId; @@ -344,7 +346,7 @@ QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) -: d(new QQmlTypePrivate(SingletonType)) + : d(new QQmlTypePrivate(SingletonType)) { d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -372,7 +374,7 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type) - : d(new QQmlTypePrivate(CompositeSingletonType)) + : d(new QQmlTypePrivate(CompositeSingletonType)) { d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -388,7 +390,7 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterType &type) -: d(new QQmlTypePrivate(CppType)) + : d(new QQmlTypePrivate(CppType)) { d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -425,7 +427,7 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) -: d(new QQmlTypePrivate(CompositeType)) + : d(new QQmlTypePrivate(CompositeType)) { d->index = index; d->elementName = elementName; @@ -437,41 +439,78 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->extraData.fd->url = type.url; } +QQmlType::QQmlType() + : d(0) +{ +} + +QQmlType::QQmlType(const QQmlType &other) + : d(other.d) +{ + if (d) + d->refCount.ref(); +} + +QQmlType &QQmlType::operator =(const QQmlType &other) +{ + if (d != other.d) { + if (d && !d->refCount.deref()) + delete d; + d = other.d; + if (d) + d->refCount.ref(); + } + return *this; +} + QQmlType::~QQmlType() { - delete d; + if (d && !d->refCount.deref()) + delete d; } -const QHashedString &QQmlType::module() const +QHashedString QQmlType::module() const { + if (!d) + return QHashedString(); return d->module; } int QQmlType::majorVersion() const { + if (!d) + return -1; return d->version_maj; } int QQmlType::minorVersion() const { + if (!d) + return -1; return d->version_min; } bool QQmlType::availableInVersion(int vmajor, int vminor) const { Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; return vmajor == d->version_maj && vminor >= d->version_min; } bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const { Q_ASSERT(vmajor >= 0 && vminor >= 0); + if (!d) + return false; return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; } // returns the nearest _registered_ super class QQmlType *QQmlType::superType() const { + if (!d) + return 0; if (!d->haveSuperType && d->baseMetaObject) { const QMetaObject *mo = d->baseMetaObject->superClass(); while (mo && !d->superType) { @@ -487,7 +526,7 @@ QQmlType *QQmlType::superType() const QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const { Q_ASSERT(isComposite()); - if (!engine) + if (!engine || !d) return 0; QQmlRefPointer td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer::Adopt); if (td.isNull() || !td->isComplete()) @@ -500,6 +539,8 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const { Q_ASSERT(isComposite()); + if (!d) + return -1; *ok = false; QQmlType *type = resolveCompositeBaseType(engine); if (!type) @@ -709,21 +750,26 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const QByteArray QQmlType::typeName() const { - if (d->regType == SingletonType || d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); - else if (d->baseMetaObject) - return d->baseMetaObject->className(); - else - return QByteArray(); + if (d) { + if (d->regType == SingletonType || d->regType == CompositeSingletonType) + return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); + else if (d->baseMetaObject) + return d->baseMetaObject->className(); + } + return QByteArray(); } -const QString &QQmlType::elementName() const +QString QQmlType::elementName() const { + if (!d) + return QString(); return d->elementName; } -const QString &QQmlType::qmlTypeName() const +QString QQmlType::qmlTypeName() const { + if (!d) + return QString(); if (d->name.isEmpty()) { if (!d->module.isEmpty()) d->name = static_cast(d->module) + QLatin1Char('/') + d->elementName; @@ -736,7 +782,7 @@ const QString &QQmlType::qmlTypeName() const QObject *QQmlType::create() const { - if (!isCreatable()) + if (!d || !isCreatable()) return 0; d->init(); @@ -752,7 +798,7 @@ QObject *QQmlType::create() const void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const { - if (!isCreatable()) + if (!d || !isCreatable()) return; d->init(); @@ -769,6 +815,8 @@ void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) con QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const { + if (!d) + return 0; if (d->regType != SingletonType && d->regType != CompositeSingletonType) return 0; return d->extraData.sd->singletonInstanceInfo; @@ -776,6 +824,8 @@ QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const QQmlCustomParser *QQmlType::customParser() const { + if (!d) + return 0; if (d->regType != CppType) return 0; return d->extraData.cd->customParser; @@ -783,32 +833,34 @@ QQmlCustomParser *QQmlType::customParser() const QQmlType::CreateFunc QQmlType::createFunction() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return 0; return d->extraData.cd->newFunc; } QString QQmlType::noCreationReason() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return QString(); return d->extraData.cd->noCreationReason; } int QQmlType::createSize() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return 0; return d->extraData.cd->allocationSize; } bool QQmlType::isCreatable() const { - return d->regType == CppType && d->extraData.cd->newFunc; + return d && d->regType == CppType && d->extraData.cd->newFunc; } bool QQmlType::isExtendedType() const { + if (!d) + return false; d->init(); return !d->metaObjects.isEmpty(); @@ -816,36 +868,38 @@ bool QQmlType::isExtendedType() const bool QQmlType::isSingleton() const { - return d->regType == SingletonType || d->regType == CompositeSingletonType; + return d && (d->regType == SingletonType || d->regType == CompositeSingletonType); } bool QQmlType::isInterface() const { - return d->regType == InterfaceType; + return d && d->regType == InterfaceType; } bool QQmlType::isComposite() const { - return d->regType == CompositeType || d->regType == CompositeSingletonType; + return d && (d->regType == CompositeType || d->regType == CompositeSingletonType); } bool QQmlType::isCompositeSingleton() const { - return d->regType == CompositeSingletonType; + return d && d->regType == CompositeSingletonType; } int QQmlType::typeId() const { - return d->typeId; + return d ? d->typeId : -1; } int QQmlType::qListTypeId() const { - return d->listId; + return d ? d->listId : -1; } const QMetaObject *QQmlType::metaObject() const { + if (!d) + return 0; d->init(); if (d->metaObjects.isEmpty()) @@ -857,11 +911,13 @@ const QMetaObject *QQmlType::metaObject() const const QMetaObject *QQmlType::baseMetaObject() const { - return d->baseMetaObject; + return d ? d->baseMetaObject : 0; } bool QQmlType::containsRevisionedAttributes() const { + if (!d) + return false; d->init(); return d->containsRevisionedAttributes; @@ -869,11 +925,13 @@ bool QQmlType::containsRevisionedAttributes() const int QQmlType::metaObjectRevision() const { - return d->revision; + return d ? d->revision : -1; } QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivate *engine) const { + if (!d) + return 0; if (d->regType == CppType) return d->extraData.cd->attachedPropertiesFunc; @@ -885,6 +943,8 @@ QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivat const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const { + if (!d) + return 0; if (d->regType == CppType) return d->extraData.cd->attachedPropertiesType; @@ -901,6 +961,8 @@ Qt 4.7 and QtQuick 1.0). */ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const { + if (!d) + return -1; if (d->regType == CppType) return d->extraData.cd->attachedPropertiesId; @@ -912,59 +974,62 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const int QQmlType::parserStatusCast() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return -1; return d->extraData.cd->parserStatusCast; } int QQmlType::propertyValueSourceCast() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return -1; return d->extraData.cd->propertyValueSourceCast; } int QQmlType::propertyValueInterceptorCast() const { - if (d->regType != CppType) + if (!d || d->regType != CppType) return -1; return d->extraData.cd->propertyValueInterceptorCast; } const char *QQmlType::interfaceIId() const { - if (d->regType != InterfaceType) + if (!d || d->regType != InterfaceType) return 0; return d->iid; } int QQmlType::index() const { - return d->index; + return d ? d->index : -1; } QUrl QQmlType::sourceUrl() const { - if (d->regType == CompositeType) - return d->extraData.fd->url; - else if (d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->url; - else - return QUrl(); + if (d) { + if (d->regType == CompositeType) + return d->extraData.fd->url; + else if (d->regType == CompositeSingletonType) + return d->extraData.sd->singletonInstanceInfo->url; + } + return QUrl(); } int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name.toString(), ok); - *ok = true; + if (d) { + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toString(), ok); + *ok = true; - d->initEnums(); + d->initEnums(); - int *rv = d->enums.value(name); - if (rv) - return *rv; + int *rv = d->enums.value(name); + if (rv) + return *rv; + } *ok = false; return -1; @@ -973,15 +1038,17 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name.toUtf16(), ok); - *ok = true; + if (d) { + if (isComposite()) + return resolveCompositeEnumValue(engine, name.toUtf16(), ok); + *ok = true; - d->initEnums(); + d->initEnums(); - int *rv = d->enums.value(name); - if (rv) - return *rv; + int *rv = d->enums.value(name); + if (rv) + return *rv; + } *ok = false; return -1; @@ -990,15 +1057,17 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const { Q_ASSERT(ok); - if (isComposite()) - return resolveCompositeEnumValue(engine, name->toQString(), ok); - *ok = true; + if (d) { + if (isComposite()) + return resolveCompositeEnumValue(engine, name->toQString(), ok); + *ok = true; - d->initEnums(); + d->initEnums(); - int *rv = d->enums.value(name); - if (rv) - return *rv; + int *rv = d->enums.value(name); + if (rv) + return *rv; + } *ok = false; return -1; diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 2b615e645a..e3752b7bf8 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -138,11 +138,18 @@ class QHashedCStringRef; class Q_QML_PRIVATE_EXPORT QQmlType { public: + QQmlType(); + QQmlType(const QQmlType &other); + QQmlType &operator =(const QQmlType &other); + ~QQmlType(); + + bool isValid() const { return d != 0; } + QByteArray typeName() const; - const QString &qmlTypeName() const; - const QString &elementName() const; + QString qmlTypeName() const; + QString elementName() const; - const QHashedString &module() const; + QHashedString module() const; int majorVersion() const; int minorVersion() const; @@ -244,7 +251,6 @@ private: QQmlType(int, const QString &, const QQmlPrivate::RegisterType &); QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeType &); QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &); - ~QQmlType(); QQmlTypePrivate *d; }; -- cgit v1.2.3 From ee1de29c2dcb16a4b0c691c7fc53310e175fa49f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 10:41:21 +0200 Subject: Use QQmlType by value in the MemberExpressionResolver Task-number: QTBUG-61536 Change-Id: Ia9a43890937399f3a109d93227e5d44420974737 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 20 ++++++++++---------- src/qml/compiler/qv4jsir_p.h | 8 +++++++- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 03a71768d8..60045952c9 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1694,19 +1694,19 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, { QV4::IR::Type result = QV4::IR::VarType; - QQmlType *type = static_cast(resolver->data); + QQmlType type = resolver->qmlType; if (member->name->constData()->isUpper()) { bool ok = false; - int value = type->enumValue(qmlEngine, *member->name, &ok); + int value = type.enumValue(qmlEngine, *member->name, &ok); if (ok) { member->setEnumValue(value); return QV4::IR::SInt32Type; } } - if (type->isCompositeSingleton()) { - QQmlRefPointer tdata = qmlEngine->typeLoader.getType(type->singletonInstanceInfo()->url); + if (type.isCompositeSingleton()) { + QQmlRefPointer tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url); Q_ASSERT(tdata); tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType() // When a singleton tries to reference itself, it may not be complete yet. @@ -1717,8 +1717,8 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, newResolver->flags |= AllPropertiesAreFinal; return newResolver->resolveMember(qmlEngine, newResolver, member); } - } else if (type->isSingleton()) { - const QMetaObject *singletonMeta = type->singletonInstanceInfo()->instanceMetaObject; + } else if (type.isSingleton()) { + const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject; if (singletonMeta) { // QJSValue-based singletons cannot be accelerated auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; @@ -1743,12 +1743,12 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, return result; } -static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType) +static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType) { Q_ASSERT(resolver); resolver->resolveMember = &resolveQmlType; - resolver->data = qmlType; + resolver->qmlType = qmlType; resolver->extraData = 0; resolver->flags = 0; } @@ -1774,7 +1774,7 @@ static QV4::IR::DiscoveredType resolveImportNamespace( if (!r.type->isSingleton()) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; - initQmlTypeResolver(newResolver, r.type); + initQmlTypeResolver(newResolver, *r.type); return QV4::IR::DiscoveredType(newResolver); } } else { @@ -1961,7 +1961,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int result = _block->TEMP(result->index); result->memberResolver = _function->New(); result->memberResolver->owner = _function; - initQmlTypeResolver(result->memberResolver, r.type); + initQmlTypeResolver(result->memberResolver, *r.type); return result; } else { Q_ASSERT(r.importNamespace); diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 6f14e3dfaf..e9344a31fb 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -54,6 +54,9 @@ #include #include #include +#ifndef V4_BOOTSTRAP +#include +#endif #include #include @@ -254,7 +257,10 @@ struct MemberExpressionResolver void clear() { *this = MemberExpressionResolver(); } ResolveFunction resolveMember; - void *data; // Could be pointer to meta object, importNameSpace, etc. - depends on resolveMember implementation +#ifndef V4_BOOTSTRAP + QQmlType qmlType; +#endif + void *data; // Could be pointer to meta object or importNameSpace void *extraData; // Could be QQmlTypeNameCache Function *owner; unsigned int flags; -- cgit v1.2.3 From a6633e41e7c6795bbbc016ce36e4ff91ec5248ad Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 13:09:03 +0200 Subject: Use QQmlType by value in the type wrapper Task-number: QTBUG-61536 Change-Id: Ie40cb3a6e170331b0ec7ab5deaf7c1d7ef0cdaeb Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4qmlcontext.cpp | 2 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 +- src/qml/qml/qqmlmetatype.cpp | 19 ++++++++++ src/qml/qml/qqmlmetatype_p.h | 5 +++ src/qml/qml/qqmltypewrapper.cpp | 67 +++++++++++++++++---------------- src/qml/qml/qqmltypewrapper_p.h | 6 ++- 6 files changed, 65 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 91d65a70c9..9411e2b8e0 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -143,7 +143,7 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); return scripts->getIndexed(r.scriptIndex); } else if (r.type) { - return QmlTypeWrapper::create(v4, scopeObject, r.type); + return QmlTypeWrapper::create(v4, scopeObject, *r.type); } else if (r.importNamespace) { return QmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index d7978cc212..73a6ab7503 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -251,7 +251,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return QV4::Encode::undefined(); } else if (r.type) { return QmlTypeWrapper::create(v4, d()->object(), - r.type, Heap::QmlTypeWrapper::ExcludeEnums); + *r.type, Heap::QmlTypeWrapper::ExcludeEnums); } else if (r.importNamespace) { return QmlTypeWrapper::create(v4, d()->object(), qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 1959146388..2ff2cdc189 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -463,6 +463,13 @@ QQmlType &QQmlType::operator =(const QQmlType &other) return *this; } +QQmlType::QQmlType(QQmlTypePrivate *priv) + : d(priv) +{ + if (d) + d->refCount.ref(); +} + QQmlType::~QQmlType() { if (d && !d->refCount.deref()) @@ -1073,6 +1080,18 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool return -1; } +void QQmlType::refHandle(QQmlTypePrivate *priv) +{ + if (priv) + priv->refCount.ref(); +} + +void QQmlType::derefHandle(QQmlTypePrivate *priv) +{ + if (priv && !priv->refCount.deref()) + delete priv; +} + QQmlTypeModule::QQmlTypeModule() : d(new QQmlTypeModulePrivate) { diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index e3752b7bf8..08198340f3 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -141,6 +141,7 @@ public: QQmlType(); QQmlType(const QQmlType &other); QQmlType &operator =(const QQmlType &other); + explicit QQmlType(QQmlTypePrivate *priv); ~QQmlType(); bool isValid() const { return d != 0; } @@ -223,6 +224,10 @@ public: int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const; int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; + + QQmlTypePrivate *handle() const { return d; } + static void refHandle(QQmlTypePrivate *priv); + static void derefHandle(QQmlTypePrivate *priv); private: QQmlType *superType() const; QQmlType *resolveCompositeBaseType(QQmlEnginePrivate *engine) const; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index be4ab68831..0656bd4c62 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -63,15 +63,22 @@ void Heap::QmlTypeWrapper::init() void Heap::QmlTypeWrapper::destroy() { + QQmlType::derefHandle(typePrivate); + typePrivate = nullptr; if (typeNamespace) typeNamespace->release(); object.destroy(); Object::destroy(); } +QQmlType Heap::QmlTypeWrapper::type() const +{ + return QQmlType(typePrivate); +} + bool QmlTypeWrapper::isSingleton() const { - return d()->type && d()->type->isSingleton(); + return d()->type().isSingleton(); } QObject* QmlTypeWrapper::singletonObject() const @@ -80,22 +87,16 @@ QObject* QmlTypeWrapper::singletonObject() const return 0; QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type->singletonInstanceInfo(); + QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); siinfo->init(e); return siinfo->qobjectApi(e); } QVariant QmlTypeWrapper::toVariant() const { - if (d()->type && d()->type->isSingleton()) { - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type->singletonInstanceInfo(); - siinfo->init(e); // note: this will also create QJSValue singleton which isn't strictly required. - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - return QVariant::fromValue(qobjectSingleton); - } - } + QObject *qobjectSingleton = singletonObject(); + if (qobjectSingleton) + return QVariant::fromValue(qobjectSingleton); // only QObject Singleton Type can be converted to a variant. return QVariant(); @@ -103,14 +104,16 @@ QVariant QmlTypeWrapper::toVariant() const // Returns a type wrapper for type t on o. This allows access of enums, and attached properties. -ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlType *t, +ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlType &t, Heap::QmlTypeWrapper::TypeNameMode mode) { - Q_ASSERT(t); + Q_ASSERT(t.isValid()); Scope scope(engine); Scoped w(scope, engine->memoryManager->allocObject()); - w->d()->mode = mode; w->d()->object = o; w->d()->type = t; + w->d()->mode = mode; w->d()->object = o; + w->d()->typePrivate = t.handle(); + QQmlType::refHandle(w->d()->typePrivate); return w.asReturnedValue(); } @@ -130,10 +133,10 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q } static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton, - QQmlType *type) + const QQmlType &type) { bool ok; - int value = type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); if (ok) return value; @@ -149,11 +152,11 @@ static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qob return -1; } -static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *name, QQmlType *type) +static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *name, const QQmlType &type) { const QString message = QStringLiteral("Cannot access enum value '%1' of '%2', enum values need to start with an uppercase letter.") - .arg(name->toQString()).arg(QLatin1String(type->typeName())); + .arg(name->toQString()).arg(QLatin1String(type.typeName())); return v4->throwTypeError(message); } @@ -172,14 +175,14 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope QQmlContextData *context = v4->callingQmlContext(); QObject *object = w->d()->object; - QQmlType *type = w->d()->type; + QQmlType type = w->d()->type(); - if (type) { + if (type.isValid()) { // singleton types are handled differently to other types. - if (type->isSingleton()) { + if (type.isSingleton()) { QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo(); + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); siinfo->init(e); QObject *qobjectSingleton = siinfo->qobjectApi(e); @@ -220,14 +223,14 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope if (name->startsWithUpper()) { bool ok = false; - int value = type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); if (ok) return QV4::Primitive::fromInt32(value).asReturnedValue(); // Fall through to base implementation } else if (w->d()->object) { - QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(v4->qmlEngine())), object); + QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(v4->qmlEngine())), object); if (ao) return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); @@ -245,7 +248,7 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope if (r.isValid()) { if (r.type) { - return create(scope.engine, object, r.type, w->d()->mode); + return create(scope.engine, object, *r.type, w->d()->mode); } else if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); return scripts->getIndexed(r.scriptIndex); @@ -269,9 +272,9 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope *hasProperty = ok; // Warn when attempting to access a lowercased enum value, non-singleton case - if (!ok && type && !type->isSingleton() && !name->startsWithUpper()) { + if (!ok && type.isValid() && !type.isSingleton() && !name->startsWithUpper()) { bool enumOk = false; - type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &enumOk); + type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &enumOk); if (enumOk) return throwLowercaseEnumError(v4, name, type); } @@ -291,16 +294,16 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scope scope(v4); QQmlContextData *context = v4->callingQmlContext(); - QQmlType *type = w->d()->type; - if (type && !type->isSingleton() && w->d()->object) { + QQmlType type = w->d()->type(); + if (type.isValid() && !type.isSingleton() && w->d()->object) { QObject *object = w->d()->object; QQmlEngine *e = scope.engine->qmlEngine(); - QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(e)), object); + QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); - } else if (type && type->isSingleton()) { + } else if (type.isValid() && type.isSingleton()) { QQmlEngine *e = scope.engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo(); + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); siinfo->init(e); QObject *qobjectSingleton = siinfo->qobjectApi(e); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 3b0ae04cc1..c21a66424b 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -76,7 +76,9 @@ struct QmlTypeWrapper : Object { TypeNameMode mode; QQmlQPointer object; - QQmlType *type; + QQmlType type() const; + + QQmlTypePrivate *typePrivate; QQmlTypeNameCache *typeNamespace; const void *importNamespace; }; @@ -93,7 +95,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object QVariant toVariant() const; - static ReturnedValue create(ExecutionEngine *, QObject *, QQmlType *, + static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &, Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const void *, Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); -- cgit v1.2.3 From 97165444ac6954766d53c3eb62eb1614644c7264 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 13:38:43 +0200 Subject: Simplify logic Task-number: QTBUG-61536 Change-Id: Ibdb10dfb8d9fbd15fddc27a2802b756381755ee7 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertyvalidator.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 383c20239f..da652e8f5d 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -628,19 +628,17 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * bool isValueSource = false; bool isPropertyInterceptor = false; - QQmlType *qmlType = 0; const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex); if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); const QMetaObject *mo = cache->firstCppMetaObject(); + QQmlType *qmlType = 0; while (mo && !qmlType) { qmlType = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } Q_ASSERT(qmlType); - } - if (qmlType) { isValueSource = qmlType->propertyValueSourceCast() != -1; isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; } -- cgit v1.2.3 From 49a11e882059ee1729f776722e085dd21d378c36 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 13:20:23 +0200 Subject: Use QQmlType by value QQmlType is now refcounted, and we need to use it by value, to control it's lifetime properly. This is required, so we can clean up the QQmlMetaTypeData cache on engine destruction and with trimComponentCache() Task-number: QTBUG-61536 Change-Id: If86391c86ea20a646ded7c9925d8f743f628fb91 Reviewed-by: Simon Hausmann --- src/imports/testlib/main.cpp | 8 +- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 5 +- src/qml/compiler/qqmlirbuilder.cpp | 12 +- src/qml/compiler/qqmlpropertycachecreator_p.h | 34 ++--- src/qml/compiler/qqmlpropertyvalidator.cpp | 20 +-- src/qml/compiler/qqmltypecompiler.cpp | 53 ++++---- src/qml/compiler/qv4compileddata.cpp | 20 +-- src/qml/compiler/qv4compileddata_p.h | 5 +- src/qml/jsruntime/qv4engine.cpp | 6 +- src/qml/jsruntime/qv4qmlcontext.cpp | 4 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 4 +- src/qml/qml/qqmlcustomparser.cpp | 10 +- src/qml/qml/qqmlengine.cpp | 47 ++++--- src/qml/qml/qqmlengine_p.h | 14 +- src/qml/qml/qqmlimport.cpp | 56 ++++---- src/qml/qml/qqmlimport_p.h | 12 +- src/qml/qml/qqmllist.cpp | 2 +- src/qml/qml/qqmlmetatype.cpp | 147 +++++++++++---------- src/qml/qml/qqmlmetatype_p.h | 52 +++++--- src/qml/qml/qqmlobjectcreator.cpp | 34 ++--- src/qml/qml/qqmlproperty.cpp | 23 ++-- src/qml/qml/qqmltypeloader.cpp | 46 +++---- src/qml/qml/qqmltypeloader_p.h | 4 +- src/qml/qml/qqmltypenamecache.cpp | 8 +- src/qml/qml/qqmltypenamecache_p.h | 22 +-- src/qml/qml/qqmltypewrapper.cpp | 6 +- src/quick/designer/qquickdesignersupportitems.cpp | 41 +++--- .../designer/qquickdesignersupportmetainfo.cpp | 4 +- 28 files changed, 356 insertions(+), 343 deletions(-) (limited to 'src') diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 3c28000e35..fc013d5afc 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -91,14 +91,14 @@ public Q_SLOTS: { QString name(v.typeName()); if (v.canConvert()) { - QQmlType *type = 0; + QQmlType type; const QMetaObject *mo = v.value()->metaObject(); - while (!type && mo) { + while (!type.isValid() && mo) { type = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } - if (type) { - name = type->qmlTypeName(); + if (type.isValid()) { + name = type.qmlTypeName(); } } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index f0bb4de016..3d08c4c809 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -695,8 +695,9 @@ bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &prope property.reset(); } else { // overwrite with default value - if (QQmlType *objType = QQmlMetaType::qmlType(object->metaObject())) { - if (QObject *emptyObject = objType->create()) { + QQmlType objType = QQmlMetaType::qmlType(object->metaObject()); + if (objType.isValid()) { + if (QObject *emptyObject = objType.create()) { if (emptyObject->property(parentProperty).isValid()) { QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read(); if (defaultValue.isValid()) { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 60045952c9..5bbf067320 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1767,14 +1767,14 @@ static QV4::IR::DiscoveredType resolveImportNamespace( if (r.scriptIndex != -1) { // TODO: remember the index and replace with subscript later. result = QV4::IR::VarType; - } else if (r.type) { + } else if (r.type.isValid()) { // TODO: Propagate singleton information, so that it is loaded // through the singleton getter in the run-time. Until then we // can't accelerate access :( - if (!r.type->isSingleton()) { + if (!r.type.isSingleton()) { auto newResolver = resolver->owner->New(); newResolver->owner = resolver->owner; - initQmlTypeResolver(newResolver, *r.type); + initQmlTypeResolver(newResolver, r.type); return QV4::IR::DiscoveredType(newResolver); } } else { @@ -1950,10 +1950,10 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int if (r.scriptIndex != -1) { return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp), _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); - } else if (r.type) { + } else if (r.type.isValid()) { QV4::IR::Name *typeName = _block->NAME(name, line, col); // Make sure the run-time loads this through the more efficient singleton getter. - typeName->qmlSingleton = r.type->isCompositeSingleton(); + typeName->qmlSingleton = r.type.isCompositeSingleton(); typeName->freeOfSideEffects = true; QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); _block->MOVE(result, typeName); @@ -1961,7 +1961,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int result = _block->TEMP(result->index); result->memberResolver = _function->New(); result->memberResolver->owner = _function; - initQmlTypeResolver(result->memberResolver, *r.type); + initQmlTypeResolver(result->memberResolver, r.type); return result; } else { Q_ASSERT(r.importNamespace); diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 3c14abc019..8fc8943366 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -211,12 +211,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCac } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex); Q_ASSERT(typeRef); - QQmlType *qmltype = typeRef->type; - if (!qmltype) { + QQmlType qmltype = typeRef->type; + if (!qmltype.isValid()) { QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) { - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + if (qmltype.isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -228,7 +228,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCac } } - const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0; + const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); if (!attachedMo) { *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); return nullptr; @@ -395,12 +395,12 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj // lazily resolved type Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType *qmltype = 0; + QQmlType qmltype; if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + if (qmltype.isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -410,7 +410,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj tdata->release(); } else { - paramTypes[i + 1] = qmltype->typeId(); + paramTypes[i + 1] = qmltype.typeId(); } } } @@ -475,14 +475,14 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || p->type == QV4::CompiledData::Property::Custom); - QQmlType *qmltype = 0; + QQmlType qmltype; if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) { return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); } - Q_ASSERT(qmltype); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(qmltype.isValid()); + if (qmltype.isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -497,9 +497,9 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj tdata->release(); } else { if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype->typeId(); + propertyType = qmltype.typeId(); } else { - propertyType = qmltype->qListTypeId(); + propertyType = qmltype.qListTypeId(); } } @@ -675,8 +675,8 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex); Q_ASSERT(typeRef); - if (typeRef->type) - *type = typeRef->type->typeId(); + if (typeRef->type.isValid()) + *type = typeRef->type.typeId(); else *type = typeRef->compilationUnit->metaTypeId; diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index da652e8f5d..4ac7aad553 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -106,8 +106,8 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, QQmlCustomParser *customParser = 0; if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type) - customParser = typeRef->type->customParser(); + if (typeRef->type.isValid()) + customParser = typeRef->type.customParser(); } QList customBindings; @@ -178,8 +178,8 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, if (notInRevision) { QString typeName = stringAt(obj->inheritedTypeNameIndex); auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (objectType && objectType->type) { - return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + if (objectType && objectType->type.isValid()) { + return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); } else { return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); } @@ -197,7 +197,7 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, collectedBindingPropertyData[i] = pd; if (name.constData()->isUpper() && !binding->isAttachedProperty()) { - QQmlType *type = 0; + QQmlType type; QQmlImportNamespace *typeNamespace = 0; imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace); if (typeNamespace) @@ -632,15 +632,15 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) { QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); const QMetaObject *mo = cache->firstCppMetaObject(); - QQmlType *qmlType = 0; - while (mo && !qmlType) { + QQmlType qmlType; + while (mo && !qmlType.isValid()) { qmlType = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } - Q_ASSERT(qmlType); + Q_ASSERT(qmlType.isValid()); - isValueSource = qmlType->propertyValueSourceCast() != -1; - isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1; + isValueSource = qmlType.propertyValueSourceCast() != -1; + isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1; } if (!isValueSource && !isPropertyInterceptor) { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index d1d22be0ac..f5cb468479 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -75,7 +75,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); it != end; ++it) { - QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0; + QQmlCustomParser *customParser = (*it)->type.customParser(); if (customParser) customParsers.insert(it.key(), customParser); } @@ -171,7 +171,6 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast(compilationUnit->data->nObjects)); - if (errors.isEmpty()) return compilationUnit; else @@ -345,11 +344,11 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedTypes.value(binding->propertyNameIndex); - QQmlType *type = typeRef ? typeRef->type : 0; - if (!type) { + QQmlType type = typeRef ? typeRef->type : nullptr; + if (!type.isValid()) { if (imports->resolveType(propertyName, &type, 0, 0, 0)) { - if (type->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type->sourceUrl()); + if (type.isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -361,7 +360,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } } - const QMetaObject *attachedType = type ? type->attachedPropertiesType(enginePrivate) : 0; + const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); if (!attachedType) COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); @@ -418,9 +417,9 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QString &originalPropertyName = stringAt(binding->propertyNameIndex); auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - const QQmlType *type = typeRef ? typeRef->type : 0; - if (type) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion())); + const QQmlType type = typeRef->type; + if (type.isValid()) { + COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion())); } else { COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName)); } @@ -612,17 +611,17 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, } return true; } - QQmlType *type = 0; + QQmlType type; imports->resolveType(typeName, &type, 0, 0, 0); - if (!type && !isQtObject) + if (!type.isValid() && !isQtObject) return true; int value = 0; bool ok = false; auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex); - if (type && tr && tr->type == type) { + if (type.isValid() && tr && tr->type == type) { QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); // When these two match, we can short cut the search @@ -633,8 +632,8 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, } } else { // Otherwise we have to search the whole type - if (type) { - value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); + if (type.isValid()) { + value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); } else { QByteArray enumName = enumValue.toUtf8(); const QMetaObject *metaObject = StaticQtMetaObject::get(); @@ -657,11 +656,9 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &e *ok = false; if (scope != QLatin1String("Qt")) { - QQmlType *type = 0; + QQmlType type; imports->resolveType(scope, &type, 0, 0, 0); - if (!type) - return -1; - return type->enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok); + return type.enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok); } const QMetaObject *mo = StaticQtMetaObject::get(); @@ -799,8 +796,8 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex); Q_ASSERT(tr); - if (QQmlType *targetType = tr->type) { - if (targetType->metaObject() == &QQmlComponent::staticMetaObject) + if (tr->type.isValid()) { + if (tr->type.metaObject() == &QQmlComponent::staticMetaObject) continue; } else if (tr->compilationUnit) { if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) @@ -829,22 +826,22 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI continue; // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" - QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); - Q_ASSERT(componentType); + QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); + Q_ASSERT(componentType.isValid()); const QString qualifier = QStringLiteral("QmlInternals"); - compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion()); + compiler->addImport(componentType.module(), qualifier, componentType.majorVersion(), componentType.minorVersion()); QmlIR::Object *syntheticComponent = pool->New(); - syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType->elementName()), compiler->registerString(QString())); + syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString())); syntheticComponent->location = binding->valueLocation; syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { auto typeRef = new QV4::CompiledData::ResolvedTypeReference; typeRef->type = componentType; - typeRef->majorVersion = componentType->majorVersion(); - typeRef->minorVersion = componentType->minorVersion(); + typeRef->majorVersion = componentType.majorVersion(); + typeRef->minorVersion = componentType.minorVersion(); resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef); } @@ -885,7 +882,7 @@ bool QQmlComponentAndAliasResolver::resolve() if (obj->inheritedTypeNameIndex) { auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex); Q_ASSERT(tref); - if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject) + if (tref->type.metaObject() == &QQmlComponent::staticMetaObject) isExplicitComponent = true; } if (!isExplicitComponent) { diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index db707061fe..6223c4b3a1 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -295,8 +295,8 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine) metaTypeId = typeRef->compilationUnit->metaTypeId; listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; } else { - metaTypeId = typeRef->type->typeId(); - listMetaTypeId = typeRef->type->qListTypeId(); + metaTypeId = typeRef->type.typeId(); + listMetaTypeId = typeRef->type.qListTypeId(); } } @@ -308,8 +308,8 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine) const QV4::CompiledData::Object *obj = data->objectAt(i); bindingCount += obj->nBindings; if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (QQmlType *qmlType = typeRef->type) { - if (qmlType->parserStatusCast() != -1) + if (typeRef->type.isValid()) { + if (typeRef->type.parserStatusCast() != -1) ++parserStatusCount; } ++objectCount; @@ -673,7 +673,7 @@ Returns the property cache, if one alread exists. The cache is not referenced. */ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const { - if (type) + if (type.isValid()) return typePropertyCache; else return compilationUnit->rootPropertyCache(); @@ -686,8 +686,8 @@ QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine { if (typePropertyCache) { return typePropertyCache; - } else if (type) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); + } else if (type.isValid()) { + typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject()); return typePropertyCache; } else { return compilationUnit->rootPropertyCache(); @@ -696,7 +696,7 @@ QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) { - if (type) { + if (type.isValid()) { bool ok = false; hash->addData(createPropertyCache(engine)->checksum(&ok)); return ok; @@ -720,8 +720,8 @@ void ResolvedTypeReference::doDynamicTypeCheck() const QMetaObject *mo = 0; if (typePropertyCache) mo = typePropertyCache->firstCppMetaObject(); - else if (type) - mo = type->metaObject(); + else if (type.isValid()) + mo = type.metaObject(); else if (compilationUnit) mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits(mo); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 23f5e31ebe..bca0dcdf8f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -923,13 +923,12 @@ protected: struct ResolvedTypeReference { ResolvedTypeReference() - : type(0) - , majorVersion(0) + : majorVersion(0) , minorVersion(0) , isFullyDynamicType(false) {} - QQmlType *type; + QQmlType type; QQmlRefPointer typePropertyCache; QQmlRefPointer compilationUnit; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0cb1b1ee13..0c4facda4d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -785,10 +785,10 @@ ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name) QQmlTypeNameCache::Result r = ctx->imports->query(name); Q_ASSERT(r.isValid()); - Q_ASSERT(r.type); - Q_ASSERT(r.type->isSingleton()); + Q_ASSERT(r.type.isValid()); + Q_ASSERT(r.type.isSingleton()); - QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); + QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); QQmlEngine *e = qmlEngine(); siinfo->init(e); diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 9411e2b8e0..144ab1b1a5 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -142,8 +142,8 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); return scripts->getIndexed(r.scriptIndex); - } else if (r.type) { - return QmlTypeWrapper::create(v4, scopeObject, *r.type); + } else if (r.type.isValid()) { + return QmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { return QmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 73a6ab7503..5dc5f5d568 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -249,9 +249,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String if (r.isValid()) { if (r.scriptIndex != -1) { return QV4::Encode::undefined(); - } else if (r.type) { + } else if (r.type.isValid()) { return QmlTypeWrapper::create(v4, d()->object(), - *r.type, Heap::QmlTypeWrapper::ExcludeEnums); + r.type, Heap::QmlTypeWrapper::ExcludeEnums); } else if (r.importNamespace) { return QmlTypeWrapper::create(v4, d()->object(), qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums); diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index 0b0bbef795..ecdaf41523 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -132,7 +132,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const if (scope != QLatin1String("Qt")) { if (imports.isNull()) return -1; - QQmlType *type = 0; + QQmlType type; if (imports.isT1()) { imports.asT1()->resolveType(scope, &type, 0, 0, 0); @@ -142,7 +142,7 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const type = result.type; } - return type ? type->enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; + return type.enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok); } const QMetaObject *mo = StaticQtMetaObject::get(); @@ -163,12 +163,10 @@ const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const { if (!imports.isT1()) return nullptr; - QQmlType *qmltype = 0; + QQmlType qmltype; if (!imports.asT1()->resolveType(name, &qmltype, 0, 0, 0)) return nullptr; - if (!qmltype) - return nullptr; - return qmltype->metaObject(); + return qmltype.metaObject(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ba22bfde76..7aae994c29 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -677,7 +677,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { - typedef QHash, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; + typedef QHash, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; if (inProgressCreations) qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations); @@ -1022,9 +1022,9 @@ QQmlEngine::~QQmlEngine() // we do this here and not in the private dtor since otherwise a crash can // occur (if we are the QObject parent of the QObject singleton instance) // XXX TODO: performance -- store list of singleton types separately? - const QList singletonTypes = QQmlMetaType::qmlSingletonTypes(); - for (QQmlType *currType : singletonTypes) - currType->singletonInstanceInfo()->destroy(this); + QList singletonTypes = QQmlMetaType::qmlSingletonTypes(); + for (const QQmlType &currType : singletonTypes) + currType.singletonInstanceInfo()->destroy(this); delete d->rootContext; d->rootContext = 0; @@ -2194,19 +2194,18 @@ QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator(); } -QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion) +QQmlPropertyCache *QQmlEnginePrivate::createCache(const QQmlType &type, int minorVersion) { - QList types; + QVector types; int maxMinorVersion = 0; - const QMetaObject *metaObject = type->metaObject(); + const QMetaObject *metaObject = type.metaObject(); while (metaObject) { - QQmlType *t = QQmlMetaType::qmlType(metaObject, type->module(), - type->majorVersion(), minorVersion); - if (t) { - maxMinorVersion = qMax(maxMinorVersion, t->minorVersion()); + QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); + if (t.isValid()) { + maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); types << t; } else { types << 0; @@ -2221,16 +2220,16 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi return c; } - QQmlPropertyCache *raw = cache(type->metaObject()); + QQmlPropertyCache *raw = cache(type.metaObject()); bool hasCopied = false; for (int ii = 0; ii < types.count(); ++ii) { - QQmlType *currentType = types.at(ii); - if (!currentType) + QQmlType currentType = types.at(ii); + if (!currentType.isValid()) continue; - int rev = currentType->metaObjectRevision(); + int rev = currentType.metaObjectRevision(); int moIndex = types.count() - 1 - ii; if (raw->allowedRevisionCache[moIndex] != rev) { @@ -2280,7 +2279,7 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi if (overloadError) { if (hasCopied) raw->release(); - error.setDescription(QLatin1String("Type ") + type->qmlTypeName() + QLatin1Char(' ') + QString::number(type->majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); return 0; } #endif @@ -2348,8 +2347,8 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache()); } else { - QQmlType *type = QQmlMetaType::qmlType(t); - return QQmlMetaObject(type?type->baseMetaObject():0); + QQmlType type = QQmlMetaType::qmlType(t); + return QQmlMetaObject(type.baseMetaObject()); } } @@ -2360,8 +2359,8 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache()); } else { - QQmlType *type = QQmlMetaType::qmlType(t); - return QQmlMetaObject(type?type->metaObject():0); + QQmlType type = QQmlMetaType::qmlType(t); + return QQmlMetaObject(type.metaObject()); } } @@ -2372,9 +2371,9 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache(); } else { - QQmlType *type = QQmlMetaType::qmlType(t); + QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); - return type?cache(type->metaObject()):0; + return type.isValid() ? cache(type.metaObject()) : 0; } } @@ -2385,9 +2384,9 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache(); } else { - QQmlType *type = QQmlMetaType::qmlType(t); + QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); - return type?cache(type->baseMetaObject()):0; + return type.isValid() ? cache(type.baseMetaObject()) : 0; } } diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 1bdeacd524..3ed8dbccff 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -208,7 +208,7 @@ public: QString offlineStorageDatabaseDirectory() const; // These methods may be called from the loader thread - inline QQmlPropertyCache *cache(QQmlType *, int); + inline QQmlPropertyCache *cache(const QQmlType &, int); using QJSEnginePrivate::cache; // These methods may be called from the loader thread @@ -260,11 +260,11 @@ public: private: // Must be called locked - QQmlPropertyCache *createCache(QQmlType *, int); + QQmlPropertyCache *createCache(const QQmlType &, int); // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. - QHash, QQmlPropertyCache *> typePropertyCache; + QHash, QQmlPropertyCache *> typePropertyCache; QHash m_qmlLists; QHash m_compositeTypes; static bool s_designerMode; @@ -375,12 +375,12 @@ Returns a QQmlPropertyCache for \a type with \a minorVersion. The returned cache is not referenced, so if it is to be stored, call addref(). */ -QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion) +QQmlPropertyCache *QQmlEnginePrivate::cache(const QQmlType &type, int minorVersion) { - Q_ASSERT(type); + Q_ASSERT(type.isValid()); - if (minorVersion == -1 || !type->containsRevisionedAttributes()) - return cache(type->metaObject()); + if (minorVersion == -1 || !type.containsRevisionedAttributes()) + return cache(type.metaObject()); Locker locker(this); QQmlPropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ec748d1ca9..89c7fd3214 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -142,13 +142,13 @@ bool isPathAbsolute(const QString &path) Errors (if there are any) are placed into \a errors, if it is nonzero. Note that errors are treated as fatal if \a errors is not set. */ -QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, +QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList *errors, int majorVersion=-1, int minorVersion=-1) { QUrl url(urlString); // ### unfortunate (costly) conversion - QQmlType *ret = QQmlMetaType::qmlType(url); - if (ret) + QQmlType ret = QQmlMetaType::qmlType(url); + if (ret.isValid()) return ret; int dot = typeName.indexOf(QLatin1Char('.')); @@ -198,7 +198,7 @@ QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringR // This means that the type couldn't be found by URL, but could not be // registered either, meaning we most likely were passed some kind of bad // data. - if (!ret) { + if (!ret.isValid()) { if (!errors) // Cannot list errors properly, just quit qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); QQmlError error; @@ -295,7 +295,7 @@ public: QList *errors); bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor, - QQmlType** type_return, QList *errors); + QQmlType *type_return, QList *errors); QUrl baseUrl; QString base; @@ -619,7 +619,7 @@ QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version) \sa addFileImport(), addLibraryImport */ bool QQmlImports::resolveType(const QHashedStringRef &type, - QQmlType** type_return, int *vmaj, int *vmin, + QQmlType *type_return, int *vmaj, int *vmin, QQmlImportNamespace** ns_return, QList *errors) const { QQmlImportNamespace* ns = d->findQualifiedNamespace(type); @@ -629,17 +629,19 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return true; } if (type_return) { - if (d->resolveType(type,vmaj,vmin,type_return, errors)) { + if (d->resolveType(type, vmaj, vmin, type_return, errors)) { if (qmlImportTrace()) { #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \ << ')' << "::resolveType: " << type.toString() << " => " - if (type_return && *type_return && (*type_return)->isCompositeSingleton()) - RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << (*type_return)->sourceUrl() << " TYPE/URL-SINGLETON"; - else if (type_return && *type_return && (*type_return)->isComposite()) - RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << (*type_return)->sourceUrl() << " TYPE/URL"; - else if (type_return && *type_return) - RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE"; + if (type_return && type_return->isValid()) { + if (type_return->isCompositeSingleton()) + RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON"; + else if (type_return->isComposite()) + RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL"; + else + RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE"; + } #undef RESOLVE_TYPE_DEBUG } return true; @@ -704,19 +706,19 @@ QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qml If the return pointer is 0, the corresponding search is not done. */ -bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type, - QQmlType** type_return, int *vmaj, int *vmin) const +bool QQmlImports::resolveType(QQmlImportNamespace *ns, const QHashedStringRef &type, + QQmlType *type_return, int *vmaj, int *vmin) const { - return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return); + return ns->resolveType(d->typeLoader, type, vmaj, vmin, type_return); } bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QString *base, bool *typeRecursionDetected) const + QQmlType* type_return, QString *base, bool *typeRecursionDetected) const { if (majversion >= 0 && minversion >= 0) { - QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion); - if (t) { + QQmlType t = QQmlMetaType::qmlType(type, uri, majversion, minversion); + if (t.isValid()) { if (vmajor) *vmajor = majversion; if (vminor) *vminor = minversion; if (type_return) @@ -766,11 +768,11 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); int major = vmajor ? *vmajor : -1; int minor = vminor ? *vminor : -1; - QQmlType *returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, 0, + QQmlType returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, 0, major, minor); if (type_return) *type_return = returnType; - return returnType != 0; + return returnType.isValid(); } } else if (!isLibrary) { QString qmlUrl; @@ -794,10 +796,10 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType *returnType = fetchOrCreateTypeForUrl(qmlUrl, type, false, 0); + QQmlType returnType = fetchOrCreateTypeForUrl(qmlUrl, type, false, 0); if (type_return) *type_return = returnType; - return returnType != 0; + return returnType.isValid(); } } } @@ -806,7 +808,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, } bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QList *errors) + QQmlType *type_return, QList *errors) { QQmlImportNamespace *s = 0; int dot = type.indexOf(Dot); @@ -835,12 +837,12 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, } QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1); if (s) { - if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return, &base, errors)) + if (s->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors)) return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); - return (*type_return != 0); + return type_return->isValid(); } } @@ -857,7 +859,7 @@ QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const } bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType** type_return, + int *vmajor, int *vminor, QQmlType *type_return, QString *base, QList *errors) { bool typeRecursionDetected = false; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 7c691a468c..8a0a4ea4f1 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -86,7 +86,7 @@ struct QQmlImportInstance static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType** type_return, + int *vmajor, int *vminor, QQmlType* type_return, QString *base = 0, bool *typeRecursionDetected = 0) const; }; @@ -101,7 +101,7 @@ public: QQmlImportInstance *findImport(const QString &uri) const; bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, QQmlType** type_return, + int *vmajor, int *vminor, QQmlType* type_return, QString *base = 0, QList *errors = 0); // Prefix when used as a qualified import. Otherwise empty. @@ -125,13 +125,13 @@ public: QUrl baseUrl() const; bool resolveType(const QHashedStringRef &type, - QQmlType** type_return, + QQmlType *type_return, int *version_major, int *version_minor, - QQmlImportNamespace** ns_return, + QQmlImportNamespace **ns_return, QList *errors = 0) const; - bool resolveType(QQmlImportNamespace*, + bool resolveType(QQmlImportNamespace *, const QHashedStringRef& type, - QQmlType** type_return, int *version_major, int *version_minor) const; + QQmlType *type_return, int *version_major, int *version_minor) const; bool addImplicitImport(QQmlImportDatabase *importDb, QList *errors); diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp index 2c71293363..71be2e82a3 100644 --- a/src/qml/qml/qqmllist.cpp +++ b/src/qml/qml/qqmllist.cpp @@ -148,7 +148,7 @@ QQmlListReference::QQmlListReference(QObject *object, const char *property, QQml d = new QQmlListReferencePrivate; d->object = object; - d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject(); + d->elementType = p ? p->rawMetaObjectForType(listType) : QQmlMetaType::qmlType(listType).baseMetaObject(); d->propertyType = data->propType(); void *args[] = { &d->property, 0 }; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 2ff2cdc189..3e29222200 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -209,7 +209,7 @@ public: int listId; int revision; mutable bool containsRevisionedAttributes; - mutable QQmlType *superType; + mutable QQmlType superType; const QMetaObject *baseMetaObject; int index; @@ -281,7 +281,7 @@ QHash QQmlTypePrivate::attachedPropertyIds; QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) : refCount(1), regType(type), iid(0), typeId(0), listId(0), revision(0), - containsRevisionedAttributes(false), superType(0), baseMetaObject(0), + containsRevisionedAttributes(false), baseMetaObject(0), index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false) { switch (type) { @@ -476,6 +476,27 @@ QQmlType::~QQmlType() delete d; } +QQmlType::QQmlType(QQmlType *otherPointer) + : d(0) +{ + if (otherPointer) { + d = otherPointer->d; + if (d) + d->refCount.ref(); + } +} + +QQmlType &QQmlType::operator =(QQmlType *otherPointer) { + if (otherPointer && otherPointer->d != d) { + if (d && !d->refCount.deref()) + delete d; + d = otherPointer->d; + if (d) + d->refCount.ref(); + } + return *this; +} + QHashedString QQmlType::module() const { if (!d) @@ -514,13 +535,13 @@ bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, in } // returns the nearest _registered_ super class -QQmlType *QQmlType::superType() const +QQmlType QQmlType::superType() const { if (!d) return 0; if (!d->haveSuperType && d->baseMetaObject) { const QMetaObject *mo = d->baseMetaObject->superClass(); - while (mo && !d->superType) { + while (mo && !d->superType.isValid()) { d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min); mo = mo->superClass(); } @@ -530,7 +551,7 @@ QQmlType *QQmlType::superType() const return d->superType; } -QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const { Q_ASSERT(isComposite()); if (!engine || !d) @@ -549,10 +570,8 @@ int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString if (!d) return -1; *ok = false; - QQmlType *type = resolveCompositeBaseType(engine); - if (!type) - return -1; - return type->enumValue(engine, name, ok); + QQmlType type = resolveCompositeBaseType(engine); + return type.enumValue(engine, name, ok); } static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, @@ -942,10 +961,10 @@ QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivat if (d->regType == CppType) return d->extraData.cd->attachedPropertiesFunc; - QQmlType *base = 0; + QQmlType base; if (d->regType == CompositeType) base = resolveCompositeBaseType(engine); - return base ? base->attachedPropertiesFunction(engine) : 0; + return base.attachedPropertiesFunction(engine); } const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) const @@ -955,10 +974,10 @@ const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) c if (d->regType == CppType) return d->extraData.cd->attachedPropertiesType; - QQmlType *base = 0; + QQmlType base; if (d->regType == CompositeType) base = resolveCompositeBaseType(engine); - return base ? base->attachedPropertiesType(engine) : 0; + return base.attachedPropertiesType(engine); } /* @@ -973,10 +992,10 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const if (d->regType == CppType) return d->extraData.cd->attachedPropertiesId; - QQmlType *base = 0; + QQmlType base; if (d->regType == CompositeType) base = resolveCompositeBaseType(engine); - return base ? base->attachedPropertiesId(engine) : 0; + return base.attachedPropertiesId(engine); } int QQmlType::parserStatusCast() const @@ -1137,46 +1156,32 @@ void QQmlTypeModulePrivate::add(QQmlType *type) list.append(type); } -QQmlType *QQmlTypeModule::type(const QHashedStringRef &name, int minor) const +QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const { QMutexLocker lock(metaTypeDataLock()); QList *types = d->typeHash.value(name); - if (!types) return 0; - - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->minorVersion() <= minor) - return types->at(ii); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->minorVersion() <= minor) + return types->at(ii); + } - return 0; + return QQmlType(); } -QQmlType *QQmlTypeModule::type(const QV4::String *name, int minor) const +QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const { QMutexLocker lock(metaTypeDataLock()); QList *types = d->typeHash.value(name); - if (!types) return 0; - - for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->minorVersion() <= minor) - return types->at(ii); - - return 0; -} - -QList QQmlTypeModule::singletonTypes(int minor) const -{ - QMutexLocker lock(metaTypeDataLock()); - - QList retn; - for (int ii = 0; ii < d->types.count(); ++ii) { - QQmlType *curr = d->types.at(ii); - if (curr->isSingleton() && curr->minorVersion() <= minor) - retn.append(curr); + if (types) { + for (int ii = 0; ii < types->count(); ++ii) + if (types->at(ii)->minorVersion() <= minor) + return types->at(ii); } - return retn; + return QQmlType(); } QQmlTypeModuleVersion::QQmlTypeModuleVersion() @@ -1213,16 +1218,18 @@ int QQmlTypeModuleVersion::minorVersion() const return m_minor; } -QQmlType *QQmlTypeModuleVersion::type(const QHashedStringRef &name) const +QQmlType QQmlTypeModuleVersion::type(const QHashedStringRef &name) const { - if (m_module) return m_module->type(name, m_minor); - else return 0; + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); } -QQmlType *QQmlTypeModuleVersion::type(const QV4::String *name) const +QQmlType QQmlTypeModuleVersion::type(const QV4::String *name) const { - if (m_module) return m_module->type(name, m_minor); - else return 0; + if (!m_module) + return QQmlType(); + return m_module->type(name, m_minor); } void qmlClearTypeRegistrations() // Declared in qqml.h @@ -1369,7 +1376,7 @@ QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMe } // NOTE: caller must hold a QMutexLocker on "data" -void addTypeToData(QQmlType* type, QQmlMetaTypeData *data) +void addTypeToData(QQmlType *type, QQmlMetaTypeData *data) { if (!type->elementName().isEmpty()) data->nameToType.insertMulti(type->elementName(), type); @@ -1855,7 +1862,7 @@ QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) Returns the type (if any) of URI-qualified named \a qualifiedName and version specified by \a version_major and \a version_minor. */ -QQmlType *QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, int version_minor) { int slash = qualifiedName.indexOf(QLatin1Char('/')); if (slash <= 0) @@ -1871,7 +1878,7 @@ QQmlType *QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, Returns the type (if any) of \a name in \a module and version specified by \a version_major and \a version_minor. */ -QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); QMutexLocker lock(metaTypeDataLock()); @@ -1881,18 +1888,18 @@ QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStrin while (it != data->nameToType.cend() && it.key() == name) { // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty if (version_major < 0 || module.isEmpty() || (*it)->availableInVersion(module, version_major,version_minor)) - return (*it); + return *(*it); ++it; } - return 0; + return QQmlType(); } /*! Returns the type (if any) that corresponds to the \a metaObject. Returns null if no type is registered. */ -QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject) +QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -1905,7 +1912,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject) by \a version_major and \a version_minor in module specified by \a uri. Returns null if no type is registered. */ -QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) { Q_ASSERT(version_major >= 0 && version_minor >= 0); QMutexLocker lock(metaTypeDataLock()); @@ -1926,7 +1933,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStri Returns the type (if any) that corresponds to the QVariant::Type \a userType. Returns null if no type is registered. */ -QQmlType *QQmlMetaType::qmlType(int userType) +QQmlType QQmlMetaType::qmlType(int userType) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -1944,7 +1951,7 @@ QQmlType *QQmlMetaType::qmlType(int userType) Returns null if no such type is registered. */ -QQmlType *QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = false */) +QQmlType QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = false */) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -1954,9 +1961,9 @@ QQmlType *QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = type = data->urlToNonFileImportType.value(url); if (type && type->sourceUrl() == url) - return type; + return *type; else - return 0; + return QQmlType(); } /*! @@ -1964,7 +1971,7 @@ QQmlType *QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = This is for use when you just got the index back from a qmlRegister function. Returns null if the index is out of bounds. */ -QQmlType *QQmlMetaType::qmlTypeFromIndex(int idx) +QQmlType QQmlMetaType::qmlTypeFromIndex(int idx) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -1996,7 +2003,7 @@ QList QQmlMetaType::qmlTypeNames() /*! Returns the list of registered QML types. */ -QList QQmlMetaType::qmlTypes() +QList QQmlMetaType::qmlTypes() { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -2007,7 +2014,7 @@ QList QQmlMetaType::qmlTypes() /*! Returns the list of all registered types. */ -QList QQmlMetaType::qmlAllTypes() +QList QQmlMetaType::qmlAllTypes() { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -2018,15 +2025,15 @@ QList QQmlMetaType::qmlAllTypes() /*! Returns the list of registered QML singleton types. */ -QList QQmlMetaType::qmlSingletonTypes() +QList QQmlMetaType::qmlSingletonTypes() { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QList retn; + QList retn; for (const auto type : qAsConst(data->nameToType)) { if (type->isSingleton()) - retn.append(type); + retn.append(*type); } return retn; } @@ -2053,9 +2060,9 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) if (!object) return typeName; - const QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); - if (type) { - typeName = type->qmlTypeName(); + QQmlType type = QQmlMetaType::qmlType(object->metaObject()); + if (type.isValid()) { + typeName = type.qmlTypeName(); const int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); if (lastSlash != -1) typeName = typeName.mid(lastSlash + 1); @@ -2071,8 +2078,8 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) if (marker != -1) { typeName = typeName.leftRef(marker) + QLatin1Char('*'); type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); - if (type) { - QString qmlTypeName = type->qmlTypeName(); + if (type.isValid()) { + QString qmlTypeName = type.qmlTypeName(); const int lastSlash = qmlTypeName.lastIndexOf(QLatin1Char('/')); if (lastSlash != -1) qmlTypeName = qmlTypeName.mid(lastSlash + 1); diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 08198340f3..d17172a917 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -77,17 +77,17 @@ class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: static QList qmlTypeNames(); - static QList qmlTypes(); - static QList qmlSingletonTypes(); - static QList qmlAllTypes(); - - static QQmlType *qmlType(const QString &qualifiedName, int, int); - static QQmlType *qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int); - static QQmlType *qmlType(const QMetaObject *); - static QQmlType *qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor); - static QQmlType *qmlType(int); - static QQmlType *qmlType(const QUrl &url, bool includeNonFileImports = false); - static QQmlType *qmlTypeFromIndex(int); + static QList qmlTypes(); + static QList qmlSingletonTypes(); + static QList qmlAllTypes(); + + static QQmlType qmlType(const QString &qualifiedName, int, int); + static QQmlType qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int); + static QQmlType qmlType(const QMetaObject *); + static QQmlType qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor); + static QQmlType qmlType(int); + static QQmlType qmlType(const QUrl &url, bool includeNonFileImports = false); + static QQmlType qmlTypeFromIndex(int); static QMetaProperty defaultProperty(const QMetaObject *); static QMetaProperty defaultProperty(QObject *); @@ -144,7 +144,16 @@ public: explicit QQmlType(QQmlTypePrivate *priv); ~QQmlType(); + // ### get rid of these two again + QQmlType(QQmlType *otherPointer); + QQmlType &operator =(QQmlType *otherPointer); + + bool operator ==(const QQmlType &other) const { + return d == other.d; + } + bool isValid() const { return d != 0; } + const QQmlTypePrivate *key() const { return d; } QByteArray typeName() const; QString qmlTypeName() const; @@ -229,8 +238,8 @@ public: static void refHandle(QQmlTypePrivate *priv); static void derefHandle(QQmlTypePrivate *priv); private: - QQmlType *superType() const; - QQmlType *resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + QQmlType superType() const; + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; friend class QQmlTypePrivate; friend struct QQmlMetaTypeData; @@ -250,6 +259,7 @@ private: friend int registerCompositeType(const QQmlPrivate::RegisterCompositeType &); friend int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &); friend int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &); + friend uint qHash(const QQmlType &t, uint seed); friend Q_QML_EXPORT void qmlClearTypeRegistrations(); QQmlType(int, const QQmlPrivate::RegisterInterface &); QQmlType(int, const QString &, const QQmlPrivate::RegisterSingletonType &); @@ -260,6 +270,12 @@ private: QQmlTypePrivate *d; }; +Q_DECLARE_TYPEINFO(QQmlMetaType, Q_MOVABLE_TYPE); + + +inline uint qHash(const QQmlType &t, uint seed = 0) { return qHash(reinterpret_cast(t.d), seed); } + + class QQmlTypeModulePrivate; class QQmlTypeModule { @@ -270,10 +286,8 @@ public: int minimumMinorVersion() const; int maximumMinorVersion() const; - QQmlType *type(const QHashedStringRef &, int) const; - QQmlType *type(const QV4::String *, int) const; - - QList singletonTypes(int) const; + QQmlType type(const QHashedStringRef &, int) const; + QQmlType type(const QV4::String *, int) const; private: //Used by register functions and creates the QQmlTypeModule for them @@ -299,8 +313,8 @@ public: QQmlTypeModule *module() const; int minorVersion() const; - QQmlType *type(const QHashedStringRef &) const; - QQmlType *type(const QV4::String *) const; + QQmlType type(const QHashedStringRef &) const; + QQmlType type(const QV4::String *) const; private: QQmlTypeModule *m_module; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 2cbcfbbfb6..6867aaad75 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -613,11 +613,11 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } -static QQmlType *qmlTypeForObject(QObject *object) +static QQmlType qmlTypeForObject(QObject *object) { - QQmlType *type = 0; + QQmlType type; const QMetaObject *mo = object->metaObject(); - while (mo && !type) { + while (mo && !type.isValid()) { type = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } @@ -654,7 +654,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) } else if (binding) { QQmlValueTypeProxyBinding *proxy = static_cast(binding); - if (qmlTypeForObject(_bindingTarget)) { + if (qmlTypeForObject(_bindingTarget).isValid()) { quint32 bindingSkipList = 0; QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultPropertyOrAlias != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty(); @@ -712,15 +712,15 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); QV4::CompiledData::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex); Q_ASSERT(tr); - QQmlType *attachedType = tr->type; - if (!attachedType) { + QQmlType attachedType = tr->type; + if (!attachedType.isValid()) { QQmlTypeNameCache::Result res = context->imports->query(stringAt(binding->propertyNameIndex)); if (res.isValid()) attachedType = res.type; else return false; } - const int id = attachedType->attachedPropertiesId(QQmlEnginePrivate::get(engine)); + const int id = attachedType.attachedPropertiesId(QQmlEnginePrivate::get(engine)); QObject *qmlObject = qmlAttachedPropertiesObjectById(id, _qobject); if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject, /*value type property*/0)) return false; @@ -850,10 +850,10 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (binding->type == QV4::CompiledData::Binding::Type_Object) { if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { // ### determine value source and interceptor casts ahead of time. - QQmlType *type = qmlTypeForObject(createdSubObject); - Q_ASSERT(type); + QQmlType type = qmlTypeForObject(createdSubObject); + Q_ASSERT(type.isValid()); - int valueSourceCast = type->propertyValueSourceCast(); + int valueSourceCast = type.propertyValueSourceCast(); if (valueSourceCast != -1) { QQmlPropertyValueSource *vs = reinterpret_cast(reinterpret_cast(createdSubObject) + valueSourceCast); QObject *target = createdSubObject->parent(); @@ -865,7 +865,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con vs->setTarget(prop); return true; } - int valueInterceptorCast = type->propertyValueInterceptorCast(); + int valueInterceptorCast = type.propertyValueInterceptorCast(); if (valueInterceptorCast != -1) { QQmlPropertyValueInterceptor *vi = reinterpret_cast(reinterpret_cast(createdSubObject) + valueInterceptorCast); QObject *target = createdSubObject->parent(); @@ -1060,13 +1060,13 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QV4::CompiledData::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); installPropertyCache = !typeRef->isFullyDynamicType; - QQmlType *type = typeRef->type; - if (type) { + QQmlType type = typeRef->type; + if (type.isValid()) { Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( - compilationUnit, obj, type->qmlTypeName(), context->url())); + compilationUnit, obj, type.qmlTypeName(), context->url())); void *ddataMemory = 0; - type->create(&instance, &ddataMemory, sizeof(QQmlData)); + type.create(&instance, &ddataMemory, sizeof(QQmlData)); if (!instance) { recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex))); return 0; @@ -1080,11 +1080,11 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo p->declarativeData = ddata; } - const int parserStatusCast = type->parserStatusCast(); + const int parserStatusCast = type.parserStatusCast(); if (parserStatusCast != -1) parserStatus = reinterpret_cast(reinterpret_cast(instance) + parserStatusCast); - customParser = type->customParser(); + customParser = type.customParser(); if (sharedState->rootContext && sharedState->rootContext->isRootObjectInCreation) { QQmlData *ddata = QQmlData::get(instance, /*create*/true); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 9b5f7b0a06..21bbcadb1c 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -253,24 +253,24 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) if (typeNameCache) { QQmlTypeNameCache::Result r = typeNameCache->query(pathName); if (r.isValid()) { - if (r.type) { + if (r.type.isValid()) { QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(enginePrivate); + QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(enginePrivate), currentObject); + currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); if (!currentObject) return; // Something is broken with the attachable type } else if (r.importNamespace) { if ((ii + 1) == path.count()) return; // No type following the namespace ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); - if (!r.type) return; // Invalid type in namespace + if (!r.type.isValid()) return; // Invalid type in namespace QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - QQmlAttachedPropertiesFunc func = r.type->attachedPropertiesFunction(enginePrivate); + QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type->attachedPropertiesId(enginePrivate), currentObject); + currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); if (!currentObject) return; // Something is broken with the attachable type } else if (r.scriptIndex != -1) { @@ -1275,10 +1275,10 @@ bool QQmlPropertyPrivate::write(QObject *object, if (enginePriv) { listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType())); } else { - QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType())); - if (!type) + QQmlType type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType())); + if (!type.isValid()) return false; - listType = type->baseMetaObject(); + listType = type.baseMetaObject(); } if (listType.isNull()) return false; @@ -1393,8 +1393,9 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi return metaType.metaObject(); if (engine) return engine->rawMetaObjectForType(userType); - if (QQmlType *type = QQmlMetaType::qmlType(userType)) - return QQmlMetaObject(type->baseMetaObject()); + QQmlType type = QQmlMetaType::qmlType(userType); + if (type.isValid()) + return QQmlMetaObject(type.baseMetaObject()); return QQmlMetaObject(); } diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index e4293596d8..fbb04af165 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2013,7 +2013,7 @@ QString QQmlTypeData::TypeReference::qualifiedName() const if (!prefix.isEmpty()) { result = prefix + QLatin1Char('.'); } - result.append(type->qmlTypeName()); + result.append(type.qmlTypeName()); return result; } @@ -2168,8 +2168,8 @@ static bool addTypeReferenceChecksumsToHash(const QListcompilationUnit(); hash->addData(unit->data->md5Checksum, sizeof(unit->data->md5Checksum)); - } else if (typeRef.type) { - const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type->metaObject()); + } else if (typeRef.type.isValid()) { + const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); bool ok = false; hash->addData(propertyCache->checksum(&ok)); if (!ok) @@ -2233,7 +2233,7 @@ void QQmlTypeData::done() const TypeReference &type = m_compositeSingletons.at(ii); Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); if (type.typeData && type.typeData->isError()) { - QString typeName = type.type->qmlTypeName(); + QString typeName = type.type.qmlTypeName(); QList errors = type.typeData->errors(); QQmlError error; @@ -2300,24 +2300,24 @@ void QQmlTypeData::done() } { - QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true); + QQmlType type = QQmlMetaType::qmlType(finalUrl(), true); if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) { - if (!type) { + if (!type.isValid()) { QQmlError error; error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); setError(error); return; - } else if (!type->isCompositeSingleton()) { + } else if (!type.isCompositeSingleton()) { QQmlError error; - error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName())); + error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName())); setError(error); return; } } else { // If the type is CompositeSingleton but there was no pragma Singleton in the // QML file, lets report an error. - if (type && type->isCompositeSingleton()) { - QString typeName = type->qmlTypeName(); + if (type.isValid() && type.isCompositeSingleton()) { + QString typeName = type.qmlTypeName(); setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); return; } @@ -2609,8 +2609,8 @@ void QQmlTypeData::resolveTypes() if (!resolveType(typeName, majorVersion, minorVersion, ref)) return; - if (ref.type->isCompositeSingleton()) { - ref.typeData = typeLoader()->getType(ref.type->sourceUrl()); + if (ref.type.isCompositeSingleton()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); addDependency(ref.typeData); ref.prefix = csRef.prefix; @@ -2637,8 +2637,8 @@ void QQmlTypeData::resolveTypes() if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, unresolvedRef->location.column, reportErrors) && reportErrors) return; - if (ref.type && ref.type->isComposite()) { - ref.typeData = typeLoader()->getType(ref.type->sourceUrl()); + if (ref.type.isComposite()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); addDependency(ref.typeData); } ref.majorVersion = majorVersion; @@ -2665,7 +2665,7 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( // Add any Composite Singletons that were used to the import cache for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*typeNameCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); m_importCache.populateCache(*typeNameCache); @@ -2673,24 +2673,24 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { QScopedPointer ref(new QV4::CompiledData::ResolvedTypeReference); - QQmlType *qmlType = resolvedType->type; + QQmlType qmlType = resolvedType->type; if (resolvedType->typeData) { - if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) { - return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName())); + if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) { + return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); } ref->compilationUnit = resolvedType->typeData->compilationUnit(); - } else if (qmlType) { + } else if (qmlType.isValid()) { ref->type = qmlType; - Q_ASSERT(ref->type); + Q_ASSERT(ref->type.isValid()); - if (resolvedType->needsCreation && !ref->type->isCreatable()) { - QString reason = ref->type->noCreationReason(); + if (resolvedType->needsCreation && !ref->type.isCreatable()) { + QString reason = ref->type.noCreationReason(); if (reason.isEmpty()) reason = tr("Element is not creatable."); return QQmlCompileError(resolvedType->location, reason); } - if (ref->type->containsRevisionedAttributes()) { + if (ref->type.containsRevisionedAttributes()) { ref->typePropertyCache = engine->cache(ref->type, resolvedType->minorVersion); } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index f367fa6f58..762fcdac65 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -393,10 +393,10 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob public: struct TypeReference { - TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0), needsCreation(true) {} + TypeReference() : majorVersion(0), minorVersion(0), typeData(0), needsCreation(true) {} QV4::CompiledData::Location location; - QQmlType *type; + QQmlType type; int majorVersion; int minorVersion; QQmlTypeData *typeData; diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index c8e2b92c29..a8f55d2e48 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -100,7 +100,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) // Look up anonymous types from the imports of this document QQmlImportNamespace *typeNamespace = 0; QList errors; - QQmlType *t = 0; + QQmlType t; bool typeFound = m_imports.resolveType(name, &t, 0, 0, &typeNamespace, &errors); if (typeFound) { return Result(t); @@ -130,7 +130,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name.toString(); QQmlImportNamespace *typeNamespace = 0; QList errors; - QQmlType *t = 0; + QQmlType t; bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); if (typeFound) { return Result(t); @@ -155,7 +155,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons QString typeName = name->toQStringNoThrow(); QQmlImportNamespace *typeNamespace = 0; QList errors; - QQmlType *t = 0; + QQmlType t; bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors); if (typeFound) { return Result(t); @@ -191,7 +191,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); QQmlImportNamespace *typeNamespace = 0; QList errors; - QQmlType *t = 0; + QQmlType t; bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); if (typeFound) { return Result(t); diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 7cdcbe91b6..0705166ec2 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -78,13 +78,13 @@ public: struct Result { inline Result(); inline Result(const void *importNamespace); - inline Result(QQmlType *type); + inline Result(const QQmlType &type); inline Result(int scriptIndex); inline Result(const Result &); inline bool isValid() const; - QQmlType *type; + QQmlType type; const void *importNamespace; int scriptIndex; }; @@ -132,9 +132,8 @@ private: { QUrl *url = urls.value(key); if (url) { - QQmlType *type = QQmlMetaType::qmlType(*url); - if (type) - return Result(type); + QQmlType type = QQmlMetaType::qmlType(*url); + return Result(type); } return Result(); @@ -145,7 +144,8 @@ private: { QVector::const_iterator end = modules.constEnd(); for (QVector::const_iterator it = modules.constBegin(); it != end; ++it) { - if (QQmlType *type = it->type(key)) + QQmlType type = it->type(key); + if (type.isValid()) return Result(type); } @@ -160,22 +160,22 @@ private: }; QQmlTypeNameCache::Result::Result() -: type(0), importNamespace(0), scriptIndex(-1) +: importNamespace(0), scriptIndex(-1) { } QQmlTypeNameCache::Result::Result(const void *importNamespace) -: type(0), importNamespace(importNamespace), scriptIndex(-1) +: importNamespace(importNamespace), scriptIndex(-1) { } -QQmlTypeNameCache::Result::Result(QQmlType *type) +QQmlTypeNameCache::Result::Result(const QQmlType &type) : type(type), importNamespace(0), scriptIndex(-1) { } QQmlTypeNameCache::Result::Result(int scriptIndex) -: type(0), importNamespace(0), scriptIndex(scriptIndex) +: importNamespace(0), scriptIndex(scriptIndex) { } @@ -186,7 +186,7 @@ QQmlTypeNameCache::Result::Result(const Result &o) bool QQmlTypeNameCache::Result::isValid() const { - return type || importNamespace || scriptIndex != -1; + return type.isValid() || importNamespace || scriptIndex != -1; } QQmlTypeNameCache::Import::Import() diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 0656bd4c62..6fab7697f8 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -247,8 +247,8 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope QQmlTypeNameCache::Result r = w->d()->typeNamespace->query(name, w->d()->importNamespace); if (r.isValid()) { - if (r.type) { - return create(scope.engine, object, *r.type, w->d()->mode); + if (r.type.isValid()) { + return create(scope.engine, object, r.type, w->d()->mode); } else if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); return scripts->getIndexed(r.scriptIndex); @@ -301,7 +301,7 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); - } else if (type.isValid() && type.isSingleton()) { + } else if (type.isSingleton()) { QQmlEngine *e = scope.engine->qmlEngine(); QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); siinfo->init(e); diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 874faed0af..38ba46e702 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -175,29 +175,24 @@ static bool isWindow(QObject *object) { return false; } -static QQmlType *getQmlType(const QString &typeName, int majorNumber, int minorNumber) +static bool isCrashingType(const QQmlType &type) { - return QQmlMetaType::qmlType(typeName, majorNumber, minorNumber); -} + QString name = type.qmlTypeName(); -static bool isCrashingType(QQmlType *type) -{ - if (type) { - if (type->qmlTypeName() == QLatin1String("QtMultimedia/MediaPlayer")) - return true; + if (type.qmlTypeName() == QLatin1String("QtMultimedia/MediaPlayer")) + return true; - if (type->qmlTypeName() == QLatin1String("QtMultimedia/Audio")) - return true; + if (type.qmlTypeName() == QLatin1String("QtMultimedia/Audio")) + return true; - if (type->qmlTypeName() == QLatin1String("QtQuick.Controls/MenuItem")) - return true; + if (type.qmlTypeName() == QLatin1String("QtQuick.Controls/MenuItem")) + return true; - if (type->qmlTypeName() == QLatin1String("QtQuick.Controls/Menu")) - return true; + if (type.qmlTypeName() == QLatin1String("QtQuick.Controls/Menu")) + return true; - if (type->qmlTypeName() == QLatin1String("QtQuick/Timer")) - return true; - } + if (type.qmlTypeName() == QLatin1String("QtQuick/Timer")) + return true; return false; } @@ -209,19 +204,19 @@ QObject *QQuickDesignerSupportItems::createPrimitive(const QString &typeName, in Q_UNUSED(disableComponentComplete) QObject *object = 0; - QQmlType *type = getQmlType(typeName, majorNumber, minorNumber); + QQmlType type = QQmlMetaType::qmlType(typeName, majorNumber, minorNumber); if (isCrashingType(type)) { object = new QObject; - } else if (type) { - if ( type->isComposite()) { - object = createComponent(type->sourceUrl(), context); + } else if (type.isValid()) { + if ( type.isComposite()) { + object = createComponent(type.sourceUrl(), context); } else { - if (type->typeName() == "QQmlComponent") { + if (type.typeName() == "QQmlComponent") { object = new QQmlComponent(context->engine(), 0); } else { - object = type->create(); + object = type.create(); } } diff --git a/src/quick/designer/qquickdesignersupportmetainfo.cpp b/src/quick/designer/qquickdesignersupportmetainfo.cpp index 332ae26bd4..b398bae55d 100644 --- a/src/quick/designer/qquickdesignersupportmetainfo.cpp +++ b/src/quick/designer/qquickdesignersupportmetainfo.cpp @@ -53,8 +53,8 @@ bool QQuickDesignerSupportMetaInfo::isSubclassOf(QObject *object, const QByteArr const QMetaObject *metaObject = object->metaObject(); while (metaObject) { - QQmlType *qmlType = QQmlMetaType::qmlType(metaObject); - if (qmlType && qmlType->qmlTypeName() == QLatin1String(superTypeName)) // ignore version numbers + QQmlType qmlType = QQmlMetaType::qmlType(metaObject); + if (qmlType.qmlTypeName() == QLatin1String(superTypeName)) // ignore version numbers return true; if (metaObject->className() == superTypeName) -- cgit v1.2.3 From db1034d093d76b4b6e66fcdd178800e0cf9d1005 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 23 Jun 2017 12:10:23 +0200 Subject: Move the engine pointer from the property cache to the VME meta object This is where it belongs, and it makes the PropertyCache independent of the engine used. Task-number: QTBUG-61536 Change-Id: I21c2674ee3e2895abd2418764d140b154b47b868 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 2 +- src/qml/jsapi/qjsengine.cpp | 2 +- src/qml/qml/qqmlobjectcreator.cpp | 2 +- src/qml/qml/qqmlopenmetaobject.cpp | 2 +- src/qml/qml/qqmlpropertycache.cpp | 42 +++++----------- src/qml/qml/qqmlpropertycache_p.h | 18 ++----- src/qml/qml/qqmlvmemetaobject.cpp | 71 +++++++++++++-------------- src/qml/qml/qqmlvmemetaobject_p.h | 3 +- src/qml/types/qqmldelegatemodel.cpp | 6 +-- src/qml/util/qqmladaptormodel.cpp | 15 +++--- src/qml/util/qqmladaptormodel_p.h | 5 +- src/quick/designer/qqmldesignermetaobject.cpp | 2 +- 12 files changed, 70 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6223c4b3a1..929339b70d 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -211,7 +211,7 @@ void CompilationUnit::unlink() if (isRegisteredWithEngine) { Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); - QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine); qmlEngine->unregisterInternalCompositeType(this); isRegisteredWithEngine = false; } diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index c678f8037a..17f3fcedae 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -759,7 +759,7 @@ void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q) QQmlPropertyCache *QJSEnginePrivate::createCache(const QMetaObject *mo) { if (!mo->superClass()) { - QQmlPropertyCache *rv = new QQmlPropertyCache(QV8Engine::getV4(q_func()), mo); + QQmlPropertyCache *rv = new QQmlPropertyCache(mo); propertyCache.insert(mo, rv); return rv; } else { diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 6867aaad75..45f7c5333a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1325,7 +1325,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * if (propertyCaches->needsVMEMetaObject(_compiledObjectIndex)) { Q_ASSERT(!cache.isNull()); // install on _object - vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compilationUnit, _compiledObjectIndex); + vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex); if (_ddata->propertyCache) _ddata->propertyCache->release(); _ddata->propertyCache = cache; diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index 49f02476a2..fc85030b3d 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -376,7 +376,7 @@ void QQmlOpenMetaObject::setCached(bool c) QQmlData *qmldata = QQmlData::get(d->object, true); if (d->cacheProperties) { if (!d->type->d->cache) - d->type->d->cache = new QQmlPropertyCache(QV8Engine::getV4(d->type->d->engine), this); + d->type->d->cache = new QQmlPropertyCache(this); qmldata->propertyCache = d->type->d->cache; d->type->d->cache->addref(); } else { diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index d18159841c..9a495e6fa3 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -99,7 +99,7 @@ static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p) // Flags that do depend on the property's QMetaProperty::userType() and thus are slow to // load -static void flagsForPropertyType(int propType, QQmlEngine *engine, QQmlPropertyData::Flags &flags) +static void flagsForPropertyType(int propType, QQmlPropertyData::Flags &flags) { Q_ASSERT(propType != -1); @@ -116,9 +116,7 @@ static void flagsForPropertyType(int propType, QQmlEngine *engine, QQmlPropertyD } else if (propType == qMetaTypeId()) { flags.type = QQmlPropertyData::Flags::V4HandleType; } else { - QQmlMetaType::TypeCategory cat = - engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType) - : QQmlMetaType::typeCategory(propType); + QQmlMetaType::TypeCategory cat = QQmlMetaType::typeCategory(propType); if (cat == QQmlMetaType::Object || QMetaType::typeFlags(propType) & QMetaType::PointerToQObject) flags.type = QQmlPropertyData::Flags::QObjectDerivedType; @@ -136,10 +134,10 @@ static int metaObjectSignalCount(const QMetaObject *metaObject) } QQmlPropertyData::Flags -QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine) +QQmlPropertyData::flagsForProperty(const QMetaProperty &p) { auto flags = fastFlagsForProperty(p); - flagsForPropertyType(p.userType(), engine, flags); + flagsForPropertyType(p.userType(), flags); return flags; } @@ -166,13 +164,13 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) } } -void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine) +void QQmlPropertyData::load(const QMetaProperty &p) { setPropType(p.userType()); setCoreIndex(p.propertyIndex()); setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal())); setFlags(fastFlagsForProperty(p)); - flagsForPropertyType(propType(), engine, _flags); + flagsForPropertyType(propType(), _flags); Q_ASSERT(p.revision() <= Q_INT16_MAX); setRevision(p.revision()); } @@ -244,19 +242,18 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m) /*! Creates a new empty QQmlPropertyCache. */ -QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e) - : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), +QQmlPropertyCache::QQmlPropertyCache() + : _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false), _metaObject(0), argumentsCache(0), _jsFactoryMethodIndex(-1) { - Q_ASSERT(engine); } /*! Creates a new QQmlPropertyCache of \a metaObject. */ -QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e, const QMetaObject *metaObject) - : QQmlPropertyCache(e) +QQmlPropertyCache::QQmlPropertyCache(const QMetaObject *metaObject) + : QQmlPropertyCache() { Q_ASSERT(metaObject); @@ -265,8 +262,6 @@ QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e, const QMetaObject QQmlPropertyCache::~QQmlPropertyCache() { - clear(); - QQmlPropertyCacheMethodArguments *args = argumentsCache; while (args) { QQmlPropertyCacheMethodArguments *next = args->next; @@ -284,24 +279,11 @@ QQmlPropertyCache::~QQmlPropertyCache() if (_ownMetaObject) free(const_cast(_metaObject)); _metaObject = 0; _parent = 0; - engine = 0; -} - -void QQmlPropertyCache::destroy() -{ - delete this; -} - -// This is inherited from QQmlCleanup, so it should only clear the things -// that are tied to the specific QQmlEngine. -void QQmlPropertyCache::clear() -{ - engine = 0; } QQmlPropertyCache *QQmlPropertyCache::copy(int reserve) { - QQmlPropertyCache *cache = new QQmlPropertyCache(engine); + QQmlPropertyCache *cache = new QQmlPropertyCache(); cache->_parent = this; cache->_parent->addref(); cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart; @@ -711,7 +693,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); } } - flagsForPropertyType(data->propType(), engine->qmlEngine(), data->_flags); + flagsForPropertyType(data->propType(), data->_flags); } } diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 64be1cb206..6444d8800a 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -288,8 +288,8 @@ public: inline bool operator==(const QQmlPropertyRawData &); - static Flags flagsForProperty(const QMetaProperty &, QQmlEngine *engine = 0); - void load(const QMetaProperty &, QQmlEngine *engine = 0); + static Flags flagsForProperty(const QMetaProperty &); + void load(const QMetaProperty &); void load(const QMetaMethod &); QString name(QObject *) const; QString name(const QMetaObject *) const; @@ -350,11 +350,11 @@ private: }; class QQmlPropertyCacheMethodArguments; -class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount, public QQmlCleanup +class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount { public: - QQmlPropertyCache(QV4::ExecutionEngine *); - QQmlPropertyCache(QV4::ExecutionEngine *, const QMetaObject *); + QQmlPropertyCache(); + QQmlPropertyCache(const QMetaObject *); virtual ~QQmlPropertyCache(); void update(const QMetaObject *); @@ -445,11 +445,6 @@ public: static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); QByteArray checksum(bool *ok); - -protected: - void destroy() override; - void clear() override; - private: friend class QQmlEnginePrivate; friend class QQmlCompiler; @@ -493,9 +488,6 @@ private: _hasPropertyOverrides |= isOverride; } -public: - QV4::ExecutionEngine *engine; - private: QQmlPropertyCache *_parent; int propertyIndexCacheStart; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 490a4e19ab..1638b5d272 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -311,24 +311,25 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje return this; } -QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, +QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, + QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), + engine(engine), ctxt(QQmlData::get(obj, true)->outerContext), aliasEndpoints(0), compilationUnit(qmlCompilationUnit), compiledObject(0) { + Q_ASSERT(engine); QQmlData::get(obj)->hasVMEMetaObject = true; if (compilationUnit && qmlObjectId >= 0) { compiledObject = compilationUnit->data->objectAt(qmlObjectId); if (compiledObject->nProperties || compiledObject->nFunctions) { - Q_ASSERT(cache && cache->engine); - QV4::ExecutionEngine *v4 = cache->engine; uint size = compiledObject->nProperties + compiledObject->nFunctions; if (size) { - QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, size); - propertyAndMethodStorage.set(v4, data); + QV4::Heap::MemberData *data = QV4::MemberData::allocate(engine, size); + propertyAndMethodStorage.set(engine, data); std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); } @@ -385,56 +386,56 @@ void QQmlVMEMetaObject::writeProperty(int id, const QString& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newString(v); + *(md->data() + id) = engine->newString(v); } void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QDate& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + *(md->data() + id) = engine->newVariantObject(QVariant::fromValue(v)); } void QQmlVMEMetaObject::writeProperty(int id, QObject* v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = QV4::QObjectWrapper::wrap(cache->engine, v); + *(md->data() + id) = QV4::QObjectWrapper::wrap(engine, v); QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id); if (v && !guard) { @@ -451,7 +452,7 @@ int QQmlVMEMetaObject::readPropertyAsInt(int id) const if (!md) return 0; - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); if (!sv->isInt32()) return 0; @@ -464,7 +465,7 @@ bool QQmlVMEMetaObject::readPropertyAsBool(int id) const if (!md) return false; - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); if (!sv->isBoolean()) return false; @@ -477,7 +478,7 @@ double QQmlVMEMetaObject::readPropertyAsDouble(int id) const if (!md) return 0.0; - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); if (!sv->isDouble()) return 0.0; @@ -490,7 +491,7 @@ QString QQmlVMEMetaObject::readPropertyAsString(int id) const if (!md) return QString(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); if (QV4::String *s = sv->stringValue()) return s->toQString(); @@ -503,7 +504,7 @@ QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) const if (!md) return QUrl(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::Url) @@ -517,7 +518,7 @@ QDate QQmlVMEMetaObject::readPropertyAsDate(int id) const if (!md) return QDate(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::Date) @@ -531,7 +532,7 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) if (!md) return QDateTime(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::DateTime) @@ -545,7 +546,7 @@ QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const if (!md) return QSizeF(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::SizeF) @@ -559,7 +560,7 @@ QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) const if (!md) return QPointF(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::PointF) @@ -573,7 +574,7 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const if (!md) return 0; - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::QObjectWrapper *wrapper = sv->as(); if (!wrapper) @@ -587,11 +588,11 @@ QList *QQmlVMEMetaObject::readPropertyAsList(int id) const if (!md) return 0; - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::Scoped v(scope, *(md->data() + id)); if (!v || (int)v->d()->data().userType() != qMetaTypeId >()) { QVariant variant(qVariantFromValue(QList())); - v = cache->engine->newVariantObject(variant); + v = engine->newVariantObject(variant); *(md->data() + id) = v; } return static_cast *>(v->d()->data().data()); @@ -603,7 +604,7 @@ QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const if (!md) return QRectF(); - QV4::Scope scope(cache->engine); + QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as(); if (!v || v->d()->data().type() != QVariant::RectF) @@ -817,7 +818,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { QV4::VariantObject *v = (md->data() + id)->as(); if (!v) { - *(md->data() + id) = cache->engine->newVariantObject(QVariant()); + *(md->data() + id) = engine->newVariantObject(QVariant()); v = (md->data() + id)->as(); QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); } @@ -1013,7 +1014,7 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const const QV4::VariantObject *v = (md->data() + id)->as(); if (v) return v->d()->data(); - return cache->engine->toVariant(*(md->data() + id), -1); + return engine->toVariant(*(md->data() + id), -1); } return QVariant(); } @@ -1073,8 +1074,8 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) // And, if the new value is a scarce resource, we need to ensure that it does not get // automatically released by the engine until no other references to it exist. - QV4::Scope scope(cache->engine); - QV4::ScopedValue newv(scope, cache->engine->fromVariant(value)); + QV4::Scope scope(engine); + QV4::ScopedValue newv(scope, engine->fromVariant(value)); QV4::Scoped v(scope, newv); if (!!v) v->addVmePropertyReference(); @@ -1099,7 +1100,7 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) v->d()->data() != value); if (v) v->removeVmePropertyReference(); - *(md->data() + id) = cache->engine->newVariantObject(value); + *(md->data() + id) = engine->newVariantObject(value); v = static_cast(md->data() + id); v->addVmePropertyReference(); } @@ -1163,15 +1164,13 @@ void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v) void QQmlVMEMetaObject::ensureQObjectWrapper() { - Q_ASSERT(cache && cache->engine); - QV4::ExecutionEngine *v4 = cache->engine; - QV4::QObjectWrapper::wrap(v4, object); + Q_ASSERT(cache); + QV4::QObjectWrapper::wrap(engine, object); } void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) { - QV4::ExecutionEngine *v4 = cache ? cache->engine : 0; - if (v4 != e) + if (engine != e) return; propertyAndMethodStorage.markOnce(e); diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index bb6fede7c8..ede1dd74f9 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -144,7 +144,7 @@ class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId); + QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId); ~QQmlVMEMetaObject(); bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; @@ -164,6 +164,7 @@ protected: int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) Q_DECL_OVERRIDE; public: + QV4::ExecutionEngine *engine; QQmlGuardedContextData ctxt; inline int propOffset() const; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 8f4b9cd519..6584ecfb84 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -941,7 +941,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); + cacheItem = m_adaptorModel.createItem(m_cacheMetaType, it.modelIndex()); if (!cacheItem) return 0; @@ -1621,7 +1621,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const if (!m_context || !m_context->isValid()) return false; - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); + QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1); if (!cacheItem) return false; if (!object.isObject()) @@ -2472,7 +2472,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) if (!cacheItem) { cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); + model->m_cacheMetaType, it.modelIndex()); if (!cacheItem) return QQmlV4Handle(QV4::Encode::undefined()); cacheItem->groups = it->flags; diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index d4aa2e19ed..2d93c331cc 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -515,16 +515,15 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - QQmlEngine *engine, - int index) const + int index) const Q_DECL_OVERRIDE { VDMAbstractItemModelDataType *dataType = const_cast(this); if (!metaObject) - dataType->initializeMetaType(model, engine); + dataType->initializeMetaType(model); return new QQmlDMAbstractItemModelData(metaType, dataType, index); } - void initializeMetaType(QQmlAdaptorModel &model, QQmlEngine *engine) + void initializeMetaType(QQmlAdaptorModel &model) { QMetaObjectBuilder builder; setModelDataType(&builder, this); @@ -549,7 +548,7 @@ public: metaObject = builder.toMetaObject(); *static_cast(this) = *metaObject; - propertyCache = new QQmlPropertyCache(QV8Engine::getV4(engine), metaObject); + propertyCache = new QQmlPropertyCache(metaObject); } }; @@ -661,8 +660,7 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - QQmlEngine *, - int index) const + int index) const Q_DECL_OVERRIDE { return new QQmlDMListAccessorData( metaType, @@ -746,8 +744,7 @@ public: QQmlDelegateModelItem *createItem( QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, - QQmlEngine *, - int index) const + int index) const Q_DECL_OVERRIDE { VDMObjectDelegateDataType *dataType = const_cast(this); if (!metaObject) diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index 78d964236e..7bbddcff07 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -82,7 +82,6 @@ public: virtual QQmlDelegateModelItem *createItem( QQmlAdaptorModel &, QQmlDelegateModelItemMetaType *, - QQmlEngine *, int) const { return 0; } virtual bool notify( @@ -122,8 +121,8 @@ public: inline int count() const { return qMax(0, accessors->count(*this)); } inline QVariant value(int index, const QString &role) const { return accessors->value(*this, index, role); } - inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, QQmlEngine *engine, int index) { - return accessors->createItem(*this, metaType, engine, index); } + inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) { + return accessors->createItem(*this, metaType, index); } inline bool hasProxyObject() const { return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; } diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp index 5e897218c5..33ea442b76 100644 --- a/src/quick/designer/qqmldesignermetaobject.cpp +++ b/src/quick/designer/qqmldesignermetaobject.cpp @@ -127,7 +127,7 @@ void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine) } QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine) - : QQmlVMEMetaObject(object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1), + : QQmlVMEMetaObject(QQmlEnginePrivate::getV4Engine(engine), object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1), m_context(engine->contextForObject(object)), m_data(new MetaPropertyData) { -- cgit v1.2.3 From c6b2dd879d02b21b18f80faab541f8f04286685a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Jun 2017 13:36:13 +0200 Subject: Move property cache from the engine to QQmlType Now that the property cache is independent of a specific engine, we can cache them in QQmlType instead of caching them per engine. This simplifies the logic and avoids duplicated property caches when multiple engines are running. Task-number: QTBUG-61536 Change-Id: I6f082e1e7495ae0f5b92559e8d0a3437c56e303a Reviewed-by: Lars Knoll --- src/qml/jsapi/qjsengine.cpp | 19 +------------------ src/qml/jsapi/qjsengine_p.h | 17 +++-------------- src/qml/qml/qqmlmetatype.cpp | 29 +++++++++++++++++++++++++++++ src/qml/qml/qqmlmetatype_p.h | 3 +++ src/qml/qml/qqmlpropertycache_p.h | 2 ++ 5 files changed, 38 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 17f3fcedae..35b4929c3a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -730,10 +730,7 @@ QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) QJSEnginePrivate::~QJSEnginePrivate() { - typedef QHash::Iterator PropertyCacheIt; - - for (PropertyCacheIt iter = propertyCache.begin(), end = propertyCache.end(); iter != end; ++iter) - (*iter)->release(); + // ### FIXME: sweep unused QQmlType instances } void QJSEnginePrivate::addToDebugServer(QJSEngine *q) @@ -756,20 +753,6 @@ void QJSEnginePrivate::removeFromDebugServer(QJSEngine *q) server->removeEngine(q); } -QQmlPropertyCache *QJSEnginePrivate::createCache(const QMetaObject *mo) -{ - if (!mo->superClass()) { - QQmlPropertyCache *rv = new QQmlPropertyCache(mo); - propertyCache.insert(mo, rv); - return rv; - } else { - QQmlPropertyCache *super = cache(mo->superClass()); - QQmlPropertyCache *rv = super->copyAndAppend(mo); - propertyCache.insert(mo, rv); - return rv; - } -} - /*! \since 5.5 \relates QJSEngine diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h index 2b462451ed..cbfe0f14a3 100644 --- a/src/qml/jsapi/qjsengine_p.h +++ b/src/qml/jsapi/qjsengine_p.h @@ -55,6 +55,7 @@ #include #include "qjsengine.h" #include "private/qtqmlglobal_p.h" +#include QT_BEGIN_NAMESPACE @@ -110,14 +111,6 @@ public: // These methods may be called from the QML loader thread inline QQmlPropertyCache *cache(QObject *obj); inline QQmlPropertyCache *cache(const QMetaObject *); - -private: - // Must be called locked - QQmlPropertyCache *createCache(const QMetaObject *); - - // These members must be protected by a QJSEnginePrivate::Locker as they are required by - // the threaded loader. Only access them through their respective accessor methods. - QHash propertyCache; }; QJSEnginePrivate::Locker::Locker(const QJSEngine *e) @@ -174,9 +167,7 @@ QQmlPropertyCache *QJSEnginePrivate::cache(QObject *obj) Locker locker(this); const QMetaObject *mo = obj->metaObject(); - QQmlPropertyCache *rv = propertyCache.value(mo); - if (!rv) rv = createCache(mo); - return rv; + return QQmlMetaType::propertyCache(mo); } /*! @@ -193,9 +184,7 @@ QQmlPropertyCache *QJSEnginePrivate::cache(const QMetaObject *metaObject) Q_ASSERT(metaObject); Locker locker(this); - QQmlPropertyCache *rv = propertyCache.value(metaObject); - if (!rv) rv = createCache(metaObject); - return rv; + return QQmlMetaType::propertyCache(metaObject); } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 3e29222200..a2b6913943 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -110,6 +110,9 @@ struct QQmlMetaTypeData QString typeRegistrationNamespace; QStringList typeRegistrationFailures; + + QHash propertyCaches; + QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); }; class QQmlTypeModulePrivate @@ -151,6 +154,9 @@ QQmlMetaTypeData::~QQmlMetaTypeData() for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) delete *i; + for (QHash::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); + it != end; ++it) + (*it)->release(); } class QQmlTypePrivate @@ -1981,6 +1987,29 @@ QQmlType QQmlMetaType::qmlTypeFromIndex(int idx) return data->types.at(idx); } +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject) +{ + if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) + return rv; + + if (!metaObject->superClass()) { + QQmlPropertyCache *rv = new QQmlPropertyCache(metaObject); + propertyCaches.insert(metaObject, rv); + return rv; + } + QQmlPropertyCache *super = propertyCache(metaObject->superClass()); + QQmlPropertyCache *rv = super->copyAndAppend(metaObject); + propertyCaches.insert(metaObject, rv); + return rv; +} + +QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return data->propertyCache(metaObject); +} + /*! Returns the list of registered QML type names. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index d17172a917..c577a9cc18 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -70,6 +70,7 @@ class QQmlTypeModule; class QHashedString; class QHashedStringRef; class QMutex; +class QQmlPropertyCache; namespace QV4 { struct String; } @@ -89,6 +90,8 @@ public: static QQmlType qmlType(const QUrl &url, bool includeNonFileImports = false); static QQmlType qmlTypeFromIndex(int); + static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); + static QMetaProperty defaultProperty(const QMetaObject *); static QMetaProperty defaultProperty(QObject *); static QMetaMethod defaultMethod(const QMetaObject *); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6444d8800a..8fdc95af3a 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -511,6 +511,8 @@ private: QByteArray _checksum; }; +typedef QQmlRefPointer QQmlPropertyCachePtr; + // QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache. // This is necessary as we delay creation of QMetaObject for synthesized QObjects, but // we don't want to needlessly generate QQmlPropertyCaches every time we encounter a -- cgit v1.2.3 From aa6120cc3524b8d603e92e952aece7494a5d8f02 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Jun 2017 17:19:26 +0200 Subject: Remove QQmlEnginePrivate::typePropertyCache Instead of maintaining a hash per QQmlType and minor version, move the version specific property caches into QQmlType. Task-number: QTBUG-61536 Change-Id: I72f3990cb93e64ac5074060b4ff327043eab7966 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlengine.cpp | 105 ---------------------------- src/qml/qml/qqmlengine_p.h | 8 +-- src/qml/qml/qqmlmetatype.cpp | 143 ++++++++++++++++++++++++++++++++++++++ src/qml/qml/qqmlmetatype_p.h | 1 + src/qml/qml/qqmlpropertycache_p.h | 1 + 5 files changed, 146 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 7aae994c29..6f5dcc0746 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -677,8 +677,6 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { - typedef QHash, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; - if (inProgressCreations) qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations); @@ -696,8 +694,6 @@ QQmlEnginePrivate::~QQmlEnginePrivate() if (incubationController) incubationController->d = 0; incubationController = 0; - for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) - (*iter)->release(); for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { iter.value()->isRegisteredWithEngine = false; @@ -2194,107 +2190,6 @@ QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator(); } -QQmlPropertyCache *QQmlEnginePrivate::createCache(const QQmlType &type, int minorVersion) -{ - QVector types; - - int maxMinorVersion = 0; - - const QMetaObject *metaObject = type.metaObject(); - - while (metaObject) { - QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); - if (t.isValid()) { - maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); - types << t; - } else { - types << 0; - } - - metaObject = metaObject->superClass(); - } - - if (QQmlPropertyCache *c = typePropertyCache.value(qMakePair(type, maxMinorVersion))) { - c->addref(); - typePropertyCache.insert(qMakePair(type, minorVersion), c); - return c; - } - - QQmlPropertyCache *raw = cache(type.metaObject()); - - bool hasCopied = false; - - for (int ii = 0; ii < types.count(); ++ii) { - QQmlType currentType = types.at(ii); - if (!currentType.isValid()) - continue; - - int rev = currentType.metaObjectRevision(); - int moIndex = types.count() - 1 - ii; - - if (raw->allowedRevisionCache[moIndex] != rev) { - if (!hasCopied) { - raw = raw->copy(); - hasCopied = true; - } - raw->allowedRevisionCache[moIndex] = rev; - } - } - - // Test revision compatibility - the basic rule is: - // * Anything that is excluded, cannot overload something that is not excluded * - - // Signals override: - // * other signals and methods of the same name. - // * properties named on - // * automatic Changed notify signals - - // Methods override: - // * other methods of the same name - - // Properties override: - // * other elements of the same name - -#if 0 - bool overloadError = false; - QString overloadName; - - for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); - !overloadError && iter != raw->stringCache.end(); - ++iter) { - - QQmlPropertyData *d = *iter; - if (raw->isAllowedInRevision(d)) - continue; // Not excluded - no problems - - // check that a regular "name" overload isn't happening - QQmlPropertyData *current = d; - while (!overloadError && current) { - current = d->overrideData(current); - if (current && raw->isAllowedInRevision(current)) - overloadError = true; - } - } - - if (overloadError) { - if (hasCopied) raw->release(); - - error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); - return 0; - } -#endif - - if (!hasCopied) raw->addref(); - typePropertyCache.insert(qMakePair(type, minorVersion), raw); - - if (minorVersion != maxMinorVersion) { - raw->addref(); - typePropertyCache.insert(qMakePair(type, maxMinorVersion), raw); - } - - return raw; -} - bool QQmlEnginePrivate::isQObject(int t) { Locker locker(this); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 3ed8dbccff..48c865abe5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -259,12 +259,8 @@ public: mutable QMutex networkAccessManagerMutex; private: - // Must be called locked - QQmlPropertyCache *createCache(const QQmlType &, int); - // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. - QHash, QQmlPropertyCache *> typePropertyCache; QHash m_qmlLists; QHash m_compositeTypes; static bool s_designerMode; @@ -383,9 +379,7 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(const QQmlType &type, int minorVersi return cache(type.metaObject()); Locker locker(this); - QQmlPropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); - if (!rv) rv = createCache(type, minorVersion); - return rv; + return QQmlMetaType::propertyCache(type, minorVersion); } QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index a2b6913943..9dd0c14723 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -113,6 +113,7 @@ struct QQmlMetaTypeData QHash propertyCaches; QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); + QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); }; class QQmlTypeModulePrivate @@ -226,6 +227,17 @@ public: mutable QStringHash enums; static QHash attachedPropertyIds; + + struct PropertyCacheByMinorVersion + { + PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {} + explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {} + QQmlPropertyCachePtr cache; + int minorVersion; + }; + QVector propertyCaches; + QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const; + void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache); }; void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) @@ -780,6 +792,25 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const } } +QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersion) const +{ + for (int i = 0; i < propertyCaches.count(); ++i) + if (propertyCaches.at(i).minorVersion == minorVersion) + return propertyCaches.at(i).cache; + return nullptr; +} + +void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache) +{ + for (int i = 0; i < propertyCaches.count(); ++i) { + if (propertyCaches.at(i).minorVersion == minorVersion) { + propertyCaches[i].cache = cache; + return; + } + } + propertyCaches.append(PropertyCacheByMinorVersion(cache, minorVersion)); +} + QByteArray QQmlType::typeName() const { if (d) { @@ -2010,6 +2041,118 @@ QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject) return data->propertyCache(metaObject); } +QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int minorVersion) +{ + Q_ASSERT(type.isValid()); + + if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(minorVersion)) + return pc; + + QVector types; + + int maxMinorVersion = 0; + + const QMetaObject *metaObject = type.metaObject(); + + while (metaObject) { + QQmlType t = QQmlMetaType::qmlType(metaObject, type.module(), type.majorVersion(), minorVersion); + if (t.isValid()) { + maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); + types << t; + } else { + types << 0; + } + + metaObject = metaObject->superClass(); + } + + if (QQmlPropertyCache *pc = type.key()->propertyCacheForMinorVersion(maxMinorVersion)) { + const_cast(type.key())->setPropertyCacheForMinorVersion(minorVersion, pc); + return pc; + } + + QQmlPropertyCache *raw = propertyCache(type.metaObject()); + + bool hasCopied = false; + + for (int ii = 0; ii < types.count(); ++ii) { + QQmlType currentType = types.at(ii); + if (!currentType.isValid()) + continue; + + int rev = currentType.metaObjectRevision(); + int moIndex = types.count() - 1 - ii; + + if (raw->allowedRevisionCache[moIndex] != rev) { + if (!hasCopied) { + raw = raw->copy(); + hasCopied = true; + } + raw->allowedRevisionCache[moIndex] = rev; + } + } + + // Test revision compatibility - the basic rule is: + // * Anything that is excluded, cannot overload something that is not excluded * + + // Signals override: + // * other signals and methods of the same name. + // * properties named on + // * automatic Changed notify signals + + // Methods override: + // * other methods of the same name + + // Properties override: + // * other elements of the same name + +#if 0 + bool overloadError = false; + QString overloadName; + + for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); + !overloadError && iter != raw->stringCache.end(); + ++iter) { + + QQmlPropertyData *d = *iter; + if (raw->isAllowedInRevision(d)) + continue; // Not excluded - no problems + + // check that a regular "name" overload isn't happening + QQmlPropertyData *current = d; + while (!overloadError && current) { + current = d->overrideData(current); + if (current && raw->isAllowedInRevision(current)) + overloadError = true; + } + } + + if (overloadError) { + if (hasCopied) raw->release(); + + error.setDescription(QLatin1String("Type ") + type.qmlTypeName() + QLatin1Char(' ') + QString::number(type.majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); + return 0; + } +#endif + + const_cast(type.key())->setPropertyCacheForMinorVersion(minorVersion, raw); + + if (hasCopied) + raw->release(); + + if (minorVersion != maxMinorVersion) + const_cast(type.key())->setPropertyCacheForMinorVersion(maxMinorVersion, raw); + + return raw; +} + +QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + return data->propertyCache(type, minorVersion); +} + /*! Returns the list of registered QML type names. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index c577a9cc18..43eda2b4f1 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -91,6 +91,7 @@ public: static QQmlType qmlTypeFromIndex(int); static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); + static QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); static QMetaProperty defaultProperty(const QMetaObject *); static QMetaProperty defaultProperty(QObject *); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 8fdc95af3a..a841570f1d 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -452,6 +452,7 @@ private: template friend class QQmlPropertyCacheAliasCreator; friend class QQmlComponentAndAliasResolver; friend class QQmlMetaObject; + friend struct QQmlMetaTypeData; inline QQmlPropertyCache *copy(int reserve); -- cgit v1.2.3 From e61dca72682c9f65c6ab6254fc7e80b49be3ad43 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 26 Jun 2017 13:45:56 +0200 Subject: Change data structures in QQmlMetaTypeData Make sure any QQmlType stored in the meta type data has a refcount of 1. This should now make it possible to clean out unused types by iterating over the list of types and removing those that have a refcount of 1. Some care is still needed for C++ registered types, that will need to get one more refcount, so we don't accidentally remove them. Task-number: QTBUG-61536 Change-Id: Id2a18dae5ddcb815f34013f5fde1f05d2d9d0214 Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll --- src/qml/compiler/qqmltypecompiler.cpp | 2 +- src/qml/qml/qqmlmetatype.cpp | 238 ++++++++++++++++------------------ src/qml/qml/qqmlmetatype_p.h | 13 +- src/qml/qml/qqmltypewrapper.cpp | 2 +- 4 files changed, 118 insertions(+), 137 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index f5cb468479..e17ab354cf 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -344,7 +344,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedTypes.value(binding->propertyNameIndex); - QQmlType type = typeRef ? typeRef->type : nullptr; + QQmlType type = typeRef ? typeRef->type : QQmlType(); if (!type.isValid()) { if (imports->resolveType(propertyName, &type, 0, 0, 0)) { if (type.isComposite()) { diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 9dd0c14723..36d70f24c8 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -69,18 +69,18 @@ struct QQmlMetaTypeData { QQmlMetaTypeData(); ~QQmlMetaTypeData(); - QList types; - typedef QHash Ids; + QList types; + typedef QHash Ids; Ids idToType; - typedef QHash Names; + typedef QHash Names; Names nameToType; - typedef QHash Files; //For file imported composite types only + typedef QHash Files; //For file imported composite types only Files urlToType; Files urlToNonFileImportType; // For non-file imported composite and composite // singleton types. This way we can locate any // of them by url, even if it was registered as // a module via QQmlPrivate::RegisterCompositeType - typedef QHash MetaObjects; + typedef QHash MetaObjects; MetaObjects metaObjectToType; typedef QHash StringConverters; StringConverters stringConverters; @@ -130,10 +130,10 @@ public: int maxMinorVersion; bool locked; - void add(QQmlType *); + void add(QQmlTypePrivate *); - QStringHash > typeHash; - QList types; + QStringHash > typeHash; + QList types; }; Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) @@ -150,9 +150,6 @@ QQmlMetaTypeData::QQmlMetaTypeData() QQmlMetaTypeData::~QQmlMetaTypeData() { - for (int i = 0; i < types.count(); ++i) - delete types.at(i); - for (TypeModules::const_iterator i = uriToModule.constBegin(), cend = uriToModule.constEnd(); i != cend; ++i) delete *i; for (QHash::Iterator it = propertyCaches.begin(), end = propertyCaches.end(); @@ -494,27 +491,6 @@ QQmlType::~QQmlType() delete d; } -QQmlType::QQmlType(QQmlType *otherPointer) - : d(0) -{ - if (otherPointer) { - d = otherPointer->d; - if (d) - d->refCount.ref(); - } -} - -QQmlType &QQmlType::operator =(QQmlType *otherPointer) { - if (otherPointer && otherPointer->d != d) { - if (d && !d->refCount.deref()) - delete d; - d = otherPointer->d; - if (d) - d->refCount.ref(); - } - return *this; -} - QHashedString QQmlType::module() const { if (!d) @@ -556,7 +532,7 @@ bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, in QQmlType QQmlType::superType() const { if (!d) - return 0; + return QQmlType(); if (!d->haveSuperType && d->baseMetaObject) { const QMetaObject *mo = d->baseMetaObject->superClass(); while (mo && !d->superType.isValid()) { @@ -573,10 +549,10 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const { Q_ASSERT(isComposite()); if (!engine || !d) - return 0; + return QQmlType(); QQmlRefPointer td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer::Adopt); if (td.isNull() || !td->isComplete()) - return 0; + return QQmlType(); QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit(); const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); return QQmlMetaType::qmlType(mo); @@ -709,18 +685,18 @@ void QQmlTypePrivate::init() const mo = mo->d.superdata; while(mo) { - QQmlType *t = metaTypeData()->metaObjectToType.value(mo); + QQmlTypePrivate *t = metaTypeData()->metaObjectToType.value(mo); if (t) { - if (t->d->regType == QQmlType::CppType) { - if (t->d->extraData.cd->extFunc) { + if (t->regType == QQmlType::CppType) { + if (t->extraData.cd->extFunc) { QMetaObjectBuilder builder; - clone(builder, t->d->extraData.cd->extMetaObject, t->d->baseMetaObject, baseMetaObject); + clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) metaObjects.constLast().metaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extraData.cd->extFunc, 0, 0 }; + QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; metaObjects << data; } } @@ -1178,14 +1154,16 @@ int QQmlTypeModule::maximumMinorVersion() const return d->maxMinorVersion; } -void QQmlTypeModulePrivate::add(QQmlType *type) +void QQmlTypeModulePrivate::add(QQmlTypePrivate *type) { - minMinorVersion = qMin(minMinorVersion, type->minorVersion()); - maxMinorVersion = qMax(maxMinorVersion, type->minorVersion()); + int minVersion = type->version_min; + minMinorVersion = qMin(minMinorVersion, minVersion); + maxMinorVersion = qMax(maxMinorVersion, minVersion); - QList &list = typeHash[type->elementName()]; + QList &list = typeHash[type->elementName]; for (int ii = 0; ii < list.count(); ++ii) { - if (list.at(ii)->minorVersion() < type->minorVersion()) { + Q_ASSERT(list.at(ii)); + if (list.at(ii)->version_min < minVersion) { list.insert(ii, type); return; } @@ -1197,11 +1175,11 @@ QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const { QMutexLocker lock(metaTypeDataLock()); - QList *types = d->typeHash.value(name); + QList *types = d->typeHash.value(name); if (types) { for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->minorVersion() <= minor) - return types->at(ii); + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); } return QQmlType(); @@ -1211,11 +1189,11 @@ QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const { QMutexLocker lock(metaTypeDataLock()); - QList *types = d->typeHash.value(name); + QList *types = d->typeHash.value(name); if (types) { for (int ii = 0; ii < types->count(); ++ii) - if (types->at(ii)->minorVersion() <= minor) - return types->at(ii); + if (types->at(ii)->version_min <= minor) + return QQmlType(types->at(ii)); } return QQmlType(); @@ -1275,9 +1253,6 @@ void qmlClearTypeRegistrations() // Declared in qqml.h QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - for (int i = 0; i < data->types.count(); ++i) - delete data->types.at(i); - for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i) delete *i; @@ -1315,14 +1290,16 @@ int registerInterface(const QQmlPrivate::RegisterInterface &interface) int index = data->types.count(); - QQmlType *type = new QQmlType(index, interface); + QQmlType type(index, interface); + QQmlTypePrivate *priv = type.priv(); + Q_ASSERT(priv); data->types.append(type); - data->idToType.insert(type->typeId(), type); - data->idToType.insert(type->qListTypeId(), type); + data->idToType.insert(priv->typeId, priv); + data->idToType.insert(priv->listId, priv); // XXX No insertMulti, so no multi-version interfaces? - if (!type->elementName().isEmpty()) - data->nameToType.insert(type->elementName(), type); + if (!priv->elementName.isEmpty()) + data->nameToType.insert(priv->elementName, priv); if (data->interfaces.size() <= interface.typeId) data->interfaces.resize(interface.typeId + 16); @@ -1413,32 +1390,34 @@ QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMe } // NOTE: caller must hold a QMutexLocker on "data" -void addTypeToData(QQmlType *type, QQmlMetaTypeData *data) +void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) { - if (!type->elementName().isEmpty()) - data->nameToType.insertMulti(type->elementName(), type); + Q_ASSERT(type); + + if (!type->elementName.isEmpty()) + data->nameToType.insertMulti(type->elementName, type); - if (type->baseMetaObject()) - data->metaObjectToType.insertMulti(type->baseMetaObject(), type); + if (type->baseMetaObject) + data->metaObjectToType.insertMulti(type->baseMetaObject, type); - if (type->typeId()) { - data->idToType.insert(type->typeId(), type); - if (data->objects.size() <= type->typeId()) - data->objects.resize(type->typeId() + 16); - data->objects.setBit(type->typeId(), true); + if (type->typeId) { + data->idToType.insert(type->typeId, type); + if (data->objects.size() <= type->typeId) + data->objects.resize(type->typeId + 16); + data->objects.setBit(type->typeId, true); } - if (type->qListTypeId()) { - if (data->lists.size() <= type->qListTypeId()) - data->lists.resize(type->qListTypeId() + 16); - data->lists.setBit(type->qListTypeId(), true); - data->idToType.insert(type->qListTypeId(), type); + if (type->listId) { + if (data->lists.size() <= type->listId) + data->lists.resize(type->listId + 16); + data->lists.setBit(type->listId, true); + data->idToType.insert(type->listId, type); } - if (!type->module().isEmpty()) { - const QHashedString &mod = type->module(); + if (!type->module.isEmpty()) { + const QHashedString &mod = type->module; - QQmlTypeModule *module = getTypeModule(mod, type->majorVersion(), data); + QQmlTypeModule *module = getTypeModule(mod, type->version_maj, data); Q_ASSERT(module); module->d->add(type); } @@ -1454,12 +1433,12 @@ int registerType(const QQmlPrivate::RegisterType &type) int index = data->types.count(); - QQmlType *dtype = new QQmlType(index, elementName, type); + QQmlType dtype(index, elementName, type); data->types.append(dtype); - addTypeToData(dtype, data); + addTypeToData(dtype.priv(), data); if (!type.typeId) - data->idToType.insert(dtype->typeId(), dtype); + data->idToType.insert(dtype.typeId(), dtype.priv()); return index; } @@ -1474,10 +1453,10 @@ int registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) int index = data->types.count(); - QQmlType *dtype = new QQmlType(index, typeName, type); + QQmlType dtype(index, typeName, type); data->types.append(dtype); - addTypeToData(dtype, data); + addTypeToData(dtype.priv(), data); return index; } @@ -1496,13 +1475,13 @@ int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingleton int index = data->types.count(); - QQmlType *dtype = new QQmlType(index, typeName, type); + QQmlType dtype(index, typeName, type); data->types.append(dtype); - addTypeToData(dtype, data); + addTypeToData(dtype.priv(), data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(type.url, dtype); + files->insertMulti(type.url, dtype.priv()); return index; } @@ -1521,12 +1500,12 @@ int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) int index = data->types.count(); - QQmlType *dtype = new QQmlType(index, typeName, type); + QQmlType dtype(index, typeName, type); data->types.append(dtype); - addTypeToData(dtype, data); + addTypeToData(dtype.priv(), data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(type.url, dtype); + files->insertMulti(type.url, dtype.priv()); return index; } @@ -1602,8 +1581,8 @@ bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorV // Has any type previously been installed to this namespace? QHashedString nameSpace(uri); - for (const QQmlType *type : data->types) - if (type->module() == nameSpace && type->majorVersion() == majorVersion) + for (const QQmlType &type : data->types) + if (type.module() == nameSpace && type.majorVersion() == majorVersion) return true; return false; @@ -1735,9 +1714,9 @@ int QQmlMetaType::listType(int id) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType *type = data->idToType.value(id); - if (type && type->qListTypeId() == id) - return type->typeId(); + QQmlTypePrivate *type = data->idToType.value(id); + if (type && type->listId == id) + return type->typeId; else return 0; } @@ -1747,9 +1726,9 @@ int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMet QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType *type = data->metaObjectToType.value(mo); - if (type && type->attachedPropertiesFunction(engine)) - return type->attachedPropertiesId(engine); + QQmlType type(data->metaObjectToType.value(mo)); + if (type.attachedPropertiesFunction(engine)) + return type.attachedPropertiesId(engine); else return -1; } @@ -1760,7 +1739,7 @@ QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePr return 0; QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - return data->types.at(id)->attachedPropertiesFunction(engine); + return data->types.at(id).attachedPropertiesFunction(engine); } QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) @@ -1843,10 +1822,10 @@ const char *QQmlMetaType::interfaceIId(int userType) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType *type = data->idToType.value(userType); + QQmlType type(data->idToType.value(userType)); lock.unlock(); - if (type && type->isInterface() && type->typeId() == userType) - return type->interfaceIId(); + if (type.isInterface() && type.typeId() == userType) + return type.interfaceIId(); else return 0; } @@ -1903,7 +1882,7 @@ QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, { int slash = qualifiedName.indexOf(QLatin1Char('/')); if (slash <= 0) - return 0; + return QQmlType(); QHashedStringRef module(qualifiedName.constData(), slash); QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.length() - slash - 1); @@ -1923,9 +1902,10 @@ QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedString QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); while (it != data->nameToType.cend() && it.key() == name) { + QQmlType t(*it); // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty - if (version_major < 0 || module.isEmpty() || (*it)->availableInVersion(module, version_major,version_minor)) - return *(*it); + if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, version_major,version_minor)) + return t; ++it; } @@ -1941,7 +1921,7 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - return data->metaObjectToType.value(metaObject); + return QQmlType(data->metaObjectToType.value(metaObject)); } /*! @@ -1957,13 +1937,13 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); while (it != data->metaObjectToType.cend() && it.key() == metaObject) { - QQmlType *t = *it; - if (version_major < 0 || module.isEmpty() || t->availableInVersion(module, version_major,version_minor)) + QQmlType t(*it); + if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, version_major,version_minor)) return t; ++it; } - return 0; + return QQmlType(); } /*! @@ -1975,11 +1955,11 @@ QQmlType QQmlMetaType::qmlType(int userType) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType *type = data->idToType.value(userType); - if (type && type->typeId() == userType) - return type; + QQmlTypePrivate *type = data->idToType.value(userType); + if (type && type->typeId == userType) + return QQmlType(type); else - return 0; + return QQmlType(); } /*! @@ -1993,12 +1973,12 @@ QQmlType QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType *type = data->urlToType.value(url); - if (!type && includeNonFileImports) - type = data->urlToNonFileImportType.value(url); + QQmlType type(data->urlToType.value(url)); + if (!type.isValid() && includeNonFileImports) + type = QQmlType(data->urlToNonFileImportType.value(url)); - if (type && type->sourceUrl() == url) - return *type; + if (type.sourceUrl() == url) + return type; else return QQmlType(); } @@ -2014,7 +1994,7 @@ QQmlType QQmlMetaType::qmlTypeFromIndex(int idx) QQmlMetaTypeData *data = metaTypeData(); if (idx < 0 || idx >= data->types.count()) - return 0; + return QQmlType(); return data->types.at(idx); } @@ -2060,7 +2040,7 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min maxMinorVersion = qMax(maxMinorVersion, t.minorVersion()); types << t; } else { - types << 0; + types << QQmlType(); } metaObject = metaObject->superClass(); @@ -2165,7 +2145,8 @@ QList QQmlMetaType::qmlTypeNames() names.reserve(data->nameToType.count()); QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); while (it != data->nameToType.cend()) { - names += (*it)->qmlTypeName(); + QQmlType t(*it); + names += t.qmlTypeName(); ++it; } @@ -2175,18 +2156,22 @@ QList QQmlMetaType::qmlTypeNames() /*! Returns the list of registered QML types. */ -QList QQmlMetaType::qmlTypes() +QList QQmlMetaType::qmlTypes() { QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); + const QQmlMetaTypeData *data = metaTypeData(); + + QList types; + for (QQmlTypePrivate *t : data->nameToType) + types.append(QQmlType(t)); - return data->nameToType.values(); + return types; } /*! Returns the list of all registered types. */ -QList QQmlMetaType::qmlAllTypes() +QList QQmlMetaType::qmlAllTypes() { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -2203,9 +2188,10 @@ QList QQmlMetaType::qmlSingletonTypes() QQmlMetaTypeData *data = metaTypeData(); QList retn; - for (const auto type : qAsConst(data->nameToType)) { - if (type->isSingleton()) - retn.append(*type); + for (const auto t : qAsConst(data->nameToType)) { + QQmlType type(t); + if (type.isSingleton()) + retn.append(type); } return retn; } diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 43eda2b4f1..0077306a64 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -78,9 +78,9 @@ class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: static QList qmlTypeNames(); - static QList qmlTypes(); + static QList qmlTypes(); static QList qmlSingletonTypes(); - static QList qmlAllTypes(); + static QList qmlAllTypes(); static QQmlType qmlType(const QString &qualifiedName, int, int); static QQmlType qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int); @@ -148,10 +148,6 @@ public: explicit QQmlType(QQmlTypePrivate *priv); ~QQmlType(); - // ### get rid of these two again - QQmlType(QQmlType *otherPointer); - QQmlType &operator =(QQmlType *otherPointer); - bool operator ==(const QQmlType &other) const { return d == other.d; } @@ -238,7 +234,7 @@ public: int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const; int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const; - QQmlTypePrivate *handle() const { return d; } + QQmlTypePrivate *priv() const { return d; } static void refHandle(QQmlTypePrivate *priv); static void derefHandle(QQmlTypePrivate *priv); private: @@ -246,7 +242,6 @@ private: QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; friend class QQmlTypePrivate; - friend struct QQmlMetaTypeData; enum RegistrationType { CppType = 0, @@ -296,7 +291,7 @@ public: private: //Used by register functions and creates the QQmlTypeModule for them friend QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data); - friend void addTypeToData(QQmlType* type, QQmlMetaTypeData *data); + friend void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data); friend struct QQmlMetaTypeData; friend Q_QML_EXPORT void qmlClearTypeRegistrations(); friend class QQmlTypeModulePrivate; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 6fab7697f8..ba07b77c72 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -112,7 +112,7 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, c Scoped w(scope, engine->memoryManager->allocObject()); w->d()->mode = mode; w->d()->object = o; - w->d()->typePrivate = t.handle(); + w->d()->typePrivate = t.priv(); QQmlType::refHandle(w->d()->typePrivate); return w.asReturnedValue(); } -- cgit v1.2.3 From 4d393779e7a6ec09267f40420f39f8dc28ed7aef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Jun 2017 14:28:57 +0200 Subject: Remove unused variable QQmlTypeModulePrivate::types is not used (anymore?). Task-number: QTBUG-61536 Change-Id: Ia7af542e6969c167d1f05a460e0926398f56f380 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlmetatype.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 36d70f24c8..59ed86f31a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -133,7 +133,6 @@ public: void add(QQmlTypePrivate *); QStringHash > typeHash; - QList types; }; Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) -- cgit v1.2.3 From 48c09a85ce397979c7e706e3694c879ffe456e09 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Jun 2017 14:52:22 +0200 Subject: Remove unused types on engine destruction The QML engine destructor as well as trimComponentCache() do now clean out unused composite types that the engine registered internally. This helps avoid 'static' leaks, when more and more types would get registered by the engine. Task-number: QTBUG-61536 Change-Id: I5b32af4b88fbf626de8c0bfbaedb2285b09e3679 Reviewed-by: Lars Knoll --- src/qml/jsapi/qjsengine.cpp | 2 +- src/qml/qml/qqmlengine.cpp | 2 + src/qml/qml/qqmlimport.cpp | 4 +- src/qml/qml/qqmlmetatype.cpp | 251 ++++++++++++++++++++++++++++------------- src/qml/qml/qqmlmetatype_p.h | 39 ++++--- src/qml/qml/qqmltypeloader.cpp | 3 + 6 files changed, 202 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 35b4929c3a..d5b8b295a7 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -730,7 +730,7 @@ QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) QJSEnginePrivate::~QJSEnginePrivate() { - // ### FIXME: sweep unused QQmlType instances + QQmlMetaType::freeUnusedTypesAndCaches(); } void QJSEnginePrivate::addToDebugServer(QJSEngine *q) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 6f5dcc0746..3003d18efc 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -694,6 +694,8 @@ QQmlEnginePrivate::~QQmlEnginePrivate() if (incubationController) incubationController->d = 0; incubationController = 0; + QQmlMetaType::freeUnusedTypesAndCaches(); + for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { iter.value()->isRegisteredWithEngine = false; diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 89c7fd3214..e197efe72b 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -183,7 +183,7 @@ QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRe minorVersion, buf.constData() }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); + ret = QQmlMetaType::registerCompositeSingletonType(reg); } else { QQmlPrivate::RegisterCompositeType reg = { url, @@ -192,7 +192,7 @@ QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRe minorVersion, buf.constData() }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); + ret = QQmlMetaType::registerCompositeType(reg); } // This means that the type couldn't be found by URL, but could not be diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 59ed86f31a..efe6e1c3be 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -69,7 +69,9 @@ struct QQmlMetaTypeData { QQmlMetaTypeData(); ~QQmlMetaTypeData(); + void registerType(QQmlTypePrivate *priv); QList types; + QSet undeletableTypes; typedef QHash Ids; Ids idToType; typedef QHash Names; @@ -131,8 +133,10 @@ public: bool locked; void add(QQmlTypePrivate *); + void remove(const QQmlTypePrivate *type); - QStringHash > typeHash; + typedef QStringHash > TypeHash; + TypeHash typeHash; }; Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData) @@ -226,7 +230,7 @@ public: struct PropertyCacheByMinorVersion { - PropertyCacheByMinorVersion() : cache(nullptr), minorVersion(-1) {} + PropertyCacheByMinorVersion() : cache(Q_NULLPTR), minorVersion(-1) {} explicit PropertyCacheByMinorVersion(QQmlPropertyCache *pc, int ver) : cache(pc), minorVersion(ver) {} QQmlPropertyCachePtr cache; int minorVersion; @@ -236,6 +240,19 @@ public: void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache); }; +void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv) +{ + for (int i = 0; i < types.count(); ++i) { + if (!types.at(i).isValid()) { + types[i] = QQmlType(priv); + priv->index = i; + return; + } + } + types.append(QQmlType(priv)); + priv->index = types.count() - 1; +} + void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle()); @@ -347,21 +364,23 @@ QQmlTypePrivate::~QQmlTypePrivate() } } -QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) +QQmlType::QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &interface) : d(new QQmlTypePrivate(InterfaceType)) { d->iid = interface.iid; d->typeId = interface.typeId; d->listId = interface.listId; - d->index = index; d->isSetup = true; d->version_maj = 0; d->version_min = 0; + data->registerType(d); } -QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) +QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) : d(new QQmlTypePrivate(SingletonType)) { + data->registerType(d); + d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -377,8 +396,6 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->revision = type.revision; } - d->index = index; - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; @@ -387,25 +404,27 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0; } -QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type) +QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeSingletonType &type) : d(new QQmlTypePrivate(CompositeSingletonType)) { + data->registerType(d); + d->elementName = elementName; d->module = QString::fromUtf8(type.uri); d->version_maj = type.versionMajor; d->version_min = type.versionMinor; - d->index = index; - d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; d->extraData.sd->singletonInstanceInfo->url = type.url; d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); } -QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterType &type) +QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterType &type) : d(new QQmlTypePrivate(CppType)) { + data->registerType(d); + d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -424,7 +443,7 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg if (d->extraData.cd->attachedPropertiesType) { QHash::Iterator iter = d->attachedPropertyIds.find(d->baseMetaObject); if (iter == d->attachedPropertyIds.end()) - iter = d->attachedPropertyIds.insert(d->baseMetaObject, index); + iter = d->attachedPropertyIds.insert(d->baseMetaObject, d->index); d->extraData.cd->attachedPropertiesId = *iter; } else { d->extraData.cd->attachedPropertiesId = -1; @@ -434,16 +453,16 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; d->extraData.cd->extFunc = type.extensionObjectCreate; d->extraData.cd->customParser = type.customParser; - d->index = index; if (type.extensionMetaObject) d->extraData.cd->extMetaObject = type.extensionMetaObject; } -QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) +QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) : d(new QQmlTypePrivate(CompositeType)) { - d->index = index; + data->registerType(d); + d->elementName = elementName; d->module = QString::fromUtf8(type.uri); @@ -772,7 +791,7 @@ QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersio for (int i = 0; i < propertyCaches.count(); ++i) if (propertyCaches.at(i).minorVersion == minorVersion) return propertyCaches.at(i).cache; - return nullptr; + return Q_NULLPTR; } void QQmlTypePrivate::setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache) @@ -1123,6 +1142,27 @@ void QQmlType::derefHandle(QQmlTypePrivate *priv) delete priv; } +namespace { +template +void removeQQmlTypePrivate(QQmlTypeContainer &container, const QQmlTypePrivate *reference) +{ + for (typename QQmlTypeContainer::iterator it = container.begin(); it != container.end();) { + if (*it == reference) + it = container.erase(it); + else + ++it; + } +} + +struct IsQQmlTypePrivate +{ + const QQmlTypePrivate *reference; + explicit IsQQmlTypePrivate(const QQmlTypePrivate *ref) : reference(ref) {} + + bool operator()(const QQmlTypePrivate *priv) const { return reference == priv; } +}; +} + QQmlTypeModule::QQmlTypeModule() : d(new QQmlTypeModulePrivate) { @@ -1170,6 +1210,24 @@ void QQmlTypeModulePrivate::add(QQmlTypePrivate *type) list.append(type); } +void QQmlTypeModulePrivate::remove(const QQmlTypePrivate *type) +{ + for (TypeHash::ConstIterator elementIt = typeHash.begin(); elementIt != typeHash.end();) { + QList &list = typeHash[elementIt.key()]; + + removeQQmlTypePrivate(list, type); + +#if 0 + if (list.isEmpty()) + elementIt = typeHash.erase(elementIt); + else + ++elementIt; +#else + ++elementIt; +#endif + } +} + QQmlType QQmlTypeModule::type(const QHashedStringRef &name, int minor) const { QMutexLocker lock(metaTypeDataLock()); @@ -1269,7 +1327,7 @@ void qmlClearTypeRegistrations() // Declared in qqml.h #endif } -int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) +static int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); @@ -1279,7 +1337,7 @@ int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) return data->parentFunctions.count() - 1; } -int registerInterface(const QQmlPrivate::RegisterInterface &interface) +QQmlType registerInterface(const QQmlPrivate::RegisterInterface &interface) { if (interface.version > 0) qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); @@ -1287,13 +1345,10 @@ int registerInterface(const QQmlPrivate::RegisterInterface &interface) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - int index = data->types.count(); - - QQmlType type(index, interface); + QQmlType type(data, interface); QQmlTypePrivate *priv = type.priv(); Q_ASSERT(priv); - data->types.append(type); data->idToType.insert(priv->typeId, priv); data->idToType.insert(priv->listId, priv); // XXX No insertMulti, so no multi-version interfaces? @@ -1307,7 +1362,7 @@ int registerInterface(const QQmlPrivate::RegisterInterface &interface) data->interfaces.setBit(interface.typeId, true); data->lists.setBit(interface.listId, true); - return index; + return type; } QString registrationTypeString(QQmlType::RegistrationType typeType) @@ -1422,45 +1477,39 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) } } -int registerType(const QQmlPrivate::RegisterType &type) +QQmlType registerType(const QQmlPrivate::RegisterType &type) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); QString elementName = QString::fromUtf8(type.elementName); if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.versionMajor)) - return -1; - - int index = data->types.count(); + return QQmlType(); - QQmlType dtype(index, elementName, type); + QQmlType dtype(data, elementName, type); - data->types.append(dtype); addTypeToData(dtype.priv(), data); if (!type.typeId) data->idToType.insert(dtype.typeId(), dtype.priv()); - return index; + return dtype; } -int registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) +QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); QString typeName = QString::fromUtf8(type.typeName); if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.versionMajor)) - return -1; - - int index = data->types.count(); + return QQmlType(); - QQmlType dtype(index, typeName, type); + QQmlType dtype(data, typeName, type); - data->types.append(dtype); addTypeToData(dtype.priv(), data); - return index; + return dtype; } -int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) +QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. QMutexLocker lock(metaTypeDataLock()); @@ -1470,22 +1519,19 @@ int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingleton if (*(type.uri) == '\0') fileImport = true; if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? 0 : type.uri, typeName)) - return -1; - - int index = data->types.count(); + return QQmlType(); - QQmlType dtype(index, typeName, type); + QQmlType dtype(data, typeName, type); - data->types.append(dtype); addTypeToData(dtype.priv(), data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); files->insertMulti(type.url, dtype.priv()); - return index; + return dtype; } -int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) +QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) { // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. QMutexLocker lock(metaTypeDataLock()); @@ -1495,18 +1541,15 @@ int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) if (*(type.uri) == '\0') fileImport = true; if (!checkRegistration(QQmlType::CompositeType, data, fileImport?0:type.uri, typeName, type.versionMajor)) - return -1; - - int index = data->types.count(); + return QQmlType(); - QQmlType dtype(index, typeName, type); - data->types.append(dtype); + QQmlType dtype(data, typeName, type); addTypeToData(dtype.priv(), data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); files->insertMulti(type.url, dtype.priv()); - return index; + return dtype; } int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) @@ -1525,22 +1568,30 @@ the future without adding exported symbols. */ int QQmlPrivate::qmlregister(RegistrationType type, void *data) { - if (type == TypeRegistration) { - return registerType(*reinterpret_cast(data)); - } else if (type == InterfaceRegistration) { - return registerInterface(*reinterpret_cast(data)); - } else if (type == AutoParentRegistration) { + if (type == AutoParentRegistration) return registerAutoParentFunction(*reinterpret_cast(data)); - } else if (type == SingletonRegistration) { - return registerSingletonType(*reinterpret_cast(data)); - } else if (type == CompositeRegistration) { - return registerCompositeType(*reinterpret_cast(data)); - } else if (type == CompositeSingletonRegistration) { - return registerCompositeSingletonType(*reinterpret_cast(data)); - } else if (type == QmlUnitCacheHookRegistration) { + else if (type == QmlUnitCacheHookRegistration) return registerQmlUnitCacheHook(*reinterpret_cast(data)); - } - return -1; + + QQmlType dtype; + if (type == TypeRegistration) + dtype = registerType(*reinterpret_cast(data)); + else if (type == InterfaceRegistration) + dtype = registerInterface(*reinterpret_cast(data)); + else if (type == SingletonRegistration) + dtype = registerSingletonType(*reinterpret_cast(data)); + else if (type == CompositeRegistration) + dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast(data)); + else if (type == CompositeSingletonRegistration) + dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast(data)); + else + return -1; + + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *typeData = metaTypeData(); + typeData->undeletableTypes.insert(dtype); + + return dtype.index(); } //From qqml.h @@ -1982,21 +2033,6 @@ QQmlType QQmlMetaType::qmlType(const QUrl &url, bool includeNonFileImports /* = return QQmlType(); } -/*! - Returns the type (if any) with the given \a index in the global type store. - This is for use when you just got the index back from a qmlRegister function. - Returns null if the index is out of bounds. -*/ -QQmlType QQmlMetaType::qmlTypeFromIndex(int idx) -{ - QMutexLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - - if (idx < 0 || idx >= data->types.count()) - return QQmlType(); - return data->types.at(idx); -} - QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QMetaObject *metaObject) { if (QQmlPropertyCache *rv = propertyCaches.value(metaObject)) @@ -2132,6 +2168,61 @@ QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVe return data->propertyCache(type, minorVersion); } +void QQmlMetaType::freeUnusedTypesAndCaches() +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + { + bool deletedAtLeastOneType; + do { + deletedAtLeastOneType = false; + QList::Iterator it = data->types.begin(); + while (it != data->types.end()) { + const QQmlTypePrivate *d = (*it).priv(); + if (d && d->refCount == 1) { + deletedAtLeastOneType = true; + + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + + for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { + QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); + modulePrivate->remove(d); + } + + *it = QQmlType(); + } else { + ++it; + } + } + } while (deletedAtLeastOneType); + } + + { + bool deletedAtLeastOneCache; + do { + deletedAtLeastOneCache = false; + QHash::Iterator it = data->propertyCaches.begin(); + while (it != data->propertyCaches.end()) { + + if ((*it)->count() == 1) { + QQmlPropertyCache *pc = Q_NULLPTR; + qSwap(pc, *it); + it = data->propertyCaches.erase(it); + pc->release(); + deletedAtLeastOneCache = true; + } else { + ++it; + } + } + } while (deletedAtLeastOneCache); + } +} + /*! Returns the list of registered QML type names. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 0077306a64..c0fbdd5060 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -77,6 +77,9 @@ namespace QV4 { struct String; } class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: + static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type); + static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type); + static QList qmlTypeNames(); static QList qmlTypes(); static QList qmlSingletonTypes(); @@ -88,11 +91,12 @@ public: static QQmlType qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor); static QQmlType qmlType(int); static QQmlType qmlType(const QUrl &url, bool includeNonFileImports = false); - static QQmlType qmlTypeFromIndex(int); static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); static QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); + static void freeUnusedTypesAndCaches(); + static QMetaProperty defaultProperty(const QMetaObject *); static QMetaProperty defaultProperty(QObject *); static QMetaMethod defaultMethod(const QMetaObject *); @@ -237,11 +241,6 @@ public: QQmlTypePrivate *priv() const { return d; } static void refHandle(QQmlTypePrivate *priv); static void derefHandle(QQmlTypePrivate *priv); -private: - QQmlType superType() const; - QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - friend class QQmlTypePrivate; enum RegistrationType { CppType = 0, @@ -250,21 +249,28 @@ private: CompositeType = 3, CompositeSingletonType = 4 }; + +private: + QQmlType superType() const; + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; + friend class QQmlTypePrivate; + friend QString registrationTypeString(RegistrationType); friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &, int); - friend int registerType(const QQmlPrivate::RegisterType &); - friend int registerSingletonType(const QQmlPrivate::RegisterSingletonType &); - friend int registerInterface(const QQmlPrivate::RegisterInterface &); - friend int registerCompositeType(const QQmlPrivate::RegisterCompositeType &); - friend int registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &); + friend QQmlType registerType(const QQmlPrivate::RegisterType &); + friend QQmlType registerSingletonType(const QQmlPrivate::RegisterSingletonType &); + friend QQmlType registerInterface(const QQmlPrivate::RegisterInterface &); friend int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &); friend uint qHash(const QQmlType &t, uint seed); friend Q_QML_EXPORT void qmlClearTypeRegistrations(); - QQmlType(int, const QQmlPrivate::RegisterInterface &); - QQmlType(int, const QString &, const QQmlPrivate::RegisterSingletonType &); - QQmlType(int, const QString &, const QQmlPrivate::RegisterType &); - QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeType &); - QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &); + friend class QQmlMetaType; + + QQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &); + QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterSingletonType &); + QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterType &); + QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeType &); + QQmlType(QQmlMetaTypeData *data, const QString &, const QQmlPrivate::RegisterCompositeSingletonType &); QQmlTypePrivate *d; }; @@ -288,6 +294,7 @@ public: QQmlType type(const QHashedStringRef &, int) const; QQmlType type(const QV4::String *, int) const; + QQmlTypeModulePrivate *priv() { return d; } private: //Used by register functions and creates the QQmlTypeModule for them friend QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index fbb04af165..2825e0d698 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1947,6 +1947,7 @@ void QQmlTypeLoader::clearCache() m_qmldirCache.clear(); m_importDirCache.clear(); m_importQmlDirCache.clear(); + QQmlMetaType::freeUnusedTypesAndCaches(); } void QQmlTypeLoader::updateTypeCacheTrimThreshold() @@ -1988,6 +1989,8 @@ void QQmlTypeLoader::trimCache() updateTypeCacheTrimThreshold(); + QQmlMetaType::freeUnusedTypesAndCaches(); + // TODO: release any scripts which are no longer referenced by any types } -- cgit v1.2.3 From d621573d121348fed943dfe73ec9a89b27a92e52 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 27 Jun 2017 08:55:01 +0200 Subject: Move the m_qmlLists member in the QML engine to QQmlMetaType This is a pure metatype id to metatype id mapping, nothing in there is specific to a QML engine instance. Thus move the mapping to QQmlMetaType and make it global for all engines. Task-number: QTBUG-61536 Change-Id: I3792567bc9f585e3e0fbbad94efd1ec3a0db3de0 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 10 ++++--- src/qml/qml/qqmlengine.cpp | 47 +++++--------------------------- src/qml/qml/qqmlengine_p.h | 1 - src/qml/qml/qqmlmetatype.cpp | 52 +++++++++++++++++++++++++++++++++++- src/qml/qml/qqmlmetatype_p.h | 4 +++ 5 files changed, 67 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 929339b70d..72b2c3fd07 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -211,8 +211,9 @@ void CompilationUnit::unlink() if (isRegisteredWithEngine) { Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); - QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine); - qmlEngine->unregisterInternalCompositeType(this); + if (engine) + QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this); + QQmlMetaType::unregisterInternalCompositeType(this); isRegisteredWithEngine = false; } @@ -285,9 +286,10 @@ IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjec void CompilationUnit::finalize(QQmlEnginePrivate *engine) { // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) + if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) { + QQmlMetaType::registerInternalCompositeType(this); engine->registerInternalCompositeType(this); - else { + } else { const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject); auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 3003d18efc..9676feb279 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2215,26 +2215,17 @@ QQmlMetaType::TypeCategory QQmlEnginePrivate::typeCategory(int t) const Locker locker(this); if (m_compositeTypes.contains(t)) return QQmlMetaType::Object; - else if (m_qmlLists.contains(t)) - return QQmlMetaType::List; - else - return QQmlMetaType::typeCategory(t); + return QQmlMetaType::typeCategory(t); } bool QQmlEnginePrivate::isList(int t) const { - Locker locker(this); - return m_qmlLists.contains(t) || QQmlMetaType::isList(t); + return QQmlMetaType::isList(t); } int QQmlEnginePrivate::listType(int t) const { - Locker locker(this); - QHash::ConstIterator iter = m_qmlLists.constFind(t); - if (iter != m_qmlLists.cend()) - return *iter; - else - return QQmlMetaType::listType(t); + return QQmlMetaType::listType(t); } QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const @@ -2289,46 +2280,20 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - QByteArray name = compilationUnit->rootPropertyCache()->className(); - - QByteArray ptr = name + '*'; - QByteArray lst = "QQmlListProperty<" + name + '>'; - - int ptr_type = QMetaType::registerNormalizedType(ptr, - QtMetaTypePrivate::QMetaTypeFunctionHelper::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper::Construct, - sizeof(QObject*), - static_cast >(QtPrivate::QMetaTypeTypeFlags::Flags), - 0); - int lst_type = QMetaType::registerNormalizedType(lst, - QtMetaTypePrivate::QMetaTypeFunctionHelper >::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper >::Construct, - sizeof(QQmlListProperty), - static_cast >(QtPrivate::QMetaTypeTypeFlags >::Flags), - static_cast(0)); - - compilationUnit->metaTypeId = ptr_type; - compilationUnit->listMetaTypeId = lst_type; compilationUnit->isRegisteredWithEngine = true; Locker locker(this); - m_qmlLists.insert(lst_type, ptr_type); // The QQmlCompiledData is not referenced here, but it is removed from this // hash in the QQmlCompiledData destructor - m_compositeTypes.insert(ptr_type, compilationUnit); + m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit); } void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) { - int ptr_type = compilationUnit->metaTypeId; - int lst_type = compilationUnit->listMetaTypeId; + compilationUnit->isRegisteredWithEngine = false; Locker locker(this); - m_qmlLists.remove(lst_type); - m_compositeTypes.remove(ptr_type); - - QMetaType::unregisterType(ptr_type); - QMetaType::unregisterType(lst_type); + m_compositeTypes.remove(compilationUnit->metaTypeId); } bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 48c865abe5..da8ea24ea0 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -261,7 +261,6 @@ public: private: // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. - QHash m_qmlLists; QHash m_compositeTypes; static bool s_designerMode; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index efe6e1c3be..ca0522aa99 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -113,6 +113,8 @@ struct QQmlMetaTypeData QString typeRegistrationNamespace; QStringList typeRegistrationFailures; + QHash qmlLists; + QHash propertyCaches; QQmlPropertyCache *propertyCache(const QMetaObject *metaObject); QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion); @@ -1552,6 +1554,47 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit return dtype; } +void QQmlMetaType::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +{ + QByteArray name = compilationUnit->rootPropertyCache()->className(); + + QByteArray ptr = name + '*'; + QByteArray lst = "QQmlListProperty<" + name + '>'; + + int ptr_type = QMetaType::registerNormalizedType(ptr, + QtMetaTypePrivate::QMetaTypeFunctionHelper::Destruct, + QtMetaTypePrivate::QMetaTypeFunctionHelper::Construct, + sizeof(QObject*), + static_cast >(QtPrivate::QMetaTypeTypeFlags::Flags), + 0); + int lst_type = QMetaType::registerNormalizedType(lst, + QtMetaTypePrivate::QMetaTypeFunctionHelper >::Destruct, + QtMetaTypePrivate::QMetaTypeFunctionHelper >::Construct, + sizeof(QQmlListProperty), + static_cast >(QtPrivate::QMetaTypeTypeFlags >::Flags), + static_cast(0)); + + compilationUnit->metaTypeId = ptr_type; + compilationUnit->listMetaTypeId = lst_type; + + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *d = metaTypeData(); + d->qmlLists.insert(lst_type, ptr_type); +} + +void QQmlMetaType::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit) +{ + int ptr_type = compilationUnit->metaTypeId; + int lst_type = compilationUnit->listMetaTypeId; + + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *d = metaTypeData(); + d->qmlLists.remove(lst_type); + + QMetaType::unregisterType(ptr_type); + QMetaType::unregisterType(lst_type); +} + int registerQmlUnitCacheHook(const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) { if (hookRegistration.version > 0) @@ -1764,6 +1807,9 @@ int QQmlMetaType::listType(int id) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); + QHash::ConstIterator iter = data->qmlLists.constFind(id); + if (iter != data->qmlLists.cend()) + return *iter; QQmlTypePrivate *type = data->idToType.value(id); if (type && type->listId == id) return type->typeId; @@ -1853,7 +1899,9 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - if (userType < data->objects.size() && data->objects.testBit(userType)) + if (data->qmlLists.contains(userType)) + return List; + else if (userType < data->objects.size() && data->objects.testBit(userType)) return Object; else if (userType < data->lists.size() && data->lists.testBit(userType)) return List; @@ -1884,6 +1932,8 @@ bool QQmlMetaType::isList(int userType) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); + if (data->qmlLists.contains(userType)) + return true; return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); } diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index c0fbdd5060..ac2133ba30 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -71,6 +71,7 @@ class QHashedString; class QHashedStringRef; class QMutex; class QQmlPropertyCache; +class QQmlCompiledData; namespace QV4 { struct String; } @@ -80,6 +81,9 @@ public: static QQmlType registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type); static QQmlType registerCompositeType(const QQmlPrivate::RegisterCompositeType &type); + static void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + static void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); + static QList qmlTypeNames(); static QList qmlTypes(); static QList qmlSingletonTypes(); -- cgit v1.2.3 From d28e7e47e0ddcbb889ced731f8666996caf045b1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 26 Jun 2017 11:49:34 +0200 Subject: Fix void * usage in our internal APIs Task-number: QTBUG-61536 Change-Id: Ia2b5cfeab093d8be91728032528788dd238c2872 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 16 +++++------ src/qml/compiler/qv4jsir_p.h | 9 ++++-- src/qml/qml/qqmlimport.cpp | 3 +- src/qml/qml/qqmltypenamecache.cpp | 32 ++++++++++----------- src/qml/qml/qqmltypenamecache_p.h | 57 ++++++++++++++++++-------------------- src/qml/qml/qqmltypewrapper.cpp | 2 +- src/qml/qml/qqmltypewrapper_p.h | 4 +-- 7 files changed, 60 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 5bbf067320..8db054c91f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1749,7 +1749,7 @@ static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, con resolver->resolveMember = &resolveQmlType; resolver->qmlType = qmlType; - resolver->extraData = 0; + resolver->typenameCache = 0; resolver->flags = 0; } @@ -1758,8 +1758,8 @@ static QV4::IR::DiscoveredType resolveImportNamespace( QV4::IR::Member *member) { QV4::IR::Type result = QV4::IR::VarType; - QQmlTypeNameCache *typeNamespace = static_cast(resolver->extraData); - void *importNamespace = resolver->data; + QQmlTypeNameCache *typeNamespace = resolver->typenameCache; + const QQmlImportRef *importNamespace = resolver->import; QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace); if (r.isValid()) { @@ -1786,11 +1786,11 @@ static QV4::IR::DiscoveredType resolveImportNamespace( } static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver, - QQmlTypeNameCache *imports, const void *importNamespace) + QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace) { resolver->resolveMember = &resolveImportNamespace; - resolver->data = const_cast(importNamespace); - resolver->extraData = imports; + resolver->import = importNamespace; + resolver->typenameCache = imports; resolver->flags = 0; } @@ -1799,7 +1799,7 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty( QV4::IR::Member *member) { QV4::IR::Type result = QV4::IR::VarType; - QQmlPropertyCache *metaObject = static_cast(resolver->data); + QQmlPropertyCache *metaObject = resolver->propertyCache; if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) { const QMetaObject *mo = metaObject->createMetaObject(); @@ -1881,7 +1881,7 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, Q_ASSERT(resolver); resolver->resolveMember = &resolveMetaObjectProperty; - resolver->data = metaObject; + resolver->propertyCache = metaObject; resolver->flags = 0; } diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index e9344a31fb..5c8e79f404 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -80,6 +80,8 @@ class QQmlType; class QQmlPropertyData; class QQmlPropertyCache; class QQmlEnginePrivate; +struct QQmlImportRef; +class QQmlTypeNameCache; namespace QV4 { @@ -251,7 +253,7 @@ struct MemberExpressionResolver Member *member); MemberExpressionResolver() - : resolveMember(0), data(0), extraData(0), owner(nullptr), flags(0) {} + : resolveMember(0), import(nullptr), propertyCache(nullptr), typenameCache(nullptr), owner(nullptr), flags(0) {} bool isValid() const { return !!resolveMember; } void clear() { *this = MemberExpressionResolver(); } @@ -260,8 +262,9 @@ struct MemberExpressionResolver #ifndef V4_BOOTSTRAP QQmlType qmlType; #endif - void *data; // Could be pointer to meta object or importNameSpace - void *extraData; // Could be QQmlTypeNameCache + const QQmlImportRef *import; + QQmlPropertyCache *propertyCache; + QQmlTypeNameCache *typenameCache; Function *owner; unsigned int flags; }; diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index e197efe72b..0bd7317470 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -420,13 +420,14 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const const QQmlImportNamespace &set = *ns; // positioning is important; we must create the namespace even if there is no module. - QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; + QQmlImportRef &typeimport = cache->m_namedImports[set.prefix]; typeimport.m_qualifier = set.prefix; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { + QQmlImportRef &typeimport = cache->m_namedImports[set.prefix]; typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion)); } } diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index a8f55d2e48..72307c2800 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -55,7 +55,7 @@ QQmlTypeNameCache::~QQmlTypeNameCache() void QQmlTypeNameCache::add(const QHashedString &name, const QUrl &url, const QHashedString &nameSpace) { if (nameSpace.length() != 0) { - Import *i = m_namedImports.value(nameSpace); + QQmlImportRef *i = m_namedImports.value(nameSpace); Q_ASSERT(i != 0); i->compositeSingletons.insert(name, url); return; @@ -69,12 +69,12 @@ void QQmlTypeNameCache::add(const QHashedString &name, const QUrl &url, const QH void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, const QHashedString &nameSpace) { - Import import; + QQmlImportRef import; import.scriptIndex = importedScriptIndex; import.m_qualifier = name; if (nameSpace.length() != 0) { - Import *i = m_namedImports.value(nameSpace); + QQmlImportRef *i = m_namedImports.value(nameSpace); Q_ASSERT(i != 0); m_namespacedImports[i].insert(name, import); return; @@ -112,22 +112,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) } QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, - const void *importNamespace) const + const QQmlImportRef *importNamespace) const { - Q_ASSERT(importNamespace); - const Import *i = static_cast(importNamespace); - Q_ASSERT(i->scriptIndex == -1); + Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1); - Result result = typeSearch(i->modules, name); + Result result = typeSearch(importNamespace->modules, name); if (!result.isValid()) - result = query(i->compositeSingletons, name); + result = query(importNamespace->compositeSingletons, name); if (!result.isValid()) { // Look up types from the imports of this document // ### it would be nice if QQmlImports allowed us to resolve a namespace // first, and then types on it. - QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name.toString(); + QString qualifiedTypeName = importNamespace->m_qualifier + QLatin1Char('.') + name.toString(); QQmlImportNamespace *typeNamespace = 0; QList errors; QQmlType t; @@ -166,29 +164,27 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons return result; } -QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, const void *importNamespace) const +QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, const QQmlImportRef *importNamespace) const { - Q_ASSERT(importNamespace); - const Import *i = static_cast(importNamespace); - Q_ASSERT(i->scriptIndex == -1); + Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1); - QMap >::const_iterator it = m_namespacedImports.constFind(i); + QMap >::const_iterator it = m_namespacedImports.constFind(importNamespace); if (it != m_namespacedImports.constEnd()) { Result r = query(*it, name); if (r.isValid()) return r; } - Result r = typeSearch(i->modules, name); + Result r = typeSearch(importNamespace->modules, name); if (!r.isValid()) - r = query(i->compositeSingletons, name); + r = query(importNamespace->compositeSingletons, name); if (!r.isValid()) { // Look up types from the imports of this document // ### it would be nice if QQmlImports allowed us to resolve a namespace // first, and then types on it. - QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); + QString qualifiedTypeName = importNamespace->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); QQmlImportNamespace *typeNamespace = 0; QList errors; QQmlType t; diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 0705166ec2..7e2cbec4b5 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -62,6 +62,23 @@ QT_BEGIN_NAMESPACE +struct QQmlImportRef { + inline QQmlImportRef() + : scriptIndex(-1) + {} + // Imported module + QVector modules; + + // Or, imported script + int scriptIndex; + + // Or, imported compositeSingletons + QStringHash compositeSingletons; + + // The qualifier of this import + QString m_qualifier; +}; + class QQmlType; class QQmlEngine; class QQmlTypeNameCache : public QQmlRefCount @@ -77,7 +94,7 @@ public: struct Result { inline Result(); - inline Result(const void *importNamespace); + inline Result(const QQmlImportRef *importNamespace); inline Result(const QQmlType &type); inline Result(int scriptIndex); inline Result(const Result &); @@ -85,42 +102,27 @@ public: inline bool isValid() const; QQmlType type; - const void *importNamespace; + const QQmlImportRef *importNamespace; int scriptIndex; }; Result query(const QHashedStringRef &) const; - Result query(const QHashedStringRef &, const void *importNamespace) const; + Result query(const QHashedStringRef &, const QQmlImportRef *importNamespace) const; Result query(const QV4::String *) const; - Result query(const QV4::String *, const void *importNamespace) const; + Result query(const QV4::String *, const QQmlImportRef *importNamespace) const; private: friend class QQmlImports; - struct Import { - inline Import(); - // Imported module - QVector modules; - - // Or, imported script - int scriptIndex; - - // Or, imported compositeSingletons - QStringHash compositeSingletons; - - // The qualifier of this import - QString m_qualifier; - }; - template - Result query(const QStringHash &imports, Key key) const + Result query(const QStringHash &imports, Key key) const { - Import *i = imports.value(key); + QQmlImportRef *i = imports.value(key); if (i) { Q_ASSERT(!i->m_qualifier.isEmpty()); if (i->scriptIndex != -1) { return Result(i->scriptIndex); } else { - return Result(static_cast(i)); + return Result(i); } } @@ -152,8 +154,8 @@ private: return Result(); } - QStringHash m_namedImports; - QMap > m_namespacedImports; + QStringHash m_namedImports; + QMap > m_namespacedImports; QVector m_anonymousImports; QStringHash m_anonymousCompositeSingletons; QQmlImports m_imports; @@ -164,7 +166,7 @@ QQmlTypeNameCache::Result::Result() { } -QQmlTypeNameCache::Result::Result(const void *importNamespace) +QQmlTypeNameCache::Result::Result(const QQmlImportRef *importNamespace) : importNamespace(importNamespace), scriptIndex(-1) { } @@ -189,11 +191,6 @@ bool QQmlTypeNameCache::Result::isValid() const return type.isValid() || importNamespace || scriptIndex != -1; } -QQmlTypeNameCache::Import::Import() -: scriptIndex(-1) -{ -} - bool QQmlTypeNameCache::isEmpty() const { return m_namedImports.isEmpty() && m_anonymousImports.isEmpty() diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index ba07b77c72..d4e1910a72 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -119,7 +119,7 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, c // Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a // namespace. -ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlTypeNameCache *t, const void *importNamespace, +ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlTypeNameCache *t, const QQmlImportRef *importNamespace, Heap::QmlTypeWrapper::TypeNameMode mode) { Q_ASSERT(t); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index c21a66424b..c06c485fb8 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -80,7 +80,7 @@ struct QmlTypeWrapper : Object { QQmlTypePrivate *typePrivate; QQmlTypeNameCache *typeNamespace; - const void *importNamespace; + const QQmlImportRef *importNamespace; }; } @@ -97,7 +97,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &, Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); - static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const void *, + static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const QQmlImportRef *, Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); -- cgit v1.2.3 From a4be64a06c94765f995d49be00eb8fc9bed83958 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 29 Jun 2017 14:56:05 +0200 Subject: Reduce memory consumption of stat() cache in QML type loader The m_importDirCache caches the result of QDir/File::exists across paths. By changing the data structure to a QCache we impose a limit on the size of the entries and thus the memory consumption for long running programs that access many qml files. Task-number: QTBUG-61536 Change-Id: I20c4dc77260fe353c13da570b7e1bbd3f47d4b79 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 40 ++++++++++++++++++---------------------- src/qml/qml/qqmltypeloader_p.h | 4 ++-- 2 files changed, 20 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2825e0d698..efa4ed2f79 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1778,23 +1778,21 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) #endif int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringRef dirPath(&path, 0, lastSlash); + QString dirPath(path.left(lastSlash)); - StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); - if (!fileSet) { - QHashedString dirPathString(dirPath.toString()); - bool exists = QDir(dirPathString).exists(); - QStringHash *files = exists ? new QStringHash : 0; - m_importDirCache.insert(dirPathString, files); - fileSet = m_importDirCache.value(dirPathString); + if (!m_importDirCache.contains(dirPath)) { + bool exists = QDir(dirPath).exists(); + QCache *entry = exists ? new QCache : 0; + m_importDirCache.insert(dirPath, entry); } - if (!(*fileSet)) + QCache *fileSet = m_importDirCache.object(dirPath); + if (!fileSet) return QString(); QString absoluteFilePath; - QHashedStringRef fileName(path.constData()+lastSlash+1, path.length()-lastSlash-1); + QString fileName(path.mid(lastSlash+1, path.length()-lastSlash-1)); - bool *value = (*fileSet)->value(fileName); + bool *value = fileSet->object(fileName); if (value) { if (*value) absoluteFilePath = path; @@ -1808,7 +1806,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) #else exists = QFile::exists(path); #endif - (*fileSet)->insert(fileName.toString(), exists); + fileSet->insert(fileName, new bool(exists)); if (exists) absoluteFilePath = path; } @@ -1843,18 +1841,16 @@ bool QQmlTypeLoader::directoryExists(const QString &path) int length = path.length(); if (path.endsWith(QLatin1Char('/'))) --length; - QStringRef dirPath(&path, 0, length); + QString dirPath(path.left(length)); - StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); - if (!fileSet) { - QHashedString dirPathString(dirPath.toString()); - bool exists = QDir(dirPathString).exists(); - QStringHash *files = exists ? new QStringHash : 0; - m_importDirCache.insert(dirPathString, files); - fileSet = m_importDirCache.value(dirPathString); + if (!m_importDirCache.contains(dirPath)) { + bool exists = QDir(dirPath).exists(); + QCache *files = exists ? new QCache : 0; + m_importDirCache.insert(dirPath, files); } - return (*fileSet); + QCache *fileSet = m_importDirCache.object(dirPath); + return fileSet != 0; } @@ -1938,7 +1934,7 @@ void QQmlTypeLoader::clearCache() (*iter)->release(); for (QmldirCache::Iterator iter = m_qmldirCache.begin(), end = m_qmldirCache.end(); iter != end; ++iter) (*iter)->release(); - qDeleteAll(m_importDirCache); + qDeleteAll(m_importQmlDirCache); m_typeCache.clear(); diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 762fcdac65..05923f77e8 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -55,6 +55,7 @@ #include #include #include +#include #if QT_CONFIG(qml_network) #include #endif @@ -362,8 +363,7 @@ private: typedef QHash TypeCache; typedef QHash ScriptCache; typedef QHash QmldirCache; - typedef QStringHash StringSet; - typedef QStringHash ImportDirCache; + typedef QCache > ImportDirCache; typedef QStringHash ImportQmlDirCache; QQmlEngine *m_engine; -- cgit v1.2.3 From 2aabfa28a5fa4a7a0a5c5f47872df19f84551fab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Jul 2017 11:42:45 +0200 Subject: Avoid meta-object creation when looking up QML properties in the property cache When looking up QML properties in the property cache, we also need to verify that they should be visible in the QML context they are requested from, i.e. if the revision of the object in that context has the property visible or not. That requires retrieving the VME meta-object for QML declared properties. We can retrieve that directly from the QObjectPrivate instead of going through the potential meta-object builder creation path. Task-number: QTBUG-61536 Change-Id: I695921d625169de5c58941087464beb5367fb2db Reviewed-by: Lars Knoll --- src/qml/qml/qqmlpropertycache.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 9a495e6fa3..019fa654d1 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -764,7 +764,11 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const { QQmlData *data = (object ? QQmlData::get(object) : 0); - const QQmlVMEMetaObject *vmemo = (data && data->hasVMEMetaObject ? static_cast(object->metaObject()) : 0); + const QQmlVMEMetaObject *vmemo = 0; + if (data && data->hasVMEMetaObject) { + QObjectPrivate *op = QObjectPrivate::get(object); + vmemo = static_cast(op->metaObject); + } return findProperty(it, vmemo, context); } @@ -808,9 +812,9 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, } if (vmemo) { - const int methodCount = vmemo->methodCount(); - const int signalCount = vmemo->signalCount(); - const int propertyCount = vmemo->propertyCount(); + const int methodCount = vmemo->cache->methodCount(); + const int signalCount = vmemo->cache->signalCount(); + const int propertyCount = vmemo->cache->propertyCount(); // Ensure that the property we resolve to is accessible from this meta-object do { -- cgit v1.2.3 From 3eb90a1fa996e42647e073dc8e1b7fcf9a68a8d2 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 5 Dec 2016 11:26:20 +0100 Subject: Doc: Mention license in Qt Quick documentation Change-Id: Ib7cb0781799191636a13aba9d357313a860876e9 Reviewed-by: Simon Hausmann --- src/quick/doc/src/qtquick.qdoc | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc index 4bdd02241d..ede1eb19ac 100644 --- a/src/quick/doc/src/qtquick.qdoc +++ b/src/quick/doc/src/qtquick.qdoc @@ -86,6 +86,14 @@ To find out more about using the QML language, see the \l{Qt QML} module documen \endlist \endlist +\section1 Licenses and Attributions + +Qt Quick is available under commercial licenses from \l{The Qt Company}. +In addition, it is available under the +\l{GNU Lesser General Public License, version 3}, or +the \l{GNU General Public License, version 2}. +See \l{Qt Licensing} for further details. + \section1 Reference Documentation Additional Qt Quick information: -- cgit v1.2.3 From 6f3108b7b14a7a2844539b61ab357f8ee6d3c5d4 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Thu, 8 Jun 2017 11:26:10 +0200 Subject: Add tabStopDistance property to QQuickTextEdit This change adds a new property to QQuickTextEdit, tabStopDistance, to control the distance between tab stops in the underlying QTextDocument. [ChangeLog][QtQuick][QQuickTextEdit] Added tabStopDistance property Change-Id: Ib75838e9765e44c74e5055a738d2a0464a341509 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktextedit.cpp | 26 ++++++++++++++++++++++++++ src/quick/items/qquicktextedit_p.h | 5 +++++ 2 files changed, 31 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index aec8666dc4..f22c5f00be 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -3050,6 +3050,32 @@ void QQuickTextEdit::resetBottomPadding() d->setBottomPadding(0, true); } +/*! + \qmlproperty real QtQuick::TextEdit::tabStopDistance + \since 5.10 + + The default distance, in device units, between tab stops. + + \sa QTextOption::setTabStop() +*/ +int QQuickTextEdit::tabStopDistance() const +{ + Q_D(const QQuickTextEdit); + return d->document->defaultTextOption().tabStop(); +} + +void QQuickTextEdit::setTabStopDistance(qreal distance) +{ + Q_D(QQuickTextEdit); + QTextOption textOptions = d->document->defaultTextOption(); + if (textOptions.tabStop() == distance) + return; + + textOptions.setTabStop(distance); + d->document->setDefaultTextOption(textOptions); + emit tabStopDistanceChanged(distance); +} + /*! \qmlmethod QtQuick::TextEdit::clear() \since 5.7 diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index c8d3515be1..23033edb88 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -111,6 +111,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged REVISION 6) Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6) Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION 7) + Q_PROPERTY(qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged REVISION 10) public: QQuickTextEdit(QQuickItem *parent=0); @@ -296,6 +297,9 @@ public: void setBottomPadding(qreal padding); void resetBottomPadding(); + int tabStopDistance() const; + void setTabStopDistance(qreal distance); + Q_SIGNALS: void textChanged(); Q_REVISION(7) void preeditTextChanged(); @@ -340,6 +344,7 @@ Q_SIGNALS: Q_REVISION(6) void leftPaddingChanged(); Q_REVISION(6) void rightPaddingChanged(); Q_REVISION(6) void bottomPaddingChanged(); + Q_REVISION(10) void tabStopDistanceChanged(qreal distance); public Q_SLOTS: void selectAll(); -- cgit v1.2.3 From a253824a258c437bf1c8c67aefd6f5f928c9dfd7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Jun 2017 11:25:18 +0200 Subject: Improve releasing of memory allocated from compilation unit strings Allocate the strings in the compilation unit as regular strings, not as identifiers. We mark the runtimeStrings in the compilation unit, so when the unit is released as part of component cache trimming, those strings can also be collected. The JS object literal class keys have to remain identifiers though. However this is just a stop-gap as the real problem is that the identifier table can be triggered to grow without bounds. Task-number: QTBUG-61536 Change-Id: I7a2854b7fa9c9953348b5e34a31833f7be67cfbf Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata.cpp | 5 +++-- src/qml/jsruntime/qv4function.cpp | 7 +++---- src/qml/jsruntime/qv4lookup.cpp | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 72b2c3fd07..8ca1a29b2a 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #endif #include #include @@ -127,7 +128,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) // memset the strings to 0 in case a GC run happens while we're within the loop below memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::Heap::String*)); for (uint i = 0; i < data->stringTableSize; ++i) - runtimeStrings[i] = engine->newIdentifier(data->stringAt(i)); + runtimeStrings[i] = engine->newString(data->stringAt(i)); runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; // memset the regexps to 0 in case a GC run happens while we're within the loop below @@ -180,7 +181,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount); QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object]; for (int j = 0; j < memberCount; ++j, ++member) - klass = klass->addMember(runtimeStrings[member->nameOffset]->identifier, member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); + klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data); runtimeClasses[i] = klass; } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 31b57b97e9..f78555dbda 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -44,6 +44,7 @@ #include "qv4engine_p.h" #include "qv4lookup_p.h" #include +#include QT_BEGIN_NAMESPACE @@ -57,8 +58,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, , codeData(0) , hasQmlDependencies(function->hasQmlDependencies()) { - Q_UNUSED(engine); - internalClass = engine->internalClasses[EngineBase::Class_Empty]; const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names @@ -81,7 +80,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); + internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); activationRequired = compiledFunction->nInnerFunctions > 0 || (compiledFunction->flags & (CompiledData::Function::HasDirectEval | CompiledData::Function::UsesArgumentsObject)); @@ -116,7 +115,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QListlocalsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); + internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); activationRequired = true; } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index faaa5539ab..7a158ece35 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -40,6 +40,7 @@ #include "qv4functionobject_p.h" #include "qv4scopedvalue_p.h" #include "qv4string_p.h" +#include QT_BEGIN_NAMESPACE @@ -49,7 +50,7 @@ using namespace QV4; ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttributes *attrs) { ExecutionEngine *engine = o->engine(); - Identifier *name = engine->current->compilationUnit->runtimeStrings[nameIndex]->identifier; + Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); int i = 0; Heap::Object *obj = o->d(); while (i < Size && obj) { @@ -85,7 +86,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs { Heap::Object *obj = thisObject->d(); ExecutionEngine *engine = thisObject->engine(); - Identifier *name = engine->current->compilationUnit->runtimeStrings[nameIndex]->identifier; + Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); int i = 0; while (i < Size && obj) { classList[i] = obj->internalClass; -- cgit v1.2.3 From 5a6887ae8536c0b751e7b870ab629ecbf8784d22 Mon Sep 17 00:00:00 2001 From: Berthold Krevert Date: Thu, 3 Aug 2017 22:51:30 +0200 Subject: Normalize dashOffset behavior between renderer backends Software and tessellation backends assume that dashOffset is defined in units of strokeWidth. That means the nvpr backend has to scale the dashOffset by the strokeWidth to keep behavior in sync. Change-Id: Ie1735f8dcdc6ac89fc4425b29166f88ad2638a92 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index 4f49bb5256..a859ca45b6 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -370,11 +370,11 @@ void QQuickShapeNvprRenderer::updateNode() } if (dirty & DirtyDash) { - dst.dashOffset = src.dashOffset; + // Multiply by strokeWidth because the Shape API follows QPen + // meaning the input dash pattern and dash offset here are in width units. + dst.dashOffset = src.dashOffset * src.strokeWidth; if (src.dashActive) { dst.dashPattern.resize(src.dashPattern.count()); - // Multiply by strokeWidth because the Shape API follows QPen - // meaning the input dash pattern here is in width units. for (int i = 0; i < src.dashPattern.count(); ++i) dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; } else { -- cgit v1.2.3 From 432e27ae092397cb2154f48103e729852c38cf2d Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 18 Apr 2017 09:07:07 -0500 Subject: Add very basic compressed texture support Allow direct loading of pkm texture files into Image. This can be extended to additional texture types, and then eventually turned into a full plugin architexture. [ChangeLog][Qt Quick] Allow direct loading of pkm texture files into Image. For example: Image { source: "myImage.pkm" } Change-Id: I1baed6c3e85a15752da8adc675482d874c9355ab Task-number: QTBUG-59872 Task-number: QTBUG-29451 Reviewed-by: Laszlo Agocs --- .../scenegraph/compressedtexture/qsgpkmhandler.cpp | 209 +++++++++++++++++++++ .../scenegraph/compressedtexture/qsgpkmhandler_p.h | 94 +++++++++ src/quick/scenegraph/scenegraph.pri | 15 ++ src/quick/scenegraph/util/qsgtexturereader.cpp | 82 ++++++++ src/quick/scenegraph/util/qsgtexturereader_p.h | 72 +++++++ src/quick/util/qquickpixmapcache.cpp | 45 ++++- 6 files changed, 510 insertions(+), 7 deletions(-) create mode 100644 src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp create mode 100644 src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h create mode 100644 src/quick/scenegraph/util/qsgtexturereader.cpp create mode 100644 src/quick/scenegraph/util/qsgtexturereader_p.h (limited to 'src') diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp new file mode 100644 index 0000000000..1b8882e9a5 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qsgpkmhandler_p.h" + +#include +#include +#include +#include +#include + +//#define ETC_DEBUG + +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8d64 +#endif + +#ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif + +#ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#endif + +#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif + +QT_BEGIN_NAMESPACE + +static const int headerSize = 16; + +static unsigned int typeMap[5] = { + GL_ETC1_RGB8_OES, + GL_COMPRESSED_RGB8_ETC2, + 0, // unused + GL_COMPRESSED_RGBA8_ETC2_EAC, + GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 +}; + +EtcTexture::EtcTexture() + : m_texture_id(0), m_uploaded(false) +{ + initializeOpenGLFunctions(); +} + +EtcTexture::~EtcTexture() +{ + if (m_texture_id) + glDeleteTextures(1, &m_texture_id); +} + +int EtcTexture::textureId() const +{ + if (m_texture_id == 0) { + EtcTexture *texture = const_cast(this); + texture->glGenTextures(1, &texture->m_texture_id); + } + return m_texture_id; +} + +bool EtcTexture::hasAlphaChannel() const +{ + return m_type == GL_COMPRESSED_RGBA8_ETC2_EAC || + m_type == GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; +} + + +void EtcTexture::bind() +{ + if (m_uploaded && m_texture_id) { + glBindTexture(GL_TEXTURE_2D, m_texture_id); + return; + } + + if (m_texture_id == 0) + glGenTextures(1, &m_texture_id); + glBindTexture(GL_TEXTURE_2D, m_texture_id); + +#ifdef ETC_DEBUG + qDebug() << "glCompressedTexImage2D, width: " << m_size.width() << "height" << m_size.height() << + "paddedWidth: " << m_paddedSize.width() << "paddedHeight: " << m_paddedSize.height(); +#endif + +#ifndef QT_NO_DEBUG + while (glGetError() != GL_NO_ERROR) { } +#endif + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + Q_ASSERT(ctx != 0); + ctx->functions()->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_type, + m_size.width(), m_size.height(), 0, + (m_paddedSize.width() * m_paddedSize.height()) / 2, + m_data.data() + headerSize); + +#ifndef QT_NO_DEBUG + // Gracefully fail in case of an error... + GLuint error = glGetError(); + if (error != GL_NO_ERROR) { + qDebug () << "glCompressedTexImage2D for compressed texture failed, error: " << error; + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + return; + } +#endif + + m_uploaded = true; + updateBindOptions(true); +} + +class QEtcTextureFactory : public QQuickTextureFactory +{ +public: + QByteArray m_data; + QSize m_size; + QSize m_paddedSize; + unsigned int m_type; + + QSize textureSize() const { return m_size; } + int textureByteCount() const { return m_data.size(); } + + QSGTexture *createTexture(QQuickWindow *) const { + EtcTexture *texture = new EtcTexture; + texture->m_data = m_data; + texture->m_size = m_size; + texture->m_paddedSize = m_paddedSize; + texture->m_type = m_type; + return texture; + } +}; + +QQuickTextureFactory *QSGPkmHandler::read(QIODevice *device) +{ + QScopedPointer ret(new QEtcTextureFactory); + ret->m_data = device->readAll(); + if (ret->m_data.isEmpty() || ret->m_data.size() < headerSize) + return nullptr; + + const char *rawData = ret->m_data.constData(); + + // magic number + if (qstrncmp(rawData, "PKM ", 4) != 0) + return nullptr; + + // currently ignore version (rawData + 4) + + // texture type + quint16 type = qFromBigEndian(rawData + 6); + static int typeCount = sizeof(typeMap)/sizeof(typeMap[0]); + if (type >= typeCount) + return nullptr; + ret->m_type = typeMap[type]; + + // texture size + ret->m_paddedSize.setWidth(qFromBigEndian(rawData + 8)); + ret->m_paddedSize.setHeight(qFromBigEndian(rawData + 10)); + if ((ret->m_paddedSize.width() * ret->m_paddedSize.height()) / 2 > ret->m_data.size() - headerSize) + return nullptr; + ret->m_size.setWidth(qFromBigEndian(rawData + 12)); + ret->m_size.setHeight(qFromBigEndian(rawData + 14)); + if (ret->m_size.isEmpty()) + return nullptr; + +#ifdef ETC_DEBUG + qDebug() << "requestTexture returning: " << ret->m_data.length() << "bytes; width: " << ret->m_size.width() << ", height: " << ret->m_size.height(); +#endif + + return ret.take(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h b/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h new file mode 100644 index 0000000000..77097cb80a --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QSGPKMHANDLER_H +#define QSGPKMHANDLER_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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGPkmHandler +{ +public: + QSGPkmHandler() {} + + QQuickTextureFactory *read(QIODevice *device); +}; + +class EtcTexture : public QSGTexture, protected QOpenGLFunctions +{ + Q_OBJECT +public: + EtcTexture(); + ~EtcTexture(); + + void bind(); + + QSize textureSize() const { return m_size; } + int textureId() const; + + bool hasAlphaChannel() const; + bool hasMipmaps() const { return false; } + + QByteArray m_data; + QSize m_size; + QSize m_paddedSize; + GLuint m_texture_id; + GLenum m_type; + bool m_uploaded; +}; + +QT_END_NAMESPACE + +#endif // QSGPKMHANDLER_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index c6db3df158..b5c72f521c 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -220,3 +220,18 @@ qtConfig(opengl(es1|es2)?) { $$PWD/shaders/visualization.frag \ $$PWD/shaders/visualization.vert } + +# Compressed Texture API +HEADERS += \ + $$PWD/util/qsgtexturereader_p.h + +SOURCES += \ + $$PWD/util/qsgtexturereader.cpp + +qtConfig(opengl(es1|es2)?) { + HEADERS += \ + $$PWD/compressedtexture/qsgpkmhandler_p.h + + SOURCES += \ + $$PWD/compressedtexture/qsgpkmhandler.cpp +} diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp new file mode 100644 index 0000000000..61729ada18 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturereader.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qsgtexturereader_p.h" + +#include + +#if QT_CONFIG(opengl) +#include +#endif + +QT_BEGIN_NAMESPACE + +QSGTextureReader::QSGTextureReader() +{ + +} + +QQuickTextureFactory *QSGTextureReader::read(QIODevice *device, const QByteArray &format) +{ +#if QT_CONFIG(opengl) + if (format == QByteArrayLiteral("pkm")) { + QSGPkmHandler handler; + return handler.read(device); + } +#else + Q_UNUSED(device) + Q_UNUSED(format) +#endif + return nullptr; +} + +bool QSGTextureReader::isTexture(QIODevice *device, const QByteArray &format) +{ +#if QT_CONFIG(opengl) + if (format == QByteArrayLiteral("pkm")) { + return device->peek(4) == QByteArrayLiteral("PKM "); + } +#else + Q_UNUSED(device) + Q_UNUSED(format) +#endif + return false; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexturereader_p.h b/src/quick/scenegraph/util/qsgtexturereader_p.h new file mode 100644 index 0000000000..7d2fc314a6 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturereader_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QSGTEXTUREREADER_H +#define QSGTEXTUREREADER_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 + +QT_BEGIN_NAMESPACE + +class QIODevice; +class QQuickTextureFactory; + +class QSGTextureReader +{ +public: + QSGTextureReader(); + + static QQuickTextureFactory *read(QIODevice *device, const QByteArray &format); + static bool isTexture(QIODevice *device, const QByteArray &format); +}; + +QT_END_NAMESPACE + +#endif // QSGTEXTUREREADER_H diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index e026608150..e218b84fff 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -49,6 +49,7 @@ #include #include +#include #include #include @@ -771,8 +772,26 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QFile f(localFile); QSize readSize; if (f.open(QIODevice::ReadOnly)) { - if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions)) - errorCode = QQuickPixmapReply::Loading; + + // for now, purely use suffix information to determine whether we are working with a compressed texture + QByteArray suffix = QFileInfo(f).suffix().toLower().toLatin1(); + if (QSGTextureReader::isTexture(&f, suffix)) { + QQuickTextureFactory *factory = QSGTextureReader::read(&f, suffix); + if (factory) { + readSize = factory->textureSize(); + } else { + errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString()); + errorCode = QQuickPixmapReply::Decoding; + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, factory); + mutex.unlock(); + return; + } else { + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions)) + errorCode = QQuickPixmapReply::Loading; + } } else { errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); errorCode = QQuickPixmapReply::Loading; @@ -1233,11 +1252,23 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QString errorString; if (f.open(QIODevice::ReadOnly)) { - QImage image; - QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform(); - if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) { - *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform); + // for now, purely use suffix information to determine whether we are working with a compressed texture + QByteArray suffix = QFileInfo(f).suffix().toLower().toLatin1(); + if (QSGTextureReader::isTexture(&f, suffix)) { + QQuickTextureFactory *factory = QSGTextureReader::read(&f, suffix); + if (factory) { + *ok = true; + return new QQuickPixmapData(declarativePixmap, factory); + } else { + errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString()); + } + } else { + QImage image; + QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform(); + if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) { + *ok = true; + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform); + } } } else { errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); -- cgit v1.2.3 From 003e24d72a9647e2dc397906234059fee19103ec Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 28 Jul 2017 13:44:09 +0200 Subject: shapes: Add support for radial gradients Task-number: QTBUG-61857 Change-Id: I580e503d8266a9dca69bb542c22228df4ff4bf94 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/imports/shapes/plugin.cpp | 1 + src/imports/shapes/qquickshape.cpp | 168 +++++++++++++++++- src/imports/shapes/qquickshape_p.h | 47 ++++++ src/imports/shapes/qquickshape_p_p.h | 31 ++-- src/imports/shapes/qquickshapegenericrenderer.cpp | 188 +++++++++++++++++---- src/imports/shapes/qquickshapegenericrenderer_p.h | 65 +++++-- src/imports/shapes/qquickshapenvprrenderer.cpp | 100 +++++++++-- src/imports/shapes/qquickshapenvprrenderer_p.h | 11 +- src/imports/shapes/qquickshapesoftwarerenderer.cpp | 43 +++-- src/imports/shapes/shaders/radialgradient.frag | 25 +++ src/imports/shapes/shaders/radialgradient.vert | 13 ++ .../shapes/shaders/radialgradient_core.frag | 29 ++++ .../shapes/shaders/radialgradient_core.vert | 15 ++ src/imports/shapes/shapes.qrc | 4 + src/quick/doc/images/shape-radial-gradient.png | Bin 0 -> 16523 bytes 15 files changed, 646 insertions(+), 94 deletions(-) create mode 100644 src/imports/shapes/shaders/radialgradient.frag create mode 100644 src/imports/shapes/shaders/radialgradient.vert create mode 100644 src/imports/shapes/shaders/radialgradient_core.frag create mode 100644 src/imports/shapes/shaders/radialgradient_core.vert create mode 100644 src/quick/doc/images/shape-radial-gradient.png (limited to 'src') diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index 1729fc88b4..186e182192 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -66,6 +66,7 @@ public: qmlRegisterType(uri, 1, 0, "ShapePath"); qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); qmlRegisterType(uri, 1, 0, "LinearGradient"); + qmlRegisterType(uri, 1, 0, "RadialGradient"); } }; diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index b8a138507a..b6174fb177 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -1061,12 +1061,12 @@ void QQuickShapeGradient::setSpread(SpreadMode mode) \brief Linear gradient \since 5.10 - Linear gradients interpolate colors between start and end points. Outside - these points the gradient is either padded, reflected or repeated depending - on the spread type. + Linear gradients interpolate colors between start and end points in Shape + items. Outside these points the gradient is either padded, reflected or + repeated depending on the spread type. - \note LinearGradient is not compatible with Rectangle items that only - support Gradient. This type is to be used with Shape. + \note LinearGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. \sa QLinearGradient */ @@ -1143,6 +1143,159 @@ void QQuickShapeLinearGradient::setY2(qreal v) } } +/*! + \qmltype RadialGradient + \instantiates QQuickShapeRadialGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Radial gradient + \since 5.10 + + Radial gradients interpolate colors between a focal circle and a center + circle in Shape items. Points outside the cone defined by the two circles + will be transparent. + + Outside the end points the gradient is either padded, reflected or repeated + depending on the spread type. + + Below is an example of a simple radial gradient. Here the colors are + interpolated between the specified point and the end points on a circle + specified by the radius: + + \code + fillGradient: RadialGradient { + centerX: 50; centerY: 50 + centerRadius: 100 + focalX: centerX; focalY: centerY + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + \endcode + + \image shape-radial-gradient.png + + Extended radial gradients, where a separate focal circle is specified, are + also supported. + + \note RadialGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. + + \sa QRadialGradient + */ + +QQuickShapeRadialGradient::QQuickShapeRadialGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::RadialGradient::centerX + \qmlproperty real QtQuick.Shapes::RadialGradient::centerY + \qmlproperty real QtQuick.Shapes::RadialGradient::focalX + \qmlproperty real QtQuick.Shapes::RadialGradient::focalY + + These properties define the center and focal points. To specify a simple + radial gradient, set focalX and focalY to the value of centerX and centerY, + respectively. + */ + +qreal QQuickShapeRadialGradient::centerX() const +{ + return m_centerPoint.x(); +} + +void QQuickShapeRadialGradient::setCenterX(qreal v) +{ + if (m_centerPoint.x() != v) { + m_centerPoint.setX(v); + emit centerXChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::centerY() const +{ + return m_centerPoint.y(); +} + +void QQuickShapeRadialGradient::setCenterY(qreal v) +{ + if (m_centerPoint.y() != v) { + m_centerPoint.setY(v); + emit centerYChanged(); + emit updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::RadialGradient::centerRadius + \qmlproperty real QtQuick.Shapes::RadialGradient::focalRadius + + These properties define the center and focal radius. For simple radial + gradients, focalRadius should be set to \c 0 (the default value). + */ + +qreal QQuickShapeRadialGradient::centerRadius() const +{ + return m_centerRadius; +} + +void QQuickShapeRadialGradient::setCenterRadius(qreal v) +{ + if (m_centerRadius != v) { + m_centerRadius = v; + emit centerRadiusChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalX() const +{ + return m_focalPoint.x(); +} + +void QQuickShapeRadialGradient::setFocalX(qreal v) +{ + if (m_focalPoint.x() != v) { + m_focalPoint.setX(v); + emit focalXChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalY() const +{ + return m_focalPoint.y(); +} + +void QQuickShapeRadialGradient::setFocalY(qreal v) +{ + if (m_focalPoint.y() != v) { + m_focalPoint.setY(v); + emit focalYChanged(); + emit updated(); + } +} + +qreal QQuickShapeRadialGradient::focalRadius() const +{ + return m_focalRadius; +} + +void QQuickShapeRadialGradient::setFocalRadius(qreal v) +{ + if (m_focalRadius != v) { + m_focalRadius = v; + emit focalRadiusChanged(); + emit updated(); + } +} + #if QT_CONFIG(opengl) // contexts sharing with each other get the same cache instance @@ -1181,7 +1334,7 @@ void QQuickShapeGradientCache::freeResource(QOpenGLContext *) m_cache.clear(); } -static void generateGradientColorTable(const QQuickShapeGradientCache::GradientDesc &gradient, +static void generateGradientColorTable(const QQuickShapeGradientCache::Key &gradient, uint *colorTable, int size, float opacity) { int pos = 0; @@ -1232,7 +1385,7 @@ static void generateGradientColorTable(const QQuickShapeGradientCache::GradientD colorTable[size-1] = last_color; } -QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) +QSGTexture *QQuickShapeGradientCache::get(const Key &grad) { QSGPlainTexture *tx = m_cache[grad]; if (!tx) { @@ -1263,6 +1416,7 @@ QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad) qWarning("Unknown gradient spread mode %d", grad.spread); break; } + tx->setFiltering(QSGTexture::Linear); m_cache[grad] = tx; } return tx; diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 3a630aacbc..b6943db37c 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -120,6 +120,53 @@ private: QPointF m_end; }; +class QQuickShapeRadialGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged) + Q_PROPERTY(qreal centerRadius READ centerRadius WRITE setCenterRadius NOTIFY centerRadiusChanged) + Q_PROPERTY(qreal focalX READ focalX WRITE setFocalX NOTIFY focalXChanged) + Q_PROPERTY(qreal focalY READ focalY WRITE setFocalY NOTIFY focalYChanged) + Q_PROPERTY(qreal focalRadius READ focalRadius WRITE setFocalRadius NOTIFY focalRadiusChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeRadialGradient(QObject *parent = nullptr); + + qreal centerX() const; + void setCenterX(qreal v); + + qreal centerY() const; + void setCenterY(qreal v); + + qreal centerRadius() const; + void setCenterRadius(qreal v); + + qreal focalX() const; + void setFocalX(qreal v); + + qreal focalY() const; + void setFocalY(qreal v); + + qreal focalRadius() const; + void setFocalRadius(qreal v); + +signals: + void centerXChanged(); + void centerYChanged(); + void focalXChanged(); + void focalYChanged(); + void centerRadiusChanged(); + void focalRadiusChanged(); + +private: + QPointF m_centerPoint; + QPointF m_focalPoint; + qreal m_centerRadius = 0; + qreal m_focalRadius = 0; +}; + class QQuickShapePath : public QQuickPath { Q_OBJECT diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index 6ca752de56..bbe9a81d4a 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -70,6 +70,16 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) + enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient }; + struct GradientDesc { // can fully describe a linear/radial/conical gradient + QGradientStops stops; + QQuickShapeGradient::SpreadMode spread; + QPointF a; // start (L) or center point (R/C) + QPointF b; // end (L) or focal point (R) + qreal v0; // center radius (R) or start angle (C) + qreal v1; // focal radius (R) + }; + virtual ~QQuickAbstractPathRenderer() { } // Gui thread @@ -171,15 +181,15 @@ public: class QQuickShapeGradientCache : public QOpenGLSharedResource { public: - struct GradientDesc { + struct Key { + Key(const QGradientStops &stops, QQuickShapeGradient::SpreadMode spread) + : stops(stops), spread(spread) + { } QGradientStops stops; - QPointF start; - QPointF end; QQuickShapeGradient::SpreadMode spread; - bool operator==(const GradientDesc &other) const + bool operator==(const Key &other) const { - return start == other.start && end == other.end && spread == other.spread - && stops == other.stops; + return spread == other.spread && stops == other.stops; } }; @@ -189,18 +199,17 @@ public: void invalidateResource() override; void freeResource(QOpenGLContext *) override; - QSGTexture *get(const GradientDesc &grad); + QSGTexture *get(const Key &grad); static QQuickShapeGradientCache *currentCache(); private: - QHash m_cache; + QHash m_cache; }; -inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0) +inline uint qHash(const QQuickShapeGradientCache::Key &v, uint seed = 0) { - uint h = seed; - h += v.start.x() + v.end.y() + v.spread; + uint h = seed + v.spread; for (int i = 0; i < 3 && i < v.stops.count(); ++i) h += v.stops[i].second.rgba(); return h; diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 398106af3d..5a2f337b51 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -97,22 +97,21 @@ void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) case MatSolidColor: // Use vertexcolor material. Items with different colors remain batchable // this way, at the expense of having to provide per-vertex color values. - if (!m_solidColorMaterial) - m_solidColorMaterial.reset(QQuickShapeGenericMaterialFactory::createVertexColor(m_window)); - m_material = m_solidColorMaterial.data(); + m_material.reset(QQuickShapeGenericMaterialFactory::createVertexColor(m_window)); break; case MatLinearGradient: - if (!m_linearGradientMaterial) - m_linearGradientMaterial.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(m_window, this)); - m_material = m_linearGradientMaterial.data(); + m_material.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(m_window, this)); + break; + case MatRadialGradient: + m_material.reset(QQuickShapeGenericMaterialFactory::createRadialGradient(m_window, this)); break; default: qWarning("Unknown material %d", m); return; } - if (material() != m_material) - setMaterial(m_material); + if (material() != m_material.data()) + setMaterial(m_material.data()); } static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api) @@ -238,19 +237,25 @@ void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::Stro void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) { ShapePathData &d(m_sp[index]); - d.fillGradientActive = gradient != nullptr; -#if QT_CONFIG(opengl) if (gradient) { d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); + d.fillGradientActive = LinearGradient; + d.fillGradient.a = QPointF(g->x1(), g->y1()); + d.fillGradient.b = QPointF(g->x2(), g->y2()); + } else if (QQuickShapeRadialGradient *g = qobject_cast(gradient)) { + d.fillGradientActive = RadialGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.b = QPointF(g->focalX(), g->focalY()); + d.fillGradient.v0 = g->centerRadius(); + d.fillGradient.v1 = g->focalRadius(); } else { Q_UNREACHABLE(); } + } else { + d.fillGradientActive = NoGradient; } -#endif d.syncDirty |= DirtyFillGradient; } @@ -554,12 +559,8 @@ void QQuickShapeGenericRenderer::updateNode() void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n) { if (d->fillGradientActive) { -#if QT_CONFIG(opengl) if (d->effectiveDirty & DirtyFillGradient) n->m_fillGradient = d->fillGradient; -#else - Q_UNUSED(n); -#endif } } @@ -585,7 +586,18 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen } if (d->fillGradientActive) { - n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatLinearGradient); + QQuickShapeGenericStrokeFillNode::Material gradMat; + switch (d->fillGradientActive) { + case LinearGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatLinearGradient; + break; + case RadialGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatRadialGradient; + break; + default: + Q_UNREACHABLE(); + } + n->activateMaterial(gradMat); if (d->effectiveDirty & DirtyFillGradient) { // Gradients are implemented via a texture-based material. n->markDirty(QSGNode::DirtyMaterial); @@ -665,7 +677,7 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow * QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); #if QT_CONFIG(opengl) - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + if (api == QSGRendererInterface::OpenGL) return new QSGVertexColorMaterial; #endif @@ -674,12 +686,12 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow * } QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window, - QQuickShapeGenericStrokeFillNode *node) + QQuickShapeGenericStrokeFillNode *node) { QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); #if QT_CONFIG(opengl) - if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"... + if (api == QSGRendererInterface::OpenGL) return new QQuickShapeLinearGradientMaterial(node); #else Q_UNUSED(node); @@ -689,6 +701,22 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindo return nullptr; } +QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QQuickShapeRadialGradientMaterial(node); +#else + Q_UNUSED(node); +#endif + + qWarning("Radial gradient material: Unsupported graphics API %d", api); + return nullptr; +} + #if QT_CONFIG(opengl) QSGMaterialType QQuickShapeLinearGradientShader::type; @@ -720,10 +748,11 @@ void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGM program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); QQuickShapeGenericStrokeFillNode *node = m->node(); - program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start)); - program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end)); + program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.a)); + program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.b)); - QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(node->m_fillGradient); + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); tx->bind(); } @@ -744,19 +773,118 @@ int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const if (a == b) return 0; - const QQuickShapeGradientCache::GradientDesc *ga = &a->m_fillGradient; - const QQuickShapeGradientCache::GradientDesc *gb = &b->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; if (int d = ga->spread - gb->spread) return d; - if (int d = ga->start.x() - gb->start.x()) + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) return d; - if (int d = ga->start.y() - gb->start.y()) + if (int d = ga->b.x() - gb->b.x()) return d; - if (int d = ga->end.x() - gb->end.x()) + if (int d = ga->b.y() - gb->b.y()) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + +QSGMaterialType QQuickShapeRadialGradientShader::type; + +QQuickShapeRadialGradientShader::QQuickShapeRadialGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/shapes/shaders/radialgradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/shapes/shaders/radialgradient.frag")); +} + +void QQuickShapeRadialGradientShader::initialize() +{ + QOpenGLShaderProgram *prog = program(); + m_opacityLoc = prog->uniformLocation("opacity"); + m_matrixLoc = prog->uniformLocation("matrix"); + m_translationPointLoc = prog->uniformLocation("translationPoint"); + m_focalToCenterLoc = prog->uniformLocation("focalToCenter"); + m_centerRadiusLoc = prog->uniformLocation("centerRadius"); + m_focalRadiusLoc = prog->uniformLocation("focalRadius"); +} + +void QQuickShapeRadialGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeRadialGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + + const QPointF centerPoint = node->m_fillGradient.a; + const QPointF focalPoint = node->m_fillGradient.b; + const QPointF focalToCenter = centerPoint - focalPoint; + const GLfloat centerRadius = node->m_fillGradient.v0; + const GLfloat focalRadius = node->m_fillGradient.v1; + + program()->setUniformValue(m_translationPointLoc, focalPoint); + program()->setUniformValue(m_centerRadiusLoc, centerRadius); + program()->setUniformValue(m_focalRadiusLoc, focalRadius); + program()->setUniformValue(m_focalToCenterLoc, focalToCenter); + + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, node->m_fillGradient.spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); +} + +char const *const *QQuickShapeRadialGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeRadialGradientMaterial *m = static_cast(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->spread - gb->spread) + return d; + + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) + return d; + if (int d = ga->b.x() - gb->b.x()) + return d; + if (int d = ga->b.y() - gb->b.y()) + return d; + + if (int d = ga->v0 - gb->v0) return d; - if (int d = ga->end.y() - gb->end.y()) + if (int d = ga->v1 - gb->v1) return d; if (int d = ga->stops.count() - gb->stops.count()) diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index dadeba3467..2561116f81 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -131,10 +131,8 @@ private: Color4ub fillColor; Qt::FillRule fillRule; QPainterPath path; - bool fillGradientActive; -#if QT_CONFIG(opengl) - QQuickShapeGradientCache::GradientDesc fillGradient; -#endif + FillGradientType fillGradientActive; + GradientDesc fillGradient; VertexContainerType fillVertices; IndexContainerType fillIndices; QSGGeometry::Type indexType; @@ -211,7 +209,8 @@ public: enum Material { MatSolidColor, - MatLinearGradient + MatLinearGradient, + MatRadialGradient }; void activateMaterial(Material m); @@ -219,16 +218,12 @@ public: QQuickWindow *window() const { return m_window; } // shadow data for custom materials -#if QT_CONFIG(opengl) - QQuickShapeGradientCache::GradientDesc m_fillGradient; -#endif + QQuickAbstractPathRenderer::GradientDesc m_fillGradient; private: QSGGeometry *m_geometry; QQuickWindow *m_window; - QSGMaterial *m_material; - QScopedPointer m_solidColorMaterial; - QScopedPointer m_linearGradientMaterial; + QScopedPointer m_material; friend class QQuickShapeGenericRenderer; }; @@ -246,6 +241,7 @@ class QQuickShapeGenericMaterialFactory public: static QSGMaterial *createVertexColor(QQuickWindow *window); static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); + static QSGMaterial *createRadialGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); }; #if QT_CONFIG(opengl) @@ -300,6 +296,53 @@ private: QQuickShapeGenericStrokeFillNode *m_node; }; +class QQuickShapeRadialGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeRadialGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_translationPointLoc = -1; + int m_focalToCenterLoc = -1; + int m_centerRadiusLoc = -1; + int m_focalRadiusLoc = -1; +}; + +class QQuickShapeRadialGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeRadialGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeRadialGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + #endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index a859ca45b6..a08da2f3fe 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -125,16 +125,24 @@ void QQuickShapeNvprRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeS void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) { ShapePathGuiData &d(m_sp[index]); - d.fillGradientActive = gradient != nullptr; if (gradient) { d.fillGradient.stops = gradient->gradientStops(); // sorted d.fillGradient.spread = gradient->spread(); if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { - d.fillGradient.start = QPointF(g->x1(), g->y1()); - d.fillGradient.end = QPointF(g->x2(), g->y2()); + d.fillGradientActive = LinearGradient; + d.fillGradient.a = QPointF(g->x1(), g->y1()); + d.fillGradient.b = QPointF(g->x2(), g->y2()); + } else if (QQuickShapeRadialGradient *g = qobject_cast(gradient)) { + d.fillGradientActive = RadialGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.b = QPointF(g->focalX(), g->focalY()); + d.fillGradient.v0 = g->centerRadius(); + d.fillGradient.v1 = g->focalRadius(); } else { Q_UNREACHABLE(); } + } else { + d.fillGradientActive = NoGradient; } d.dirty |= DirtyFillGradient; m_accDirty |= DirtyFillGradient; @@ -485,6 +493,49 @@ QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMate Q_ASSERT(mtl.uniLoc[2] >= 0); mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd"); Q_ASSERT(mtl.uniLoc[3] >= 0); + } else if (m == MatRadialGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "uniform sampler2D gradTab;\n" + "uniform float opacity;\n" + "uniform vec2 focalToCenter;\n" + "uniform float centerRadius;\n" + "uniform float focalRadius;\n" + "uniform vec2 translationPoint;\n" + "layout(location = 0) in vec2 uv;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 coord = uv - translationPoint;\n" + " float rd = centerRadius - focalRadius;\n" + " float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter));\n" + " float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd;\n" + " float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);\n" + " float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord));\n" + " vec4 result = vec4(0.0);\n" + " if (det >= 0.0) {\n" + " float detSqrt = sqrt(det);\n" + " float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);\n" + " if (focalRadius + w * (centerRadius - focalRadius) >= 0.0)\n" + " result = texture(gradTab, vec2(w, 0.5)) * opacity;\n" + " }\n" + " fragColor = result;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for radial gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "focalToCenter"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "centerRadius"); + Q_ASSERT(mtl.uniLoc[3] >= 0); + mtl.uniLoc[4] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "focalRadius"); + Q_ASSERT(mtl.uniLoc[4] >= 0); + mtl.uniLoc[5] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "translationPoint"); + Q_ASSERT(mtl.uniLoc[5] >= 0); } else { Q_UNREACHABLE(); } @@ -542,17 +593,40 @@ void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) { QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; if (d->fillGradientActive) { - mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); - QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(d->fillGradient); + const QQuickShapeGradientCache::Key cacheKey(d->fillGradient.stops, d->fillGradient.spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); tx->bind(); - // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) - // where x and y are in path coordinate space, which is just what - // we need since the gradient's start and stop are in that space too. - GLfloat coeff[6] = { 1, 0, 0, - 0, 1, 0 }; - nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y()); - f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y()); + + if (d->fillGradientActive == QQuickAbstractPathRenderer::LinearGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); + // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) + // where x and y are in path coordinate space, which is just what + // we need since the gradient's start and stop are in that space too. + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.a.x(), d->fillGradient.a.y()); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.b.x(), d->fillGradient.b.y()); + } else if (d->fillGradientActive == QQuickAbstractPathRenderer::RadialGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatRadialGradient); + // simply drive uv (location 0) with x and y, just like for the linear gradient + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + + const QPointF centerPoint = d->fillGradient.a; + const QPointF focalPoint = d->fillGradient.b; + const QPointF focalToCenter = centerPoint - focalPoint; + const GLfloat centerRadius = d->fillGradient.v0; + const GLfloat focalRadius = d->fillGradient.v1; + + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], focalToCenter.x(), focalToCenter.y()); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[3], centerRadius); + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[4], focalRadius); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[5], focalPoint.x(), focalPoint.y()); + } else { + Q_UNREACHABLE(); + } } else { mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index 7eb2924ab7..b3d92dbdbc 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -116,8 +116,8 @@ private: bool dashActive; qreal dashOffset; QVector dashPattern; - bool fillGradientActive; - QQuickShapeGradientCache::GradientDesc fillGradient; + FillGradientType fillGradientActive; + GradientDesc fillGradient; }; void convertPath(const QQuickPath *path, ShapePathGuiData *d); @@ -136,6 +136,7 @@ public: enum Material { MatSolid, MatLinearGradient, + MatRadialGradient, NMaterials }; @@ -143,7 +144,7 @@ public: struct MaterialDesc { GLuint ppl = 0; GLuint prg = 0; - int uniLoc[4]; + int uniLoc[8]; }; void create(QQuickNvprFunctions *nvpr); @@ -199,8 +200,8 @@ private: GLenum fillRule; GLfloat dashOffset; QVector dashPattern; - bool fillGradientActive; - QQuickShapeGradientCache::GradientDesc fillGradient; + QQuickAbstractPathRenderer::FillGradientType fillGradientActive; + QQuickAbstractPathRenderer::GradientDesc fillGradient; QOpenGLFramebufferObject *fallbackFbo = nullptr; bool fallbackValid = false; QSize fallbackSize; diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp index 4e6e758697..15803dcf0a 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -130,26 +130,35 @@ void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::Str m_accDirty |= DirtyPen; } +static inline void setupPainterGradient(QGradient *painterGradient, const QQuickShapeGradient &g) +{ + painterGradient->setStops(g.gradientStops()); // sorted + switch (g.spread()) { + case QQuickShapeGradient::PadSpread: + painterGradient->setSpread(QGradient::PadSpread); + break; + case QQuickShapeGradient::RepeatSpread: + painterGradient->setSpread(QGradient::RepeatSpread); + break; + case QQuickShapeGradient::ReflectSpread: + painterGradient->setSpread(QGradient::ReflectSpread); + break; + default: + break; + } +} + void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient) { ShapePathGuiData &d(m_sp[index]); - if (QQuickShapeLinearGradient *linearGradient = qobject_cast(gradient)) { - QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(), - linearGradient->x2(), linearGradient->y2()); - painterGradient.setStops(linearGradient->gradientStops()); // sorted - switch (gradient->spread()) { - case QQuickShapeGradient::PadSpread: - painterGradient.setSpread(QGradient::PadSpread); - break; - case QQuickShapeGradient::RepeatSpread: - painterGradient.setSpread(QGradient::RepeatSpread); - break; - case QQuickShapeGradient::ReflectSpread: - painterGradient.setSpread(QGradient::ReflectSpread); - break; - default: - break; - } + if (QQuickShapeLinearGradient *g = qobject_cast(gradient)) { + QLinearGradient painterGradient(g->x1(), g->y1(), g->x2(), g->y2()); + setupPainterGradient(&painterGradient, *g); + d.brush = QBrush(painterGradient); + } else if (QQuickShapeRadialGradient *g = qobject_cast(gradient)) { + QRadialGradient painterGradient(g->centerX(), g->centerY(), g->centerRadius(), + g->focalX(), g->focalY(), g->focalRadius()); + setupPainterGradient(&painterGradient, *g); d.brush = QBrush(painterGradient); } else { d.brush = QBrush(d.fillColor); diff --git a/src/imports/shapes/shaders/radialgradient.frag b/src/imports/shapes/shaders/radialgradient.frag new file mode 100644 index 0000000000..0f503bc0f7 --- /dev/null +++ b/src/imports/shapes/shaders/radialgradient.frag @@ -0,0 +1,25 @@ +uniform sampler2D gradTabTexture; +uniform lowp float opacity; + +uniform highp vec2 focalToCenter; +uniform highp float centerRadius; +uniform highp float focalRadius; + +varying highp vec2 coord; + +void main() +{ + highp float rd = centerRadius - focalRadius; + highp float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter)); + highp float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd; + highp float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2); + highp float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord)); + lowp vec4 result = vec4(0.0); + if (det >= 0.0) { + highp float detSqrt = sqrt(det); + highp float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); + if (focalRadius + w * (centerRadius - focalRadius) >= 0.0) + result = texture2D(gradTabTexture, vec2(w, 0.5)) * opacity; + } + gl_FragColor = result; +} diff --git a/src/imports/shapes/shaders/radialgradient.vert b/src/imports/shapes/shaders/radialgradient.vert new file mode 100644 index 0000000000..3350b0675a --- /dev/null +++ b/src/imports/shapes/shaders/radialgradient.vert @@ -0,0 +1,13 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +varying vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shaders/radialgradient_core.frag b/src/imports/shapes/shaders/radialgradient_core.frag new file mode 100644 index 0000000000..706ce53e4d --- /dev/null +++ b/src/imports/shapes/shaders/radialgradient_core.frag @@ -0,0 +1,29 @@ +#version 150 core + +uniform sampler2D gradTabTexture; +uniform float opacity; + +uniform vec2 focalToCenter; +uniform float centerRadius; +uniform float focalRadius; + +in vec2 coord; + +out vec4 fragColor; + +void main() +{ + float rd = centerRadius - focalRadius; + float b = 2.0 * (rd * focalRadius + dot(coord, focalToCenter)); + float fmp2_m_radius2 = -focalToCenter.x * focalToCenter.x - focalToCenter.y * focalToCenter.y + rd * rd; + float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2); + float det = b * b - 4.0 * fmp2_m_radius2 * ((focalRadius * focalRadius) - dot(coord, coord)); + vec4 result = vec4(0.0); + if (det >= 0.0) { + float detSqrt = sqrt(det); + float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2); + if (focalRadius + w * (centerRadius - focalRadius) >= 0.0) + result = texture(gradTabTexture, vec2(w, 0.5)) * opacity; + } + fragColor = result; +} diff --git a/src/imports/shapes/shaders/radialgradient_core.vert b/src/imports/shapes/shaders/radialgradient_core.vert new file mode 100644 index 0000000000..f94a56401b --- /dev/null +++ b/src/imports/shapes/shaders/radialgradient_core.vert @@ -0,0 +1,15 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +out vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shapes.qrc b/src/imports/shapes/shapes.qrc index 65ee2007f9..92912ede48 100644 --- a/src/imports/shapes/shapes.qrc +++ b/src/imports/shapes/shapes.qrc @@ -8,5 +8,9 @@ shaders/lineargradient.frag shaders/lineargradient_core.vert shaders/lineargradient_core.frag + shaders/radialgradient.vert + shaders/radialgradient.frag + shaders/radialgradient_core.vert + shaders/radialgradient_core.frag diff --git a/src/quick/doc/images/shape-radial-gradient.png b/src/quick/doc/images/shape-radial-gradient.png new file mode 100644 index 0000000000..bfff2e4b6b Binary files /dev/null and b/src/quick/doc/images/shape-radial-gradient.png differ -- cgit v1.2.3 From 1b0c9b46ce13b0f9c533f18fb420ff10ad56e4f6 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Thu, 27 Jul 2017 10:42:04 +0200 Subject: Fix some bugs related to child mouse filtering - Only abort event delivery early if event that got filtered was accepted (previously we aborted as soon as the event got filtered, even if the event was filtered, but explicitly *not* accepted) - If the event that got filtered was *not* accepted, we do not abort event delivery, but we need to remove the item from the list of target items that we will deliver to later - If childMouseEventFilter returns true it should not automatically mean that the event was accepted. Change-Id: I2f2415379061131af1d5102e03d01f010e1a8168 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickwindow.cpp | 12 ++++++++---- src/quick/items/qquickwindow_p.h | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e2dcff7770..7b331251f0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2421,8 +2421,12 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, if (allowChildEventFiltering && !handlersOnly) { updateFilteringParentItems(targetItems); - if (sendFilteredPointerEvent(event, nullptr)) - return true; + QQuickItem *filteredItem; + if (sendFilteredPointerEvent(event, nullptr, &filteredItem)) { + if (event->isAccepted()) + return true; + targetItems.removeAll(filteredItem); + } } for (QQuickItem *item: targetItems) { @@ -2745,7 +2749,7 @@ void QQuickWindowPrivate::updateFilteringParentItems(const QVector } } -bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver) +bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered) { if (!allowChildEventFiltering) return false; @@ -2759,7 +2763,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ QPointF localPos = item->mapFromScene(pme->point(0)->scenePos()); QMouseEvent *me = pme->asMouseEvent(localPos); if (filteringParent->childMouseEventFilter(item, me)) { - event->setAccepted(true); + if (itemThatFiltered) *itemThatFiltered = item; ret = true; } } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index c2d2a7a8a4..14564a7f55 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -148,7 +148,7 @@ public: static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent); bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet *); - bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver); + bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered = 0); #if QT_CONFIG(wheelevent) bool deliverWheelEvent(QQuickItem *, QWheelEvent *); #endif -- cgit v1.2.3 From 0047b3bdc0938077d7049f4103f835b52897b93a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 31 Jul 2017 13:29:40 +0200 Subject: shapes: Add support for conical gradients Task-number: QTBUG-61857 Change-Id: Iacefcc3b22b31ed3dbcfbf7f00c8b0ea51c63b95 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/imports/shapes/plugin.cpp | 1 + src/imports/shapes/qquickshape.cpp | 83 ++++++++++++++++ src/imports/shapes/qquickshape_p.h | 30 ++++++ src/imports/shapes/qquickshapegenericrenderer.cpp | 109 +++++++++++++++++++++ src/imports/shapes/qquickshapegenericrenderer_p.h | 49 ++++++++- src/imports/shapes/qquickshapenvprrenderer.cpp | 58 ++++++++++- src/imports/shapes/qquickshapenvprrenderer_p.h | 1 + src/imports/shapes/qquickshapesoftwarerenderer.cpp | 4 + src/imports/shapes/shaders/conicalgradient.frag | 19 ++++ src/imports/shapes/shaders/conicalgradient.vert | 13 +++ .../shapes/shaders/conicalgradient_core.frag | 22 +++++ .../shapes/shaders/conicalgradient_core.vert | 15 +++ src/imports/shapes/shapes.qrc | 4 + 13 files changed, 403 insertions(+), 5 deletions(-) create mode 100644 src/imports/shapes/shaders/conicalgradient.frag create mode 100644 src/imports/shapes/shaders/conicalgradient.vert create mode 100644 src/imports/shapes/shaders/conicalgradient_core.frag create mode 100644 src/imports/shapes/shaders/conicalgradient_core.vert (limited to 'src') diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index 186e182192..239ef78e55 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -67,6 +67,7 @@ public: qmlRegisterUncreatableType(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); qmlRegisterType(uri, 1, 0, "LinearGradient"); qmlRegisterType(uri, 1, 0, "RadialGradient"); + qmlRegisterType(uri, 1, 0, "ConicalGradient"); } }; diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index b6174fb177..1ed61ff476 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -1296,6 +1296,89 @@ void QQuickShapeRadialGradient::setFocalRadius(qreal v) } } +/*! + \qmltype ConicalGradient + \instantiates QQuickShapeConicalGradient + \inqmlmodule QtQuick.Shapes + \ingroup qtquick-paths + \ingroup qtquick-views + \inherits ShapeGradient + \brief Conical gradient + \since 5.10 + + Conical gradients interpolate colors counter-clockwise around a center + point in Shape items. + + \note The \l{ShapeGradient.spread}{spread mode} setting has no effect for + conical gradients. + + \note ConicalGradient is only supported in combination with Shape items. It + is not compatible with \l Rectangle, as that only supports \l Gradient. + + \sa QConicalGradient + */ + +QQuickShapeConicalGradient::QQuickShapeConicalGradient(QObject *parent) + : QQuickShapeGradient(parent) +{ +} + +/*! + \qmlproperty real QtQuick.Shapes::ConicalGradient::centerX + \qmlproperty real QtQuick.Shapes::ConicalGradient::centerY + + These properties define the center point of the conical gradient. + */ + +qreal QQuickShapeConicalGradient::centerX() const +{ + return m_centerPoint.x(); +} + +void QQuickShapeConicalGradient::setCenterX(qreal v) +{ + if (m_centerPoint.x() != v) { + m_centerPoint.setX(v); + emit centerXChanged(); + emit updated(); + } +} + +qreal QQuickShapeConicalGradient::centerY() const +{ + return m_centerPoint.y(); +} + +void QQuickShapeConicalGradient::setCenterY(qreal v) +{ + if (m_centerPoint.y() != v) { + m_centerPoint.setY(v); + emit centerYChanged(); + emit updated(); + } +} + +/*! + \qmlproperty real QtQuick.Shapes::ConicalGradient::angle + + This property defines the start angle for the conical gradient. The value + is in degrees (0-360). + */ + +qreal QQuickShapeConicalGradient::angle() const +{ + return m_angle; +} + +void QQuickShapeConicalGradient::setAngle(qreal v) +{ + if (m_angle != v) { + m_angle = v; + emit angleChanged(); + emit updated(); + } +} + #if QT_CONFIG(opengl) // contexts sharing with each other get the same cache instance diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index b6943db37c..27b02bc962 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -167,6 +167,36 @@ private: qreal m_focalRadius = 0; }; +class QQuickShapeConicalGradient : public QQuickShapeGradient +{ + Q_OBJECT + Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged) + Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickShapeConicalGradient(QObject *parent = nullptr); + + qreal centerX() const; + void setCenterX(qreal v); + + qreal centerY() const; + void setCenterY(qreal v); + + qreal angle() const; + void setAngle(qreal v); + +signals: + void centerXChanged(); + void centerYChanged(); + void angleChanged(); + +private: + QPointF m_centerPoint; + qreal m_angle = 0; +}; + class QQuickShapePath : public QQuickPath { Q_OBJECT diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 5a2f337b51..98ba89dc3d 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -105,6 +105,9 @@ void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m) case MatRadialGradient: m_material.reset(QQuickShapeGenericMaterialFactory::createRadialGradient(m_window, this)); break; + case MatConicalGradient: + m_material.reset(QQuickShapeGenericMaterialFactory::createConicalGradient(m_window, this)); + break; default: qWarning("Unknown material %d", m); return; @@ -250,6 +253,10 @@ void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient d.fillGradient.b = QPointF(g->focalX(), g->focalY()); d.fillGradient.v0 = g->centerRadius(); d.fillGradient.v1 = g->focalRadius(); + } else if (QQuickShapeConicalGradient *g = qobject_cast(gradient)) { + d.fillGradientActive = ConicalGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.v0 = g->angle(); } else { Q_UNREACHABLE(); } @@ -594,6 +601,9 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen case RadialGradient: gradMat = QQuickShapeGenericStrokeFillNode::MatRadialGradient; break; + case ConicalGradient: + gradMat = QQuickShapeGenericStrokeFillNode::MatConicalGradient; + break; default: Q_UNREACHABLE(); } @@ -717,6 +727,22 @@ QSGMaterial *QQuickShapeGenericMaterialFactory::createRadialGradient(QQuickWindo return nullptr; } +QSGMaterial *QQuickShapeGenericMaterialFactory::createConicalGradient(QQuickWindow *window, + QQuickShapeGenericStrokeFillNode *node) +{ + QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi(); + +#if QT_CONFIG(opengl) + if (api == QSGRendererInterface::OpenGL) + return new QQuickShapeConicalGradientMaterial(node); +#else + Q_UNUSED(node); +#endif + + qWarning("Conical gradient material: Unsupported graphics API %d", api); + return nullptr; +} + #if QT_CONFIG(opengl) QSGMaterialType QQuickShapeLinearGradientShader::type; @@ -900,6 +926,89 @@ int QQuickShapeRadialGradientMaterial::compare(const QSGMaterial *other) const return 0; } +QSGMaterialType QQuickShapeConicalGradientShader::type; + +QQuickShapeConicalGradientShader::QQuickShapeConicalGradientShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, + QStringLiteral(":/qt-project.org/shapes/shaders/conicalgradient.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, + QStringLiteral(":/qt-project.org/shapes/shaders/conicalgradient.frag")); +} + +void QQuickShapeConicalGradientShader::initialize() +{ + QOpenGLShaderProgram *prog = program(); + m_opacityLoc = prog->uniformLocation("opacity"); + m_matrixLoc = prog->uniformLocation("matrix"); + m_angleLoc = prog->uniformLocation("angle"); + m_translationPointLoc = prog->uniformLocation("translationPoint"); +} + +void QQuickShapeConicalGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *) +{ + QQuickShapeConicalGradientMaterial *m = static_cast(mat); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); + + QQuickShapeGenericStrokeFillNode *node = m->node(); + + const QPointF centerPoint = node->m_fillGradient.a; + const GLfloat angle = -qDegreesToRadians(node->m_fillGradient.v0); + + program()->setUniformValue(m_angleLoc, angle); + program()->setUniformValue(m_translationPointLoc, centerPoint); + + const QQuickShapeGradientCache::Key cacheKey(node->m_fillGradient.stops, QQuickShapeGradient::RepeatSpread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); +} + +char const *const *QQuickShapeConicalGradientShader::attributeNames() const +{ + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; + return attr; +} + +int QQuickShapeConicalGradientMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QQuickShapeConicalGradientMaterial *m = static_cast(other); + + QQuickShapeGenericStrokeFillNode *a = node(); + QQuickShapeGenericStrokeFillNode *b = m->node(); + Q_ASSERT(a && b); + if (a == b) + return 0; + + const QQuickAbstractPathRenderer::GradientDesc *ga = &a->m_fillGradient; + const QQuickAbstractPathRenderer::GradientDesc *gb = &b->m_fillGradient; + + if (int d = ga->a.x() - gb->a.x()) + return d; + if (int d = ga->a.y() - gb->a.y()) + return d; + + if (int d = ga->v0 - gb->v0) + return d; + + if (int d = ga->stops.count() - gb->stops.count()) + return d; + + for (int i = 0; i < ga->stops.count(); ++i) { + if (int d = ga->stops[i].first - gb->stops[i].first) + return d; + if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba()) + return d; + } + + return 0; +} + #endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h index 2561116f81..32cec798ec 100644 --- a/src/imports/shapes/qquickshapegenericrenderer_p.h +++ b/src/imports/shapes/qquickshapegenericrenderer_p.h @@ -210,7 +210,8 @@ public: enum Material { MatSolidColor, MatLinearGradient, - MatRadialGradient + MatRadialGradient, + MatConicalGradient }; void activateMaterial(Material m); @@ -242,6 +243,7 @@ public: static QSGMaterial *createVertexColor(QQuickWindow *window); static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); static QSGMaterial *createRadialGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); + static QSGMaterial *createConicalGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node); }; #if QT_CONFIG(opengl) @@ -343,6 +345,51 @@ private: QQuickShapeGenericStrokeFillNode *m_node; }; +class QQuickShapeConicalGradientShader : public QSGMaterialShader +{ +public: + QQuickShapeConicalGradientShader(); + + void initialize() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + + static QSGMaterialType type; + +private: + int m_opacityLoc = -1; + int m_matrixLoc = -1; + int m_angleLoc = -1; + int m_translationPointLoc = -1; +}; + +class QQuickShapeConicalGradientMaterial : public QSGMaterial +{ +public: + QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node) + : m_node(node) + { + setFlag(Blending | RequiresFullMatrix); + } + + QSGMaterialType *type() const override + { + return &QQuickShapeConicalGradientShader::type; + } + + int compare(const QSGMaterial *other) const override; + + QSGMaterialShader *createShader() const override + { + return new QQuickShapeConicalGradientShader; + } + + QQuickShapeGenericStrokeFillNode *node() const { return m_node; } + +private: + QQuickShapeGenericStrokeFillNode *m_node; +}; + #endif // QT_CONFIG(opengl) QT_END_NAMESPACE diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index a08da2f3fe..27a0d6ca96 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -138,6 +139,10 @@ void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gr d.fillGradient.b = QPointF(g->focalX(), g->focalY()); d.fillGradient.v0 = g->centerRadius(); d.fillGradient.v1 = g->focalRadius(); + } else if (QQuickShapeConicalGradient *g = qobject_cast(gradient)) { + d.fillGradientActive = ConicalGradient; + d.fillGradient.a = QPointF(g->centerX(), g->centerY()); + d.fillGradient.v0 = g->angle(); } else { Q_UNREACHABLE(); } @@ -536,6 +541,37 @@ QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMate Q_ASSERT(mtl.uniLoc[4] >= 0); mtl.uniLoc[5] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "translationPoint"); Q_ASSERT(mtl.uniLoc[5] >= 0); + } else if (m == MatConicalGradient) { + static const char *fragSrc = + "#version 310 es\n" + "precision highp float;\n" + "#define INVERSE_2PI 0.1591549430918953358\n" + "uniform sampler2D gradTab;\n" + "uniform float opacity;\n" + "uniform float angle;\n" + "uniform vec2 translationPoint;\n" + "layout(location = 0) in vec2 uv;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " vec2 coord = uv - translationPoint;\n" + " float t;\n" + " if (abs(coord.y) == abs(coord.x))\n" + " t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI;\n" + " else\n" + " t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI;\n" + " fragColor = texture(gradTab, vec2(t - floor(t), 0.5)) * opacity;\n" + "}\n"; + if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) { + qWarning("NVPR: Failed to create shader pipeline for conical gradient"); + return nullptr; + } + Q_ASSERT(mtl.ppl && mtl.prg); + mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity"); + Q_ASSERT(mtl.uniLoc[1] >= 0); + mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "angle"); + Q_ASSERT(mtl.uniLoc[2] >= 0); + mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "translationPoint"); + Q_ASSERT(mtl.uniLoc[3] >= 0); } else { Q_UNREACHABLE(); } @@ -593,10 +629,7 @@ void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) { QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr; if (d->fillGradientActive) { - const QQuickShapeGradientCache::Key cacheKey(d->fillGradient.stops, d->fillGradient.spread); - QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); - tx->bind(); - + QQuickShapeGradient::SpreadMode spread = d->fillGradient.spread; if (d->fillGradientActive == QQuickAbstractPathRenderer::LinearGradient) { mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient); // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5]) @@ -624,9 +657,26 @@ void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d) f->glProgramUniform1f(mtl->prg, mtl->uniLoc[3], centerRadius); f->glProgramUniform1f(mtl->prg, mtl->uniLoc[4], focalRadius); f->glProgramUniform2f(mtl->prg, mtl->uniLoc[5], focalPoint.x(), focalPoint.y()); + } else if (d->fillGradientActive == QQuickAbstractPathRenderer::ConicalGradient) { + mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatConicalGradient); + // same old + GLfloat coeff[6] = { 1, 0, 0, + 0, 1, 0 }; + nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff); + + const QPointF centerPoint = d->fillGradient.a; + const GLfloat angle = -qDegreesToRadians(d->fillGradient.v0); + + f->glProgramUniform1f(mtl->prg, mtl->uniLoc[2], angle); + f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], centerPoint.x(), centerPoint.y()); + + spread = QQuickShapeGradient::RepeatSpread; } else { Q_UNREACHABLE(); } + const QQuickShapeGradientCache::Key cacheKey(d->fillGradient.stops, spread); + QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(cacheKey); + tx->bind(); } else { mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid); f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0], diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h index b3d92dbdbc..f6c9fc169e 100644 --- a/src/imports/shapes/qquickshapenvprrenderer_p.h +++ b/src/imports/shapes/qquickshapenvprrenderer_p.h @@ -137,6 +137,7 @@ public: MatSolid, MatLinearGradient, MatRadialGradient, + MatConicalGradient, NMaterials }; diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp index 15803dcf0a..ed13afbc7e 100644 --- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp +++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp @@ -160,6 +160,10 @@ void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient g->focalX(), g->focalY(), g->focalRadius()); setupPainterGradient(&painterGradient, *g); d.brush = QBrush(painterGradient); + } else if (QQuickShapeConicalGradient *g = qobject_cast(gradient)) { + QConicalGradient painterGradient(g->centerX(), g->centerY(), g->angle()); + setupPainterGradient(&painterGradient, *g); + d.brush = QBrush(painterGradient); } else { d.brush = QBrush(d.fillColor); } diff --git a/src/imports/shapes/shaders/conicalgradient.frag b/src/imports/shapes/shaders/conicalgradient.frag new file mode 100644 index 0000000000..af5fdd5ee0 --- /dev/null +++ b/src/imports/shapes/shaders/conicalgradient.frag @@ -0,0 +1,19 @@ +#define INVERSE_2PI 0.1591549430918953358 + +uniform sampler2D gradTabTexture; +uniform lowp float opacity; + +uniform highp float angle; + +varying highp vec2 coord; + +void main() +{ + highp float t; + if (abs(coord.y) == abs(coord.x)) + t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI; + else + t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI; + gl_FragColor = texture2D(gradTabTexture, vec2(t - floor(t), 0.5)) * opacity; + +} diff --git a/src/imports/shapes/shaders/conicalgradient.vert b/src/imports/shapes/shaders/conicalgradient.vert new file mode 100644 index 0000000000..3350b0675a --- /dev/null +++ b/src/imports/shapes/shaders/conicalgradient.vert @@ -0,0 +1,13 @@ +attribute vec4 vertexCoord; +attribute vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +varying vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shaders/conicalgradient_core.frag b/src/imports/shapes/shaders/conicalgradient_core.frag new file mode 100644 index 0000000000..e18b80159a --- /dev/null +++ b/src/imports/shapes/shaders/conicalgradient_core.frag @@ -0,0 +1,22 @@ +#version 150 core + +#define INVERSE_2PI 0.1591549430918953358 + +uniform sampler2D gradTabTexture; +uniform float opacity; + +uniform float angle; + +in vec2 coord; + +out vec4 fragColor; + +void main() +{ + float t; + if (abs(coord.y) == abs(coord.x)) + t = (atan(-coord.y + 0.002, coord.x) + angle) * INVERSE_2PI; + else + t = (atan(-coord.y, coord.x) + angle) * INVERSE_2PI; + fragColor = texture(gradTabTexture, vec2(t - floor(t), 0.5)) * opacity; +} diff --git a/src/imports/shapes/shaders/conicalgradient_core.vert b/src/imports/shapes/shaders/conicalgradient_core.vert new file mode 100644 index 0000000000..f94a56401b --- /dev/null +++ b/src/imports/shapes/shaders/conicalgradient_core.vert @@ -0,0 +1,15 @@ +#version 150 core + +in vec4 vertexCoord; +in vec4 vertexColor; + +uniform mat4 matrix; +uniform vec2 translationPoint; + +out vec2 coord; + +void main() +{ + coord = vertexCoord.xy - translationPoint; + gl_Position = matrix * vertexCoord; +} diff --git a/src/imports/shapes/shapes.qrc b/src/imports/shapes/shapes.qrc index 92912ede48..f139861693 100644 --- a/src/imports/shapes/shapes.qrc +++ b/src/imports/shapes/shapes.qrc @@ -12,5 +12,9 @@ shaders/radialgradient.frag shaders/radialgradient_core.vert shaders/radialgradient_core.frag + shaders/conicalgradient.vert + shaders/conicalgradient.frag + shaders/conicalgradient_core.vert + shaders/conicalgradient_core.frag -- cgit v1.2.3 From e8161969828a67584fa3ab1cdb2bd79eb447c1da Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 8 Aug 2017 09:23:45 +0200 Subject: Fix hardcoding font family before the font is loaded When you hardcoded the font family of an application font before the font was actually loaded, the QTextLayout would cache the glyph indexes from the fallback font, and these would later be applied to the proper font in the render thread, causing corrupted text and warnings. It is easy enough to work around this by binding the font.family property to the name property of the FontLoader, but to handle the error case more gracefully, we now check during the polish phase whether the actual font assignment has changed, and redo the QTextLayout when we have to. [ChangeLog][QtQuick][Text] Fixed an issue when the family of an application font was hardcoded and applied to text before the font itself was loaded. Task-number: QTBUG-61984 Change-Id: Ib0e80a78f3b6fe4a3b0188c69516a20d0bf4cb45 Reviewed-by: Lars Knoll --- src/quick/items/qquicktext.cpp | 8 ++++++++ src/quick/items/qquicktext_p_p.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 2e66367e85..5ca0bb5f79 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1059,6 +1059,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) if (eos != multilengthEos) truncated = true; + assignedFont = QFontInfo(font).family(); + if (elide) { if (!elideLayout) { elideLayout = new QTextLayout; @@ -2414,6 +2416,12 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data void QQuickText::updatePolish() { Q_D(QQuickText); + // If the fonts used for rendering are different from the ones used in the GUI thread, + // it means we will get warnings and corrupted text. If this case is detected, we need + // to update the text layout before creating the scenegraph nodes. + if (!d->assignedFont.isEmpty() && QFontInfo(d->font).family() != d->assignedFont) + d->polishSize = true; + if (d->polishSize) { d->updateSize(); d->polishSize = false; diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 957641ec0a..87f5162384 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -154,6 +154,8 @@ public: QQuickText::RenderType renderType; UpdateType updateType; + QString assignedFont; + bool maximumLineCountValid:1; bool updateOnComponentComplete:1; bool richText:1; -- cgit v1.2.3 From faa2f631478d425edc083e63b49cb3bcb8e47587 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 31 Jul 2017 16:45:00 +0200 Subject: shapes: Remove unused signal Gradients are set just like with Rectangle, and there is no notify signal. Remove the never emitted signal from the header. Change-Id: If3ae880f4cce4042ce6b399a6fde8013acd5a2b1 Reviewed-by: Mitch Curtis --- src/imports/shapes/qquickshape_p.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 27b02bc962..365d9644c7 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -289,7 +289,6 @@ Q_SIGNALS: void strokeStyleChanged(); void dashOffsetChanged(); void dashPatternChanged(); - void fillGradientChanged(); private: Q_DISABLE_COPY(QQuickShapePath) -- cgit v1.2.3 From 3128a680598667b24f138a274131f3906870bf09 Mon Sep 17 00:00:00 2001 From: Berthold Krevert Date: Sun, 6 Aug 2017 16:43:36 +0200 Subject: Nvpr renderer: Adapt QPen behavior when setting dashPattern Fallback to correct default values if dashPattern array is empty (while strokeStyle is set to DashLine at the same time) and check for odd sized dashPattern arrays. Change-Id: Ic1dbf9f80c5efaab3bbf138158c12979a4414595 Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapenvprrenderer.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp index 27a0d6ca96..c0a918bda5 100644 --- a/src/imports/shapes/qquickshapenvprrenderer.cpp +++ b/src/imports/shapes/qquickshapenvprrenderer.cpp @@ -387,9 +387,22 @@ void QQuickShapeNvprRenderer::updateNode() // meaning the input dash pattern and dash offset here are in width units. dst.dashOffset = src.dashOffset * src.strokeWidth; if (src.dashActive) { - dst.dashPattern.resize(src.dashPattern.count()); - for (int i = 0; i < src.dashPattern.count(); ++i) - dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + if (src.dashPattern.isEmpty()) { + // default values for DashLine as defined in qpen.cpp + dst.dashPattern.resize(2); + dst.dashPattern[0] = 4 * src.strokeWidth; // dash + dst.dashPattern[1] = 2 * src.strokeWidth; // space + } else { + dst.dashPattern.resize(src.dashPattern.count()); + for (int i = 0; i < src.dashPattern.count(); ++i) + dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth; + + // QPen expects a dash pattern of even length and so should we + if (src.dashPattern.count() % 2 != 0) { + qWarning("QQuickShapeNvprRenderNode: dash pattern not of even length"); + dst.dashPattern << src.strokeWidth; + } + } } else { dst.dashPattern.clear(); } -- cgit v1.2.3 From d419377d2041b35639a5ab81688a0bc30b5e2994 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Mon, 10 Jul 2017 13:52:35 -0500 Subject: Fix ListView::StrictlyEnforceRange with resizing delegate When fixupPosition is called for a ListView with StrictlyEnforceRange, the original reason for the move is lost, and the fixup is applied immediately. There are already checks for whether the view is moving, so expand these checks to include movement caused by highlight. Change-Id: I25f771b9a529d31dc28acb9f91fcd2b582428200 Task-number: QTBUG-33568 Reviewed-by: Robin Burchell --- src/quick/items/qquickitemview.cpp | 2 +- src/quick/items/qquickitemview_p_p.h | 1 + src/quick/items/qquicklistview.cpp | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 084b1f197a..1d0d042839 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1921,7 +1921,7 @@ void QQuickItemViewPrivate::layout() markExtentsDirty(); updateHighlight(); - if (!q->isMoving() && !q->isFlicking()) { + if (!q->isMoving() && !q->isFlicking() && !movingFromHighlight()) { fixupPosition(); refill(); } diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index b6353246e8..2c04022cde 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -363,6 +363,7 @@ protected: virtual void createHighlight() = 0; virtual void updateHighlight() = 0; virtual void resetHighlightPosition() = 0; + virtual bool movingFromHighlight() { return false; } virtual void setPosition(qreal pos) = 0; virtual void fixupPosition() = 0; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 979a3557a1..edfa970035 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -101,6 +101,7 @@ public: void createHighlight() override; void updateHighlight() override; void resetHighlightPosition() override; + bool movingFromHighlight() override; void setPosition(qreal pos) override; void layoutVisibleItems(int fromModelIndex = 0) override; @@ -945,6 +946,17 @@ void QQuickListViewPrivate::resetHighlightPosition() static_cast(highlight)->setPosition(static_cast(currentItem)->itemPosition()); } +bool QQuickListViewPrivate::movingFromHighlight() +{ + if (!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) + return false; + + return (highlightPosAnimator && highlightPosAnimator->isRunning()) || + (highlightHeightAnimator && highlightHeightAnimator->isRunning()) || + (highlightWidthAnimator && highlightWidthAnimator->isRunning()); +} + + QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion) { Q_Q(QQuickListView); -- cgit v1.2.3 From b04b7d340f39dfdde560b1303ea6a8c8ace8056e Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Thu, 10 Aug 2017 12:29:38 +0200 Subject: Initialize members in parser Change-Id: Iea5f8ca269bb759c615c4768d63a7c09ec9975fa Reviewed-by: Simon Hausmann --- src/qml/parser/qqmljslexer.cpp | 1 + src/qml/parser/qqmljsparser.cpp | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 53e67fde03..78ed5e3b2c 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -86,6 +86,7 @@ static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) Lexer::Lexer(Engine *engine) : _engine(engine) , _codePtr(0) + , _endPtr(0) , _lastLinePtr(0) , _tokenLinePtr(0) , _tokenStartPtr(0) diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index 3844fe4473..e176c6e3cf 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -92,6 +92,7 @@ Parser::Parser(Engine *engine): location_stack(0), string_stack(0), program(0), + yylval(0), first_token(0), last_token(0) { -- cgit v1.2.3 From fc3ecd2522deb3f6d8d48b66dbd89402e1ab4b53 Mon Sep 17 00:00:00 2001 From: Aleksei Ilin Date: Tue, 8 Aug 2017 12:44:42 +0200 Subject: Fix crash in QQuickAnimatedImage Check d->_movie pointer before dereferencing Task-number: QTBUG-62380 Change-Id: I62314c7c0d4a7e41fa6f8c4629d16f30d6036156 Reviewed-by: Simon Hausmann Reviewed-by: Frederik Gladhorn Reviewed-by: Shawn Rutledge --- src/quick/items/qquickanimatedimage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index adf460886a..a30d71dd1e 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -370,7 +370,7 @@ void QQuickAnimatedImage::movieRequestFinished() } #endif - if (!d->_movie->isValid()) { + if (!d->_movie || !d->_movie->isValid()) { qmlWarning(this) << "Error Reading Animated Image File " << d->url.toString(); delete d->_movie; d->_movie = 0; -- cgit v1.2.3 From 3856f9509fc7c236df3837e653fb994d08be7e58 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Fri, 11 Aug 2017 11:15:04 +0200 Subject: Doc: Fix PointerHandler QML docs qquickpinchhandler.cpp:138: warning: Missing property type for QQuickPinchHandler::minimumX qquickpinchhandler.cpp:151: warning: Missing property type for QQuickPinchHandler::maximumX qquickpinchhandler.cpp:164: warning: Missing property type for QQuickPinchHandler::minimumY qquickpinchhandler.cpp:177: warning: Missing property type for QQuickPinchHandler::maximumY qquickpinchhandler.cpp:194: warning: Missing property type for QQuickPinchHandler::minimumTouchPoints qquickpinchhandler.cpp:198: warning: Missing property type for QQuickPinchHandler::active qquickpointerdevicehandler.cpp:107: warning: Missing property type for QQuickPointerDeviceHandler::acceptedModifiers qquickpointerhandler.cpp:47: warning: C++ class QQuickPointerHandler not found: \instantiates QQuickPointerHandler qquickpointerhandler.cpp:176: warning: Missing property type for QQuickPointerHandler::enabled qquickpointerhandler.cpp:255: warning: Missing property type for QQuickPointerHandler::parent qquicktaphandler.cpp:175: warning: Missing property type for longPressThreshold qquicktaphandler.cpp:235: warning: Missing property type for gesturePolicy qquicktaphandler.cpp:252: warning: Missing property type for pressed qquicktaphandler.cpp:329: warning: Missing '\endqml' qquicktaphandler.cpp:340: warning: Missing property type for tapCount qquicktaphandler.cpp:353: warning: Missing property type for timeHeld Change-Id: I8a5bd0ec7c5603573f39f5b5f1f24d5735ba98dd Reviewed-by: Shawn Rutledge --- src/quick/handlers/qquickpointerdevicehandler.cpp | 9 +-- src/quick/handlers/qquickpointerhandler.cpp | 8 +-- src/quick/handlers/qquicktaphandler.cpp | 71 +++++++++++++---------- 3 files changed, 48 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp index 203f712179..dd0ff1f44c 100644 --- a/src/quick/handlers/qquickpointerdevicehandler.cpp +++ b/src/quick/handlers/qquickpointerdevicehandler.cpp @@ -80,16 +80,17 @@ void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::Po } /*! - \qmlproperty QQuickPointerDeviceHandler::acceptedModifiers + \qmlproperty int PointerDeviceHandler::acceptedModifiers If this property is set, it will require the given keyboard modifiers to be pressed in order to react to pointer events, and otherwise ignore them. - If this property is set to Qt.KeyboardModifierMask (the default value), + If this property is set to \c Qt.KeyboardModifierMask (the default value), then the PointerHandler ignores the modifier keys. - For example an Item could have two handlers of the same type, one of which - is enabled only if the required keyboard modifiers are pressed: + For example, an \l [QML] Item could have two handlers of the same type, + one of which is enabled only if the required keyboard modifiers are + pressed: \qml Item { diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 1ed80c0a5b..bcccfac318 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -46,10 +46,10 @@ Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active") /*! \qmltype PointerHandler - \instantiates QQuickPointerHandler + //! \instantiates QQuickPointerHandler \inqmlmodule QtQuick \ingroup qtquick-handlers - \brief Handler for pointer events + \brief Handler for pointer events. PointerHandler is a handler for pointer events regardless of source. They may represent events from a touch, mouse or tablet device. @@ -165,7 +165,7 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const } /*! - \qmlproperty QQuickPointerHandler::enabled + \qmlproperty bool PointerHandler::enabled If a PointerHandler is disabled, it will reject all events and no signals will be emitted. @@ -241,7 +241,7 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event) } /*! - \qmlproperty QQuickPointerHandler::parent + \qmlproperty Item PointerHandler::parent The \l Item which is the scope of the handler; the Item in which it was declared. The handler will handle events on behalf of this Item, which means a diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 272c6de000..1bcf42a073 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -56,7 +56,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); \instantiates QQuickTapHandler \inqmlmodule QtQuick \ingroup qtquick-handlers - \brief Handler for taps and clicks + \brief Handler for taps and clicks. TapHandler is a handler for taps on a touchscreen or clicks on a mouse. @@ -65,9 +65,9 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1); whether the press and release occur close together: if you press the button and then change your mind, you need to drag all the way off the edge of the button in order to cancel the click. Therefore the default - \l gesturePolicy is \l ReleaseWithinBounds. If you want to require + \l gesturePolicy is \c TapHandler.ReleaseWithinBounds. If you want to require that the press and release are close together in both space and time, - set it to \l DragThreshold. + set it to \c TapHandler.DragThreshold. For multi-tap gestures (double-tap, triple-tap etc.), the distance moved must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and @@ -164,7 +164,7 @@ void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point) } /*! - \qmlproperty longPressThreshold + \qmlproperty real TapHandler::longPressThreshold The time in seconds that an event point must be pressed in order to trigger a long press gesture and emit the \l longPressed() signal. @@ -202,7 +202,7 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) } /*! - \qmlproperty gesturePolicy + \qmlproperty enumeration TapHandler::gesturePolicy The spatial constraint for a tap or long press gesture to be recognized, in addition to the constraint that the release must occur before @@ -211,26 +211,30 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) If the spatial constraint is violated, \l isPressed transitions immediately from true to false, regardless of the time held. - \c DragThreshold means that the event point must not move significantly. - If the mouse, finger or stylus moves past the system-wide drag threshold - (QStyleHints::startDragDistance), the tap gesture is canceled, even if - the button or finger is still pressed. This policy can be useful whenever - TapHandler needs to cooperate with other pointer handlers (for example - \l DragHandler), because in this case TapHandler will never grab. - - \c WithinBounds means that if the event point leaves the bounds of the - \l target item, the tap gesture is canceled. The TapHandler will grab on - press, but release the grab as soon as the boundary constraint is no - longer satisfied. - - \c ReleaseWithinBounds (the default value) means that at the time of release - (the mouse button is released or the finger is lifted), if the event point - is outside the bounds of the \l target item, a tap gesture is not - recognized. This is the default value, because it corresponds to typical - button behavior: you can cancel a click by dragging outside the button, and - you can also change your mind by dragging back inside the button before - release. Note that it's necessary for TapHandler to grab on press and - retain it until release (greedy grab) in order to detect this gesture. + \value TapHandler.DragThreshold + The event point must not move significantly. If the mouse, finger + or stylus moves past the system-wide drag threshold + (QStyleHints::startDragDistance), the tap gesture is canceled, even + if the button or finger is still pressed. This policy can be useful + whenever TapHandler needs to cooperate with other pointer handlers + (for example \l DragHandler), because in this case TapHandler will + never grab. + + \value TapHandler.WithinBounds + If the event point leaves the bounds of the \l target item, the tap + gesture is canceled. The TapHandler will grab on press, but release + the grab as soon as the boundary constraint is no longer satisfied. + + \value TapHandler.ReleaseWithinBounds + (the default value) At the time of release (the mouse button is + released or the finger is lifted), if the event point is outside + the bounds of the \l target item, a tap gesture is not recognized. + This is the default value, because it corresponds to typical button + behavior: you can cancel a click by dragging outside the button, + and you can also change your mind by dragging back inside the button + before release. Note that it's necessary for TapHandler to grab on + press and retain it until release (greedy grab) in order to detect + this gesture. */ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy) { @@ -242,12 +246,13 @@ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gestureP } /*! - \qmlproperty pressed + \qmlproperty bool TapHandler::pressed + \readonly - This property will be true whenever the mouse or touch point is pressed, + Holds true whenever the mouse or touch point is pressed, and any movement since the press is compliant with the current \l gesturePolicy. When the event point is released or the policy is - violated, pressed will change to false. + violated, \e pressed will change to false. */ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point) { @@ -320,11 +325,11 @@ void QQuickTapHandler::updateTimeHeld() } /*! - \qmlproperty tapCount + \qmlproperty int TapHandler::tapCount The number of taps which have occurred within the time and space constraints to be considered a single gesture. For example, to detect - a double-tap, you can write + a double-tap, you can write: \qml Rectangle { @@ -335,10 +340,11 @@ void QQuickTapHandler::updateTimeHeld() onTapped: if (tapCount == 2) doubleTap() } } + \endqml */ /*! - \qmlproperty timeHeld + \qmlproperty real TapHandler::timeHeld The amount of time in seconds that a pressed point has been held, without moving beyond the drag threshold. It will be updated at least once per @@ -347,7 +353,8 @@ void QQuickTapHandler::updateTimeHeld() possible to trigger one of a series of actions depending on how long the press is held. - A value less than zero means no point is being held within this handler's Item. + A value of less than zero means no point is being held within this + handler's \l [QML] Item. */ QT_END_NAMESPACE -- cgit v1.2.3 From 17513266b81e88b84fda250734f07c94a198fabc Mon Sep 17 00:00:00 2001 From: Anton Kreuzkamp Date: Wed, 26 Apr 2017 16:31:48 +0200 Subject: Add API to learn about QQmlBinding's dependencies Adds a method `dependencies()` to QQmlBinding, that returns a QVector of all properties the binding depends on. The API is meant to be used in debugging tools (e.g. in GammaRay). Also adds a public method subBindings() to QQmlValueTypeProxyBinding in order to be able to access their dependencies. Change-Id: Ib833703ec9e632661626c4532b8d73997f38e62b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 32 +++++++++++++++++++++++++++++++ src/qml/qml/qqmlbinding_p.h | 9 +++++++++ src/qml/qml/qqmljavascriptexpression_p.h | 14 +++++++------- src/qml/qml/qqmlnotifier_p.h | 5 +++-- src/qml/qml/qqmlvaluetypeproxybinding.cpp | 5 +++++ src/qml/qml/qqmlvaluetypeproxybinding_p.h | 1 + 6 files changed, 57 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 84f30af066..566fbb86ac 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -54,6 +54,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -579,6 +580,37 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD } } +QVector QQmlBinding::dependencies() const +{ + QVector dependencies; + if (!m_target.data()) + return dependencies; + + for (const auto &guardList : { permanentGuards, activeGuards }) { + for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) { + if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*. + continue; + + QObject *senderObject = guard->senderAsObject(); + if (!senderObject) + continue; + + const QMetaObject *senderMeta = senderObject->metaObject(); + if (!senderMeta) + continue; + + for (int i = 0; i < senderMeta->propertyCount(); i++) { + QMetaProperty property = senderMeta->property(i); + if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) { + dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name()))); + } + } + } + } + + return dependencies; +} + class QObjectPointerBinding: public QQmlNonbindingBinding { QQmlMetaObject targetMetaObject; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 1c894148e4..38d59a8919 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -102,6 +102,15 @@ public: QString expressionIdentifier() const override; void expressionChanged() override; + /** + * This method returns a snapshot of the currently tracked dependencies of + * this binding. The dependencies can change upon reevaluation. This method is + * used in GammaRay to visualize binding hierarchies. + * + * Call this method from the UI thread. + */ + QVector dependencies() const; + protected: virtual void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) = 0; diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 8b4b64f4d2..e9a9f4feee 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -162,6 +162,13 @@ protected: void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); void setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit); + // We store some flag bits in the following flag pointers. + // activeGuards:flag1 - notifyOnValueChanged + // activeGuards:flag2 - useSharedContext + QBiPointer m_scopeObject; + QForwardFieldList activeGuards; + QForwardFieldList permanentGuards; + private: friend class QQmlContextData; friend class QQmlPropertyCapture; @@ -170,13 +177,6 @@ private: QQmlDelayedError *m_error; - // We store some flag bits in the following flag pointers. - // activeGuards:flag1 - notifyOnValueChanged - // activeGuards:flag2 - useSharedContext - QBiPointer m_scopeObject; - QForwardFieldList activeGuards; - QForwardFieldList permanentGuards; - QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index 6e91369793..a99b13f155 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -109,6 +109,9 @@ public: inline int signalIndex() const { return sourceSignal; } + inline QObject *senderAsObject() const; + inline QQmlNotifier *senderAsNotifier() const; + private: friend class QQmlData; friend class QQmlNotifier; @@ -117,8 +120,6 @@ private: // endpoint is connected to. While the endpoint is notifying, the // senderPtr points to another qintptr that contains this value. qintptr senderPtr; - inline QObject *senderAsObject() const; - inline QQmlNotifier *senderAsNotifier() const; Callback callback:4; int needsConnectNotify:1; diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index 4e2e7b06c7..7a3e4b2df4 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -72,6 +72,11 @@ bool QQmlValueTypeProxyBinding::isValueTypeProxy() const return true; } +QQmlAbstractBinding *QQmlValueTypeProxyBinding::subBindings() const +{ + return m_bindings.data(); +} + QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(QQmlPropertyIndex propertyIndex) const { QQmlAbstractBinding *binding = m_bindings.data(); diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index 92b5470f39..ba0d305bd9 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -60,6 +60,7 @@ class QQmlValueTypeProxyBinding : public QQmlAbstractBinding public: QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex); + QQmlAbstractBinding *subBindings() const; QQmlAbstractBinding *binding(QQmlPropertyIndex targetPropertyIndex) const; void removeBindings(quint32 mask); -- cgit v1.2.3 From 1b21b73e89942d567c90a17a3bf7a7ecae3de258 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 25 Jul 2017 12:48:33 +0200 Subject: QQuickTextInput: Allow going from an Acceptable to an Intermediate state When editing text with a validator set then it can happen that it would no longer be in the acceptable state. This ensures that it does not prevent editing the text when an input mask is used to go back to an Intermediate state. Change-Id: I6da533d18035e9da468939c28a961bc8f2f3090b Reviewed-by: Robin Burchell Reviewed-by: Mitch Curtis --- src/quick/items/qquicktextinput.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 43899ba457..28b089da49 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -3545,18 +3545,23 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo #endif if (m_maskData) { + m_validInput = true; if (m_text.length() != m_maxLength) { + m_validInput = false; m_acceptableInput = false; } else { for (int i = 0; i < m_maxLength; ++i) { if (m_maskData[i].separator) { if (m_text.at(i) != m_maskData[i].maskChar) { + m_validInput = false; m_acceptableInput = false; break; } } else { if (!isValidInput(m_text.at(i), m_maskData[i].maskChar)) { m_acceptableInput = false; + if (m_text.at(i) != m_blank) + m_validInput = false; break; } } -- cgit v1.2.3 From 684b0e6609387bdfbf99f24fb0e18a8bbab02bcb Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 17 Mar 2017 17:48:55 +0100 Subject: Remove hack to make QtC understand that typeof null is "object" Newer Qt Creators know that. Change-Id: If4d3da5a46b49864a77854fff79e54cef2ea26d6 Reviewed-by: hjk --- src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index e89b7a63d4..4573fb9d9a 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -149,8 +149,6 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution dict.insert(valueKey, QJsonValue::Undefined); return 0; case QV4::Value::Null_Type: - // "null" is not the correct type, but we leave this in until QtC can deal with "object" - dict.insert(QStringLiteral("type"), QStringLiteral("null")); dict.insert(valueKey, QJsonValue::Null); return 0; case QV4::Value::Boolean_Type: -- cgit v1.2.3 From 6034d89bdd907d4795c19e8ac752f4eb51c82a94 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 9 Aug 2017 17:52:01 +0200 Subject: Mention the t (zone) format specifier for formatDateTime It was missing from the QML doc for Qt.formatDateTime(). Change-Id: Ic496fe0a7508dca2534ba8d67f0e82305fb2e31b Reviewed-by: Lars Knoll --- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 8cc0b32168..64ab436363 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -851,6 +851,8 @@ In addition the following expressions can be used to specify the time: \li use AM/PM display. \e AP will be replaced by either "AM" or "PM". \row \li ap \li use am/pm display. \e ap will be replaced by either "am" or "pm". + \row \li t + \li include a time-zone indicator. \endtable All other input characters will be ignored. Any sequence of characters that -- cgit v1.2.3 From 3513995d8fde7f002977275463fcea1b86f4a693 Mon Sep 17 00:00:00 2001 From: Aleksei Ilin Date: Tue, 8 Aug 2017 12:44:42 +0200 Subject: Fix crash in QQuickAnimatedImage Check d->_movie pointer before dereferencing Task-number: QTBUG-62380 Change-Id: I62314c7c0d4a7e41fa6f8c4629d16f30d6036156 Reviewed-by: Simon Hausmann Reviewed-by: Frederik Gladhorn Reviewed-by: Shawn Rutledge (cherry-picked from fc3ecd2522deb3f6d8d48b66dbd89402e1ab4b53) --- src/quick/items/qquickanimatedimage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index adf460886a..a30d71dd1e 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -370,7 +370,7 @@ void QQuickAnimatedImage::movieRequestFinished() } #endif - if (!d->_movie->isValid()) { + if (!d->_movie || !d->_movie->isValid()) { qmlWarning(this) << "Error Reading Animated Image File " << d->url.toString(); delete d->_movie; d->_movie = 0; -- cgit v1.2.3 From b160a38b7babeb3cb009748a008075ab07926428 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 14 Aug 2017 10:29:53 +0200 Subject: Update layout when QQuickWidget changes size Task-number: QTBUG-47722 Change-Id: I612f89945961a3995878c424bf27dbbedf8375c8 Reviewed-by: Laszlo Agocs --- src/quickwidgets/qquickwidget.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 2c3c72d5f1..f71cf3c41b 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -768,6 +768,7 @@ void QQuickWidgetPrivate::updateSize() QSize newSize = QSize(root->width(), root->height()); if (newSize.isValid() && newSize != q->size()) { q->resize(newSize); + q->updateGeometry(); } } else if (resizeMode == QQuickWidget::SizeRootObjectToView) { bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width()); -- cgit v1.2.3 From 74ca31771f9f936cf4c4bbe9216183d46fcf3617 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Mon, 14 Aug 2017 06:30:25 +0200 Subject: Remove unnecesary if and dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 182648 Dereference before null check There may be a null pointer dereference, or else the comparison against null is unnecessary. In QQuickEventPoint::estimatedVelocity(): All paths that lead to this null pointer comparison already dereference the pointer earlier (CWE-476) Coverity-Id: 182648 Change-Id: Ie8ca1a58b9c11f7c459d719ccd0a3f3fa9eaeea5 Reviewed-by: Shawn Rutledge Reviewed-by: Frederik Gladhorn Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents.cpp | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 1e0d268f93..af2ba232f4 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -826,26 +826,23 @@ QVector2D QQuickEventPoint::estimatedVelocity() const prevPoint->pos = QPointF(); g_previousPointData->insert(m_pointId, prevPoint); } - if (prevPoint) { - const ulong timeElapsed = m_timestamp - prevPoint->timestamp; - if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint - return m_velocity; - - QVector2D newVelocity; - if (prevPoint->timestamp != 0) - newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed; - - // VERY simple kalman filter: does a weighted average - // where the older velocities get less and less significant - static const float KalmanGain = 0.7f; - QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain); - - prevPoint->velocity = filteredVelocity; - prevPoint->pos = m_scenePos; - prevPoint->timestamp = m_timestamp; - return filteredVelocity; - } - return QVector2D(); + const ulong timeElapsed = m_timestamp - prevPoint->timestamp; + if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint + return m_velocity; + + QVector2D newVelocity; + if (prevPoint->timestamp != 0) + newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed; + + // VERY simple kalman filter: does a weighted average + // where the older velocities get less and less significant + static const float KalmanGain = 0.7f; + QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain); + + prevPoint->velocity = filteredVelocity; + prevPoint->pos = m_scenePos; + prevPoint->timestamp = m_timestamp; + return filteredVelocity; } /*! -- cgit v1.2.3 From cf1dcc857a5b9fdc55f21508c812bb4110cf93b7 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 17 Aug 2017 18:24:39 -0500 Subject: Support explicit enum value declaration in QML Allow declarations such as: enum MyEnum { Value1 = 1, Value2 } Not all features of C++ enums are supported. Specifically, we don't yet allow: * Negative numbers (Value1 = -1) * Assignment of other values (Value2 = Value1) Change-Id: I4776f8d86bd0c8688c7dd8b7d4ccb2f72fdfe721 Task-number: QTBUG-14861 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 11 +- src/qml/parser/qqmljs.g | 22 + src/qml/parser/qqmljsast_p.h | 18 +- src/qml/parser/qqmljsgrammar.cpp | 1127 +++++++++++++++++++----------------- src/qml/parser/qqmljsgrammar_p.h | 12 +- src/qml/parser/qqmljsparser.cpp | 379 ++++++------ src/qml/parser/qqmljsparser_p.h | 4 +- 7 files changed, 833 insertions(+), 740 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index b58920d812..a26c80e093 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #ifndef V4_BOOTSTRAP #include @@ -740,16 +741,20 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) enumeration->enumValues = New>(); QQmlJS::AST::UiEnumMemberList *e = node->members; - int i = -1; while (e) { EnumValue *enumValue = New(); QString member = e->member.toString(); enumValue->nameIndex = registerString(member); - enumValue->value = ++i; - if (member.at(0).isLower()) COMPILE_EXCEPTION(e->memberToken, tr("Enum names must begin with an upper case letter")); + double part; + if (std::modf(e->value, &part) != 0.0) + COMPILE_EXCEPTION(e->valueToken, tr("Enum value must be an integer")); + if (e->value > std::numeric_limits::max() || e->value < std::numeric_limits::min()) + COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range")); + enumValue->value = e->value; + enumValue->location.line = e->memberToken.startLine; enumValue->location.column = e->memberToken.startColumn; enumeration->enumValues->append(enumValue); diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 395e62e657..667bcd1801 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -1230,6 +1230,17 @@ case $rule_number: { } ./ +EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; +/. +case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval); + node->memberToken = loc(1); + node->valueToken = loc(3); + sym(1).Node = node; + break; +} +./ + EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER; /. case $rule_number: { @@ -1240,6 +1251,17 @@ case $rule_number: { } ./ +EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; +/. +case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval); + node->memberToken = loc(3); + node->valueToken = loc(5); + sym(1).Node = node; + break; +} +./ + JsIdentifier: T_IDENTIFIER; JsIdentifier: T_PROPERTY ; diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index d458b2cd35..aa48accfe0 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -2791,12 +2791,21 @@ class QML_PARSER_EXPORT UiEnumMemberList: public Node { QQMLJS_DECLARE_AST_NODE(UiEnumMemberList) public: - UiEnumMemberList(const QStringRef &member) - : next(this), member(member) + UiEnumMemberList(const QStringRef &member, double v = 0.0) + : next(this), member(member), value(v) { kind = K; } UiEnumMemberList(UiEnumMemberList *previous, const QStringRef &member) : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + value = previous->value + 1; + } + + UiEnumMemberList(UiEnumMemberList *previous, const QStringRef &member, double v) + : member(member), value(v) { kind = K; next = previous->next; @@ -2807,7 +2816,8 @@ public: { return memberToken; } SourceLocation lastSourceLocation() const override - { return next ? next->lastSourceLocation() : memberToken; } + { return next ? next->lastSourceLocation() : + valueToken.isValid() ? valueToken : memberToken; } void accept0(Visitor *visitor) override; @@ -2821,7 +2831,9 @@ public: // attributes UiEnumMemberList *next; QStringRef member; + double value; SourceLocation memberToken; + SourceLocation valueToken; }; class QML_PARSER_EXPORT UiEnumDeclaration: public UiObjectMember diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp index 8e47898e2f..f345990ff9 100644 --- a/src/qml/parser/qqmljsgrammar.cpp +++ b/src/qml/parser/qqmljsgrammar.cpp @@ -64,36 +64,36 @@ const short QQmlJSGrammar::lhs [] = { 132, 132, 132, 132, 132, 132, 132, 113, 140, 140, 140, 140, 141, 141, 142, 142, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 145, 145, 126, - 126, 126, 126, 126, 126, 126, 146, 146, 146, 146, + 113, 113, 113, 113, 113, 113, 113, 145, 145, 145, + 145, 126, 126, 126, 126, 126, 126, 126, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 131, 148, 148, 148, 148, 147, - 147, 152, 152, 152, 150, 150, 153, 153, 153, 153, + 146, 146, 146, 146, 146, 146, 131, 148, 148, 148, + 148, 147, 147, 152, 152, 152, 150, 150, 153, 153, + 153, 153, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, - 156, 156, 156, 157, 157, 122, 122, 122, 122, 122, - 160, 160, 161, 161, 161, 161, 159, 159, 162, 162, - 163, 163, 164, 164, 164, 165, 165, 165, 165, 165, - 165, 165, 165, 165, 165, 166, 166, 166, 166, 167, - 167, 167, 168, 168, 168, 168, 169, 169, 169, 169, - 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, - 171, 171, 171, 171, 172, 172, 172, 172, 172, 173, - 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, - 178, 179, 179, 180, 180, 181, 181, 182, 182, 183, - 183, 184, 184, 151, 151, 185, 185, 186, 186, 186, - 186, 186, 186, 186, 186, 186, 186, 186, 186, 111, - 111, 187, 187, 188, 188, 189, 189, 110, 110, 110, + 156, 156, 156, 156, 156, 157, 157, 122, 122, 122, + 122, 122, 160, 160, 161, 161, 161, 161, 159, 159, + 162, 162, 163, 163, 164, 164, 164, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, + 166, 167, 167, 167, 168, 168, 168, 168, 169, 169, + 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, + 170, 171, 171, 171, 171, 171, 172, 172, 172, 172, + 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, + 177, 178, 178, 179, 179, 180, 180, 181, 181, 182, + 182, 183, 183, 184, 184, 151, 151, 185, 185, 186, + 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, + 186, 111, 111, 187, 187, 188, 188, 189, 189, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 133, 198, 198, 197, 197, 144, 144, 199, - 199, 199, 200, 200, 202, 202, 201, 203, 206, 204, - 204, 207, 205, 205, 134, 135, 135, 136, 136, 190, - 190, 190, 190, 190, 190, 190, 190, 191, 191, 191, - 191, 192, 192, 192, 192, 193, 193, 137, 138, 208, - 208, 211, 211, 209, 209, 212, 210, 194, 195, 195, - 139, 139, 139, 213, 214, 196, 196, 215, 143, 158, - 158, 216, 216, 155, 155, 154, 154, 217, 114, 114, - 218, 218, 112, 112, 149, 149, 219 + 110, 110, 110, 110, 133, 198, 198, 197, 197, 144, + 144, 199, 199, 199, 200, 200, 202, 202, 201, 203, + 206, 204, 204, 207, 205, 205, 134, 135, 135, 136, + 136, 190, 190, 190, 190, 190, 190, 190, 190, 191, + 191, 191, 191, 192, 192, 192, 192, 193, 193, 137, + 138, 208, 208, 211, 211, 209, 209, 212, 210, 194, + 195, 195, 139, 139, 139, 213, 214, 196, 196, 215, + 143, 158, 158, 216, 216, 155, 155, 154, 154, 217, + 114, 114, 218, 218, 112, 112, 149, 149, 219 }; const short QQmlJSGrammar::rhs [] = { @@ -104,115 +104,116 @@ const short QQmlJSGrammar::rhs [] = { 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 0, 1, 2, 4, 6, 6, 3, 3, 7, 7, 4, 4, 5, 5, 8, 8, 5, 6, - 6, 10, 6, 7, 1, 1, 5, 1, 3, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 2, 3, 3, 4, - 5, 3, 4, 3, 1, 1, 2, 3, 4, 1, - 2, 3, 7, 8, 1, 3, 1, 1, 1, 1, + 6, 10, 6, 7, 1, 1, 5, 1, 3, 3, + 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 3, 4, 5, 3, 4, 3, 1, 1, 2, 3, + 4, 1, 2, 3, 7, 8, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 4, 3, 5, - 1, 2, 4, 4, 4, 3, 0, 1, 1, 3, - 1, 1, 1, 2, 2, 1, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 1, 3, 3, 3, 1, - 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, - 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, - 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 3, 5, 1, 2, 4, 4, 4, 3, 0, 1, + 1, 3, 1, 1, 1, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 1, 3, 3, + 3, 1, 3, 3, 1, 3, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, + 3, 1, 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 5, 1, 5, 1, 3, 1, 3, 1, 1, 1, + 3, 1, 5, 1, 5, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 3, 0, 1, 1, 3, 0, 1, 1, 1, 1, + 1, 1, 3, 0, 1, 1, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 1, 2, 0, 1, 3, 3, 1, - 1, 1, 1, 3, 1, 3, 2, 2, 2, 0, - 1, 2, 0, 1, 1, 2, 2, 7, 5, 7, - 7, 7, 5, 9, 10, 7, 8, 2, 2, 3, - 3, 2, 2, 3, 3, 3, 3, 5, 5, 3, - 5, 1, 2, 0, 1, 4, 3, 3, 3, 3, - 3, 3, 4, 5, 2, 2, 2, 1, 8, 8, - 7, 1, 3, 0, 1, 0, 1, 1, 1, 1, - 1, 2, 1, 1, 0, 1, 2 + 1, 1, 1, 1, 3, 1, 2, 0, 1, 3, + 3, 1, 1, 1, 1, 3, 1, 3, 2, 2, + 2, 0, 1, 2, 0, 1, 1, 2, 2, 7, + 5, 7, 7, 7, 5, 9, 10, 7, 8, 2, + 2, 3, 3, 2, 2, 3, 3, 3, 3, 5, + 5, 3, 5, 1, 2, 0, 1, 4, 3, 3, + 3, 3, 3, 3, 4, 5, 2, 2, 2, 1, + 8, 8, 7, 1, 3, 0, 1, 0, 1, 1, + 1, 1, 1, 2, 1, 1, 0, 1, 2 }; const short QQmlJSGrammar::action_default [] = { - 0, 0, 28, 0, 0, 0, 28, 0, 193, 260, - 224, 232, 228, 172, 244, 220, 3, 157, 88, 173, - 236, 240, 161, 190, 171, 176, 156, 210, 197, 0, - 95, 96, 91, 0, 85, 80, 365, 0, 0, 0, - 0, 93, 0, 0, 89, 92, 84, 0, 0, 81, - 83, 86, 82, 94, 87, 0, 90, 0, 0, 186, - 0, 0, 173, 192, 175, 174, 0, 0, 0, 188, - 189, 187, 191, 0, 221, 0, 0, 0, 0, 211, - 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, - 195, 196, 194, 199, 203, 202, 200, 198, 213, 212, - 214, 0, 229, 0, 225, 0, 0, 167, 154, 166, - 155, 121, 122, 123, 149, 124, 151, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 150, - 137, 138, 152, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 153, 0, 0, 165, 261, 168, 0, - 169, 0, 170, 164, 0, 257, 250, 248, 255, 256, - 254, 253, 259, 252, 251, 249, 258, 245, 0, 233, - 0, 0, 237, 0, 0, 241, 0, 0, 167, 159, - 0, 158, 0, 163, 177, 0, 354, 354, 355, 0, - 352, 0, 353, 0, 356, 268, 275, 274, 282, 270, - 0, 271, 0, 357, 0, 364, 272, 273, 88, 278, - 276, 361, 358, 363, 279, 0, 291, 0, 0, 0, - 0, 348, 0, 365, 290, 262, 305, 0, 0, 0, - 292, 0, 0, 280, 281, 0, 269, 277, 306, 307, - 0, 354, 0, 0, 356, 0, 349, 350, 0, 338, - 362, 0, 322, 323, 324, 325, 0, 318, 319, 320, - 321, 346, 347, 0, 0, 0, 0, 0, 310, 311, - 312, 266, 264, 226, 234, 230, 246, 222, 267, 0, - 173, 238, 242, 215, 204, 0, 0, 223, 0, 0, - 0, 0, 216, 0, 0, 0, 0, 0, 208, 206, - 209, 207, 205, 218, 217, 219, 0, 231, 0, 227, - 0, 265, 173, 0, 247, 262, 263, 0, 262, 0, - 0, 314, 0, 0, 0, 316, 0, 235, 0, 0, - 239, 0, 0, 243, 303, 0, 295, 304, 298, 0, - 302, 0, 262, 296, 0, 262, 0, 0, 315, 0, - 0, 0, 317, 0, 0, 0, 309, 0, 308, 88, - 115, 366, 0, 0, 120, 284, 287, 0, 121, 291, - 124, 151, 126, 127, 91, 132, 133, 85, 134, 290, - 137, 89, 92, 262, 86, 94, 140, 87, 142, 90, - 144, 145, 292, 147, 148, 153, 0, 117, 116, 119, - 103, 118, 102, 0, 112, 285, 283, 0, 0, 0, - 356, 0, 113, 161, 162, 167, 0, 160, 0, 326, - 327, 0, 354, 0, 0, 356, 0, 114, 0, 0, - 0, 329, 334, 332, 335, 0, 0, 333, 334, 0, - 330, 0, 331, 286, 337, 0, 286, 336, 0, 339, - 340, 0, 286, 341, 342, 0, 0, 343, 0, 0, - 0, 344, 345, 179, 178, 0, 0, 0, 313, 0, - 0, 0, 328, 300, 293, 0, 301, 297, 0, 299, - 288, 0, 289, 294, 0, 0, 356, 0, 351, 106, - 0, 0, 110, 97, 0, 99, 108, 0, 100, 109, - 111, 101, 107, 98, 0, 104, 183, 181, 185, 182, - 180, 184, 359, 6, 360, 4, 2, 75, 105, 0, - 0, 0, 81, 83, 82, 37, 5, 0, 76, 0, + 0, 0, 28, 0, 0, 0, 28, 0, 195, 262, + 226, 234, 230, 174, 246, 222, 3, 159, 90, 175, + 238, 242, 163, 192, 173, 178, 158, 212, 199, 0, + 97, 98, 93, 0, 87, 82, 367, 0, 0, 0, + 0, 95, 0, 0, 91, 94, 86, 0, 0, 83, + 85, 88, 84, 96, 89, 0, 92, 0, 0, 188, + 0, 0, 175, 194, 177, 176, 0, 0, 0, 190, + 191, 189, 193, 0, 223, 0, 0, 0, 0, 213, + 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, + 197, 198, 196, 201, 205, 204, 202, 200, 215, 214, + 216, 0, 231, 0, 227, 0, 0, 169, 156, 168, + 157, 123, 124, 125, 151, 126, 153, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 152, + 139, 140, 154, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 155, 0, 0, 167, 263, 170, 0, + 171, 0, 172, 166, 0, 259, 252, 250, 257, 258, + 256, 255, 261, 254, 253, 251, 260, 247, 0, 235, + 0, 0, 239, 0, 0, 243, 0, 0, 169, 161, + 0, 160, 0, 165, 179, 0, 356, 356, 357, 0, + 354, 0, 355, 0, 358, 270, 277, 276, 284, 272, + 0, 273, 0, 359, 0, 366, 274, 275, 90, 280, + 278, 363, 360, 365, 281, 0, 293, 0, 0, 0, + 0, 350, 0, 367, 292, 264, 307, 0, 0, 0, + 294, 0, 0, 282, 283, 0, 271, 279, 308, 309, + 0, 356, 0, 0, 358, 0, 351, 352, 0, 340, + 364, 0, 324, 325, 326, 327, 0, 320, 321, 322, + 323, 348, 349, 0, 0, 0, 0, 0, 312, 313, + 314, 268, 266, 228, 236, 232, 248, 224, 269, 0, + 175, 240, 244, 217, 206, 0, 0, 225, 0, 0, + 0, 0, 218, 0, 0, 0, 0, 0, 210, 208, + 211, 209, 207, 220, 219, 221, 0, 233, 0, 229, + 0, 267, 175, 0, 249, 264, 265, 0, 264, 0, + 0, 316, 0, 0, 0, 318, 0, 237, 0, 0, + 241, 0, 0, 245, 305, 0, 297, 306, 300, 0, + 304, 0, 264, 298, 0, 264, 0, 0, 317, 0, + 0, 0, 319, 0, 0, 0, 311, 0, 310, 90, + 117, 368, 0, 0, 122, 286, 289, 0, 123, 293, + 126, 153, 128, 129, 93, 134, 135, 87, 136, 292, + 139, 91, 94, 264, 88, 96, 142, 89, 144, 92, + 146, 147, 294, 149, 150, 155, 0, 119, 118, 121, + 105, 120, 104, 0, 114, 287, 285, 0, 0, 0, + 358, 0, 115, 163, 164, 169, 0, 162, 0, 328, + 329, 0, 356, 0, 0, 358, 0, 116, 0, 0, + 0, 331, 336, 334, 337, 0, 0, 335, 336, 0, + 332, 0, 333, 288, 339, 0, 288, 338, 0, 341, + 342, 0, 288, 343, 344, 0, 0, 345, 0, 0, + 0, 346, 347, 181, 180, 0, 0, 0, 315, 0, + 0, 0, 330, 302, 295, 0, 303, 299, 0, 301, + 290, 0, 291, 296, 0, 0, 358, 0, 353, 108, + 0, 0, 112, 99, 0, 101, 110, 0, 102, 111, + 113, 103, 109, 100, 0, 106, 185, 183, 187, 184, + 182, 186, 361, 6, 362, 4, 2, 75, 107, 0, + 0, 0, 83, 85, 84, 37, 5, 0, 76, 0, 51, 50, 49, 0, 0, 51, 0, 0, 0, 52, 0, 67, 68, 0, 65, 0, 66, 41, 42, 43, 44, 46, 47, 71, 45, 0, 0, 0, 78, 0, - 77, 79, 0, 51, 0, 0, 0, 0, 0, 61, - 0, 62, 0, 0, 32, 0, 0, 72, 33, 0, - 36, 34, 30, 0, 35, 31, 0, 63, 0, 64, - 161, 0, 69, 73, 0, 0, 0, 0, 161, 286, - 0, 70, 88, 121, 291, 124, 151, 126, 127, 91, - 132, 133, 134, 290, 137, 89, 92, 262, 94, 140, - 87, 142, 90, 144, 145, 292, 147, 148, 153, 74, - 0, 59, 53, 60, 54, 0, 0, 0, 0, 56, - 0, 57, 58, 55, 0, 0, 0, 0, 48, 0, - 38, 39, 0, 40, 8, 0, 0, 9, 0, 11, - 0, 10, 0, 1, 27, 15, 14, 26, 13, 12, - 29, 7, 0, 18, 0, 19, 0, 24, 25, 0, - 20, 21, 0, 22, 23, 16, 17, 367 + 77, 80, 0, 81, 0, 79, 0, 51, 0, 0, + 0, 0, 0, 61, 0, 62, 0, 0, 32, 0, + 0, 72, 33, 0, 36, 34, 30, 0, 35, 31, + 0, 63, 0, 64, 163, 0, 69, 73, 0, 0, + 0, 0, 163, 288, 0, 70, 90, 123, 293, 126, + 153, 128, 129, 93, 134, 135, 136, 292, 139, 91, + 94, 264, 96, 142, 89, 144, 92, 146, 147, 294, + 149, 150, 155, 74, 0, 59, 53, 60, 54, 0, + 0, 0, 0, 56, 0, 57, 58, 55, 0, 0, + 0, 0, 48, 0, 38, 39, 0, 40, 8, 0, + 0, 9, 0, 11, 0, 10, 0, 1, 27, 15, + 14, 26, 13, 12, 29, 7, 0, 18, 0, 19, + 0, 24, 25, 0, 20, 21, 0, 22, 23, 16, + 17, 369 }; const short QQmlJSGrammar::goto_default [] = { - 7, 663, 213, 200, 211, 526, 513, 658, 671, 512, - 657, 661, 659, 667, 22, 664, 662, 660, 18, 525, - 583, 573, 580, 575, 553, 195, 199, 201, 206, 237, - 214, 234, 564, 635, 634, 205, 236, 557, 26, 491, + 7, 667, 213, 200, 211, 526, 513, 662, 675, 512, + 661, 665, 663, 671, 22, 668, 666, 664, 18, 525, + 587, 577, 584, 579, 553, 195, 199, 201, 206, 237, + 214, 234, 568, 639, 638, 205, 236, 557, 26, 491, 490, 362, 361, 9, 360, 363, 204, 484, 364, 109, 17, 149, 24, 13, 148, 19, 25, 59, 23, 8, 28, 27, 283, 15, 277, 10, 273, 12, 275, 11, @@ -224,243 +225,259 @@ const short QQmlJSGrammar::goto_default [] = { }; const short QQmlJSGrammar::action_index [] = { - 301, 1388, 2901, 2901, 2797, 1095, 106, 97, 99, -108, - 92, 88, 85, 364, -108, 338, 96, -108, -108, 805, - 100, 161, 281, 247, -108, -108, -108, 394, 274, 1388, - -108, -108, -108, 516, -108, -108, 2589, 1786, 1388, 1388, - 1388, -108, 997, 1388, -108, -108, -108, 1388, 1388, -108, - -108, -108, -108, -108, -108, 1388, -108, 1388, 1388, -108, - 1388, 1388, 116, 235, -108, -108, 1388, 1388, 1388, -108, - -108, -108, 220, 1388, 326, 1388, 1388, 1388, 1388, 384, - 1388, 1388, 1388, 1388, 1388, 1388, 193, 1388, 1388, 1388, - 115, 103, 102, 211, 227, 231, 257, 230, 424, 414, - 404, 1388, 85, 1388, 78, 2381, 1388, 1388, -108, -108, + 350, 1528, 3041, 3041, 2937, 1235, 115, 105, 239, -108, + 102, 93, 95, 205, -108, 422, 110, -108, -108, 727, + 92, 118, 265, 256, -108, -108, -108, 507, 247, 1528, + -108, -108, -108, 665, -108, -108, 2729, 1826, 1528, 1528, + 1528, -108, 1041, 1528, -108, -108, -108, 1528, 1528, -108, + -108, -108, -108, -108, -108, 1528, -108, 1528, 1528, -108, + 1528, 1528, 174, 221, -108, -108, 1528, 1528, 1528, -108, + -108, -108, 177, 1528, 422, 1528, 1528, 1528, 1528, 411, + 1528, 1528, 1528, 1528, 1528, 1528, 211, 1528, 1528, 1528, + 142, 148, 154, 217, 223, 226, 227, 231, 507, 385, + 395, 1528, 57, 1528, 83, 2521, 1528, 1528, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, - -108, -108, -108, -108, 157, 1388, -108, -108, 68, 61, - -108, 1388, -108, -108, 1388, -108, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -108, -108, -108, 1388, 59, - 1388, 1388, 71, 60, 1388, -108, 2381, 1388, 1388, -108, - 271, -108, 54, -108, -108, 53, 428, 434, 58, 51, - -108, 437, -108, 50, 2901, -108, -108, -108, -108, -108, - 232, -108, 528, -108, 48, -108, -108, -108, 55, -108, - -108, -108, 2901, -108, -108, 634, -108, 637, 101, 2797, - 56, 90, 89, 3109, -108, 1388, -108, 86, 1388, 77, - -108, 84, 82, -108, -108, 425, -108, -108, -108, -108, - 81, 346, 75, 66, 2901, 74, -108, -108, 2797, -108, - -108, 121, -108, -108, -108, -108, 117, -108, -108, -108, - -108, -108, -108, 63, 69, 1388, 108, 160, -108, -108, - -108, 1586, -108, 80, 67, 72, -108, 324, 76, 73, - 711, 83, 122, 449, 308, 411, 1388, 331, 1388, 1388, - 1388, 1388, 449, 1388, 1388, 1388, 1388, 1388, 303, 299, - 298, 284, 280, 449, 357, 449, 1388, -20, 1388, 57, - 1388, -108, 805, 1388, -108, 1388, 70, 62, 1388, 64, - 2797, -108, 1388, 204, 2797, -108, 1388, 65, 1388, 1388, - 104, 105, 1388, -108, 91, 136, 171, -108, -108, 1388, - -108, 528, 1388, -108, -6, 1388, 87, 2797, -108, 1388, - 129, 2797, -108, 1388, 125, 2797, 93, 2797, -108, 79, - -108, 22, -36, 20, -108, -108, 2797, -34, 581, 15, - 594, 113, 1388, 2797, 16, -12, 508, 2485, -11, 11, - 997, 21, 24, 1489, 2485, 25, -2, 3, 1388, 94, - -32, 1388, -4, 1388, -30, -29, 2693, -108, -108, -108, - -108, -108, -108, 1388, -108, -108, -108, -19, -9, 37, - 2901, -1, -108, 285, -108, 1388, 5, -108, 134, -108, - -108, 39, 421, 40, 44, 2901, 41, -108, 1388, 141, - 47, -108, 52, -108, 36, 206, 1388, -108, 30, 29, - -108, -16, -108, 2797, -108, 123, 2797, -108, 270, -108, - -108, 132, 2797, 18, -108, 27, 28, -108, 528, -10, - 31, -108, -108, -108, -108, 1388, 133, 2797, -108, 1388, - 124, 2797, -108, 9, -108, 209, -108, -108, 1388, -108, - -108, 445, -108, -108, 0, -17, 2901, -51, -108, -108, - 111, 1986, -108, -108, 1686, -108, -108, 1886, -108, -108, - -108, -108, -108, -108, 120, -108, -108, -108, -108, -108, - -108, -108, -108, -108, 2901, -108, -108, -108, 112, -22, - 23, 901, 216, -26, 13, -108, -108, 218, -108, 210, - 45, -108, -108, 621, 201, -108, 162, 43, 401, -108, - 144, -108, -108, 219, -108, 2277, -108, -108, -108, -108, - -108, -108, -108, -108, -108, -31, 17, 186, -108, 19, - -108, -108, 208, 34, 621, 200, 166, 528, 228, -108, - -5, -108, 901, 190, -108, -13, 794, -108, -108, 1291, - -108, -108, -108, 1193, -108, -108, 212, -108, 2277, -108, - 352, -13, -108, -108, 185, 521, 26, 2083, 319, 3005, - 4, -108, 1, 571, 2, 700, 146, 1388, 2797, -7, - -25, 511, -24, 6, 997, 7, 8, 1489, 46, 38, - 49, 1388, 94, 12, 1388, 42, 1388, 32, 33, -108, - 277, -108, 273, -108, -3, 35, 524, 233, 621, -108, - 98, -108, -108, -108, 2180, 901, 1786, 14, -108, 153, - -108, -108, 10, -108, -108, 901, 901, 110, 901, -108, - 300, -108, 95, -108, -108, 176, 158, -108, -108, -108, - -108, -108, 528, -108, 172, -108, 109, -108, -108, 528, - -108, -108, 126, -108, -108, -108, -108, -108, + -108, -108, -108, -108, 179, 1528, -108, -108, 77, 36, + -108, 1528, -108, -108, 1528, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, 1528, 56, + 1528, 1528, 80, 74, 1528, -108, 2521, 1528, 1528, -108, + 125, -108, 55, -108, -108, 53, 410, 418, 72, 52, + -108, 392, -108, 46, 3041, -108, -108, -108, -108, -108, + 273, -108, 396, -108, 44, -108, -108, -108, 76, -108, + -108, -108, 3041, -108, -108, 744, -108, 589, 98, 2937, + 91, 90, 88, 3249, -108, 1528, -108, 86, 1528, 81, + -108, 75, 73, -108, -108, 586, -108, -108, -108, -108, + 71, 491, 69, 65, 3041, 64, -108, -108, 2937, -108, + -108, 139, -108, -108, -108, -108, 134, -108, -108, -108, + -108, -108, -108, 63, 66, 1528, 147, 264, -108, -108, + -108, 1726, -108, 87, 68, 70, -108, 334, 82, 78, + 796, 89, 121, 349, 318, 469, 1528, 330, 1528, 1528, + 1528, 1528, 359, 1528, 1528, 1528, 1528, 1528, 284, 289, + 303, 306, 313, 365, 369, 375, 1528, 58, 1528, 85, + 1528, -108, 849, 1528, -108, 1528, 79, 59, 1528, 61, + 2937, -108, 1528, 146, 2937, -108, 1528, 62, 1528, 1528, + 106, 99, 1528, -108, 96, 176, 171, -108, -108, 1528, + -108, 407, 1528, -108, 97, 1528, -52, 2937, -108, 1528, + 120, 2937, -108, 1528, 123, 2937, 101, 2937, -108, 116, + -108, 84, 45, 0, -108, -108, 2937, -39, 641, -3, + 652, 156, 1528, 2937, -7, -11, 567, 2625, -21, 5, + 945, 2, 94, 1629, 2625, -1, -26, 6, 1528, 10, + -15, 1528, 14, 1528, -25, -12, 2833, -108, -108, -108, + -108, -108, -108, 1528, -108, -108, -108, -14, -58, -13, + 3041, -36, -108, 287, -108, 1528, -46, -108, 153, -108, + -108, -31, 586, -57, -32, 3041, 11, -108, 1528, 168, + 29, -108, 47, -108, 48, 169, 1528, -108, 49, 50, + -108, 9, -108, 2937, -108, 136, 2937, -108, 275, -108, + -108, 126, 2937, 35, -108, 33, 37, -108, 466, -4, + 38, -108, -108, -108, -108, 1528, 130, 2937, -108, 1528, + 117, 2937, -108, 34, -108, 296, -108, -108, 1528, -108, + -108, 404, -108, -108, 12, 40, 3041, 13, -108, -108, + 155, 1926, -108, -108, 2026, -108, -108, 2126, -108, -108, + -108, -108, -108, -108, 144, -108, -108, -108, -108, -108, + -108, -108, -108, -108, 3041, -108, -108, -108, 132, -27, + 15, 1137, 218, -23, 17, -108, -108, 196, -108, 242, + 8, -108, -108, 579, 237, -108, 127, 18, 415, -108, + 103, -108, -108, 236, -108, 2223, -108, -108, -108, -108, + -108, -108, -108, -108, -108, 27, 21, 137, 31, 20, + -108, 23, -5, -108, -9, -108, 332, -10, 571, 201, + 195, 586, 225, -108, 7, -108, 1137, 165, -108, 4, + 1137, -108, -108, 1333, -108, -108, -108, 1431, -108, -108, + 184, -108, 2223, -108, 331, 3, -108, -108, 202, 531, + 26, 2417, 327, 3145, 1, -108, 25, 629, 24, 649, + 124, 1528, 2937, 22, -6, 514, -8, 19, 1041, 16, + 94, 1629, 28, 42, 67, 1528, 60, 43, 1528, 54, + 1528, 41, 39, -108, 234, -108, 228, -108, 51, -2, + 564, 233, 575, -108, 100, -108, -108, -108, 2320, 1137, + 1826, 32, -108, 122, -108, -108, 30, -108, -108, 1137, + 1137, 104, 903, -108, 308, -108, 108, -108, -108, 133, + 119, -108, -108, -108, -108, -108, 451, -108, 164, -108, + 161, -108, -108, 458, -108, -108, 151, -108, -108, -108, + -108, -108, - -112, -3, 65, 88, 78, 292, -4, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -70, - -112, -112, -112, -112, -112, -112, -112, -112, -112, 70, - -112, -112, -112, 10, -112, -112, 2, -24, 15, 96, - 99, -112, 130, 103, -112, -112, -112, 48, 62, -112, - -112, -112, -112, -112, -112, 57, -112, 82, 203, -112, - 187, 181, -112, -112, -112, -112, 169, 177, 178, -112, - -112, -112, -112, 189, -112, 196, 198, 206, 161, -112, - 107, 113, 135, 116, 117, 119, -112, 125, 128, 133, - -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, 139, -112, 145, -112, 180, 8, -42, -112, -112, + -112, 18, 86, 97, 69, 316, 7, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, -112, -64, + -112, -112, -112, -112, -112, -112, -112, -112, -112, 66, + -112, -112, -112, -17, -112, -112, -10, -36, 3, 90, + 95, -112, 149, 74, -112, -112, -112, 67, 13, -112, + -112, -112, -112, -112, -112, 178, -112, 181, 185, -112, + 189, 190, -112, -112, -112, -112, 198, 208, 212, -112, + -112, -112, -112, 209, -112, 201, 164, 111, 113, -112, + 116, 130, 131, 132, 134, 142, -112, 118, 124, 144, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, 154, -112, 155, -112, 268, 28, -8, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, 5, -112, -112, -112, -112, - -112, 31, -112, -112, 32, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -112, -112, 86, -112, - 77, 17, -112, -112, 28, -112, 273, 37, 79, -112, - -112, -112, -112, -112, -112, -112, 61, 95, -112, -112, - -112, 46, -112, -112, 58, -112, -112, -112, -112, -112, - -112, -112, 45, -112, -112, -112, -112, -112, -112, -112, - -112, -112, 131, -112, -112, 29, -112, 33, -112, 84, - -112, 39, -112, 223, -112, 42, -112, -112, 35, 16, - -112, -112, -112, -112, -112, 49, -112, -112, -112, -112, - -112, 184, -112, -112, 194, -112, -112, -112, 199, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, 42, -112, -112, -112, -112, + -112, 47, -112, -112, 50, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -112, -112, 159, -112, + 158, 56, -112, -112, 57, -112, 362, 60, 157, -112, + -112, -112, -112, -112, -112, -112, 20, 151, -112, -112, + -112, 25, -112, -112, 30, -112, -112, -112, -112, -112, + -112, -112, 31, -112, -112, -112, -112, -112, -112, -112, + -112, -112, 233, -112, -112, 34, -112, 35, -112, 225, + -112, 36, -112, 216, -112, 55, -112, -112, 53, 39, -112, -112, -112, -112, -112, 19, -112, -112, -112, -112, - -112, 76, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -112, -11, 214, -112, 215, 221, - 224, 225, -112, 92, 90, 83, 74, 73, -112, -112, - -112, -112, -112, -112, -112, -112, 237, -112, 234, -112, - 243, -112, -112, 254, -112, 67, -112, -112, 85, -112, - 167, -112, 26, -112, 219, -112, 247, -112, 244, 241, - -112, -112, 233, -112, -112, -112, -112, -112, -112, 213, - -112, 108, 183, -112, -112, 193, -112, 202, -112, 106, - -112, 209, -112, 55, -112, 317, -112, 205, -112, -112, - -112, -112, -112, -112, -112, -112, 191, -112, 59, -112, - 60, -112, 134, 179, -112, -112, 44, 160, -112, -112, - 156, -112, -112, 41, 211, -112, -112, -112, 50, -112, - 36, 192, -112, 63, -112, -112, 72, -112, -112, -112, - -112, -112, -112, 23, -112, -112, -112, -112, -112, -112, - 75, -112, -112, -112, -112, 114, -112, -112, -112, -112, - -112, -112, 64, -112, -112, 69, -112, -112, 52, -112, - -112, -112, -112, -112, -50, -112, 56, -112, -45, -112, - -112, -112, -112, 293, -112, -112, 264, -112, -112, -112, - -112, -112, 89, -64, -112, -112, 24, -112, 25, -112, - 27, -112, -112, -112, -112, 34, -112, 122, -112, 47, - -112, 66, -112, -112, -112, -112, -112, -112, 38, -112, - -112, 220, -112, -112, -112, -112, 197, -112, -112, -112, - -112, 30, -112, -112, 120, -112, -112, 3, -112, -112, + -112, 94, -112, -112, 92, -112, -112, -112, 117, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, 195, -112, -112, -112, -112, -112, - -112, 51, -112, -112, -112, -112, -112, -112, -112, 40, - -112, -112, -112, -15, -28, -112, -112, -112, -12, -112, - -112, -112, -112, -112, -112, 314, -112, -112, -112, -112, + -112, -112, -112, -112, -112, 33, -112, -112, -112, -112, + -112, 88, -112, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, 51, 220, -112, 227, 235, + 236, 244, -112, 21, 17, 102, 91, 89, -112, -112, + -112, -112, -112, -112, -112, -112, 211, -112, 247, -112, + 257, -112, -112, 264, -112, 75, -112, -112, 103, -112, + 112, -112, 29, -112, 73, -112, 263, -112, 255, 254, + -112, -112, 245, -112, -112, -112, -112, -112, -112, 248, + -112, 65, 105, -112, -112, 99, -112, 162, -112, 37, + -112, 115, -112, 44, -112, 135, -112, 137, -112, -112, + -112, -112, -112, -112, -112, -112, 138, -112, 24, -112, + 26, -112, 104, 140, -112, -112, 32, 64, -112, -112, + 174, -112, -112, 48, 87, -112, -112, -112, 54, -112, + 40, 71, -112, 150, -112, -112, 197, -112, -112, -112, + -112, -112, -112, 12, -112, -112, -112, -112, -112, -112, + 206, -112, -112, -112, -112, 207, -112, -112, -112, -112, + -112, -112, 231, -112, -112, 239, -112, -112, 43, -112, + -112, -112, -112, -112, -59, -112, 38, -112, -62, -112, + -112, -112, -112, 258, -112, -112, 259, -112, -112, -112, + -112, -112, 163, -72, -112, -112, 41, -112, 62, -112, + 61, -112, -112, -112, -112, 59, -112, 193, -112, 58, + -112, 204, -112, -112, -112, -112, -112, -112, 52, -112, + -112, 175, -112, -112, -112, -112, 186, -112, -112, -112, + -112, 49, -112, -112, 173, -112, -112, 45, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -112, -112, 7, 0, -112, 21, -112, -112, - -112, -112, 147, -112, -112, -112, 236, -112, -112, 324, - -112, -112, -112, 424, -112, -112, -112, -112, 360, -112, - -112, 13, -112, -112, -1, 12, -112, 338, -112, 212, - 4, -112, -112, 9, -112, -6, -112, 208, 246, -112, - -112, 6, -112, -112, 71, -112, -112, 18, -112, -112, - -112, 14, -112, -10, 53, -112, 43, -112, -112, -112, - -112, -112, -30, -112, -112, -112, -5, -18, -2, -112, - -112, -112, -112, -112, 378, 81, 311, 1, -112, -112, - -112, -112, 11, -112, -112, 20, 22, 207, 80, -112, + -112, -112, -112, -112, 213, -112, -112, -112, -112, -112, + -112, 46, -112, -112, -112, -112, -112, -112, -112, 27, + -112, -112, -112, -18, -9, -112, -112, -112, 15, -112, + -112, -112, -112, -112, -112, 331, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, -112, - -112, -112, -8, -112, -112, -112, -112, -112, -112, -9, - -112, -112, -112, -112, -112, -112, -112, -112 + -112, -112, -112, -112, -112, -112, -112, -112, 11, -6, + -112, 10, -112, -112, -112, -112, 156, -112, -112, -112, + 240, -112, -112, 330, -112, -112, -112, 332, -112, -112, + -112, -112, 376, -112, -112, 8, -112, -112, -7, 76, + -112, 358, -112, 228, 5, -112, -112, 6, -112, 4, + -112, 79, 221, -112, -112, 2, -112, -112, 174, -112, + -112, 16, -112, -112, -112, 14, -112, -16, 70, -112, + 63, -112, -112, -112, -112, -112, -30, -112, -112, -112, + -15, -28, -13, -112, -112, -112, -112, -112, 460, 93, + 307, -12, -112, -112, -112, -112, -11, -112, -112, -2, + -1, 85, 84, -112, -112, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -3, -112, -112, -112, + -112, -112, -112, 0, -112, -112, -112, -112, -112, -112, + -112, -112 }; const short QQmlJSGrammar::action_info [] = { - -130, 452, 556, -146, 488, 637, 465, 469, 248, -149, - -141, 271, 353, -150, -138, -119, 486, 408, -150, 402, - 579, 406, -149, -130, 271, 353, 478, 403, -138, 572, - 396, -119, -118, 597, 428, 436, 443, 579, 456, 442, - 594, 436, 630, 579, 529, 452, 558, 579, 561, -146, - 460, 409, 555, -118, 412, 345, -141, 436, 286, 308, - 485, 452, 248, 458, 452, 417, 191, 174, 465, 469, - 410, 565, 539, 168, 428, 422, 151, 425, 145, 73, - 432, 286, 534, 194, 310, 326, 248, 0, 0, 187, - 0, 0, 271, 73, 0, 640, 427, 687, 0, 244, - 424, -143, 168, 247, 145, 265, 326, 101, 339, 357, - 452, 193, 332, 306, 183, 306, 145, 241, 469, 494, - 465, 153, 428, 318, 320, 353, 186, 176, 145, 246, - 446, 145, 145, 145, 315, 243, 101, 145, 455, 60, - 264, 145, 60, 60, 341, 0, 177, 347, 0, 145, - 61, 308, 456, 61, 61, 60, 686, 685, 64, 642, - 641, 576, 262, 261, 103, 145, 61, 495, 267, 65, - 678, 677, 328, 176, 262, 261, 329, 537, 260, 259, - 505, 537, 255, 254, 471, 355, 538, 684, 683, 351, - 567, 176, 177, 467, 559, 420, 419, 342, 576, 655, - 656, 430, 349, 655, 656, 542, 541, 262, 261, 650, - 177, 170, 145, 146, 535, 171, 439, 481, 87, 588, - 88, 270, 268, 176, 0, 644, 545, 0, 0, 535, - 535, 89, 66, 681, 680, 570, 87, 0, 88, 530, - 145, 560, 177, 0, 415, 563, 577, 66, 0, 89, - 269, 579, 87, 0, 88, 87, 87, 88, 88, 66, - 532, 440, 535, 0, 324, 89, 0, 679, 89, 89, - 482, 480, 531, 589, 587, 532, 532, 67, 145, 145, - 546, 544, 87, 68, 88, 532, 0, 531, 531, 571, - 569, 532, 67, 239, 238, 89, 176, 531, 68, 87, - 176, 88, 535, 531, 67, 87, 0, 88, 532, 87, - 68, 88, 89, 632, 645, 177, 0, 178, 89, 177, - 531, 415, 89, 87, 87, 88, 88, 181, 87, 0, - 88, 450, 449, 87, 176, 88, 89, 89, 633, 631, - 0, 89, 288, 289, 75, 76, 89, 674, 532, 288, - 289, 0, -105, 177, 0, 178, 75, 76, 0, 0, - 531, 675, 673, 0, 0, 0, 0, 176, 0, 290, - 291, 77, 78, 0, 0, 35, 290, 291, 0, 105, - 293, 294, 0, 77, 78, -105, 177, 0, 178, 295, - 0, 0, 296, 0, 297, 672, 0, 0, 106, 0, - 107, 6, 5, 4, 1, 3, 2, 80, 81, 0, - 0, 0, 49, 52, 50, 82, 83, 80, 81, 84, - 0, 85, 0, 0, 0, 82, 83, 80, 81, 84, - 35, 85, 0, 0, 0, 82, 83, 80, 81, 84, - 35, 85, 46, 34, 51, 82, 83, 80, 81, 84, - 35, 85, 0, 0, 35, 82, 83, 35, 0, 84, - 0, 85, 0, 35, 0, 0, 35, 49, 52, 50, - 0, 0, 293, 294, 35, 0, 0, 49, 52, 50, - 0, 295, 0, 0, 296, 0, 297, 49, 52, 50, - 0, 49, 52, 50, 49, 52, 50, 46, 34, 51, - 49, 52, 50, 49, 52, 50, 0, 46, 34, 51, - 0, 49, 52, 50, 0, 0, 0, 46, 34, 51, - 0, 46, 34, 51, 46, 34, 51, 0, 0, 0, - 46, 34, 51, 46, 34, 51, 537, 35, 0, 537, - 35, 46, 34, 51, 186, 35, 0, 186, 0, 0, - 35, 0, 186, 35, 0, 0, 0, 35, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 49, 52, 50, 49, 52, 50, - 0, 0, 49, 52, 50, 0, 0, 49, 52, 50, - 49, 52, 50, 0, 49, 52, 50, 0, 0, 0, - 35, 0, 0, 0, 46, 34, 51, 46, 34, 51, - 35, 0, 46, 34, 51, 0, 0, 46, 34, 51, - 46, 34, 51, 35, 46, 34, 51, 0, 0, 0, - 0, 0, 253, 252, 0, 0, 537, 49, 52, 50, - 0, 0, 253, 252, 0, 0, 0, 49, 52, 50, - 35, 0, 0, 0, 0, 258, 257, 0, 0, 0, - 49, 52, 50, 35, 0, 0, 35, 46, 34, 51, + -132, 425, 409, 424, -151, 422, -120, 403, 347, -140, + 428, 465, -152, -143, 417, 353, 406, -145, 452, 412, + 410, -148, 408, -140, 469, 271, -152, 569, 353, -132, + 271, -151, 248, 601, 583, -120, 583, 583, 565, 529, + 562, 576, 563, 598, 555, 534, 634, 539, 564, 561, + 558, 478, 436, 436, 436, 456, 460, 443, 644, 641, + 556, -148, 432, 583, 442, 583, 427, -145, 488, 458, + 452, 452, 485, 486, -143, 469, 452, 465, 428, 194, + 191, 174, 168, 248, 73, 151, 286, 145, 286, 187, + 310, 326, 396, 0, 168, 0, 153, 0, 244, 247, + 402, -121, 265, 73, 101, 691, 332, 241, 326, 469, + 306, 465, 193, 339, 452, 183, 306, 357, 145, 246, + 318, 320, 428, 248, 353, 145, 186, 271, 145, 243, + 580, 145, 455, 145, 176, 0, 103, 308, 145, 315, + 264, 101, 537, 446, 145, 559, 456, 176, 176, 308, + 0, 538, 145, 177, 145, 145, 0, 0, 345, 262, + 261, 646, 645, 494, 542, 541, 177, 177, 170, 690, + 689, 328, 171, 580, 103, 329, 145, 471, 654, 439, + 351, 181, 60, 355, 341, 262, 261, 145, 60, 66, + 467, 592, 560, 61, 60, 260, 259, 659, 660, 61, + 255, 254, 349, 648, 505, 61, 324, 267, 659, 660, + 537, 495, 688, 687, 420, 419, 64, 262, 261, 571, + 105, 581, 682, 681, 440, 685, 684, 65, 430, 583, + 535, 535, 574, 66, 67, 146, 87, 342, 88, 106, + 68, 107, 87, 545, 88, 593, 591, 567, 87, 89, + 88, 87, 87, 88, 88, 89, 87, 535, 88, 683, + 0, 89, 535, 0, 89, 89, 535, 0, 66, 89, + 636, 530, 87, 0, 88, 0, 532, 532, 67, 60, + 176, 145, 0, 145, 68, 89, 575, 573, 531, 531, + 61, 0, 649, 532, 0, 637, 635, 546, 544, 177, + 0, 178, 176, 532, 481, 531, 0, 0, 532, 87, + 0, 88, 532, 67, 87, 531, 88, 532, 0, 68, + 531, 177, 89, 415, 531, 270, 268, 89, 87, 531, + 88, 87, 0, 88, 239, 238, 450, 449, 87, 0, + 88, 89, 176, 87, 89, 88, 176, 176, 288, 289, + 0, 89, 288, 289, 269, 678, 89, 482, 480, 0, + -107, 177, 0, 178, -107, 177, 177, 178, 415, 679, + 677, 0, 293, 294, 0, 290, 291, 0, 0, 290, + 291, 295, 293, 294, 296, 0, 297, 0, 293, 294, + 0, 295, 293, 294, 296, 0, 297, 295, 293, 294, + 296, 295, 297, 676, 296, 0, 297, 295, 80, 81, + 296, 0, 297, 0, 0, 0, 82, 83, 80, 81, + 84, 35, 85, 0, 0, 35, 82, 83, 0, 0, + 84, 0, 85, 35, 80, 81, 35, 0, 0, 35, + 75, 76, 82, 83, 35, 0, 84, 35, 85, 0, + 6, 5, 4, 1, 3, 2, 0, 0, 49, 52, + 50, 0, 49, 52, 50, 0, 0, 77, 78, 0, + 49, 52, 50, 49, 52, 50, 49, 52, 50, 0, + 35, 49, 52, 50, 49, 52, 50, 35, 46, 34, + 51, 0, 46, 34, 51, 35, 0, 0, 35, 0, + 46, 34, 51, 46, 34, 51, 46, 34, 51, 0, + 0, 46, 34, 51, 46, 34, 51, 49, 52, 50, + 35, 0, 0, 0, 49, 52, 50, 0, 0, 0, + 80, 81, 49, 52, 50, 49, 52, 50, 82, 83, + 0, 0, 84, 35, 85, 0, 537, 46, 34, 51, + 186, 0, 0, 0, 46, 34, 51, 49, 52, 50, + 35, 0, 46, 34, 51, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 537, + 49, 52, 50, 0, 0, 0, 537, 46, 34, 51, + 537, 0, 0, 35, 537, 0, 35, 49, 52, 50, + 35, 0, 0, 186, 35, 0, 0, 0, 35, 0, + 46, 34, 51, 0, 0, 35, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 49, 52, 50, - 46, 34, 51, 0, 0, 253, 252, 0, 258, 257, - 49, 52, 50, 49, 52, 50, 0, 0, 0, 0, - 0, 0, 0, 0, 155, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 156, 0, 0, 0, 157, 35, - 46, 34, 51, 46, 34, 51, 0, 158, 0, 159, - 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, - 160, 0, 161, 64, 0, 0, 0, 0, 0, 0, - 162, 258, 257, 163, 65, 0, 49, 52, 50, 164, - 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 166, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 30, 31, 155, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 156, 0, - 0, 0, 157, 35, 0, 0, 0, 36, 37, 0, - 38, 158, 0, 159, 0, 0, 0, 521, 0, 0, - 0, 45, 0, 0, 160, 0, 161, 64, 0, 0, - 0, 0, 0, 0, 162, 0, 0, 163, 65, 53, - 49, 52, 50, 164, 54, 0, 0, 0, 0, 165, - 0, 0, 0, 0, 0, 44, 56, 32, 0, 0, - 0, 0, 41, 0, 0, 166, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 49, 52, 50, 49, 52, 50, 0, 49, 52, 50, + 0, 49, 52, 50, 0, 49, 52, 50, 0, 0, + 258, 257, 49, 52, 50, 49, 52, 50, 35, 0, + 46, 34, 51, 46, 34, 51, 0, 46, 34, 51, + 35, 46, 34, 51, 0, 46, 34, 51, 35, 0, + 0, 35, 46, 34, 51, 46, 34, 51, 0, 0, + 253, 252, 0, 0, 35, 49, 52, 50, 0, 0, + 0, 186, 253, 252, 0, 0, 0, 49, 52, 50, + 258, 257, 0, 258, 257, 49, 52, 50, 49, 52, + 50, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 155, 49, 52, 50, 0, 0, 0, 46, 34, 51, + 156, 0, 0, 0, 157, 46, 34, 51, 46, 34, + 51, 0, 0, 158, 0, 159, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 160, 0, 161, 64, + 0, 0, 0, 35, 0, 0, 162, 0, 0, 163, + 65, 0, 0, 0, 0, 164, 0, 0, 0, 0, + 0, 165, 0, 0, 0, 0, 0, 0, 0, 155, + 0, 0, 0, 0, 0, 253, 252, 166, 0, 156, + 49, 52, 50, 157, 0, 0, 0, 0, 0, 0, + 0, 0, 158, 0, 159, 0, 0, 322, 0, 0, + 0, 0, 0, 0, 0, 160, 0, 161, 64, 0, + 46, 34, 51, 0, 0, 162, 0, 0, 163, 65, + 0, 0, 155, 0, 164, 0, 0, 0, 0, 0, + 165, 0, 156, 0, 0, 0, 157, 0, 0, 0, + 0, 0, 0, 0, 0, 158, 166, 159, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, + 161, 64, 0, 0, 0, 0, 0, 0, 162, 0, + 0, 163, 65, 0, 0, 0, 0, 164, 0, 0, + 0, 0, 0, 165, 0, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 166, + 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, + 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 53, 49, + 52, 50, 0, 54, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 44, 56, 32, 0, 42, 0, + 0, 41, 45, 0, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 56, 32, 0, + 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, - 0, 0, 0, 0, 521, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, @@ -470,7 +487,7 @@ const short QQmlJSGrammar::action_info [] = { 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, - 42, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 521, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 56, @@ -490,7 +507,7 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 584, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 585, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 522, 524, 523, 0, 54, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 44, 56, 32, 216, 224, 0, @@ -500,7 +517,7 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 221, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, 45, 0, - 0, 0, 0, 0, 0, 0, 581, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 588, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 522, 524, 523, 0, 54, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 44, 56, 32, 216, 224, 0, 0, 41, @@ -515,7 +532,7 @@ const short QQmlJSGrammar::action_info [] = { 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -139, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -141, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, @@ -540,27 +557,27 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 498, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 500, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 492, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 498, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, @@ -570,14 +587,14 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, - 0, 0, 35, 222, 0, 0, 599, 37, 0, 38, + 0, 0, 35, 222, 0, 0, 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 226, 0, 0, 0, 53, 49, @@ -587,7 +604,7 @@ const short QQmlJSGrammar::action_info [] = { 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, - 222, 0, 0, 599, 646, 0, 38, 0, 0, 0, + 222, 0, 0, 603, 650, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 226, 0, 0, 0, 53, 49, 52, 50, 227, @@ -597,7 +614,7 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 222, 0, 0, - 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 603, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 226, 0, 0, 0, 53, 49, 52, 50, 227, 54, 0, 55, @@ -666,177 +683,195 @@ const short QQmlJSGrammar::action_info [] = { 227, 54, 228, 55, 229, 57, 230, 58, 231, 232, 0, 0, 44, 56, 32, 216, 224, 218, 0, 41, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 603, - 112, 113, 0, 0, 605, 117, 607, 30, 31, 608, - 0, 120, 0, 0, 0, 123, 610, 611, 0, 0, - 0, 0, 0, 0, 35, 612, 127, 128, 223, 37, - 0, 38, 0, 0, 0, 39, 0, 40, 614, 43, - 0, 0, 616, 0, 0, 0, 47, 0, 48, 0, - 0, 0, 0, 0, 617, 0, 226, 0, 0, 0, - 618, 49, 52, 50, 619, 620, 621, 55, 623, 624, - 625, 626, 627, 628, 0, 0, 615, 622, 609, 604, - 613, 606, 132, 41, 0, 0, 121, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 607, + 112, 113, 0, 0, 609, 117, 611, 30, 31, 612, + 0, 120, 0, 0, 0, 123, 614, 615, 0, 0, + 0, 0, 0, 0, 35, 616, 127, 128, 223, 37, + 0, 38, 0, 0, 0, 39, 0, 40, 618, 43, + 0, 0, 620, 0, 0, 0, 47, 0, 48, 0, + 0, 0, 0, 0, 621, 0, 226, 0, 0, 0, + 622, 49, 52, 50, 623, 624, 625, 55, 627, 628, + 629, 630, 631, 632, 0, 0, 619, 626, 613, 608, + 617, 610, 132, 41, 0, 0, 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 112, 113, 0, 0, 370, 117, 372, 30, 31, 373, 0, 120, 0, 0, 0, 123, 375, 376, 0, 0, 0, 0, 0, 0, 35, 378, 127, 128, 223, 37, 0, 38, 0, 0, 0, 39, 0, 40, 380, 43, 0, 0, 382, 0, 0, 0, - 47, 0, 48, 0, -286, 0, 0, 0, 383, 0, + 47, 0, 48, 0, -288, 0, 0, 0, 383, 0, 226, 0, 0, 0, 385, 49, 52, 50, 386, 387, 388, 55, 390, 391, 392, 393, 394, 395, 0, 0, 381, 389, 374, 369, 379, 371, 132, 41, 0, 0, 121, 0, 0, 0, 0, 46, 377, 384, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 150, 636, 543, 536, 654, 540, 334, 154, 682, - 676, 144, 256, 643, 638, 451, 639, 448, 504, 489, - 397, 316, 266, 651, 185, 586, 629, 251, 185, 323, - 596, 595, 566, 653, 665, 593, 666, 466, 448, 568, - 180, 451, 457, 459, 316, 316, 499, 251, 147, 462, - 470, 256, 461, 448, 437, 429, 441, 185, 354, 445, - 173, 451, 185, 240, 192, 562, 404, 473, 472, 0, - 316, 175, 533, 502, 152, 167, 208, 251, 256, 190, - 516, 479, 190, 208, 208, 413, 263, 208, 316, 0, - 397, 365, 515, 208, 518, 518, 208, 0, 62, 670, - 464, 0, 208, 62, 652, 509, 208, 208, 62, 350, - 463, 423, 62, 190, 511, 426, 398, 62, 62, 510, - 464, 411, 150, 414, 468, 62, 334, 184, 62, 62, - 182, 280, 62, 302, 301, 250, 284, 62, 62, 463, - 208, 62, 189, 300, 413, 62, 317, 62, 172, 208, - 299, 62, 298, 506, 62, 169, 507, 150, 62, 497, - 508, 518, 62, 496, 319, 416, 574, 86, 62, 321, - 413, 62, 62, 93, 62, 514, 95, 96, 397, 97, - 62, 263, 414, 62, 90, 208, 316, 91, 62, 62, - 62, 184, 92, 405, 62, 94, 316, 208, 108, 250, - 62, 249, 190, 343, 348, 407, 102, 358, 414, 208, - 104, 352, 208, 208, 365, 208, 62, 208, 669, 668, - 208, 325, 100, 208, 62, 365, 69, 208, 110, 397, - 602, 242, 62, 62, 70, 71, 62, 208, 473, 72, - 245, 359, 62, 487, 62, 63, 0, 62, 263, 463, - 518, 62, 74, 62, 0, 578, 421, 79, 62, 98, - 464, 62, 344, 62, 208, 184, 365, 99, 312, 62, - 62, 0, 346, 284, 284, 284, 62, 292, 287, 62, - 62, 284, 208, 303, 284, 284, 304, 305, 312, 62, - 340, 108, 62, 284, 284, 365, 312, 284, 312, 62, - 309, 284, 62, 284, 284, 307, 518, 284, 0, 312, - 333, 208, 0, 483, 284, 527, 330, 327, 331, 356, - 311, 110, 179, 0, 0, 590, 0, 517, 528, 582, - 574, 314, 649, 0, 0, 208, 0, 0, 518, 547, - 548, 549, 550, 554, 551, 552, 0, 527, 0, 0, - 0, 0, 598, 447, 489, 0, 0, 0, 0, 517, - 528, 600, 601, 547, 548, 549, 550, 554, 551, 552, - 0, 0, 0, 0, 590, 0, 0, 0, 0, 0, - 0, 0, 444, 591, 592, 547, 548, 549, 550, 554, - 551, 552, 598, 0, 0, 0, 0, 0, 0, 0, - 0, 647, 648, 547, 548, 549, 550, 554, 551, 552, + 543, 185, 640, 647, 642, 643, 504, 489, 397, 451, + 655, 657, 669, 670, 154, 680, 658, 448, 686, 316, + 185, 16, 256, 536, 251, 599, 570, 633, 572, 590, + 597, 144, 323, 540, 457, 150, 266, 473, 190, 441, + 350, 445, 251, 192, 256, 437, 429, 354, 208, 240, + 185, 316, 251, 256, 185, 404, 448, 448, 316, 533, + 566, 470, 466, 180, 451, 451, 462, 0, 62, 334, + 510, 516, 62, 0, 0, 325, 62, 299, 316, 0, + 459, 298, 397, 334, 0, 147, 461, 208, 499, 0, + 152, 208, 502, 167, 600, 479, 673, 672, 518, 173, + 175, 515, 316, 674, 208, 397, 316, 518, 316, 407, + 208, 0, 190, 0, 321, 208, 656, 352, 62, 249, + 464, 62, 62, 184, 509, 62, 62, 463, 463, 62, + 208, 508, 421, 208, 62, 208, 184, 356, 245, 358, + 405, 242, 263, 280, 62, 62, 62, 506, 284, 302, + 62, 301, 507, 208, 317, 208, 208, 62, 208, 62, + 343, 184, 300, 413, 348, 365, 62, 0, 62, 190, + 518, 62, 99, 62, 100, 578, 86, 90, 346, 62, + 208, 208, 319, 91, 344, 62, 62, 62, 413, 62, + 93, 94, 95, 473, 96, 468, 514, 62, 189, 62, + 150, 414, 97, 92, 208, 62, 472, 464, 182, 62, + 62, 208, 497, 62, 62, 397, 496, 250, 365, 62, + 104, 102, 208, 263, 208, 98, 414, 263, 169, 172, + 365, 208, 487, 62, 359, 511, 62, 250, 463, 208, + 62, 398, 464, 208, 62, 62, 606, 63, 72, 190, + 150, 208, 411, 62, 518, 69, 62, 208, 416, 582, + 365, 365, 79, 62, 62, 70, 62, 62, 483, 71, + 0, 284, 74, 0, 0, 62, 208, 208, 423, 307, + 284, 0, 62, 0, 287, 426, 108, 284, 0, 292, + 62, 62, 0, 0, 0, 284, 284, 303, 304, 62, + 312, 0, 62, 312, 284, 284, 305, 284, 284, 312, + 62, 0, 312, 309, 284, 284, 110, 284, 62, 312, + 0, 594, 333, 284, 284, 340, 578, 330, 653, 0, + 518, 331, 0, 327, 311, 586, 0, 589, 0, 527, + 0, 314, 0, 0, 518, 0, 518, 444, 447, 0, + 489, 517, 528, 527, 0, 527, 547, 548, 549, 550, + 554, 551, 552, 0, 0, 517, 528, 517, 528, 0, + 0, 0, 602, 0, 0, 0, 0, 0, 0, 0, + 108, 604, 605, 547, 548, 549, 550, 554, 551, 552, + 594, 0, 0, 0, 0, 0, 0, 0, 0, 595, + 596, 547, 548, 549, 550, 554, 551, 552, 0, 0, + 110, 179, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 585, - 0, 0, 0, 0, 0, 0, 0, 0, 518, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 517, - 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 602, 0, 0, 0, 0, 0, + 0, 0, 0, 651, 652, 547, 548, 549, 550, 554, + 551, 552, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 }; const short QQmlJSGrammar::action_check [] = { - 7, 33, 33, 7, 55, 8, 36, 36, 7, 7, - 7, 36, 36, 7, 7, 7, 33, 36, 7, 55, - 33, 55, 7, 7, 36, 36, 17, 7, 7, 34, - 8, 7, 7, 7, 36, 5, 7, 33, 20, 55, - 66, 5, 29, 33, 66, 33, 29, 33, 29, 7, - 60, 60, 29, 7, 55, 61, 7, 5, 1, 79, - 60, 33, 7, 36, 33, 60, 8, 7, 36, 36, - 33, 37, 29, 2, 36, 36, 8, 33, 8, 1, - 33, 1, 37, 33, 8, 2, 7, -1, -1, 36, - -1, -1, 36, 1, -1, 60, 55, 0, -1, 33, - 60, 7, 2, 55, 8, 36, 2, 48, 17, 16, - 33, 60, 7, 48, 60, 48, 8, 36, 36, 8, - 36, 60, 36, 61, 60, 36, 36, 15, 8, 55, - 7, 8, 8, 8, 61, 60, 48, 8, 6, 40, - 77, 8, 40, 40, 8, -1, 34, 60, -1, 8, - 51, 79, 20, 51, 51, 40, 61, 62, 42, 61, - 62, 8, 61, 62, 79, 8, 51, 56, 60, 53, - 61, 62, 50, 15, 61, 62, 54, 15, 61, 62, - 60, 15, 61, 62, 60, 60, 24, 61, 62, 60, - 24, 15, 34, 60, 8, 61, 62, 61, 8, 93, - 94, 60, 31, 93, 94, 61, 62, 61, 62, 56, - 34, 50, 8, 56, 29, 54, 10, 8, 25, 7, - 27, 61, 62, 15, -1, 7, 7, -1, -1, 29, - 29, 38, 12, 61, 62, 7, 25, -1, 27, 29, - 8, 55, 34, -1, 36, 29, 56, 12, -1, 38, - 90, 33, 25, -1, 27, 25, 25, 27, 27, 12, - 75, 55, 29, -1, 60, 38, -1, 95, 38, 38, - 61, 62, 87, 61, 62, 75, 75, 57, 8, 8, - 61, 62, 25, 63, 27, 75, -1, 87, 87, 61, - 62, 75, 57, 61, 62, 38, 15, 87, 63, 25, - 15, 27, 29, 87, 57, 25, -1, 27, 75, 25, - 63, 27, 38, 36, 96, 34, -1, 36, 38, 34, - 87, 36, 38, 25, 25, 27, 27, 56, 25, -1, - 27, 61, 62, 25, 15, 27, 38, 38, 61, 62, - -1, 38, 18, 19, 18, 19, 38, 47, 75, 18, - 19, -1, 33, 34, -1, 36, 18, 19, -1, -1, - 87, 61, 62, -1, -1, -1, -1, 15, -1, 45, - 46, 45, 46, -1, -1, 29, 45, 46, -1, 15, - 23, 24, -1, 45, 46, 33, 34, -1, 36, 32, - -1, -1, 35, -1, 37, 95, -1, -1, 34, -1, - 36, 100, 101, 102, 103, 104, 105, 23, 24, -1, - -1, -1, 66, 67, 68, 31, 32, 23, 24, 35, - -1, 37, -1, -1, -1, 31, 32, 23, 24, 35, - 29, 37, -1, -1, -1, 31, 32, 23, 24, 35, - 29, 37, 96, 97, 98, 31, 32, 23, 24, 35, - 29, 37, -1, -1, 29, 31, 32, 29, -1, 35, - -1, 37, -1, 29, -1, -1, 29, 66, 67, 68, - -1, -1, 23, 24, 29, -1, -1, 66, 67, 68, - -1, 32, -1, -1, 35, -1, 37, 66, 67, 68, - -1, 66, 67, 68, 66, 67, 68, 96, 97, 98, - 66, 67, 68, 66, 67, 68, -1, 96, 97, 98, - -1, 66, 67, 68, -1, -1, -1, 96, 97, 98, - -1, 96, 97, 98, 96, 97, 98, -1, -1, -1, - 96, 97, 98, 96, 97, 98, 15, 29, -1, 15, - 29, 96, 97, 98, 36, 29, -1, 36, -1, -1, - 29, -1, 36, 29, -1, -1, -1, 29, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 66, 67, 68, 66, 67, 68, - -1, -1, 66, 67, 68, -1, -1, 66, 67, 68, - 66, 67, 68, -1, 66, 67, 68, -1, -1, -1, - 29, -1, -1, -1, 96, 97, 98, 96, 97, 98, - 29, -1, 96, 97, 98, -1, -1, 96, 97, 98, - 96, 97, 98, 29, 96, 97, 98, -1, -1, -1, - -1, -1, 61, 62, -1, -1, 15, 66, 67, 68, - -1, -1, 61, 62, -1, -1, -1, 66, 67, 68, - 29, -1, -1, -1, -1, 61, 62, -1, -1, -1, - 66, 67, 68, 29, -1, -1, 29, 96, 97, 98, + 7, 33, 60, 60, 7, 36, 7, 7, 60, 7, + 36, 36, 7, 7, 60, 36, 55, 7, 33, 55, + 33, 7, 36, 7, 36, 36, 7, 37, 36, 7, + 36, 7, 7, 7, 33, 7, 33, 33, 47, 66, + 17, 34, 47, 66, 29, 37, 29, 29, 17, 29, + 29, 17, 5, 5, 5, 20, 60, 7, 60, 8, + 33, 7, 33, 33, 55, 33, 55, 7, 55, 36, + 33, 33, 60, 33, 7, 36, 33, 36, 36, 33, + 8, 7, 2, 7, 1, 8, 1, 8, 1, 36, + 8, 2, 8, -1, 2, -1, 60, -1, 33, 55, + 55, 7, 36, 1, 48, 0, 7, 36, 2, 36, + 48, 36, 60, 17, 33, 60, 48, 16, 8, 55, + 61, 60, 36, 7, 36, 8, 36, 36, 8, 60, + 8, 8, 6, 8, 15, -1, 79, 79, 8, 61, + 77, 48, 15, 7, 8, 8, 20, 15, 15, 79, + -1, 24, 8, 34, 8, 8, -1, -1, 61, 61, + 62, 61, 62, 8, 61, 62, 34, 34, 50, 61, + 62, 50, 54, 8, 79, 54, 8, 60, 56, 10, + 60, 56, 40, 60, 8, 61, 62, 8, 40, 12, + 60, 7, 55, 51, 40, 61, 62, 93, 94, 51, + 61, 62, 31, 7, 60, 51, 60, 60, 93, 94, + 15, 56, 61, 62, 61, 62, 42, 61, 62, 24, + 15, 56, 61, 62, 55, 61, 62, 53, 60, 33, + 29, 29, 7, 12, 57, 56, 25, 61, 27, 34, + 63, 36, 25, 7, 27, 61, 62, 29, 25, 38, + 27, 25, 25, 27, 27, 38, 25, 29, 27, 95, + -1, 38, 29, -1, 38, 38, 29, -1, 12, 38, + 36, 29, 25, -1, 27, -1, 75, 75, 57, 40, + 15, 8, -1, 8, 63, 38, 61, 62, 87, 87, + 51, -1, 96, 75, -1, 61, 62, 61, 62, 34, + -1, 36, 15, 75, 8, 87, -1, -1, 75, 25, + -1, 27, 75, 57, 25, 87, 27, 75, -1, 63, + 87, 34, 38, 36, 87, 61, 62, 38, 25, 87, + 27, 25, -1, 27, 61, 62, 61, 62, 25, -1, + 27, 38, 15, 25, 38, 27, 15, 15, 18, 19, + -1, 38, 18, 19, 90, 47, 38, 61, 62, -1, + 33, 34, -1, 36, 33, 34, 34, 36, 36, 61, + 62, -1, 23, 24, -1, 45, 46, -1, -1, 45, + 46, 32, 23, 24, 35, -1, 37, -1, 23, 24, + -1, 32, 23, 24, 35, -1, 37, 32, 23, 24, + 35, 32, 37, 95, 35, -1, 37, 32, 23, 24, + 35, -1, 37, -1, -1, -1, 31, 32, 23, 24, + 35, 29, 37, -1, -1, 29, 31, 32, -1, -1, + 35, -1, 37, 29, 23, 24, 29, -1, -1, 29, + 18, 19, 31, 32, 29, -1, 35, 29, 37, -1, + 100, 101, 102, 103, 104, 105, -1, -1, 66, 67, + 68, -1, 66, 67, 68, -1, -1, 45, 46, -1, + 66, 67, 68, 66, 67, 68, 66, 67, 68, -1, + 29, 66, 67, 68, 66, 67, 68, 29, 96, 97, + 98, -1, 96, 97, 98, 29, -1, -1, 29, -1, + 96, 97, 98, 96, 97, 98, 96, 97, 98, -1, + -1, 96, 97, 98, 96, 97, 98, 66, 67, 68, + 29, -1, -1, -1, 66, 67, 68, -1, -1, -1, + 23, 24, 66, 67, 68, 66, 67, 68, 31, 32, + -1, -1, 35, 29, 37, -1, 15, 96, 97, 98, + 36, -1, -1, -1, 96, 97, 98, 66, 67, 68, + 29, -1, 96, 97, 98, 96, 97, 98, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, + 66, 67, 68, -1, -1, -1, 15, 96, 97, 98, + 15, -1, -1, 29, 15, -1, 29, 66, 67, 68, + 29, -1, -1, 36, 29, -1, -1, -1, 29, -1, + 96, 97, 98, -1, -1, 29, -1, -1, 29, -1, -1, -1, -1, -1, -1, -1, -1, 96, 97, 98, - -1, -1, -1, -1, -1, -1, -1, 66, 67, 68, - 96, 97, 98, -1, -1, 61, 62, -1, 61, 62, - 66, 67, 68, 66, 67, 68, -1, -1, -1, -1, - -1, -1, -1, -1, 3, -1, -1, 96, 97, 98, - -1, -1, -1, -1, 13, -1, -1, -1, 17, 29, - 96, 97, 98, 96, 97, 98, -1, 26, -1, 28, - -1, -1, 31, -1, -1, -1, -1, -1, -1, -1, - 39, -1, 41, 42, -1, -1, -1, -1, -1, -1, - 49, 61, 62, 52, 53, -1, 66, 67, 68, 58, - -1, -1, -1, -1, -1, 64, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 80, -1, -1, -1, -1, 96, 97, 98, -1, - -1, -1, -1, -1, -1, -1, 12, 13, 3, -1, - -1, -1, -1, -1, -1, -1, 22, -1, 13, -1, - -1, -1, 17, 29, -1, -1, -1, 33, 34, -1, - 36, 26, -1, 28, -1, -1, -1, 43, -1, -1, - -1, 47, -1, -1, 39, -1, 41, 42, -1, -1, - -1, -1, -1, -1, 49, -1, -1, 52, 53, 65, - 66, 67, 68, 58, 70, -1, -1, -1, -1, 64, - -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, -1, 88, -1, -1, 80, -1, -1, -1, -1, - 96, 97, 98, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 66, 67, 68, 66, 67, 68, -1, 66, 67, 68, + -1, 66, 67, 68, -1, 66, 67, 68, -1, -1, + 61, 62, 66, 67, 68, 66, 67, 68, 29, -1, + 96, 97, 98, 96, 97, 98, -1, 96, 97, 98, + 29, 96, 97, 98, -1, 96, 97, 98, 29, -1, + -1, 29, 96, 97, 98, 96, 97, 98, -1, -1, + 61, 62, -1, -1, 29, 66, 67, 68, -1, -1, + -1, 36, 61, 62, -1, -1, -1, 66, 67, 68, + 61, 62, -1, 61, 62, 66, 67, 68, 66, 67, + 68, -1, -1, -1, -1, 96, 97, 98, -1, -1, + 3, 66, 67, 68, -1, -1, -1, 96, 97, 98, + 13, -1, -1, -1, 17, 96, 97, 98, 96, 97, + 98, -1, -1, 26, -1, 28, -1, -1, -1, -1, + -1, 96, 97, 98, -1, -1, 39, -1, 41, 42, + -1, -1, -1, 29, -1, -1, 49, -1, -1, 52, + 53, -1, -1, -1, -1, 58, -1, -1, -1, -1, + -1, 64, -1, -1, -1, -1, -1, -1, -1, 3, + -1, -1, -1, -1, -1, 61, 62, 80, -1, 13, + 66, 67, 68, 17, -1, -1, -1, -1, -1, -1, + -1, -1, 26, -1, 28, -1, -1, 31, -1, -1, + -1, -1, -1, -1, -1, 39, -1, 41, 42, -1, + 96, 97, 98, -1, -1, 49, -1, -1, 52, 53, + -1, -1, 3, -1, 58, -1, -1, -1, -1, -1, + 64, -1, 13, -1, -1, -1, 17, -1, -1, -1, + -1, -1, -1, -1, -1, 26, 80, 28, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 39, -1, + 41, 42, -1, -1, -1, -1, -1, -1, 49, -1, + -1, 52, 53, -1, -1, -1, -1, 58, -1, -1, + -1, -1, -1, 64, -1, 12, 13, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, 80, + -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, + -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, + 47, -1, -1, -1, -1, -1, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, 65, 66, + 67, 68, -1, 70, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, 81, 82, 83, -1, 43, -1, + -1, 88, 47, -1, -1, -1, -1, -1, -1, 96, + 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, + -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, @@ -1069,60 +1104,64 @@ const short QQmlJSGrammar::action_check [] = { 91, -1, -1, -1, -1, 96, 97, 98, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 3, 43, 32, 18, 32, 9, 18, 18, 78, 18, - 18, 3, 18, 18, 32, 25, 18, 3, 3, 43, - 18, 3, 3, 22, 18, 18, 22, 18, 18, 3, - 18, 32, 32, 22, 14, 22, 14, 3, 3, 18, - 3, 25, 106, 18, 3, 3, 43, 18, 43, 25, - 3, 18, 25, 3, 104, 3, 101, 18, 3, 3, - 43, 25, 18, 18, 18, 14, 43, 18, 2, -1, - 3, 43, 32, 43, 43, 43, 18, 18, 18, 18, - 2, 43, 18, 18, 18, 14, 2, 18, 3, -1, - 18, 2, 4, 18, 14, 14, 18, -1, 55, 19, - 57, -1, 18, 55, 23, 57, 18, 18, 55, 3, - 57, 47, 55, 18, 57, 46, 44, 55, 55, 57, - 57, 46, 43, 52, 2, 55, 18, 57, 55, 55, - 51, 55, 55, 60, 60, 4, 60, 55, 55, 57, - 18, 55, 47, 60, 14, 55, 79, 55, 71, 18, - 60, 55, 60, 57, 55, 69, 57, 43, 55, 39, - 57, 14, 55, 43, 79, 51, 19, 60, 55, 2, - 14, 55, 55, 60, 55, 110, 60, 60, 18, 60, - 55, 2, 52, 55, 59, 18, 3, 59, 55, 55, - 55, 57, 59, 2, 55, 60, 3, 18, 18, 4, - 55, 2, 18, 95, 2, 45, 67, 2, 52, 18, - 65, 2, 18, 18, 2, 18, 55, 18, 11, 12, - 18, 2, 61, 18, 55, 2, 57, 18, 48, 18, - 18, 47, 55, 55, 57, 57, 55, 18, 18, 58, - 46, 18, 55, 46, 55, 58, -1, 55, 2, 57, - 14, 55, 63, 55, -1, 19, 45, 61, 55, 61, - 57, 55, 79, 55, 18, 57, 2, 61, 55, 55, - 55, -1, 79, 60, 60, 60, 55, 62, 64, 55, - 55, 60, 18, 62, 60, 60, 62, 62, 55, 55, - 77, 18, 55, 60, 60, 2, 55, 60, 55, 55, - 66, 60, 55, 60, 60, 68, 14, 60, -1, 55, - 77, 18, -1, 93, 60, 23, 72, 70, 77, 2, - 77, 48, 49, -1, -1, 14, -1, 35, 36, 5, - 19, 77, 21, -1, -1, 18, -1, -1, 14, 25, - 26, 27, 28, 29, 30, 31, -1, 23, -1, -1, - -1, -1, 14, 89, 43, -1, -1, -1, -1, 35, - 36, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 18, 18, 32, 18, 32, 18, 3, 43, 18, 25, + 22, 22, 14, 14, 78, 18, 9, 3, 18, 3, + 18, 3, 18, 32, 18, 32, 32, 22, 18, 18, + 22, 3, 3, 18, 106, 43, 3, 18, 18, 101, + 3, 3, 18, 18, 18, 104, 3, 3, 18, 18, + 18, 3, 18, 18, 18, 43, 3, 3, 3, 32, + 14, 3, 3, 3, 25, 25, 25, -1, 55, 18, + 57, 2, 55, -1, -1, 2, 55, 60, 3, -1, + 18, 60, 18, 18, -1, 43, 25, 18, 43, -1, + 43, 18, 43, 43, 18, 43, 11, 12, 14, 43, + 43, 4, 3, 19, 18, 18, 3, 14, 3, 45, + 18, -1, 18, -1, 2, 18, 23, 2, 55, 2, + 57, 55, 55, 57, 57, 55, 55, 57, 57, 55, + 18, 57, 45, 18, 55, 18, 57, 2, 46, 2, + 2, 47, 2, 55, 55, 55, 55, 57, 60, 60, + 55, 60, 57, 18, 79, 18, 18, 55, 18, 55, + 95, 57, 60, 14, 2, 2, 55, -1, 55, 18, + 14, 55, 61, 55, 61, 19, 60, 59, 79, 55, + 18, 18, 79, 59, 79, 55, 55, 55, 14, 55, + 60, 60, 60, 18, 60, 2, 110, 55, 47, 55, + 43, 52, 60, 59, 18, 55, 2, 57, 51, 55, + 55, 18, 39, 55, 55, 18, 43, 4, 2, 55, + 65, 67, 18, 2, 18, 61, 52, 2, 69, 71, + 2, 18, 46, 55, 18, 57, 55, 4, 57, 18, + 55, 44, 57, 18, 55, 55, 18, 58, 58, 18, + 43, 18, 46, 55, 14, 57, 55, 18, 51, 19, + 2, 2, 61, 55, 55, 57, 55, 55, 93, 57, + -1, 60, 63, -1, -1, 55, 18, 18, 47, 68, + 60, -1, 55, -1, 64, 46, 18, 60, -1, 62, + 55, 55, -1, -1, -1, 60, 60, 62, 62, 55, + 55, -1, 55, 55, 60, 60, 62, 60, 60, 55, + 55, -1, 55, 66, 60, 60, 48, 60, 55, 55, + -1, 14, 77, 60, 60, 77, 19, 72, 21, -1, + 14, 77, -1, 70, 77, 5, -1, 5, -1, 23, + -1, 77, -1, -1, 14, -1, 14, 89, 89, -1, + 43, 35, 36, 23, -1, 23, 25, 26, 27, 28, + 29, 30, 31, -1, -1, 35, 36, 35, 36, -1, + -1, -1, 14, -1, -1, -1, -1, -1, -1, -1, + 18, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 14, -1, -1, -1, -1, -1, -1, -1, -1, 23, + 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, + 48, 49, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14, -1, -1, -1, -1, -1, - -1, -1, 89, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 14, -1, -1, -1, -1, -1, -1, -1, - -1, 23, 24, 25, 26, 27, 28, 29, 30, 31, + -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, + 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, - -1, -1, -1, -1, -1, -1, -1, -1, 14, -1, - -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, - 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1 + -1, -1 }; QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h index aa8450f218..9d028f2191 100644 --- a/src/qml/parser/qqmljsgrammar_p.h +++ b/src/qml/parser/qqmljsgrammar_p.h @@ -169,15 +169,15 @@ public: T_XOR = 79, T_XOR_EQ = 80, - ACCEPT_STATE = 687, - RULE_COUNT = 367, - STATE_COUNT = 688, + ACCEPT_STATE = 691, + RULE_COUNT = 369, + STATE_COUNT = 692, TERMINAL_COUNT = 108, NON_TERMINAL_COUNT = 112, - GOTO_INDEX_OFFSET = 688, - GOTO_INFO_OFFSET = 3217, - GOTO_CHECK_OFFSET = 3217 + GOTO_INDEX_OFFSET = 692, + GOTO_INFO_OFFSET = 3357, + GOTO_CHECK_OFFSET = 3357 }; static const char *const spell[]; diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index e176c6e3cf..718ff4d09a 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -92,7 +92,6 @@ Parser::Parser(Engine *engine): location_stack(0), string_stack(0), program(0), - yylval(0), first_token(0), last_token(0) { @@ -673,55 +672,71 @@ case 77: { } case 78: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval); + node->memberToken = loc(1); + node->valueToken = loc(3); + sym(1).Node = node; + break; +} + +case 79: { AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3)); node->memberToken = loc(3); sym(1).Node = node; break; } -case 86: { +case 80: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval); + node->memberToken = loc(3); + node->valueToken = loc(5); + sym(1).Node = node; + break; +} + +case 88: { AST::ThisExpression *node = new (pool) AST::ThisExpression(); node->thisToken = loc(1); sym(1).Node = node; } break; -case 87: { +case 89: { AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 88: { +case 90: { AST::NullExpression *node = new (pool) AST::NullExpression(); node->nullToken = loc(1); sym(1).Node = node; } break; -case 89: { +case 91: { AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); node->trueToken = loc(1); sym(1).Node = node; } break; -case 90: { +case 92: { AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); node->falseToken = loc(1); sym(1).Node = node; } break; -case 91: { +case 93: { AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); node->literalToken = loc(1); sym(1).Node = node; } break; -case 92: -case 93: { +case 94: +case 95: { AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); node->literalToken = loc(1); sym(1).Node = node; } break; -case 94: { +case 96: { bool rx = lexer->scanRegExp(Lexer::NoPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -737,7 +752,7 @@ case 94: { sym(1).Node = node; } break; -case 95: { +case 97: { bool rx = lexer->scanRegExp(Lexer::EqualPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -753,28 +768,28 @@ case 95: { sym(1).Node = node; } break; -case 96: { +case 98: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); node->lbracketToken = loc(1); node->rbracketToken = loc(2); sym(1).Node = node; } break; -case 97: { +case 99: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 98: { +case 100: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 99: { +case 101: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), (AST::Elision *) 0); node->lbracketToken = loc(1); @@ -783,7 +798,7 @@ case 99: { sym(1).Node = node; } break; -case 100: { +case 102: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), sym(4).Elision->finish()); node->lbracketToken = loc(1); @@ -792,7 +807,7 @@ case 100: { sym(1).Node = node; } break; -case 101: { +case 103: { AST::ObjectLiteral *node = 0; if (sym(2).Node) node = new (pool) AST::ObjectLiteral( @@ -804,7 +819,7 @@ case 101: { sym(1).Node = node; } break; -case 102: { +case 104: { AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( sym(2).PropertyAssignmentList->finish ()); node->lbraceToken = loc(1); @@ -812,14 +827,14 @@ case 102: { sym(1).Node = node; } break; -case 103: { +case 105: { AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); node->lparenToken = loc(1); node->rparenToken = loc(3); sym(1).Node = node; } break; -case 104: { +case 106: { if (AST::ArrayMemberExpression *mem = AST::cast(sym(1).Expression)) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, QLatin1String("Ignored annotation"))); @@ -839,48 +854,48 @@ case 104: { } } break; -case 105: { +case 107: { sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); } break; -case 106: { +case 108: { sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); } break; -case 107: { +case 109: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, (AST::Elision *) 0, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 108: { +case 110: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), sym(4).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 109: { +case 111: { AST::Elision *node = new (pool) AST::Elision(); node->commaToken = loc(1); sym(1).Node = node; } break; -case 110: { +case 112: { AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); node->commaToken = loc(2); sym(1).Node = node; } break; -case 111: { +case 113: { AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( sym(1).PropertyName, sym(3).Expression); node->colonToken = loc(2); sym(1).Node = node; } break; -case 112: { +case 114: { AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( sym(2).PropertyName, sym(6).FunctionBody); node->getSetToken = loc(1); @@ -891,7 +906,7 @@ case 112: { sym(1).Node = node; } break; -case 113: { +case 115: { AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); node->getSetToken = loc(1); @@ -902,56 +917,56 @@ case 113: { sym(1).Node = node; } break; -case 114: { +case 116: { sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); } break; -case 115: { +case 117: { AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); node->commaToken = loc(2); sym(1).Node = node; } break; -case 116: { +case 118: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 117: { +case 119: { AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 118: { +case 120: { AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 119: { +case 121: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 157: { +case 159: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 158: { +case 160: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 159: { +case 161: { AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); node->newToken = loc(1); node->lparenToken = loc(3); @@ -959,316 +974,309 @@ case 159: { sym(1).Node = node; } break; -case 161: { +case 163: { AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); node->newToken = loc(1); sym(1).Node = node; } break; -case 162: { +case 164: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 163: { +case 165: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 164: { +case 166: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 165: { +case 167: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 166: { +case 168: { sym(1).Node = 0; } break; -case 167: { +case 169: { sym(1).Node = sym(1).ArgumentList->finish(); } break; -case 168: { +case 170: { sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); } break; -case 169: { +case 171: { AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 173: { +case 175: { AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); node->incrementToken = loc(2); sym(1).Node = node; } break; -case 174: { +case 176: { AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); node->decrementToken = loc(2); sym(1).Node = node; } break; -case 176: { +case 178: { AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); node->deleteToken = loc(1); sym(1).Node = node; } break; -case 177: { +case 179: { AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); node->voidToken = loc(1); sym(1).Node = node; } break; -case 178: { +case 180: { AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); node->typeofToken = loc(1); sym(1).Node = node; } break; -case 179: { +case 181: { AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); node->incrementToken = loc(1); sym(1).Node = node; } break; -case 180: { +case 182: { AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); node->decrementToken = loc(1); sym(1).Node = node; } break; -case 181: { +case 183: { AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); node->plusToken = loc(1); sym(1).Node = node; } break; -case 182: { +case 184: { AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); node->minusToken = loc(1); sym(1).Node = node; } break; -case 183: { +case 185: { AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); node->tildeToken = loc(1); sym(1).Node = node; } break; -case 184: { +case 186: { AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); node->notToken = loc(1); sym(1).Node = node; } break; -case 186: { +case 188: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mul, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 187: { +case 189: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Div, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 188: { +case 190: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mod, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 190: { +case 192: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 191: { +case 193: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 193: { +case 195: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 194: { +case 196: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 195: { +case 197: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 197: { +case 199: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 198: { +case 200: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 199: { +case 201: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 200: { +case 202: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 201: { +case 203: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 202: { +case 204: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 204: { +case 206: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 205: { +case 207: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 206: { +case 208: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 207: { +case 209: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 208: { +case 210: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 210: { +case 212: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 211: { +case 213: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 212: { +case 214: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 213: { +case 215: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 215: { +case 217: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 216: { +case 218: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 217: { +case 219: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 218: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::StrictNotEqual, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; -} break; - case 220: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitAnd, sym(3).Expression); + QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; @@ -1282,7 +1290,7 @@ case 222: { case 224: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitXor, sym(3).Expression); + QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; @@ -1296,7 +1304,7 @@ case 226: { case 228: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::BitOr, sym(3).Expression); + QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; @@ -1310,7 +1318,7 @@ case 230: { case 232: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::And, sym(3).Expression); + QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; @@ -1324,7 +1332,7 @@ case 234: { case 236: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, - QSOperator::Or, sym(3).Expression); + QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; @@ -1337,6 +1345,13 @@ case 238: { } break; case 240: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 242: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1344,7 +1359,7 @@ case 240: { sym(1).Node = node; } break; -case 242: { +case 244: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1352,112 +1367,112 @@ case 242: { sym(1).Node = node; } break; -case 244: { +case 246: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 246: { +case 248: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 247: { +case 249: { sym(1).ival = QSOperator::Assign; } break; -case 248: { +case 250: { sym(1).ival = QSOperator::InplaceMul; } break; -case 249: { +case 251: { sym(1).ival = QSOperator::InplaceDiv; } break; -case 250: { +case 252: { sym(1).ival = QSOperator::InplaceMod; } break; -case 251: { +case 253: { sym(1).ival = QSOperator::InplaceAdd; } break; -case 252: { +case 254: { sym(1).ival = QSOperator::InplaceSub; } break; -case 253: { +case 255: { sym(1).ival = QSOperator::InplaceLeftShift; } break; -case 254: { +case 256: { sym(1).ival = QSOperator::InplaceRightShift; } break; -case 255: { +case 257: { sym(1).ival = QSOperator::InplaceURightShift; } break; -case 256: { +case 258: { sym(1).ival = QSOperator::InplaceAnd; } break; -case 257: { +case 259: { sym(1).ival = QSOperator::InplaceXor; } break; -case 258: { +case 260: { sym(1).ival = QSOperator::InplaceOr; } break; -case 260: { +case 262: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 261: { +case 263: { sym(1).Node = 0; } break; -case 264: { +case 266: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 265: { +case 267: { sym(1).Node = 0; } break; -case 282: { +case 284: { AST::Block *node = new (pool) AST::Block(sym(2).StatementList); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 283: { +case 285: { sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); } break; -case 284: { +case 286: { sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); } break; -case 285: { +case 287: { sym(1).Node = 0; } break; -case 286: { +case 288: { sym(1).Node = sym(1).StatementList->finish (); } break; -case 288: { +case 290: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; if (sym(1).ival == T_LET) s = AST::VariableDeclaration::BlockScope; @@ -1470,82 +1485,82 @@ case 288: { sym(1).Node = node; } break; -case 289: { +case 291: { sym(1).ival = T_LET; } break; -case 290: { +case 292: { sym(1).ival = T_CONST; } break; -case 291: { +case 293: { sym(1).ival = T_VAR; } break; -case 292: { +case 294: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 293: { +case 295: { AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( sym(1).VariableDeclarationList, sym(3).VariableDeclaration); node->commaToken = loc(2); sym(1).Node = node; } break; -case 294: { +case 296: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 295: { +case 297: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); } break; -case 296: { +case 298: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 297: { +case 299: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 298: { +case 300: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 299: { +case 301: { sym(1).Node = 0; } break; -case 301: { +case 303: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 302: { +case 304: { sym(1).Node = 0; } break; -case 304: { +case 306: { AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); node->semicolonToken = loc(1); sym(1).Node = node; } break; -case 306: { +case 308: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 307: { +case 309: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1554,7 +1569,7 @@ case 307: { sym(1).Node = node; } break; -case 308: { +case 310: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1562,7 +1577,7 @@ case 308: { sym(1).Node = node; } break; -case 311: { +case 313: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); node->doToken = loc(1); node->whileToken = loc(3); @@ -1572,7 +1587,7 @@ case 311: { sym(1).Node = node; } break; -case 312: { +case 314: { AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); node->whileToken = loc(1); node->lparenToken = loc(2); @@ -1580,7 +1595,7 @@ case 312: { sym(1).Node = node; } break; -case 313: { +case 315: { AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); node->forToken = loc(1); @@ -1591,7 +1606,7 @@ case 313: { sym(1).Node = node; } break; -case 314: { +case 316: { AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( sym(4).VariableDeclarationList->finish(s), sym(6).Expression, @@ -1605,7 +1620,7 @@ case 314: { sym(1).Node = node; } break; -case 315: { +case 317: { AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); @@ -1615,7 +1630,7 @@ case 315: { sym(1).Node = node; } break; -case 316: { +case 318: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); node->forToken = loc(1); @@ -1626,14 +1641,14 @@ case 316: { sym(1).Node = node; } break; -case 318: { +case 320: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); node->continueToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 320: { +case 322: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); node->continueToken = loc(1); node->identifierToken = loc(2); @@ -1641,14 +1656,14 @@ case 320: { sym(1).Node = node; } break; -case 322: { +case 324: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); node->breakToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 324: { +case 326: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); node->breakToken = loc(1); node->identifierToken = loc(2); @@ -1656,14 +1671,14 @@ case 324: { sym(1).Node = node; } break; -case 326: { +case 328: { AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); node->returnToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 327: { +case 329: { AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); node->withToken = loc(1); node->lparenToken = loc(2); @@ -1671,7 +1686,7 @@ case 327: { sym(1).Node = node; } break; -case 328: { +case 330: { AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); node->switchToken = loc(1); node->lparenToken = loc(2); @@ -1679,83 +1694,83 @@ case 328: { sym(1).Node = node; } break; -case 329: { +case 331: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 330: { +case 332: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(5); sym(1).Node = node; } break; -case 331: { +case 333: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); } break; -case 332: { +case 334: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); } break; -case 333: { +case 335: { sym(1).Node = 0; } break; -case 334: { +case 336: { sym(1).Node = sym(1).CaseClauses->finish (); } break; -case 335: { +case 337: { AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); node->caseToken = loc(1); node->colonToken = loc(3); sym(1).Node = node; } break; -case 336: { +case 338: { AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); node->defaultToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 337: { +case 339: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); node->identifierToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 339: { +case 341: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); node->throwToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 340: { +case 342: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); node->tryToken = loc(1); sym(1).Node = node; } break; -case 341: { +case 343: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 342: { +case 344: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 343: { +case 345: { AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); node->catchToken = loc(1); node->lparenToken = loc(2); @@ -1764,20 +1779,20 @@ case 343: { sym(1).Node = node; } break; -case 344: { +case 346: { AST::Finally *node = new (pool) AST::Finally(sym(2).Block); node->finallyToken = loc(1); sym(1).Node = node; } break; -case 346: { +case 348: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); node->debuggerToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 348: { +case 350: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); node->identifierToken = loc(2); @@ -1788,7 +1803,7 @@ case 348: { sym(1).Node = node; } break; -case 349: { +case 351: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); if (! stringRef(2).isNull()) @@ -1800,7 +1815,7 @@ case 349: { sym(1).Node = node; } break; -case 350: { +case 352: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); node->functionToken = loc(1); node->lparenToken = loc(2); @@ -1810,56 +1825,56 @@ case 350: { sym(1).Node = node; } break; -case 351: { +case 353: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 352: { +case 354: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); node->commaToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 353: { +case 355: { sym(1).Node = 0; } break; -case 354: { +case 356: { sym(1).Node = sym(1).FormalParameterList->finish (); } break; -case 355: { +case 357: { sym(1).Node = 0; } break; -case 357: { +case 359: { sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); } break; -case 359: { +case 361: { sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); } break; -case 360: { +case 362: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); } break; -case 361: { +case 363: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); } break; -case 362: { +case 364: { sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); } break; -case 363: { +case 365: { sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); } break; -case 364: { +case 366: { sym(1).Node = 0; } break; diff --git a/src/qml/parser/qqmljsparser_p.h b/src/qml/parser/qqmljsparser_p.h index 6273ed48b3..9dfee70f3a 100644 --- a/src/qml/parser/qqmljsparser_p.h +++ b/src/qml/parser/qqmljsparser_p.h @@ -247,9 +247,9 @@ protected: -#define J_SCRIPT_REGEXPLITERAL_RULE1 94 +#define J_SCRIPT_REGEXPLITERAL_RULE1 96 -#define J_SCRIPT_REGEXPLITERAL_RULE2 95 +#define J_SCRIPT_REGEXPLITERAL_RULE2 97 QT_QML_END_NAMESPACE -- cgit v1.2.3 From 4c2ec889328b0a3988e7a7e8f4cae434e647f8cc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 Aug 2017 11:25:50 +0200 Subject: Restore yylval initialization Forward port commit b04b7d340f39dfdde560b1303ea6a8c8ace8056e to do the initialization in the original file, not in the generated one. Change-Id: I070d9668c75be20c014835514db49d1a6518d590 Reviewed-by: Robert Loehning Reviewed-by: Michael Brasser --- src/qml/parser/qqmljs.g | 1 + src/qml/parser/qqmljsparser.cpp | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 667bcd1801..d2d947e55c 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -451,6 +451,7 @@ Parser::Parser(Engine *engine): location_stack(0), string_stack(0), program(0), + yylval(0), first_token(0), last_token(0) { diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index 718ff4d09a..df16a24bcc 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -92,6 +92,7 @@ Parser::Parser(Engine *engine): location_stack(0), string_stack(0), program(0), + yylval(0), first_token(0), last_token(0) { -- cgit v1.2.3 From 46ed14da325c6c0382c0bc54cacc347d2d7f2b0a Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Wed, 16 Aug 2017 18:23:04 +0200 Subject: Retranslate all child contexts Do not only reevaluate the expressions of the first child, but of all child contexts. Task-number: QTBUG-15602 Change-Id: Ieacc441f14f7a26793bfcdc2206030a78d2d429c Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlengine.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index eb2fbcdd73..194c58b805 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1342,8 +1342,11 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) void QQmlEngine::retranslate() { Q_D(QQmlEngine); - if (QQmlContextData *firstChildContext = QQmlContextData::get(d->rootContext)->childContexts) - firstChildContext->refreshExpressions(); + QQmlContextData *context = QQmlContextData::get(d->rootContext)->childContexts; + while (context) { + context->refreshExpressions(); + context = context->nextChild; + } } /*! -- cgit v1.2.3