aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp77
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp159
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h4
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h22
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp17
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h18
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile.cpp13
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile_p.h9
-rw-r--r--src/quick/items/items.pri81
-rw-r--r--src/quick/items/items.qrc4
-rw-r--r--src/quick/items/qquickanchors.cpp32
-rw-r--r--src/quick/items/qquickanchors_p_p.h4
-rw-r--r--src/quick/items/qquickanimatedimage.cpp11
-rw-r--r--src/quick/items/qquickanimatedimage_p_p.h12
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp684
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h286
-rw-r--r--src/quick/items/qquickanimatedsprite_p_p.h96
-rw-r--r--src/quick/items/qquickborderimage.cpp102
-rw-r--r--src/quick/items/qquickborderimage_p.h2
-rw-r--r--src/quick/items/qquickborderimage_p_p.h30
-rw-r--r--src/quick/items/qquickclipnode.cpp2
-rw-r--r--src/quick/items/qquickdrag.cpp53
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickdroparea.cpp2
-rw-r--r--src/quick/items/qquickevents.cpp537
-rw-r--r--src/quick/items/qquickevents_p_p.h402
-rw-r--r--src/quick/items/qquickflickable.cpp71
-rw-r--r--src/quick/items/qquickflickable_p.h2
-rw-r--r--src/quick/items/qquickflickable_p_p.h3
-rw-r--r--src/quick/items/qquickframebufferobject.cpp13
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp658
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h153
-rw-r--r--src/quick/items/qquickgraphicsinfo.cpp303
-rw-r--r--src/quick/items/qquickgraphicsinfo_p.h166
-rw-r--r--src/quick/items/qquickgridview.cpp18
-rw-r--r--src/quick/items/qquickimage.cpp4
-rw-r--r--src/quick/items/qquickimagebase.cpp2
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp13
-rw-r--r--src/quick/items/qquickitem.cpp422
-rw-r--r--src/quick/items/qquickitem.h3
-rw-r--r--src/quick/items/qquickitem_p.h38
-rw-r--r--src/quick/items/qquickitemanimation.cpp4
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h59
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp3
-rw-r--r--src/quick/items/qquickitemsmodule.cpp21
-rw-r--r--src/quick/items/qquickitemview.cpp59
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/items/qquicklistview.cpp50
-rw-r--r--src/quick/items/qquicklistview_p.h6
-rw-r--r--src/quick/items/qquickloader.cpp5
-rw-r--r--src/quick/items/qquickloader_p_p.h2
-rw-r--r--src/quick/items/qquickmousearea.cpp44
-rw-r--r--src/quick/items/qquickmousearea_p_p.h3
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp71
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h4
-rw-r--r--src/quick/items/qquickopenglinfo.cpp4
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp973
-rw-r--r--src/quick/items/qquickopenglshadereffect_p.h194
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp (renamed from src/quick/items/qquickshadereffectnode.cpp)97
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h (renamed from src/quick/items/qquickshadereffectnode_p.h)52
-rw-r--r--src/quick/items/qquickpainteditem.cpp29
-rw-r--r--src/quick/items/qquickpathview.cpp69
-rw-r--r--src/quick/items/qquickpathview_p.h3
-rw-r--r--src/quick/items/qquickpathview_p_p.h6
-rw-r--r--src/quick/items/qquickpositioners_p_p.h4
-rw-r--r--src/quick/items/qquickrectangle.cpp7
-rw-r--r--src/quick/items/qquickrendercontrol.cpp65
-rw-r--r--src/quick/items/qquickrepeater.cpp4
-rw-r--r--src/quick/items/qquickshadereffect.cpp1315
-rw-r--r--src/quick/items/qquickshadereffect_p.h123
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp287
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h59
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp11
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h12
-rw-r--r--src/quick/items/qquicksprite_p.h2
-rw-r--r--src/quick/items/qquickspriteengine.cpp144
-rw-r--r--src/quick/items/qquickspriteengine_p.h38
-rw-r--r--src/quick/items/qquickspritesequence.cpp402
-rw-r--r--src/quick/items/qquickspritesequence_p.h68
-rw-r--r--src/quick/items/qquickspritesequence_p_p.h90
-rw-r--r--src/quick/items/qquickstateoperations.cpp26
-rw-r--r--src/quick/items/qquicktext.cpp46
-rw-r--r--src/quick/items/qquicktextcontrol.cpp33
-rw-r--r--src/quick/items/qquicktextcontrol_p.h3
-rw-r--r--src/quick/items/qquicktextdocument.cpp2
-rw-r--r--src/quick/items/qquicktextedit.cpp72
-rw-r--r--src/quick/items/qquicktextedit_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp94
-rw-r--r--src/quick/items/qquicktextinput_p.h5
-rw-r--r--src/quick/items/qquicktextinput_p_p.h2
-rw-r--r--src/quick/items/qquicktextnode.cpp6
-rw-r--r--src/quick/items/qquicktextnode_p.h15
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp40
-rw-r--r--src/quick/items/qquicktextnodeengine_p.h19
-rw-r--r--src/quick/items/qquickview.cpp32
-rw-r--r--src/quick/items/qquickview_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp1571
-rw-r--r--src/quick/items/qquickwindow.h21
-rw-r--r--src/quick/items/qquickwindow_p.h92
-rw-r--r--src/quick/items/shaders/sprite.frag12
-rw-r--r--src/quick/items/shaders/sprite.vert23
-rw-r--r--src/quick/items/shaders/sprite_core.frag16
-rw-r--r--src/quick/items/shaders/sprite_core.vert24
104 files changed, 7454 insertions, 3638 deletions
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 89a68e4f33..3b2b125c63 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -43,7 +43,7 @@
#include <private/qquickcanvascontext_p.h>
#include <private/qquickcontext2d_p.h>
#include <private/qquickcontext2dtexture_p.h>
-#include <qsgsimpletexturenode.h>
+#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QtGui/QGuiApplication>
@@ -59,24 +59,11 @@
QT_BEGIN_NAMESPACE
-class QQuickCanvasNode : public QSGSimpleTextureNode
-{
-public:
- QQuickCanvasNode() {
- qsgnode_set_description(this, QStringLiteral("canvasnode"));
- setOwnsTexture(false);
- }
-
- ~QQuickCanvasNode() {
- delete texture();
- }
-};
-
class QQuickCanvasTextureProvider : public QSGTextureProvider
{
public:
- QQuickCanvasNode *node;
- QSGTexture *texture() const Q_DECL_OVERRIDE { return node ? node->texture() : 0; }
+ QSGTexture *tex;
+ QSGTexture *texture() const Q_DECL_OVERRIDE { return tex; }
void fireTextureChanged() { emit textureChanged(); }
};
@@ -187,7 +174,8 @@ public:
QUrl baseUrl;
QMap<int, QV4::PersistentValue> animationCallbacks;
mutable QQuickCanvasTextureProvider *textureProvider;
- QQuickCanvasNode *node;
+ QSGInternalImageNode *node;
+ QSGTexture *nodeTexture;
};
QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
@@ -203,6 +191,7 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
, renderStrategy(QQuickCanvasItem::Immediate)
, textureProvider(0)
, node(0)
+ , nodeTexture(0)
{
implicitAntialiasing = true;
}
@@ -256,17 +245,18 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
The Canvas item supports two render targets: \c Canvas.Image and
\c Canvas.FramebufferObject.
- The \c Canvas.Image render target is a \a QImage object. This render
- target supports background thread rendering, allowing complex or long
- running painting to be executed without blocking the UI.
+ The \c Canvas.Image render target is a \a QImage object. This render target
+ supports background thread rendering, allowing complex or long running
+ painting to be executed without blocking the UI. This is the only render
+ target that is supported by all Qt Quick backends.
The Canvas.FramebufferObject render target utilizes OpenGL hardware
acceleration rather than rendering into system memory, which in many cases
- results in faster rendering. Canvas.FramebufferObject relies on the
- OpenGL extensions \c GL_EXT_framebuffer_multisample and
- \c GL_EXT_framebuffer_blit for antialiasing. It will also use more
- graphics memory when rendering strategy is anything other than
- Canvas.Cooperative.
+ results in faster rendering. Canvas.FramebufferObject relies on the OpenGL
+ extensions \c GL_EXT_framebuffer_multisample and \c GL_EXT_framebuffer_blit
+ for antialiasing. It will also use more graphics memory when rendering
+ strategy is anything other than Canvas.Cooperative. Framebuffer objects may
+ not be available with Qt Quick backends other than OpenGL.
The default render target is Canvas.Image and the default renderStrategy is
Canvas.Immediate.
@@ -301,7 +291,14 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
classes that consume texture providers.
- \sa Context2D
+ \note In general large canvases, frequent updates, and animation should be
+ avoided with the Canvas.Image render target. This is because with
+ accelerated graphics APIs each update will lead to a texture upload. Also,
+ if possible, prefer QQuickPaintedItem and implement drawing in C++ via
+ QPainter instead of the more expensive and likely less performing
+ JavaScript and Context2D approach.
+
+ \sa Context2D QQuickPaintedItem
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -712,7 +709,7 @@ void QQuickCanvasItem::updatePolish()
for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
QV4::ScopedFunctionObject f(scope, it.value().value());
callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
- f->call(callData);
+ f->call(scope, callData);
}
}
else {
@@ -739,16 +736,16 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (!d->context || d->canvasWindow.size().isEmpty()) {
if (d->textureProvider) {
- d->textureProvider->node = 0;
+ d->textureProvider->tex = 0;
d->textureProvider->fireTextureChanged();
}
delete oldNode;
return 0;
}
- QQuickCanvasNode *node = static_cast<QQuickCanvasNode*>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = new QQuickCanvasNode();
+ node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createInternalImageNode();
d->node = node;
}
@@ -765,22 +762,27 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context);
QQuickContext2DTexture *factory = ctx->texture();
- QSGTexture *texture = factory->textureForNextFrame(node->texture(), window());
+ QSGTexture *texture = factory->textureForNextFrame(d->nodeTexture, window());
if (!texture) {
delete node;
d->node = 0;
+ delete d->nodeTexture;
+ d->nodeTexture = 0;
if (d->textureProvider) {
- d->textureProvider->node = 0;
+ d->textureProvider->tex = 0;
d->textureProvider->fireTextureChanged();
}
return 0;
}
+ d->nodeTexture = texture;
node->setTexture(texture);
- node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->update();
if (d->textureProvider) {
- d->textureProvider->node = node;
+ d->textureProvider->tex = d->nodeTexture;
d->textureProvider->fireTextureChanged();
}
return node;
@@ -800,14 +802,17 @@ QSGTextureProvider *QQuickCanvasItem::textureProvider() const
return QQuickItem::textureProvider();
Q_D(const QQuickCanvasItem);
+#ifndef QT_NO_OPENGL
QQuickWindow *w = window();
- if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
+ if (!w || !w->isSceneGraphInitialized()
+ || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
qWarning("QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+#endif
if (!d->textureProvider)
d->textureProvider = new QQuickCanvasTextureProvider;
- d->textureProvider->node = d->node;
+ d->textureProvider->tex = d->nodeTexture;
return d->textureProvider;
}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b2117d3eb9..2483a8eadb 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -43,6 +43,7 @@
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
+#include <qsgrendererinterface.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qquicksvgparser_p.h>
@@ -74,6 +75,10 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#ifndef QT_NO_OPENGL
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+
#include <cmath>
#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
#include <ctype.h>
@@ -484,34 +489,43 @@ namespace QV4 {
namespace Heap {
struct QQuickJSContext2D : Object {
- QQuickJSContext2D() {}
+ void init() { Object::init(); }
QQuickContext2D* context;
};
struct QQuickJSContext2DPrototype : Object {
- QQuickJSContext2DPrototype() {}
+ void init() { Object::init(); }
};
struct QQuickContext2DStyle : Object {
- QQuickContext2DStyle()
+ void init()
{
+ brush = new QBrush;
patternRepeatX = false;
patternRepeatY = false;
}
+ void destroy() {
+ delete brush;
+ Object::destroy();
+ }
- QBrush brush;
+ QBrush *brush;
bool patternRepeatX:1;
bool patternRepeatY:1;
};
struct QQuickJSContext2DPixelData : Object {
- QQuickJSContext2DPixelData();
+ void init();
+ void destroy() {
+ delete image;
+ Object::destroy();
+ }
- QImage image;
+ QImage *image;
};
struct QQuickJSContext2DImageData : Object {
- QQuickJSContext2DImageData();
+ void init();
QV4::Value pixelData;
};
@@ -789,7 +803,7 @@ static QPainter::CompositionMode qt_composite_mode_from_string(const QString &co
} else if (compositeOperator == QLatin1String("destination-over")) {
return QPainter::CompositionMode_DestinationOver;
} else if (compositeOperator == QLatin1String("lighter")) {
- return QPainter::CompositionMode_Lighten;
+ return QPainter::CompositionMode_Plus;
} else if (compositeOperator == QLatin1String("copy")) {
return QPainter::CompositionMode_Source;
} else if (compositeOperator == QLatin1String("xor")) {
@@ -852,7 +866,7 @@ static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
case QPainter::CompositionMode_Xor:
return QStringLiteral("xor");
case QPainter::CompositionMode_Plus:
- return QStringLiteral("plus");
+ return QStringLiteral("lighter");
case QPainter::CompositionMode_Multiply:
return QStringLiteral("qt-multiply");
case QPainter::CompositionMode_Screen:
@@ -892,8 +906,10 @@ struct QQuickJSContext2DPixelData : public QV4::Object
static QV4::ReturnedValue proto_get_length(QV4::CallContext *ctx);
};
-QV4::Heap::QQuickJSContext2DPixelData::QQuickJSContext2DPixelData()
+void QV4::Heap::QQuickJSContext2DPixelData::init()
{
+ Object::init();
+ image = new QImage;
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
o->setArrayType(QV4::Heap::ArrayData::Custom);
@@ -915,8 +931,9 @@ struct QQuickJSContext2DImageData : public QV4::Object
}
};
-QV4::Heap::QQuickJSContext2DImageData::QQuickJSContext2DImageData()
+void QV4::Heap::QQuickJSContext2DImageData::init()
{
+ Object::init();
pixelData = QV4::Primitive::undefinedValue();
QV4::Scope scope(internalClass->engine);
@@ -938,11 +955,11 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
pixelData->setPrototype(p);
if (image.isNull()) {
- pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
- pixelData->d()->image.fill(0x00000000);
+ *pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
+ pixelData->d()->image->fill(0x00000000);
} else {
Q_ASSERT(image.width() == qRound(w) && image.height() == qRound(h));
- pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
+ *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
}
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DImageData>());
@@ -1387,9 +1404,9 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(QV4::CallContext *ctx
r->d()->context->m_fillStyle.set(scope.engine, value);
} else {
QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
- if (style && style->d()->brush != r->d()->context->state.fillStyle) {
- r->d()->context->state.fillStyle = style->d()->brush;
- r->d()->context->buffer()->setFillStyle(style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
+ if (style && *style->d()->brush != r->d()->context->state.fillStyle) {
+ r->d()->context->state.fillStyle = *style->d()->brush;
+ r->d()->context->buffer()->setFillStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
r->d()->context->m_fillStyle.set(scope.engine, value);
r->d()->context->state.fillPatternRepeatX = style->d()->patternRepeatX;
r->d()->context->state.fillPatternRepeatY = style->d()->patternRepeatY;
@@ -1496,9 +1513,9 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(QV4::CallContext *c
r->d()->context->m_strokeStyle.set(scope.engine, value);
} else {
QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
- if (style && style->d()->brush != r->d()->context->state.strokeStyle) {
- r->d()->context->state.strokeStyle = style->d()->brush;
- r->d()->context->buffer()->setStrokeStyle(style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
+ if (style && *style->d()->brush != r->d()->context->state.strokeStyle) {
+ r->d()->context->state.strokeStyle = *style->d()->brush;
+ r->d()->context->buffer()->setStrokeStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
r->d()->context->m_strokeStyle.set(scope.engine, value);
r->d()->context->state.strokePatternRepeatX = style->d()->patternRepeatX;
r->d()->context->state.strokePatternRepeatY = style->d()->patternRepeatY;
@@ -1556,7 +1573,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(QV4::
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
+ *gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
return gradient.asReturnedValue();
}
@@ -1607,7 +1624,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(QV4::
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ *gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
return gradient.asReturnedValue();
}
@@ -1650,7 +1667,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(QV4:
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QConicalGradient(x, y, angle);
+ *gradient->d()->brush = QConicalGradient(x, y, angle);
return gradient.asReturnedValue();
}
@@ -1715,7 +1732,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon
if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
style = static_cast<Qt::BrushStyle>(patternMode);
}
- pattern->d()->brush = QBrush(color, style);
+ *pattern->d()->brush = QBrush(color, style);
} else {
QImage patternTexture;
@@ -1723,14 +1740,14 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon
QV4::ScopedString s(scope, scope.engine->newString(QStringLiteral("data")));
QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, o->get(s));
if (!!pixelData) {
- patternTexture = pixelData->d()->image;
+ patternTexture = *pixelData->d()->image;
}
} else {
patternTexture = r->d()->context->createPixmap(QUrl(ctx->args()[0].toQStringNoThrow()))->image();
}
if (!patternTexture.isNull()) {
- pattern->d()->brush.setTextureImage(patternTexture);
+ pattern->d()->brush->setTextureImage(patternTexture);
QString repetition = ctx->args()[1].toQStringNoThrow();
if (repetition == QLatin1String("repeat") || repetition.isEmpty()) {
@@ -2669,15 +2686,15 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(QV4::CallContext *ctx
QString textAlign = s->toQString();
QQuickContext2D::TextAlignType ta;
- if (textAlign == QStringLiteral("start"))
+ if (textAlign == QLatin1String("start"))
ta = QQuickContext2D::Start;
- else if (textAlign == QStringLiteral("end"))
+ else if (textAlign == QLatin1String("end"))
ta = QQuickContext2D::End;
- else if (textAlign == QStringLiteral("left"))
+ else if (textAlign == QLatin1String("left"))
ta = QQuickContext2D::Left;
- else if (textAlign == QStringLiteral("right"))
+ else if (textAlign == QLatin1String("right"))
ta = QQuickContext2D::Right;
- else if (textAlign == QStringLiteral("center"))
+ else if (textAlign == QLatin1String("center"))
ta = QQuickContext2D::Center;
else
return QV4::Encode::undefined();
@@ -2920,8 +2937,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg);
if (!!imageData) {
QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
- if (pix && !pix->d()->image.isNull()) {
- pixmap.adopt(new QQuickCanvasPixmap(pix->d()->image));
+ if (pix && !pix->d()->image->isNull()) {
+ pixmap.adopt(new QQuickCanvasPixmap(*pix->d()->image));
} else {
V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
}
@@ -3029,7 +3046,7 @@ QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(QV4::CallContext
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (!r)
return QV4::Encode(0);
- return QV4::Encode(r->d()->image.width());
+ return QV4::Encode(r->d()->image->width());
}
/*!
@@ -3045,7 +3062,7 @@ QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(QV4::CallContex
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (!r)
return QV4::Encode(0);
- return QV4::Encode(r->d()->image.height());
+ return QV4::Encode(r->d()->image->height());
}
/*!
@@ -3083,10 +3100,10 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(QV4::CallContext
{
QV4::Scope scope(ctx);
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, ctx->thisObject().as<QQuickJSContext2DPixelData>());
- if (!r || r->d()->image.isNull())
+ if (!r || r->d()->image->isNull())
return QV4::Encode::undefined();
- return QV4::Encode(r->d()->image.width() * r->d()->image.height() * 4);
+ return QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4);
}
QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
@@ -3096,13 +3113,13 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
QV4::Scope scope(v4);
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m));
- if (index < static_cast<quint32>(r->d()->image.width() * r->d()->image.height() * 4)) {
+ if (index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4)) {
if (hasProperty)
*hasProperty = true;
- const quint32 w = r->d()->image.width();
+ const quint32 w = r->d()->image->width();
const quint32 row = (index / 4) / w;
const quint32 col = (index / 4) % w;
- const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image.constScanLine(row));
+ const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image->constScanLine(row));
pixel += col;
switch (index % 4) {
case 0:
@@ -3131,12 +3148,12 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
const int v = value.toInt32();
- if (r && index < static_cast<quint32>(r->d()->image.width() * r->d()->image.height() * 4) && v >= 0 && v <= 255) {
- const quint32 w = r->d()->image.width();
+ if (r && index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4) && v >= 0 && v <= 255) {
+ const quint32 w = r->d()->image->width();
const quint32 row = (index / 4) / w;
const quint32 col = (index / 4) % w;
- QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image.scanLine(row));
+ QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image->scanLine(row));
pixel += col;
switch (index % 4) {
case 0:
@@ -3187,8 +3204,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(QV4::CallC
if (!!imgData) {
QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (pa) {
- qreal w = pa->d()->image.width();
- qreal h = pa->d()->image.height();
+ qreal w = pa->d()->image->width();
+ qreal h = pa->d()->image->height();
return qt_create_image_data(w, h, scope.engine, QImage());
}
} else if (arg0->isString()) {
@@ -3266,8 +3283,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(QV4::CallCont
QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (pixelArray) {
- w = pixelArray->d()->image.width();
- h = pixelArray->d()->image.height();
+ w = pixelArray->d()->image->width();
+ h = pixelArray->d()->image->height();
if (ctx->argc() == 7) {
dirtyX = ctx->args()[3].toNumber();
@@ -3316,7 +3333,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(QV4::CallCont
dirtyHeight = h;
}
- QImage image = pixelArray->d()->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ QImage image = pixelArray->d()->image->copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
r->d()->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
}
return ctx->thisObject().asReturnedValue();
@@ -3351,9 +3368,9 @@ QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(QV4::CallCo
if (ctx->argc() == 2) {
- if (!style->d()->brush.gradient())
+ if (!style->d()->brush->gradient())
V4THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
- QGradient gradient = *(style->d()->brush.gradient());
+ QGradient gradient = *(style->d()->brush->gradient());
qreal pos = ctx->args()[0].toNumber();
QColor color;
@@ -3371,7 +3388,7 @@ QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(QV4::CallCo
} else {
V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
}
- style->d()->brush = gradient;
+ *style->d()->brush = gradient;
}
return ctx->thisObject().asReturnedValue();
@@ -3980,10 +3997,12 @@ public:
~QQuickContext2DThreadCleanup()
{
+#ifndef QT_NO_OPENGL
context->makeCurrent(surface);
delete texture;
context->doneCurrent();
delete context;
+#endif
surface->deleteLater();
}
@@ -4019,6 +4038,7 @@ QQuickContext2D::~QQuickContext2D()
delete m_buffer;
if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+#ifndef QT_NO_OPENGL
if (m_renderStrategy == QQuickCanvasItem::Immediate && m_glContext) {
Q_ASSERT(QThread::currentThread() == m_glContext->thread());
m_glContext->makeCurrent(m_surface.data());
@@ -4039,6 +4059,7 @@ QQuickContext2D::~QQuickContext2D()
m_texture->deleteLater();
}
}
+#endif
} else {
// Image based does not have GL resources, but must still be deleted
// on its designated thread after it has completed whatever it might
@@ -4064,8 +4085,6 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_canvas = canvasItem;
m_renderTarget = canvasItem->renderTarget();
-
- QQuickWindow *window = canvasItem->window();
m_renderStrategy = canvasItem->renderStrategy();
#ifdef Q_OS_WIN
@@ -4084,12 +4103,24 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_renderTarget = QQuickCanvasItem::Image;
}
+ // Disable Framebuffer Object based rendering when not running with OpenGL
+ if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+ QSGRendererInterface *rif = canvasItem->window()->rendererInterface();
+ if (rif && rif->graphicsApi() != QSGRendererInterface::OpenGL)
+ m_renderTarget = QQuickCanvasItem::Image;
+ }
+
switch (m_renderTarget) {
case QQuickCanvasItem::Image:
m_texture = new QQuickContext2DImageTexture;
break;
case QQuickCanvasItem::FramebufferObject:
+#ifndef QT_NO_OPENGL
m_texture = new QQuickContext2DFBOTexture;
+#else
+ // It shouldn't be possible to use a FramebufferObject without OpenGL
+ m_texture = nullptr;
+#endif
break;
}
@@ -4103,18 +4134,27 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_thread = QThread::currentThread();
QThread *renderThread = m_thread;
- QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
+#ifndef QT_NO_OPENGL
+ QQuickWindow *window = canvasItem->window();
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ QThread *sceneGraphThread = wd->context->thread();
if (m_renderStrategy == QQuickCanvasItem::Threaded)
renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
renderThread = sceneGraphThread;
+#else
+ if (m_renderStrategy == QQuickCanvasItem::Threaded)
+ renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
+#endif
+
if (renderThread && renderThread != QThread::currentThread())
m_texture->moveToThread(renderThread);
-
+#ifndef QT_NO_OPENGL
if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
- QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->openglContext();
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(QQuickWindowPrivate::get(window)->context);
+ QOpenGLContext *cc = openglRenderContext->openglContext();
m_surface.reset(new QOffscreenSurface);
m_surface->setFormat(window->format());
m_surface->create();
@@ -4125,7 +4165,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_glContext->moveToThread(renderThread);
m_texture->initializeOpenGL(m_glContext, m_surface.data());
}
-
+#endif
connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
reset();
@@ -4172,6 +4212,7 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
flush();
m_texture->grabImage(bounds);
} else {
+#ifndef QT_NO_OPENGL
QQuickWindow *window = m_canvas->window();
QOpenGLContext *ctx = window ? window->openglContext() : 0;
if (ctx && ctx->isValid()) {
@@ -4187,6 +4228,10 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
qWarning() << "Cannot read pixels from canvas before opengl context is valid";
return QImage();
}
+#else
+ flush();
+ m_texture->grabImage(bounds);
+#endif
}
} else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index cfb62ee052..b526c8320d 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -126,8 +126,8 @@ public:
struct State {
State()
- : strokeStyle(QColor("#000000"))
- , fillStyle(QColor("#000000"))
+ : strokeStyle(QColor(Qt::black))
+ , fillStyle(QColor(Qt::black))
, fillPatternRepeatX(false)
, fillPatternRepeatY(false)
, strokePatternRepeatX(false)
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 58b374e696..8d659040b3 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -42,9 +42,11 @@
#include <qqml.h>
#include <QtCore/QMutex>
#include <QtQuick/qsgtexture.h>
-#include <QtGui/QOpenGLContext>
#include <QtGui/QPaintEngine>
-#include <QtGui/private/qopenglpaintengine_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtGui/private/qopenglpaintengine_p.h>
+#endif
#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
index a82b88f36f..f281cec2d5 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -70,7 +70,7 @@ public:
inline int size() {return commands.size();}
inline bool isEmpty() const {return commands.isEmpty(); }
inline bool hasNext() const {return cmdIdx < commands.size(); }
- inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; }
+ inline QQuickContext2D::PaintCommand takeNextCommand() { return commands.at(cmdIdx++); }
inline qreal takeGlobalAlpha() { return takeReal(); }
inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast<QPainter::CompositionMode>(takeInt()); }
@@ -227,20 +227,20 @@ public:
colors << color;
}
- inline QTransform takeMatrix() { return matrixes[matrixIdx++]; }
+ inline QTransform takeMatrix() { return matrixes.at(matrixIdx++); }
- inline QRectF takeRect() { return rects[rectIdx++]; }
+ inline QRectF takeRect() { return rects.at(rectIdx++); }
- inline QPainterPath takePath() { return pathes[pathIdx++]; }
+ inline QPainterPath takePath() { return pathes.at(pathIdx++); }
- inline const QImage& takeImage() { return images[imageIdx++]; }
- inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps[pixmapIdx++]; }
+ inline const QImage& takeImage() { return images.at(imageIdx++); }
+ inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps.at(pixmapIdx++); }
- inline int takeInt() { return ints[intIdx++]; }
- inline bool takeBool() {return bools[boolIdx++]; }
- inline qreal takeReal() { return reals[realIdx++]; }
- inline QColor takeColor() { return colors[colorIdx++]; }
- inline QBrush takeBrush() { return brushes[brushIdx++]; }
+ inline int takeInt() { return ints.at(intIdx++); }
+ inline bool takeBool() {return bools.at(boolIdx++); }
+ inline qreal takeReal() { return reals.at(realIdx++); }
+ inline QColor takeColor() { return colors.at(colorIdx++); }
+ inline QBrush takeBrush() { return brushes.at(brushIdx++); }
void replay(QPainter* painter, QQuickContext2D::State& state, const QVector2D &scaleFactor);
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index 375f537b9b..4435c0c37b 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -44,14 +44,16 @@
#include <QtQuick/private/qsgtexture_p.h>
#include "qquickcontext2dcommandbuffer_p.h"
#include <QOpenGLPaintDevice>
-
+#ifndef QT_NO_OPENGL
#include <QOpenGLFramebufferObject>
#include <QOpenGLFramebufferObjectFormat>
+#include <QOpenGLFunctions>
+#endif
#include <QtCore/QThread>
#include <QtGui/QGuiApplication>
QT_BEGIN_NAMESPACE
-
+#ifndef QT_NO_OPENGL
#define QT_MINIMUM_FBO_SIZE 64
static inline int qt_next_power_of_two(int v)
@@ -85,10 +87,12 @@ struct GLAcquireContext {
}
QOpenGLContext *ctx;
};
-
+#endif
QQuickContext2DTexture::QQuickContext2DTexture()
: m_context(0)
+#ifndef QT_NO_OPENGL
, m_gl(0)
+#endif
, m_surface(0)
, m_item(0)
, m_canvasWindowChanged(false)
@@ -250,9 +254,9 @@ void QQuickContext2DTexture::paint(QQuickContext2DCommandBuffer *ccb)
return;
}
QQuickContext2D::mutex.unlock();
-
+#ifndef QT_NO_OPENGL
GLAcquireContext currentContext(m_gl, m_surface);
-
+#endif
if (!m_tiledCanvas) {
paintWithoutTiles(ccb);
delete ccb;
@@ -379,7 +383,7 @@ bool QQuickContext2DTexture::event(QEvent *e)
}
return QObject::event(e);
}
-
+#ifndef QT_NO_OPENGL
static inline QSize npotAdjustedSize(const QSize &size)
{
static bool checked = false;
@@ -650,6 +654,7 @@ void QQuickContext2DFBOTexture::endPainting()
m_fbo->bindDefault();
}
+#endif
QQuickContext2DImageTexture::QQuickContext2DImageTexture()
: QQuickContext2DTexture()
diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h
index edc7283283..ed38382892 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h
@@ -54,10 +54,10 @@
#include <QtQuick/qsgtexture.h>
#include "qquickcanvasitem_p.h"
#include "qquickcontext2d_p.h"
-
-#include <QOpenGLContext>
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLContext>
+# include <QOpenGLFramebufferObject>
+#endif
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QThread>
@@ -121,11 +121,12 @@ public:
// Called during sync() on the scene graph thread while GUI is blocked.
virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame, QQuickWindow *window) = 0;
bool event(QEvent *e);
-
+#ifndef QT_NO_OPENGL
void initializeOpenGL(QOpenGLContext *gl, QOffscreenSurface *s) {
m_gl = gl;
m_surface = s;
}
+#endif
Q_SIGNALS:
void textureChanged();
@@ -152,8 +153,9 @@ protected:
QList<QQuickContext2DTile*> m_tiles;
QQuickContext2D *m_context;
-
+#ifndef QT_NO_OPENGL
QOpenGLContext *m_gl;
+#endif
QSurface *m_surface;
QQuickContext2D::State m_state;
@@ -174,7 +176,7 @@ protected:
uint m_painting : 1;
uint m_onCustomThread : 1; // Not GUI and not SGRender
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTexture : public QQuickContext2DTexture
{
Q_OBJECT
@@ -209,7 +211,7 @@ private:
GLuint m_displayTextures[2];
int m_displayTexture;
};
-
+#endif
class QSGPlainTexture;
class QQuickContext2DImageTexture : public QQuickContext2DTexture
{
diff --git a/src/quick/items/context2d/qquickcontext2dtile.cpp b/src/quick/items/context2d/qquickcontext2dtile.cpp
index a1503762de..95b6c9d961 100644
--- a/src/quick/items/context2d/qquickcontext2dtile.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtile.cpp
@@ -38,10 +38,11 @@
****************************************************************************/
#include "qquickcontext2dtile_p.h"
-
-#include <QOpenGLFramebufferObject>
-#include <QOpenGLFramebufferObjectFormat>
-#include <QOpenGLPaintDevice>
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+# include <QOpenGLFramebufferObjectFormat>
+# include <QOpenGLPaintDevice>
+#endif
QT_BEGIN_NAMESPACE
@@ -96,7 +97,7 @@ QPainter* QQuickContext2DTile::createPainter(bool smooth, bool antialiasing)
return 0;
}
-
+#ifndef QT_NO_OPENGL
QQuickContext2DFBOTile::QQuickContext2DFBOTile()
: QQuickContext2DTile()
, m_fbo(0)
@@ -146,7 +147,7 @@ void QQuickContext2DFBOTile::setRect(const QRect& r)
m_fbo = new QOpenGLFramebufferObject(r.size(), format);
}
}
-
+#endif
QQuickContext2DImageTile::QQuickContext2DImageTile()
: QQuickContext2DTile()
diff --git a/src/quick/items/context2d/qquickcontext2dtile_p.h b/src/quick/items/context2d/qquickcontext2dtile_p.h
index d9be2023c4..a87202daae 100644
--- a/src/quick/items/context2d/qquickcontext2dtile_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtile_p.h
@@ -52,8 +52,9 @@
//
#include "qquickcontext2d_p.h"
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+#endif
QT_BEGIN_NAMESPACE
class QQuickContext2DTexture;
@@ -82,7 +83,7 @@ protected:
QPainter m_painter;
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTile : public QQuickContext2DTile
{
public:
@@ -99,7 +100,7 @@ private:
QOpenGLFramebufferObject *m_fbo;
};
-
+#endif
class QQuickContext2DImageTile : public QQuickContext2DTile
{
public:
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index ab966b6ccc..d91705451e 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -63,10 +63,6 @@ HEADERS += \
$$PWD/qquickstateoperations_p.h \
$$PWD/qquickimplicitsizeitem_p.h \
$$PWD/qquickimplicitsizeitem_p_p.h \
- $$PWD/qquickspriteengine_p.h \
- $$PWD/qquicksprite_p.h \
- $$PWD/qquickspritesequence_p.h \
- $$PWD/qquickanimatedsprite_p.h \
$$PWD/qquickdrag_p.h \
$$PWD/qquickdroparea_p.h \
$$PWD/qquickmultipointtoucharea_p.h \
@@ -76,11 +72,20 @@ HEADERS += \
$$PWD/qquickscreen_p.h \
$$PWD/qquickwindowattached_p.h \
$$PWD/qquickwindowmodule_p.h \
- $$PWD/qquickframebufferobject.h \
- $$PWD/qquickitemgrabresult.h \
+ $$PWD/qquickshadereffectsource_p.h \
+ $$PWD/qquickshadereffectmesh_p.h \
+ $$PWD/qquickshadereffect_p.h \
+ $$PWD/qquickgenericshadereffect_p.h \
$$PWD/qquickrendercontrol.h \
$$PWD/qquickrendercontrol_p.h \
- $$PWD/qquickopenglinfo_p.h
+ $$PWD/qquickgraphicsinfo_p.h \
+ $$PWD/qquickitemgrabresult.h \
+ $$PWD/qquickspriteengine_p.h \
+ $$PWD/qquicksprite_p.h \
+ $$PWD/qquickspritesequence_p.h \
+ $$PWD/qquickanimatedsprite_p.h \
+ $$PWD/qquickanimatedsprite_p_p.h \
+ $$PWD/qquickspritesequence_p_p.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -120,10 +125,6 @@ SOURCES += \
$$PWD/qquickitemanimation.cpp \
$$PWD/qquickstateoperations.cpp \
$$PWD/qquickimplicitsizeitem.cpp \
- $$PWD/qquickspriteengine.cpp \
- $$PWD/qquicksprite.cpp \
- $$PWD/qquickspritesequence.cpp \
- $$PWD/qquickanimatedsprite.cpp \
$$PWD/qquickaccessibleattached.cpp \
$$PWD/qquickdrag.cpp \
$$PWD/qquickdroparea.cpp \
@@ -133,36 +134,42 @@ SOURCES += \
$$PWD/qquickwindowmodule.cpp \
$$PWD/qquickscreen.cpp \
$$PWD/qquickwindowattached.cpp \
- $$PWD/qquickframebufferobject.cpp \
- $$PWD/qquickitemgrabresult.cpp \
+ $$PWD/qquickshadereffectsource.cpp \
+ $$PWD/qquickshadereffectmesh.cpp \
+ $$PWD/qquickshadereffect.cpp \
+ $$PWD/qquickgenericshadereffect.cpp \
$$PWD/qquickrendercontrol.cpp \
- $$PWD/qquickopenglinfo.cpp
+ $$PWD/qquickgraphicsinfo.cpp \
+ $$PWD/qquickitemgrabresult.cpp \
+ $$PWD/qquickspriteengine.cpp \
+ $$PWD/qquicksprite.cpp \
+ $$PWD/qquickspritesequence.cpp \
+ $$PWD/qquickanimatedsprite.cpp
-SOURCES += \
- $$PWD/qquickshadereffect.cpp \
- $$PWD/qquickshadereffectmesh.cpp \
- $$PWD/qquickshadereffectnode.cpp \
- $$PWD/qquickshadereffectsource.cpp \
+# Items that depend on OpenGL Renderer
+qtConfig(opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qquickopenglinfo.cpp \
+ $$PWD/qquickopenglshadereffect.cpp \
+ $$PWD/qquickopenglshadereffectnode.cpp \
+ $$PWD/qquickframebufferobject.cpp
-HEADERS += \
- $$PWD/qquickshadereffect_p.h \
- $$PWD/qquickshadereffectmesh_p.h \
- $$PWD/qquickshadereffectnode_p.h \
- $$PWD/qquickshadereffectsource_p.h \
+ HEADERS += \
+ $$PWD/qquickopenglinfo_p.h \
+ $$PWD/qquickopenglshadereffect_p.h \
+ $$PWD/qquickopenglshadereffectnode_p.h \
+ $$PWD/qquickframebufferobject.h
-OTHER_FILES += \
- $$PWD/shaders/sprite.vert \
- $$PWD/shaders/sprite.frag \
- $$PWD/shaders/shadereffect.vert \
- $$PWD/shaders/shadereffect.frag \
- $$PWD/shaders/shadereffectfallback.vert \
- $$PWD/shaders/shadereffectfallback.frag \
- $$PWD/shaders/sprite_core.vert \
- $$PWD/shaders/sprite_core.frag \
- $$PWD/shaders/shadereffect_core.vert \
- $$PWD/shaders/shadereffect_core.frag \
- $$PWD/shaders/shadereffectfallback_core.vert \
- $$PWD/shaders/shadereffectfallback_core.frag
+ OTHER_FILES += \
+ $$PWD/shaders/shadereffect.vert \
+ $$PWD/shaders/shadereffect.frag \
+ $$PWD/shaders/shadereffectfallback.vert \
+ $$PWD/shaders/shadereffectfallback.frag \
+ $$PWD/shaders/shadereffect_core.vert \
+ $$PWD/shaders/shadereffect_core.frag \
+ $$PWD/shaders/shadereffectfallback_core.vert \
+ $$PWD/shaders/shadereffectfallback_core.frag
+}
RESOURCES += \
$$PWD/items.qrc
diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc
index 99f9b5224f..6aaf757c29 100644
--- a/src/quick/items/items.qrc
+++ b/src/quick/items/items.qrc
@@ -1,7 +1,5 @@
<RCC>
<qresource prefix="/qt-project.org/items">
- <file>shaders/sprite.frag</file>
- <file>shaders/sprite.vert</file>
<file>shaders/shadereffect.vert</file>
<file>shaders/shadereffect.frag</file>
<file>shaders/shadereffectfallback.frag</file>
@@ -10,7 +8,5 @@
<file>shaders/shadereffect_core.vert</file>
<file>shaders/shadereffectfallback_core.frag</file>
<file>shaders/shadereffectfallback_core.vert</file>
- <file>shaders/sprite_core.frag</file>
- <file>shaders/sprite_core.vert</file>
</qresource>
</RCC>
diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp
index 4cbd41106e..b6978e534e 100644
--- a/src/quick/items/qquickanchors.cpp
+++ b/src/quick/items/qquickanchors.cpp
@@ -285,26 +285,26 @@ void QQuickAnchorsPrivate::clearItem(QQuickItem *item)
}
}
-int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
+QQuickGeometryChange QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
{
- QQuickItemPrivate::GeometryChangeTypes dependency = QQuickItemPrivate::NoChange;
+ QQuickGeometryChange dependency;
if (!controlItem || inDestructor)
return dependency;
if (fill == controlItem) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::SizeChange;
+ dependency.setSizeChange(true);
else //sibling
- dependency |= QQuickItemPrivate::GeometryChange;
+ dependency.setAllChanged(true);
return dependency; //exit early
}
if (centerIn == controlItem) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::SizeChange;
+ dependency.setSizeChange(true);
else //sibling
- dependency |= QQuickItemPrivate::GeometryChange;
+ dependency.setAllChanged(true);
return dependency; //exit early
}
@@ -312,9 +312,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
(usedAnchors & QQuickAnchors::RightAnchor && rightAnchorItem == controlItem) ||
(usedAnchors & QQuickAnchors::HCenterAnchor && hCenterAnchorItem == controlItem)) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::WidthChange;
+ dependency.setWidthChange(true);
else //sibling
- dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::XChange | QQuickItemPrivate::WidthChange);
+ dependency.setHorizontalChange(true);
}
if ((usedAnchors & QQuickAnchors::TopAnchor && topAnchorItem == controlItem) ||
@@ -322,9 +322,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
(usedAnchors & QQuickAnchors::VCenterAnchor && vCenterAnchorItem == controlItem) ||
(usedAnchors & QQuickAnchors::BaselineAnchor && baselineAnchorItem == controlItem)) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::HeightChange;
+ dependency.setHeightChange(true);
else //sibling
- dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::YChange | QQuickItemPrivate::HeightChange);
+ dependency.setVerticalChange(true);
}
return dependency;
@@ -336,7 +336,7 @@ void QQuickAnchorsPrivate::addDepend(QQuickItem *item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->updateOrAddGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
+ p->updateOrAddGeometryChangeListener(this, calculateDependency(item));
}
void QQuickAnchorsPrivate::remDepend(QQuickItem *item)
@@ -345,7 +345,7 @@ void QQuickAnchorsPrivate::remDepend(QQuickItem *item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->updateOrRemoveGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
+ p->updateOrRemoveGeometryChangeListener(this, calculateDependency(item));
}
bool QQuickAnchors::mirrored()
@@ -492,7 +492,7 @@ void QQuickAnchorsPrivate::update()
}
}
-void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, const QRectF &oldG)
+void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &)
{
if (!isItemComplete())
return;
@@ -502,11 +502,9 @@ void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG,
} else if (centerIn) {
centerInChanged();
} else {
- if ((usedAnchors & QQuickAnchors::Horizontal_Mask)
- && (newG.x() != oldG.x() || newG.width() != oldG.width()))
+ if ((usedAnchors & QQuickAnchors::Horizontal_Mask) && change.horizontalChange())
updateHorizontalAnchors();
- if ((usedAnchors & QQuickAnchors::Vertical_Mask)
- && (newG.y() != oldG.y() || newG.height() != oldG.height()))
+ if ((usedAnchors & QQuickAnchors::Vertical_Mask) && change.verticalChange())
updateVerticalAnchors();
}
}
diff --git a/src/quick/items/qquickanchors_p_p.h b/src/quick/items/qquickanchors_p_p.h
index da659946c0..3357e134bf 100644
--- a/src/quick/items/qquickanchors_p_p.h
+++ b/src/quick/items/qquickanchors_p_p.h
@@ -124,7 +124,7 @@ public:
void clearItem(QQuickItem *);
- int calculateDependency(QQuickItem *);
+ QQuickGeometryChange calculateDependency(QQuickItem *);
void addDepend(QQuickItem *);
void remDepend(QQuickItem *);
bool isItemComplete() const;
@@ -141,7 +141,7 @@ public:
void updateMe();
// QQuickItemGeometryListener interface
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
QQuickAnchorsPrivate *anchorPrivate() Q_DECL_OVERRIDE { return this; }
bool checkHValid() const;
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index ab5170cd65..6f14bf15fe 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -47,8 +47,10 @@
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
#include <QtGui/qmovie.h>
+#ifndef QT_NO_NETWORK
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -144,8 +146,10 @@ QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
QQuickAnimatedImage::~QQuickAnimatedImage()
{
Q_D(QQuickAnimatedImage);
+#ifndef QT_NO_NETWORK
if (d->reply)
d->reply->deleteLater();
+#endif
delete d->_movie;
qDeleteAll(d->frameMap);
d->frameMap.clear();
@@ -264,10 +268,12 @@ void QQuickAnimatedImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#ifndef QT_NO_NETWORK
if (d->reply) {
d->reply->deleteLater();
d->reply = 0;
}
+#endif
d->setImage(QImage());
qDeleteAll(d->frameMap);
@@ -319,6 +325,7 @@ void QQuickAnimatedImage::load()
d->_movie = new QMovie(lf);
movieRequestFinished();
} else {
+#ifndef QT_NO_NETWORK
if (d->status != Loading) {
d->status = Loading;
emit statusChanged(d->status);
@@ -335,6 +342,7 @@ void QQuickAnimatedImage::load()
this, SLOT(movieRequestFinished()));
QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(requestProgress(qint64,qint64)));
+#endif
}
}
}
@@ -343,8 +351,10 @@ void QQuickAnimatedImage::load()
void QQuickAnimatedImage::movieRequestFinished()
{
+
Q_D(QQuickAnimatedImage);
+#ifndef QT_NO_NETWORK
if (d->reply) {
d->redirectCount++;
if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
@@ -360,6 +370,7 @@ void QQuickAnimatedImage::movieRequestFinished()
d->redirectCount=0;
d->_movie = new QMovie(d->reply);
}
+#endif
if (!d->_movie->isValid()) {
qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h
index 6d32f1071f..ed735d1c9c 100644
--- a/src/quick/items/qquickanimatedimage_p_p.h
+++ b/src/quick/items/qquickanimatedimage_p_p.h
@@ -58,7 +58,9 @@
QT_BEGIN_NAMESPACE
class QMovie;
+#ifndef QT_NO_NETWORK
class QNetworkReply;
+#endif
class QQuickAnimatedImagePrivate : public QQuickImagePrivate
{
@@ -66,7 +68,11 @@ class QQuickAnimatedImagePrivate : public QQuickImagePrivate
public:
QQuickAnimatedImagePrivate()
- : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0), oldPlaying(false), currentSourceSize(0, 0)
+ : playing(true), paused(false), preset_currentframe(0), _movie(0), oldPlaying(false)
+#ifndef QT_NO_NETWORK
+ , reply(0), redirectCount(0)
+#endif
+ , currentSourceSize(0, 0)
{
}
@@ -76,9 +82,11 @@ public:
bool paused;
int preset_currentframe;
QMovie *_movie;
+ bool oldPlaying;
+#ifndef QT_NO_NETWORK
QNetworkReply *reply;
int redirectCount;
- bool oldPlaying;
+#endif
QMap<int, QQuickPixmap *> frameMap;
QSize currentSourceSize;
};
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 6207a86877..8aeef4ef4a 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickanimatedsprite_p.h"
+#include "qquickanimatedsprite_p_p.h"
#include "qquicksprite_p.h"
#include "qquickspriteengine_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -56,111 +57,6 @@
QT_BEGIN_NAMESPACE
-class QQuickAnimatedSpriteMaterial : public QSGMaterial
-{
-public:
- QQuickAnimatedSpriteMaterial();
- ~QQuickAnimatedSpriteMaterial();
- QSGMaterialType *type() const Q_DECL_OVERRIDE { static QSGMaterialType type; return &type; }
- QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
- int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE
- {
- return this - static_cast<const QQuickAnimatedSpriteMaterial *>(other);
- }
-
- QSGTexture *texture;
-
- float animT;
- float animX1;
- float animY1;
- float animX2;
- float animY2;
- float animW;
- float animH;
-};
-
-QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial()
- : texture(0)
- , animT(0.0f)
- , animX1(0.0f)
- , animY1(0.0f)
- , animX2(0.0f)
- , animY2(0.0f)
- , animW(1.0f)
- , animH(1.0f)
-{
- setFlag(Blending, true);
-}
-
-QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial()
-{
- delete texture;
-}
-
-class AnimatedSpriteMaterialData : public QSGMaterialShader
-{
-public:
- AnimatedSpriteMaterialData()
- : QSGMaterialShader()
- {
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag"));
- }
-
- void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE
- {
- QQuickAnimatedSpriteMaterial *m = static_cast<QQuickAnimatedSpriteMaterial *>(newEffect);
- m->texture->bind();
-
- program()->setUniformValue(m_opacity_id, state.opacity());
- program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
- program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
-
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
- }
-
- void initialize() Q_DECL_OVERRIDE {
- m_matrix_id = program()->uniformLocation("qt_Matrix");
- m_opacity_id = program()->uniformLocation("qt_Opacity");
- m_animData_id = program()->uniformLocation("animData");
- m_animPos_id = program()->uniformLocation("animPos");
- }
-
- char const *const *attributeNames() const Q_DECL_OVERRIDE {
- static const char *attr[] = {
- "vPos",
- "vTex",
- 0
- };
- return attr;
- }
-
- int m_matrix_id;
- int m_opacity_id;
- int m_animData_id;
- int m_animPos_id;
-};
-
-QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const
-{
- return new AnimatedSpriteMaterialData;
-}
-
-struct AnimatedSpriteVertex {
- float x;
- float y;
- float tx;
- float ty;
-};
-
-struct AnimatedSpriteVertices {
- AnimatedSpriteVertex v1;
- AnimatedSpriteVertex v2;
- AnimatedSpriteVertex v3;
- AnimatedSpriteVertex v4;
-};
-
/*!
\qmltype AnimatedSprite
\instantiates QQuickAnimatedSprite
@@ -316,18 +212,11 @@ struct AnimatedSpriteVertices {
//TODO: Implicitly size element to size of sprite
QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
- QQuickItem(parent)
- , m_sprite(new QQuickSprite(this))
- , m_spriteEngine(0)
- , m_curFrame(0)
- , m_pleaseReset(false)
- , m_running(true)
- , m_paused(false)
- , m_interpolate(true)
- , m_loops(-1)
- , m_curLoop(0)
- , m_pauseOffset(0)
+ QQuickItem(*(new QQuickAnimatedSpritePrivate), parent)
{
+ Q_D(QQuickAnimatedSprite);
+ d->m_sprite = new QQuickSprite(this);
+
setFlag(ItemHasContents);
connect(this, SIGNAL(widthChanged()),
this, SLOT(reset()));
@@ -335,6 +224,96 @@ QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
this, SLOT(reset()));
}
+bool QQuickAnimatedSprite::running() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_running;
+}
+
+bool QQuickAnimatedSprite::interpolate() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_interpolate;
+}
+
+QUrl QQuickAnimatedSprite::source() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->source();
+}
+
+bool QQuickAnimatedSprite::reverse() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->reverse();
+}
+
+bool QQuickAnimatedSprite::frameSync() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameSync();
+}
+
+int QQuickAnimatedSprite::frameCount() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frames();
+}
+
+int QQuickAnimatedSprite::frameHeight() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameHeight();
+}
+
+int QQuickAnimatedSprite::frameWidth() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameWidth();
+}
+
+int QQuickAnimatedSprite::frameX() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameX();
+}
+
+int QQuickAnimatedSprite::frameY() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameY();
+}
+
+qreal QQuickAnimatedSprite::frameRate() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameRate();
+}
+
+int QQuickAnimatedSprite::frameDuration() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameDuration();
+}
+
+int QQuickAnimatedSprite::loops() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_loops;
+}
+
+bool QQuickAnimatedSprite::paused() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_paused;
+}
+
+int QQuickAnimatedSprite::currentFrame() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_curFrame;
+}
+
bool QQuickAnimatedSprite::isCurrentFrameChangedConnected()
{
IS_SIGNAL_CONNECTED(this, QQuickAnimatedSprite, currentFrameChanged, (int));
@@ -349,23 +328,25 @@ void QQuickAnimatedSprite::reloadImage()
void QQuickAnimatedSprite::componentComplete()
{
+ Q_D(const QQuickAnimatedSprite);
createEngine();
QQuickItem::componentComplete();
- if (m_running)
+ if (d->m_running)
start();
}
void QQuickAnimatedSprite::start()
{
- m_running = true;
+ Q_D(QQuickAnimatedSprite);
+ d->m_running = true;
if (!isComponentComplete())
return;
- m_curLoop = 0;
- m_timestamp.start();
- if (m_spriteEngine) {
- m_spriteEngine->stop(0);
- m_spriteEngine->updateSprites(0);
- m_spriteEngine->start(0);
+ d->m_curLoop = 0;
+ d->m_timestamp.start();
+ if (d->m_spriteEngine) {
+ d->m_spriteEngine->stop(0);
+ d->m_spriteEngine->updateSprites(0);
+ d->m_spriteEngine->start(0);
}
emit currentFrameChanged(0);
emit runningChanged(true);
@@ -374,10 +355,11 @@ void QQuickAnimatedSprite::start()
void QQuickAnimatedSprite::stop()
{
- m_running = false;
+ Q_D(QQuickAnimatedSprite);
+ d->m_running = false;
if (!isComponentComplete())
return;
- m_pauseOffset = 0;
+ d->m_pauseOffset = 0;
emit runningChanged(false);
maybeUpdate();
}
@@ -389,14 +371,15 @@ void QQuickAnimatedSprite::stop()
*/
void QQuickAnimatedSprite::advance(int frames)
{
+ Q_D(QQuickAnimatedSprite);
if (!frames)
return;
//TODO-C: May not work when running - only when paused
- m_curFrame += frames;
- while (m_curFrame < 0)
- m_curFrame += m_spriteEngine->maxFrames();
- m_curFrame = m_curFrame % m_spriteEngine->maxFrames();
- emit currentFrameChanged(m_curFrame);
+ d->m_curFrame += frames;
+ while (d->m_curFrame < 0)
+ d->m_curFrame += d->m_spriteEngine->maxFrames();
+ d->m_curFrame = d->m_curFrame % d->m_spriteEngine->maxFrames();
+ emit currentFrameChanged(d->m_curFrame);
maybeUpdate();
}
@@ -418,10 +401,12 @@ void QQuickAnimatedSprite::maybeUpdate()
*/
void QQuickAnimatedSprite::pause()
{
- if (m_paused)
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_paused)
return;
- m_pauseOffset = m_timestamp.elapsed();
- m_paused = true;
+ d->m_pauseOffset = d->m_timestamp.elapsed();
+ d->m_paused = true;
emit pausedChanged(true);
maybeUpdate();
}
@@ -436,245 +421,362 @@ void QQuickAnimatedSprite::pause()
*/
void QQuickAnimatedSprite::resume()
{
- if (!m_paused)
+ Q_D(QQuickAnimatedSprite);
+
+ if (!d->m_paused)
return;
- m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
- m_paused = false;
+ d->m_pauseOffset = d->m_pauseOffset - d->m_timestamp.elapsed();
+ d->m_paused = false;
emit pausedChanged(false);
maybeUpdate();
}
-void QQuickAnimatedSprite::createEngine()
+void QQuickAnimatedSprite::setRunning(bool arg)
{
- if (m_spriteEngine)
- delete m_spriteEngine;
- QList<QQuickSprite*> spriteList;
- spriteList << m_sprite;
- m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
- m_spriteEngine->startAssemblingImage();
- reset();
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_running != arg) {
+ if (d->m_running)
+ stop();
+ else
+ start();
+ }
}
-static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
-};
+void QQuickAnimatedSprite::setPaused(bool arg)
+{
+ Q_D(const QQuickAnimatedSprite);
-static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet =
+ if (d->m_paused != arg) {
+ if (d->m_paused)
+ resume();
+ else
+ pause();
+ }
+}
+
+void QQuickAnimatedSprite::setInterpolate(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_interpolate != arg) {
+ d->m_interpolate = arg;
+ Q_EMIT interpolateChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setSource(QUrl arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_source != arg) {
+ d->m_sprite->setSource(arg);
+ Q_EMIT sourceChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setReverse(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_reverse != arg) {
+ d->m_sprite->setReverse(arg);
+ Q_EMIT reverseChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setFrameSync(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameSync != arg) {
+ d->m_sprite->setFrameSync(arg);
+ Q_EMIT frameSyncChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameCount(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frames != arg) {
+ d->m_sprite->setFrameCount(arg);
+ Q_EMIT frameCountChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameHeight(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameHeight != arg) {
+ d->m_sprite->setFrameHeight(arg);
+ Q_EMIT frameHeightChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameWidth(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameWidth != arg) {
+ d->m_sprite->setFrameWidth(arg);
+ Q_EMIT frameWidthChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameX(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameX != arg) {
+ d->m_sprite->setFrameX(arg);
+ Q_EMIT frameXChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameY(int arg)
{
- 2, // Attribute Count
- (2+2) * sizeof(float),
- AnimatedSprite_Attributes
-};
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameY != arg) {
+ d->m_sprite->setFrameY(arg);
+ Q_EMIT frameYChanged(arg);
+ reloadImage();
+ }
+}
-void QQuickAnimatedSprite::sizeVertices(QSGGeometryNode *node)
+void QQuickAnimatedSprite::setFrameRate(qreal arg)
{
- AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) node->geometry()->vertexData();
- p->v1.x = 0;
- p->v1.y = 0;
+ Q_D(QQuickAnimatedSprite);
- p->v2.x = width();
- p->v2.y = 0;
+ if (d->m_sprite->m_frameRate != arg) {
+ d->m_sprite->setFrameRate(arg);
+ Q_EMIT frameRateChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
- p->v3.x = 0;
- p->v3.y = height();
+void QQuickAnimatedSprite::setFrameDuration(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
- p->v4.x = width();
- p->v4.y = height();
+ if (d->m_sprite->m_frameDuration != arg) {
+ d->m_sprite->setFrameDuration(arg);
+ Q_EMIT frameDurationChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
+
+void QQuickAnimatedSprite::resetFrameRate()
+{
+ setFrameRate(-1.0);
}
-QSGGeometryNode* QQuickAnimatedSprite::buildNode()
+void QQuickAnimatedSprite::resetFrameDuration()
{
- if (!m_spriteEngine) {
+ setFrameDuration(-1);
+}
+
+void QQuickAnimatedSprite::setLoops(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_loops != arg) {
+ d->m_loops = arg;
+ Q_EMIT loopsChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setCurrentFrame(int arg) //TODO-C: Probably only works when paused
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_curFrame != arg) {
+ d->m_curFrame = arg;
+ Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance!
+ update();
+ }
+}
+
+void QQuickAnimatedSprite::createEngine()
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_spriteEngine)
+ delete d->m_spriteEngine;
+ QList<QQuickSprite*> spriteList;
+ spriteList << d->m_sprite;
+ d->m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
+ d->m_spriteEngine->startAssemblingImage();
+ reset();
+}
+
+QSGSpriteNode* QQuickAnimatedSprite::initNode()
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (!d->m_spriteEngine) {
qmlInfo(this) << "No sprite engine...";
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
- m_spriteEngine->startAssemblingImage();
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
+ d->m_spriteEngine->startAssemblingImage();
maybeUpdate();//Schedule another update, where we will check again
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
maybeUpdate();//Schedule another update, where we will check again
- return 0;
+ return nullptr;
}
- QQuickAnimatedSpriteMaterial *material = new QQuickAnimatedSpriteMaterial();
-
- QImage image = m_spriteEngine->assembledImage(); //Engine prints errors if there are any
+ QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize()); //Engine prints errors if there are any
if (image.isNull())
- return 0;
- m_sheetSize = QSizeF(image.size());
- material->texture = window()->createTextureFromImage(image);
- m_spriteEngine->start(0);
- material->animT = 0;
- material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
- material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
- material->animX2 = material->animX1;
- material->animY2 = material->animY1;
- material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
-
- int vCount = 4;
- int iCount = 6;
- QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
-
- AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData();
-
- QRectF texRect = material->texture->normalizedTextureSubRect();
-
- p->v1.tx = texRect.topLeft().x();
- p->v1.ty = texRect.topLeft().y();
-
- p->v2.tx = texRect.topRight().x();
- p->v2.ty = texRect.topRight().y();
-
- p->v3.tx = texRect.bottomLeft().x();
- p->v3.ty = texRect.bottomLeft().y();
-
- p->v4.tx = texRect.bottomRight().x();
- p->v4.ty = texRect.bottomRight().y();
-
- quint16 *indices = g->indexDataAsUShort();
- indices[0] = 0;
- indices[1] = 1;
- indices[2] = 2;
- indices[3] = 1;
- indices[4] = 3;
- indices[5] = 2;
-
-
- QSGGeometryNode *node = new QSGGeometryNode();
- node->setGeometry(g);
- node->setMaterial(material);
- node->setFlag(QSGGeometryNode::OwnsMaterial);
- node->setFlag(QSGGeometryNode::OwnsGeometry);
- sizeVertices(node);
+ return nullptr;
+
+ QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
+
+ d->m_sheetSize = QSize(image.size());
+ node->setTexture(window()->createTextureFromImage(image));
+ d->m_spriteEngine->start(0);
+ node->setTime(0.0f);
+ node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
+ node->setSheetSize(d->m_sheetSize);
+ node->setSize(QSizeF(width(), height()));
return node;
}
void QQuickAnimatedSprite::reset()
{
- m_pleaseReset = true;
+ Q_D(QQuickAnimatedSprite);
+ d->m_pleaseReset = true;
maybeUpdate();
}
QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if (m_pleaseReset) {
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_pleaseReset) {
delete oldNode;
- oldNode = 0;
- m_pleaseReset = false;
+ oldNode = nullptr;
+ d->m_pleaseReset = false;
}
- QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
+ QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
if (!node)
- node = buildNode();
+ node = initNode();
if (node)
prepareNextFrame(node);
- if (m_running) {
- if (!m_paused)
- maybeUpdate();
-
- if (node) {
- node->markDirty(QSGNode::DirtyMaterial);
- }
- }
+ if (d->m_running && !d->m_paused)
+ maybeUpdate();
return node;
}
-void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
+void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
{
- int timeInt = m_timestamp.elapsed() + m_pauseOffset;
+ Q_D(QQuickAnimatedSprite);
+
+ int timeInt = d->m_timestamp.elapsed() + d->m_pauseOffset;
qreal time = timeInt / 1000.;
int frameAt;
qreal progress = 0.0;
- int lastFrame = m_curFrame;
- if (m_running && !m_paused) {
- const int nColumns = int(m_sheetSize.width()) / m_spriteEngine->spriteWidth();
+ int lastFrame = d->m_curFrame;
+ if (d->m_running && !d->m_paused) {
+ const int nColumns = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
//Advance State (keeps time for psuedostates)
- m_spriteEngine->updateSprites(timeInt);
+ d->m_spriteEngine->updateSprites(timeInt);
//Advance AnimatedSprite
- qreal animT = m_spriteEngine->spriteStart()/1000.0;
- const int frameCountInRow = m_spriteEngine->spriteFrames();
- const qreal frameDuration = m_spriteEngine->spriteDuration()/frameCountInRow;
+ qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
+ const int frameCountInRow = d->m_spriteEngine->spriteFrames();
+ const qreal frameDuration = d->m_spriteEngine->spriteDuration() / frameCountInRow;
if (frameDuration > 0) {
qreal frame = (time - animT)/(frameDuration / 1000.0);
- bool lastLoop = m_loops > 0 && m_curLoop == m_loops-1;
+ bool lastLoop = d->m_loops > 0 && d->m_curLoop == d->m_loops-1;
//don't visually interpolate for the last frame of the last loop
const int max = lastLoop ? frameCountInRow - 1 : frameCountInRow;
frame = qBound(qreal(0.0), frame, qreal(max));
double intpart;
progress = std::modf(frame,&intpart);
frameAt = (int)intpart;
- const int rowIndex = m_spriteEngine->spriteY()/frameHeight();
+ const int rowIndex = d->m_spriteEngine->spriteY()/frameHeight();
const int newFrame = rowIndex * nColumns + frameAt;
- if (m_curFrame > newFrame) //went around
- m_curLoop++;
- m_curFrame = newFrame;
+ if (d->m_curFrame > newFrame) //went around
+ d->m_curLoop++;
+ d->m_curFrame = newFrame;
} else {
- m_curFrame++;
- if (m_curFrame >= m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows
- m_curFrame = 0;
- m_curLoop++;
+ d->m_curFrame++;
+ if (d->m_curFrame >= d->m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows
+ d->m_curFrame = 0;
+ d->m_curLoop++;
}
- frameAt = m_curFrame % nColumns;
+ frameAt = d->m_curFrame % nColumns;
if (frameAt == 0)
- m_spriteEngine->advance();
+ d->m_spriteEngine->advance();
progress = 0;
}
- if (m_loops > 0 && m_curLoop >= m_loops) {
+ if (d->m_loops > 0 && d->m_curLoop >= d->m_loops) {
frameAt = 0;
- m_running = false;
+ d->m_running = false;
emit runningChanged(false);
maybeUpdate();
}
} else {
- frameAt = m_curFrame;
+ frameAt = d->m_curFrame;
}
- if (m_curFrame != lastFrame) {
+ if (d->m_curFrame != lastFrame) {
if (isCurrentFrameChangedConnected())
- emit currentFrameChanged(m_curFrame);
+ emit currentFrameChanged(d->m_curFrame);
maybeUpdate();
}
- qreal frameCount = m_spriteEngine->spriteFrames();
- bool reverse = m_spriteEngine->sprite()->reverse();
+ qreal frameCount = d->m_spriteEngine->spriteFrames();
+ bool reverse = d->m_spriteEngine->sprite()->reverse();
if (reverse)
frameAt = (frameCount - 1) - frameAt;
- qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- qreal x1;
- qreal y1;
- if (m_paused) {
- int spriteY = m_spriteEngine->spriteY();
+ int w = d->m_spriteEngine->spriteWidth();
+ int h = d->m_spriteEngine->spriteHeight();
+ int x1;
+ int y1;
+ if (d->m_paused) {
+ int spriteY = d->m_spriteEngine->spriteY();
if (reverse) {
- int rows = m_spriteEngine->maxFrames() * m_spriteEngine->spriteWidth() / m_sheetSize.width();
- spriteY -= rows * m_spriteEngine->spriteHeight();
+ int rows = d->m_spriteEngine->maxFrames() * d->m_spriteEngine->spriteWidth() / d->m_sheetSize.width();
+ spriteY -= rows * d->m_spriteEngine->spriteHeight();
frameAt = (frameCount - 1) - frameAt;
}
- int position = frameAt * m_spriteEngine->spriteWidth() + m_spriteEngine->spriteX();
- int row = position / m_sheetSize.width();
+ int position = frameAt * d->m_spriteEngine->spriteWidth() + d->m_spriteEngine->spriteX();
+ int row = position / d->m_sheetSize.width();
- x1 = (position - (row * m_sheetSize.width())) / m_sheetSize.width();
- y1 = (row * m_spriteEngine->spriteHeight() + spriteY) / m_sheetSize.height();
+ x1 = (position - (row * d->m_sheetSize.width()));
+ y1 = (row * d->m_spriteEngine->spriteHeight() + spriteY);
} else {
- x1 = m_spriteEngine->spriteX() / m_sheetSize.width() + frameAt * w;
- y1 = m_spriteEngine->spriteY() / m_sheetSize.height();
+ x1 = d->m_spriteEngine->spriteX() + frameAt * w;
+ y1 = d->m_spriteEngine->spriteY();
}
//### hard-coded 0/1 work because we are the only
// images in the sprite sheet (without this we cannot assume
// where in the sheet we begin/end).
- qreal x2;
- qreal y2;
+ int x2;
+ int y2;
if (reverse) {
if (frameAt > 0) {
x2 = x1 - w;
@@ -684,9 +786,9 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
y2 = y1 - h;
if (y2 < 0.0) {
//the last row may not fill the entire width
- int maxRowFrames = m_sheetSize.width() / m_spriteEngine->spriteWidth();
- if (m_spriteEngine->maxFrames() % maxRowFrames)
- x2 = ((m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
+ int maxRowFrames = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
+ if (d->m_spriteEngine->maxFrames() % maxRowFrames)
+ x2 = ((d->m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
y2 = 1.0 - h;
}
@@ -703,15 +805,13 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
}
}
- QQuickAnimatedSpriteMaterial *material = static_cast<QQuickAnimatedSpriteMaterial *>(node->material());
- material->animX1 = x1;
- material->animY1 = y1;
- material->animX2 = x2;
- material->animY2 = y2;
- material->animW = w;
- material->animH = h;
- material->animT = m_interpolate ? progress : 0.0;
- material->texture->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->setSourceA(QPoint(x1, y1));
+ node->setSourceB(QPoint(x2, y2));
+ node->setSpriteSize(QSize(w, h));
+ node->setTime(d->m_interpolate ? progress : 0.0);
+ node->setSize(QSizeF(width(), height()));
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->update();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index 9d8b4dad40..564c402fbb 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -62,6 +62,8 @@ class QQuickSprite;
class QQuickSpriteEngine;
class QSGGeometryNode;
class QQuickAnimatedSpriteMaterial;
+class QQuickAnimatedSpritePrivate;
+class QSGSpriteNode;
class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem
{
Q_OBJECT
@@ -94,80 +96,21 @@ public:
};
Q_ENUM(LoopParameters)
- bool running() const
- {
- return m_running;
- }
-
- bool interpolate() const
- {
- return m_interpolate;
- }
-
- QUrl source() const
- {
- return m_sprite->source();
- }
-
- bool reverse() const
- {
- return m_sprite->reverse();
- }
-
- bool frameSync() const
- {
- return m_sprite->frameSync();
- }
-
- int frameCount() const
- {
- return m_sprite->frames();
- }
-
- int frameHeight() const
- {
- return m_sprite->frameHeight();
- }
-
- int frameWidth() const
- {
- return m_sprite->frameWidth();
- }
-
- int frameX() const
- {
- return m_sprite->frameX();
- }
-
- int frameY() const
- {
- return m_sprite->frameY();
- }
-
- qreal frameRate() const
- {
- return m_sprite->frameRate();
- }
-
- int frameDuration() const
- {
- return m_sprite->frameDuration();
- }
-
- int loops() const
- {
- return m_loops;
- }
-
- bool paused() const
- {
- return m_paused;
- }
-
- int currentFrame() const
- {
- return m_curFrame;
- }
+ bool running() const;
+ bool interpolate() const;
+ QUrl source() const;
+ bool reverse() const;
+ bool frameSync() const;
+ int frameCount() const;
+ int frameHeight() const;
+ int frameWidth() const;
+ int frameX() const;
+ int frameY() const;
+ qreal frameRate() const;
+ int frameDuration() const;
+ int loops() const;
+ bool paused() const;
+ int currentFrame() const;
Q_SIGNALS:
@@ -176,27 +119,16 @@ Q_SIGNALS:
void interpolateChanged(bool arg);
void sourceChanged(QUrl arg);
-
void reverseChanged(bool arg);
-
void frameSyncChanged(bool arg);
-
void frameCountChanged(int arg);
-
void frameHeightChanged(int arg);
-
void frameWidthChanged(int arg);
-
void frameXChanged(int arg);
-
void frameYChanged(int arg);
-
void frameRateChanged(qreal arg);
-
void frameDurationChanged(int arg);
-
void loopsChanged(int arg);
-
void currentFrameChanged(int arg);
public Q_SLOTS:
@@ -207,157 +139,27 @@ public Q_SLOTS:
void pause();
void resume();
- void setRunning(bool arg)
- {
- if (m_running != arg) {
- if (m_running)
- stop();
- else
- start();
- }
- }
-
- void setPaused(bool arg)
- {
- if (m_paused != arg) {
- if (m_paused)
- resume();
- else
- pause();
- }
- }
-
- void setInterpolate(bool arg)
- {
- if (m_interpolate != arg) {
- m_interpolate = arg;
- Q_EMIT interpolateChanged(arg);
- }
- }
-
- void setSource(QUrl arg)
- {
- if (m_sprite->m_source != arg) {
- m_sprite->setSource(arg);
- Q_EMIT sourceChanged(arg);
- reloadImage();
- }
- }
-
- void setReverse(bool arg)
- {
- if (m_sprite->m_reverse != arg) {
- m_sprite->setReverse(arg);
- Q_EMIT reverseChanged(arg);
- }
- }
-
- void setFrameSync(bool arg)
- {
- if (m_sprite->m_frameSync != arg) {
- m_sprite->setFrameSync(arg);
- Q_EMIT frameSyncChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void setFrameCount(int arg)
- {
- if (m_sprite->m_frames != arg) {
- m_sprite->setFrameCount(arg);
- Q_EMIT frameCountChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameHeight(int arg)
- {
- if (m_sprite->m_frameHeight != arg) {
- m_sprite->setFrameHeight(arg);
- Q_EMIT frameHeightChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameWidth(int arg)
- {
- if (m_sprite->m_frameWidth != arg) {
- m_sprite->setFrameWidth(arg);
- Q_EMIT frameWidthChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameX(int arg)
- {
- if (m_sprite->m_frameX != arg) {
- m_sprite->setFrameX(arg);
- Q_EMIT frameXChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameY(int arg)
- {
- if (m_sprite->m_frameY != arg) {
- m_sprite->setFrameY(arg);
- Q_EMIT frameYChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameRate(qreal arg)
- {
- if (m_sprite->m_frameRate != arg) {
- m_sprite->setFrameRate(arg);
- Q_EMIT frameRateChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void setFrameDuration(int arg)
- {
- if (m_sprite->m_frameDuration != arg) {
- m_sprite->setFrameDuration(arg);
- Q_EMIT frameDurationChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void resetFrameRate()
- {
- setFrameRate(-1.0);
- }
-
- void resetFrameDuration()
- {
- setFrameDuration(-1);
- }
-
- void setLoops(int arg)
- {
- if (m_loops != arg) {
- m_loops = arg;
- Q_EMIT loopsChanged(arg);
- }
- }
-
- void setCurrentFrame(int arg) //TODO-C: Probably only works when paused
- {
- if (m_curFrame != arg) {
- m_curFrame = arg;
- Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance!
- update();
- }
- }
+ void setRunning(bool arg);
+ void setPaused(bool arg);
+ void setInterpolate(bool arg);
+ void setSource(QUrl arg);
+ void setReverse(bool arg);
+ void setFrameSync(bool arg);
+ void setFrameCount(int arg);
+ void setFrameHeight(int arg);
+ void setFrameWidth(int arg);
+ void setFrameX(int arg);
+ void setFrameY(int arg);
+ void setFrameRate(qreal arg);
+ void setFrameDuration(int arg);
+ void resetFrameRate();
+ void resetFrameDuration();
+ void setLoops(int arg);
+ void setCurrentFrame(int arg);
private Q_SLOTS:
void createEngine();
- void sizeVertices(QSGGeometryNode *node);
protected Q_SLOTS:
void reset();
@@ -368,21 +170,13 @@ protected:
private:
void maybeUpdate();
bool isCurrentFrameChangedConnected();
- void prepareNextFrame(QSGGeometryNode *node);
+ void prepareNextFrame(QSGSpriteNode *node);
void reloadImage();
- QSGGeometryNode* buildNode();
- QQuickSprite* m_sprite;
- QQuickSpriteEngine* m_spriteEngine;
- QElapsedTimer m_timestamp;
- int m_curFrame;
- bool m_pleaseReset;
- bool m_running;
- bool m_paused;
- bool m_interpolate;
- QSizeF m_sheetSize;
- int m_loops;
- int m_curLoop;
- int m_pauseOffset;
+ QSGSpriteNode* initNode();
+
+private:
+ Q_DISABLE_COPY(QQuickAnimatedSprite)
+ Q_DECLARE_PRIVATE(QQuickAnimatedSprite)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p_p.h b/src/quick/items/qquickanimatedsprite_p_p.h
new file mode 100644
index 0000000000..0e4a1e9066
--- /dev/null
+++ b/src/quick/items/qquickanimatedsprite_p_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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 QQUICKANIMATEDSPRITE_P_P_H
+#define QQUICKANIMATEDSPRITE_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 "qquickitem_p.h"
+#include "qquicksprite_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAnimatedSprite;
+
+class QQuickAnimatedSpritePrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickAnimatedSprite)
+
+public:
+ QQuickAnimatedSpritePrivate()
+ : m_sprite(nullptr)
+ , m_spriteEngine(nullptr)
+ , m_curFrame(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_paused(false)
+ , m_interpolate(true)
+ , m_loops(-1)
+ , m_curLoop(0)
+ , m_pauseOffset(0)
+ {
+ }
+
+ QQuickSprite* m_sprite;
+ QQuickSpriteEngine* m_spriteEngine;
+ QElapsedTimer m_timestamp;
+ int m_curFrame;
+ bool m_pleaseReset;
+ bool m_running;
+ bool m_paused;
+ bool m_interpolate;
+ QSize m_sheetSize;
+ int m_loops;
+ int m_curLoop;
+ int m_pauseOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKANIMATEDSPRITE_P_P_H
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index 66f414d816..ca5ad8dbbd 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -43,7 +43,9 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
+#ifndef QT_NO_NETWORK
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtCore/qfile.h>
#include <QtCore/qmath.h>
#include <QtGui/qguiapplication.h>
@@ -169,9 +171,11 @@ QQuickBorderImage::QQuickBorderImage(QQuickItem *parent)
QQuickBorderImage::~QQuickBorderImage()
{
+#ifndef QT_NO_NETWORK
Q_D(QQuickBorderImage);
if (d->sciReply)
d->sciReply->deleteLater();
+#endif
}
/*!
@@ -270,10 +274,12 @@ void QQuickBorderImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#ifndef QT_NO_NETWORK
if (d->sciReply) {
d->sciReply->deleteLater();
d->sciReply = 0;
}
+#endif
d->url = url;
d->sciurl = QUrl();
@@ -311,6 +317,7 @@ void QQuickBorderImage::load()
setGridScaledImage(QQuickGridScaledImage(&file));
return;
} else {
+#ifndef QT_NO_NETWORK
if (d->progress != 0.0) {
d->progress = 0.0;
emit progressChanged(d->progress);
@@ -320,6 +327,7 @@ void QQuickBorderImage::load()
d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
this, QQuickBorderImage, SLOT(sciRequestFinished()))
+#endif
}
} else {
QQuickPixmap::Options options;
@@ -529,6 +537,7 @@ void QQuickBorderImage::requestFinished()
pixmapChange();
}
+#ifndef QT_NO_NETWORK
#define BORDERIMAGE_MAX_REDIRECT 16
void QQuickBorderImage::sciRequestFinished()
@@ -558,12 +567,59 @@ void QQuickBorderImage::sciRequestFinished()
setGridScaledImage(sci);
}
}
+#endif // QT_NO_NETWORK
void QQuickBorderImage::doUpdate()
{
update();
}
+void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border,
+ const QSize &sourceSize,
+ const QSizeF &targetSize,
+ int horizontalTileMode,
+ int verticalTileMode,
+ qreal devicePixelRatio,
+ QRectF *targetRect,
+ QRectF *innerTargetRect,
+ QRectF *innerSourceRect,
+ QRectF *subSourceRect)
+{
+ *innerSourceRect = QRectF(0, 0, 1, 1);
+ *targetRect = QRectF(0, 0, targetSize.width(), targetSize.height());
+ *innerTargetRect = *targetRect;
+
+ if (border) {
+ *innerSourceRect = QRectF(border->left() * devicePixelRatio / qreal(sourceSize.width()),
+ border->top() * devicePixelRatio / qreal(sourceSize.height()),
+ qMax<qreal>(0, sourceSize.width() - (border->right() + border->left()) * devicePixelRatio) / sourceSize.width(),
+ qMax<qreal>(0, sourceSize.height() - (border->bottom() + border->top()) * devicePixelRatio) / sourceSize.height());
+ *innerTargetRect = QRectF(border->left(),
+ border->top(),
+ qMax<qreal>(0, targetSize.width() - (border->right() + border->left())),
+ qMax<qreal>(0, targetSize.height() - (border->bottom() + border->top())));
+ }
+
+ qreal hTiles = 1;
+ qreal vTiles = 1;
+ const QSizeF innerTargetSize = innerTargetRect->size() * devicePixelRatio;
+ if (innerSourceRect->width() != 0
+ && horizontalTileMode != QQuickBorderImage::Stretch) {
+ hTiles = innerTargetSize.width() / qreal(innerSourceRect->width() * sourceSize.width());
+ if (horizontalTileMode == QQuickBorderImage::Round)
+ hTiles = qCeil(hTiles);
+ }
+ if (innerSourceRect->height() != 0
+ && verticalTileMode != QQuickBorderImage::Stretch) {
+ vTiles = innerTargetSize.height() / qreal(innerSourceRect->height() * sourceSize.height());
+ if (verticalTileMode == QQuickBorderImage::Round)
+ vTiles = qCeil(vTiles);
+ }
+
+ *subSourceRect = QRectF(0, 0, hTiles, vTiles);
+}
+
+
QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
Q_D(QQuickBorderImage);
@@ -575,12 +631,12 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
bool updatePixmap = d->pixmapChanged;
d->pixmapChanged = false;
if (!node) {
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
updatePixmap = true;
}
@@ -588,45 +644,25 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
node->setTexture(texture);
// Don't implicitly create the scalegrid in the rendering thread...
- QRectF innerSourceRect(0, 0, 1, 1);
- QRectF targetRect(0, 0, width(), height());
- QRectF innerTargetRect = targetRect;
- if (d->border) {
- const QQuickScaleGrid *border = d->getScaleGrid();
- innerSourceRect = QRectF(border->left() * d->devicePixelRatio / qreal(d->pix.width()),
- border->top() * d->devicePixelRatio / qreal(d->pix.height()),
- qMax<qreal>(0, d->pix.width() - (border->right() + border->left()) * d->devicePixelRatio) / d->pix.width(),
- qMax<qreal>(0, d->pix.height() - (border->bottom() + border->top()) * d->devicePixelRatio) / d->pix.height());
- innerTargetRect = QRectF(border->left(),
- border->top(),
- qMax<qreal>(0, width() - (border->right() + border->left())),
- qMax<qreal>(0, height() - (border->bottom() + border->top())));
- }
- qreal hTiles = 1;
- qreal vTiles = 1;
- const QSizeF innerTargetSize = innerTargetRect.size() * d->devicePixelRatio;
- if (innerSourceRect.width() != 0
- && d->horizontalTileMode != QQuickBorderImage::Stretch) {
- hTiles = innerTargetSize.width() / qreal(innerSourceRect.width() * d->pix.width());
- if (d->horizontalTileMode == QQuickBorderImage::Round)
- hTiles = qCeil(hTiles);
- }
- if (innerSourceRect.height() != 0
- && d->verticalTileMode != QQuickBorderImage::Stretch) {
- vTiles = innerTargetSize.height() / qreal(innerSourceRect.height() * d->pix.height());
- if (d->verticalTileMode == QQuickBorderImage::Round)
- vTiles = qCeil(vTiles);
- }
+ QRectF targetRect;
+ QRectF innerTargetRect;
+ QRectF innerSourceRect;
+ QRectF subSourceRect;
+ d->calculateRects(d->border,
+ QSize(d->pix.width(), d->pix.height()), QSizeF(width(), height()),
+ d->horizontalTileMode, d->verticalTileMode, d->devicePixelRatio,
+ &targetRect, &innerTargetRect,
+ &innerSourceRect, &subSourceRect);
node->setTargetRect(targetRect);
node->setInnerSourceRect(innerSourceRect);
node->setInnerTargetRect(innerTargetRect);
- node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles));
+ node->setSubSourceRect(subSourceRect);
node->setMirror(d->mirror);
node->setMipmapFiltering(QSGTexture::None);
node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
- if (innerSourceRect == QRectF(0, 0, 1, 1) && (vTiles > 1 || hTiles > 1)) {
+ if (innerSourceRect == QRectF(0, 0, 1, 1) && (subSourceRect.width() > 1 || subSourceRect.height() > 1)) {
node->setHorizontalWrapMode(QSGTexture::Repeat);
node->setVerticalWrapMode(QSGTexture::Repeat);
} else {
diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h
index 7f8b172a21..f2764660f6 100644
--- a/src/quick/items/qquickborderimage_p.h
+++ b/src/quick/items/qquickborderimage_p.h
@@ -101,7 +101,9 @@ private:
private Q_SLOTS:
void doUpdate();
void requestFinished() Q_DECL_OVERRIDE;
+#ifndef QT_NO_NETWORK
void sciRequestFinished();
+#endif
private:
Q_DISABLE_COPY(QQuickBorderImage)
diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h
index 478de88a52..56fbbab049 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -58,17 +58,20 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_NETWORK
class QNetworkReply;
+#endif
class QQuickBorderImagePrivate : public QQuickImageBasePrivate
{
Q_DECLARE_PUBLIC(QQuickBorderImage)
public:
QQuickBorderImagePrivate()
- : border(0), sciReply(0),
- horizontalTileMode(QQuickBorderImage::Stretch),
- verticalTileMode(QQuickBorderImage::Stretch),
- redirectCount(0), pixmapChanged(false)
+ : border(0), horizontalTileMode(QQuickBorderImage::Stretch),
+ verticalTileMode(QQuickBorderImage::Stretch), pixmapChanged(false)
+#ifndef QT_NO_NETWORK
+ , sciReply(0), redirectCount(0)
+#endif
{
}
@@ -88,14 +91,27 @@ public:
return border;
}
+ static void calculateRects(const QQuickScaleGrid *border,
+ const QSize &sourceSize,
+ const QSizeF &targetSize,
+ int horizontalTileMode,
+ int verticalTileMode,
+ qreal devicePixelRatio,
+ QRectF *targetRect,
+ QRectF *innerTargetRect,
+ QRectF *innerSourceRect,
+ QRectF *subSourceRect);
+
QQuickScaleGrid *border;
QUrl sciurl;
- QNetworkReply *sciReply;
QQuickBorderImage::TileMode horizontalTileMode;
QQuickBorderImage::TileMode verticalTileMode;
- int redirectCount;
-
bool pixmapChanged : 1;
+
+#ifndef QT_NO_NETWORK
+ QNetworkReply *sciReply;
+ int redirectCount;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp
index 56f8227c05..747e844172 100644
--- a/src/quick/items/qquickclipnode.cpp
+++ b/src/quick/items/qquickclipnode.cpp
@@ -113,7 +113,7 @@ void QQuickDefaultClipNode::updateGeometry()
}
}
- markDirty(DirtyGeometry);
setClipRect(m_rect);
+ markDirty(DirtyGeometry);
}
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index 558f52a1af..b943c28661 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -45,6 +45,7 @@
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
+#include <private/qquickpixmapcache_p.h>
#include <private/qv8engine_p.h>
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qmimedata.h>
@@ -81,7 +82,7 @@ public:
{
}
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void itemParentChanged(QQuickItem *, QQuickItem *parent) Q_DECL_OVERRIDE;
void updatePosition();
void restartDrag();
@@ -110,6 +111,8 @@ public:
bool eventQueued : 1;
bool overrideActions : 1;
QPointF hotSpot;
+ QUrl imageSource;
+ QQuickPixmap pixmapLoader;
QStringList keys;
QVariantMap externalMimeData;
QQuickDrag::DragType dragType;
@@ -145,9 +148,10 @@ public:
\sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
*/
-void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
+ const QRectF &)
{
- if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
+ if (!change.positionChange() || !active || itemMoved)
return;
updatePosition();
}
@@ -409,6 +413,43 @@ void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
}
/*!
+ \qmlattachedproperty QUrl QtQuick::Drag::imageSource
+ \since 5.8
+
+ This property holds the URL of the image which will be used to represent
+ the data during the drag and drop operation. Changing this property after
+ the drag operation has started will have no effect.
+
+ The example below uses an item's contents as a drag image:
+
+ \snippet qml/externaldrag.qml 0
+
+ \sa Item::grabToImage()
+*/
+
+QUrl QQuickDragAttached::imageSource() const
+{
+ Q_D(const QQuickDragAttached);
+ return d->imageSource;
+}
+
+void QQuickDragAttached::setImageSource(const QUrl &url)
+{
+ Q_D(QQuickDragAttached);
+ if (d->imageSource != url) {
+ d->imageSource = url;
+
+ if (url.isEmpty()) {
+ d->pixmapLoader.clear();
+ } else {
+ d->pixmapLoader.load(qmlEngine(this), url);
+ }
+
+ Q_EMIT imageSourceChanged();
+ }
+}
+
+/*!
\qmlattachedproperty stringlist QtQuick::Drag::keys
This property holds a list of keys that can be used by a DropArea to filter drag events.
@@ -727,9 +768,9 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
mimeData->setData(it.key(), it.value().toString().toUtf8());
drag->setMimeData(mimeData);
-
- // TODO: how to handle drag image?
- // drag->setPixmap(iconPixmap);
+ if (pixmapLoader.isReady()) {
+ drag->setPixmap(QPixmap::fromImage(pixmapLoader.image()));
+ }
emit q->dragStarted();
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index c1695e49f0..17721251d9 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -58,6 +58,7 @@
#include <QtCore/qmimedata.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qurl.h>
#ifndef QT_NO_DRAGANDDROP
@@ -247,6 +248,7 @@ class QQuickDragAttached : public QObject
Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource)
Q_PROPERTY(QObject *target READ target NOTIFY targetChanged)
Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged)
+ Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged REVISION 8)
Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged)
Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged)
@@ -268,6 +270,9 @@ public:
QPointF hotSpot() const;
void setHotSpot(const QPointF &hotSpot);
+ QUrl imageSource() const;
+ void setImageSource(const QUrl &url);
+
QStringList keys() const;
void setKeys(const QStringList &keys);
@@ -300,6 +305,7 @@ Q_SIGNALS:
void sourceChanged();
void targetChanged();
void hotSpotChanged();
+ void imageSourceChanged();
void keysChanged();
void mimeDataChanged();
void supportedActionsChanged();
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index 1701441240..8dcc13971e 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -235,7 +235,7 @@ bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const
return true;
QRegExp copy = keyRegExp;
- foreach (const QString &key, keys) {
+ for (const QString &key : keys) {
if (copy.exactMatch(key))
return true;
}
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index cc45891189..a5497f4627 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -38,9 +38,16 @@
****************************************************************************/
#include "qquickevents_p_p.h"
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
+#include <private/qdebug_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events")
+Q_LOGGING_CATEGORY(lcPointerGrab, "qt.quick.pointer.grab")
+
/*!
\qmltype KeyEvent
\instantiates QQuickKeyEvent
@@ -415,4 +422,534 @@ Item {
\endqml
*/
+/*!
+ \qmlproperty int QtQuick::WheelEvent::inverted
+
+ Returns whether the delta values delivered with the event are inverted.
+
+ Normally, a vertical wheel will produce a WheelEvent with positive delta
+ values if the top of the wheel is rotating away from the hand operating it.
+ Similarly, a horizontal wheel movement will produce a QWheelEvent with
+ positive delta values if the top of the wheel is moved to the left.
+
+ However, on some platforms this is configurable, so that the same
+ operations described above will produce negative delta values (but with the
+ same magnitude). For instance, in a QML component (such as a tumbler or a
+ slider) where it is appropriate to synchronize the movement or rotation of
+ an item with the direction of the wheel, regardless of the system settings,
+ the wheel event handler can use the inverted property to decide whether to
+ negate the angleDelta or pixelDelta values.
+
+ \note Many platforms provide no such information. On such platforms
+ \l inverted always returns false.
+*/
+
+typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
+Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices)
+
+Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
+ (QQuickPointerDevice::Mouse,
+ QQuickPointerDevice::GenericPointer,
+ QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover,
+ 1, 3, QLatin1String("core pointer"), 0))
+
+typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
+Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
+
+QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d)
+{
+ if (g_touchDevices->contains(d))
+ return g_touchDevices->value(d);
+
+ QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen;
+ QString name;
+ int maximumTouchPoints = 10;
+ QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position);
+ if (d) {
+ QQuickPointerDevice::Capabilities caps =
+ static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0x0F);
+ if (d->type() == QTouchDevice::TouchPad) {
+ type = QQuickPointerDevice::TouchPad;
+ caps |= QQuickPointerDevice::Scroll;
+ }
+ name = d->name();
+ maximumTouchPoints = d->maximumTouchPoints();
+ } else {
+ qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent";
+ }
+
+ QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger,
+ caps, maximumTouchPoints, 0, name, 0);
+ g_touchDevices->insert(d, dev);
+ return dev;
+}
+
+QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
+{
+ return g_touchDevices->values();
+}
+
+QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice()
+{
+ return g_genericMouseDevice;
+}
+
+QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id)
+{
+ auto it = g_tabletDevices->find(id);
+ if (it != g_tabletDevices->end())
+ return it.value();
+
+ // ### Figure out how to populate the tablet devices
+ return nullptr;
+}
+
+void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp)
+{
+ m_scenePos = scenePos;
+ m_pointId = pointId;
+ m_valid = true;
+ m_accept = false;
+ m_state = static_cast<QQuickEventPoint::State>(state);
+ m_timestamp = timestamp;
+ if (state == Qt::TouchPointPressed)
+ m_pressTimestamp = timestamp;
+ // TODO calculate velocity
+}
+
+QQuickItem *QQuickEventPoint::grabber() const
+{
+ return m_grabber.data();
+}
+
+void QQuickEventPoint::setGrabber(QQuickItem *grabber)
+{
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled()) && m_grabber.data() != grabber) {
+ auto device = static_cast<const QQuickPointerEvent *>(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;
+ }
+ m_grabber = QPointer<QQuickItem>(grabber);
+}
+
+void QQuickEventPoint::setAccepted(bool accepted)
+{
+ if (m_accept != accepted) {
+ qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted;
+ m_accept = accepted;
+ }
+}
+
+QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent)
+ : QQuickEventPoint(parent), m_rotation(0), m_pressure(0)
+{}
+
+void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp)
+{
+ QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp);
+ m_rotation = tp.rotation();
+ m_pressure = tp.pressure();
+ m_uniqueId = tp.uniqueId();
+}
+
+/*!
+ \internal
+ \class QQuickPointerEvent
+
+ QQuickPointerEvent is used as a long-lived object to store data related to
+ an event from a pointing device, such as a mouse, touch or tablet event,
+ during event delivery. It also provides properties which may be used later
+ to expose the event to QML, the same as is done with QQuickMouseEvent,
+ QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be
+ delivered at a time, this class is effectively a singleton. We don't worry
+ about the QObject overhead because the instances are long-lived: we don't
+ dynamically create and destroy objects of this type for each event.
+*/
+
+QQuickPointerEvent::~QQuickPointerEvent()
+{}
+
+QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
+{
+ auto ev = static_cast<QMouseEvent*>(event);
+ m_event = ev;
+ if (!event)
+ return this;
+
+ m_device = QQuickPointerDevice::genericMouseDevice();
+ m_button = ev->button();
+ m_pressedButtons = ev->buttons();
+ Qt::TouchPointState state = Qt::TouchPointStationary;
+ switch (ev->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ state = Qt::TouchPointPressed;
+ break;
+ case QEvent::MouseButtonRelease:
+ state = Qt::TouchPointReleased;
+ break;
+ case QEvent::MouseMove:
+ state = Qt::TouchPointMoved;
+ break;
+ default:
+ break;
+ }
+ m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0
+ return this;
+}
+
+QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
+{
+ auto ev = static_cast<QTouchEvent*>(event);
+ m_event = ev;
+ if (!event)
+ return this;
+
+ m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_button = Qt::NoButton;
+ m_pressedButtons = Qt::NoButton;
+
+ const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
+ int newPointCount = tps.count();
+ m_touchPoints.reserve(newPointCount);
+
+ 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<QQuickItem*> 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;
+ if (auto point = pointById(tps.at(i).id()))
+ grabber = point->grabber();
+ grabbers.append(grabber);
+ }
+
+ for (int i = 0; i < newPointCount; ++i) {
+ auto point = m_touchPoints.at(i);
+ point->reset(tps.at(i), ev->timestamp());
+ if (point->state() == QQuickEventPoint::Pressed) {
+ if (grabbers.at(i))
+ qWarning() << "TouchPointPressed without previous release event" << point;
+ point->setGrabber(nullptr);
+ } else {
+ point->setGrabber(grabbers.at(i));
+ }
+ }
+ m_pointCount = newPointCount;
+ return this;
+}
+
+QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
+ if (i == 0)
+ return m_mousePoint;
+ return nullptr;
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
+ if (i >= 0 && i < m_pointCount)
+ return m_touchPoints.at(i);
+ return nullptr;
+}
+
+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)
+{
+ Q_UNUSED(m_reserved);
+}
+
+QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
+{
+ return static_cast<QQuickPointerEvent *>(parent());
+}
+
+bool QQuickPointerMouseEvent::allPointsAccepted() const {
+ return m_mousePoint->isAccepted();
+}
+
+QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
+{
+ auto event = static_cast<QMouseEvent *>(m_event);
+ event->setLocalPos(localPos);
+ return event;
+}
+
+QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const
+{
+ QVector<QQuickItem *> result;
+ if (QQuickItem *grabber = m_mousePoint->grabber())
+ result << grabber;
+ return result;
+}
+
+void QQuickPointerMouseEvent::clearGrabbers() const {
+ m_mousePoint->setGrabber(nullptr);
+}
+
+bool QQuickPointerMouseEvent::isPressEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) &&
+ (me->buttons() & me->button()) == me->buttons());
+}
+
+bool QQuickPointerTouchEvent::allPointsAccepted() const {
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (!m_touchPoints.at(i)->isAccepted())
+ return false;
+ }
+ return true;
+}
+
+QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
+{
+ QVector<QQuickItem *> result;
+ for (int i = 0; i < m_pointCount; ++i) {
+ auto point = m_touchPoints.at(i);
+ if (QQuickItem *grabber = point->grabber()) {
+ if (!result.contains(grabber))
+ result << grabber;
+ }
+ }
+ return result;
+}
+
+void QQuickPointerTouchEvent::clearGrabbers() const {
+ for (auto point: m_touchPoints)
+ point->setGrabber(nullptr);
+}
+
+bool QQuickPointerTouchEvent::isPressEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
+}
+
+QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
+{
+ QVector<QPointF> points;
+ for (int i = 0; i < pointCount(); ++i) {
+ if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed)
+ points << point(i)->scenePos();
+ }
+ return points;
+}
+
+/*!
+ \internal
+ Populate the reusable synth-mouse event from one touchpoint.
+ It's required that isTouchEvent() be true when this is called.
+ If the touchpoint cannot be found, this returns nullptr.
+ Ownership of the event is NOT transferred to the caller.
+*/
+QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const {
+ const QTouchEvent::TouchPoint *p = touchPointById(pointID);
+ if (!p)
+ return nullptr;
+ QEvent::Type type;
+ Qt::MouseButton buttons = Qt::LeftButton;
+ switch (p->state()) {
+ case Qt::TouchPointPressed:
+ type = QEvent::MouseButtonPress;
+ break;
+ case Qt::TouchPointMoved:
+ case Qt::TouchPointStationary:
+ type = QEvent::MouseMove;
+ break;
+ case Qt::TouchPointReleased:
+ type = QEvent::MouseButtonRelease;
+ buttons = Qt::NoButton;
+ break;
+ default:
+ Q_ASSERT(false);
+ return nullptr;
+ }
+ m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()),
+ p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers());
+ m_synthMouseEvent.setAccepted(true);
+ m_synthMouseEvent.setTimestamp(m_event->timestamp());
+ // In the future we will try to always have valid velocity in every QQuickEventPoint.
+ // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity
+ // and if it is set, then it does not need to do its own velocity calculations.
+ // That's probably the only usecase for this, so far. Some day Flickable should handle
+ // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete.
+ // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity
+ // so that we don't need to convert m_device->capabilities().
+ if (m_device)
+ QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity());
+ QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt);
+ return &m_synthMouseEvent;
+}
+
+/*!
+ \internal
+ Returns a pointer to the QQuickEventPoint which has the \a pointId as
+ \l {QQuickEventPoint::pointId}{pointId}.
+ Returns nullptr if there is no point with that ID.
+
+ \fn QQuickPointerEvent::pointById(quint64 pointId) const
+*/
+QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const {
+ if (m_mousePoint && pointId == m_mousePoint->pointId())
+ return m_mousePoint;
+ return nullptr;
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 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())
+ return *it;
+ return nullptr;
+}
+
+
+/*!
+ \internal
+ Returns a pointer to the original TouchPoint which has the same
+ \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
+ QTouchEvent, and if that point is found. Otherwise, returns nullptr.
+*/
+const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const {
+ const QTouchEvent *ev = asTouchEvent();
+ if (!ev)
+ return nullptr;
+ const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
+ auto it = std::find_if(tps.constBegin(), tps.constEnd(),
+ [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } );
+ // return the pointer to the actual TP in QTouchEvent::_touchPoints
+ return (it == tps.constEnd() ? nullptr : it.operator->());
+}
+
+/*!
+ \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.
+*/
+QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
+{
+ QList<QTouchEvent::TouchPoint> touchPoints;
+ Qt::TouchPointStates eventStates;
+ // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here
+ // 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
+
+ QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
+ for (int i = 0; i < m_pointCount; ++i) {
+ auto p = m_touchPoints.at(i);
+ if (p->isAccepted())
+ continue;
+ bool isGrabber = p->grabber() == item;
+ bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos()));
+ if (!(isGrabber || isPressInside || isFiltering))
+ continue;
+
+ const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
+ if (tp) {
+ eventStates |= tp->state();
+ QTouchEvent::TouchPoint tpCopy = *tp;
+ tpCopy.setPos(item->mapFromScene(tpCopy.scenePos()));
+ tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos()));
+ tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos()));
+ tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect()));
+ tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D());
+ touchPoints << tpCopy;
+ }
+ }
+
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty())
+ return nullptr;
+
+ // if all points have the same state, set the event type accordingly
+ const QTouchEvent &event = *asTouchEvent();
+ QEvent::Type eventType = event.type();
+ switch (eventStates) {
+ case Qt::TouchPointPressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case Qt::TouchPointReleased:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ QTouchEvent *touchEvent = new QTouchEvent(eventType);
+ touchEvent->setWindow(event.window());
+ touchEvent->setTarget(item);
+ touchEvent->setDevice(event.device());
+ touchEvent->setModifiers(event.modifiers());
+ touchEvent->setTouchPoints(touchPoints);
+ touchEvent->setTouchPointStates(eventStates);
+ touchEvent->setTimestamp(event.timestamp());
+ touchEvent->accept();
+ return touchEvent;
+}
+
+QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
+{
+ return static_cast<QTouchEvent *>(m_event);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ if (!dev) {
+ dbg << "QQuickPointerDevice(0)";
+ return dbg;
+ }
+ dbg << "QQuickPointerDevice("<< dev->name() << ' ';
+ QtDebugUtils::formatQEnum(dbg, dev->type());
+ dbg << ' ';
+ QtDebugUtils::formatQEnum(dbg, dev->pointerType());
+ dbg << " caps:";
+ QtDebugUtils::formatQFlags(dbg, dev->capabilities());
+ if (dev->type() == QQuickPointerDevice::TouchScreen ||
+ dev->type() == QQuickPointerDevice::TouchPad)
+ dbg << " maxTouchPoints:" << dev->maximumTouchPoints();
+ else
+ dbg << " buttonCount:" << dev->buttonCount();
+ dbg << ')';
+ return dbg;
+}
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QQuickPointerEvent(dev:";
+ QtDebugUtils::formatQEnum(dbg, event->device()->type());
+ if (event->buttons() != Qt::NoButton) {
+ dbg << " buttons:";
+ QtDebugUtils::formatQEnum(dbg, event->buttons());
+ }
+ dbg << " [";
+ int c = event->pointCount();
+ for (int i = 0; i < c; ++i)
+ dbg << event->point(i) << ' ';
+ dbg << "])";
+ return dbg;
+}
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted()
+ << " state:";
+ QtDebugUtils::formatQEnum(dbg, event->state());
+ dbg << " scenePos:" << event->scenePos() << " id:" << event->pointId()
+ << " timeHeld:" << event->timeHeld() << ')';
+ return dbg;
+}
+
+#endif
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index b28ab555b0..61bbb4ecda 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -55,12 +55,20 @@
#include <qqml.h>
#include <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
#include <QtGui/qvector2d.h>
#include <QtGui/qevent.h>
#include <QtGui/qkeysequence.h>
+#include <QtQuick/qquickitem.h>
QT_BEGIN_NAMESPACE
+class QQuickPointerDevice;
+class QQuickPointerEvent;
+class QQuickPointerMouseEvent;
+class QQuickPointerTabletEvent;
+class QQuickPointerTouchEvent;
+
class QQuickKeyEvent : public QObject
{
Q_OBJECT
@@ -73,10 +81,22 @@ class QQuickKeyEvent : public QObject
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1)
- : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); }
- QQuickKeyEvent(const QKeyEvent &ke)
- : event(ke) { event.setAccepted(false); }
+ QQuickKeyEvent()
+ : event(QEvent::None, 0, 0)
+ {}
+
+ void reset(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
+ const QString &text = QString(), bool autorep = false, ushort count = 1)
+ {
+ event = QKeyEvent(type, key, modifiers, text, autorep, count);
+ event.setAccepted(false);
+ }
+
+ void reset(const QKeyEvent &ke)
+ {
+ event = ke;
+ event.setAccepted(false);
+ }
int key() const { return event.key(); }
QString text() const { return event.text(); }
@@ -109,10 +129,24 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickMouseEvent(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers
- , bool isClick=false, bool wasHeld=false)
- : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers)
- , _source(Qt::MouseEventNotSynthesized), _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {}
+ QQuickMouseEvent()
+ : _x(0), _y(0), _button(Qt::NoButton), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier)
+ , _source(Qt::MouseEventNotSynthesized), _wasHeld(false), _isClick(false), _accepted(false)
+ {}
+
+ void reset(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers, bool isClick = false, bool wasHeld = false)
+ {
+ _x = x;
+ _y = y;
+ _button = button;
+ _buttons = buttons;
+ _modifiers = modifiers;
+ _source = Qt::MouseEventNotSynthesized;
+ _wasHeld = wasHeld;
+ _isClick = isClick;
+ _accepted = true;
+ }
qreal x() const { return _x; }
qreal y() const { return _y; }
@@ -139,9 +173,9 @@ private:
Qt::MouseButtons _buttons;
Qt::KeyboardModifiers _modifiers;
Qt::MouseEventSource _source;
- bool _wasHeld;
- bool _isClick;
- bool _accepted;
+ bool _wasHeld : 1;
+ bool _isClick : 1;
+ bool _accepted : 1;
};
class QQuickWheelEvent : public QObject
@@ -153,13 +187,27 @@ class QQuickWheelEvent : public QObject
Q_PROPERTY(QPoint pixelDelta READ pixelDelta)
Q_PROPERTY(int buttons READ buttons)
Q_PROPERTY(int modifiers READ modifiers)
+ Q_PROPERTY(bool inverted READ inverted)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickWheelEvent(qreal x, qreal y, const QPoint& angleDelta, const QPoint& pixelDelta,
- Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
- : _x(x), _y(y), _angleDelta(angleDelta), _pixelDelta(pixelDelta), _buttons(buttons),
- _modifiers(modifiers), _accepted(true) {}
+ QQuickWheelEvent()
+ : _x(0), _y(0), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier)
+ , _inverted(false), _accepted(false)
+ {}
+
+ void reset(qreal x, qreal y, const QPoint &angleDelta, const QPoint &pixelDelta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, bool inverted)
+ {
+ _x = x;
+ _y = y;
+ _angleDelta = angleDelta;
+ _pixelDelta = pixelDelta;
+ _buttons = buttons;
+ _modifiers = modifiers;
+ _accepted = true;
+ _inverted = inverted;
+ }
qreal x() const { return _x; }
qreal y() const { return _y; }
@@ -167,7 +215,7 @@ public:
QPoint pixelDelta() const { return _pixelDelta; }
int buttons() const { return _buttons; }
int modifiers() const { return _modifiers; }
-
+ bool inverted() const { return _inverted; }
bool isAccepted() { return _accepted; }
void setAccepted(bool accepted) { _accepted = accepted; }
@@ -178,13 +226,335 @@ private:
QPoint _pixelDelta;
Qt::MouseButtons _buttons;
Qt::KeyboardModifiers _modifiers;
+ bool _inverted;
+ bool _accepted;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
+
+public:
+ QQuickCloseEvent()
+ : _accepted(true) {}
+
+ bool isAccepted() { return _accepted; }
+ void setAccepted(bool accepted) { _accepted = accepted; }
+
+private:
bool _accepted;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF scenePos READ scenePos)
+ Q_PROPERTY(State state READ state)
+ 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)
+
+public:
+ enum State {
+ Pressed = Qt::TouchPointPressed,
+ Updated = Qt::TouchPointMoved,
+ Stationary = Qt::TouchPointStationary,
+ Released = Qt::TouchPointReleased
+ // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe
+ };
+ Q_ENUM(State)
+
+ QQuickEventPoint(QQuickPointerEvent *parent);
+
+ void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp);
+
+ void invalidate() { m_valid = false; }
+
+ QQuickPointerEvent *pointerEvent() const;
+ QPointF scenePos() const { return m_scenePos; }
+ 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);
+ QQuickItem *grabber() const;
+ void setGrabber(QQuickItem *grabber);
+
+private:
+ QPointF m_scenePos;
+ quint64 m_pointId;
+ QPointer<QQuickItem> m_grabber;
+ ulong m_timestamp;
+ ulong m_pressTimestamp;
+ State m_state;
+ bool m_valid : 1;
+ bool m_accept : 1;
+ int m_reserved : 30;
+
+ Q_DISABLE_COPY(QQuickEventPoint)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId)
+
+public:
+ QQuickEventTouchPoint(QQuickPointerTouchEvent *parent);
+
+ void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp);
+
+ qreal rotation() const { return m_rotation; }
+ qreal pressure() const { return m_pressure; }
+ QPointerUniqueId uniqueId() const { return m_uniqueId; }
+
+private:
+ qreal m_rotation;
+ qreal m_pressure;
+ QPointerUniqueId m_uniqueId;
+
+ Q_DISABLE_COPY(QQuickEventTouchPoint)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(const QQuickPointerDevice *device READ device)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
+ Q_PROPERTY(Qt::MouseButtons button READ button)
+ Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
+
+public:
+ QQuickPointerEvent(QObject *parent = nullptr)
+ : QObject(parent)
+ , m_device(nullptr)
+ , m_event(nullptr)
+ , m_button(Qt::NoButton)
+ , m_pressedButtons(Qt::NoButton)
+ { }
+
+ virtual ~QQuickPointerEvent();
+
+public: // property accessors
+ QQuickPointerDevice *device() const { return m_device; }
+ Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; }
+ Qt::MouseButton button() const { return m_button; }
+ Qt::MouseButtons buttons() const { return m_pressedButtons; }
+
+public: // helpers for C++ only (during event delivery)
+ virtual QQuickPointerEvent *reset(QEvent *ev) = 0;
+
+ virtual bool isPressEvent() const = 0;
+ virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; }
+ virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; }
+ virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; }
+ virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; }
+ virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; }
+ virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
+ bool isValid() const { return m_event != nullptr; }
+ virtual bool allPointsAccepted() const = 0;
+ bool isAccepted() { return m_event->isAccepted(); }
+ void setAccepted(bool accepted) { m_event->setAccepted(accepted); }
+ QVector<QPointF> unacceptedPressedPointScenePositions() const;
+
+ virtual int pointCount() const = 0;
+ virtual QQuickEventPoint *point(int i) const = 0;
+ virtual QQuickEventPoint *pointById(quint64 pointId) const = 0;
+ virtual QVector<QQuickItem *> grabbers() const = 0;
+ virtual void clearGrabbers() const = 0;
+
+ ulong timestamp() const { return m_event->timestamp(); }
+
+protected:
+ QQuickPointerDevice *m_device;
+ QInputEvent *m_event; // original event as received by QQuickWindow
+ Qt::MouseButton m_button;
+ Qt::MouseButtons m_pressedButtons;
+
+ Q_DISABLE_COPY(QQuickPointerEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerMouseEvent(QObject *parent = nullptr)
+ : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
+ 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;
+ bool allPointsAccepted() const override;
+ QVector<QQuickItem *> grabbers() const override;
+ void clearGrabbers() const override;
+
+ QMouseEvent *asMouseEvent(const QPointF& localPos) const;
+
+private:
+ QQuickEventPoint *m_mousePoint;
+
+ Q_DISABLE_COPY(QQuickPointerMouseEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerTouchEvent(QObject *parent = nullptr)
+ : QQuickPointerEvent(parent)
+ , m_pointCount(0)
+ , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier)
+ { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; }
+ 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;
+ const QTouchEvent::TouchPoint *touchPointById(int pointId) const;
+ bool allPointsAccepted() const override;
+ QVector<QQuickItem *> grabbers() const override;
+ void clearGrabbers() const override;
+
+ QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const;
+ QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const;
+
+ QTouchEvent *asTouchEvent() const;
+
+private:
+ int m_pointCount;
+ QVector<QQuickEventTouchPoint *> m_touchPoints;
+ mutable QMouseEvent m_synthMouseEvent;
+
+ Q_DISABLE_COPY(QQuickPointerTouchEvent)
+};
+
+// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(DeviceType type READ type CONSTANT)
+ Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT)
+ Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT)
+ Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT)
+ Q_PROPERTY(int buttonCount READ buttonCount CONSTANT)
+ Q_PROPERTY(QString name READ name CONSTANT)
+ Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT)
+
+public:
+ enum DeviceType {
+ UnknownDevice = 0x0000,
+ Mouse = 0x0001,
+ TouchScreen = 0x0002,
+ TouchPad = 0x0004,
+ Puck = 0x0008,
+ Stylus = 0x0010,
+ Airbrush = 0x0020,
+ AllDevices = 0x003F
+ };
+ Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
+ Q_ENUM(DeviceType)
+ Q_FLAG(DeviceTypes)
+
+ enum PointerType {
+ GenericPointer = 0x0001,
+ Finger = 0x0002,
+ Pen = 0x0004,
+ Eraser = 0x0008,
+ Cursor = 0x0010,
+ AllPointerTypes = 0x001F
+ };
+ Q_DECLARE_FLAGS(PointerTypes, PointerType)
+ Q_ENUM(PointerType)
+ Q_FLAG(PointerTypes)
+
+ enum CapabilityFlag {
+ Position = QTouchDevice::Position,
+ Area = QTouchDevice::Area,
+ Pressure = QTouchDevice::Pressure,
+ Velocity = QTouchDevice::Velocity,
+ // some bits reserved in case we need more of QTouchDevice::Capabilities
+ Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?)
+ Hover = 0x0200,
+ Rotation = 0x0400,
+ XTilt = 0x0800,
+ YTilt = 0x1000
+ };
+ Q_DECLARE_FLAGS(Capabilities, CapabilityFlag)
+ 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; }
+ bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; }
+ int maximumTouchPoints() const { return m_maximumTouchPoints; }
+ int buttonCount() const { return m_buttonCount; }
+ QString name() const { return m_name; }
+ qint64 uniqueId() const { return m_uniqueId; }
+ QQuickPointerEvent *pointerEvent() const { return m_event; }
+
+ static QQuickPointerDevice *touchDevice(QTouchDevice *d);
+ static QList<QQuickPointerDevice *> touchDevices();
+ static QQuickPointerDevice *genericMouseDevice();
+ static QQuickPointerDevice *tabletDevice(qint64);
+
+private:
+ DeviceType m_deviceType;
+ PointerType m_pointerType;
+ Capabilities m_capabilities;
+ int m_maximumTouchPoints;
+ int m_buttonCount;
+ QString m_name;
+ qint64 m_uniqueId;
+ // the device-specific event instance which is reused during event delivery
+ QQuickPointerEvent *m_event;
+
+ Q_DISABLE_COPY(QQuickPointerDevice)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities)
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerDevice *);
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerEvent *);
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventPoint *);
+//Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventTouchPoint *); TODO maybe
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickKeyEvent)
QML_DECLARE_TYPE(QQuickMouseEvent)
QML_DECLARE_TYPE(QQuickWheelEvent)
+QML_DECLARE_TYPE(QQuickCloseEvent)
+QML_DECLARE_TYPE(QQuickPointerDevice)
+QML_DECLARE_TYPE(QPointerUniqueId)
+QML_DECLARE_TYPE(QQuickPointerEvent)
#endif // QQUICKEVENTS_P_P_H
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 49117d27d1..12b01b8aaf 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -55,6 +55,8 @@
#include <QtCore/qmath.h>
#include "qplatformdefs.h"
+#include <cmath>
+
QT_BEGIN_NAMESPACE
// FlickThreshold determines how far the "mouse" must have moved
@@ -69,6 +71,21 @@ static const int RetainGrabVelocity = 100;
static const int MovementEndingTimerInterval = 100;
#endif
+// Currently std::round can't be used on Android when using ndk g++, so
+// use C version instead. We could just define two versions of Round, one
+// for float and one for double, but then only one of them would be used
+// and compiler would trigger a warning about unused function.
+//
+// See https://code.google.com/p/android/issues/detail?id=54418
+template<typename T>
+static T Round(T t) {
+ return round(t);
+}
+template<>
+Q_DECL_UNUSED float Round<float>(float f) {
+ return roundf(f);
+}
+
static qreal EaseOvershoot(qreal t) {
return qAtan(t);
}
@@ -292,14 +309,14 @@ void QQuickFlickablePrivate::AxisData::updateVelocity()
}
}
-void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
+void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
{
Q_Q(QQuickFlickable);
if (item == contentItem) {
Qt::Orientations orient = 0;
- if (newGeom.x() != oldGeom.x())
+ if (change.xChange())
orient |= Qt::Horizontal;
- if (newGeom.y() != oldGeom.y())
+ if (change.yChange())
orient |= Qt::Vertical;
if (orient)
q->viewportMoved(orient);
@@ -351,7 +368,7 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt
qreal dist = v2 / (accel * 2.0);
if (v > 0)
dist = -dist;
- qreal target = -qRound(-(data.move.value() - dist));
+ qreal target = -Round(-(data.move.value() - dist));
dist = -target + data.move.value();
accel = v2 / (2.0f * qAbs(dist));
@@ -455,18 +472,18 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt
} else if (data.move.value() <= maxExtent) {
resetTimeline(data);
adjustContentPos(data, maxExtent);
- } else if (-qRound(-data.move.value()) != data.move.value()) {
+ } else if (-Round(-data.move.value()) != data.move.value()) {
// We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
resetTimeline(data);
qreal val = data.move.value();
- if (qAbs(-qRound(-val) - val) < 0.25) // round small differences
- val = -qRound(-val);
+ if (std::abs(-Round(-val) - val) < 0.25) // round small differences
+ val = -Round(-val);
else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
- val = -qFloor(-val);
+ val = -std::floor(-val);
else if (data.smoothVelocity.value() < 0)
- val = -qCeil(-val);
+ val = -std::ceil(-val);
else // otherwise round
- val = -qRound(-val);
+ val = -Round(-val);
timeline.set(data.move, val);
}
data.inOvershoot = false;
@@ -1553,12 +1570,12 @@ void QQuickFlickablePrivate::replayDelayedPress()
//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
void QQuickFlickablePrivate::setViewportX(qreal x)
{
- contentItem->setX(pixelAligned ? -qRound(-x) : x);
+ contentItem->setX(pixelAligned ? -Round(-x) : x);
}
void QQuickFlickablePrivate::setViewportY(qreal y)
{
- contentItem->setY(pixelAligned ? -qRound(-y) : y);
+ contentItem->setY(pixelAligned ? -Round(-y) : y);
}
void QQuickFlickable::timerEvent(QTimerEvent *event)
@@ -2219,21 +2236,28 @@ void QQuickFlickablePrivate::cancelInteraction()
}
}
-bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
+/*!
+ QQuickFlickable::filterMouseEvent checks filtered mouse events and potentially steals them.
+
+ This is how flickable takes over events from other items (\a receiver) that are on top of it.
+ It filters their events and may take over (grab) the \a event.
+ Return true if the mouse event will be stolen.
+ \internal
+*/
+bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
{
Q_D(QQuickFlickable);
QPointF localPos = mapFromScene(event->windowPos());
- QQuickWindow *c = window();
- QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
- if (grabber == this && d->stealMouse) {
+ Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
+ if (receiver == this && d->stealMouse) {
// we are already the grabber and we do want the mouse event to ourselves.
return true;
}
- bool grabberDisabled = grabber && !grabber->isEnabled();
+ bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
+ if ((stealThisEvent || contains(localPos)) && (!receiver || !receiver->keepMouseGrab() || receiverDisabled)) {
QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
mouseEvent->setAccepted(false);
@@ -2243,7 +2267,7 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
break;
case QEvent::MouseButtonPress:
d->handleMousePressEvent(mouseEvent.data());
- d->captureDelayedPress(item, event);
+ d->captureDelayedPress(receiver, event);
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
break;
case QEvent::MouseButtonRelease:
@@ -2253,15 +2277,14 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
default:
break;
}
- grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
- if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
+ if ((receiver && stealThisEvent && !receiver->keepMouseGrab() && receiver != this) || receiverDisabled) {
d->clearDelayedPress();
grabMouse();
} else if (d->delayedPressEvent) {
grabMouse();
}
- const bool filtered = stealThisEvent || d->delayedPressEvent || grabberDisabled;
+ const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled;
if (filtered) {
event->setAccepted(true);
}
@@ -2270,7 +2293,7 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
d->lastPosTime = -1;
returnToBounds();
}
- if (event->type() == QEvent::MouseButtonRelease || (grabber && grabber->keepMouseGrab() && !grabberDisabled)) {
+ if (event->type() == QEvent::MouseButtonRelease || (receiver && receiver->keepMouseGrab() && !receiverDisabled)) {
// mouse released, or another item has claimed the grab
d->lastPosTime = -1;
d->clearDelayedPress();
@@ -2290,7 +2313,7 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
- return sendMouseEvent(i, static_cast<QMouseEvent *>(e));
+ return filterMouseEvent(i, static_cast<QMouseEvent *>(e));
case QEvent::UngrabMouse:
if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
// The grab has been taken away from a child and given to some other item.
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 318b8ce473..610bfd1427 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -275,7 +275,7 @@ protected:
void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry) Q_DECL_OVERRIDE;
void mouseUngrabEvent() Q_DECL_OVERRIDE;
- bool sendMouseEvent(QQuickItem *item, QMouseEvent *event);
+ bool filterMouseEvent(QQuickItem *receiver, QMouseEvent *event);
bool xflick() const;
bool yflick() const;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 3c59b19ec2..ac1e39d829 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -62,6 +62,7 @@
#include <private/qquicktimeline_p_p.h>
#include <private/qquickanimation_p_p.h>
#include <private/qquicktransitionmanager_p_p.h>
+#include <private/qpodvector_p.h>
QT_BEGIN_NAMESPACE
@@ -193,7 +194,7 @@ public:
qreal overShootDistance(qreal size);
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void draggingStarting();
void draggingEnding();
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0356b72e1d..857cd44b15 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -40,10 +40,11 @@
#include "qquickframebufferobject.h"
#include <QtGui/QOpenGLFramebufferObject>
-
+#include <QtGui/QOpenGLFunctions>
#include <private/qquickitem_p.h>
#include <QSGSimpleTextureNode>
+#include <QSGRendererInterface>
QT_BEGIN_NAMESPACE
@@ -260,6 +261,12 @@ public:
int devicePixelRatio;
};
+static inline bool isOpenGL(QSGRenderContext *rc)
+{
+ QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
+ return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL;
+}
+
/*!
* \internal
*/
@@ -278,6 +285,8 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode
Q_D(QQuickFramebufferObject);
if (!n) {
+ if (!isOpenGL(d->sceneGraphRenderContext()))
+ return 0;
if (!d->node)
d->node = new QSGFramebufferObjectNode;
n = d->node;
@@ -360,6 +369,8 @@ QSGTextureProvider *QQuickFramebufferObject::textureProvider() const
qWarning("QQuickFramebufferObject::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+ if (!isOpenGL(d->sceneGraphRenderContext()))
+ return 0;
if (!d->node)
d->node = new QSGFramebufferObjectNode;
return d->node;
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
new file mode 100644
index 0000000000..9714f39663
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -0,0 +1,658 @@
+/****************************************************************************
+**
+** 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 <private/qquickgenericshadereffect_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickitem_p.h>
+#include <QSignalMapper>
+
+QT_BEGIN_NAMESPACE
+
+// The generic shader effect is used when the scenegraph backend indicates
+// SupportsShaderEffectNode. This, unlike the monolithic and interconnected (e.g.
+// with particles) OpenGL variant, passes most of the work to a scenegraph node
+// created via the adaptation layer, thus allowing different implementation in
+// the backends.
+
+QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent)
+ : QObject(parent)
+ , m_item(item)
+ , m_meshResolution(1, 1)
+ , m_mesh(nullptr)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_blending(true)
+ , m_supportsAtlasTextures(false)
+ , m_mgr(nullptr)
+ , m_fragNeedsUpdate(true)
+ , m_vertNeedsUpdate(true)
+ , m_dirty(0)
+{
+ qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
+ for (int i = 0; i < NShader; ++i)
+ m_inProgress[i] = nullptr;
+}
+
+QQuickGenericShaderEffect::~QQuickGenericShaderEffect()
+{
+ for (int i = 0; i < NShader; ++i) {
+ disconnectSignals(Shader(i));
+ for (const auto &sm : qAsConst(m_signalMappers[i]))
+ delete sm.mapper;
+ }
+
+ delete m_mgr;
+}
+
+void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src)
+{
+ // Compare the actual values since they are often just filenames.
+ // Optimizing by comparing constData() is a bad idea since seemingly static
+ // strings in QML may in fact have different addresses when a binding
+ // triggers assigning the "same" value to the property.
+ if (m_fragShader == src)
+ return;
+
+ m_fragShader = src;
+
+ m_fragNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
+{
+ if (m_vertShader == src)
+ return;
+
+ m_vertShader = src;
+
+ m_vertNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ emit m_item->vertexShaderChanged();
+}
+
+void QQuickGenericShaderEffect::setBlending(bool enable)
+{
+ if (m_blending == enable)
+ return;
+
+ m_blending = enable;
+ m_item->update();
+ emit m_item->blendingChanged();
+}
+
+QVariant QQuickGenericShaderEffect::mesh() const
+{
+ return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
+ : qVariantFromValue(m_meshResolution);
+}
+
+void QQuickGenericShaderEffect::setMesh(const QVariant &mesh)
+{
+ QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
+ if (newMesh && newMesh == m_mesh)
+ return;
+
+ if (m_mesh)
+ disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
+
+ m_mesh = newMesh;
+
+ if (m_mesh) {
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(markGeometryDirtyAndUpdate()));
+ } else {
+ if (mesh.canConvert<QSize>()) {
+ m_meshResolution = mesh.toSize();
+ } else {
+ QList<QByteArray> res = mesh.toByteArray().split('x');
+ bool ok = res.size() == 2;
+ if (ok) {
+ int w = res.at(0).toInt(&ok);
+ if (ok) {
+ int h = res.at(1).toInt(&ok);
+ if (ok)
+ m_meshResolution = QSize(w, h);
+ }
+ }
+ if (!ok)
+ qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
+ }
+ m_defaultMesh.setResolution(m_meshResolution);
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
+ m_item->update();
+
+ emit m_item->meshChanged();
+}
+
+void QQuickGenericShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
+{
+ if (m_cullMode == face)
+ return;
+
+ m_cullMode = face;
+ m_item->update();
+ emit m_item->cullModeChanged();
+}
+
+void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports)
+{
+ if (m_supportsAtlasTextures == supports)
+ return;
+
+ m_supportsAtlasTextures = supports;
+ markGeometryDirtyAndUpdate();
+ emit m_item->supportsAtlasTexturesChanged();
+}
+
+QString QQuickGenericShaderEffect::parseLog()
+{
+ maybeUpdateShaders();
+ return log();
+}
+
+QString QQuickGenericShaderEffect::log() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QString();
+
+ return mgr->log();
+}
+
+QQuickShaderEffect::Status QQuickGenericShaderEffect::status() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::Uncompiled;
+
+ return QQuickShaderEffect::Status(mgr->status());
+}
+
+void QQuickGenericShaderEffect::handleEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ const auto &vars(m_shaders[shaderType].shaderInfo.variables);
+ for (int idx = 0; idx < vars.count(); ++idx) {
+ if (vars[idx].name == e->propertyName()) {
+ propertyChanged((shaderType << 16) | idx);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void QQuickGenericShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+}
+
+QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
+
+ if (m_item->width() <= 0 || m_item->height() <= 0) {
+ delete node;
+ return nullptr;
+ }
+
+ // Do not change anything while a new shader is being reflected or compiled.
+ if (m_inProgress[Vertex] || m_inProgress[Fragment])
+ return node;
+
+ // The manager should be already created on the gui thread. Just take that instance.
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr) {
+ delete node;
+ return nullptr;
+ }
+
+ if (!node) {
+ QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->window())->context;
+ node = rc->sceneGraphContext()->createShaderEffectNode(rc, mgr);
+ m_dirty = QSGShaderEffectNode::DirtyShaderAll;
+ }
+
+ QSGShaderEffectNode::SyncData sd;
+ sd.dirty = m_dirty;
+ sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
+ sd.blending = m_blending;
+ sd.vertex.shader = &m_shaders[Vertex];
+ sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
+ sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
+ sd.fragment.shader = &m_shaders[Fragment];
+ sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
+ sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
+ node->syncMaterial(&sd);
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
+ node->setGeometry(nullptr);
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
+ const QRectF rect(0, 0, m_item->width(), m_item->height());
+ QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+ QSGGeometry *geometry = node->geometry();
+
+ const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
+ geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
+
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ m_dirty = 0;
+ for (int i = 0; i < NShader; ++i) {
+ m_dirtyConstants[i].clear();
+ m_dirtyTextures[i].clear();
+ }
+
+ return node;
+}
+
+void QQuickGenericShaderEffect::maybeUpdateShaders()
+{
+ if (m_vertNeedsUpdate)
+ m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
+ if (m_fragNeedsUpdate)
+ m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
+ if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
+ // This function is invoked either from componentComplete or in a
+ // response to a previous invocation's polish() request. If this is
+ // case #1 then updateShader can fail due to not having a window or
+ // scenegraph ready. Schedule the polish to try again later. In case #2
+ // the backend probably does not have shadereffect support so there is
+ // nothing to do for us here.
+ if (!m_item->window() || !m_item->window()->isSceneGraphInitialized())
+ m_item->polish();
+ }
+}
+
+void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ // Move the window ref.
+ if (change == QQuickItem::ItemSceneChange) {
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (value.window)
+ QQuickItemPrivate::get(source)->refWindow(value.window);
+ else
+ QQuickItemPrivate::get(source)->derefWindow();
+ }
+ }
+ }
+ }
+ }
+}
+
+QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager() const
+{
+ if (!m_mgr) {
+ // return null if this is not the gui thread and not already created
+ if (QThread::currentThread() != m_item->thread())
+ return m_mgr;
+ QQuickWindow *w = m_item->window();
+ if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
+ m_mgr = QQuickWindowPrivate::get(w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager();
+ if (m_mgr) {
+ connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(logChanged()));
+ connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(statusChanged()));
+ connect(m_mgr, SIGNAL(textureChanged()), this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas()));
+ connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickGenericShaderEffect::shaderCodePrepared);
+ }
+ }
+ }
+
+ return m_mgr;
+}
+
+void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
+{
+ for (auto &sm : m_signalMappers[shaderType]) {
+ if (sm.active) {
+ sm.active = false;
+ QObject::disconnect(m_item, nullptr, sm.mapper, SLOT(map()));
+ QObject::disconnect(sm.mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int)));
+ }
+ }
+ for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+struct ShaderInfoCache
+{
+ bool contains(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.contains(key);
+ }
+
+ QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.value(key);
+ }
+
+ void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
+ {
+ m_shaderInfoCache.insert(key, value);
+ }
+
+ QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
+};
+
+Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
+
+bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return false;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ disconnectSignals(shaderType);
+
+ m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
+ m_shaders[shaderType].varData.clear();
+
+ if (!src.isEmpty()) {
+ if (shaderInfoCache()->contains(src)) {
+ m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src);
+ m_shaders[shaderType].hasShaderCode = true;
+ } else {
+ // Each prepareShaderCode call needs its own work area, hence the
+ // dynamic alloc. If there are calls in progress, let those run to
+ // finish, their results can then simply be ignored because
+ // m_inProgress indicates what we care about.
+ m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint =
+ shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+ // Figure out what input parameters and variables are used in the
+ // shader. For file-based shader source/bytecode this is where the data
+ // is pulled in from the file. Some backends may choose to do
+ // source->bytecode compilation as well in this step.
+ mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]);
+ // the rest is handled in shaderCodePrepared()
+ return true;
+ }
+ } else {
+ m_shaders[shaderType].hasShaderCode = false;
+ if (shaderType == Fragment) {
+ // With built-in shaders hasShaderCode is set to false and all
+ // metadata is empty, as it is left up to the node to provide a
+ // built-in default shader and its metadata. However, in case of
+ // the built-in fragment shader the value for 'source' has to be
+ // provided and monitored like with an application-provided shader.
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 0;
+ v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ m_shaders[shaderType].shaderInfo.variables.append(v);
+ }
+ }
+
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+ return true;
+}
+
+void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
+{
+ const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
+
+ // If another call was made to updateShader() for the same shader type in
+ // the meantime then our results are useless, just drop them.
+ if (result != m_inProgress[shaderType]) {
+ delete result;
+ return;
+ }
+
+ m_shaders[shaderType].shaderInfo = *result;
+ delete result;
+ m_inProgress[shaderType] = nullptr;
+
+ if (!ok) {
+ qWarning("ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log()));
+ m_shaders[shaderType].hasShaderCode = false;
+ return;
+ }
+
+ m_shaders[shaderType].hasShaderCode = true;
+ shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo);
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
+ m_shaders[shaderType].varData.resize(varCount);
+
+ // Reuse signal mappers as much as possible since the mapping is based on
+ // the index and shader type which are both constant.
+ if (m_signalMappers[shaderType].count() < varCount)
+ m_signalMappers[shaderType].resize(varCount);
+
+ // Hook up the signals to get notified about changes for properties that
+ // correspond to variables in the shader. Store also the values.
+ for (int i = 0; i < varCount; ++i) {
+ const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
+ QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
+ const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
+ if (isSpecial) {
+ if (v.name == QByteArrayLiteral("qt_Opacity"))
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ else if (v.name == QByteArrayLiteral("qt_Matrix"))
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ else if (v.name.startsWith("qt_SubRect_"))
+ vd.specialType = QSGShaderEffectNode::VariableData::SubRect;
+ continue;
+ }
+
+ // The value of a property corresponding to a sampler is the source
+ // item ref, unless there are separate texture objects in which case
+ // the sampler is ignored (here).
+ if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ if (texturesSeparate) {
+ vd.specialType = QSGShaderEffectNode::VariableData::Unused;
+ continue;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ }
+ } else if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
+ Q_ASSERT(texturesSeparate);
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::None;
+ }
+
+ // Find the property on the ShaderEffect item.
+ const int propIdx = m_item->metaObject()->indexOfProperty(v.name.constData());
+ if (propIdx >= 0) {
+ QMetaProperty mp = m_item->metaObject()->property(propIdx);
+ if (!mp.hasNotifySignal())
+ qWarning("ShaderEffect: property '%s' does not have notification method", v.name.constData());
+
+ // 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) {
+ sm.mapper = new QSignalMapper;
+ sm.mapper->setMapping(m_item, i | (shaderType << 16));
+ }
+ sm.active = true;
+ const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
+ QObject::connect(m_item, signalName, sm.mapper, SLOT(map()));
+ QObject::connect(sm.mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int)));
+ } else {
+ // Do not warn for dynamic properties.
+ if (!m_item->property(v.name.constData()).isValid())
+ qWarning("ShaderEffect: '%s' does not have a matching property!", v.name.constData());
+ }
+
+ vd.value = m_item->property(v.name.constData());
+
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->refWindow(m_item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+bool QQuickGenericShaderEffect::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (int idx = 0; idx < m_shaders[shaderType].varData.count(); ++idx) {
+ if (shaderType != typeToSkip || idx != indexToSkip) {
+ const auto &vd(m_shaders[shaderType].varData[idx]);
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void QQuickGenericShaderEffect::propertyChanged(int mappedId)
+{
+ const Shader type = Shader(mappedId >> 16);
+ const int idx = mappedId & 0xFFFF;
+ const auto &v(m_shaders[type].shaderInfo.variables[idx]);
+ auto &vd(m_shaders[type].varData[idx]);
+
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ // QObject::disconnect() will disconnect all matching connections.
+ // If the same source has been attached to two separate
+ // textures/samplers, then changing one of them would trigger both
+ // to be disconnected. So check first.
+ if (sourceIsUnique(source, type, idx))
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ vd.value = m_item->property(v.name.constData());
+
+ source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ // 'source' needs a window to get a scene graph node. It usually gets one through its
+ // parent, but if the source item is "inline" rather than a reference -- i.e.
+ // "property variant source: Image { }" instead of "property variant source: foo" -- it
+ // will not get a parent. In those cases, 'source' should get the window from 'item'.
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->refWindow(m_item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
+ m_dirtyTextures[type].insert(idx);
+
+ } else {
+ vd.value = m_item->property(v.name.constData());
+ m_dirty |= QSGShaderEffectNode::DirtyShaderConstant;
+ m_dirtyConstants[type].insert(idx);
+ }
+
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::sourceDestroyed(QObject *object)
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (auto &vd : m_shaders[shaderType].varData) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
+ if (qvariant_cast<QObject *>(vd.value) == object)
+ vd.value = QVariant();
+ }
+ }
+ }
+}
+
+void QQuickGenericShaderEffect::markGeometryDirtyAndUpdate()
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
+{
+ if (m_supportsAtlasTextures)
+ markGeometryDirtyAndUpdate();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h
new file mode 100644
index 0000000000..ab19816493
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** 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 QQUICKGENERICSHADEREFFECT_P_H
+#define QQUICKGENERICSHADEREFFECT_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 <QtQuick/qquickitem.h>
+#include <private/qtquickglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSignalMapper;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickGenericShaderEffect : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent = 0);
+ ~QQuickGenericShaderEffect();
+
+ QByteArray fragmentShader() const { return m_fragShader; }
+ void setFragmentShader(const QByteArray &src);
+
+ QByteArray vertexShader() const { return m_vertShader; }
+ void setVertexShader(const QByteArray &src);
+
+ bool blending() const { return m_blending; }
+ void setBlending(bool enable);
+
+ QVariant mesh() const;
+ void setMesh(const QVariant &mesh);
+
+ QQuickShaderEffect::CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(QQuickShaderEffect::CullMode face);
+
+ QString log() const;
+ QQuickShaderEffect::Status status() const;
+
+ bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ void setSupportsAtlasTextures(bool supports);
+
+ QString parseLog();
+
+ void handleEvent(QEvent *);
+ void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *);
+ void handleComponentComplete();
+ void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+ void maybeUpdateShaders();
+
+private slots:
+ void propertyChanged(int mappedId);
+ void sourceDestroyed(QObject *object);
+ void markGeometryDirtyAndUpdate();
+ void markGeometryDirtyAndUpdateIfSupportsAtlas();
+ void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result);
+
+private:
+ QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
+
+ enum Shader {
+ Vertex,
+ Fragment,
+
+ NShader
+ };
+ bool updateShader(Shader shaderType, const QByteArray &src);
+ void updateShaderVars(Shader shaderType);
+ void disconnectSignals(Shader shaderType);
+ bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
+
+ QQuickShaderEffect *m_item;
+ QSize m_meshResolution;
+ QQuickShaderEffectMesh *m_mesh;
+ QQuickGridMesh m_defaultMesh;
+ QQuickShaderEffect::CullMode m_cullMode;
+ bool m_blending;
+ bool m_supportsAtlasTextures;
+ mutable QSGGuiThreadShaderEffectManager *m_mgr;
+ QByteArray m_fragShader;
+ bool m_fragNeedsUpdate;
+ QByteArray m_vertShader;
+ bool m_vertNeedsUpdate;
+
+ QSGShaderEffectNode::ShaderData m_shaders[NShader];
+ QSGShaderEffectNode::DirtyShaderFlags m_dirty;
+ QSet<int> m_dirtyConstants[NShader];
+ QSet<int> m_dirtyTextures[NShader];
+ QSGGuiThreadShaderEffectManager::ShaderInfo *m_inProgress[NShader];
+
+ struct SignalMapper {
+ SignalMapper() : mapper(nullptr), active(false) { }
+ QSignalMapper *mapper;
+ bool active;
+ };
+ QVector<SignalMapper> m_signalMappers[NShader];
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGENERICSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp
new file mode 100644
index 0000000000..761d0c3cad
--- /dev/null
+++ b/src/quick/items/qquickgraphicsinfo.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "qquickgraphicsinfo_p.h"
+#include "qquickwindow.h"
+#include "qquickitem.h"
+#include <QtGui/qopenglcontext.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype GraphicsInfo
+ \instantiates QQuickGraphicsInfo
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \since 5.8
+ \since QtQuick 2.8
+ \brief Provides information about the used Qt Quick backend
+
+ The GraphicsInfo attached type provides information about the scenegraph
+ backend used to render the contents of the associated window.
+
+ If the item to which the properties are attached is not currently
+ associated with any window, the properties are set to default values. When
+ the associated window changes, the properties will update.
+ */
+
+QQuickGraphicsInfo::QQuickGraphicsInfo(QQuickItem *item)
+ : QObject(item)
+ , m_window(0)
+ , m_api(Unknown)
+ , m_shaderType(UnknownShadingLanguage)
+ , m_shaderCompilationType(ShaderCompilationType(0))
+ , m_shaderSourceType(ShaderSourceType(0))
+ , m_majorVersion(2)
+ , m_minorVersion(0)
+ , m_profile(OpenGLNoProfile)
+ , m_renderableType(SurfaceFormatUnspecified)
+{
+ connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(setWindow(QQuickWindow*)));
+ setWindow(item->window());
+}
+
+QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
+{
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(object))
+ return new QQuickGraphicsInfo(item);
+
+ return nullptr;
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::api
+
+ This property describes the graphics API that is currently in use.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.Unknown - the default value when no active scenegraph is associated with the item
+ \li GraphicsInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
+ \li GraphicsInfo.OpenGL - OpenGL or OpenGL ES
+ \li GraphicsInfo.Direct3D12 - Direct3D 12
+ \endlist
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderType
+
+ This property contains the shading language supported by the Qt Quick
+ backend the application is using.
+
+ \list
+ \li GraphicsInfo.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
+ \li GraphicsInfo.GLSL - GLSL or GLSL ES
+ \li GraphicsInfo.HLSL - HLSL
+ \endlist
+
+ \note The value is only up-to-date once the item is associated with a
+ window. Bindings relying on the value have to keep this in mind since the
+ value may change from GraphicsInfo.UnknownShadingLanguage to the actual
+ value after component initialization is complete. This is particularly
+ relevant for ShaderEffect items inside ShaderEffectSource items set as
+ property values.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderCompilationType, shaderSourceType
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderCompilationType
+
+ This property contains a bitmask of the shader compilation approaches
+ supported by the Qt Quick backend the application is using.
+
+ \list
+ \li GraphicsInfo.RuntimeCompilation
+ \li GraphicsInfo.OfflineCompilation
+ \endlist
+
+ With OpenGL the value is GraphicsInfo.RuntimeCompilation, which corresponds
+ to the traditional way of using ShaderEffect. Non-OpenGL backends are
+ expected to focus more on GraphicsInfo.OfflineCompilation, however.
+
+ \note The value is only up-to-date once the item is associated with a
+ window. Bindings relying on the value have to keep this in mind since the
+ value may change from \c 0 to the actual bitmask after component
+ initialization is complete. This is particularly relevant for ShaderEffect
+ items inside ShaderEffectSource items set as property values.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderType, shaderSourceType
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderSourceType
+
+ This property contains a bitmask of the supported ways of providing shader
+ sources.
+
+ \list
+ \li GraphicsInfo.ShaderSourceString
+ \li GraphicsInfo.ShaderSourceFile
+ \li GraphicsInfo.ShaderByteCode
+ \endlist
+
+ With OpenGL the value is GraphicsInfo.ShaderSourceString, which corresponds
+ to the traditional way of inlining GLSL source code into QML. Other,
+ non-OpenGL Qt Quick backends may however decide not to support inlined
+ shader sources, or even shader sources at all. In this case shaders are
+ expected to be pre-compiled into formats like SPIR-V or D3D shader
+ bytecode.
+
+ \note The value is only up-to-date once the item is associated with a
+ window. Bindings relying on the value have to keep this in mind since the
+ value may change from \c 0 to the actual bitmask after component
+ initialization is complete. This is particularly relevant for ShaderEffect
+ items inside ShaderEffectSource items set as property values.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderType, shaderCompilationType
+*/
+
+/*!
+ \qmlproperty int QtQuick::GraphicsInfo::majorVersion
+
+ This property holds the major version of the graphics API in use.
+
+ With OpenGL the default version is \c 2.0.
+
+ \sa minorVersion, profile
+ */
+
+/*!
+ \qmlproperty int QtQuick::GraphicsInfo::minorVersion
+
+ This property holds the minor version of the graphics API in use.
+
+ With OpenGL the default version is \c 2.0.
+
+ \sa majorVersion, profile
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::profile
+
+ This property holds the configured OpenGL context profile.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.OpenGLNoProfile (default) - OpenGL version is lower than 3.2 or OpenGL is not in use.
+ \li GraphicsInfo.OpenGLCoreProfile - Functionality deprecated in OpenGL version 3.0 is not available.
+ \li GraphicsInfo.OpenGLCompatibilityProfile - Functionality from earlier OpenGL versions is available.
+ \endlist
+
+ Reusable QML components will typically use this property in bindings in order to
+ choose between core and non core profile compatible shader sources.
+
+ \sa majorVersion, minorVersion, QSurfaceFormat
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::renderableType
+
+ This property holds the renderable type. The value has no meaning for APIs
+ other than OpenGL.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.SurfaceFormatUnspecified (default) - Unspecified rendering method
+ \li GraphicsInfo.SurfaceFormatOpenGL - Desktop OpenGL or other graphics API
+ \li GraphicsInfo.SurfaceFormatOpenGLES - OpenGL ES
+ \endlist
+
+ \sa QSurfaceFormat
+ */
+
+void QQuickGraphicsInfo::updateInfo()
+{
+ // The queries via the RIF do not depend on isSceneGraphInitialized(), they only need a window.
+ if (m_window) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ if (rif) {
+ GraphicsApi newAPI = GraphicsApi(rif->graphicsApi());
+ if (m_api != newAPI) {
+ m_api = newAPI;
+ emit apiChanged();
+ m_shaderType = ShaderType(rif->shaderType());
+ emit shaderTypeChanged();
+ m_shaderCompilationType = ShaderCompilationType(int(rif->shaderCompilationType()));
+ emit shaderCompilationTypeChanged();
+ m_shaderSourceType = ShaderSourceType(int(rif->shaderSourceType()));
+ emit shaderSourceTypeChanged();
+ }
+ }
+ }
+
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+#ifndef QT_NO_OPENGL
+ if (m_window && m_window->isSceneGraphInitialized()) {
+ QOpenGLContext *context = m_window->openglContext();
+ if (context)
+ format = context->format();
+ }
+#endif
+ if (m_majorVersion != format.majorVersion()) {
+ m_majorVersion = format.majorVersion();
+ emit majorVersionChanged();
+ }
+ if (m_minorVersion != format.minorVersion()) {
+ m_minorVersion = format.minorVersion();
+ emit minorVersionChanged();
+ }
+ OpenGLContextProfile profile = static_cast<OpenGLContextProfile>(format.profile());
+ if (m_profile != profile) {
+ m_profile = profile;
+ emit profileChanged();
+ }
+ RenderableType renderableType = static_cast<RenderableType>(format.renderableType());
+ if (m_renderableType != renderableType) {
+ m_renderableType = renderableType;
+ emit renderableTypeChanged();
+ }
+}
+
+void QQuickGraphicsInfo::setWindow(QQuickWindow *window)
+{
+ if (m_window != window) {
+ if (m_window) {
+ disconnect(m_window, SIGNAL(sceneGraphInitialized()), this, SLOT(updateInfo()));
+ disconnect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(updateInfo()));
+ }
+ if (window) {
+ connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(updateInfo()));
+ connect(window, SIGNAL(sceneGraphInvalidated()), this, SLOT(updateInfo()));
+ }
+ m_window = window;
+ }
+ updateInfo();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickgraphicsinfo_p.h b/src/quick/items/qquickgraphicsinfo_p.h
new file mode 100644
index 0000000000..9ef7bacb3e
--- /dev/null
+++ b/src/quick/items/qquickgraphicsinfo_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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 QQUICKGRAPHICSINFO_P_H
+#define QQUICKGRAPHICSINFO_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 <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
+#include <QtQml/qqml.h>
+#include <QtGui/qsurfaceformat.h>
+#include <QtQuick/qsgrendererinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickWindow;
+
+class QQuickGraphicsInfo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(GraphicsApi api READ api NOTIFY apiChanged FINAL)
+ Q_PROPERTY(ShaderType shaderType READ shaderType NOTIFY shaderTypeChanged FINAL)
+ Q_PROPERTY(ShaderCompilationType shaderCompilationType READ shaderCompilationType NOTIFY shaderCompilationTypeChanged FINAL)
+ Q_PROPERTY(ShaderSourceType shaderSourceType READ shaderSourceType NOTIFY shaderSourceTypeChanged FINAL)
+
+ Q_PROPERTY(int majorVersion READ majorVersion NOTIFY majorVersionChanged FINAL)
+ Q_PROPERTY(int minorVersion READ minorVersion NOTIFY minorVersionChanged FINAL)
+ Q_PROPERTY(OpenGLContextProfile profile READ profile NOTIFY profileChanged FINAL)
+ Q_PROPERTY(RenderableType renderableType READ renderableType NOTIFY renderableTypeChanged FINAL)
+
+public:
+ enum GraphicsApi {
+ Unknown = QSGRendererInterface::Unknown,
+ Software = QSGRendererInterface::Software,
+ OpenGL = QSGRendererInterface::OpenGL,
+ Direct3D12 = QSGRendererInterface::Direct3D12
+ };
+ Q_ENUM(GraphicsApi)
+
+ enum ShaderType {
+ UnknownShadingLanguage = QSGRendererInterface::UnknownShadingLanguage,
+ GLSL = QSGRendererInterface::GLSL,
+ HLSL = QSGRendererInterface::HLSL
+ };
+ Q_ENUM(ShaderType)
+
+ enum ShaderCompilationType {
+ RuntimeCompilation = QSGRendererInterface::RuntimeCompilation,
+ OfflineCompilation = QSGRendererInterface::OfflineCompilation
+ };
+ Q_ENUM(ShaderCompilationType)
+
+ enum ShaderSourceType {
+ ShaderSourceString = QSGRendererInterface::ShaderSourceString,
+ ShaderSourceFile = QSGRendererInterface::ShaderSourceFile,
+ ShaderByteCode = QSGRendererInterface::ShaderByteCode
+ };
+ Q_ENUM(ShaderSourceType)
+
+ enum OpenGLContextProfile {
+ OpenGLNoProfile = QSurfaceFormat::NoProfile,
+ OpenGLCoreProfile = QSurfaceFormat::CoreProfile,
+ OpenGLCompatibilityProfile = QSurfaceFormat::CompatibilityProfile
+ };
+ Q_ENUM(OpenGLContextProfile)
+
+ enum RenderableType {
+ SurfaceFormatUnspecified = QSurfaceFormat::DefaultRenderableType,
+ SurfaceFormatOpenGL = QSurfaceFormat::OpenGL,
+ SurfaceFormatOpenGLES = QSurfaceFormat::OpenGLES
+ };
+ Q_ENUM(RenderableType)
+
+ QQuickGraphicsInfo(QQuickItem *item = 0);
+
+ static QQuickGraphicsInfo *qmlAttachedProperties(QObject *object);
+
+ GraphicsApi api() const { return m_api; }
+ ShaderType shaderType() const { return m_shaderType; }
+ ShaderCompilationType shaderCompilationType() const { return m_shaderCompilationType; }
+ ShaderSourceType shaderSourceType() const { return m_shaderSourceType; }
+
+ int majorVersion() const { return m_majorVersion; }
+ int minorVersion() const { return m_minorVersion; }
+ OpenGLContextProfile profile() const { return m_profile; }
+ RenderableType renderableType() const { return m_renderableType; }
+
+Q_SIGNALS:
+ void apiChanged();
+ void shaderTypeChanged();
+ void shaderCompilationTypeChanged();
+ void shaderSourceTypeChanged();
+
+ void majorVersionChanged();
+ void minorVersionChanged();
+ void profileChanged();
+ void renderableTypeChanged();
+
+private Q_SLOTS:
+ void updateInfo();
+ void setWindow(QQuickWindow *window);
+
+private:
+ QPointer<QQuickWindow> m_window;
+ GraphicsApi m_api;
+ ShaderType m_shaderType;
+ ShaderCompilationType m_shaderCompilationType;
+ ShaderSourceType m_shaderSourceType;
+ int m_majorVersion;
+ int m_minorVersion;
+ OpenGLContextProfile m_profile;
+ RenderableType m_renderableType;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QQuickGraphicsInfo, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKGRAPHICSINFO_P_H
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index fe33019d27..14ea43f123 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -478,7 +478,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qreal colPos = colPosAt(visibleIndex);
qreal rowPos = rowPosAt(visibleIndex);
if (visibleItems.count()) {
- FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
+ FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.constLast());
rowPos = lastItem->rowPos();
int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
if (++colNum >= columns) {
@@ -536,7 +536,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
// Find first column
if (visibleItems.count()) {
- FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
rowPos = firstItem->rowPos();
colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize());
if (--colNum < 0) {
@@ -586,7 +586,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
bool changed = false;
while (visibleItems.count() > 1
- && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
+ && (item = static_cast<FxGridItemSG*>(visibleItems.constFirst()))
&& item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
if (item->attached->delayRemove())
break;
@@ -598,7 +598,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
changed = true;
}
while (visibleItems.count() > 1
- && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
+ && (item = static_cast<FxGridItemSG*>(visibleItems.constLast()))
&& item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
if (item->attached->delayRemove())
break;
@@ -623,7 +623,7 @@ void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex)
const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning;
const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd;
- FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
qreal rowPos = firstItem->rowPos();
qreal colPos = firstItem->colPos();
int col = visibleIndex % columns;
@@ -679,7 +679,7 @@ void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos)
{
- FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.constFirst());
item->setPosition(0, pos);
}
@@ -692,7 +692,7 @@ void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int
if (moveCount == 0 && changeBeforeVisible != 0)
moveCount += (changeBeforeVisible % columns) - (columns - 1);
- FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
}
@@ -2517,7 +2517,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
int markerItemIndex = -1;
for (int i=0; i<visibleItems.count(); i++) {
- if (visibleItems[i]->index == afterModelIndex) {
+ if (visibleItems.at(i)->index == afterModelIndex) {
markerItemIndex = i;
break;
}
@@ -2536,7 +2536,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
- FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems[i]);
+ FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems.at(i));
if (!gridItem->transitionScheduledOrRunning()) {
qreal origRowPos = gridItem->colPos();
qreal origColPos = gridItem->rowPos();
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index a168b43fd1..e36c070248 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -614,10 +614,10 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
d->pixmapChanged = true;
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
}
QRectF targetRect;
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 60e31631c0..a2b99b6395 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -355,7 +355,7 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe
if (disable2xImageLoading)
return;
- QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
// Non-local file path: @2x loading is not supported.
if (localFile.isEmpty())
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 5dadf81ce4..08886329fd 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -42,6 +42,19 @@
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.
+*/
void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickImplicitSizeItem);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 8e53e8b029..814a73d9a4 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -66,7 +66,6 @@
#include <private/qqmlopenmetaobject_p.h>
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
-#include <private/qqmlaccessors_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <private/qv4engine_p.h>
@@ -85,9 +84,8 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DEBUG
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
-#endif
+Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET)
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
{
@@ -99,7 +97,8 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
<< item->hasActiveFocus()
<< item->isFocusScope()
<< item;
- foreach (QQuickItem *child, item->childItems()) {
+ const auto childItems = item->childItems();
+ for (QQuickItem *child : childItems) {
debugFocusTree(
child,
item->isFocusScope() || !scope ? item : scope,
@@ -108,37 +107,6 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
}
}
-static void QQuickItem_parentNotifier(QObject *o, QQmlNotifier **n)
-{
- QQuickItemPrivate *d = QQuickItemPrivate::get(static_cast<QQuickItem *>(o));
- *n = &d->parentNotifier;
-}
-
-QML_PRIVATE_ACCESSOR(QQuickItem, QQuickItem *, parent, parentItem)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, x, x)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, y, y)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, width, width)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, height, height)
-
-static QQmlAccessors QQuickItem_parent = { QQuickItem_parentRead, QQuickItem_parentNotifier };
-static QQmlAccessors QQuickItem_x = { QQuickItem_xRead, 0 };
-static QQmlAccessors QQuickItem_y = { QQuickItem_yRead, 0 };
-static QQmlAccessors QQuickItem_width = { QQuickItem_widthRead, 0 };
-static QQmlAccessors QQuickItem_height = { QQuickItem_heightRead, 0 };
-
-QML_DECLARE_PROPERTIES(QQuickItem) {
- { QML_PROPERTY_NAME(parent), 0, &QQuickItem_parent },
- { QML_PROPERTY_NAME(x), 0, &QQuickItem_x },
- { QML_PROPERTY_NAME(y), 0, &QQuickItem_y },
- { QML_PROPERTY_NAME(width), 0, &QQuickItem_width },
- { QML_PROPERTY_NAME(height), 0, &QQuickItem_height }
-};
-
-void QQuickItemPrivate::registerAccessorProperties()
-{
- QML_DEFINE_PROPERTIES(QQuickItem);
-}
-
/*!
\qmltype Transform
\instantiates QQuickTransform
@@ -178,8 +146,8 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent)
QQuickTransform::~QQuickTransform()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
+ for (QQuickItem *item : qAsConst(d->items)) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
p->transforms.removeOne(this);
p->dirty(QQuickItemPrivate::Transform);
}
@@ -188,8 +156,8 @@ QQuickTransform::~QQuickTransform()
void QQuickTransform::update()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
+ for (QQuickItem *item : qAsConst(d->items)) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
p->dirty(QQuickItemPrivate::Transform);
}
}
@@ -201,9 +169,7 @@ QQuickContents::QQuickContents(QQuickItem *item)
QQuickContents::~QQuickContents()
{
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ for (QQuickItem *child : m_item->childItems()) {
QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
}
@@ -226,9 +192,8 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
} else {
qreal top = std::numeric_limits<qreal>::max();
qreal bottom = -std::numeric_limits<qreal>::max();
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ const QList<QQuickItem*> children = m_item->childItems();
+ for (QQuickItem *child : qAsConst(children)) {
qreal y = child->y();
if (y + child->height() > bottom)
bottom = y + child->height();
@@ -261,9 +226,8 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
} else {
qreal left = std::numeric_limits<qreal>::max();
qreal right = -std::numeric_limits<qreal>::max();
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ const QList<QQuickItem*> children = m_item->childItems();
+ for (QQuickItem *child : qAsConst(children)) {
qreal x = child->x();
if (x + child->width() > right)
right = x + child->width();
@@ -282,9 +246,7 @@ void QQuickContents::complete()
{
QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children);
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ for (QQuickItem *child : m_item->childItems()) {
QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
//###what about changes to visibility?
}
@@ -296,15 +258,15 @@ void QQuickContents::updateRect()
QQuickItemPrivate::get(m_item)->emitChildrenRectChanged(rectF());
}
-void QQuickContents::itemGeometryChanged(QQuickItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickContents::itemGeometryChanged(QQuickItem *changed, QQuickGeometryChange change, const QRectF &)
{
Q_UNUSED(changed)
bool wChanged = false;
bool hChanged = false;
//### we can only pass changed if the left edge has moved left, or the right edge has moved right
- if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x())
+ if (change.horizontalChange())
wChanged = calcWidth(/*changed*/);
- if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y())
+ if (change.verticalChange())
hChanged = calcHeight(/*changed*/);
if (wChanged || hChanged)
updateRect();
@@ -1339,8 +1301,7 @@ void QQuickKeysAttached::componentComplete()
#ifndef QT_NO_IM
Q_D(QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *targetItem = d->targets.at(ii);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
d->item->setFlag(QQuickItem::ItemAcceptsInputMethod);
break;
@@ -1362,11 +1323,10 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
// first process forwards
if (d->item && d->item->window()) {
d->inPress = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible()) {
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(i, event);
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
d->inPress = false;
return;
@@ -1376,7 +1336,8 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
d->inPress = false;
}
- QQuickKeyEvent ke(*event);
+ QQuickKeyEvent &ke = d->theKeyEvent;
+ ke.reset(*event);
QByteArray keySignal = keyToSignal(event->key());
if (!keySignal.isEmpty()) {
keySignal += "(QQuickKeyEvent*)";
@@ -1405,11 +1366,10 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
if (d->item && d->item->window()) {
d->inRelease = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible()) {
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(i, event);
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
d->inRelease = false;
return;
@@ -1419,7 +1379,8 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
d->inRelease = false;
}
- QQuickKeyEvent ke(*event);
+ QQuickKeyEvent &ke = d->theKeyEvent;
+ ke.reset(*event);
emit released(&ke);
event->setAccepted(ke.isAccepted());
@@ -1432,12 +1393,11 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post)
Q_D(QQuickKeysAttached);
if (post == m_processPost && d->item && !d->inIM && d->item->window()) {
d->inIM = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod)) {
- d->item->window()->sendEvent(i, event);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
- d->imeItem = i;
+ d->imeItem = targetItem;
d->inIM = false;
return;
}
@@ -1452,13 +1412,12 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) {
- //### how robust is i == d->imeItem check?
- QVariant v = i->inputMethodQuery(query);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod) && targetItem == d->imeItem) {
+ //### how robust is targetItem == d->imeItem check?
+ QVariant v = targetItem->inputMethodQuery(query);
if (v.userType() == QVariant::RectF)
- v = d->item->mapRectFromItem(i, v.toRectF()); //### cost?
+ v = d->item->mapRectFromItem(targetItem, v.toRectF()); //### cost?
return v;
}
}
@@ -1491,6 +1450,9 @@ QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj)
behavior to all child items as well. If the \c LayoutMirroring attached property has not been defined
for an item, mirroring is not enabled.
+ \note Since Qt 5.8, \c LayoutMirroring can be attached to a \l Window. In practice, it is the same as
+ attaching \c LayoutMirroring to the window's \c contentItem.
+
The following example shows mirroring in action. The \l Row below is specified as being anchored
to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally
reversed and it is now anchored to the right. Also, since items in a \l Row are positioned
@@ -1540,11 +1502,15 @@ QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj)
QQuickLayoutMirroringAttached::QQuickLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0)
{
- if (QQuickItem *item = qobject_cast<QQuickItem*>(parent)) {
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(parent))
itemPrivate = QQuickItemPrivate::get(item);
+ else if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent))
+ itemPrivate = QQuickItemPrivate::get(window->contentItem());
+
+ if (itemPrivate)
itemPrivate->extra.value().layoutDirectionAttached = this;
- } else
- qmlInfo(parent) << tr("LayoutDirection attached property only works with Items");
+ else
+ qmlInfo(parent) << tr("LayoutDirection attached property only works with Items and Windows");
}
QQuickLayoutMirroringAttached * QQuickLayoutMirroringAttached::qmlAttachedProperties(QObject *object)
@@ -1615,11 +1581,9 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
if (isMirrorImplicit)
setLayoutMirror(inherit ? inheritedLayoutMirror : false);
- for (int i = 0; i < childItems.count(); ++i) {
- if (QQuickItem *child = qmlobject_cast<QQuickItem *>(childItems.at(i))) {
- QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
- childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
- }
+ for (QQuickItem *child : qAsConst(childItems)) {
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
+ childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
}
}
@@ -2326,29 +2290,11 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent)
d->init(parent);
}
-#ifndef QT_NO_DEBUG
-static int qt_item_count = 0;
-
-static void qt_print_item_count()
-{
- qDebug("Number of leaked items: %i", qt_item_count);
- qt_item_count = -1;
-}
-#endif
-
/*!
Destroys the QQuickItem.
*/
QQuickItem::~QQuickItem()
{
-#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
- --qt_item_count;
- if (qt_item_count < 0)
- qDebug("Item destroyed after qt_print_item_count() was called.");
- }
-#endif
-
Q_D(QQuickItem);
if (d->windowRefCount > 1)
@@ -2362,7 +2308,7 @@ QQuickItem::~QQuickItem()
while (!d->childItems.isEmpty())
d->childItems.constFirst()->setParentItem(0);
- const auto listeners = d->changeListeners;
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
if (anchor)
@@ -2391,8 +2337,7 @@ QQuickItem::~QQuickItem()
remove themselves from our list of transforms when that list has already
been destroyed after ~QQuickItem() has run.
*/
- for (int ii = 0; ii < d->transforms.count(); ++ii) {
- QQuickTransform *t = d->transforms.at(ii);
+ for (QQuickTransform *t : qAsConst(d->transforms)) {
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(this);
}
@@ -2758,8 +2703,6 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
d->itemChange(ItemParentHasChanged, d->parentItem);
- d->parentNotifier.notify();
-
emit parentChanged(d->parentItem);
if (isVisible() && d->parentItem)
emit d->parentItem->visibleChildrenChanged();
@@ -2883,8 +2826,8 @@ QList<QQuickItem *> QQuickItemPrivate::paintOrderChildItems() const
// If none of the items have set Z then the paint order list is the same as
// the childItems list. This is by far the most common case.
bool haveZ = false;
- for (int i = 0; i < childItems.count(); ++i) {
- if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) {
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ if (QQuickItemPrivate::get(childItem)->z() != 0.) {
haveZ = true;
break;
}
@@ -2913,9 +2856,11 @@ void QQuickItemPrivate::addChild(QQuickItem *child)
// if the added child has a cursor and we do not currently have any children
// with cursors, bubble the notification up
- if (childPrivate->hasCursorInChild && !hasCursorInChild)
+ if (childPrivate->subtreeCursorEnabled && !subtreeCursorEnabled)
setHasCursorInChild(true);
#endif
+ if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled)
+ setHasHoverInChild(true);
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
@@ -2938,9 +2883,11 @@ void QQuickItemPrivate::removeChild(QQuickItem *child)
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
// turn it off, if nothing else is using it
- if (childPrivate->hasCursorInChild && hasCursorInChild)
+ if (childPrivate->subtreeCursorEnabled && subtreeCursorEnabled)
setHasCursorInChild(false);
#endif
+ if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled)
+ setHasHoverInChild(false);
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
@@ -2979,8 +2926,7 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c)
if (!parentItem)
QQuickWindowPrivate::get(window)->parentlessItems.insert(q);
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItem *child = childItems.at(ii);
+ for (QQuickItem *child : qAsConst(childItems)) {
QQuickItemPrivate::get(child)->refWindow(c);
}
@@ -3007,13 +2953,7 @@ void QQuickItemPrivate::derefWindow()
QQuickWindowPrivate *c = QQuickWindowPrivate::get(window);
if (polishScheduled)
c->itemsToPolish.removeOne(q);
- QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(c->itemForTouchPointId);
- while (itemTouchMapIt.hasNext()) {
- if (itemTouchMapIt.next().value() == q)
- itemTouchMapIt.remove();
- }
- if (c->mouseGrabberItem == q)
- c->mouseGrabberItem = 0;
+ c->removeGrabber(q);
#ifndef QT_NO_CURSOR
if (c->cursorItem == q) {
c->cursorItem = 0;
@@ -3038,8 +2978,7 @@ void QQuickItemPrivate::derefWindow()
paintNode = 0;
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItem *child = childItems.at(ii);
+ for (QQuickItem *child : qAsConst(childItems)) {
QQuickItemPrivate::get(child)->derefWindow();
}
@@ -3162,7 +3101,8 @@ QQuickItemPrivate::QQuickItemPrivate()
, isAccessible(false)
, culled(false)
, hasCursor(false)
- , hasCursorInChild(false)
+ , subtreeCursorEnabled(false)
+ , subtreeHoverEnabled(false)
, activeFocusOnTab(false)
, implicitAntialiasing(false)
, antialiasingValid(false)
@@ -3196,21 +3136,8 @@ QQuickItemPrivate::~QQuickItemPrivate()
void QQuickItemPrivate::init(QQuickItem *parent)
{
-#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
- ++qt_item_count;
- static bool atexit_registered = false;
- if (!atexit_registered) {
- atexit(qt_print_item_count);
- atexit_registered = true;
- }
- }
-#endif
-
Q_Q(QQuickItem);
- registerAccessorProperties();
-
baselineOffset = 0.0;
if (parent) {
@@ -3355,7 +3282,7 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
- foreach (QObject *object, quickItemPrivate->extra->resourcesList) {
+ for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) {
qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
}
@@ -3496,8 +3423,7 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty<QQuickTransform> *prop)
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- for (int ii = 0; ii < p->transforms.count(); ++ii) {
- QQuickTransform *t = p->transforms.at(ii);
+ for (QQuickTransform *t : qAsConst(p->transforms)) {
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(that);
}
@@ -3621,7 +3547,7 @@ QQuickAnchors *QQuickItemPrivate::anchors() const
void QQuickItemPrivate::siblingOrderChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::SiblingOrder) {
change.listener->itemSiblingOrderChanged(q);
@@ -3725,32 +3651,31 @@ void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
if (d->_anchors)
QQuickAnchorsPrivate::get(d->_anchors)->updateMe();
- bool xChange = (newGeometry.x() != oldGeometry.x());
- bool yChange = (newGeometry.y() != oldGeometry.y());
- bool widthChange = (newGeometry.width() != oldGeometry.width());
- bool heightChange = (newGeometry.height() != oldGeometry.height());
-
- const auto listeners = d->changeListeners;
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Geometry) {
- if (change.gTypes == QQuickItemPrivate::GeometryChange) {
- change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
- } else if ((xChange && (change.gTypes & QQuickItemPrivate::XChange)) ||
- (yChange && (change.gTypes & QQuickItemPrivate::YChange)) ||
- (widthChange && (change.gTypes & QQuickItemPrivate::WidthChange)) ||
- (heightChange && (change.gTypes & QQuickItemPrivate::HeightChange))) {
- change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
- }
+ QQuickGeometryChange change;
+ QRectF diff(newGeometry.x() - oldGeometry.x(),
+ newGeometry.y() - oldGeometry.y(),
+ newGeometry.width() - oldGeometry.width(),
+ newGeometry.height() - oldGeometry.height());
+ change.setXChange(diff.x() != 0);
+ change.setYChange(diff.y() != 0);
+ change.setWidthChange(diff.width() != 0);
+ change.setHeightChange(diff.height() != 0);
+
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &listener : listeners) {
+ if (listener.types & QQuickItemPrivate::Geometry) {
+ if (change.matches(listener.gTypes))
+ listener.listener->itemGeometryChanged(this, change, diff);
}
}
- if (xChange)
+ if (change.xChange())
emit xChanged();
- if (yChange)
+ if (change.yChange())
emit yChanged();
- if (widthChange)
+ if (change.widthChange())
emit widthChanged();
- if (heightChange)
+ if (change.heightChange())
emit heightChanged();
}
@@ -3815,6 +3740,11 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *upda
return 0;
}
+QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
+: transformNode(0)
+{
+}
+
/*!
This function is called when an item should release graphics
resources which are not already managed by the nodes returend from
@@ -3864,7 +3794,8 @@ void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *liste
changeListeners.removeOne(change);
}
-void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types)
+void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener,
+ QQuickGeometryChange types)
{
ChangeListener change(listener, types);
int index = changeListeners.indexOf(change);
@@ -3875,10 +3806,10 @@ void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListen
}
void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener,
- GeometryChangeTypes types)
+ QQuickGeometryChange types)
{
ChangeListener change(listener, types);
- if (types == NoChange) {
+ if (types.noChange()) {
changeListeners.removeOne(change);
} else {
int index = changeListeners.indexOf(change);
@@ -4147,7 +4078,8 @@ bool QQuickItem::childMouseEventFilter(QQuickItem *item, QEvent *event)
*/
void QQuickItem::windowDeactivateEvent()
{
- foreach (QQuickItem* item, childItems()) {
+ const auto children = childItems();
+ for (QQuickItem* item : children) {
item->windowDeactivateEvent();
}
}
@@ -4290,7 +4222,7 @@ void QQuickItem::setBaselineOffset(qreal offset)
d->baselineOffset = offset;
- const auto listeners = d->changeListeners;
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Geometry) {
QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
@@ -4862,14 +4794,16 @@ void QQuickItem::componentComplete()
QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete();
}
- if (d->extra.isAllocated() && d->extra->layer)
- d->extra->layer->componentComplete();
+ if (d->extra.isAllocated()) {
+ if (d->extra->layer)
+ d->extra->layer->componentComplete();
- if (d->extra.isAllocated() && d->extra->keyHandler)
- d->extra->keyHandler->componentComplete();
+ if (d->extra->keyHandler)
+ d->extra->keyHandler->componentComplete();
- if (d->extra.isAllocated() && d->extra->contents)
- d->extra->contents->complete();
+ if (d->extra->contents)
+ d->extra->contents->complete();
+ }
if (d->window && d->dirtyAttributes) {
d->addToDirtyList();
@@ -5763,15 +5697,13 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
if (window) {
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- if (windowPriv->mouseGrabberItem == q)
- q->ungrabMouse();
- if (!effectiveVisible)
- q->ungrabTouchPoints();
+ windowPriv->removeGrabber(q);
}
bool childVisibilityChanged = false;
- for (int ii = 0; ii < childItems.count(); ++ii)
- childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ childVisibilityChanged |= QQuickItemPrivate::get(childItem)->setEffectiveVisibleRecur(newEffectiveVisible);
+ }
itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible);
#ifndef QT_NO_ACCESSIBILITY
@@ -5813,18 +5745,15 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
if (window) {
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- if (windowPriv->mouseGrabberItem == q)
- q->ungrabMouse();
- if (!effectiveEnable)
- q->ungrabTouchPoints();
+ windowPriv->removeGrabber(q);
if (scope && !effectiveEnable && activeFocus) {
windowPriv->clearFocusInScope(
scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
}
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ QQuickItemPrivate::get(childItem)->setEffectiveEnableRecur(
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
@@ -5968,7 +5897,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Children) {
change.listener->itemChildAdded(q, data.item);
@@ -5978,7 +5907,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemChildRemovedChange: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Children) {
change.listener->itemChildRemoved(q, data.item);
@@ -5991,7 +5920,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemVisibleHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Visibility) {
change.listener->itemVisibilityChanged(q);
@@ -6001,7 +5930,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Parent) {
change.listener->itemParentChanged(q, data.item);
@@ -6011,7 +5940,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemOpacityHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Opacity) {
change.listener->itemOpacityChanged(q);
@@ -6024,7 +5953,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemRotationHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Rotation) {
change.listener->itemRotationChanged(q);
@@ -6386,7 +6315,7 @@ void QQuickItem::resetWidth()
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::ImplicitWidth) {
change.listener->itemImplicitWidthChanged(q);
@@ -6550,7 +6479,7 @@ void QQuickItem::resetHeight()
void QQuickItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::ImplicitHeight) {
change.listener->itemImplicitHeightChanged(q);
@@ -6977,7 +6906,7 @@ void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
}
/*!
- Returns whether mouse events of this item's children should be filtered
+ Returns whether mouse and touch events of this item's children should be filtered
through this item.
\sa setFiltersChildMouseEvents(), childMouseEventFilter()
@@ -6989,7 +6918,7 @@ bool QQuickItem::filtersChildMouseEvents() const
}
/*!
- Sets whether mouse events of this item's children should be filtered
+ Sets whether mouse and touch events of this item's children should be filtered
through this item.
If \a filter is true, childMouseEventFilter() will be called when
@@ -7040,6 +6969,7 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
{
Q_D(QQuickItem);
d->hoverEnabled = enabled;
+ d->setHasHoverInChild(enabled);
}
void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
@@ -7048,17 +6978,18 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
Q_Q(QQuickItem);
// if we're asked to turn it off (because of an unsetcursor call, or a node
- // removal) then we should check our children and make sure it's really ok
- // to turn it off.
- if (!hasCursor && hasCursorInChild) {
- foreach (QQuickItem *otherChild, childItems) {
+ // removal) then we should make sure it's really ok to turn it off.
+ if (!hasCursor && subtreeCursorEnabled) {
+ if (hasCursor)
+ return; // nope! sorry, I have a cursor myself
+ for (QQuickItem *otherChild : qAsConst(childItems)) {
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
- if (otherChildPrivate->hasCursorInChild)
+ if (otherChildPrivate->subtreeCursorEnabled || otherChildPrivate->hasCursor)
return; // nope! sorry, something else wants it kept on.
}
}
- hasCursorInChild = hasCursor;
+ subtreeCursorEnabled = hasCursor;
QQuickItem *parent = q->parentItem();
if (parent) {
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
@@ -7067,6 +6998,31 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
#endif
}
+void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
+{
+ Q_Q(QQuickItem);
+
+ // if we're asked to turn it off (because of a setAcceptHoverEvents call, or a node
+ // removal) then we should make sure it's really ok to turn it off.
+ if (!hasHover && subtreeHoverEnabled) {
+ if (hoverEnabled)
+ return; // nope! sorry, I need hover myself
+ for (QQuickItem *otherChild : qAsConst(childItems)) {
+ QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
+ if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
+ return; // nope! sorry, something else wants it kept on.
+ }
+ }
+
+ qCDebug(DBG_HOVER_TRACE) << q << subtreeHoverEnabled << "->" << hasHover;
+ subtreeHoverEnabled = hasHover;
+ QQuickItem *parent = q->parentItem();
+ if (parent) {
+ QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
+ parentPrivate->setHasHoverInChild(hasHover);
+ }
+}
+
#ifndef QT_NO_CURSOR
/*!
@@ -7157,6 +7113,11 @@ void QQuickItem::unsetCursor()
Grabs the mouse input.
This item will receive all mouse events until ungrabMouse() is called.
+ Usually this function should not be called, since accepting for example
+ a mouse press event makes sure that the following events are delivered
+ to the item.
+ If an item wants to take over mouse events from the current receiver,
+ it needs to call this function.
\warning This function should be used with caution.
*/
@@ -7171,6 +7132,12 @@ void QQuickItem::grabMouse()
/*!
Releases the mouse grab following a call to grabMouse().
+
+ Note that this function should only be called when the item wants
+ to stop handling further events. There is no need to call this function
+ after a release or cancel event since no future events will be received
+ in any case. No move or release events will be delivered after this
+ function was called.
*/
void QQuickItem::ungrabMouse()
{
@@ -7178,15 +7145,7 @@ void QQuickItem::ungrabMouse()
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- if (windowPriv->mouseGrabberItem != this) {
- qWarning("QQuickItem::ungrabMouse(): Item is not the mouse grabber.");
- return;
- }
-
- windowPriv->mouseGrabberItem = 0;
-
- QEvent ev(QEvent::UngrabMouse);
- d->window->sendEvent(this, &ev);
+ windowPriv->removeGrabber(this, true, false);
}
@@ -7239,31 +7198,16 @@ void QQuickItem::grabTouchPoints(const QVector<int> &ids)
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
-
- QSet<QQuickItem*> ungrab;
- for (int i = 0; i < ids.count(); ++i) {
- QQuickItem *oldGrabber = windowPriv->itemForTouchPointId.value(ids.at(i));
- if (oldGrabber == this)
- return;
-
- windowPriv->itemForTouchPointId[ids.at(i)] = this;
- if (oldGrabber)
- ungrab.insert(oldGrabber);
-
- QQuickItem *mouseGrabber = windowPriv->mouseGrabberItem;
- if (windowPriv->touchMouseId == ids.at(i) && mouseGrabber && mouseGrabber != this) {
- windowPriv->mouseGrabberItem = 0;
- QEvent ev(QEvent::UngrabMouse);
- d->window->sendEvent(mouseGrabber, &ev);
- }
- }
- foreach (QQuickItem *oldGrabber, ungrab)
- oldGrabber->touchUngrabEvent();
+ windowPriv->grabTouchPoints(this, ids);
}
/*!
Ungrabs the touch points owned by this item.
+ \note there is hardly any reason to call this function. It should only be
+ called when an item does not want to receive any further events, so no
+ move or release events will be delivered after calling this function.
+
\sa grabTouchPoints()
*/
void QQuickItem::ungrabTouchPoints()
@@ -7272,14 +7216,7 @@ void QQuickItem::ungrabTouchPoints()
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
-
- QMutableHashIterator<int, QQuickItem*> i(windowPriv->itemForTouchPointId);
- while (i.hasNext()) {
- i.next();
- if (i.value() == this)
- i.remove();
- }
- touchUngrabEvent();
+ windowPriv->removeGrabber(this, false, true);
}
/*!
@@ -7338,7 +7275,9 @@ void QQuickItem::setKeepTouchGrab(bool keep)
bool QQuickItem::contains(const QPointF &point) const
{
Q_D(const QQuickItem);
- return QRectF(0, 0, d->width, d->height).contains(point);
+ qreal x = point.x();
+ qreal y = point.y();
+ return x >= 0 && y >= 0 && x <= d->width && y <= d->height;
}
/*!
@@ -8141,7 +8080,7 @@ void QQuickItemLayer::itemOpacityChanged(QQuickItem *item)
updateOpacity();
}
-void QQuickItemLayer::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &)
+void QQuickItemLayer::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
{
updateGeometry();
}
@@ -8252,7 +8191,6 @@ QAccessible::Role QQuickItemPrivate::accessibleRole() const
namespace QV4 {
namespace Heap {
struct QQuickItemWrapper : public QObjectWrapper {
- QQuickItemWrapper(QQuickItem *item) : QObjectWrapper(item) {}
};
}
}
@@ -8267,7 +8205,7 @@ DEFINE_OBJECT_VTABLE(QQuickItemWrapper);
void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e)
{
QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
- if (QQuickItem *item = static_cast<QQuickItem*>(This->object.data())) {
+ if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
foreach (QQuickItem *child, QQuickItemPrivate::get(item)->childItems)
QV4::QObjectWrapper::markWrapper(child, e);
}
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c4ee48fdbd..510d46739a 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -148,7 +148,6 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickItemLayer *layer READ layer DESIGNABLE false CONSTANT FINAL)
Q_CLASSINFO("DefaultProperty", "data")
- Q_CLASSINFO("qt_HasQmlAccessors", "true")
Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QV4::ExecutionEngine*)")
public:
@@ -370,7 +369,6 @@ Q_SIGNALS:
void clipChanged(bool);
Q_REVISION(1) void windowChanged(QQuickWindow* window);
- // XXX todo
void childrenChanged();
void opacityChanged();
void enabledChanged();
@@ -456,7 +454,6 @@ private:
Q_DECLARE_PRIVATE(QQuickItem)
};
-// XXX todo
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItem::Flags)
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 8cda5f89c6..49a674f2c6 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -56,6 +56,7 @@
#include "qquickanchors_p.h"
#include "qquickanchors_p_p.h"
#include "qquickitemchangelistener_p.h"
+#include "qquickevents_p_p.h"
#include "qquickwindow_p.h"
@@ -76,7 +77,6 @@
#include <QtCore/qelapsedtimer.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
-#include <QtQuick/private/qquickshadereffect_p.h>
QT_BEGIN_NAMESPACE
@@ -98,7 +98,7 @@ public:
void complete();
protected:
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE;
void itemChildAdded(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
void itemChildRemoved(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
@@ -188,7 +188,7 @@ public:
QQuickShaderEffectSource *effectSource() const { return m_effectSource; }
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void itemOpacityChanged(QQuickItem *) Q_DECL_OVERRIDE;
void itemParentChanged(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
void itemSiblingOrderChanged(QQuickItem *) Q_DECL_OVERRIDE;
@@ -244,8 +244,6 @@ public:
static QQuickItemPrivate* get(QQuickItem *item) { return item->d_func(); }
static const QQuickItemPrivate* get(const QQuickItem *item) { return item->d_func(); }
- static void registerAccessorProperties();
-
QQuickItemPrivate();
~QQuickItemPrivate();
void init(QQuickItem *parent);
@@ -319,24 +317,12 @@ public:
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
- enum GeometryChangeType {
- NoChange = 0,
- XChange = 0x01,
- YChange = 0x02,
- WidthChange = 0x04,
- HeightChange = 0x08,
- SizeChange = WidthChange | HeightChange,
- GeometryChange = XChange | YChange | SizeChange
- };
-
- Q_DECLARE_FLAGS(GeometryChangeTypes, GeometryChangeType)
-
struct ChangeListener {
- ChangeListener(QQuickItemChangeListener *l = Q_NULLPTR, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(GeometryChange) {}
- ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::GeometryChangeTypes gt) : listener(l), types(Geometry), gTypes(gt) {}
+ ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {}
+ ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {}
QQuickItemChangeListener *listener;
QQuickItemPrivate::ChangeTypes types;
- QQuickItemPrivate::GeometryChangeTypes gTypes; //NOTE: not used for ==
+ QQuickGeometryChange gTypes; //NOTE: not used for ==
bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
@@ -390,8 +376,8 @@ public:
void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types);
void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types);
- void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
- void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
+ void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types);
+ void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types);
QQuickStateGroup *_states();
QQuickStateGroup *_stateGroup;
@@ -427,8 +413,9 @@ public:
bool isAccessible:1;
bool culled:1;
bool hasCursor:1;
- bool hasCursorInChild:1;
+ bool subtreeCursorEnabled:1;
// Bit 32
+ bool subtreeHoverEnabled:1;
bool activeFocusOnTab:1;
bool implicitAntialiasing:1;
bool antialiasingValid:1;
@@ -488,7 +475,6 @@ public:
inline QSGRenderContext *sceneGraphRenderContext() const;
QQuickItem *parentItem;
- QQmlNotifier parentNotifier;
QList<QQuickItem *> childItems;
mutable QList<QQuickItem *> *sortedChildItems;
@@ -606,6 +592,9 @@ public:
virtual void mirrorChange() {}
void setHasCursorInChild(bool hasCursor);
+ void setHasHoverInChild(bool hasHover);
+
+ virtual void updatePolish() { }
};
/*
@@ -773,6 +762,7 @@ public:
QQuickItem *imeItem;
QList<QQuickItem *> targets;
QQuickItem *item;
+ QQuickKeyEvent theKeyEvent;
};
class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 027c07ec07..5c0caf5ca2 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -339,9 +339,9 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
qreal w = target->width();
qreal h = target->height();
if (pc->widthIsSet() && i < actions.size() - 1)
- w = actions[++i].toValue.toReal();
+ w = actions.at(++i).toValue.toReal();
if (pc->heightIsSet() && i < actions.size() - 1)
- h = actions[++i].toValue.toReal();
+ h = actions.at(++i).toValue.toReal();
const QPointF &transformOrigin
= d->computeTransformOrigin(target->transformOrigin(), w,h);
qreal tempxt = transformOrigin.x();
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 6e3ef25506..19ff73056b 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -58,12 +58,69 @@ QT_BEGIN_NAMESPACE
class QRectF;
class QQuickItem;
class QQuickAnchorsPrivate;
+
+class QQuickGeometryChange
+{
+public:
+ enum Kind: int {
+ Nothing = 0x00,
+ X = 0x01,
+ Y = 0x02,
+ Width = 0x04,
+ Height = 0x08,
+
+ Size = Width | Height,
+ All = X | Y | Size
+ };
+
+ QQuickGeometryChange(int change = Nothing)
+ : kind(change)
+ {}
+
+ bool noChange() const { return kind == Nothing; }
+ bool anyChange() const { return !noChange(); }
+
+ bool xChange() const { return kind & X; }
+ bool yChange() const { return kind & Y; }
+ bool widthChange() const { return kind & Width; }
+ bool heightChange() const { return kind & Height; }
+
+ bool positionChange() const { return xChange() || yChange(); }
+ bool sizeChange() const { return widthChange() || heightChange(); }
+
+ bool horizontalChange() const { return xChange() || widthChange(); }
+ bool verticalChange() const { return yChange() || heightChange(); }
+
+ void setXChange(bool enabled) { set(X, enabled); }
+ void setYChange(bool enabled) { set(Y, enabled); }
+ void setWidthChange(bool enabled) { set(Width, enabled); }
+ void setHeightChange(bool enabled) { set(Height, enabled); }
+ void setSizeChange(bool enabled) { set(Size, enabled); }
+ void setAllChanged(bool enabled) { set(All, enabled); }
+ void setHorizontalChange(bool enabled) { set(X | Width, enabled); }
+ void setVerticalChange(bool enabled) { set(Y | Height, enabled); }
+
+ void set(int bits, bool enabled)
+ {
+ if (enabled) {
+ kind |= bits;
+ } else {
+ kind &= ~bits;
+ }
+ }
+
+ bool matches(QQuickGeometryChange other) const { return kind & other.kind; }
+
+private:
+ int kind;
+};
+
class QQuickItemChangeListener
{
public:
virtual ~QQuickItemChangeListener() {}
- virtual void itemGeometryChanged(QQuickItem *, const QRectF & /* new */, const QRectF & /* old */ ) {}
+ virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* diff */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
virtual void itemVisibilityChanged(QQuickItem *) {}
virtual void itemOpacityChanged(QQuickItem *) {}
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index f8327a1c6e..0db5323863 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -240,8 +240,7 @@ void QQuickItemGrabResult::render()
return;
d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
- QSGContext *sg = QSGRenderContext::from(QOpenGLContext::currentContext())->sceneGraphContext();
- const QSize minSize = sg->minimumFBOSize();
+ const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->minimumFBOSize();
d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()),
qMax(minSize.height(), d->textureSize.height())));
d->texture->scheduleUpdate();
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 08d95119d7..1bc1aebe4f 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -41,7 +41,6 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
-#include "qquickitemgrabresult.h"
#include "qquickevents_p_p.h"
#include "qquickrectangle_p.h"
#include "qquickfocusscope_p.h"
@@ -70,18 +69,23 @@
#include "qquicktranslate_p.h"
#include "qquickstateoperations_p.h"
#include "qquickitemanimation_p.h"
-#include <private/qquickshadereffect_p.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
//#include <private/qquickpincharea_p.h>
#include <QtQuick/private/qquickcanvasitem_p.h>
#include <QtQuick/private/qquickcontext2d_p.h>
+#include "qquickitemgrabresult.h"
#include "qquicksprite_p.h"
#include "qquickspritesequence_p.h"
#include "qquickanimatedsprite_p.h"
+#ifndef QT_NO_OPENGL
+# include "qquickopenglinfo_p.h"
+#endif
+#include "qquickgraphicsinfo_p.h"
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
#include "qquickdrag_p.h"
#include "qquickdroparea_p.h"
#include "qquickmultipointtoucharea_p.h"
-#include "qquickopenglinfo_p.h"
#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
@@ -129,7 +133,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
{
QQmlPrivate::RegisterAutoParent autoparent = { 0, &qquickitem_autoParent };
QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
- QQuickItemPrivate::registerAccessorProperties();
#ifdef QT_NO_MOVIE
qmlRegisterTypeNotAvailable(uri,major,minor,"AnimatedImage", QCoreApplication::translate("QQuickAnimatedImage","Qt was built without support for QMovie"));
@@ -178,7 +181,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput");
qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
-
qmlRegisterType<QQuickItemGrabResult>();
qmlRegisterType<QQuickItemLayer>();
qmlRegisterType<QQuickAnchors>();
@@ -207,10 +209,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPinch>(uri,major,minor,"Pinch");
qmlRegisterType<QQuickPinchEvent>();
- qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterType<QQuickShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
qmlRegisterUncreatableType<QQuickShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh."));
qmlRegisterType<QQuickGridMesh>("QtQuick", 2, 0, "GridMesh");
+ qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem"));
@@ -262,8 +264,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView");
qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
- qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#ifndef QT_NO_OPENGL
+ qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#endif
qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea");
qmlRegisterType<QQuickImage, 2>(uri, 2, 5,"Image");
qmlRegisterType<QQuickMouseArea, 2>(uri, 2, 5, "MouseArea");
@@ -288,6 +292,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathView, 7>(uri, 2, 7, "PathView");
qmlRegisterUncreatableType<QQuickMouseEvent, 7>(uri, 2, 7, nullptr, QQuickMouseEvent::tr("MouseEvent is only available within handlers in MouseArea"));
+
+ qmlRegisterUncreatableType<QQuickGraphicsInfo>(uri, 2, 8,"GraphicsInfo", QQuickGraphicsInfo::tr("GraphicsInfo is only available via attached properties"));
+ qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh");
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index aff03b7539..e017d6564a 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -181,7 +181,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet)
int moveId = -1;
int moveOffset = 0;
- foreach (const QQmlChangeSet::Change &r, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &r : changeSet.removes()) {
itemCount -= r.count;
if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
newCurrentIndex -= r.count;
@@ -200,7 +200,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet)
currentChanged = true;
}
}
- foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
if (moveId == -1) {
if (itemCount && newCurrentIndex >= i.index) {
newCurrentIndex += i.count;
@@ -1199,16 +1199,17 @@ void QQuickItemViewPrivate::showVisibleItems() const
{
qDebug() << "Visible items:";
for (int i = 0; i < visibleItems.count(); ++i) {
- qDebug() << "\t" << visibleItems[i]->index
- << visibleItems[i]->item->objectName()
- << visibleItems[i]->position();
+ qDebug() << "\t" << visibleItems.at(i)->index
+ << visibleItems.at(i)->item->objectName()
+ << visibleItems.at(i)->position();
}
}
-void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickItemView);
- QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+ QQuickFlickablePrivate::itemGeometryChanged(item, change, diff);
if (!q->isComponentComplete())
return;
@@ -1621,12 +1622,10 @@ qreal QQuickItemViewPrivate::contentStartOffset() const
int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
{
- if (visibleItems.count()) {
- int i = visibleItems.count() - 1;
- while (i > 0 && visibleItems.at(i)->index == -1)
- --i;
- if (visibleItems.at(i)->index != -1)
- return visibleItems.at(i)->index;
+ for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
+ auto item = *it;
+ if (item->index != -1)
+ return item->index;
}
return defaultValue;
}
@@ -1657,9 +1656,10 @@ FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
int QQuickItemViewPrivate::findLastIndexInView() const
{
const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
- for (int i=visibleItems.count() - 1; i>=0; i--) {
- if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
- return visibleItems.at(i)->index;
+ for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
+ auto item = *it;
+ if (item->index != -1 && item->position() <= viewEndPos)
+ return item->index;
}
return -1;
}
@@ -1997,7 +1997,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
prevViewPos = prevFirstVisible->position();
prevFirstVisibleIndex = prevFirstVisible->index;
}
- qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
+ qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0;
totalInsertionResult->visiblePos = prevViewPos;
totalRemovalResult->visiblePos = prevViewPos;
@@ -2076,13 +2076,13 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
// can transition it from this "original" position to its new position in the view
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
for (int i=0; i<movingIntoView.count(); i++) {
- int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
+ int fromIndex = findMoveKeyIndex(movingIntoView.at(i).moveKey, removals);
if (fromIndex >= 0) {
if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
- repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
+ repositionItemAt(movingIntoView.at(i).item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
else
- repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
- movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
+ repositionItemAt(movingIntoView.at(i).item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
+ movingIntoView.at(i).item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
}
}
}
@@ -2128,11 +2128,11 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
Q_Q(QQuickItemView);
bool visibleAffected = false;
- if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
- if (removal.index > visibleItems.last()->index)
+ if (visibleItems.count() && removal.index + removal.count > visibleItems.constLast()->index) {
+ if (removal.index > visibleItems.constLast()->index)
removeResult->countChangeAfterVisibleItems += removal.count;
else
- removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
+ removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index);
}
QList<FxViewItem*>::Iterator it = visibleItems.begin();
@@ -2222,10 +2222,11 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
qreal moveBackwardsBy = 0;
// shift visibleItems.first() relative to the number of added/removed items
- if (visibleItems.first()->position() > prevViewPos) {
+ const auto pos = visibleItems.constFirst()->position();
+ if (pos > prevViewPos) {
moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
- } else if (visibleItems.first()->position() < prevViewPos) {
+ } else if (pos < prevViewPos) {
moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
}
@@ -2303,7 +2304,7 @@ bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, co
void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
{
for (int i=0; i<releasePendingTransition.count(); i++) {
- if (releasePendingTransition[i]->transitionableItem == item) {
+ if (releasePendingTransition.at(i)->transitionableItem == item) {
releaseItem(releasePendingTransition.takeAt(i));
return;
}
@@ -2323,8 +2324,8 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
return 0;
for (int i=0; i<releasePendingTransition.count(); i++) {
- if (releasePendingTransition[i]->index == modelIndex
- && !releasePendingTransition[i]->isPendingRemoval()) {
+ if (releasePendingTransition.at(i)->index == modelIndex
+ && !releasePendingTransition.at(i)->isPendingRemoval()) {
releasePendingTransition[i]->releaseAfterTransition = false;
return releasePendingTransition.takeAt(i);
}
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 5e104cf208..0893d6e749 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -378,7 +378,7 @@ protected:
virtual void updateSectionCriteria() {}
virtual void updateSections() {}
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
};
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index a98a5c8f56..47efecdb2d 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -131,7 +131,7 @@ public:
void updateAverage();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE;
void fixupPosition() Q_DECL_OVERRIDE;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) Q_DECL_OVERRIDE;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
@@ -400,7 +400,7 @@ FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
++idx;
}
if (lastIndex == modelIndex-1)
- return visibleItems.last();
+ return visibleItems.constLast();
return 0;
}
@@ -752,7 +752,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
}
}
- while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
+ while (visibleItems.count() > 1 && (item = visibleItems.constLast()) && item->position() > bufferTo) {
if (item->attached->delayRemove())
break;
qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << (QObject *)(item->item);
@@ -839,7 +839,7 @@ void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
{
- FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
+ FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.constFirst());
item->setPosition(pos);
}
@@ -848,12 +848,12 @@ void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int
if (!visibleItems.count())
return;
qreal diff = forwards - backwards;
- static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
+ static_cast<FxListItemSG*>(visibleItems.constFirst())->setPosition(visibleItems.constFirst()->position() + diff);
}
void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
{
- if (item != visibleItems.first())
+ if (item != visibleItems.constFirst())
QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult);
}
@@ -1270,7 +1270,7 @@ void QQuickListViewPrivate::initializeCurrentItem()
if (!actualItem) {
if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
// We can calculate exact postion in this case
- listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
+ listItem->setPosition(visibleItems.constFirst()->position() - currentItem->size() - spacing);
} else {
// Create current item now and position as best we can.
// Its position will be corrected when it becomes visible.
@@ -1400,10 +1400,12 @@ bool QQuickListViewPrivate::hasStickyFooter() const
return footer && footerPositioning != QQuickListView::InlineFooter;
}
-void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickListView);
- QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+
+ QQuickItemViewPrivate::itemGeometryChanged(item, change, diff);
if (!q->isComponentComplete())
return;
@@ -1417,29 +1419,31 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &
}
if (item != contentItem && (!highlight || item != highlight->item)) {
- if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
- || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
+ if ((orient == QQuickListView::Vertical && change.heightChange())
+ || (orient == QQuickListView::Horizontal && change.widthChange())) {
// if visibleItems.first() has resized, adjust its pos since it is used to
// position all subsequent items
- if (visibleItems.count() && item == visibleItems.first()->item) {
- FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
+ if (visibleItems.count() && item == visibleItems.constFirst()->item) {
+ FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst());
+ const QRectF oldGeometry(item->x() - diff.x(),
+ item->y() - diff.y(),
+ item->width() - diff.width(),
+ item->height() - diff.height());
if (listItem->transitionScheduledOrRunning())
return;
if (orient == QQuickListView::Vertical) {
const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height();
- qreal diff = newGeometry.height() - oldGeometry.height();
if (verticalLayoutDirection == QQuickListView::TopToBottom && oldItemEndPosition < q->contentY())
- listItem->setPosition(listItem->position() - diff, true);
+ listItem->setPosition(listItem->position() - diff.height(), true);
else if (verticalLayoutDirection == QQuickListView::BottomToTop && oldItemEndPosition > q->contentY())
- listItem->setPosition(listItem->position() + diff, true);
+ listItem->setPosition(listItem->position() + diff.height(), true);
} else {
const qreal oldItemEndPosition = q->effectiveLayoutDirection() == Qt::RightToLeft ? -oldGeometry.x() : oldGeometry.x() + oldGeometry.width();
- qreal diff = newGeometry.width() - oldGeometry.width();
if (q->effectiveLayoutDirection() == Qt::LeftToRight && oldItemEndPosition < q->contentX())
- listItem->setPosition(listItem->position() - diff, true);
+ listItem->setPosition(listItem->position() - diff.width(), true);
else if (q->effectiveLayoutDirection() == Qt::RightToLeft && oldItemEndPosition > q->contentX())
- listItem->setPosition(listItem->position() + diff, true);
+ listItem->setPosition(listItem->position() + diff.width(), true);
}
}
forceLayoutPolish();
@@ -3107,7 +3111,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
int i = visibleItems.count() - 1;
while (i > 0 && visibleItems.at(i)->index == -1)
--i;
- if (i == 0 && visibleItems.first()->index == -1) {
+ if (i == 0 && visibleItems.constFirst()->index == -1) {
// there are no visible items except items marked for removal
index = visibleItems.count();
} else if (visibleItems.at(i)->index + 1 == modelIndex
@@ -3132,7 +3136,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
qreal pos = 0;
if (visibleItems.count()) {
pos = index < visibleItems.count() ? visibleItems.at(index)->position()
- : visibleItems.last()->endPosition()+spacing;
+ : visibleItems.constLast()->endPosition() + spacing;
}
// Update the indexes of the following visible items.
@@ -3267,7 +3271,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
int markerItemIndex = -1;
for (int i=0; i<visibleItems.count(); i++) {
- if (visibleItems[i]->index == afterModelIndex) {
+ if (visibleItems.at(i)->index == afterModelIndex) {
markerItemIndex = i;
break;
}
@@ -3280,7 +3284,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
- (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
- FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
+ FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
if (!listItem->transitionScheduledOrRunning()) {
qreal pos = listItem->position();
listItem->setPosition(pos - sizeRemoved);
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index b8ab38bbba..5a5e8a55fe 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -53,11 +53,13 @@
#include "qquickitemview_p.h"
+#include <private/qtquickglobal_p.h>
+
QT_BEGIN_NAMESPACE
class QQuickListView;
class QQuickListViewPrivate;
-class Q_AUTOTEST_EXPORT QQuickViewSection : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickViewSection : public QObject
{
Q_OBJECT
Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
@@ -103,7 +105,7 @@ private:
class QQmlInstanceModel;
class QQuickListViewAttached;
-class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView
+class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickListView)
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 63c9558d7a..9aea9c50df 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -65,11 +65,12 @@ QQuickLoaderPrivate::~QQuickLoaderPrivate()
disposeInitialPropertyValues();
}
-void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change
+ , const QRectF &diff)
{
if (resizeItem == item)
_q_updateSize(false);
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 9ef89a74d6..fcccbfe4f5 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -84,7 +84,7 @@ public:
QQuickLoaderPrivate();
~QQuickLoaderPrivate();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE;
void itemImplicitWidthChanged(QQuickItem *) Q_DECL_OVERRIDE;
void itemImplicitHeightChanged(QQuickItem *) Q_DECL_OVERRIDE;
void clear();
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 609666d32b..10e6afad33 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -40,7 +40,6 @@
#include "qquickmousearea_p.h"
#include "qquickmousearea_p_p.h"
#include "qquickwindow.h"
-#include "qquickevents_p_p.h"
#include "qquickdrag_p.h"
#include <private/qqmldata_p.h>
@@ -55,6 +54,8 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
+
QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
: enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false),
moved(false), stealMouse(false), doubleClick(false), preventStealing(false),
@@ -404,8 +405,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
/*!
\qmlsignal QtQuick::MouseArea::canceled()
- This signal is emitted when mouse events have been canceled, either because an event was not accepted, or
- because another item stole the mouse event handling.
+ This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling.
This signal is for advanced use: it is useful when there is more than one MouseArea
that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
@@ -765,7 +765,8 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
}
#endif
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
me.setSource(event->source());
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
@@ -807,7 +808,8 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
Q_D(QQuickMouseArea);
if (d->enabled) {
d->saveEvent(event);
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
me.setSource(event->source());
me.setAccepted(d->isDoubleClickConnected());
emit this->doubleClicked(&me);
@@ -827,7 +829,8 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
d->lastPos = event->posF();
d->lastModifiers = event->modifiers();
setHovered(true);
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -840,10 +843,11 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
Q_D(QQuickMouseArea);
if (!d->enabled && !d->pressed) {
QQuickItem::hoverMoveEvent(event);
- } else {
+ } else if (d->lastPos != event->posF()) {
d->lastPos = event->posF();
d->lastModifiers = event->modifiers();
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -870,8 +874,9 @@ void QQuickMouseArea::wheelEvent(QWheelEvent *event)
return;
}
- QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
- event->pixelDelta(), event->buttons(), event->modifiers());
+ QQuickWheelEvent &we = d->quickWheelEvent;
+ we.reset(event->posF().x(), event->posF().y(), event->angleDelta(), event->pixelDelta(),
+ event->buttons(), event->modifiers(), event->inverted());
we.setAccepted(d->isWheelConnected());
emit wheel(&we);
if (!we.isAccepted())
@@ -1004,7 +1009,8 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event)
#endif
if (d->pressed && dragged == false && d->hovered == true) {
d->longPress = true;
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
me.setSource(Qt::MouseEventSynthesizedByQt);
me.setAccepted(d->isPressAndHoldConnected());
emit pressAndHold(&me);
@@ -1085,8 +1091,7 @@ void QQuickMouseArea::setHoverEnabled(bool h)
\qmlproperty bool QtQuick::MouseArea::containsMouse
This property holds whether the mouse is currently inside the mouse area.
- \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
- In addition, if hoverEnabled is false, containsMouse will only be valid
+ \warning If hoverEnabled is false, containsMouse will only be valid
when the mouse is pressed while the mouse cursor is inside the MouseArea.
*/
bool QQuickMouseArea::hovered() const
@@ -1127,6 +1132,7 @@ void QQuickMouseArea::setHovered(bool h)
{
Q_D(QQuickMouseArea);
if (d->hovered != h) {
+ qCDebug(DBG_HOVER_TRACE) << this << d->hovered << "->" << h;
d->hovered = h;
emit hoveredChanged();
d->hovered ? emit entered() : emit exited();
@@ -1182,7 +1188,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
Qt::MouseButtons oldPressed = d->pressed;
if (wasPressed != p) {
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
me.setSource(source);
if (p) {
d->pressed |= button;
@@ -1192,6 +1199,11 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
+
+ if (!me.isAccepted()) {
+ d->pressed = Qt::NoButton;
+ }
+
if (!oldPressed) {
emit pressedChanged();
emit containsPressChanged();
@@ -1348,8 +1360,8 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (!qmlVisualTouchDebugging())
return 0;
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(QColor(255, 0, 0, 50));
diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h
index dc00ffe52c..b59e02910f 100644
--- a/src/quick/items/qquickmousearea_p_p.h
+++ b/src/quick/items/qquickmousearea_p_p.h
@@ -52,6 +52,7 @@
//
#include "qquickitem_p.h"
+#include "qquickevents_p_p.h"
#include <QtGui/qevent.h>
#include <QtCore/qbasictimer.h>
@@ -108,6 +109,8 @@ public:
#ifndef QT_NO_CURSOR
QCursor *cursor;
#endif
+ QQuickMouseEvent quickMouseEvent;
+ QQuickWheelEvent quickWheelEvent;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 9d8e7aedd4..706980cd13 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -362,7 +362,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea()
{
clearTouchLists();
- foreach (QObject *obj, _touchPoints) {
+ for (QObject *obj : qAsConst(_touchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined())
delete dtp;
@@ -448,20 +448,12 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event)
}
}
updateTouchData(event);
- if (event->type() == QEvent::TouchEnd) {
- //TODO: move to window
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
- }
+ if (event->type() == QEvent::TouchEnd)
+ ungrab();
break;
}
case QEvent::TouchCancel:
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
+ ungrab();
break;
default:
QQuickItem::touchEvent(event);
@@ -478,8 +470,10 @@ void QQuickMultiPointTouchArea::grabGesture()
QVector<int> ids;
ids.reserve(_touchPoints.size());
- for (auto it = _touchPoints.keyBegin(), end = _touchPoints.keyEnd(); it != end; ++it)
- ids.append(*it);
+ for (auto it = _touchPoints.keyBegin(), end = _touchPoints.keyEnd(); it != end; ++it) {
+ if (*it != -1) // -1 might be the mouse-point, but we already grabbed the mouse above.
+ ids.append(*it);
+ }
grabTouchPoints(ids);
setKeepTouchGrab(true);
}
@@ -532,7 +526,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
}
int numTouchPoints = touchPoints.count();
//always remove released touches, and make sure we handle all releases before adds.
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
Qt::TouchPointState touchPointState = p.state();
int id = p.id();
if (touchPointState & Qt::TouchPointReleased) {
@@ -547,7 +541,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
}
}
if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
Qt::TouchPointState touchPointState = p.state();
int id = p.id();
if (touchPointState & Qt::TouchPointReleased) {
@@ -557,13 +551,13 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
addTouchPoint(&p);
started = true;
} else if (touchPointState & Qt::TouchPointMoved) {
- QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
+ QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
_movedTouchPoints.append(dtp);
updateTouchPoint(dtp,&p);
moved = true;
} else {
- QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
+ QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
updateTouchPoint(dtp,&p);
}
@@ -573,7 +567,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
if (!_stealMouse /* !ignoring gesture*/) {
bool offerGrab = false;
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
if (p.state() == Qt::TouchPointReleased)
continue;
const QPointF &currentPos = p.scenePos();
@@ -607,7 +601,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
void QQuickMultiPointTouchArea::clearTouchLists()
{
- foreach (QObject *obj, _releasedTouchPoints) {
+ for (QObject *obj : qAsConst(_releasedTouchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined()) {
_touchPoints.remove(dtp->pointId());
@@ -624,7 +618,7 @@ void QQuickMultiPointTouchArea::clearTouchLists()
void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
{
QQuickTouchPoint *dtp = 0;
- foreach (QQuickTouchPoint* tp, _touchPrototypes) {
+ for (QQuickTouchPoint* tp : qAsConst(_touchPrototypes)) {
if (!tp->inUse()) {
tp->setInUse(true);
dtp = tp;
@@ -644,7 +638,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
{
QQuickTouchPoint *dtp = 0;
- foreach (QQuickTouchPoint *tp, _touchPrototypes)
+ for (QQuickTouchPoint *tp : qAsConst(_touchPrototypes))
if (!tp->inUse()) {
tp->setInUse(true);
dtp = tp;
@@ -784,18 +778,17 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event)
void QQuickMultiPointTouchArea::ungrab()
{
+ _stealMouse = false;
+ setKeepMouseGrab(false);
+ setKeepTouchGrab(false);
+ ungrabTouchPoints();
+
if (_touchPoints.count()) {
- QQuickWindow *c = window();
- if (c && c->mouseGrabberItem() == this) {
- _stealMouse = false;
- setKeepMouseGrab(false);
- }
- setKeepTouchGrab(false);
- foreach (QObject *obj, _touchPoints)
+ for (QObject *obj : qAsConst(_touchPoints))
static_cast<QQuickTouchPoint*>(obj)->setPressed(false);
emit canceled(_touchPoints.values());
clearTouchLists();
- foreach (QObject *obj, _touchPoints) {
+ for (QObject *obj : qAsConst(_touchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined())
delete dtp;
@@ -861,10 +854,10 @@ bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event)
return false;
}
-bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *event)
+bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEvent *event)
{
if (!isEnabled() || !isVisible())
- return QQuickItem::childMouseEventFilter(i, event);
+ return QQuickItem::childMouseEventFilter(receiver, event);
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
@@ -881,17 +874,13 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *eve
if (!shouldFilter(event))
return false;
updateTouchData(event);
- //TODO: verify this behavior
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
+ ungrab();
}
break;
default:
break;
}
- return QQuickItem::childMouseEventFilter(i, event);
+ return QQuickItem::childMouseEventFilter(receiver, event);
}
bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
@@ -914,7 +903,7 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
QTouchEvent *te = static_cast<QTouchEvent*>(event);
- foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) {
+ for (const QTouchEvent::TouchPoint &point : te->touchPoints()) {
if (contains(mapFromScene(point.scenePos()))) {
containsPoint = true;
break;
@@ -940,8 +929,8 @@ QSGNode *QQuickMultiPointTouchArea::updatePaintNode(QSGNode *oldNode, UpdatePain
if (!qmlVisualTouchDebugging())
return 0;
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(QColor(255, 0, 0, 50));
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index 52913f53f6..541eb04764 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -231,7 +231,7 @@ public:
static QQuickTouchPoint* touchPoint_at(QQmlListProperty<QQuickTouchPoint> *list, int index) {
QQuickMultiPointTouchArea *q = static_cast<QQuickMultiPointTouchArea*>(list->object);
- return q->_touchPrototypes[index];
+ return q->_touchPrototypes.value(index);
}
Q_SIGNALS:
@@ -247,7 +247,7 @@ Q_SIGNALS:
protected:
void touchEvent(QTouchEvent *) Q_DECL_OVERRIDE;
- bool childMouseEventFilter(QQuickItem *i, QEvent *event) Q_DECL_OVERRIDE;
+ bool childMouseEventFilter(QQuickItem *receiver, QEvent *event) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
diff --git a/src/quick/items/qquickopenglinfo.cpp b/src/quick/items/qquickopenglinfo.cpp
index b380a93e76..4bb13b84aa 100644
--- a/src/quick/items/qquickopenglinfo.cpp
+++ b/src/quick/items/qquickopenglinfo.cpp
@@ -61,6 +61,10 @@ QT_BEGIN_NAMESPACE
format. When it becomes associated with a surface, all properties
will update.
+ \deprecated
+
+ \warning This type is deprecated. Use GraphicsInfo instead.
+
\sa ShaderEffect
*/
QQuickOpenGLInfo::QQuickOpenGLInfo(QQuickItem *item)
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
new file mode 100644
index 0000000000..b974641cca
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -0,0 +1,973 @@
+/****************************************************************************
+**
+** 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 <private/qquickopenglshadereffect_p.h>
+
+#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/private/qsgshadersourcebuilder_p.h>
+#include "qquickitem_p.h"
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/qsgtextureprovider.h>
+#include "qquickwindow.h"
+
+#include "qquickimage_p.h"
+#include "qquickshadereffectsource_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+#include <QtQml/qqmlfile.h>
+#include <QtCore/qsignalmapper.h>
+#include <QtCore/qfileselector.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+ enum VariableQualifier {
+ AttributeQualifier,
+ UniformQualifier
+ };
+
+ inline bool qt_isalpha(char c)
+ {
+ char ch = c | 0x20;
+ return (ch >= 'a' && ch <= 'z') || c == '_';
+ }
+
+ inline bool qt_isalnum(char c)
+ {
+ return qt_isalpha(c) || (c >= '0' && c <= '9');
+ }
+
+ inline bool qt_isspace(char c)
+ {
+ return c == ' ' || (c >= 0x09 && c <= 0x0d);
+ }
+
+ // Returns -1 if not found, returns index to first character after the name if found.
+ int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
+ int &typeIndex, int &typeLength,
+ int &nameIndex, int &nameLength,
+ QQuickOpenGLShaderEffectCommon::Key::ShaderType shaderType)
+ {
+ enum Identifier {
+ QualifierIdentifier, // Base state
+ PrecisionIdentifier,
+ TypeIdentifier,
+ NameIdentifier
+ };
+ Identifier expected = QualifierIdentifier;
+ bool compilerDirectiveExpected = index == 0;
+
+ while (index < length) {
+ // Skip whitespace.
+ while (qt_isspace(s[index])) {
+ compilerDirectiveExpected |= s[index] == '\n';
+ ++index;
+ }
+
+ if (qt_isalpha(s[index])) {
+ // Read identifier.
+ int idIndex = index;
+ ++index;
+ while (qt_isalnum(s[index]))
+ ++index;
+ int idLength = index - idIndex;
+
+ const int attrLen = sizeof("attribute") - 1;
+ const int inLen = sizeof("in") - 1;
+ const int uniLen = sizeof("uniform") - 1;
+ const int loLen = sizeof("lowp") - 1;
+ const int medLen = sizeof("mediump") - 1;
+ const int hiLen = sizeof("highp") - 1;
+
+ switch (expected) {
+ case QualifierIdentifier:
+ if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
+ decl = AttributeQualifier;
+ expected = PrecisionIdentifier;
+ } else if (shaderType == QQuickOpenGLShaderEffectCommon::Key::VertexShader
+ && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) {
+ decl = AttributeQualifier;
+ expected = PrecisionIdentifier;
+ } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
+ decl = UniformQualifier;
+ expected = PrecisionIdentifier;
+ }
+ break;
+ case PrecisionIdentifier:
+ if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
+ || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
+ || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
+ {
+ expected = TypeIdentifier;
+ break;
+ }
+ // Fall through.
+ case TypeIdentifier:
+ typeIndex = idIndex;
+ typeLength = idLength;
+ expected = NameIdentifier;
+ break;
+ case NameIdentifier:
+ nameIndex = idIndex;
+ nameLength = idLength;
+ return index; // Attribute or uniform declaration found. Return result.
+ default:
+ break;
+ }
+ } else if (s[index] == '#' && compilerDirectiveExpected) {
+ // Skip compiler directives.
+ ++index;
+ while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
+ ++index;
+ } else if (s[index] == '/' && s[index + 1] == '/') {
+ // Skip comments.
+ index += 2;
+ while (index < length && s[index] != '\n')
+ ++index;
+ } else if (s[index] == '/' && s[index + 1] == '*') {
+ // Skip comments.
+ index += 2;
+ while (index < length && (s[index] != '*' || s[index + 1] != '/'))
+ ++index;
+ if (index < length)
+ index += 2; // Skip star-slash.
+ } else {
+ expected = QualifierIdentifier;
+ ++index;
+ }
+ compilerDirectiveExpected = false;
+ }
+ return -1;
+ }
+}
+
+namespace QtPrivate {
+class MappedSlotObject: public QtPrivate::QSlotObjectBase
+{
+public:
+ typedef std::function<void()> PropChangedFunc;
+
+ explicit MappedSlotObject(PropChangedFunc func)
+ : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
+ { ref(); }
+
+ void setSignalIndex(int idx) { _signalIndex = idx; }
+ int signalIndex() const { return _signalIndex; }
+
+private:
+ int _signalIndex;
+ PropChangedFunc func;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
+ {
+ auto thiz = static_cast<MappedSlotObject*>(this_);
+ switch (which) {
+ case Destroy:
+ delete thiz;
+ break;
+ case Call:
+ thiz->func();
+ break;
+ case Compare:
+ *ret = thiz == reinterpret_cast<MappedSlotObject *>(a[0]);
+ break;
+ case NumOperations: ;
+ }
+ }
+};
+}
+
+QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon()
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ clearSignalMappers(shaderType);
+}
+
+void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
+{
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (signalMappers[shaderType].at(i) == 0)
+ continue;
+ const UniformData &d = uniformData[shaderType].at(i);
+ auto mapper = signalMappers[shaderType].at(i);
+ void *a = mapper;
+ QObjectPrivate::disconnect(item, mapper->signalIndex(), &a);
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType)
+{
+ QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item);
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (signalMappers[shaderType].at(i) == 0)
+ continue;
+ const UniformData &d = uniformData[shaderType].at(i);
+ QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr);
+ if (pd && !pd->isFunction()) {
+ if (pd->notifyIndex() == -1) {
+ qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData());
+ } else {
+ auto *mapper = signalMappers[shaderType].at(i);
+ mapper->setSignalIndex(pd->notifyIndex());
+ Q_ASSERT(item->metaObject() == itemMetaObject);
+ QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper,
+ Qt::AutoConnection, nullptr, itemMetaObject);
+ }
+ } else {
+ // If the source is set via a dynamic property, like the layer is, then we need this
+ // check to disable the warning.
+ if (!item->property(d.name).isValid())
+ qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData());
+ }
+
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->refWindow(item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateParseLog(bool ignoreAttributes)
+{
+ parseLog.clear();
+ if (!ignoreAttributes) {
+ if (!attributes.contains(qtPositionAttributeName())) {
+ parseLog += QLatin1String("Warning: Missing reference to \'");
+ parseLog += QLatin1String(qtPositionAttributeName());
+ parseLog += QLatin1String("\'.\n");
+ }
+ if (!attributes.contains(qtTexCoordAttributeName())) {
+ parseLog += QLatin1String("Warning: Missing reference to \'");
+ parseLog += QLatin1String(qtTexCoordAttributeName());
+ parseLog += QLatin1String("\'.\n");
+ }
+ }
+ bool respectsMatrix = false;
+ bool respectsOpacity = false;
+ for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
+ respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i)
+ respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
+ }
+ if (!respectsMatrix)
+ parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
+ if (!respectsOpacity)
+ parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
+}
+
+void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType,
+ const QByteArray &code)
+{
+ QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item);
+ int index = 0;
+ int typeIndex = -1;
+ int typeLength = 0;
+ int nameIndex = -1;
+ int nameLength = 0;
+ const char *s = code.constData();
+ VariableQualifier decl = AttributeQualifier;
+ while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
+ nameIndex, nameLength, shaderType)) != -1)
+ {
+ if (decl == AttributeQualifier) {
+ if (shaderType == Key::VertexShader)
+ attributes.append(QByteArray(s + nameIndex, nameLength));
+ } else {
+ Q_ASSERT(decl == UniformQualifier);
+
+ const int sampLen = sizeof("sampler2D") - 1;
+ const int opLen = sizeof("qt_Opacity") - 1;
+ const int matLen = sizeof("qt_Matrix") - 1;
+ const int srLen = sizeof("qt_SubRect_") - 1;
+
+ UniformData d;
+ QtPrivate::MappedSlotObject *mapper = nullptr;
+ d.name = QByteArray(s + nameIndex, nameLength);
+ if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
+ d.specialType = UniformData::Opacity;
+ } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
+ d.specialType = UniformData::Matrix;
+ } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
+ d.specialType = UniformData::SubRect;
+ } else {
+ if (QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr)) {
+ if (!pd->isFunction())
+ d.propertyIndex = pd->coreIndex();
+ }
+ const int mappedId = uniformData[shaderType].size() | (shaderType << 16);
+ 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;
+ d.setValueFromProperty(item, itemMetaObject);
+ }
+ uniformData[shaderType].append(d);
+ signalMappers[shaderType].append(mapper);
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType)
+{
+ disconnectPropertySignals(item, shaderType);
+ uniformData[shaderType].clear();
+ clearSignalMappers(shaderType);
+ if (shaderType == Key::VertexShader)
+ attributes.clear();
+
+ // A qrc or file URL means the shader source is to be read from the specified file.
+ QUrl srcUrl(QString::fromUtf8(source.sourceCode[shaderType]));
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ if (!fileSelector) {
+ fileSelector = new QFileSelector(item);
+ // There may not be an OpenGL context accessible here. So rely on
+ // the window's requestedFormat().
+ if (item->window()
+ && item->window()->requestedFormat().profile() == QSurfaceFormat::CoreProfile) {
+ fileSelector->setExtraSelectors(QStringList() << QStringLiteral("glslcore"));
+ }
+ }
+ const QString fn = fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
+ QFile f(fn);
+ if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ source.sourceCode[shaderType] = f.readAll();
+ f.close();
+ } else {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ source.sourceCode[shaderType] = QByteArray();
+ }
+ }
+
+ const QByteArray &code = source.sourceCode[shaderType];
+ if (code.isEmpty()) {
+ // Optimize for default code.
+ if (shaderType == Key::VertexShader) {
+ attributes.append(QByteArray(qtPositionAttributeName()));
+ attributes.append(QByteArray(qtTexCoordAttributeName()));
+ UniformData d;
+ d.name = "qt_Matrix";
+ d.specialType = UniformData::Matrix;
+ uniformData[Key::VertexShader].append(d);
+ signalMappers[Key::VertexShader].append(0);
+ } else if (shaderType == Key::FragmentShader) {
+ UniformData d;
+ d.name = "qt_Opacity";
+ d.specialType = UniformData::Opacity;
+ uniformData[Key::FragmentShader].append(d);
+ signalMappers[Key::FragmentShader].append(0);
+ const int mappedId = 1 | (Key::FragmentShader << 16);
+ auto mapper = new QtPrivate::MappedSlotObject([this, mappedId](){mappedPropertyChanged(mappedId);});
+ const char *sourceName = "source";
+ d.name = sourceName;
+ d.setValueFromProperty(item, itemMetaObject);
+ d.specialType = UniformData::Sampler;
+ uniformData[Key::FragmentShader].append(d);
+ signalMappers[Key::FragmentShader].append(mapper);
+ }
+ } else {
+ lookThroughShaderCode(item, itemMetaObject, shaderType, code);
+ }
+
+ connectPropertySignals(item, itemMetaObject, shaderType);
+}
+
+void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode *node,
+ QQuickOpenGLShaderEffectMaterial *material,
+ bool updateUniforms, bool updateUniformValues,
+ bool updateTextureProviders)
+{
+ if (updateUniforms) {
+ for (int i = 0; i < material->textureProviders.size(); ++i) {
+ QSGTextureProvider *t = material->textureProviders.at(i);
+ if (t) {
+ QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ }
+ }
+
+ // First make room in the textureProviders array. Set to proper value further down.
+ 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)
+ ++textureProviderCount;
+ }
+ material->uniforms[shaderType] = uniformData[shaderType];
+ }
+ material->textureProviders.fill(0, textureProviderCount);
+ updateUniformValues = false;
+ updateTextureProviders = true;
+ }
+
+ if (updateUniformValues) {
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
+ for (int i = 0; i < uniformData[shaderType].size(); ++i)
+ material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
+ }
+ }
+
+ if (updateTextureProviders) {
+ int index = 0;
+ 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)
+ continue;
+ QSGTextureProvider *oldProvider = material->textureProviders.at(index);
+ QSGTextureProvider *newProvider = 0;
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source && source->isTextureProvider())
+ newProvider = source->textureProvider();
+ if (newProvider != oldProvider) {
+ if (oldProvider) {
+ QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ }
+ if (newProvider) {
+ Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
+ "QQuickOpenGLShaderEffect::updatePaintNode",
+ "Texture provider must belong to the rendering thread");
+ QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ } else {
+ const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
+ qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
+ d.name.constData(), typeName);
+ }
+ material->textureProviders[index] = newProvider;
+ }
+ ++index;
+ }
+ }
+ Q_ASSERT(index == material->textureProviders.size());
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
+{
+ // See comment in QQuickOpenGLShaderEffectCommon::propertyChanged().
+ if (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) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->refWindow(window);
+ }
+ }
+ }
+ } else {
+ 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) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->derefWindow();
+ }
+ }
+ }
+ }
+}
+
+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<QObject *>()) {
+ if (qvariant_cast<QObject *>(d.value) == object)
+ d.value = QVariant();
+ }
+ }
+ }
+}
+
+static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip)
+{
+ for (int s=0; s<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++s) {
+ for (int i=0; i<uniformData[s].size(); ++i) {
+ if (s == typeToSkip && i == indexToSkip)
+ continue;
+ const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i];
+ if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
+ return false;
+ }
+ }
+ return true;
+}
+
+void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item,
+ const QMetaObject *itemMetaObject,
+ int mappedId, bool *textureProviderChanged)
+{
+ Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
+ int index = mappedId & 0xffff;
+ UniformData &d = uniformData[shaderType][index];
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+
+ // QObject::disconnect() will disconnect all matching connections. If the same
+ // source has been attached to two separate samplers, then changing one of them
+ // would trigger both to be disconnected. Without the connection we'll end up
+ // with a dangling pointer in the uniformData.
+ if (qquick_uniqueInUniformData(source, uniformData, shaderType, index))
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ d.setValueFromProperty(item, itemMetaObject);
+
+ source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ // 'source' needs a window to get a scene graph node. It usually gets one through its
+ // parent, but if the source item is "inline" rather than a reference -- i.e.
+ // "property variant source: Image { }" instead of "property variant source: foo" -- it
+ // will not get a parent. In those cases, 'source' should get the window from 'item'.
+ if (item->window())
+ QQuickItemPrivate::get(source)->refWindow(item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ if (textureProviderChanged)
+ *textureProviderChanged = true;
+ } else {
+ d.setValueFromProperty(item, itemMetaObject);
+ if (textureProviderChanged)
+ *textureProviderChanged = false;
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::clearSignalMappers(int shader)
+{
+ for (auto mapper : qAsConst(signalMappers[shader])) {
+ if (mapper)
+ mapper->destroyIfLastRef();
+ }
+ signalMappers[shader].clear();
+}
+
+QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent)
+ : QObject(parent)
+ , m_item(item)
+ , m_itemMetaObject(nullptr)
+ , m_meshResolution(1, 1)
+ , m_mesh(0)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_status(QQuickShaderEffect::Uncompiled)
+ , m_common(this, [this](int mappedId){this->propertyChanged(mappedId);})
+ , m_blending(true)
+ , m_dirtyUniforms(true)
+ , m_dirtyUniformValues(true)
+ , m_dirtyTextureProviders(true)
+ , m_dirtyProgram(true)
+ , m_dirtyParseLog(true)
+ , m_dirtyMesh(true)
+ , m_dirtyGeometry(true)
+ , m_customVertexShader(false)
+ , m_supportsAtlasTextures(false)
+ , m_vertNeedsUpdate(true)
+ , m_fragNeedsUpdate(true)
+{
+}
+
+QQuickOpenGLShaderEffect::~QQuickOpenGLShaderEffect()
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ m_common.disconnectPropertySignals(m_item, Key::ShaderType(shaderType));
+}
+
+void QQuickOpenGLShaderEffect::setFragmentShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::FragmentShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+
+ m_fragNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ m_item->update();
+ if (m_status != QQuickShaderEffect::Uncompiled) {
+ m_status = QQuickShaderEffect::Uncompiled;
+ emit m_item->statusChanged();
+ }
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickOpenGLShaderEffect::setVertexShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::VertexShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+ m_customVertexShader = true;
+
+ m_vertNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ m_item->update();
+ if (m_status != QQuickShaderEffect::Uncompiled) {
+ m_status = QQuickShaderEffect::Uncompiled;
+ emit m_item->statusChanged();
+ }
+ emit m_item->vertexShaderChanged();
+}
+
+void QQuickOpenGLShaderEffect::setBlending(bool enable)
+{
+ if (blending() == enable)
+ return;
+
+ m_blending = enable;
+ m_item->update();
+
+ emit m_item->blendingChanged();
+}
+
+QVariant QQuickOpenGLShaderEffect::mesh() const
+{
+ return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
+ : qVariantFromValue(m_meshResolution);
+}
+
+void QQuickOpenGLShaderEffect::setMesh(const QVariant &mesh)
+{
+ QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
+ if (newMesh && newMesh == m_mesh)
+ return;
+ if (m_mesh)
+ disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
+ m_mesh = newMesh;
+ if (m_mesh) {
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
+ } else {
+ if (mesh.canConvert<QSize>()) {
+ m_meshResolution = mesh.toSize();
+ } else {
+ QList<QByteArray> res = mesh.toByteArray().split('x');
+ bool ok = res.size() == 2;
+ if (ok) {
+ int w = res.at(0).toInt(&ok);
+ if (ok) {
+ int h = res.at(1).toInt(&ok);
+ if (ok)
+ m_meshResolution = QSize(w, h);
+ }
+ }
+ if (!ok)
+ qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
+ }
+ m_defaultMesh.setResolution(m_meshResolution);
+ }
+
+ m_dirtyMesh = true;
+ m_dirtyParseLog = true;
+ m_item->update();
+ emit m_item->meshChanged();
+}
+
+void QQuickOpenGLShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
+{
+ if (face == m_cullMode)
+ return;
+ m_cullMode = face;
+ m_item->update();
+ emit m_item->cullModeChanged();
+}
+
+void QQuickOpenGLShaderEffect::setSupportsAtlasTextures(bool supports)
+{
+ if (supports == m_supportsAtlasTextures)
+ return;
+ m_supportsAtlasTextures = supports;
+ updateGeometry();
+ emit m_item->supportsAtlasTexturesChanged();
+}
+
+QString QQuickOpenGLShaderEffect::parseLog()
+{
+ maybeUpdateShaders(true);
+
+ if (m_dirtyParseLog) {
+ m_common.updateParseLog(m_mesh != 0);
+ m_dirtyParseLog = false;
+ }
+ return m_common.parseLog;
+}
+
+void QQuickOpenGLShaderEffect::handleEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
+ if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
+ bool textureProviderChanged;
+ m_common.propertyChanged(m_item, m_itemMetaObject,
+ (shaderType << 16) | i, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ m_item->update();
+ }
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffect::updateGeometry()
+{
+ m_dirtyGeometry = true;
+ m_item->update();
+}
+
+void QQuickOpenGLShaderEffect::updateGeometryIfAtlased()
+{
+ if (m_supportsAtlasTextures)
+ updateGeometry();
+}
+
+void QQuickOpenGLShaderEffect::updateLogAndStatus(const QString &log, int status)
+{
+ m_log = parseLog() + log;
+ m_status = QQuickShaderEffect::Status(status);
+ emit m_item->logChanged();
+ emit m_item->statusChanged();
+}
+
+void QQuickOpenGLShaderEffect::sourceDestroyed(QObject *object)
+{
+ m_common.sourceDestroyed(object);
+}
+
+void QQuickOpenGLShaderEffect::propertyChanged(int mappedId)
+{
+ bool textureProviderChanged;
+ m_common.propertyChanged(m_item, m_itemMetaObject, mappedId, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ m_item->update();
+}
+
+void QQuickOpenGLShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
+{
+ m_dirtyGeometry = true;
+}
+
+QSGNode *QQuickOpenGLShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ QQuickOpenGLShaderEffectNode *node = static_cast<QQuickOpenGLShaderEffectNode *>(oldNode);
+
+ // In the case of zero-size or a bad vertex shader, don't try to create a node...
+ if (m_common.attributes.isEmpty() || m_item->width() <= 0 || m_item->height() <= 0) {
+ if (node)
+ delete node;
+ return 0;
+ }
+
+ if (!node) {
+ node = new QQuickOpenGLShaderEffectNode;
+ node->setMaterial(new QQuickOpenGLShaderEffectMaterial(node));
+ node->setFlag(QSGNode::OwnsMaterial, true);
+ m_dirtyProgram = true;
+ m_dirtyUniforms = true;
+ m_dirtyGeometry = true;
+ connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
+ connect(node, &QQuickOpenGLShaderEffectNode::dirtyTexture,
+ this, &QQuickOpenGLShaderEffect::updateGeometryIfAtlased);
+ }
+
+ QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(node->material());
+
+ // Update blending
+ if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
+ material->setFlag(QSGMaterial::Blending, m_blending);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (int(material->cullMode) != int(m_cullMode)) {
+ material->cullMode = QQuickShaderEffect::CullMode(m_cullMode);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_dirtyProgram) {
+ Key s = m_common.source;
+ QSGShaderSourceBuilder builder;
+ if (s.sourceCode[Key::FragmentShader].isEmpty()) {
+ builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag"));
+ s.sourceCode[Key::FragmentShader] = builder.source();
+ builder.clear();
+ }
+ if (s.sourceCode[Key::VertexShader].isEmpty()) {
+ builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert"));
+ s.sourceCode[Key::VertexShader] = builder.source();
+ }
+
+ material->setProgramSource(s);
+ material->attributes = m_common.attributes;
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyProgram = false;
+ m_dirtyUniforms = true;
+ }
+
+ if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
+ m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
+ m_dirtyTextureProviders);
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
+ }
+
+ QRectF srcRect(0, 0, 1, 1);
+ bool geometryUsesTextureSubRect = false;
+ if (m_supportsAtlasTextures && material->textureProviders.size() == 1) {
+ QSGTextureProvider *provider = material->textureProviders.at(0);
+ if (provider->texture()) {
+ srcRect = provider->texture()->normalizedTextureSubRect();
+ geometryUsesTextureSubRect = true;
+ }
+ }
+
+ if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) {
+ material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
+ material->geometryUsesTextureSubRect = geometryUsesTextureSubRect;
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_dirtyMesh) {
+ node->setGeometry(0);
+ m_dirtyMesh = false;
+ m_dirtyGeometry = true;
+ }
+
+ if (m_dirtyGeometry) {
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ QSGGeometry *geometry = node->geometry();
+ QRectF rect(0, 0, m_item->width(), m_item->height());
+ QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+
+ int posIndex = 0;
+ if (!mesh->validateAttributes(m_common.attributes, &posIndex)) {
+ QString log = mesh->log();
+ if (!log.isNull()) {
+ m_log = parseLog();
+ m_log += QLatin1String("*** Mesh ***\n");
+ m_log += log;
+ m_status = QQuickShaderEffect::Error;
+ emit m_item->logChanged();
+ emit m_item->statusChanged();
+ }
+ delete node;
+ return 0;
+ }
+
+ geometry = mesh->updateGeometry(geometry, m_common.attributes.count(), posIndex, srcRect, rect);
+
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirtyGeometry = false;
+ }
+
+ return node;
+}
+
+void QQuickOpenGLShaderEffect::maybeUpdateShaders(bool force)
+{
+ if (!m_itemMetaObject)
+ m_itemMetaObject = m_item->metaObject();
+
+ // Defer processing if a window is not yet associated with the item. This
+ // is because the actual scenegraph backend is not known so conditions
+ // based on GraphicsInfo.shaderType and similar evaluate to wrong results.
+ if (!m_item->window() && !force) {
+ m_item->polish();
+ return;
+ }
+
+ if (m_vertNeedsUpdate) {
+ m_vertNeedsUpdate = false;
+ m_common.updateShader(m_item, m_itemMetaObject, Key::VertexShader);
+ }
+
+ if (m_fragNeedsUpdate) {
+ m_fragNeedsUpdate = false;
+ m_common.updateShader(m_item, m_itemMetaObject, Key::FragmentShader);
+ }
+}
+
+void QQuickOpenGLShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ if (change == QQuickItem::ItemSceneChange)
+ m_common.updateWindow(value.window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h
new file mode 100644
index 0000000000..ed56a76409
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect_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 QQUICKOPENGLSHADEREFFECT_P_H
+#define QQUICKOPENGLSHADEREFFECT_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 <QtQuick/qquickitem.h>
+
+#include <QtQuick/qsgmaterial.h>
+#include <private/qtquickglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+#include <QtCore/qpointer.h>
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSignalMapper;
+class QFileSelector;
+class QQuickOpenGLCustomMaterialShader;
+
+namespace QtPrivate {
+class MappedSlotObject;
+}
+
+// Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle.
+struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon
+{
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickOpenGLShaderEffectCommon(QObject *host, std::function<void(int)> mappedPropertyChanged)
+ : host(host), mappedPropertyChanged(mappedPropertyChanged), fileSelector(nullptr)
+ { }
+
+ ~QQuickOpenGLShaderEffectCommon();
+
+ void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
+ void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType);
+ void updateParseLog(bool ignoreAttributes);
+ void lookThroughShaderCode(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType, const QByteArray &code);
+ void updateShader(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType);
+ void updateMaterial(QQuickOpenGLShaderEffectNode *node, QQuickOpenGLShaderEffectMaterial *material,
+ bool updateUniforms, bool updateUniformValues, bool updateTextureProviders);
+ void updateWindow(QQuickWindow *window);
+
+ // Called by slots in QQuickOpenGLShaderEffect:
+ void sourceDestroyed(QObject *object);
+ void propertyChanged(QQuickItem *item, const QMetaObject *itemMetaObject, int mappedId, bool *textureProviderChanged);
+
+ void clearSignalMappers(int shader);
+
+ QObject *host;
+ std::function<void(int)> mappedPropertyChanged;
+ Key source;
+ QVector<QByteArray> attributes;
+ QVector<UniformData> uniformData[Key::ShaderTypeCount];
+ QVector<QtPrivate::MappedSlotObject *> signalMappers[Key::ShaderTypeCount];
+ QString parseLog;
+ QFileSelector *fileSelector;
+};
+
+
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffect : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent = 0);
+ ~QQuickOpenGLShaderEffect();
+
+ QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
+ void setFragmentShader(const QByteArray &code);
+
+ QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
+ void setVertexShader(const QByteArray &code);
+
+ bool blending() const { return m_blending; }
+ void setBlending(bool enable);
+
+ QVariant mesh() const;
+ void setMesh(const QVariant &mesh);
+
+ QQuickShaderEffect::CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(QQuickShaderEffect::CullMode face);
+
+ QString log() const { return m_log; }
+ QQuickShaderEffect::Status status() const { return m_status; }
+
+ bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ void setSupportsAtlasTextures(bool supports);
+
+ QString parseLog();
+
+ void handleEvent(QEvent *);
+ void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *);
+ void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+ void maybeUpdateShaders(bool force = false);
+
+private Q_SLOTS:
+ void updateGeometry();
+ void updateGeometryIfAtlased();
+ void updateLogAndStatus(const QString &log, int status);
+ void sourceDestroyed(QObject *object);
+
+private:
+ void propertyChanged(int mappedId);
+
+ friend class QQuickCustomMaterialShader;
+ friend class QQuickOpenGLShaderEffectNode;
+
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickShaderEffect *m_item;
+ const QMetaObject *m_itemMetaObject;
+ QSize m_meshResolution;
+ QQuickShaderEffectMesh *m_mesh;
+ QQuickGridMesh m_defaultMesh;
+ QQuickShaderEffect::CullMode m_cullMode;
+ QString m_log;
+ QQuickShaderEffect::Status m_status;
+
+ QQuickOpenGLShaderEffectCommon m_common;
+
+ uint m_blending : 1;
+ uint m_dirtyUniforms : 1;
+ uint m_dirtyUniformValues : 1;
+ uint m_dirtyTextureProviders : 1;
+ uint m_dirtyProgram : 1;
+ uint m_dirtyParseLog : 1;
+ uint m_dirtyMesh : 1;
+ uint m_dirtyGeometry : 1;
+ uint m_customVertexShader : 1;
+ uint m_supportsAtlasTextures : 1;
+ uint m_vertNeedsUpdate : 1;
+ uint m_fragNeedsUpdate : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKOPENGLSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index 246a713dca..02b76b2dbc 100644
--- a/src/quick/items/qquickshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -37,14 +37,15 @@
**
****************************************************************************/
-#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
-#include "qquickshadereffect_p.h"
+#include "qquickopenglshadereffect_p.h"
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgshadersourcebuilder_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtCore/qmutex.h>
+#include <QtGui/qopenglfunctions.h>
QT_BEGIN_NAMESPACE
@@ -61,29 +62,29 @@ static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProvider
class QQuickCustomMaterialShader : public QSGMaterialShader
{
public:
- QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
+ QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
void deactivate() Q_DECL_OVERRIDE;
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) Q_DECL_OVERRIDE;
char const *const *attributeNames() const Q_DECL_OVERRIDE;
protected:
- friend class QQuickShaderEffectNode;
+ friend class QQuickOpenGLShaderEffectNode;
void compile() Q_DECL_OVERRIDE;
const char *vertexShader() const Q_DECL_OVERRIDE;
const char *fragmentShader() const Q_DECL_OVERRIDE;
- const QQuickShaderEffectMaterialKey m_key;
+ const QQuickOpenGLShaderEffectMaterialKey m_key;
QVector<QByteArray> m_attributes;
QVector<const char *> m_attributeNames;
QString m_log;
bool m_compiled;
- QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ QVector<int> m_uniformLocs[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount];
uint m_initialized : 1;
};
-QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
+QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
: m_key(key)
, m_attributes(attributes)
, m_compiled(false)
@@ -104,11 +105,11 @@ void QQuickCustomMaterialShader::deactivate()
void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
Q_ASSERT(newEffect != 0);
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect);
+ QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(newEffect);
if (!material->m_emittedLogChanged && material->m_node) {
material->m_emittedLogChanged = true;
emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled
@@ -117,7 +118,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
int textureProviderIndex = 0;
if (!m_initialized) {
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
Q_ASSERT(m_uniformLocs[shaderType].isEmpty());
m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size());
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
@@ -138,7 +139,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
}
QOpenGLFunctions *functions = state.context()->functions();
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
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);
@@ -230,14 +231,14 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
}
functions->glActiveTexture(GL_TEXTURE0);
- const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect);
+ const QQuickOpenGLShaderEffectMaterial *oldMaterial = static_cast<const QQuickOpenGLShaderEffectMaterial *>(oldEffect);
if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) {
switch (material->cullMode) {
- case QQuickShaderEffectMaterial::FrontFaceCulling:
+ case QQuickShaderEffect::FrontFaceCulling:
functions->glEnable(GL_CULL_FACE);
functions->glCullFace(GL_FRONT);
break;
- case QQuickShaderEffectMaterial::BackFaceCulling:
+ case QQuickShaderEffect::BackFaceCulling:
functions->glEnable(GL_CULL_FACE);
functions->glCullFace(GL_BACK);
break;
@@ -322,16 +323,16 @@ void QQuickCustomMaterialShader::compile()
const char *QQuickCustomMaterialShader::vertexShader() const
{
- return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData();
+ return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::VertexShader].constData();
}
const char *QQuickCustomMaterialShader::fragmentShader() const
{
- return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData();
+ return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::FragmentShader].constData();
}
-bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const
+bool QQuickOpenGLShaderEffectMaterialKey::operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const
{
for (int shaderType = 0; shaderType < ShaderTypeCount; ++shaderType) {
if (sourceCode[shaderType] != other.sourceCode[shaderType])
@@ -340,39 +341,39 @@ bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMateria
return true;
}
-bool QQuickShaderEffectMaterialKey::operator != (const QQuickShaderEffectMaterialKey &other) const
+bool QQuickOpenGLShaderEffectMaterialKey::operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const
{
return !(*this == other);
}
-uint qHash(const QQuickShaderEffectMaterialKey &key)
+uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key)
{
uint hash = 1;
- typedef QQuickShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
hash = hash * 31337 + qHash(key.sourceCode[shaderType]);
return hash;
}
-class QQuickShaderEffectMaterialCache : public QObject
+class QQuickOpenGLShaderEffectMaterialCache : public QObject
{
Q_OBJECT
public:
- static QQuickShaderEffectMaterialCache *get(bool create = true) {
+ static QQuickOpenGLShaderEffectMaterialCache *get(bool create = true) {
QOpenGLContext *ctx = QOpenGLContext::currentContext();
- QQuickShaderEffectMaterialCache *me = ctx->findChild<QQuickShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly);
+ QQuickOpenGLShaderEffectMaterialCache *me = ctx->findChild<QQuickOpenGLShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly);
if (!me && create) {
- me = new QQuickShaderEffectMaterialCache();
+ me = new QQuickOpenGLShaderEffectMaterialCache();
me->setObjectName(QStringLiteral("__qt_ShaderEffectCache"));
me->setParent(ctx);
}
return me;
}
- QHash<QQuickShaderEffectMaterialKey, QSGMaterialType *> cache;
+ QHash<QQuickOpenGLShaderEffectMaterialKey, QSGMaterialType *> cache;
};
-QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
- : cullMode(NoCulling)
+QQuickOpenGLShaderEffectMaterial::QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node)
+ : cullMode(QQuickShaderEffect::NoCulling)
, geometryUsesTextureSubRect(false)
, m_node(node)
, m_emittedLogChanged(false)
@@ -380,17 +381,17 @@ QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *n
setFlag(Blending | RequiresFullMatrix, true);
}
-QSGMaterialType *QQuickShaderEffectMaterial::type() const
+QSGMaterialType *QQuickOpenGLShaderEffectMaterial::type() const
{
return m_type;
}
-QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const
+QSGMaterialShader *QQuickOpenGLShaderEffectMaterial::createShader() const
{
return new QQuickCustomMaterialShader(m_source, attributes);
}
-bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &other) const
+bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformData &other) const
{
if (specialType != other.specialType)
return false;
@@ -407,14 +408,14 @@ bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &ot
}
}
-int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const
+int QQuickOpenGLShaderEffectMaterial::compare(const QSGMaterial *o) const
{
- const QQuickShaderEffectMaterial *other = static_cast<const QQuickShaderEffectMaterial *>(o);
+ const QQuickOpenGLShaderEffectMaterial *other = static_cast<const QQuickOpenGLShaderEffectMaterial *>(o);
if ((hasAtlasTexture(textureProviders) && !geometryUsesTextureSubRect) || (hasAtlasTexture(other->textureProviders) && !other->geometryUsesTextureSubRect))
return 1;
if (cullMode != other->cullMode)
return 1;
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
if (uniforms[shaderType] != other->uniforms[shaderType])
return 1;
}
@@ -438,12 +439,12 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const
return 0;
}
-void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source)
+void QQuickOpenGLShaderEffectMaterial::setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source)
{
m_source = source;
m_emittedLogChanged = false;
- QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get();
+ QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get();
m_type = cache->cache.value(m_source);
if (!m_type) {
m_type = new QSGMaterialType();
@@ -451,16 +452,16 @@ void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMateri
}
}
-void QQuickShaderEffectMaterial::cleanupMaterialCache()
+void QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache()
{
- QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get(false);
+ QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get(false);
if (cache) {
- qDeleteAll(cache->cache.values());
+ qDeleteAll(cache->cache);
delete cache;
}
}
-void QQuickShaderEffectMaterial::updateTextures() const
+void QQuickOpenGLShaderEffectMaterial::updateTextures() const
{
for (int i = 0; i < textureProviders.size(); ++i) {
if (QSGTextureProvider *provider = textureProviders.at(i)) {
@@ -470,7 +471,7 @@ void QQuickShaderEffectMaterial::updateTextures() const
}
}
-void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
+void QQuickOpenGLShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
{
for (int i = 0; i < textureProviders.size(); ++i) {
if (provider == textureProviders.at(i))
@@ -479,7 +480,7 @@ void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *p
}
-QQuickShaderEffectNode::QQuickShaderEffectNode()
+QQuickOpenGLShaderEffectNode::QQuickOpenGLShaderEffectNode()
{
QSGNode::setFlag(UsePreprocess, true);
@@ -488,28 +489,28 @@ QQuickShaderEffectNode::QQuickShaderEffectNode()
#endif
}
-QQuickShaderEffectNode::~QQuickShaderEffectNode()
+QQuickOpenGLShaderEffectNode::~QQuickOpenGLShaderEffectNode()
{
}
-void QQuickShaderEffectNode::markDirtyTexture()
+void QQuickOpenGLShaderEffectNode::markDirtyTexture()
{
markDirty(DirtyMaterial);
Q_EMIT dirtyTexture();
}
-void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object)
+void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(QObject *object)
{
Q_ASSERT(material());
- static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
}
-void QQuickShaderEffectNode::preprocess()
+void QQuickOpenGLShaderEffectNode::preprocess()
{
Q_ASSERT(material());
- static_cast<QQuickShaderEffectMaterial *>(material())->updateTextures();
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->updateTextures();
}
-#include "qquickshadereffectnode.moc"
+#include "qquickopenglshadereffectnode.moc"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h
index ff32f4e290..5abe3ae3d0 100644
--- a/src/quick/items/qquickshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQUICKSHADEREFFECTNODE_P_H
-#define QQUICKSHADEREFFECTNODE_P_H
+#ifndef QQUICKOPENGLSHADEREFFECTNODE_P_H
+#define QQUICKOPENGLSHADEREFFECTNODE_P_H
//
// W A R N I N G
@@ -56,13 +56,14 @@
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/qquickitem.h>
#include <private/qtquickglobal_p.h>
+#include <private/qquickshadereffect_p.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-struct QQuickShaderEffectMaterialKey {
+struct QQuickOpenGLShaderEffectMaterialKey {
enum ShaderType
{
VertexShader,
@@ -72,15 +73,15 @@ struct QQuickShaderEffectMaterialKey {
QByteArray sourceCode[ShaderTypeCount];
- bool operator == (const QQuickShaderEffectMaterialKey &other) const;
- bool operator != (const QQuickShaderEffectMaterialKey &other) const;
+ bool operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const;
+ bool operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const;
};
-uint qHash(const QQuickShaderEffectMaterialKey &key);
+uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key);
class QQuickCustomMaterialShader;
-class QQuickShaderEffectNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMaterial : public QSGMaterial
+class QQuickOpenGLShaderEffectNode;
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMaterial
{
public:
struct UniformData
@@ -89,30 +90,33 @@ public:
QByteArray name;
QVariant value;
+ int propertyIndex = -1;
SpecialType specialType;
bool operator == (const UniformData &other) const;
- };
- enum CullMode
- {
- NoCulling,
- BackFaceCulling,
- FrontFaceCulling
+ void setValueFromProperty(QObject *item, const QMetaObject *itemMetaObject)
+ {
+ if (propertyIndex == -1) {
+ value = item->property(name);
+ } else {
+ value = itemMetaObject->property(propertyIndex).read(item);
+ }
+ }
};
- explicit QQuickShaderEffectMaterial(QQuickShaderEffectNode *node = 0);
+ explicit QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node = 0);
QSGMaterialType *type() const Q_DECL_OVERRIDE;
QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE;
QVector<QByteArray> attributes;
- QVector<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ QVector<UniformData> uniforms[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount];
QVector<QSGTextureProvider *> textureProviders;
- CullMode cullMode;
+ QQuickShaderEffect::CullMode cullMode;
bool geometryUsesTextureSubRect;
- void setProgramSource(const QQuickShaderEffectMaterialKey &source);
+ void setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source);
void updateTextures() const;
void invalidateTextureProvider(QSGTextureProvider *provider);
@@ -127,21 +131,21 @@ protected:
// type. The type is cleaned up in cleanupMaterialCache() which is called
// when the GL context is shut down.
QSGMaterialType *m_type;
- QQuickShaderEffectMaterialKey m_source;
+ QQuickOpenGLShaderEffectMaterialKey m_source;
- QQuickShaderEffectNode *m_node;
+ QQuickOpenGLShaderEffectNode *m_node;
bool m_emittedLogChanged;
};
class QSGShaderEffectMesh;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectNode : public QObject, public QSGGeometryNode
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectNode : public QObject, public QSGGeometryNode
{
Q_OBJECT
public:
- QQuickShaderEffectNode();
- virtual ~QQuickShaderEffectNode();
+ QQuickOpenGLShaderEffectNode();
+ virtual ~QQuickOpenGLShaderEffectNode();
void preprocess() Q_DECL_OVERRIDE;
@@ -156,4 +160,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-#endif // QQUICKSHADEREFFECTNODE_P_H
+#endif // QQUICKOPENGLSHADEREFFECTNODE_P_H
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index 75919d0791..d21eb93dbf 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -63,13 +63,14 @@ public:
\inmodule QtQuick
- The QQuickPaintedItem makes it possible to use the QPainter API with the QML Scene Graph.
- It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint
- onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject.
- When the render target is a QImage, QPainter first renders into the image then
- the content is uploaded to the texture.
- When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture.
- Call update() to trigger a repaint.
+ The QQuickPaintedItem makes it possible to use the QPainter API with the
+ QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
+ uses a QPainter to paint onto the texture. The render target can be either
+ a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
+ render target is a QImage, QPainter first renders into the image then the
+ content is uploaded to the texture. When a QOpenGLFramebufferObject is
+ used, QPainter paints directly onto the texture. Call update() to trigger a
+ repaint.
To enable QPainter to do anti-aliased rendering, use setAntialiasing().
@@ -78,6 +79,10 @@ public:
public function: paint(), which implements the actual painting. The
painting will be inside the rectangle spanning from 0,0 to
width(),height().
+
+ \note It important to understand the performance implications such items
+ can incur. See QQuickPaintedItem::RenderTarget and
+ QQuickPaintedItem::renderTarget.
*/
/*!
@@ -172,8 +177,6 @@ QQuickPaintedItem::~QQuickPaintedItem()
is processed by the QML Scene Graph when the next frame is rendered. The item will only be
redrawn if it is visible.
- Note that calling this function will trigger a repaint of the whole scene.
-
\sa paint()
*/
void QQuickPaintedItem::update(const QRect &rect)
@@ -499,6 +502,12 @@ void QQuickPaintedItem::setFillColor(const QColor &c)
the QQuickPaintedItem::FramebufferObject render target if the item gets resized often.
By default, the render target is QQuickPaintedItem::Image.
+
+ \note Some Qt Quick backends may not support all render target options. For
+ example, it is likely that non-OpenGL backends will lack support for
+ QQuickPaintedItem::FramebufferObject and
+ QQuickPaintedItem::InvertedYFramebufferObject. Requesting these will then
+ be ignored.
*/
QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const
{
@@ -652,11 +661,13 @@ QSGTextureProvider *QQuickPaintedItem::textureProvider() const
return QQuickItem::textureProvider();
Q_D(const QQuickPaintedItem);
+#ifndef QT_NO_OPENGL
QQuickWindow *w = window();
if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
qWarning("QQuickPaintedItem::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+#endif
if (!d->textureProvider)
d->textureProvider = new QQuickPaintedItemTextureProvider();
d->textureProvider->node = d->node;
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index c975207dad..e54ee66fe5 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -191,7 +191,8 @@ void QQuickPathView::initItem(int index, QObject *object)
att->m_view = this;
qreal percent = d->positionOfIndex(index);
if (percent < 1.0 && d->path) {
- foreach (const QString &attr, d->path->attributes())
+ const auto attributes = d->path->attributes();
+ for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
item->setZ(d->requestedZ);
}
@@ -230,7 +231,8 @@ QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
// pre-create one metatype to share with all attached objects
attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q));
if (path) {
- foreach (const QString &attr, path->attributes())
+ const auto attributes = path->attributes();
+ for (const QString &attr : attributes)
attType->createProperty(attr.toUtf8());
}
}
@@ -244,10 +246,9 @@ void QQuickPathViewPrivate::clear()
releaseItem(currentItem);
currentItem = 0;
}
- for (int i=0; i<items.count(); i++){
- QQuickItem *p = items[i];
+ for (QQuickItem *p : qAsConst(items))
releaseItem(p);
- }
+
if (requestedIndex >= 0) {
if (model)
model->cancel(requestedIndex);
@@ -417,12 +418,9 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
void QQuickPathView::pathUpdated()
{
Q_D(QQuickPathView);
- QList<QQuickItem*>::iterator it = d->items.begin();
- while (it != d->items.end()) {
- QQuickItem *item = *it;
+ for (QQuickItem *item : qAsConst(d->items)) {
if (QQuickPathViewAttached *att = d->attached(item))
att->m_percent = -1;
- ++it;
}
refill();
}
@@ -435,7 +433,8 @@ void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
if (qFuzzyCompare(att->m_percent, percent))
return;
att->m_percent = percent;
- foreach (const QString &attr, path->attributes())
+ const auto attributes = path->attributes();
+ for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
att->setOnPath(percent < 1.0);
}
@@ -1518,17 +1517,8 @@ void QQuickPathView::positionViewAtIndex(int index, int mode)
int QQuickPathView::indexAt(qreal x, qreal y) const
{
Q_D(const QQuickPathView);
- if (!d->isValid())
- return -1;
-
- for (int idx = 0; idx < d->items.count(); ++idx) {
- QQuickItem *item = d->items.at(idx);
- QPointF p = item->mapFromItem(this, QPointF(x, y));
- if (item->contains(p))
- return d->model->indexOf(item, 0);
- }
-
- return -1;
+ QQuickItem *item = itemAt(x, y);
+ return item ? d->model->indexOf(item, 0) : -1;
}
/*!
@@ -1545,8 +1535,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
if (!d->isValid())
return 0;
- for (int idx = 0; idx < d->items.count(); ++idx) {
- QQuickItem *item = d->items.at(idx);
+ for (QQuickItem *item : d->items) {
QPointF p = item->mapFromItem(this, QPointF(x, y));
if (item->contains(p))
return item;
@@ -1557,8 +1546,9 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
- qreal samples = qMin(path->path().length()/5, qreal(500.0));
- qreal res = path->path().length()/samples;
+ const auto pathLength = path->path().length();
+ qreal samples = qMin(pathLength / 5, qreal(500.0));
+ qreal res = pathLength / samples;
qreal mindist = 1e10; // big number
QPointF nearPoint = path->pointAt(0);
@@ -1755,12 +1745,13 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
qreal velocity = calcVelocity();
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
- qreal pixelVelocity = (path->path().length()/count) * velocity;
+ const auto averageItemLength = path->path().length() / count;
+ qreal pixelVelocity = averageItemLength * velocity;
if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
- velocity = maxVel / (path->path().length()/count);
+ velocity = maxVel / averageItemLength;
}
// Calculate the distance to be travelled
qreal v2 = velocity*velocity;
@@ -2004,8 +1995,8 @@ void QQuickPathView::refill()
endPos = -1.0;
startPos = 2.0;
- for (int i = 0; i < d->items.count(); i++) {
- int idx = d->model->indexOf(d->items[i], 0);
+ for (QQuickItem * item : qAsConst(d->items)) {
+ int idx = d->model->indexOf(item, 0);
qreal curPos = d->positionOfIndex(idx);
if (curPos > endPos) {
endPos = curPos;
@@ -2096,7 +2087,7 @@ void QQuickPathView::refill()
if (!waiting && d->items.count() < count+d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count();
idx = startIdx;
- QQuickItem *lastItem = d->items[0];
+ QQuickItem *lastItem = d->items.at(0);
while (idx != endIdx) {
nextPos = d->positionOfIndex(idx);
if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
@@ -2164,8 +2155,9 @@ void QQuickPathView::refill()
if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
att->setOnPath(currentVisible);
}
- while (d->itemCache.count())
- d->releaseItem(d->itemCache.takeLast());
+ for (QQuickItem *item : qAsConst(d->itemCache))
+ d->releaseItem(item);
+ d->itemCache.clear();
d->inRefill = false;
if (currentChanged)
@@ -2193,7 +2185,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
int moveOffset = 0;
bool currentChanged = false;
bool changedOffset = false;
- foreach (const QQmlChangeSet::Change &r, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &r : changeSet.removes()) {
if (moveId == -1 && d->currentIndex >= r.index + r.count) {
d->currentIndex -= r.count;
currentChanged = true;
@@ -2219,7 +2211,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
}
d->modelCount -= r.count;
}
- foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
if (d->modelCount) {
if (moveId == -1 && i.index <= d->currentIndex) {
d->currentIndex += i.count;
@@ -2249,8 +2241,9 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
d->items.clear();
if (!d->modelCount) {
- while (d->itemCache.count())
- d->releaseItem(d->itemCache.takeLast());
+ for (QQuickItem * item : qAsConst(d->itemCache))
+ d->releaseItem(item);
+ d->itemCache.clear();
d->offset = 0;
changedOffset = true;
d->tl.reset(d->moveOffset);
@@ -2318,8 +2311,8 @@ void QQuickPathViewPrivate::createCurrentItem()
return;
bool inItems = false;
- for (int i = 0; i < items.count(); i++) {
- if (model->indexOf(items[i], 0) == currentIndex) {
+ for (QQuickItem *item : qAsConst(items)) {
+ if (model->indexOf(item, 0) == currentIndex) {
inItems = true;
break;
}
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index daec965f02..bdd56b6311 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickpath_p.h>
QT_BEGIN_NAMESPACE
@@ -61,7 +62,7 @@ class QQmlChangeSet;
class QQuickPathViewPrivate;
class QQuickPathViewAttached;
-class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem
+class Q_QUICK_PRIVATE_EXPORT QQuickPathView : public QQuickItem
{
Q_OBJECT
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index d9c4baf572..0441603a4b 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -61,6 +61,7 @@
#include <private/qquickanimation_p_p.h>
#include <private/qqmldelegatemodel_p.h>
#include <private/qquicktimeline_p_p.h>
+#include <private/qpodvector_p.h>
QT_BEGIN_NAMESPACE
@@ -75,9 +76,8 @@ public:
void init();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE {
- if ((newGeometry.size() != oldGeometry.size())
- && (!highlightItem || item != highlightItem)) {
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE {
+ if (change.sizeChange() && (!highlightItem || item != highlightItem)) {
if (QQuickPathViewAttached *att = attached(item))
att->m_percent = -1;
scheduleLayout();
diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h
index f0fbac2df7..d18f9b3724 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -137,9 +137,9 @@ public:
setPositioningDirty();
}
- void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE
{
- if (newGeometry.size() != oldGeometry.size())
+ if (change.sizeChange())
setPositioningDirty();
}
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 05ff0c0fda..7ba2421d62 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -44,7 +44,6 @@
#include <private/qsgadaptationlayer_p.h>
#include <QtGui/qpixmapcache.h>
-#include <QtCore/qstringbuilder.h>
#include <QtCore/qmath.h>
#include <QtCore/qmetaobject.h>
@@ -73,7 +72,7 @@ QT_BEGIN_NAMESPACE
QQuickPen::QQuickPen(QObject *parent)
: QObject(parent)
, m_width(1)
- , m_color("#000000")
+ , m_color(Qt::black)
, m_aligned(true)
, m_valid(false)
{
@@ -483,8 +482,8 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
return 0;
}
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(d->color);
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index e36df53d38..8b69447002 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -44,7 +44,11 @@
#include <QtCore/QTime>
#include <QtQuick/private/qquickanimatorcontroller_p.h>
-#include <QtGui/QOpenGLContext>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -52,14 +56,13 @@
#include <QtQuick/QQuickWindow>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qsgsoftwarerenderer_p.h>
#include <QtCore/private/qobject_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
QT_BEGIN_NAMESPACE
-
+#ifndef QT_NO_OPENGL
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-
+#endif
/*!
\class QQuickRenderControl
@@ -121,6 +124,10 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
To send events, for example mouse or keyboard events, to the scene, use
QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver.
+ \note In general QQuickRenderControl is supported in combination with all Qt
+ Quick backends. However, some functionality, in particular grab(), may not be
+ available in all cases.
+
\inmodule QtQuick
*/
@@ -134,7 +141,7 @@ QQuickRenderControlPrivate::QQuickRenderControlPrivate()
qAddPostRoutine(cleanup);
sg = QSGContext::createDefaultContext();
}
- rc = new QSGRenderContext(sg);
+ rc = sg->createRenderContext();
}
void QQuickRenderControlPrivate::cleanup()
@@ -183,7 +190,9 @@ void QQuickRenderControlPrivate::windowDestroyed()
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = 0;
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
window = 0;
}
@@ -204,8 +213,9 @@ void QQuickRenderControl::prepareThread(QThread *targetThread)
}
/*!
- Initializes the scene graph resources. The context \a gl has to
- be the current context.
+ Initializes the scene graph resources. The context \a gl has to be the
+ current OpenGL context or null if it is not relevant because a Qt Quick
+ backend other than OpenGL is in use.
\note Qt Quick does not take ownership of the context. It is up to the
application to destroy it after a call to invalidate() or after the
@@ -213,8 +223,9 @@ void QQuickRenderControl::prepareThread(QThread *targetThread)
*/
void QQuickRenderControl::initialize(QOpenGLContext *gl)
{
- Q_D(QQuickRenderControl);
+ Q_D(QQuickRenderControl);
+#ifndef QT_NO_OPENGL
if (!d->window) {
qWarning("QQuickRenderControl::initialize called with no associated window");
return;
@@ -229,9 +240,10 @@ void QQuickRenderControl::initialize(QOpenGLContext *gl)
// It cannot be done here since the surface to use may not be the
// surface belonging to window. In fact window may not have a native
// window/surface at all.
-
d->rc->initialize(gl);
-
+#else
+ Q_UNUSED(gl)
+#endif
d->initialized = true;
}
@@ -247,7 +259,7 @@ void QQuickRenderControl::polishItems()
return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
- cd->flushDelayedTouchEvent();
+ cd->flushFrameSynchronousEvents();
if (!d->window)
return;
cd->polishItems();
@@ -362,8 +374,31 @@ QImage QQuickRenderControl::grab()
if (!d->window)
return QImage();
- render();
- QImage grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
+ QImage grabContent;
+
+ if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) {
+#ifndef QT_NO_OPENGL
+ render();
+ grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
+#endif
+ } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
+ if (softwareRenderer) {
+ const qreal dpr = d->window->effectiveDevicePixelRatio();
+ const QSize imageSize = d->window->size() * dpr;
+ grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
+ grabContent.setDevicePixelRatio(dpr);
+ QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
+ softwareRenderer->setCurrentPaintDevice(&grabContent);
+ softwareRenderer->markDirty();
+ render();
+ softwareRenderer->setCurrentPaintDevice(prevDev);
+ }
+ } else {
+ qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
+ }
+
return grabContent;
}
diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp
index 198573fda5..4f46f41b0d 100644
--- a/src/quick/items/qquickrepeater.cpp
+++ b/src/quick/items/qquickrepeater.cpp
@@ -461,7 +461,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
int difference = 0;
QHash<int, QVector<QPointer<QQuickItem> > > moved;
- foreach (const QQmlChangeSet::Change &remove, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &remove : changeSet.removes()) {
int index = qMin(remove.index, d->deletables.count());
int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
if (remove.isMove()) {
@@ -483,7 +483,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
difference -= remove.count;
}
- foreach (const QQmlChangeSet::Change &insert, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &insert : changeSet.inserts()) {
int index = qMin(insert.index, d->deletables.count());
if (insert.isMove()) {
QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index 5b337baac4..5670696ce2 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -38,506 +38,15 @@
****************************************************************************/
#include <private/qquickshadereffect_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
-#include <QtQuick/qsgmaterial.h>
-#include <QtQuick/private/qsgshadersourcebuilder_p.h>
-#include "qquickitem_p.h"
-
-#include <QtQuick/private/qsgcontext_p.h>
-#include <QtQuick/qsgtextureprovider.h>
-#include "qquickwindow.h"
-
-#include "qquickimage_p.h"
-#include "qquickshadereffectsource_p.h"
-
-#include <QtCore/qsignalmapper.h>
-#include <QtGui/qopenglframebufferobject.h>
+#include <private/qsgcontextplugin_p.h>
+#include <private/qquickitem_p.h>
+#ifndef QT_NO_OPENGL
+#include <private/qquickopenglshadereffect_p.h>
+#endif
+#include <private/qquickgenericshadereffect_p.h>
QT_BEGIN_NAMESPACE
-static const char qt_position_attribute_name[] = "qt_Vertex";
-static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
-
-const char *qtPositionAttributeName()
-{
- return qt_position_attribute_name;
-}
-
-const char *qtTexCoordAttributeName()
-{
- return qt_texcoord_attribute_name;
-}
-
-namespace {
-
- enum VariableQualifier {
- AttributeQualifier,
- UniformQualifier
- };
-
- inline bool qt_isalpha(char c)
- {
- char ch = c | 0x20;
- return (ch >= 'a' && ch <= 'z') || c == '_';
- }
-
- inline bool qt_isalnum(char c)
- {
- return qt_isalpha(c) || (c >= '0' && c <= '9');
- }
-
- inline bool qt_isspace(char c)
- {
- return c == ' ' || (c >= 0x09 && c <= 0x0d);
- }
-
- // Returns -1 if not found, returns index to first character after the name if found.
- int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
- int &typeIndex, int &typeLength,
- int &nameIndex, int &nameLength,
- QQuickShaderEffectCommon::Key::ShaderType shaderType)
- {
- enum Identifier {
- QualifierIdentifier, // Base state
- PrecisionIdentifier,
- TypeIdentifier,
- NameIdentifier
- };
- Identifier expected = QualifierIdentifier;
- bool compilerDirectiveExpected = index == 0;
-
- while (index < length) {
- // Skip whitespace.
- while (qt_isspace(s[index])) {
- compilerDirectiveExpected |= s[index] == '\n';
- ++index;
- }
-
- if (qt_isalpha(s[index])) {
- // Read identifier.
- int idIndex = index;
- ++index;
- while (qt_isalnum(s[index]))
- ++index;
- int idLength = index - idIndex;
-
- const int attrLen = sizeof("attribute") - 1;
- const int inLen = sizeof("in") - 1;
- const int uniLen = sizeof("uniform") - 1;
- const int loLen = sizeof("lowp") - 1;
- const int medLen = sizeof("mediump") - 1;
- const int hiLen = sizeof("highp") - 1;
-
- switch (expected) {
- case QualifierIdentifier:
- if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
- decl = AttributeQualifier;
- expected = PrecisionIdentifier;
- } else if (shaderType == QQuickShaderEffectCommon::Key::VertexShader
- && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) {
- decl = AttributeQualifier;
- expected = PrecisionIdentifier;
- } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
- decl = UniformQualifier;
- expected = PrecisionIdentifier;
- }
- break;
- case PrecisionIdentifier:
- if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
- || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
- || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
- {
- expected = TypeIdentifier;
- break;
- }
- // Fall through.
- case TypeIdentifier:
- typeIndex = idIndex;
- typeLength = idLength;
- expected = NameIdentifier;
- break;
- case NameIdentifier:
- nameIndex = idIndex;
- nameLength = idLength;
- return index; // Attribute or uniform declaration found. Return result.
- default:
- break;
- }
- } else if (s[index] == '#' && compilerDirectiveExpected) {
- // Skip compiler directives.
- ++index;
- while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
- ++index;
- } else if (s[index] == '/' && s[index + 1] == '/') {
- // Skip comments.
- index += 2;
- while (index < length && s[index] != '\n')
- ++index;
- } else if (s[index] == '/' && s[index + 1] == '*') {
- // Skip comments.
- index += 2;
- while (index < length && (s[index] != '*' || s[index + 1] != '/'))
- ++index;
- if (index < length)
- index += 2; // Skip star-slash.
- } else {
- expected = QualifierIdentifier;
- ++index;
- }
- compilerDirectiveExpected = false;
- }
- return -1;
- }
-}
-
-
-
-QQuickShaderEffectCommon::~QQuickShaderEffectCommon()
-{
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
- qDeleteAll(signalMappers[shaderType]);
-}
-
-void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
-{
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (signalMappers[shaderType].at(i) == 0)
- continue;
- const UniformData &d = uniformData[shaderType].at(i);
- QSignalMapper *mapper = signalMappers[shaderType].at(i);
- QObject::disconnect(item, 0, mapper, SLOT(map()));
- QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->derefWindow();
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
-{
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (signalMappers[shaderType].at(i) == 0)
- continue;
- const UniformData &d = uniformData[shaderType].at(i);
- int pi = item->metaObject()->indexOfProperty(d.name.constData());
- if (pi >= 0) {
- QMetaProperty mp = item->metaObject()->property(pi);
- if (!mp.hasNotifySignal())
- qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData());
- const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
- QSignalMapper *mapper = signalMappers[shaderType].at(i);
- QObject::connect(item, signalName, mapper, SLOT(map()));
- QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
- } else {
- // If the source is set via a dynamic property, like the layer is, then we need this
- // check to disable the warning.
- if (!item->property(d.name.constData()).isValid())
- qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData());
- }
-
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->refWindow(item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes)
-{
- parseLog.clear();
- if (!ignoreAttributes) {
- if (!attributes.contains(qt_position_attribute_name)) {
- parseLog += QLatin1String("Warning: Missing reference to \'");
- parseLog += QLatin1String(qt_position_attribute_name);
- parseLog += QLatin1String("\'.\n");
- }
- if (!attributes.contains(qt_texcoord_attribute_name)) {
- parseLog += QLatin1String("Warning: Missing reference to \'");
- parseLog += QLatin1String(qt_texcoord_attribute_name);
- parseLog += QLatin1String("\'.\n");
- }
- }
- bool respectsMatrix = false;
- bool respectsOpacity = false;
- for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
- respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i)
- respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
- }
- if (!respectsMatrix)
- parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
- if (!respectsOpacity)
- parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
-}
-
-void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
-{
- int index = 0;
- int typeIndex = -1;
- int typeLength = 0;
- int nameIndex = -1;
- int nameLength = 0;
- const char *s = code.constData();
- VariableQualifier decl = AttributeQualifier;
- while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
- nameIndex, nameLength, shaderType)) != -1)
- {
- if (decl == AttributeQualifier) {
- if (shaderType == Key::VertexShader)
- attributes.append(QByteArray(s + nameIndex, nameLength));
- } else {
- Q_ASSERT(decl == UniformQualifier);
-
- const int sampLen = sizeof("sampler2D") - 1;
- const int opLen = sizeof("qt_Opacity") - 1;
- const int matLen = sizeof("qt_Matrix") - 1;
- const int srLen = sizeof("qt_SubRect_") - 1;
-
- UniformData d;
- QSignalMapper *mapper = 0;
- d.name = QByteArray(s + nameIndex, nameLength);
- if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
- d.specialType = UniformData::Opacity;
- } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
- d.specialType = UniformData::Matrix;
- } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
- d.specialType = UniformData::SubRect;
- } else {
- mapper = new QSignalMapper;
- mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
- d.value = item->property(d.name.constData());
- bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
- d.specialType = sampler ? UniformData::Sampler : UniformData::None;
- }
- uniformData[shaderType].append(d);
- signalMappers[shaderType].append(mapper);
- }
- }
-}
-
-void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
-{
- disconnectPropertySignals(item, shaderType);
- qDeleteAll(signalMappers[shaderType]);
- uniformData[shaderType].clear();
- signalMappers[shaderType].clear();
- if (shaderType == Key::VertexShader)
- attributes.clear();
-
- const QByteArray &code = source.sourceCode[shaderType];
- if (code.isEmpty()) {
- // Optimize for default code.
- if (shaderType == Key::VertexShader) {
- attributes.append(QByteArray(qt_position_attribute_name));
- attributes.append(QByteArray(qt_texcoord_attribute_name));
- UniformData d;
- d.name = "qt_Matrix";
- d.specialType = UniformData::Matrix;
- uniformData[Key::VertexShader].append(d);
- signalMappers[Key::VertexShader].append(0);
- } else if (shaderType == Key::FragmentShader) {
- UniformData d;
- d.name = "qt_Opacity";
- d.specialType = UniformData::Opacity;
- uniformData[Key::FragmentShader].append(d);
- signalMappers[Key::FragmentShader].append(0);
- QSignalMapper *mapper = new QSignalMapper;
- mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
- const char *sourceName = "source";
- d.name = sourceName;
- d.value = item->property(sourceName);
- d.specialType = UniformData::Sampler;
- uniformData[Key::FragmentShader].append(d);
- signalMappers[Key::FragmentShader].append(mapper);
- }
- } else {
- lookThroughShaderCode(item, shaderType, code);
- }
-
- connectPropertySignals(item, shaderType);
-}
-
-void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
- QQuickShaderEffectMaterial *material,
- bool updateUniforms, bool updateUniformValues,
- bool updateTextureProviders)
-{
- if (updateUniforms) {
- for (int i = 0; i < material->textureProviders.size(); ++i) {
- QSGTextureProvider *t = material->textureProviders.at(i);
- if (t) {
- QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- }
-
- // First make room in the textureProviders array. Set to proper value further down.
- 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)
- ++textureProviderCount;
- }
- material->uniforms[shaderType] = uniformData[shaderType];
- }
- material->textureProviders.fill(0, textureProviderCount);
- updateUniformValues = false;
- updateTextureProviders = true;
- }
-
- if (updateUniformValues) {
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
- for (int i = 0; i < uniformData[shaderType].size(); ++i)
- material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
- }
- }
-
- if (updateTextureProviders) {
- int index = 0;
- 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)
- continue;
- QSGTextureProvider *oldProvider = material->textureProviders.at(index);
- QSGTextureProvider *newProvider = 0;
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source && source->isTextureProvider())
- newProvider = source->textureProvider();
- if (newProvider != oldProvider) {
- if (oldProvider) {
- QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- if (newProvider) {
- Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
- "QQuickShaderEffect::updatePaintNode",
- "Texture provider must belong to the rendering thread");
- QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- } else {
- const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
- qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
- d.name.constData(), typeName);
- }
- material->textureProviders[index] = newProvider;
- }
- ++index;
- }
- }
- Q_ASSERT(index == material->textureProviders.size());
- }
-}
-
-void QQuickShaderEffectCommon::updateWindow(QQuickWindow *window)
-{
- // See comment in QQuickShaderEffectCommon::propertyChanged().
- if (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) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source)
- QQuickItemPrivate::get(source)->refWindow(window);
- }
- }
- }
- } else {
- 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) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source)
- QQuickItemPrivate::get(source)->derefWindow();
- }
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::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<QObject *>()) {
- if (qvariant_cast<QObject *>(d.value) == object)
- d.value = QVariant();
- }
- }
- }
-}
-
-static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip)
-{
- for (int s=0; s<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++s) {
- for (int i=0; i<uniformData[s].size(); ++i) {
- if (s == typeToSkip && i == indexToSkip)
- continue;
- const QQuickShaderEffectMaterial::UniformData &d = uniformData[s][i];
- if (d.specialType == QQuickShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
- return false;
- }
- }
- return true;
-}
-
-void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
- bool *textureProviderChanged)
-{
- Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
- int index = mappedId & 0xffff;
- UniformData &d = uniformData[shaderType][index];
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->derefWindow();
-
- // QObject::disconnect() will disconnect all matching connections. If the same
- // source has been attached to two separate samplers, then changing one of them
- // would trigger both to be disconnected. Without the connection we'll end up
- // with a dangling pointer in the uniformData.
- if (qquick_uniqueInUniformData(source, uniformData, shaderType, index))
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
-
- d.value = item->property(d.name.constData());
-
- source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- // 'source' needs a window to get a scene graph node. It usually gets one through its
- // parent, but if the source item is "inline" rather than a reference -- i.e.
- // "property variant source: Image { }" instead of "property variant source: foo" -- it
- // will not get a parent. In those cases, 'source' should get the window from 'item'.
- if (item->window())
- QQuickItemPrivate::get(source)->refWindow(item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- if (textureProviderChanged)
- *textureProviderChanged = true;
- } else {
- d.value = item->property(d.name.constData());
- if (textureProviderChanged)
- *textureProviderChanged = false;
- }
-}
-
-
/*!
\qmltype ShaderEffect
\instantiates QQuickShaderEffect
@@ -546,11 +55,18 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\ingroup qtquick-effects
\brief Applies custom shaders to a rectangle
- The ShaderEffect type applies a custom OpenGL
- \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
+ The ShaderEffect type applies a custom
+ \l{vertexShader}{vertex} and \l{fragmentShader}{fragment (pixel)} shader to a
rectangle. It allows you to write effects such as drop shadow, blur,
colorize and page curl directly in QML.
+ \note Depending on the Qt Quick scenegraph backend in use, the ShaderEffect
+ type may not be supported (for example, with the software backend), or may
+ use a different shading language with rules and expectations different from
+ OpenGL and GLSL.
+
+ \section1 OpenGL and GLSL
+
There are two types of input to the \l vertexShader:
uniform variables and attributes. Some are predefined:
\list
@@ -650,9 +166,293 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\endqml
\endtable
- By default, the ShaderEffect consists of four vertices, one for each
- corner. For non-linear vertex transformations, like page curl, you can
- specify a fine grid of vertices by specifying a \l mesh resolution.
+ \note Scene Graph textures have origin in the top-left corner rather than
+ bottom-left which is common in OpenGL.
+
+ For information about the GLSL version being used, see \l QtQuick::GraphicsInfo.
+
+ Starting from Qt 5.8 ShaderEffect also supports reading the GLSL source
+ code from files. Whenever the fragmentShader or vertexShader property value
+ is a URL with the \c file or \c qrc schema, it is treated as a file
+ reference and the source code is read from the specified file.
+
+ \section1 Direct3D and HLSL
+
+ Direct3D backends provide ShaderEffect support with HLSL. The Direct3D 12
+ backend requires using at least Shader Model 5.0 both for vertex and pixel
+ shaders. When necessary, GraphicsInfo.shaderType can be used to decide
+ at runtime what kind of value to assign to \l fragmentShader or
+ \l vertexShader.
+
+ All concepts described above for OpenGL and GLSL apply to Direct3D and HLSL
+ as well. There are however a number of notable practical differences, which
+ are the following:
+
+ Instead of uniforms, HLSL shaders are expected to use a single constant
+ buffer, assigned to register \c b0. The special names \c qt_Matrix,
+ \c qt_Opacity, and \c qt_SubRect_<name> function the same way as with GLSL.
+ All other members of the buffer are expected to map to properties in the
+ ShaderEffect item.
+
+ \note The buffer layout must be compatible for both shaders. This means
+ that application-provided shaders must make sure \c qt_Matrix and
+ \c qt_Opacity are included in the buffer, starting at offset 0, when custom
+ code is provided for one type of shader only, leading to ShaderEffect
+ providing the other shader. This is due to ShaderEffect's built-in shader code
+ declaring a constant buffer containing \c{float4x4 qt_Matrix; float qt_Opacity;}.
+
+ Unlike GLSL's attributes, no names are used for vertex input elements.
+ Therefore qt_Vertex and qt_MultiTexCoord0 are not relevant. Instead, the
+ standard Direct3D semantics, \c POSITION and \c TEXCOORD (or \c TEXCOORD0)
+ are used for identifying the correct input layout.
+
+ Unlike GLSL's samplers, texture and sampler objects are separate in HLSL.
+ Shaders are expected to expect 2D, non-array, non-multisample textures.
+ Both the texture and sampler binding points are expected to be sequential
+ and start from 0 (meaning registers \c{t0, t1, ...}, and \c{s0, s1, ...},
+ respectively). Unlike with OpenGL, samplers are not mapped to Qt Quick item
+ properties and therefore the name of the sampler is not relevant. Instead,
+ it is the textures that map to properties referencing \l Image or
+ \l ShaderEffectSource items.
+
+ Unlike OpenGL, backends for modern APIs will typically prefer offline
+ compilation and shipping pre-compiled bytecode with applications instead of
+ inlined shader source strings. In this case the string properties for
+ vertex and fragment shaders are treated as URLs referring to local files or
+ files shipped via the Qt resource system.
+
+ To check at runtime what is supported, use the
+ GraphicsInfo.shaderSourceType and GraphicsInfo.shaderCompilationType
+ properties. Note that these are bitmasks, because some backends may support
+ multiple approaches.
+
+ In case of Direct3D 12, all combinations are supported. If the vertexShader
+ and fragmentShader properties form a valid URL with the \c file or \c qrc
+ schema, the bytecode or HLSL source code is read from the specified file.
+ The type of the file contents is detected automatically. Otherwise, the
+ string is treated as HLSL source code and is compiled at runtime, assuming
+ Shader Model 5.0 and an entry point of \c{"main"}. This allows dynamically
+ constructing shader strings. However, whenever the shader source code is
+ static, it is strongly recommended to pre-compile to bytecode using the
+ \c fxc tool and refer to these files from QML. This will be a lot more
+ efficient at runtime and allows catching syntax errors in the shaders at
+ compile time.
+
+ Unlike OpenGL, the Direct3D backend is able to perform runtime shader
+ compilation on dedicated threads. This is managed transparently to the
+ applications, and means that ShaderEffect items that contain HLSL source
+ strings do not block the rendering or other parts of the application until
+ the bytecode is ready.
+
+ Using files with bytecode is more flexible also when it comes to the entry
+ point name (it can be anything, not limited to \c main) and the shader
+ model (it can be something newer than 5.0, for instance 5.1).
+
+ \table 70%
+ \row
+ \li \qml
+ import QtQuick 2.0
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ fragmentShader: "qrc:/effect_ps.cso"
+ }
+ }
+ }
+ \endqml
+ \row
+ \li where \c effect_ps.cso is the compiled bytecode for the following HLSL shader:
+ \code
+ cbuffer ConstantBuffer : register(b0)
+ {
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ };
+ Texture2D src : register(t0);
+ SamplerState srcSampler : register(s0);
+ float4 ExamplePixelShader(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+ {
+ float4 tex = src.Sample(srcSampler, coord);
+ float3 col = dot(tex.rgb, float3(0.344, 0.5, 0.156));
+ return float4(col, tex.a) * qt_Opacity;
+ }
+ \endcode
+ \endtable
+
+ The above is equivalent to the OpenGL example presented earlier. The vertex
+ shader is provided implicitly by ShaderEffect. Note that the output of the
+ pixel shader is using premultiplied alpha and that \c qt_Matrix is present
+ in the constant buffer at offset 0, even though the pixel shader does not
+ use the value.
+
+ If desired, the HLSL source code can be placed directly into the QML
+ source, similarly to how its done with GLSL. The only difference in this
+ case is the entry point name, which must be \c main when using inline
+ source strings.
+
+ Alternatively, we could also have referred to a file containing the source
+ of the effect instead of the compiled bytecode version.
+
+ Some effects will want to provide a vertex shader as well. Below is a
+ similar effect with both the vertex and fragment shader provided by the
+ application. This time the colorization factor is provided by the QML item
+ instead of hardcoding it in the shader. This can allow, among others,
+ animating the value using QML's and Qt Quick's standard facilities.
+
+ \table 70%
+ \row
+ \li \qml
+ import QtQuick 2.0
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ property variant color: Qt.vector3d(0.344, 0.5, 0.156)
+ vertexShader: "qrc:/effect_vs.cso"
+ fragmentShader: "qrc:/effect_ps.cso"
+ }
+ }
+ }
+ \endqml
+ \row
+ \li where \c effect_vs.cso and \c effect_ps.cso are the compiled bytecode
+ for \c ExampleVertexShader and \c ExamplePixelShader. The source code is
+ presented as one snippet here, the shaders can however be placed in
+ separate source files as well.
+ \code
+ cbuffer ConstantBuffer : register(b0)
+ {
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float3 color;
+ };
+ Texture2D src : register(t0);
+ SamplerState srcSampler : register(s0);
+ struct PSInput
+ {
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ };
+ PSInput ExampleVertexShader(float4 position : POSITION, float2 coord : TEXCOORD0)
+ {
+ PSInput result;
+ result.position = mul(qt_Matrix, position);
+ result.coord = coord;
+ return result;
+ }
+ float4 ExamplePixelShader(PSInput input) : SV_TARGET
+ {
+ float4 tex = src.Sample(srcSampler, coord);
+ float3 col = dot(tex.rgb, color);
+ return float4(col, tex.a) * qt_Opacity;
+ }
+ \endcode
+ \endtable
+
+ \note With OpenGL the \c y coordinate runs from bottom to top whereas with
+ Direct 3D it goes top to bottom. For shader effect sources Qt Quick hides
+ the difference by treating QtQuick::ShaderEffectSource::textureMirroring as
+ appropriate, meaning texture coordinates in HLSL version of the shaders
+ will not need any adjustments compared to the equivalent GLSL code.
+
+ \section1 Cross-platform, Cross-API ShaderEffect Items
+
+ Some applications will want to be functional with multiple accelerated
+ graphics backends. This has consequences for ShaderEffect items because the
+ supported shading languages may vary from backend to backend.
+
+ There are two approaches to handle this: either write conditional property
+ values based on GraphicsInfo.shaderType, or use file selectors. In practice
+ the latter is strongly recommended as it leads to more concise and cleaner
+ application code. The only case it is not suitable is when the source
+ strings are constructed dynamically.
+
+ \table 70%
+ \row
+ \li \qml
+ import QtQuick 2.8 // for GraphicsInfo
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ property variant color: Qt.vector3d(0.344, 0.5, 0.156)
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ?
+ "varying highp vec2 coord;
+ uniform sampler2D src;
+ uniform lowp float qt_Opacity;
+ void main() {
+ lowp vec4 tex = texture2D(src, coord);
+ gl_FragColor = vec4(vec3(dot(tex.rgb,
+ vec3(0.344, 0.5, 0.156))),
+ tex.a) * qt_Opacity;"
+ : GraphicsInfo.shaderType === GraphicsInfo.HLSL ?
+ "cbuffer ConstantBuffer : register(b0)
+ {
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ };
+ Texture2D src : register(t0);
+ SamplerState srcSampler : register(s0);
+ float4 ExamplePixelShader(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+ {
+ float4 tex = src.Sample(srcSampler, coord);
+ float3 col = dot(tex.rgb, float3(0.344, 0.5, 0.156));
+ return float4(col, tex.a) * qt_Opacity;
+ }"
+ : ""
+ }
+ }
+ }
+ \endqml
+ \row
+
+ \li This is the first approach based on GraphicsInfo. Note that the value
+ reported by GraphicsInfo is not up-to-date until the ShaderEffect item gets
+ associated with a QQuickWindow. Before that, the reported value is
+ GraphicsInfo.UnknownShadingLanguage. The alternative is to place the GLSL
+ source code and the compiled D3D bytecode into the files
+ \c{shaders/effect.frag} and \c{shaders/+hlsl/effect.frag}, include them in
+ the Qt resource system, and let the ShaderEffect's internal QFileSelector
+ do its job. The selector-less version is the GLSL source, while the \c hlsl
+ selector is used when running on the D3D12 backend. The file under
+ \c{+hlsl} can then contain either HLSL source code or compiled bytecode
+ from the \c fxc tool. Additionally, when using a version 3.2 or newer core
+ profile context with OpenGL, GLSL sources with a core profile compatible
+ syntax can be placed under \c{+glslcore}.
+ \qml
+ import QtQuick 2.8 // for GraphicsInfo
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ property variant color: Qt.vector3d(0.344, 0.5, 0.156)
+ fragmentShader: "qrc:shaders/effect.frag" // selects the correct variant automatically
+ }
+ }
+ }
+ \endqml
+ \endtable
\section1 ShaderEffect and Item Layers
@@ -675,97 +475,125 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\li \snippet qml/opacitymask.qml 1
\endtable
- The \l {Qt Graphical Effects} module contains several ready-made effects
- for using with Qt Quick applications.
+ \section1 Other notes
- \note Scene Graph textures have origin in the top-left corner rather than
- bottom-left which is common in OpenGL.
+ By default, the ShaderEffect consists of four vertices, one for each
+ corner. For non-linear vertex transformations, like page curl, you can
+ specify a fine grid of vertices by specifying a \l mesh resolution.
- For information about the GLSL version being used, see \l QtQuick::OpenGLInfo.
+ The \l {Qt Graphical Effects} module contains several ready-made effects
+ for using with Qt Quick applications.
\sa {Item Layers}
*/
+class QQuickShaderEffectPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickShaderEffect)
+
+public:
+ void updatePolish() override;
+};
+
+QSGContextFactoryInterface::Flags qsg_backend_flags();
+
QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
- : QQuickItem(parent)
- , m_meshResolution(1, 1)
- , m_mesh(0)
- , m_cullMode(NoCulling)
- , m_status(Uncompiled)
- , m_blending(true)
- , m_dirtyUniforms(true)
- , m_dirtyUniformValues(true)
- , m_dirtyTextureProviders(true)
- , m_dirtyProgram(true)
- , m_dirtyParseLog(true)
- , m_dirtyMesh(true)
- , m_dirtyGeometry(true)
- , m_customVertexShader(false)
- , m_supportsAtlasTextures(false)
+ : QQuickItem(*new QQuickShaderEffectPrivate, parent),
+#ifndef QT_NO_OPENGL
+ m_glImpl(nullptr),
+#endif
+ m_impl(nullptr)
{
setFlag(QQuickItem::ItemHasContents);
-}
-QQuickShaderEffect::~QQuickShaderEffect()
-{
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
- m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
+#ifndef QT_NO_OPENGL
+ if (!qsg_backend_flags().testFlag(QSGContextFactoryInterface::SupportsShaderEffectNode))
+ m_glImpl = new QQuickOpenGLShaderEffect(this, this);
+
+ if (!m_glImpl)
+#endif
+ m_impl = new QQuickGenericShaderEffect(this, this);
}
/*!
\qmlproperty string QtQuick::ShaderEffect::fragmentShader
- This property holds the fragment shader's GLSL source code.
- The default shader expects the texture coordinate to be passed from the
- vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
- sampler2D named "source".
+ This property holds the fragment (pixel) shader's source code or a
+ reference to the pre-compiled bytecode. Some APIs, like OpenGL, always
+ support runtime compilation and therefore the traditional Qt Quick way of
+ inlining shader source strings is functional. Qt Quick backends for other
+ APIs may however limit support to pre-compiled bytecode like SPIR-V or D3D
+ shader bytecode. There the string is simply a filename, which may be a file
+ in the filesystem or bundled with the executable via Qt's resource system.
+
+ With GLSL the default shader expects the texture coordinate to be passed
+ from the vertex shader as \c{varying highp vec2 qt_TexCoord0}, and it
+ samples from a sampler2D named \c source. With HLSL the texture is named
+ \c source, while the vertex shader is expected to provide
+ \c{float2 coord : TEXCOORD0} in its output in addition to
+ \c{float4 position : SV_POSITION} (names can differ since linking is done
+ based on the semantics).
+
+ \sa vertexShader, GraphicsInfo
*/
+QByteArray QQuickShaderEffect::fragmentShader() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->fragmentShader();
+#endif
+ return m_impl->fragmentShader();
+}
+
void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
{
- if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setFragmentShader(code);
return;
- m_common.source.sourceCode[Key::FragmentShader] = code;
- m_dirtyProgram = true;
- m_dirtyParseLog = true;
-
- if (isComponentComplete())
- m_common.updateShader(this, Key::FragmentShader);
-
- update();
- if (m_status != Uncompiled) {
- m_status = Uncompiled;
- emit statusChanged();
}
- emit fragmentShaderChanged();
+#endif
+ m_impl->setFragmentShader(code);
}
/*!
\qmlproperty string QtQuick::ShaderEffect::vertexShader
- This property holds the vertex shader's GLSL source code.
- The default shader passes the texture coordinate along to the fragment
- shader as "varying highp vec2 qt_TexCoord0".
+ This property holds the vertex shader's source code or a reference to the
+ pre-compiled bytecode. Some APIs, like OpenGL, always support runtime
+ compilation and therefore the traditional Qt Quick way of inlining shader
+ source strings is functional. Qt Quick backends for other APIs may however
+ limit support to pre-compiled bytecode like SPIR-V or D3D shader bytecode.
+ There the string is simply a filename, which may be a file in the
+ filesystem or bundled with the executable via Qt's resource system.
+
+ With GLSL the default shader passes the texture coordinate along to the
+ fragment shader as \c{varying highp vec2 qt_TexCoord0}. With HLSL it is
+ enough to use the standard \c TEXCOORD0 semantic, for example
+ \c{float2 coord : TEXCOORD0}.
+
+ \sa fragmentShader, GraphicsInfo
*/
+QByteArray QQuickShaderEffect::vertexShader() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->vertexShader();
+#endif
+ return m_impl->vertexShader();
+}
+
void QQuickShaderEffect::setVertexShader(const QByteArray &code)
{
- if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setVertexShader(code);
return;
- m_common.source.sourceCode[Key::VertexShader] = code;
- m_dirtyProgram = true;
- m_dirtyParseLog = true;
- m_customVertexShader = true;
-
- if (isComponentComplete())
- m_common.updateShader(this, Key::VertexShader);
-
- update();
- if (m_status != Uncompiled) {
- m_status = Uncompiled;
- emit statusChanged();
}
- emit vertexShaderChanged();
+#endif
+ m_impl->setVertexShader(code);
}
/*!
@@ -777,15 +605,24 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code)
property to false when blending is not needed. The default value is true.
*/
+bool QQuickShaderEffect::blending() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->blending();
+#endif
+ return m_impl->blending();
+}
+
void QQuickShaderEffect::setBlending(bool enable)
{
- if (blending() == enable)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setBlending(enable);
return;
-
- m_blending = enable;
- update();
-
- emit blendingChanged();
+ }
+#endif
+ m_impl->setBlending(enable);
}
/*!
@@ -803,44 +640,22 @@ void QQuickShaderEffect::setBlending(bool enable)
QVariant QQuickShaderEffect::mesh() const
{
- return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
- : qVariantFromValue(m_meshResolution);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->mesh();
+#endif
+ return m_impl->mesh();
}
void QQuickShaderEffect::setMesh(const QVariant &mesh)
{
- QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
- if (newMesh && newMesh == m_mesh)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setMesh(mesh);
return;
- if (m_mesh)
- disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
- m_mesh = newMesh;
- if (m_mesh) {
- connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
- } else {
- if (mesh.canConvert<QSize>()) {
- m_meshResolution = mesh.toSize();
- } else {
- QList<QByteArray> res = mesh.toByteArray().split('x');
- bool ok = res.size() == 2;
- if (ok) {
- int w = res.at(0).toInt(&ok);
- if (ok) {
- int h = res.at(1).toInt(&ok);
- if (ok)
- m_meshResolution = QSize(w, h);
- }
- }
- if (!ok)
- qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
- }
- m_defaultMesh.setResolution(m_meshResolution);
}
-
- m_dirtyMesh = true;
- m_dirtyParseLog = true;
- update();
- emit meshChanged();
+#endif
+ m_impl->setMesh(mesh);
}
/*!
@@ -857,13 +672,24 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
The default is NoCulling.
*/
+QQuickShaderEffect::CullMode QQuickShaderEffect::cullMode() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->cullMode();
+#endif
+ return m_impl->cullMode();
+}
+
void QQuickShaderEffect::setCullMode(CullMode face)
{
- if (face == m_cullMode)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setCullMode(face);
return;
- m_cullMode = face;
- update();
- emit cullModeChanged();
+ }
+#endif
+ return m_impl->setCullMode(face);
}
/*!
@@ -887,41 +713,24 @@ void QQuickShaderEffect::setCullMode(CullMode face)
\since QtQuick 2.4
*/
-void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
-{
- if (supports == m_supportsAtlasTextures)
- return;
- m_supportsAtlasTextures = supports;
- updateGeometry();
- emit supportsAtlasTexturesChanged();
-}
-
-QString QQuickShaderEffect::parseLog()
+bool QQuickShaderEffect::supportsAtlasTextures() const
{
- if (m_dirtyParseLog) {
- m_common.updateParseLog(m_mesh != 0);
- m_dirtyParseLog = false;
- }
- return m_common.parseLog;
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->supportsAtlasTextures();
+#endif
+ return m_impl->supportsAtlasTextures();
}
-bool QQuickShaderEffect::event(QEvent *event)
+void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
{
- if (event->type() == QEvent::DynamicPropertyChange) {
- QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
- if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
- bool textureProviderChanged;
- m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
- m_dirtyTextureProviders |= textureProviderChanged;
- m_dirtyUniformValues = true;
- update();
- }
- }
- }
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setSupportsAtlasTextures(supports);
+ return;
}
- return QQuickItem::event(event);
+#endif
+ m_impl->setSupportsAtlasTextures(supports);
}
/*!
@@ -935,9 +744,17 @@ bool QQuickShaderEffect::event(QEvent *event)
\li ShaderEffect.Error - the shader program failed to compile or link.
\endlist
- When setting the fragment or vertex shader source code, the status will become Uncompiled.
- The first time the ShaderEffect is rendered with new shader source code, the shaders are
- compiled and linked, and the status is updated to Compiled or Error.
+ When setting the fragment or vertex shader source code, the status will
+ become Uncompiled. The first time the ShaderEffect is rendered with new
+ shader source code, the shaders are compiled and linked, and the status is
+ updated to Compiled or Error.
+
+ When runtime compilation is not in use and the shader properties refer to
+ files with bytecode, the status is always Compiled. The contents of the
+ shader is not examined (apart from basic reflection to discover vertex
+ input elements and constant buffer data) until later in the rendering
+ pipeline so potential errors (like layout or root signature mismatches)
+ will only be detected at a later point.
\sa log
*/
@@ -945,185 +762,115 @@ bool QQuickShaderEffect::event(QEvent *event)
/*!
\qmlproperty string QtQuick::ShaderEffect::log
- This property holds a log of warnings and errors from the latest attempt at compiling and
- linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
- or Error.
+ This property holds a log of warnings and errors from the latest attempt at
+ compiling and linking the OpenGL shader program. It is updated at the same
+ time \l status is set to Compiled or Error.
\sa status
*/
-void QQuickShaderEffect::updateGeometry()
+QString QQuickShaderEffect::log() const
{
- m_dirtyGeometry = true;
- update();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->log();
+#endif
+ return m_impl->log();
}
-void QQuickShaderEffect::updateGeometryIfAtlased()
+QQuickShaderEffect::Status QQuickShaderEffect::status() const
{
- if (m_supportsAtlasTextures)
- updateGeometry();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->status();
+#endif
+ return m_impl->status();
}
-void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
+bool QQuickShaderEffect::event(QEvent *e)
{
- m_log = parseLog() + log;
- m_status = Status(status);
- emit logChanged();
- emit statusChanged();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleEvent(e);
+ return QQuickItem::event(e);
+ }
+#endif
+ m_impl->handleEvent(e);
+ return QQuickItem::event(e);
}
-void QQuickShaderEffect::sourceDestroyed(QObject *object)
+void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- m_common.sourceDestroyed(object);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleGeometryChanged(newGeometry, oldGeometry);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+ return;
+ }
+#endif
+ m_impl->handleGeometryChanged(newGeometry, oldGeometry);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
-
-void QQuickShaderEffect::propertyChanged(int mappedId)
+QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
- bool textureProviderChanged;
- m_common.propertyChanged(this, mappedId, &textureProviderChanged);
- m_dirtyTextureProviders |= textureProviderChanged;
- m_dirtyUniformValues = true;
- update();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
+#endif
+ return m_impl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
}
-void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickShaderEffect::componentComplete()
{
- m_dirtyGeometry = true;
- QQuickItem::geometryChanged(newGeometry, oldGeometry);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->maybeUpdateShaders();
+ QQuickItem::componentComplete();
+ return;
+ }
+#endif
+ m_impl->maybeUpdateShaders();
+ QQuickItem::componentComplete();
}
-QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
{
- QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
-
- // In the case of zero-size or a bad vertex shader, don't try to create a node...
- if (m_common.attributes.isEmpty() || width() <= 0 || height() <= 0) {
- if (node)
- delete node;
- return 0;
- }
-
- if (!node) {
- node = new QQuickShaderEffectNode;
- node->setMaterial(new QQuickShaderEffectMaterial(node));
- node->setFlag(QSGNode::OwnsMaterial, true);
- m_dirtyProgram = true;
- m_dirtyUniforms = true;
- m_dirtyGeometry = true;
- connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
- connect(node, &QQuickShaderEffectNode::dirtyTexture,
- this, &QQuickShaderEffect::updateGeometryIfAtlased);
- }
-
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
-
- // Update blending
- if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
- material->setFlag(QSGMaterial::Blending, m_blending);
- node->markDirty(QSGNode::DirtyMaterial);
- }
-
- if (int(material->cullMode) != int(m_cullMode)) {
- material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
- node->markDirty(QSGNode::DirtyMaterial);
- }
-
- if (m_dirtyProgram) {
- Key s = m_common.source;
- QSGShaderSourceBuilder builder;
- if (s.sourceCode[Key::FragmentShader].isEmpty()) {
- builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag"));
- s.sourceCode[Key::FragmentShader] = builder.source();
- builder.clear();
- }
- if (s.sourceCode[Key::VertexShader].isEmpty()) {
- builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert"));
- s.sourceCode[Key::VertexShader] = builder.source();
- }
-
- material->setProgramSource(s);
- material->attributes = m_common.attributes;
- node->markDirty(QSGNode::DirtyMaterial);
- m_dirtyProgram = false;
- m_dirtyUniforms = true;
- }
-
- if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
- m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
- m_dirtyTextureProviders);
- node->markDirty(QSGNode::DirtyMaterial);
- m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
- }
-
- QRectF srcRect(0, 0, 1, 1);
- bool geometryUsesTextureSubRect = false;
- if (m_supportsAtlasTextures && material->textureProviders.size() == 1) {
- QSGTextureProvider *provider = material->textureProviders.at(0);
- if (provider && provider->texture()) {
- srcRect = provider->texture()->normalizedTextureSubRect();
- geometryUsesTextureSubRect = true;
- }
- }
-
- if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) {
- material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader);
- node->markDirty(QSGNode::DirtyMaterial);
- }
-
- if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
- material->geometryUsesTextureSubRect = geometryUsesTextureSubRect;
- node->markDirty(QSGNode::DirtyMaterial);
- }
-
- if (m_dirtyMesh) {
- node->setGeometry(0);
- m_dirtyMesh = false;
- m_dirtyGeometry = true;
- }
-
- if (m_dirtyGeometry) {
- node->setFlag(QSGNode::OwnsGeometry, false);
- QSGGeometry *geometry = node->geometry();
- QRectF rect(0, 0, width(), height());
- QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
-
- geometry = mesh->updateGeometry(geometry, m_common.attributes, srcRect, rect);
- if (!geometry) {
- QString log = mesh->log();
- if (!log.isNull()) {
- m_log = parseLog();
- m_log += QLatin1String("*** Mesh ***\n");
- m_log += log;
- m_status = Error;
- emit logChanged();
- emit statusChanged();
- }
- delete node;
- return 0;
- }
-
- node->setGeometry(geometry);
- node->setFlag(QSGNode::OwnsGeometry, true);
-
- m_dirtyGeometry = false;
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleItemChange(change, value);
+ QQuickItem::itemChange(change, value);
+ return;
}
+#endif
+ m_impl->handleItemChange(change, value);
+ QQuickItem::itemChange(change, value);
+}
- return node;
+bool QQuickShaderEffect::isComponentComplete() const
+{
+ return QQuickItem::isComponentComplete();
}
-void QQuickShaderEffect::componentComplete()
+QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests
{
- m_common.updateShader(this, Key::VertexShader);
- m_common.updateShader(this, Key::FragmentShader);
- QQuickItem::componentComplete();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->parseLog();
+#endif
+ return m_impl->parseLog();
}
-void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
+void QQuickShaderEffectPrivate::updatePolish()
{
- if (change == QQuickItem::ItemSceneChange)
- m_common.updateWindow(value.window);
- QQuickItem::itemChange(change, value);
+ Q_Q(QQuickShaderEffect);
+#ifndef QT_NO_OPENGL
+ if (q->m_glImpl) {
+ q->m_glImpl->maybeUpdateShaders();
+ return;
+ }
+#endif
+ q->m_impl->maybeUpdateShaders();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index fb266d4c44..17b009039a 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -52,51 +52,13 @@
//
#include <QtQuick/qquickitem.h>
-
-#include <QtQuick/qsgmaterial.h>
#include <private/qtquickglobal_p.h>
-#include <private/qsgadaptationlayer_p.h>
-#include <private/qquickshadereffectnode_p.h>
-#include "qquickshadereffectmesh_p.h"
-
-#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-const char *qtPositionAttributeName();
-const char *qtTexCoordAttributeName();
-
-class QSGContext;
-class QSignalMapper;
-class QQuickCustomMaterialShader;
-
-// Common class for QQuickShaderEffect and QQuickCustomParticle.
-struct Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectCommon
-{
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
-
- ~QQuickShaderEffectCommon();
- void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void updateParseLog(bool ignoreAttributes);
- void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code);
- void updateShader(QQuickItem *item, Key::ShaderType shaderType);
- void updateMaterial(QQuickShaderEffectNode *node, QQuickShaderEffectMaterial *material,
- bool updateUniforms, bool updateUniformValues, bool updateTextureProviders);
- void updateWindow(QQuickWindow *window);
-
- // Called by slots in QQuickShaderEffect:
- void sourceDestroyed(QObject *object);
- void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged);
-
- Key source;
- QVector<QByteArray> attributes;
- QVector<UniformData> uniformData[Key::ShaderTypeCount];
- QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount];
- QString parseLog;
-};
-
+class QQuickOpenGLShaderEffect;
+class QQuickGenericShaderEffect;
+class QQuickShaderEffectPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
{
@@ -111,16 +73,14 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1)
public:
- enum CullMode
- {
- NoCulling = QQuickShaderEffectMaterial::NoCulling,
- BackFaceCulling = QQuickShaderEffectMaterial::BackFaceCulling,
- FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling
+ enum CullMode {
+ NoCulling,
+ BackFaceCulling,
+ FrontFaceCulling
};
Q_ENUM(CullMode)
- enum Status
- {
+ enum Status {
Compiled,
Uncompiled,
Error
@@ -128,32 +88,30 @@ public:
Q_ENUM(Status)
QQuickShaderEffect(QQuickItem *parent = 0);
- ~QQuickShaderEffect();
- QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
+ QByteArray fragmentShader() const;
void setFragmentShader(const QByteArray &code);
- QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
+ QByteArray vertexShader() const;
void setVertexShader(const QByteArray &code);
- bool blending() const { return m_blending; }
+ bool blending() const;
void setBlending(bool enable);
QVariant mesh() const;
void setMesh(const QVariant &mesh);
- CullMode cullMode() const { return m_cullMode; }
+ CullMode cullMode() const;
void setCullMode(CullMode face);
- QString log() const { return m_log; }
- Status status() const { return m_status; }
-
- bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ bool supportsAtlasTextures() const;
void setSupportsAtlasTextures(bool supports);
- QString parseLog();
+ QString log() const;
+ Status status() const;
- bool event(QEvent *) Q_DECL_OVERRIDE;
+ bool isComponentComplete() const;
+ QString parseLog();
Q_SIGNALS:
void fragmentShaderChanged();
@@ -166,44 +124,19 @@ Q_SIGNALS:
void supportsAtlasTexturesChanged();
protected:
- void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
- QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
- void componentComplete() Q_DECL_OVERRIDE;
- void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
-
-private Q_SLOTS:
- void updateGeometry();
- void updateGeometryIfAtlased();
- void updateLogAndStatus(const QString &log, int status);
- void sourceDestroyed(QObject *object);
- void propertyChanged(int mappedId);
+ bool event(QEvent *e) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
+ void componentComplete() override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
- friend class QQuickCustomMaterialShader;
- friend class QQuickShaderEffectNode;
-
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
-
- QSize m_meshResolution;
- QQuickShaderEffectMesh *m_mesh;
- QQuickGridMesh m_defaultMesh;
- CullMode m_cullMode;
- QString m_log;
- Status m_status;
-
- QQuickShaderEffectCommon m_common;
-
- uint m_blending : 1;
- uint m_dirtyUniforms : 1;
- uint m_dirtyUniformValues : 1;
- uint m_dirtyTextureProviders : 1;
- uint m_dirtyProgram : 1;
- uint m_dirtyParseLog : 1;
- uint m_dirtyMesh : 1;
- uint m_dirtyGeometry : 1;
- uint m_customVertexShader : 1;
- uint m_supportsAtlasTextures : 1;
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffect *m_glImpl;
+#endif
+ QQuickGenericShaderEffect *m_impl;
+
+ Q_DECLARE_PRIVATE(QQuickShaderEffect)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp
index 0811025654..8616bf8022 100644
--- a/src/quick/items/qquickshadereffectmesh.cpp
+++ b/src/quick/items/qquickshadereffectmesh.cpp
@@ -40,9 +40,25 @@
#include "qquickshadereffectmesh_p.h"
#include <QtQuick/qsggeometry.h>
#include "qquickshadereffect_p.h"
+#include "qquickscalegrid_p_p.h"
+#include "qquickborderimage_p_p.h"
+#include <QtQuick/private/qsgbasicinternalimagenode_p.h>
QT_BEGIN_NAMESPACE
+static const char qt_position_attribute_name[] = "qt_Vertex";
+static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
+
+const char *qtPositionAttributeName()
+{
+ return qt_position_attribute_name;
+}
+
+const char *qtTexCoordAttributeName()
+{
+ return qt_texcoord_attribute_name;
+}
+
QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
: QObject(parent)
{
@@ -67,54 +83,64 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent)
{
}
-QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &dstRect)
+bool QQuickGridMesh::validateAttributes(const QVector<QByteArray> &attributes, int *posIndex)
{
- int vmesh = m_resolution.height();
- int hmesh = m_resolution.width();
- int attrCount = attributes.count();
-
+ const int attrCount = attributes.count();
int positionIndex = attributes.indexOf(qtPositionAttributeName());
int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
- if (!geometry) {
- switch (attrCount) {
- case 0:
- m_log = QLatin1String("Error: No attributes specified.");
- return 0;
- case 1:
- if (positionIndex != 0) {
+ switch (attrCount) {
+ case 0:
+ m_log = QLatin1String("Error: No attributes specified.");
+ return false;
+ case 1:
+ if (positionIndex != 0) {
+ m_log = QLatin1String("Error: Missing \'");
+ m_log += QLatin1String(qtPositionAttributeName());
+ m_log += QLatin1String("\' attribute.\n");
+ return false;
+ }
+ break;
+ case 2:
+ if (positionIndex == -1 || texCoordIndex == -1) {
+ m_log.clear();
+ if (positionIndex == -1) {
m_log = QLatin1String("Error: Missing \'");
m_log += QLatin1String(qtPositionAttributeName());
m_log += QLatin1String("\' attribute.\n");
- return 0;
}
- break;
- case 2:
- if (positionIndex == -1 || texCoordIndex == -1) {
- m_log.clear();
- if (positionIndex == -1) {
- m_log = QLatin1String("Error: Missing \'");
- m_log += QLatin1String(qtPositionAttributeName());
- m_log += QLatin1String("\' attribute.\n");
- }
- if (texCoordIndex == -1) {
- m_log += QLatin1String("Error: Missing \'");
- m_log += QLatin1String(qtTexCoordAttributeName());
- m_log += QLatin1String("\' attribute.\n");
- }
- return 0;
+ if (texCoordIndex == -1) {
+ m_log += QLatin1String("Error: Missing \'");
+ m_log += QLatin1String(qtTexCoordAttributeName());
+ m_log += QLatin1String("\' attribute.\n");
}
- break;
- default:
- m_log = QLatin1String("Error: Too many attributes specified.");
- return 0;
+ return false;
}
+ break;
+ default:
+ m_log = QLatin1String("Error: Too many attributes specified.");
+ return false;
+ }
+
+ if (posIndex)
+ *posIndex = positionIndex;
+
+ return true;
+}
+QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &dstRect)
+{
+ int vmesh = m_resolution.height();
+ int hmesh = m_resolution.width();
+
+ if (!geometry) {
+ Q_ASSERT(attrCount == 1 || attrCount == 2);
geometry = new QSGGeometry(attrCount == 1
? QSGGeometry::defaultAttributes_Point2D()
: QSGGeometry::defaultAttributes_TexturedPoint2D(),
(vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2),
- GL_UNSIGNED_SHORT);
+ QSGGeometry::TypeUnsignedShort);
} else {
geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2));
@@ -129,7 +155,7 @@ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector
for (int ix = 0; ix <= hmesh; ++ix) {
float fx = ix / float(hmesh);
for (int ia = 0; ia < attrCount; ++ia) {
- if (ia == positionIndex) {
+ if (ia == posIndex) {
vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
vdata->y = y;
++vdata;
@@ -217,4 +243,197 @@ QSize QQuickGridMesh::resolution() const
return m_resolution;
}
+/*!
+ \qmltype BorderImageMesh
+ \instantiates QQuickBorderImageMesh
+ \inqmlmodule QtQuick
+ \since 5.8
+ \ingroup qtquick-effects
+ \brief Defines a mesh with vertices arranged like those of a BorderImage.
+
+ BorderImageMesh provides BorderImage-like capabilities to a ShaderEffect
+ without the need for a potentially costly ShaderEffectSource.
+
+ The following are functionally equivalent:
+ \qml
+ BorderImage {
+ id: borderImage
+ border {
+ left: 10
+ right: 10
+ top: 10
+ bottom: 10
+ }
+ source: "myImage.png"
+ visible: false
+ }
+ ShaderEffectSource {
+ id: effectSource
+ sourceItem: borderImage
+ visible: false
+ }
+ ShaderEffect {
+ property var source: effectSource
+ ...
+ }
+ \endqml
+
+ \qml
+ Image {
+ id: image
+ source: "myImage.png"
+ visible: false
+ }
+ ShaderEffect {
+ property var source: image
+ mesh: BorderImageMesh {
+ border {
+ left: 10
+ right: 10
+ top: 10
+ bottom: 10
+ }
+ size: image.sourceSize
+ }
+ ...
+ }
+ \endqml
+
+ But the BorderImageMesh version can typically be better optimized.
+*/
+QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent)
+ : QQuickShaderEffectMesh(parent), m_border(new QQuickScaleGrid(this)),
+ m_horizontalTileMode(QQuickBorderImageMesh::Stretch),
+ m_verticalTileMode(QQuickBorderImageMesh::Stretch)
+{
+}
+
+bool QQuickBorderImageMesh::validateAttributes(const QVector<QByteArray> &attributes, int *posIndex)
+{
+ Q_UNUSED(attributes);
+ Q_UNUSED(posIndex);
+ return true;
+}
+
+QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect)
+{
+ Q_UNUSED(attrCount);
+ Q_UNUSED(posIndex);
+
+ QRectF innerSourceRect;
+ QRectF targetRect;
+ QRectF innerTargetRect;
+ QRectF subSourceRect;
+
+ QQuickBorderImagePrivate::calculateRects(m_border, m_size, rect.size(), m_horizontalTileMode, m_verticalTileMode,
+ 1, &targetRect, &innerTargetRect, &innerSourceRect, &subSourceRect);
+
+ QRectF sourceRect = srcRect;
+ QRectF modifiedInnerSourceRect(sourceRect.x() + innerSourceRect.x() * sourceRect.width(),
+ sourceRect.y() + innerSourceRect.y() * sourceRect.height(),
+ innerSourceRect.width() * sourceRect.width(),
+ innerSourceRect.height() * sourceRect.height());
+
+ geometry = QSGBasicInternalImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect,
+ modifiedInnerSourceRect, subSourceRect, geometry);
+
+ return geometry;
+}
+
+/*!
+ \qmlpropertygroup QtQuick::BorderImageMesh::border
+ \qmlproperty int QtQuick::BorderImageMesh::border.left
+ \qmlproperty int QtQuick::BorderImageMesh::border.right
+ \qmlproperty int QtQuick::BorderImageMesh::border.top
+ \qmlproperty int QtQuick::BorderImageMesh::border.bottom
+
+ The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
+ as shown below:
+
+ \image declarative-scalegrid.png
+
+ Each border line (left, right, top, and bottom) specifies an offset in pixels
+ from the respective edge of the mesh. By default, each border line has
+ a value of 0.
+
+ For example, the following definition sets the bottom line 10 pixels up from
+ the bottom of the mesh:
+
+ \qml
+ BorderImageMesh {
+ border.bottom: 10
+ // ...
+ }
+ \endqml
+*/
+QQuickScaleGrid *QQuickBorderImageMesh::border()
+{
+ return m_border;
+}
+
+/*!
+ \qmlproperty size QtQuick::BorderImageMesh::size
+
+ The base size of the mesh. This generally corresponds to the \l {Image::}{sourceSize}
+ of the image being used by the ShaderEffect.
+*/
+QSize QQuickBorderImageMesh::size() const
+{
+ return m_size;
+}
+
+void QQuickBorderImageMesh::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+ m_size = size;
+ Q_EMIT sizeChanged();
+ Q_EMIT geometryChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::BorderImageMesh::horizontalTileMode
+ \qmlproperty enumeration QtQuick::BorderImageMesh::verticalTileMode
+
+ This property describes how to repeat or stretch the middle parts of an image.
+
+ \list
+ \li BorderImage.Stretch - Scales the image to fit to the available area.
+ \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
+ \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
+ \endlist
+
+ The default tile mode for each property is BorderImage.Stretch.
+*/
+
+QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::horizontalTileMode() const
+{
+ return m_horizontalTileMode;
+}
+
+void QQuickBorderImageMesh::setHorizontalTileMode(TileMode t)
+{
+ if (t == m_horizontalTileMode)
+ return;
+ m_horizontalTileMode = t;
+ Q_EMIT horizontalTileModeChanged();
+ Q_EMIT geometryChanged();
+}
+
+QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::verticalTileMode() const
+{
+ return m_verticalTileMode;
+}
+
+void QQuickBorderImageMesh::setVerticalTileMode(TileMode t)
+{
+ if (t == m_verticalTileMode)
+ return;
+
+ m_verticalTileMode = t;
+ Q_EMIT verticalTileModeChanged();
+ Q_EMIT geometryChanged();
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 7f9c95888c..c5f1d19866 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -43,8 +43,8 @@
#include <QtGui/qcolor.h>
#include <QtCore/qobject.h>
#include <QtCore/qsize.h>
-#include <QtCore/qvariant.h>
-#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qbytearray.h>
#ifndef QQUICKSHADEREFFECTMESH_P_H
#define QQUICKSHADEREFFECTMESH_P_H
@@ -62,6 +62,9 @@
QT_BEGIN_NAMESPACE
+const char *qtPositionAttributeName();
+const char *qtTexCoordAttributeName();
+
class QSGGeometry;
class QRectF;
@@ -70,8 +73,10 @@ class QQuickShaderEffectMesh : public QObject
Q_OBJECT
public:
QQuickShaderEffectMesh(QObject *parent = 0);
- // If 'geometry' != 0, 'attributes' is the same as last time the function was called.
- virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) = 0;
+ virtual bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) = 0;
+ // If 'geometry' != 0, 'attrCount' is the same as last time the function was called.
+ virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) = 0;
// If updateGeometry() fails, the reason should appear in the log.
virtual QString log() const { return QString(); }
@@ -86,7 +91,9 @@ class QQuickGridMesh : public QQuickShaderEffectMesh
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
public:
QQuickGridMesh(QObject *parent = 0);
- QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
+ bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) Q_DECL_OVERRIDE;
+ QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
QString log() const Q_DECL_OVERRIDE { return m_log; }
void setResolution(const QSize &res);
@@ -100,6 +107,48 @@ private:
QString m_log;
};
+class QQuickScaleGrid;
+class QQuickBorderImageMesh : public QQuickShaderEffectMesh
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickScaleGrid *border READ border CONSTANT)
+ Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged)
+ Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged)
+ Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged)
+public:
+ QQuickBorderImageMesh(QObject *parent = 0);
+
+ bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) override;
+ QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) override;
+
+ QQuickScaleGrid *border();
+
+ enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile };
+ Q_ENUM(TileMode)
+
+ QSize size() const;
+ void setSize(const QSize &size);
+
+ TileMode horizontalTileMode() const;
+ void setHorizontalTileMode(TileMode);
+
+ TileMode verticalTileMode() const;
+ void setVerticalTileMode(TileMode);
+
+Q_SIGNALS:
+ void sizeChanged();
+ void horizontalTileModeChanged();
+ void verticalTileModeChanged();
+
+private:
+ QQuickScaleGrid *m_border;
+ QSize m_size;
+ TileMode m_horizontalTileMode;
+ TileMode m_verticalTileMode;
+};
+
inline QColor qt_premultiply_color(const QColor &c)
{
return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index a8cf6155a0..a60a06f59a 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -45,7 +45,6 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <qsgsimplerectnode.h>
-#include "qopenglframebufferobject.h"
#include "qmath.h"
#include <QtQuick/private/qsgtexture_p.h>
#include <QtCore/QRunnable>
@@ -309,11 +308,11 @@ QQuickItem *QQuickShaderEffectSource::sourceItem() const
return m_sourceItem;
}
-void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
+void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
{
Q_ASSERT(item == m_sourceItem);
Q_UNUSED(item);
- if (newRect.size() != oldRect.size())
+ if (change.sizeChange())
update();
}
@@ -680,7 +679,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
m_texture->setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
m_texture->setSize(textureSize);
m_texture->setRecursive(m_recursive);
- m_texture->setFormat(GLenum(m_format));
+ m_texture->setFormat(m_format);
m_texture->setHasMipmaps(m_mipmap);
m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally);
m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically);
@@ -709,9 +708,9 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
node->setFlag(QSGNode::UsePreprocess);
node->setTexture(m_texture);
QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode;
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 92b04f4ca7..ee18bf195a 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -55,7 +55,7 @@
#include <QtQuick/qsgtextureprovider.h>
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgcontext_p.h>
-#include <private/qsgdefaultimagenode_p.h>
+#include <private/qsgdefaultinternalimagenode_p.h>
#include <private/qquickitemchangelistener_p.h>
#include "qpointer.h"
@@ -93,11 +93,11 @@ public:
Repeat
};
Q_ENUM(WrapMode)
-
+ // Equivalents to GL_ALPHA and similar type constants.
enum Format {
- Alpha = GL_ALPHA,
- RGB = GL_RGB,
- RGBA = GL_RGBA
+ Alpha = 0x1906,
+ RGB = 0x1907,
+ RGBA = 0x1908
};
Q_ENUM(Format)
@@ -168,7 +168,7 @@ protected:
void releaseResources() Q_DECL_OVERRIDE;
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
- void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
private:
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index 1b8c8cee84..684d432f39 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -300,7 +300,7 @@ private Q_SLOTS:
private:
friend class QQuickImageParticle;
- friend class QQuickSpriteSequence;
+ //friend class QQuickSpriteSequence;
friend class QQuickAnimatedSprite;
friend class QQuickSpriteEngine;
friend class QQuickStochasticEngine;
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index df044988e0..10e0d51709 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -94,7 +94,7 @@ QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) :
setCount(1);
}
-QQuickStochasticEngine::QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent) :
+QQuickStochasticEngine::QQuickStochasticEngine(const QList<QQuickStochasticState *> &states, QObject *parent) :
QObject(parent), m_states(states), m_timeOffset(0), m_addAdvance(false)
{
//Default size 1
@@ -110,10 +110,10 @@ QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent)
{
}
-QQuickSpriteEngine::QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent)
- : QQuickStochasticEngine(parent), m_startedImageAssembly(false), m_loaded(false)
+QQuickSpriteEngine::QQuickSpriteEngine(const QList<QQuickSprite *> &sprites, QObject *parent)
+ : QQuickSpriteEngine(parent)
{
- foreach (QQuickSprite* sprite, sprites)
+ for (QQuickSprite* sprite : sprites)
m_states << (QQuickStochasticState*)sprite;
}
@@ -122,7 +122,7 @@ QQuickSpriteEngine::~QQuickSpriteEngine()
}
-int QQuickSpriteEngine::maxFrames()
+int QQuickSpriteEngine::maxFrames() const
{
return m_maxFrames;
}
@@ -140,7 +140,7 @@ TODO: Above idea needs to have the varying duration offset added to it
m_startTimes will be set in advance/restart to 0->(m_framesPerRow-1) and can be used directly as extra.
This makes it 'frame' instead, but is more memory efficient than two arrays and less hideous than a vector of unions.
*/
-int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration)
+int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) const
{
int myRowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow / m_sprites[state]->m_frames;
if (rowDuration)
@@ -153,7 +153,7 @@ int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDura
return (m_timeOffset - m_startTimes[sprite]) / myRowDuration;
}
-int QQuickSpriteEngine::spriteState(int sprite)
+int QQuickSpriteEngine::spriteState(int sprite) const
{
if (!m_loaded)
return 0;
@@ -174,7 +174,7 @@ int QQuickSpriteEngine::spriteState(int sprite)
return state + extra;
}
-int QQuickSpriteEngine::spriteStart(int sprite)
+int QQuickSpriteEngine::spriteStart(int sprite) const
{
if (!m_duration[sprite] || !m_loaded)
return m_timeOffset;
@@ -188,7 +188,7 @@ int QQuickSpriteEngine::spriteStart(int sprite)
return m_startTimes[sprite] + extra*rowDuration;
}
-int QQuickSpriteEngine::spriteFrames(int sprite)
+int QQuickSpriteEngine::spriteFrames(int sprite) const
{
if (!m_loaded)
return 1;
@@ -215,7 +215,7 @@ int QQuickSpriteEngine::spriteFrames(int sprite)
return m_sprites[state]->m_framesPerRow;
}
-int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame
+int QQuickSpriteEngine::spriteDuration(int sprite) const //Full duration, not per frame
{
if (!m_duration[sprite] || !m_loaded)
return m_duration[sprite];
@@ -235,7 +235,7 @@ int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame
return rowDuration;
}
-int QQuickSpriteEngine::spriteY(int sprite)
+int QQuickSpriteEngine::spriteY(int sprite) const
{
if (!m_loaded)
return 0;
@@ -257,7 +257,7 @@ int QQuickSpriteEngine::spriteY(int sprite)
return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra;
}
-int QQuickSpriteEngine::spriteX(int sprite)
+int QQuickSpriteEngine::spriteX(int sprite) const
{
if (!m_loaded)
return 0;
@@ -280,24 +280,24 @@ int QQuickSpriteEngine::spriteX(int sprite)
return m_sprites[state]->m_rowStartX;
}
-QQuickSprite* QQuickSpriteEngine::sprite(int sprite)
+QQuickSprite* QQuickSpriteEngine::sprite(int sprite) const
{
return m_sprites[m_things[sprite]];
}
-int QQuickSpriteEngine::spriteWidth(int sprite)
+int QQuickSpriteEngine::spriteWidth(int sprite) const
{
int state = m_things[sprite];
return m_sprites[state]->m_frameWidth;
}
-int QQuickSpriteEngine::spriteHeight(int sprite)
+int QQuickSpriteEngine::spriteHeight(int sprite) const
{
int state = m_things[sprite];
return m_sprites[state]->m_frameHeight;
}
-int QQuickSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together
+int QQuickSpriteEngine::spriteCount() const //TODO: Actually image state count, need to rename these things to make sense together
{
return m_imageStateCount;
}
@@ -312,14 +312,14 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump)
return;
}
- if (m_things[sprite] == state)
+ if (m_things.at(sprite) == state)
return;//Already there
m_things[sprite] = state;
- m_duration[sprite] = m_states[state]->variedDuration();
+ m_duration[sprite] = m_states.at(state)->variedDuration();
m_goals[sprite] = -1;
restart(sprite);
emit stateChanged(sprite);
- emit m_states[state]->entered();
+ emit m_states.at(state)->entered();
return;
}
@@ -329,7 +329,7 @@ QQuickPixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprite
return QQuickPixmap::Null;
int null, loading, ready;
null = loading = ready = 0;
- foreach (QQuickSprite* s, m_sprites) {
+ for (QQuickSprite* s : qAsConst(m_sprites)) {
switch (s->m_pix.status()) {
// ### Maybe add an error message here, because this null shouldn't be reached but when it does, the image fails without an error message.
case QQuickPixmap::Null : null++; break;
@@ -358,7 +358,7 @@ void QQuickSpriteEngine::startAssemblingImage()
QList<QQuickStochasticState*> removals;
- foreach (QQuickStochasticState* s, m_states){
+ for (QQuickStochasticState* s : qAsConst(m_states)) {
QQuickSprite* sprite = qobject_cast<QQuickSprite*>(s);
if (sprite) {
m_sprites << sprite;
@@ -367,16 +367,16 @@ void QQuickSpriteEngine::startAssemblingImage()
qDebug() << "Error: Non-sprite in QQuickSpriteEngine";
}
}
- foreach (QQuickStochasticState* s, removals)
+ for (QQuickStochasticState* s : qAsConst(removals))
m_states.removeAll(s);
m_startedImageAssembly = true;
}
-QImage QQuickSpriteEngine::assembledImage()
+QImage QQuickSpriteEngine::assembledImage(int maxSize)
{
QQuickPixmap::Status stat = status();
if (!m_errorsPrinted && stat == QQuickPixmap::Error) {
- foreach (QQuickSprite* s, m_sprites)
+ for (QQuickSprite* s : qAsConst(m_sprites))
if (s->m_pix.isError())
qmlInfo(s) << s->m_pix.error();
m_errorsPrinted = true;
@@ -389,17 +389,8 @@ QImage QQuickSpriteEngine::assembledImage()
int w = 0;
m_maxFrames = 0;
m_imageStateCount = 0;
- int maxSize = 0;
-
- //If there is no current OpenGL Context
- if (!QOpenGLContext::currentContext())
- return QImage();
- QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
-#ifdef SPRITE_IMAGE_DEBUG
- qDebug() << "MAX TEXTURE SIZE" << maxSize;
-#endif
- foreach (QQuickSprite* state, m_sprites){
+ for (QQuickSprite* state : qAsConst(m_sprites)) {
if (state->frames() > m_maxFrames)
m_maxFrames = state->frames();
@@ -450,7 +441,7 @@ QImage QQuickSpriteEngine::assembledImage()
image.fill(0);
QPainter p(&image);
int y = 0;
- foreach (QQuickSprite* state, m_sprites){
+ for (QQuickSprite* state : qAsConst(m_sprites)) {
QImage img(state->m_pix.image());
int frameWidth = state->m_frameWidth;
int frameHeight = state->m_frameHeight;
@@ -525,8 +516,8 @@ void QQuickStochasticEngine::start(int index, int state)
if (index >= m_things.count())
return;
m_things[index] = state;
- m_duration[index] = m_states[state]->variedDuration();
- if (m_states[state]->randomStart())
+ m_duration[index] = m_states.at(state)->variedDuration();
+ if (m_states.at(state)->randomStart())
m_startTimes[index] = NINF;
else
m_startTimes[index] = 0;
@@ -547,33 +538,33 @@ void QQuickStochasticEngine::stop(int index)
void QQuickStochasticEngine::restart(int index)
{
- bool randomStart = (m_startTimes[index] == NINF);
+ bool randomStart = (m_startTimes.at(index) == NINF);
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
m_startTimes[index] += m_advanceTime.elapsed();
if (randomStart)
- m_startTimes[index] -= qrand() % m_duration[index];
- int time = m_duration[index] + m_startTimes[index];
+ m_startTimes[index] -= qrand() % m_duration.at(index);
+ int time = m_duration.at(index) + m_startTimes.at(index);
for (int i=0; i<m_stateUpdates.count(); i++)
m_stateUpdates[i].second.removeAll(index);
- if (m_duration[index] >= 0)
+ if (m_duration.at(index) >= 0)
addToUpdateList(time, index);
}
void QQuickSpriteEngine::restart(int index) //Reimplemented to recognize and handle pseudostates
{
- bool randomStart = (m_startTimes[index] == NINF);
- if (m_loaded && m_sprites[m_things[index]]->frameSync()) {//Manually advanced
+ bool randomStart = (m_startTimes.at(index) == NINF);
+ if (m_loaded && m_sprites.at(m_things.at(index))->frameSync()) {//Manually advanced
m_startTimes[index] = 0;
- if (randomStart && m_sprites[m_things[index]]->m_generatedCount)
- m_startTimes[index] += qrand() % m_sprites[m_things[index]]->m_generatedCount;
+ if (randomStart && m_sprites.at(m_things.at(index))->m_generatedCount)
+ m_startTimes[index] += qrand() % m_sprites.at(m_things.at(index))->m_generatedCount;
} else {
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
m_startTimes[index] += m_advanceTime.elapsed();
if (randomStart)
- m_startTimes[index] -= qrand() % m_duration[index];
- int time = spriteDuration(index) + m_startTimes[index];
+ m_startTimes[index] -= qrand() % m_duration.at(index);
+ int time = spriteDuration(index) + m_startTimes.at(index);
if (randomStart) {
int curTime = m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0);
while (time < curTime) //Fast forward through psuedostates as needed
@@ -590,11 +581,11 @@ void QQuickStochasticEngine::advance(int idx)
{
if (idx >= m_things.count())
return;//TODO: Proper fix(because this has happened and I just ignored it)
- int nextIdx = nextState(m_things[idx],idx);
+ int nextIdx = nextState(m_things.at(idx), idx);
m_things[idx] = nextIdx;
- m_duration[idx] = m_states[nextIdx]->variedDuration();
+ m_duration[idx] = m_states.at(nextIdx)->variedDuration();
restart(idx);
- emit m_states[nextIdx]->entered();
+ emit m_states.at(nextIdx)->entered();
emit stateChanged(idx);
}
@@ -607,29 +598,29 @@ void QQuickSpriteEngine::advance(int idx) //Reimplemented to recognize and handl
if (idx >= m_things.count())
return;//TODO: Proper fix(because this has happened and I just ignored it)
- if (m_duration[idx] == 0) {
- if (m_sprites[m_things[idx]]->frameSync()) {
+ if (m_duration.at(idx) == 0) {
+ if (m_sprites.at(m_things.at(idx))->frameSync()) {
//Manually called, advance inner substate count
m_startTimes[idx]++;
- if (m_startTimes[idx] < m_sprites[m_things[idx]]->m_generatedCount) {
+ if (m_startTimes.at(idx) < m_sprites.at(m_things.at(idx))->m_generatedCount) {
//only a pseudostate ended
emit stateChanged(idx);
return;
}
}
//just go past the pseudostate logic
- } else if (m_startTimes[idx] + m_duration[idx]
+ } else if (m_startTimes.at(idx) + m_duration.at(idx)
> int(m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0))) {
//only a pseduostate ended
emit stateChanged(idx);
addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTime.elapsed() : 0), idx);
return;
}
- int nextIdx = nextState(m_things[idx],idx);
+ int nextIdx = nextState(m_things.at(idx), idx);
m_things[idx] = nextIdx;
- m_duration[idx] = m_states[nextIdx]->variedDuration();
+ m_duration[idx] = m_states.at(nextIdx)->variedDuration();
restart(idx);
- emit m_states[nextIdx]->entered();
+ emit m_states.at(nextIdx)->entered();
emit stateChanged(idx);
}
@@ -640,16 +631,16 @@ int QQuickStochasticEngine::nextState(int curState, int curThing)
if (goalPath == -1){//Random
qreal r =(qreal) qrand() / (qreal) RAND_MAX;
qreal total = 0.0;
- for (QVariantMap::const_iterator iter=m_states[curState]->m_to.constBegin();
- iter!=m_states[curState]->m_to.constEnd(); ++iter)
+ for (QVariantMap::const_iterator iter=m_states.at(curState)->m_to.constBegin();
+ iter!=m_states.at(curState)->m_to.constEnd(); ++iter)
total += (*iter).toReal();
r*=total;
- for (QVariantMap::const_iterator iter= m_states[curState]->m_to.constBegin();
- iter!=m_states[curState]->m_to.constEnd(); ++iter){
+ for (QVariantMap::const_iterator iter= m_states.at(curState)->m_to.constBegin();
+ iter!=m_states.at(curState)->m_to.constEnd(); ++iter){
if (r < (*iter).toReal()){
bool superBreak = false;
for (int i=0; i<m_states.count(); i++){
- if (m_states[i]->name() == iter.key()){
+ if (m_states.at(i)->name() == iter.key()){
nextIdx = i;
superBreak = true;
break;
@@ -673,8 +664,9 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis
//Sprite State Update;
m_timeOffset = time;
m_addAdvance = false;
- while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
- foreach (int idx, m_stateUpdates.first().second)
+ while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.constFirst().first){
+ const auto copy = m_stateUpdates.constFirst().second;
+ for (int idx : copy)
advance(idx);
m_stateUpdates.pop_front();
}
@@ -683,14 +675,14 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis
m_addAdvance = true;
if (m_stateUpdates.isEmpty())
return uint(-1);
- return m_stateUpdates.first().first;
+ return m_stateUpdates.constFirst().first;
}
int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
{
QString goalName;
- if (m_goals[spriteIdx] != -1)
- goalName = m_states[m_goals[spriteIdx]]->name();
+ if (m_goals.at(spriteIdx) != -1)
+ goalName = m_states.at(m_goals.at(spriteIdx))->name();
else
goalName = m_globalGoal;
if (goalName.isEmpty())
@@ -698,16 +690,16 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
//TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitrarily anyways)
// Paraphrased - implement in an *efficient* manner
for (int i=0; i<m_states.count(); i++)
- if (m_states[curIdx]->name() == goalName)
+ if (m_states.at(curIdx)->name() == goalName)
return curIdx;
if (dist < 0)
dist = m_states.count();
- QQuickStochasticState* curState = m_states[curIdx];
+ QQuickStochasticState* curState = m_states.at(curIdx);
for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); ++iter){
if (iter.key() == goalName)
for (int i=0; i<m_states.count(); i++)
- if (m_states[i]->name() == goalName)
+ if (m_states.at(i)->name() == goalName)
return i;
}
QSet<int> options;
@@ -716,7 +708,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
iter!=curState->m_to.constEnd(); ++iter){
int option = -1;
for (int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient...
- if (m_states[j]->name() == iter.key())
+ if (m_states.at(j)->name() == iter.key())
if (goalSeek(j, spriteIdx, i) != -1)
option = j;
if (option != -1)
@@ -730,13 +722,13 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
qreal total = 0;
for (QSet<int>::const_iterator iter=options.constBegin();
iter!=options.constEnd(); ++iter)
- total += curState->m_to.value(m_states[(*iter)]->name()).toReal();
+ total += curState->m_to.value(m_states.at((*iter))->name()).toReal();
r *= total;
for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); ++iter){
bool superContinue = true;
for (int j=0; j<m_states.count(); j++)
- if (m_states[j]->name() == iter.key())
+ if (m_states.at(j)->name() == iter.key())
if (options.contains(j))
superContinue = false;
if (superContinue)
@@ -744,7 +736,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
if (r < (*iter).toReal()){
bool superBreak = false;
for (int j=0; j<m_states.count(); j++){
- if (m_states[j]->name() == iter.key()){
+ if (m_states.at(j)->name() == iter.key()){
option = j;
superBreak = true;
break;
@@ -764,10 +756,10 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
void QQuickStochasticEngine::addToUpdateList(uint t, int idx)
{
for (int i=0; i<m_stateUpdates.count(); i++){
- if (m_stateUpdates[i].first==t){
+ if (m_stateUpdates.at(i).first == t){
m_stateUpdates[i].second << idx;
return;
- }else if (m_stateUpdates[i].first > t){
+ } else if (m_stateUpdates.at(i).first > t) {
QList<int> tmpList;
tmpList << idx;
m_stateUpdates.insert(i, qMakePair(t, tmpList));
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index 65f58fafcb..485afc16e5 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -189,7 +189,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject
Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states)
public:
explicit QQuickStochasticEngine(QObject *parent = 0);
- QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent=0);
+ QQuickStochasticEngine(const QList<QQuickStochasticState*> &states, QObject *parent = 0);
~QQuickStochasticEngine();
QQmlListProperty<QQuickStochasticState> states()
@@ -210,11 +210,11 @@ public:
virtual void restart(int index=0);
virtual void advance(int index=0);//Sends state to the next chosen state, unlike goal.
void stop(int index=0);
- int curState(int index=0) {return m_things[index];}
+ int curState(int index=0) const {return m_things[index];}
- QQuickStochasticState* state(int idx){return m_states[idx];}
- int stateIndex(QQuickStochasticState* s){return m_states.indexOf(s);}
- int stateIndex(const QString& s) {
+ QQuickStochasticState* state(int idx) const {return m_states[idx];}
+ int stateIndex(QQuickStochasticState* s) const {return m_states.indexOf(s);}
+ int stateIndex(const QString& s) const {
for (int i=0; i<m_states.count(); i++)
if (m_states[i]->name() == s)
return i;
@@ -266,24 +266,24 @@ class Q_QUICK_PRIVATE_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites)
public:
explicit QQuickSpriteEngine(QObject *parent = 0);
- QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent=0);
+ QQuickSpriteEngine(const QList<QQuickSprite*> &sprites, QObject *parent = 0);
~QQuickSpriteEngine();
QQmlListProperty<QQuickSprite> sprites()
{
return QQmlListProperty<QQuickSprite>(this, m_sprites);
}
- QQuickSprite* sprite(int sprite=0);
- int spriteState(int sprite=0);
- int spriteStart(int sprite=0);
- int spriteFrames(int sprite=0);
- int spriteDuration(int sprite=0);
- int spriteX(int sprite=0);
- int spriteY(int sprite=0);
- int spriteWidth(int sprite=0);
- int spriteHeight(int sprite=0);
- int spriteCount();//Like state count
- int maxFrames();
+ QQuickSprite* sprite(int sprite = 0) const;
+ int spriteState(int sprite = 0) const;
+ int spriteStart(int sprite = 0) const;
+ int spriteFrames(int sprite = 0) const;
+ int spriteDuration(int sprite = 0) const;
+ int spriteX(int sprite = 0) const;
+ int spriteY(int sprite = 0) const;
+ int spriteWidth(int sprite = 0) const;
+ int spriteHeight(int sprite = 0) const;
+ int spriteCount() const;//Like state count
+ int maxFrames() const;
void restart(int index=0) Q_DECL_OVERRIDE;
void advance(int index=0) Q_DECL_OVERRIDE;
@@ -295,10 +295,10 @@ public:
bool isError() { return status() == QQuickPixmap::Error; }
QQuickPixmap::Status status();//Composed status of all Sprites
void startAssemblingImage();
- QImage assembledImage();
+ QImage assembledImage(int maxSize = 2048);
private:
- int pseudospriteProgress(int,int,int*rd=0);
+ int pseudospriteProgress(int, int, int *rd = 0) const;
QList<QQuickSprite*> m_sprites;
bool m_startedImageAssembly;
bool m_loaded;
diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp
index f32e1afd50..25a39f951a 100644
--- a/src/quick/items/qquickspritesequence.cpp
+++ b/src/quick/items/qquickspritesequence.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickspritesequence_p.h"
+#include "qquickspritesequence_p_p.h"
#include "qquicksprite_p.h"
#include "qquickspriteengine_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -54,111 +55,6 @@
QT_BEGIN_NAMESPACE
-class QQuickSpriteSequenceMaterial : public QSGMaterial
-{
-public:
- QQuickSpriteSequenceMaterial();
- ~QQuickSpriteSequenceMaterial();
- QSGMaterialType *type() const Q_DECL_OVERRIDE{ static QSGMaterialType type; return &type; }
- QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
- int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE
- {
- return this - static_cast<const QQuickSpriteSequenceMaterial *>(other);
- }
-
- QSGTexture *texture;
-
- float animT;
- float animX1;
- float animY1;
- float animX2;
- float animY2;
- float animW;
- float animH;
-};
-
-QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
- : texture(0)
- , animT(0.0f)
- , animX1(0.0f)
- , animY1(0.0f)
- , animX2(0.0f)
- , animY2(0.0f)
- , animW(1.0f)
- , animH(1.0f)
-{
- setFlag(Blending, true);
-}
-
-QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
-{
- delete texture;
-}
-
-class SpriteSequenceMaterialData : public QSGMaterialShader
-{
-public:
- SpriteSequenceMaterialData()
- : QSGMaterialShader()
- {
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag"));
- }
-
- void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE
- {
- QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(newEffect);
- m->texture->bind();
-
- program()->setUniformValue(m_opacity_id, state.opacity());
- program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
- program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
-
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
- }
-
- void initialize() Q_DECL_OVERRIDE {
- m_matrix_id = program()->uniformLocation("qt_Matrix");
- m_opacity_id = program()->uniformLocation("qt_Opacity");
- m_animData_id = program()->uniformLocation("animData");
- m_animPos_id = program()->uniformLocation("animPos");
- }
-
- char const *const *attributeNames() const Q_DECL_OVERRIDE {
- static const char *attr[] = {
- "vPos",
- "vTex",
- 0
- };
- return attr;
- }
-
- int m_matrix_id;
- int m_opacity_id;
- int m_animData_id;
- int m_animPos_id;
-};
-
-QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
-{
- return new SpriteSequenceMaterialData;
-}
-
-struct SpriteVertex {
- float x;
- float y;
- float tx;
- float ty;
-};
-
-struct SpriteVertices {
- SpriteVertex v1;
- SpriteVertex v2;
- SpriteVertex v3;
- SpriteVertex v4;
-};
-
/*!
\qmltype SpriteSequence
\instantiates QQuickSpriteSequence
@@ -218,213 +114,185 @@ struct SpriteVertices {
//TODO: Implicitly size element to size of first sprite?
QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
- QQuickItem(parent)
- , m_node(0)
- , m_material(0)
- , m_spriteEngine(0)
- , m_curFrame(0)
- , m_pleaseReset(false)
- , m_running(true)
- , m_interpolate(true)
- , m_curStateIdx(0)
+ QQuickItem(*(new QQuickSpriteSequencePrivate), parent)
{
setFlag(ItemHasContents);
connect(this, SIGNAL(runningChanged(bool)),
this, SLOT(update()));
- connect(this, SIGNAL(widthChanged()),
- this, SLOT(sizeVertices()));
- connect(this, SIGNAL(heightChanged()),
- this, SLOT(sizeVertices()));
}
void QQuickSpriteSequence::jumpTo(const QString &sprite)
{
- if (!m_spriteEngine)
+ Q_D(QQuickSpriteSequence);
+ if (!d->m_spriteEngine)
return;
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite), 0, true);
}
void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
{
- if (m_goalState != sprite){
- m_goalState = sprite;
+ Q_D(QQuickSpriteSequence);
+ if (d->m_goalState != sprite){
+ d->m_goalState = sprite;
emit goalSpriteChanged(sprite);
- if (m_spriteEngine)
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
+ if (d->m_spriteEngine)
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite));
}
}
-QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
+void QQuickSpriteSequence::setRunning(bool arg)
{
- return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+ Q_D(QQuickSpriteSequence);
+ if (d->m_running != arg) {
+ d->m_running = arg;
+ Q_EMIT runningChanged(arg);
+ }
}
-void QQuickSpriteSequence::createEngine()
+void QQuickSpriteSequence::setInterpolate(bool arg)
{
- //TODO: delay until component complete
- if (m_spriteEngine)
- delete m_spriteEngine;
- if (m_sprites.count()) {
- m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
- if (!m_goalState.isEmpty())
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(m_goalState));
- } else {
- m_spriteEngine = 0;
+ Q_D(QQuickSpriteSequence);
+ if (d->m_interpolate != arg) {
+ d->m_interpolate = arg;
+ Q_EMIT interpolateChanged(arg);
}
- reset();
}
-static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
-};
-
-static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
+QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
{
- 2, // Attribute Count
- (2+2) * sizeof(float),
- SpriteSequence_Attributes
-};
+ Q_D(QQuickSpriteSequence);
+ return QQmlListProperty<QQuickSprite>(this, &d->m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
-void QQuickSpriteSequence::sizeVertices()
+bool QQuickSpriteSequence::running() const
{
- if (!m_node)
- return;
+ Q_D(const QQuickSpriteSequence);
+ return d->m_running;
+}
- SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
- p->v1.x = 0;
- p->v1.y = 0;
+bool QQuickSpriteSequence::interpolate() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_interpolate;
+}
- p->v2.x = width();
- p->v2.y = 0;
+QString QQuickSpriteSequence::goalSprite() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_goalState;
+}
- p->v3.x = 0;
- p->v3.y = height();
+QString QQuickSpriteSequence::currentSprite() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_curState;
+}
- p->v4.x = width();
- p->v4.y = height();
+void QQuickSpriteSequence::createEngine()
+{
+ Q_D(QQuickSpriteSequence);
+ //TODO: delay until component complete
+ if (d->m_spriteEngine)
+ delete d->m_spriteEngine;
+ if (d->m_sprites.count()) {
+ d->m_spriteEngine = new QQuickSpriteEngine(d->m_sprites, this);
+ if (!d->m_goalState.isEmpty())
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(d->m_goalState));
+ } else {
+ d->m_spriteEngine = 0;
+ }
+ reset();
}
-QSGGeometryNode* QQuickSpriteSequence::buildNode()
+QSGSpriteNode *QQuickSpriteSequence::initNode()
{
- if (!m_spriteEngine) {
+ Q_D(QQuickSpriteSequence);
+
+ if (!d->m_spriteEngine) {
qmlInfo(this) << "No sprite engine...";
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
- m_spriteEngine->startAssemblingImage();
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
+ d->m_spriteEngine->startAssemblingImage();
update();//Schedule another update, where we will check again
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
update();//Schedule another update, where we will check again
- return 0;
+ return nullptr;
}
- m_material = new QQuickSpriteSequenceMaterial();
-
- QImage image = m_spriteEngine->assembledImage();
+ QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize());
if (image.isNull())
- return 0;
- m_sheetSize = QSizeF(image.size());
- m_material->texture = window()->createTextureFromImage(image);
- m_material->texture->setFiltering(QSGTexture::Linear);
- m_spriteEngine->start(0);
- m_material->animT = 0;
- m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
- m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
- m_material->animX2 = m_material->animX1;
- m_material->animY2 = m_material->animY1;
- m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
- emit currentSpriteChanged(m_curState);
-
- int vCount = 4;
- int iCount = 6;
- QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
-
- SpriteVertices *p = (SpriteVertices *) g->vertexData();
- QRectF texRect = m_material->texture->normalizedTextureSubRect();
-
- p->v1.tx = texRect.topLeft().x();
- p->v1.ty = texRect.topLeft().y();
-
- p->v2.tx = texRect.topRight().x();
- p->v2.ty = texRect.topRight().y();
-
- p->v3.tx = texRect.bottomLeft().x();
- p->v3.ty = texRect.bottomLeft().y();
-
- p->v4.tx = texRect.bottomRight().x();
- p->v4.ty = texRect.bottomRight().y();
-
- quint16 *indices = g->indexDataAsUShort();
- indices[0] = 0;
- indices[1] = 1;
- indices[2] = 2;
- indices[3] = 1;
- indices[4] = 3;
- indices[5] = 2;
-
-
- m_timestamp.start();
- m_node = new QSGGeometryNode();
- m_node->setGeometry(g);
- m_node->setMaterial(m_material);
- m_node->setFlag(QSGGeometryNode::OwnsMaterial);
- sizeVertices();
- return m_node;
+ return nullptr;
+
+ QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
+
+ d->m_sheetSize = QSize(image.size());
+ node->setTexture(window()->createTextureFromImage(image));
+ d->m_spriteEngine->start(0);
+ node->setTime(0.0f);
+ node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
+ node->setSheetSize(d->m_sheetSize);
+ node->setSize(QSizeF(width(), height()));
+
+ d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
+ emit currentSpriteChanged(d->m_curState);
+ d->m_timestamp.start();
+ return node;
}
void QQuickSpriteSequence::reset()
{
- m_pleaseReset = true;
+ Q_D(QQuickSpriteSequence);
+ d->m_pleaseReset = true;
}
-QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if (m_pleaseReset) {
- delete m_node;
+ Q_D(QQuickSpriteSequence);
- m_node = 0;
- m_material = 0;
- m_pleaseReset = false;
+ if (d->m_pleaseReset) {
+ delete oldNode;
+
+ oldNode = nullptr;
+ d->m_pleaseReset = false;
}
- prepareNextFrame();
+ QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
+ if (!node)
+ node = initNode();
+
+ if (node)
+ prepareNextFrame(node);
- if (m_running) {
+ if (d->m_running) {
update();
- if (m_node)
- m_node->markDirty(QSGNode::DirtyMaterial);
}
- return m_node;
+ return node;
}
-void QQuickSpriteSequence::prepareNextFrame()
+void QQuickSpriteSequence::prepareNextFrame(QSGSpriteNode *node)
{
- if (m_node == 0)
- m_node = buildNode();
- if (m_node == 0) //error creating node
- return;
+ Q_D(QQuickSpriteSequence);
- uint timeInt = m_timestamp.elapsed();
+ uint timeInt = d->m_timestamp.elapsed();
qreal time = timeInt / 1000.;
//Advance State
- m_spriteEngine->updateSprites(timeInt);
- if (m_curStateIdx != m_spriteEngine->curState()) {
- m_curStateIdx = m_spriteEngine->curState();
- m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
- emit currentSpriteChanged(m_curState);
- m_curFrame= -1;
+ d->m_spriteEngine->updateSprites(timeInt);
+ if (d->m_curStateIdx != d->m_spriteEngine->curState()) {
+ d->m_curStateIdx = d->m_spriteEngine->curState();
+ d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
+ emit currentSpriteChanged(d->m_curState);
+ d->m_curFrame= -1;
}
//Advance Sprite
- qreal animT = m_spriteEngine->spriteStart()/1000.0;
- qreal frameCount = m_spriteEngine->spriteFrames();
- qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
+ qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
+ qreal frameCount = d->m_spriteEngine->spriteFrames();
+ qreal frameDuration = d->m_spriteEngine->spriteDuration()/frameCount;
double frameAt;
qreal progress;
if (frameDuration > 0) {
@@ -432,32 +300,32 @@ void QQuickSpriteSequence::prepareNextFrame()
frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
progress = std::modf(frame,&frameAt);
} else {
- m_curFrame++;
- if (m_curFrame >= frameCount){
- m_curFrame = 0;
- m_spriteEngine->advance();
+ d->m_curFrame++;
+ if (d->m_curFrame >= frameCount){
+ d->m_curFrame = 0;
+ d->m_spriteEngine->advance();
}
- frameAt = m_curFrame;
+ frameAt = d->m_curFrame;
progress = 0;
}
- if (m_spriteEngine->sprite()->reverse())
- frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
- qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
- qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
+ if (d->m_spriteEngine->sprite()->reverse())
+ frameAt = (d->m_spriteEngine->spriteFrames() - 1) - frameAt;
+ int y = d->m_spriteEngine->spriteY();
+ int w = d->m_spriteEngine->spriteWidth();
+ int h = d->m_spriteEngine->spriteHeight();
+ int x1 = d->m_spriteEngine->spriteX();
x1 += frameAt * w;
- qreal x2 = x1;
+ int x2 = x1;
if (frameAt < (frameCount-1))
x2 += w;
- m_material->animX1 = x1;
- m_material->animY1 = y;
- m_material->animX2 = x2;
- m_material->animY2 = y;
- m_material->animW = w;
- m_material->animH = h;
- m_material->animT = m_interpolate ? progress : 0.0;
+ node->setSourceA(QPoint(x1, y));
+ node->setSourceB(QPoint(x2, y));
+ node->setSpriteSize(QSize(w, h));
+ node->setTime(d->m_interpolate ? progress : 0.0);
+ node->setSize(QSizeF(width(), height()));
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->update();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h
index b4cc133821..34af110a98 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -59,8 +59,8 @@ QT_BEGIN_NAMESPACE
class QSGContext;
class QQuickSprite;
class QQuickSpriteEngine;
-class QSGGeometryNode;
-class QQuickSpriteSequenceMaterial;
+class QQuickSpriteSequencePrivate;
+class QSGSpriteNode;
class Q_AUTOTEST_EXPORT QQuickSpriteSequence : public QQuickItem
{
Q_OBJECT
@@ -77,25 +77,10 @@ public:
QQmlListProperty<QQuickSprite> sprites();
- bool running() const
- {
- return m_running;
- }
-
- bool interpolate() const
- {
- return m_interpolate;
- }
-
- QString goalSprite() const
- {
- return m_goalState;
- }
-
- QString currentSprite() const
- {
- return m_curState;
- }
+ bool running() const;
+ bool interpolate() const;
+ QString goalSprite() const;
+ QString currentSprite() const;
Q_SIGNALS:
@@ -108,46 +93,23 @@ public Q_SLOTS:
void jumpTo(const QString &sprite);
void setGoalSprite(const QString &sprite);
-
- void setRunning(bool arg)
- {
- if (m_running != arg) {
- m_running = arg;
- Q_EMIT runningChanged(arg);
- }
- }
-
- void setInterpolate(bool arg)
- {
- if (m_interpolate != arg) {
- m_interpolate = arg;
- Q_EMIT interpolateChanged(arg);
- }
- }
+ void setRunning(bool arg);
+ void setInterpolate(bool arg);
private Q_SLOTS:
void createEngine();
- void sizeVertices();
protected:
void reset();
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
private:
- void prepareNextFrame();
- QSGGeometryNode* buildNode();
- QSGGeometryNode *m_node;
- QQuickSpriteSequenceMaterial *m_material;
- QList<QQuickSprite*> m_sprites;
- QQuickSpriteEngine* m_spriteEngine;
- QTime m_timestamp;
- int m_curFrame;
- bool m_pleaseReset;
- bool m_running;
- bool m_interpolate;
- QString m_goalState;
- QString m_curState;
- int m_curStateIdx;
- QSizeF m_sheetSize;
+ void prepareNextFrame(QSGSpriteNode *node);
+ QSGSpriteNode* initNode();
+
+
+private:
+ Q_DISABLE_COPY(QQuickSpriteSequence)
+ Q_DECLARE_PRIVATE(QQuickSpriteSequence)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspritesequence_p_p.h b/src/quick/items/qquickspritesequence_p_p.h
new file mode 100644
index 0000000000..4f352b5b69
--- /dev/null
+++ b/src/quick/items/qquickspritesequence_p_p.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef QQUICKSPRITESEQUENCE_P_P_H
+#define QQUICKSPRITESEQUENCE_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 "qquickitem_p.h"
+#include "qquicksprite_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSpriteSequence;
+
+class QQuickSpriteSequencePrivate :public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSpriteSequence)
+public:
+ QQuickSpriteSequencePrivate()
+ : m_spriteEngine(nullptr)
+ , m_curFrame(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_interpolate(true)
+ , m_curStateIdx(0)
+ {
+
+ }
+ QList<QQuickSprite*> m_sprites;
+ QQuickSpriteEngine* m_spriteEngine;
+ QTime m_timestamp;
+ int m_curFrame;
+ bool m_pleaseReset;
+ bool m_running;
+ bool m_interpolate;
+ QString m_goalState;
+ QString m_curState;
+ int m_curStateIdx;
+ QSize m_sheetSize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSPRITESEQUENCE_P_P_H
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index 73b1680305..7e485c675c 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -359,8 +359,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction xa(d->target, QLatin1String("x"), x);
actions << xa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->xString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("x"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->xString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction xa;
xa.property = property;
@@ -378,8 +378,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ya(d->target, QLatin1String("y"), y);
actions << ya;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->yString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("y"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->yString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ya;
ya.property = property;
@@ -397,8 +397,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction sa(d->target, QLatin1String("scale"), scale);
actions << sa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->scaleString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("scale"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->scaleString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction sa;
sa.property = property;
@@ -416,8 +416,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation);
actions << ra;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->rotationString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("rotation"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->rotationString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ra;
ra.property = property;
@@ -435,8 +435,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction wa(d->target, QLatin1String("width"), width);
actions << wa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->widthString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("width"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->widthString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction wa;
wa.property = property;
@@ -454,8 +454,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ha(d->target, QLatin1String("height"), height);
actions << ha;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->heightString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("height"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->heightString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ha;
ha.property = property;
@@ -866,31 +866,31 @@ QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions()
d->baselineProp = QQmlProperty(d->target, QLatin1String("anchors.baseline"));
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::LeftAnchor) {
- d->leftBinding = new QQmlBinding(d->anchorSet->d_func()->leftScript, d->target, qmlContext(this));
+ d->leftBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->leftProp)->core, d->anchorSet->d_func()->leftScript, d->target, qmlContext(this));
d->leftBinding->setTarget(d->leftProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::RightAnchor) {
- d->rightBinding = new QQmlBinding(d->anchorSet->d_func()->rightScript, d->target, qmlContext(this));
+ d->rightBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->rightProp)->core, d->anchorSet->d_func()->rightScript, d->target, qmlContext(this));
d->rightBinding->setTarget(d->rightProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::HCenterAnchor) {
- d->hCenterBinding = new QQmlBinding(d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this));
+ d->hCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->hCenterProp)->core, d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this));
d->hCenterBinding->setTarget(d->hCenterProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::TopAnchor) {
- d->topBinding = new QQmlBinding(d->anchorSet->d_func()->topScript, d->target, qmlContext(this));
+ d->topBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->topProp)->core, d->anchorSet->d_func()->topScript, d->target, qmlContext(this));
d->topBinding->setTarget(d->topProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BottomAnchor) {
- d->bottomBinding = new QQmlBinding(d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this));
+ d->bottomBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->bottomProp)->core, d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this));
d->bottomBinding->setTarget(d->bottomProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::VCenterAnchor) {
- d->vCenterBinding = new QQmlBinding(d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this));
+ d->vCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->vCenterProp)->core, d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this));
d->vCenterBinding->setTarget(d->vCenterProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BaselineAnchor) {
- d->baselineBinding = new QQmlBinding(d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this));
+ d->baselineBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->baselineProp)->core, d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this));
d->baselineBinding->setTarget(d->baselineProp);
}
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 80321cf5d1..2bfb4501fc 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -69,6 +69,7 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
@@ -321,7 +322,7 @@ void QQuickText::imageDownloadFinished()
if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
bool needToUpdateLayout = false;
- foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) {
+ for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) {
if (!img->size.isValid()) {
img->size = img->pix->implicitSize();
needToUpdateLayout = true;
@@ -1114,7 +1115,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
QList<QQuickStyledTextImgTag *> imagesInLine;
if (extra.isAllocated()) {
- foreach (QQuickStyledTextImgTag *image, extra->imgTags) {
+ for (QQuickStyledTextImgTag *image : qAsConst(extra->imgTags)) {
if (image->position >= line.textStart() &&
image->position < line.textStart() + line.textLength()) {
@@ -1151,7 +1152,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
}
}
- foreach (QQuickStyledTextImgTag *image, imagesInLine) {
+ for (QQuickStyledTextImgTag *image : qAsConst(imagesInLine)) {
totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
const int leadX = line.cursorToX(image->position);
const int trailX = line.cursorToX(image->position, QTextLine::Trailing);
@@ -1431,6 +1432,39 @@ QQuickText::~QQuickText()
Text { text: "Hello"; font.capitalization: Font.AllLowercase }
\endqml
*/
+
+/*!
+ \qmlproperty enumeration QtQuick::Text::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type Text.NativeRendering.
+
+ \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. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -2308,7 +2342,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
if (d->extra.isAllocated()) {
- foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) {
+ for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) {
QQuickPixmap *pix = img->pix;
if (pix && pix->isReady())
node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
@@ -2562,7 +2596,8 @@ QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mo
QTextLine line = layout->lineAt(i);
if (line.naturalTextRect().contains(mousePos)) {
int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter);
- foreach (const QTextLayout::FormatRange &formatRange, layout->formats()) {
+ const auto formats = layout->formats();
+ for (const QTextLayout::FormatRange &formatRange : formats) {
if (formatRange.format.isAnchor()
&& charPos >= formatRange.start
&& charPos < formatRange.start + formatRange.length) {
@@ -2691,6 +2726,7 @@ QString QQuickText::hoveredLink() const
void QQuickTextPrivate::processHoverEvent(QHoverEvent *event)
{
Q_Q(QQuickText);
+ qCDebug(DBG_HOVER_TRACE) << q;
QString link;
if (isLinkHoveredConnected()) {
if (event->type() != QEvent::HoverLeave)
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 0cee9c7d27..ef7485a8e9 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -44,6 +44,7 @@
#include <qcoreapplication.h>
#include <qfont.h>
+#include <qfontmetrics.h>
#include <qevent.h>
#include <qdebug.h>
#include <qdrag.h>
@@ -57,6 +58,7 @@
#include "qtextlist.h"
#include "qtextdocumentwriter.h"
#include "private/qtextcursor_p.h"
+#include <QtCore/qloggingcategory.h>
#include <qtextformat.h>
#include <qdatetime.h>
@@ -75,6 +77,7 @@
const int textCursorWidth = 1;
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
#ifndef QT_NO_CONTEXTMENU
#endif
@@ -982,6 +985,14 @@ process:
{
QString text = e->text();
if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
+ if (overwriteMode
+ // no need to call deleteChar() if we have a selection, insertText
+ // does it already
+ && !cursor.hasSelection()
+ && !cursor.atBlockEnd()) {
+ cursor.deleteChar();
+ }
+
cursor.insertText(text);
selectionChanged();
} else {
@@ -1024,6 +1035,12 @@ QRectF QQuickTextControlPrivate::rectForPosition(int position) const
if (line.isValid()) {
qreal x = line.cursorToX(relativePos);
qreal w = 0;
+ if (overwriteMode) {
+ if (relativePos < line.textLength() - line.textStart())
+ w = line.cursorToX(relativePos + 1) - x;
+ else
+ w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), textCursorWidth + w, line.height());
} else {
r = QRectF(layoutPos.x(), layoutPos.y(), textCursorWidth, 10); // #### correct height
@@ -1498,6 +1515,7 @@ void QQuickTextControlPrivate::hoverEvent(QHoverEvent *e, const QPointF &pos)
hoveredLink = link;
emit q->linkHovered(link);
}
+ qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hoveredLink" << hoveredLink;
}
bool QQuickTextControl::hasImState() const
@@ -1506,6 +1524,21 @@ bool QQuickTextControl::hasImState() const
return d->hasImState;
}
+bool QQuickTextControl::overwriteMode() const
+{
+ Q_D(const QQuickTextControl);
+ return d->overwriteMode;
+}
+
+void QQuickTextControl::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextControl);
+ if (d->overwriteMode == overwrite)
+ return;
+ d->overwriteMode = overwrite;
+ emit overwriteModeChanged(overwrite);
+}
+
bool QQuickTextControl::cursorVisible() const
{
Q_D(const QQuickTextControl);
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index ca5a3d3191..602e457cb8 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -94,6 +94,8 @@ public:
#endif
bool hasImState() const;
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
bool cursorVisible() const;
void setCursorVisible(bool visible);
QRectF anchorRect() const;
@@ -149,6 +151,7 @@ Q_SIGNALS:
void copyAvailable(bool b);
void selectionChanged();
void cursorPositionChanged();
+ void overwriteModeChanged(bool overwriteMode);
// control signals
void updateCursorRequest();
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 3eacfd61bc..1dc54eb107 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -219,7 +219,7 @@ QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
void QQuickTextDocumentWithImageResources::clearResources()
{
- foreach (QQuickPixmap *pixmap, m_resources)
+ for (QQuickPixmap *pixmap : qAsConst(m_resources))
pixmap->clear(this);
qDeleteAll(m_resources);
m_resources.clear();
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 9b23abc877..c81544cbdb 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -153,7 +153,7 @@ namespace {
newNode->setFlag(QSGNode::OwnedByParent);
}
- void resetCursorNode(QSGRectangleNode* newNode)
+ void resetCursorNode(QSGInternalRectangleNode* newNode)
{
if (cursorNode)
removeChildNode(cursorNode);
@@ -165,7 +165,7 @@ namespace {
}
}
- QSGRectangleNode *cursorNode;
+ QSGInternalRectangleNode *cursorNode;
QQuickTextNode* frameDecorationsNode;
};
@@ -323,6 +323,39 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty enumeration QtQuick::TextEdit::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type TextEdit.NativeRendering.
+
+ \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. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; renderType: TextEdit.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -1583,6 +1616,32 @@ bool QQuickTextEdit::event(QEvent *event)
}
/*!
+ \qmlproperty bool QtQuick::TextEdit::overwriteMode
+ \since 5.8
+ Whether text entered by the user will overwrite existing text.
+
+ As with many text editors, the text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is \c true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is \c false (new text does not overwrite existing text).
+*/
+bool QQuickTextEdit::overwriteMode() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->control->overwriteMode();
+}
+
+void QQuickTextEdit::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextEdit);
+ d->control->setOverwriteMode(overwrite);
+}
+
+/*!
\overload
Handles the given key \a event.
*/
@@ -2006,7 +2065,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
// Having nodes spanning across frame boundaries will break the current bookkeeping mechanism. We need to prevent that.
QList<int> frameBoundaries;
frameBoundaries.reserve(frames.size());
- Q_FOREACH (QTextFrame *frame, frames)
+ for (QTextFrame *frame : qAsConst(frames))
frameBoundaries.append(frame->firstPosition());
std::sort(frameBoundaries.begin(), frameBoundaries.end());
@@ -2066,9 +2125,9 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
if (d->cursorComponent == 0) {
- QSGRectangleNode* cursor = 0;
+ QSGInternalRectangleNode* cursor = 0;
if (!isReadOnly() && d->cursorVisible && d->control->cursorOn())
- cursor = d->sceneGraphContext()->createRectangleNode(d->control->cursorRect(), d->color);
+ cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
rootNode->resetCursorNode(cursor);
}
@@ -2193,6 +2252,7 @@ void QQuickTextEditPrivate::init()
qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
qmlobject_connect(control, QQuickTextControl, SIGNAL(linkHovered(QString)), q, QQuickTextEdit, SIGNAL(linkHovered(QString)));
+ qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool)));
qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
#ifndef QT_NO_CLIPBOARD
@@ -2439,7 +2499,7 @@ void QQuickTextEdit::updateWholeDocument()
{
Q_D(QQuickTextEdit);
if (!d->textNodeMap.isEmpty()) {
- Q_FOREACH (TextNode* node, d->textNodeMap)
+ for (TextNode* node : qAsConst(d->textNodeMap))
node->setDirty();
}
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index f6ecb984e3..42c9064860 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -86,6 +86,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
Q_PROPERTY(QQmlComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged)
Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
@@ -199,6 +200,9 @@ public:
QQmlComponent* cursorDelegate() const;
void setCursorDelegate(QQmlComponent*);
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
int selectionStart() const;
int selectionEnd() const;
@@ -313,6 +317,7 @@ Q_SIGNALS:
void readOnlyChanged(bool isReadOnly);
void cursorVisibleChanged(bool isCursorVisible);
void cursorDelegateChanged();
+ void overwriteModeChanged(bool overwriteMode);
void activeFocusOnPressChanged(bool activeFocusOnPressed);
void persistentSelectionChanged(bool isPersistentSelection);
void textMarginChanged(qreal textMargin);
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 168fd0d9b6..6543a64624 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -345,6 +345,38 @@ QString QQuickTextInputPrivate::realText() const
\endqml
*/
+/*!
+ \qmlproperty enumeration QtQuick::TextInput::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type TextInput.NativeRendering.
+
+ \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. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
@@ -808,7 +840,14 @@ QRectF QQuickTextInput::cursorRectangle() const
return QRectF();
qreal x = l.cursorToX(c) - d->hscroll + leftPadding();
qreal y = l.y() - d->vscroll + topPadding();
- return QRectF(x, y, 1, l.height());
+ qreal w = 1;
+ if (d->overwriteMode) {
+ if (c < text().length())
+ w = l.cursorToX(c + 1) - x;
+ else
+ w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
+ return QRectF(x, y, w, l.height());
}
/*!
@@ -1289,7 +1328,14 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
return QRectF();
qreal x = l.cursorToX(pos) - d->hscroll;
qreal y = l.y() - d->vscroll;
- return QRectF(x, y, 1, l.height());
+ qreal w = 1;
+ if (d->overwriteMode) {
+ if (pos < text().length())
+ w = l.cursorToX(pos + 1) - x;
+ else
+ w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
+ return QRectF(x, y, w, l.height());
}
/*!
@@ -1371,6 +1417,36 @@ int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPositi
return line.isValid() ? line.xToCursor(x, position) : 0;
}
+/*!
+ \qmlproperty bool QtQuick::TextInput::overwriteMode
+ \since 5.8
+
+ Whether text entered by the user will overwrite existing text.
+
+ As with many text editors, the text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is \c true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is \c false (new text does not overwrite existing text).
+*/
+bool QQuickTextInput::overwriteMode() const
+{
+ Q_D(const QQuickTextInput);
+ return d->overwriteMode;
+}
+
+void QQuickTextInput::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextInput);
+ if (d->overwriteMode == overwrite)
+ return;
+ d->overwriteMode = overwrite;
+ emit overwriteModeChanged(overwrite);
+}
+
void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
{
Q_D(QQuickTextInput);
@@ -2606,8 +2682,8 @@ void QQuickTextInputPrivate::init()
#endif
q->setFlag(QQuickItem::ItemHasContents);
#ifndef QT_NO_CLIPBOARD
- q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
- q, SLOT(q_canPasteChanged()));
+ qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()),
+ q, QQuickTextInput, SLOT(q_canPasteChanged()));
#endif // QT_NO_CLIPBOARD
lastSelectionStart = 0;
@@ -2768,7 +2844,7 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
// characters)
QChar* uc = str.data();
for (int i = 0; i < (int)str.length(); ++i) {
- if ((uc[i] < 0x20 && uc[i] != 0x09)
+ if ((uc[i].unicode() < 0x20 && uc[i] != QChar::Tabulation)
|| uc[i] == QChar::LineSeparator
|| uc[i] == QChar::ParagraphSeparator
|| uc[i] == QChar::ObjectReplacementCharacter)
@@ -4454,6 +4530,14 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
if (unknown && !m_readOnly) {
QString t = event->text();
if (!t.isEmpty() && t.at(0).isPrint()) {
+ if (overwriteMode
+ // no need to call del() if we have a selection, insert
+ // does it already
+ && !hasSelectedText()
+ && !(m_cursor == q_func()->text().length())) {
+ del();
+ }
+
insert(t);
event->accept();
return;
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 211d9146fc..d0461f551e 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -79,6 +79,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
Q_PROPERTY(QQmlComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged)
Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
@@ -245,6 +246,9 @@ public:
QQmlComponent* cursorDelegate() const;
void setCursorDelegate(QQmlComponent*);
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
bool focusOnPress() const;
void setFocusOnPress(bool);
@@ -325,6 +329,7 @@ Q_SIGNALS:
void readOnlyChanged(bool isReadOnly);
void cursorVisibleChanged(bool isCursorVisible);
void cursorDelegateChanged();
+ void overwriteModeChanged(bool overwriteMode);
void maximumLengthChanged(int maximumLength);
void validatorChanged();
void inputMaskChanged(const QString &inputMask);
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 57a9f6413a..93a8778c40 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -158,6 +158,7 @@ public:
, m_passwordEchoEditing(false)
, inLayout(false)
, requireImplicitWidth(false)
+ , overwriteMode(false)
{
}
@@ -299,6 +300,7 @@ public:
bool m_passwordEchoEditing : 1;
bool inLayout:1;
bool requireImplicitWidth:1;
+ bool overwriteMode:1;
static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
return t->d_func();
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index b0bb60f566..8716f98bff 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -136,7 +136,7 @@ void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
delete m_cursorNode;
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- m_cursorNode = sg->sceneGraphContext()->createRectangleNode(rect, color);
+ m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, color);
appendChildNode(m_cursorNode);
}
@@ -151,14 +151,14 @@ void QQuickTextNode::clearCursor()
void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
{
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- appendChildNode(sg->sceneGraphContext()->createRectangleNode(rect, color));
+ appendChildNode(sg->sceneGraphContext()->createInternalRectangleNode(rect, color));
}
void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
{
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QSGImageNode *node = sg->sceneGraphContext()->createImageNode();
+ QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode();
QSGTexture *texture = sg->createTexture(image);
if (m_ownerElement->smooth()) {
texture->setFiltering(QSGTexture::Linear);
diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h
index 0006cf1156..2969ce9dbc 100644
--- a/src/quick/items/qquicktextnode_p.h
+++ b/src/quick/items/qquicktextnode_p.h
@@ -68,7 +68,7 @@ class QColor;
class QTextDocument;
class QSGContext;
class QRawFont;
-class QSGRectangleNode;
+class QSGInternalRectangleNode;
class QSGClipNode;
class QSGTexture;
@@ -77,15 +77,6 @@ class QQuickTextNodeEngine;
class Q_QUICK_PRIVATE_EXPORT QQuickTextNode : public QSGTransformNode
{
public:
- enum Decoration {
- NoDecoration = 0x0,
- Underline = 0x1,
- Overline = 0x2,
- StrikeOut = 0x4,
- Background = 0x8
- };
- Q_DECLARE_FLAGS(Decorations, Decoration)
-
QQuickTextNode(QQuickItem *ownerElement);
~QQuickTextNode();
@@ -106,7 +97,7 @@ public:
void setCursor(const QRectF &rect, const QColor &color);
void clearCursor();
- QSGRectangleNode *cursorNode() const { return m_cursorNode; }
+ QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
@@ -118,7 +109,7 @@ public:
void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; }
private:
- QSGRectangleNode *m_cursorNode;
+ QSGInternalRectangleNode *m_cursorNode;
QList<QSGTexture *> m_textures;
QQuickItem *m_ownerElement;
bool m_useNativeRenderer;
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index ef94b0eef7..4631b2e724 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -68,7 +68,7 @@ QQuickTextNodeEngine::BinaryTreeNodeKey::BinaryTreeNodeKey(BinaryTreeNode *node)
QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g,
SelectionState selState,
const QRectF &brect,
- const QQuickTextNode::Decorations &decs,
+ const Decorations &decs,
const QColor &c,
const QColor &bc,
const QPointF &pos, qreal a)
@@ -90,7 +90,7 @@ QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g,
void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState,
- QQuickTextNode::Decorations decorations, const QColor &textColor,
+ Decorations decorations, const QColor &textColor,
const QColor &backgroundColor, const QPointF &position)
{
QRectF searchRect = glyphRun.boundingRect();
@@ -99,10 +99,10 @@ void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArray<BinaryTreeNode
if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height()))
return;
- decorations |= (glyphRun.underline() ? QQuickTextNode::Underline : QQuickTextNode::NoDecoration);
- decorations |= (glyphRun.overline() ? QQuickTextNode::Overline : QQuickTextNode::NoDecoration);
- decorations |= (glyphRun.strikeOut() ? QQuickTextNode::StrikeOut : QQuickTextNode::NoDecoration);
- decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration);
+ decorations |= (glyphRun.underline() ? Decoration::Underline : Decoration::NoDecoration);
+ decorations |= (glyphRun.overline() ? Decoration::Overline : Decoration::NoDecoration);
+ decorations |= (glyphRun.strikeOut() ? Decoration::StrikeOut : Decoration::NoDecoration);
+ decorations |= (backgroundColor.isValid() ? Decoration::Background : Decoration::NoDecoration);
qreal ascent = glyphRun.rawFont().ascent();
insert(binaryTree, BinaryTreeNode(glyphRun,
@@ -237,7 +237,7 @@ void QQuickTextNodeEngine::processCurrentLine()
SelectionState currentSelectionState = Unselected;
QRectF currentRect;
- QQuickTextNode::Decorations currentDecorations = QQuickTextNode::NoDecoration;
+ Decorations currentDecorations = Decoration::NoDecoration;
qreal underlineOffset = 0.0;
qreal underlineThickness = 0.0;
@@ -271,7 +271,7 @@ void QQuickTextNodeEngine::processCurrentLine()
currentSelectionState = node->selectionState;
// Update decorations
- if (currentDecorations != QQuickTextNode::NoDecoration) {
+ if (currentDecorations != Decoration::NoDecoration) {
decorationRect.setY(m_position.y() + m_currentLine.y());
decorationRect.setHeight(m_currentLine.height());
@@ -279,16 +279,16 @@ void QQuickTextNodeEngine::processCurrentLine()
decorationRect.setRight(node->boundingRect.left());
TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor);
- if (currentDecorations & QQuickTextNode::Underline)
+ if (currentDecorations & Decoration::Underline)
pendingUnderlines.append(textDecoration);
- if (currentDecorations & QQuickTextNode::Overline)
+ if (currentDecorations & Decoration::Overline)
pendingOverlines.append(textDecoration);
- if (currentDecorations & QQuickTextNode::StrikeOut)
+ if (currentDecorations & Decoration::StrikeOut)
pendingStrikeOuts.append(textDecoration);
- if (currentDecorations & QQuickTextNode::Background)
+ if (currentDecorations & Decoration::Background)
m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor));
}
@@ -344,7 +344,7 @@ void QQuickTextNodeEngine::processCurrentLine()
// If previous item(s) had underline and current does not, then we add the
// pending lines to the lists and likewise for overlines and strikeouts
if (!pendingUnderlines.isEmpty()
- && !(node->decorations & QQuickTextNode::Underline)) {
+ && !(node->decorations & Decoration::Underline)) {
addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
pendingUnderlines.clear();
@@ -377,19 +377,19 @@ void QQuickTextNodeEngine::processCurrentLine()
// Merge current values with previous. Prefer greatest thickness
QRawFont rawFont = node->glyphRun.rawFont();
- if (node->decorations & QQuickTextNode::Underline) {
+ if (node->decorations & Decoration::Underline) {
if (rawFont.lineThickness() > underlineThickness) {
underlineThickness = rawFont.lineThickness();
underlineOffset = rawFont.underlinePosition();
}
}
- if (node->decorations & QQuickTextNode::Overline) {
+ if (node->decorations & Decoration::Overline) {
overlineOffset = -rawFont.ascent();
overlineThickness = rawFont.lineThickness();
}
- if (node->decorations & QQuickTextNode::StrikeOut) {
+ if (node->decorations & Decoration::StrikeOut) {
strikeOutThickness = rawFont.lineThickness();
strikeOutOffset = rawFont.ascent() / -3.0;
}
@@ -495,7 +495,7 @@ void QQuickTextNodeEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun)
BinaryTreeNode::insert(&m_currentLineTree,
glyphRun,
Unselected,
- QQuickTextNode::NoDecoration,
+ Decoration::NoDecoration,
m_textColor,
m_backgroundColor,
m_position);
@@ -507,7 +507,7 @@ void QQuickTextNodeEngine::addSelectedGlyphs(const QGlyphRun &glyphRun)
BinaryTreeNode::insert(&m_currentLineTree,
glyphRun,
Selected,
- QQuickTextNode::NoDecoration,
+ Decoration::NoDecoration,
m_textColor,
m_backgroundColor,
m_position);
@@ -734,10 +734,13 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
QVector<QPointF> glyphPositions = glyphRun.positions();
glyphPositions.reserve(count);
+ QRectF glyphBoundingRect = glyphRun.boundingRect();
+
for (int j = 1; j < nodes.size(); ++j) {
BinaryTreeNode *otherNode = nodes.at(j);
glyphIndexes += otherNode->glyphRun.glyphIndexes();
primaryNode->ranges += otherNode->ranges;
+ glyphBoundingRect = glyphBoundingRect.united(otherNode->boundingRect);
QVector<QPointF> otherPositions = otherNode->glyphRun.positions();
for (int k = 0; k < otherPositions.size(); ++k)
@@ -749,6 +752,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
glyphRun.setGlyphIndexes(glyphIndexes);
glyphRun.setPositions(glyphPositions);
+ glyphRun.setBoundingRect(glyphBoundingRect);
}
}
}
diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h
index 87235344e6..91ed6f4430 100644
--- a/src/quick/items/qquicktextnodeengine_p.h
+++ b/src/quick/items/qquicktextnodeengine_p.h
@@ -68,8 +68,15 @@ QT_BEGIN_NAMESPACE
// number of nodes, and join decorations in neighbouring items
class QQuickTextNodeEngine {
-
public:
+ enum Decoration {
+ NoDecoration = 0x0,
+ Underline = 0x1,
+ Overline = 0x2,
+ StrikeOut = 0x4,
+ Background = 0x8
+ };
+ Q_DECLARE_FLAGS(Decorations, Decoration)
enum SelectionState {
Unselected,
@@ -79,26 +86,26 @@ public:
struct BinaryTreeNode {
BinaryTreeNode()
- : selectionState(Unselected), clipNode(0), decorations(QQuickTextNode::NoDecoration)
+ : selectionState(Unselected), clipNode(0), decorations(Decoration::NoDecoration)
, ascent(0.0), leftChildIndex(-1), rightChildIndex(-1)
{
}
BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a)
- : boundingRect(brect), selectionState(selState), clipNode(0), decorations(QQuickTextNode::NoDecoration)
+ : boundingRect(brect), selectionState(selState), clipNode(0), decorations(Decoration::NoDecoration)
, image(i), ascent(a), leftChildIndex(-1), rightChildIndex(-1)
{
}
BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect,
- const QQuickTextNode::Decorations &decs, const QColor &c, const QColor &bc,
+ const Decorations &decs, const QColor &c, const QColor &bc,
const QPointF &pos, qreal a);
QGlyphRun glyphRun;
QRectF boundingRect;
SelectionState selectionState;
QQuickDefaultClipNode *clipNode;
- QQuickTextNode::Decorations decorations;
+ Decorations decorations;
QColor color;
QColor backgroundColor;
QPointF position;
@@ -114,7 +121,7 @@ public:
{ insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent)); }
static void insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState,
- QQuickTextNode::Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position);
+ Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position);
static void insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const BinaryTreeNode &binaryTreeNode);
static void inOrder(const QVarLengthArray<BinaryTreeNode, 16> &binaryTree, QVarLengthArray<int> *sortedIndexes, int currentIndex = 0);
};
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index f8973ebfba..a167f01484 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -44,9 +44,6 @@
#include "qquickitem_p.h"
#include "qquickitemchangelistener_p.h"
-#include <private/qqmldebugconnector_p.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <QtQml/qqmlengine.h>
@@ -113,14 +110,15 @@ void QQuickViewPrivate::execute()
}
}
-void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickView);
if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) {
// wait for both width and height to be changed
resizetimer.start(0,q);
}
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
/*!
@@ -176,9 +174,8 @@ QQuickView::QQuickView(QWindow *parent)
*/
QQuickView::QQuickView(const QUrl &source, QWindow *parent)
-: QQuickWindow(*(new QQuickViewPrivate), parent)
+ : QQuickView(parent)
{
- d_func()->init();
setSource(source);
}
@@ -249,8 +246,8 @@ void QQuickView::setContent(const QUrl& url, QQmlComponent *component, QObject*
d->component = component;
if (d->component && d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -415,9 +412,14 @@ void QQuickViewPrivate::updateSize()
q->resize(newSize);
}
} else if (resizeMode == QQuickView::SizeRootObjectToView) {
- if (!qFuzzyCompare(q->width(), root->width()))
+ bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
+ bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
+
+ if (needToUpdateWidth && needToUpdateHeight)
+ root->setSize(QSizeF(q->width(), q->height()));
+ else if (needToUpdateWidth)
root->setWidth(q->width());
- if (!qFuzzyCompare(q->height(), root->height()))
+ else if (needToUpdateHeight)
root->setHeight(q->height());
}
}
@@ -455,8 +457,8 @@ void QQuickView::continueExecute()
disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -467,8 +469,8 @@ void QQuickView::continueExecute()
QObject *obj = d->component->create();
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index fd033daf2f..a090bdc9f7 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -88,7 +88,7 @@ public:
~QQuickViewPrivate();
void execute();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void initResize();
void updateSize();
void setRootObject(QObject *);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 10e439182d..0151a4e3ca 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -74,16 +74,20 @@
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
-
-#include <private/qopenglvertexarrayobject_p.h>
+#ifndef QT_NO_OPENGL
+# include <private/qopenglvertexarrayobject_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch");
-Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target");
-Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse");
-Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus");
-Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty");
+Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
+Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
+Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
+Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
+Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
+Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
@@ -199,11 +203,6 @@ thus the first item that has focus will get it (assuming the scope doesn't alrea
have a scope focused item), and the other items will have their focus cleared.
*/
-QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
-: transformNode(0)
-{
-}
-
QQuickRootItem::QQuickRootItem()
{
}
@@ -289,7 +288,9 @@ void QQuickWindowPrivate::polishItems()
int recursionSafeguard = INT_MAX;
while (!itemsToPolish.isEmpty() && --recursionSafeguard > 0) {
QQuickItem *item = itemsToPolish.takeLast();
- QQuickItemPrivate::get(item)->polishScheduled = false;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->polishScheduled = false;
+ itemPrivate->updatePolish();
item->updatePolish();
}
@@ -470,7 +471,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
QQuickWindowPrivate::QQuickWindowPrivate()
: contentItem(0)
, activeFocusItem(0)
- , mouseGrabberItem(0)
#ifndef QT_NO_CURSOR
, cursorItem(0)
#endif
@@ -478,6 +478,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, dragGrabber(0)
#endif
, touchMouseId(-1)
+ , touchMouseDevice(nullptr)
, touchMousePressTimestamp(0)
, dirtyItemList(0)
, devicePixelRatio(0)
@@ -485,7 +486,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, renderer(0)
, windowManager(0)
, renderControl(0)
- , touchRecursionGuard(0)
+ , pointerEventRecursionGuard(0)
, customRenderStage(0)
, clearColor(Qt::white)
, clearBeforeRendering(true)
@@ -621,8 +622,17 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp)
return doubleClicked;
}
-bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
+bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
{
+ Q_Q(QQuickWindow);
+ auto device = pointerEvent->device();
+
+ // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
+ Q_ASSERT(pointerEvent->asPointerTouchEvent());
+ QTouchEvent *event = pointerEvent->asPointerTouchEvent()->touchEventForItem(item);
+ if (!event)
+ return false;
+
// For each point, check if it is accepted, if not, try the next point.
// Any of the fingers can become the mouse one.
// This can happen because a mouse area might not accept an event at some point but another.
@@ -636,65 +646,45 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
if (!item->contains(pos))
break;
- // Store the id already here and restore it to -1 if the event does not get
- // accepted. Cannot defer setting the new value because otherwise if the event
- // handler spins the event loop all subsequent moves and releases get lost.
- touchMouseId = p.id();
- itemForTouchPointId[touchMouseId] = item;
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item;
QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false));
// Send a single press and see if that's accepted
- if (!mouseGrabberItem)
- item->grabMouse();
- item->grabTouchPoints(QVector<int>() << touchMouseId);
-
QCoreApplication::sendEvent(item, mousePress.data());
event->setAccepted(mousePress->isAccepted());
- if (!mousePress->isAccepted()) {
- touchMouseId = -1;
- if (itemForTouchPointId.value(p.id()) == item) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated";
- itemForTouchPointId.remove(p.id());
+ if (mousePress->isAccepted()) {
+ touchMouseDevice = device;
+ touchMouseId = p.id();
+ if (!q->mouseGrabberItem())
+ item->grabMouse();
+ auto pointerEventPoint = pointerEvent->pointById(p.id());
+ pointerEventPoint->setGrabber(item);
+
+ if (checkIfDoubleClicked(event->timestamp())) {
+ QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false));
+ QCoreApplication::sendEvent(item, mouseDoubleClick.data());
+ event->setAccepted(mouseDoubleClick->isAccepted());
+ if (!mouseDoubleClick->isAccepted()) {
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+ }
}
- if (mouseGrabberItem == item)
- item->ungrabMouse();
- }
-
- if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) {
- QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false));
- QCoreApplication::sendEvent(item, mouseDoubleClick.data());
- event->setAccepted(mouseDoubleClick->isAccepted());
- if (mouseDoubleClick->isAccepted()) {
- touchMouseIdCandidates.clear();
- return true;
- } else {
- touchMouseId = -1;
- }
- }
- // The event was accepted, we are done.
- if (mousePress->isAccepted()) {
- touchMouseIdCandidates.clear();
return true;
}
- // The event was not accepted but touchMouseId was set.
- if (touchMouseId != -1)
- return false;
// try the next point
// Touch point was there before and moved
- } else if (p.id() == touchMouseId) {
+ } else if (touchMouseDevice == device && p.id() == touchMouseId) {
if (p.state() & Qt::TouchPointMoved) {
- if (mouseGrabberItem) {
+ if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false));
QCoreApplication::sendEvent(item, me.data());
event->setAccepted(me->isAccepted());
if (me->isAccepted()) {
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem;
- itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
- return true;
}
+ return event->isAccepted();
} else {
// no grabber, check if we care about mouse hover
// FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
@@ -716,8 +706,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
}
} else if (p.state() & Qt::TouchPointReleased) {
// currently handled point was released
- touchMouseId = -1;
- if (mouseGrabberItem) {
+ if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false));
QCoreApplication::sendEvent(item, me.data());
@@ -729,8 +718,11 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
Qt::NoButton, Qt::NoButton, event->modifiers());
QCoreApplication::sendEvent(item, &mm);
}
- if (mouseGrabberItem) // might have ungrabbed due to event
- mouseGrabberItem->ungrabMouse();
+ if (q->mouseGrabberItem()) // might have ungrabbed due to event
+ q->mouseGrabberItem()->ungrabMouse();
+
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
return me->isAccepted();
}
}
@@ -743,39 +735,94 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
{
Q_Q(QQuickWindow);
- if (mouseGrabberItem == grabber)
+ if (q->mouseGrabberItem() == grabber)
return;
- QQuickItem *oldGrabber = mouseGrabberItem;
- mouseGrabberItem = grabber;
+ qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber;
+ QQuickItem *oldGrabber = q->mouseGrabberItem();
- if (touchMouseId != -1) {
+ if (grabber && touchMouseId != -1 && touchMouseDevice) {
// update the touch item for mouse touch id to the new grabber
- itemForTouchPointId.remove(touchMouseId);
- if (grabber) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << mouseGrabberItem;
- itemForTouchPointId[touchMouseId] = grabber;
- }
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem();
+ auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId);
+ if (point)
+ point->setGrabber(grabber);
+ } else {
+ QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ Q_ASSERT(event->pointCount() == 1);
+ event->point(0)->setGrabber(grabber);
}
if (oldGrabber) {
- QEvent ev(QEvent::UngrabMouse);
- q->sendEvent(oldGrabber, &ev);
+ QEvent e(QEvent::UngrabMouse);
+ QSet<QQuickItem *> hasFiltered;
+ if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered))
+ oldGrabber->mouseUngrabEvent();
}
}
-void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
+void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids)
{
- QMatrix4x4 transformMatrix(transform);
- for (int i=0; i<touchPoints.count(); i++) {
- QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
- touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
- touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
- touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
- touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
+ QSet<QQuickItem*> 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);
+ continue;
+ }
+ if (id == touchMouseId) {
+ auto point = touchMouseDevice->pointerEvent()->pointById(id);
+ auto touchMouseGrabber = point->grabber();
+ if (touchMouseGrabber) {
+ point->setGrabber(nullptr);
+ touchMouseGrabber->mouseUngrabEvent();
+ ungrab.insert(touchMouseGrabber);
+ touchMouseDevice = nullptr;
+ touchMouseId = -1;
+ }
+ qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null";
+ }
+
+ const auto touchDevices = QQuickPointerDevice::touchDevices();
+ for (auto device : touchDevices) {
+ auto point = device->pointerEvent()->pointById(id);
+ if (!point)
+ continue;
+ QQuickItem *oldGrabber = point->grabber();
+ if (oldGrabber == grabber)
+ continue;
+
+ point->setGrabber(grabber);
+ if (oldGrabber)
+ ungrab.insert(oldGrabber);
+ }
}
+ for (QQuickItem *oldGrabber : qAsConst(ungrab))
+ oldGrabber->touchUngrabEvent();
}
+void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
+{
+ Q_Q(QQuickWindow);
+ if (Q_LIKELY(touch)) {
+ 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)->grabber() == grabber) {
+ pointerEvent->point(i)->setGrabber(nullptr);
+ // FIXME send ungrab event only once
+ grabber->touchUngrabEvent();
+ }
+ }
+ }
+ }
+ if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) {
+ qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null";
+ setMouseGrabber(nullptr);
+ }
+}
/*!
Translates the data in \a touchEvent to this window. This method leaves the item local positions in
@@ -787,16 +834,9 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
for (int i = 0; i < touchPoints.count(); ++i) {
QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
- touchPoint.setScreenRect(touchPoint.sceneRect());
- touchPoint.setStartScreenPos(touchPoint.startScenePos());
- touchPoint.setLastScreenPos(touchPoint.lastScenePos());
-
touchPoint.setSceneRect(touchPoint.rect());
touchPoint.setStartScenePos(touchPoint.startPos());
touchPoint.setLastScenePos(touchPoint.lastPos());
-
- if (i == 0)
- lastMousePosition = touchPoint.pos().toPoint();
}
touchEvent->setTouchPoints(touchPoints);
}
@@ -907,17 +947,16 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
// Now that all the state is changed, emit signals & events
- // We must do this last, as this process may result in further changes to
- // focus.
+ // We must do this last, as this process may result in further changes to focus.
if (oldActiveFocusItem) {
QFocusEvent event(QEvent::FocusOut, reason);
- q->sendEvent(oldActiveFocusItem, &event);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
}
// Make sure that the FocusOut didn't result in another focus change.
if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
QFocusEvent event(QEvent::FocusIn, reason);
- q->sendEvent(newActiveFocusItem, &event);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
}
if (activeFocusItem != currentActiveFocusItem)
@@ -1004,13 +1043,13 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
// focus.
if (oldActiveFocusItem) {
QFocusEvent event(QEvent::FocusOut, reason);
- q->sendEvent(oldActiveFocusItem, &event);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
}
// Make sure that the FocusOut didn't result in another focus change.
if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
QFocusEvent event(QEvent::FocusIn, reason);
- q->sendEvent(newActiveFocusItem, &event);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
}
if (activeFocusItem != currentActiveFocusItem)
@@ -1120,8 +1159,8 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
\section1 Rendering
- QQuickWindow uses a scene graph on top of OpenGL to
- render. This scene graph is disconnected from the QML scene and
+ QQuickWindow uses a scene graph to represent what needs to be rendered.
+ This scene graph is disconnected from the QML scene and
potentially lives in another thread, depending on the platform
implementation. Since the rendering scene graph lives
independently from the QML scene, it can also be completely
@@ -1135,9 +1174,9 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
\section2 Integration with OpenGL
- It is possible to integrate OpenGL calls directly into the
- QQuickWindow using the same OpenGL context as the Qt Quick Scene
- Graph. This is done by connecting to the
+ When using the default OpenGL adaptation, it is possible to integrate
+ OpenGL calls directly into the QQuickWindow using the same OpenGL
+ context as the Qt Quick Scene Graph. This is done by connecting to the
QQuickWindow::beforeRendering() or QQuickWindow::afterRendering()
signal.
@@ -1150,10 +1189,10 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
When a QQuickWindow instance is deliberately hidden with hide() or
setVisible(false), it will stop rendering and its scene graph and
- OpenGL context might be released. The sceneGraphInvalidated()
+ graphics context might be released. The sceneGraphInvalidated()
signal will be emitted when this happens.
- \warning It is crucial that OpenGL operations and interaction with
+ \warning It is crucial that graphics operations and interaction with
the scene graph happens exclusively on the rendering thread,
primarily during the updatePaintNode() phase.
@@ -1169,8 +1208,9 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
required to aggressively release these resources. The
releaseResources() can be used to force the clean up of certain
resources. Calling releaseResources() may result in the entire
- scene graph and its OpenGL context being deleted. The
- sceneGraphInvalidated() signal will be emitted when this happens.
+ scene graph and in the case of the OpenGL adaptation the associated
+ context will be deleted. The sceneGraphInvalidated() signal will be
+ emitted when this happens.
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
@@ -1193,10 +1233,8 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
Constructs a window for displaying a QML scene with parent window \a parent.
*/
QQuickWindow::QQuickWindow(QWindow *parent)
- : QWindow(*(new QQuickWindowPrivate), parent)
+ : QQuickWindow(*new QQuickWindowPrivate, parent)
{
- Q_D(QQuickWindow);
- d->init(this);
}
@@ -1306,6 +1344,9 @@ void QQuickWindow::releaseResources()
The OpenGL context is still released when the last QQuickWindow is
deleted.
+ \note This only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\sa setPersistentSceneGraph(),
QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized()
*/
@@ -1323,7 +1364,8 @@ void QQuickWindow::setPersistentOpenGLContext(bool persistent)
lifetime of the QQuickWindow.
\note This is a hint. When and how this happens is implementation
- specific.
+ specific. It also only has an effect when using the default OpenGL
+ scene graph adaptation
*/
bool QQuickWindow::isPersistentOpenGLContext() const
@@ -1438,7 +1480,16 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
{
Q_D(const QQuickWindow);
- return d->mouseGrabberItem;
+ if (d->touchMouseId != -1 && d->touchMouseDevice) {
+ QQuickPointerEvent *event = d->touchMouseDevice->pointerEvent();
+ auto point = event->pointById(d->touchMouseId);
+ Q_ASSERT(point);
+ return point->grabber();
+ }
+
+ QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ Q_ASSERT(event->pointCount());
+ return event->point(0)->grabber();
}
@@ -1451,7 +1502,7 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
- foreach (QQuickItem* item, hoverItems)
+ for (QQuickItem* item : qAsConst(hoverItems))
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
hoverItems.clear();
return accepted;
@@ -1468,8 +1519,7 @@ bool QQuickWindow::event(QEvent *e)
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
QTouchEvent *touch = static_cast<QTouchEvent*>(e);
- d->translateTouchEvent(touch);
- d->deliverTouchEvent(touch);
+ d->handleTouchEvent(touch);
if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// we consume all touch events ourselves to avoid duplicate
// mouse delivery by QtGui mouse synthesis
@@ -1493,7 +1543,7 @@ bool QQuickWindow::event(QEvent *e)
break;
case QEvent::Leave:
d->clearHover();
- d->lastMousePosition = QPoint();
+ d->lastMousePosition = QPointF();
break;
#ifndef QT_NO_DRAGANDDROP
case QEvent::DragEnter:
@@ -1519,8 +1569,8 @@ bool QQuickWindow::event(QEvent *e)
if (d->activeFocusItem)
qGuiApp->inputMethod()->commit();
#endif
- if (d->mouseGrabberItem)
- d->mouseGrabberItem->ungrabMouse();
+ if (mouseGrabberItem())
+ mouseGrabberItem()->ungrabMouse();
break;
case QEvent::UpdateRequest: {
if (d->windowManager)
@@ -1534,7 +1584,7 @@ bool QQuickWindow::event(QEvent *e)
#endif
case QEvent::ShortcutOverride:
if (d->activeFocusItem)
- sendEvent(d->activeFocusItem, static_cast<QKeyEvent *>(e));
+ QCoreApplication::sendEvent(d->activeFocusItem, e);
return true;
default:
break;
@@ -1586,143 +1636,44 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t
return me;
}
-bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
+void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
{
Q_Q(QQuickWindow);
-
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(event->windowPos());
- if (!item->contains(p))
- return false;
- }
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverInitialMousePressEvent(child, event))
- return true;
- }
-
- if (itemPrivate->acceptedMouseButtons() & event->button()) {
- QPointF localPos = item->mapFromScene(event->windowPos());
- if (item->contains(localPos)) {
- QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
- me->accept();
- item->grabMouse();
- q->sendEvent(item, me.data());
- event->setAccepted(me->isAccepted());
- if (me->isAccepted())
- return true;
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
+ auto point = pointerEvent->point(0);
+ lastMousePosition = point->scenePos();
+ QQuickItem *grabber = point->grabber();
+ 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;
}
- }
-
- return false;
-}
-
-bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
-{
- Q_Q(QQuickWindow);
-
- lastMousePosition = event->windowPos();
-
- if (!mouseGrabberItem &&
- event->type() == QEvent::MouseButtonPress &&
- (event->buttons() & event->button()) == event->buttons()) {
- if (deliverInitialMousePressEvent(contentItem, event))
- event->accept();
- else
- event->ignore();
- return event->isAccepted();
- }
- if (mouseGrabberItem) {
- if (event->button() != Qt::NoButton
- && mouseGrabberItem->acceptedMouseButtons()
- && !(mouseGrabberItem->acceptedMouseButtons() & event->button())) {
- event->ignore();
- return false;
- }
- QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
- QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
+ // send update
+ QPointF localPos = grabber->mapFromScene(lastMousePosition);
+ auto me = pointerEvent->asMouseEvent(localPos);
me->accept();
- q->sendEvent(mouseGrabberItem, me.data());
- event->setAccepted(me->isAccepted());
- if (me->isAccepted())
- return true;
- }
-
- return false;
-}
+ q->sendEvent(grabber, me);
+ point->setAccepted(me->isAccepted());
-/*! \reimp */
-void QQuickWindow::mousePressEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
- event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
- d->deliverMouseEvent(event);
-}
-
-/*! \reimp */
-void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
- event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
-
- if (!d->mouseGrabberItem) {
- QWindow::mouseReleaseEvent(event);
- return;
- }
-
- d->deliverMouseEvent(event);
- if (d->mouseGrabberItem && !event->buttons())
- d->mouseGrabberItem->ungrabMouse();
-}
-
-/*! \reimp */
-void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
- event->button(), event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
+ // release event, make sure to ungrab if there still is a grabber
+ if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem())
+ q->mouseGrabberItem()->ungrabMouse();
+ } else {
+ // send initial press
+ bool delivered = false;
+ if (pointerEvent->isPressEvent()) {
+ QSet<QQuickItem*> hasFiltered;
+ delivered = deliverPressEvent(pointerEvent, &hasFiltered);
+ }
- if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
- if (d->deliverInitialMousePressEvent(d->contentItem, event))
- event->accept();
- else
- event->ignore();
- return;
+ if (!delivered)
+ // make sure not to accept unhandled events
+ pointerEvent->setAccepted(false);
}
-
- d->deliverMouseEvent(event);
}
bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
@@ -1730,7 +1681,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
Qt::KeyboardModifiers modifiers, ulong timestamp,
bool accepted)
{
- Q_Q(QQuickWindow);
const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
//create copy of event
@@ -1743,48 +1693,11 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
return true;
}
- q->sendEvent(item, &hoverEvent);
+ QCoreApplication::sendEvent(item, &hoverEvent);
return hoverEvent.isAccepted();
}
-/*! \reimp */
-void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
- event->localPos().x(), event->localPos().y());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
-
-#ifndef QT_NO_CURSOR
- d->updateCursor(event->windowPos());
-#endif
-
- if (!d->mouseGrabberItem) {
- if (d->lastMousePosition.isNull())
- d->lastMousePosition = event->windowPos();
- QPointF last = d->lastMousePosition;
- d->lastMousePosition = event->windowPos();
-
- bool accepted = event->isAccepted();
- bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted);
- if (!delivered) {
- //take care of any exits
- accepted = d->clearHover(event->timestamp());
- }
- event->setAccepted(accepted);
- return;
- }
-
- d->deliverMouseEvent(event);
-}
-
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
{
@@ -1797,19 +1710,22 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
return false;
}
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
- return true;
+ qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
+ if (itemPrivate->subtreeHoverEnabled) {
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
+ continue;
+ if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
+ return true;
+ }
}
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (item->contains(p)) {
- if (!hoverItems.isEmpty() && hoverItems[0] == item) {
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
//move
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
@@ -1820,24 +1736,24 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
itemsToHover << parent;
// Leaving from previous hovered items until we reach the item or one of its ancestors.
- while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
+ while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
}
- if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
// ### Shouldn't we send moves for the parent items as well?
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
// Enter items that are not entered yet.
int startIdx = -1;
if (!hoverItems.isEmpty())
- startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
+ startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
if (startIdx == -1)
startIdx = itemsToHover.count() - 1;
for (int i = startIdx; i >= 0; i--) {
- QQuickItem *itemToHover = itemsToHover[i];
+ QQuickItem *itemToHover = itemsToHover.at(i);
QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
// The item may be about to be deleted or reparented to another window
// due to another hover event delivered in this function. If that is the
@@ -1861,7 +1777,6 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
#ifndef QT_NO_WHEELEVENT
bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
{
- Q_Q(QQuickWindow);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
@@ -1885,7 +1800,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event
QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
wheel.accept();
- q->sendEvent(item, &wheel);
+ QCoreApplication::sendEvent(item, &wheel);
if (wheel.isAccepted()) {
event->accept();
return true;
@@ -1952,20 +1867,22 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
Q_Q(QQuickWindow);
+
// A TouchCancel event will typically not contain any points.
// Deliver it to all items that have active touches.
- QSet<QQuickItem *> cancelDelivered;
- foreach (QQuickItem *item, itemForTouchPointId) {
- if (cancelDelivered.contains(item))
- continue;
- cancelDelivered.insert(item);
- q->sendEvent(item, event);
+ QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent();
+ QVector<QQuickItem *> grabbers = pointerEvent->grabbers();
+
+ for (QQuickItem *grabber: qAsConst(grabbers)) {
+ q->sendEvent(grabber, event);
}
touchMouseId = -1;
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
+ touchMouseDevice = nullptr;
+ if (q->mouseGrabberItem())
+ q->mouseGrabberItem()->ungrabMouse();
+
// The next touch event can only be a TouchBegin so clean up.
- itemForTouchPointId.clear();
+ pointerEvent->clearGrabbers();
return true;
}
@@ -1975,88 +1892,185 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent()
// Set delayedTouch to 0 before delivery to avoid redelivery in case of
// event loop recursions (e.g if it the touch starts a dnd session).
QScopedPointer<QTouchEvent> e(delayedTouch.take());
- reallyDeliverTouchEvent(e.data());
+ deliverPointerEvent(pointerEventInstance(e.data()));
}
static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
-// check what kind of touch we have (begin/update) and
-// call deliverTouchPoints to actually dispatch the points
-void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
+bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
{
- qCDebug(DBG_TOUCH) << event;
Q_Q(QQuickWindow);
+ Qt::TouchPointStates states = event->touchPointStates();
+ if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0)
+ || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) {
+ // we can only compress something that isn't a press or release
+ return false;
+ }
- if (qquickwindow_no_touch_compression || touchRecursionGuard) {
- reallyDeliverTouchEvent(event);
- return;
+ if (!delayedTouch) {
+ delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ delayedTouch->setTimestamp(event->timestamp());
+ if (renderControl)
+ QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
+ else if (windowManager)
+ windowManager->maybeUpdate(q);
+ return true;
}
- Qt::TouchPointStates states = event->touchPointStates();
- if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) != 0)
- && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) {
- // we can only compress something that isn't a press or release
- if (!delayedTouch) {
- delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ // check if this looks like the last touch event
+ if (delayedTouch->type() == event->type() &&
+ delayedTouch->device() == event->device() &&
+ delayedTouch->modifiers() == event->modifiers() &&
+ delayedTouch->touchPoints().count() == event->touchPoints().count())
+ {
+ // possible match.. is it really the same?
+ bool mismatch = false;
+
+ QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
+ Qt::TouchPointStates states;
+ for (int i = 0; i < event->touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &tp = tpts.at(i);
+ const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
+ if (tp.id() != tpDelayed.id()) {
+ mismatch = true;
+ break;
+ }
+
+ if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
+ tpts[i].setState(Qt::TouchPointMoved);
+ tpts[i].setLastPos(tpDelayed.lastPos());
+ tpts[i].setLastScenePos(tpDelayed.lastScenePos());
+ tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
+ tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
+
+ states |= tpts.at(i).state();
+ }
+
+ // matching touch event? then merge the new event into the old one
+ if (!mismatch) {
+ delayedTouch->setTouchPoints(tpts);
delayedTouch->setTimestamp(event->timestamp());
- if (renderControl)
- QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
- else if (windowManager)
- windowManager->maybeUpdate(q);
- return;
- } else {
- // check if this looks like the last touch event
- if (delayedTouch->type() == event->type() &&
- delayedTouch->device() == event->device() &&
- delayedTouch->modifiers() == event->modifiers() &&
- delayedTouch->touchPoints().count() == event->touchPoints().count())
- {
- // possible match.. is it really the same?
- bool mismatch = false;
-
- QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
- Qt::TouchPointStates states;
- for (int i = 0; i < event->touchPoints().count(); ++i) {
- const QTouchEvent::TouchPoint &tp = tpts.at(i);
- const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
- if (tp.id() != tpDelayed.id()) {
- mismatch = true;
- break;
- }
+ return true;
+ }
+ }
- if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
- tpts[i].setState(Qt::TouchPointMoved);
- tpts[i].setLastPos(tpDelayed.lastPos());
- tpts[i].setLastScenePos(tpDelayed.lastScenePos());
- tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
- tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
+ // merging wasn't possible, so deliver the delayed event first, and then delay this one
+ deliverDelayedTouchEvent();
+ delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ delayedTouch->setTimestamp(event->timestamp());
+ return true;
+}
- states |= tpts.at(i).state();
- }
+// entry point for touch event delivery:
+// - translate the event to window coordinates
+// - compress the event instead of delivering it if applicable
+// - call deliverTouchPoints to actually dispatch the points
+void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
+{
+ translateTouchEvent(event);
+ if (event->touchPoints().size())
+ lastMousePosition = event->touchPoints().at(0).pos();
- // matching touch event? then merge the new event into the old one
- if (!mismatch) {
- delayedTouch->setTouchPoints(tpts);
- delayedTouch->setTimestamp(event->timestamp());
- return;
- }
- }
+ qCDebug(DBG_TOUCH) << event;
+
+ if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
+ deliverPointerEvent(pointerEventInstance(event));
+ return;
+ }
- // merging wasn't possible, so deliver the delayed event first, and then delay this one
+ if (!compressTouchEvent(event)) {
+ if (delayedTouch)
deliverDelayedTouchEvent();
- delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
- delayedTouch->setTimestamp(event->timestamp());
+ deliverPointerEvent(pointerEventInstance(event));
+ }
+}
+
+/*! \reimp */
+void QQuickWindow::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+
+void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
+{
+ Q_Q(QQuickWindow);
+
+ if (event->source() == Qt::MouseEventSynthesizedBySystem) {
+ event->accept();
+ return;
+ }
+ qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons();
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
+ event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
+ event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseButtonDblClick:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
+ event->button(), event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseMove:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
+ event->localPos().x(), event->localPos().y());
+
+ qCDebug(DBG_HOVER_TRACE) << this;
+
+ #ifndef QT_NO_CURSOR
+ updateCursor(event->windowPos());
+ #endif
+
+ if (!q->mouseGrabberItem()) {
+ QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
+ lastMousePosition = event->windowPos();
+
+ bool accepted = event->isAccepted();
+ bool delivered = deliverHoverEvent(contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted);
+ if (!delivered) {
+ //take care of any exits
+ accepted = clearHover(event->timestamp());
+ }
+ event->setAccepted(accepted);
return;
}
- } else {
- if (delayedTouch)
- deliverDelayedTouchEvent();
- reallyDeliverTouchEvent(event);
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
}
}
-void QQuickWindowPrivate::flushDelayedTouchEvent()
+void QQuickWindowPrivate::flushFrameSynchronousEvents()
{
+ Q_Q(QQuickWindow);
+
if (delayedTouch) {
deliverDelayedTouchEvent();
@@ -2066,169 +2080,232 @@ void QQuickWindowPrivate::flushDelayedTouchEvent()
if (ut && ut->hasStartAnimationPending())
ut->startAnimations();
}
+
+ // Once per frame, send a synthetic hover, in case items have changed position.
+ // For instance, during animation (including the case of a ListView
+ // whose delegates contain MouseAreas), a MouseArea needs to know
+ // whether it has moved into a position where it is now under the cursor.
+ if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) {
+ bool accepted = false;
+ bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
+ if (!delivered)
+ clearHover(); // take care of any exits
+ }
}
-void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event)
-{
- qCDebug(DBG_TOUCH) << " - delivering" << event;
+/*!
+ \internal
+ Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event.
- // If users spin the eventloop as a result of touch delivery, we disable
- // touch compression and send events directly. This is because we consider
- // the usecase a bit evil, but we at least don't want to lose events.
- ++touchRecursionGuard;
-
- // List of all items that received an event before
- // When we have TouchBegin this is and will stay empty
- QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
-
- // Figure out who accepted a touch point last and put it in updatedPoints
- // Add additional item to newPoints
- const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
- QList<QTouchEvent::TouchPoint> newPoints;
- for (int i=0; i<touchPoints.count(); i++) {
- const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
- if (touchPoint.state() == Qt::TouchPointPressed) {
- newPoints << touchPoint;
- } else {
- // TouchPointStationary is relevant only to items which
- // are also receiving touch points with some other state.
- // But we have not yet decided which points go to which item,
- // so for now we must include all non-new points in updatedPoints.
- if (itemForTouchPointId.contains(touchPoint.id())) {
- QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
- if (item)
- updatedPoints[item].append(touchPoint);
- }
- }
+ There is a unique instance per QQuickPointerDevice, which is determined
+ from \a event's device.
+*/
+QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event)
+{
+ QQuickPointerDevice *dev = nullptr;
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ dev = QQuickPointerDevice::genericMouseDevice();
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device());
+ break;
+ // TODO tablet event types
+ default:
+ break;
}
+ Q_ASSERT(dev);
+ return dev->pointerEvent()->reset(event);
+}
- // Deliver the event, but only if there is at least one new point
- // or some item accepted a point and should receive an update
- if (newPoints.count() > 0 || updatedPoints.count() > 0) {
- QSet<int> acceptedNewPoints;
- QSet<QQuickItem *> hasFiltered;
- event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered));
- } else
- event->ignore();
+void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
+{
+ // If users spin the eventloop as a result of event delivery, we disable
+ // event compression and send events directly. This is because we consider
+ // the usecase a bit evil, but we at least don't want to lose events.
+ ++pointerEventRecursionGuard;
- // Remove released points from itemForTouchPointId
- if (event->touchPointStates() & Qt::TouchPointReleased) {
- for (int i=0; i<touchPoints.count(); i++) {
- if (touchPoints[i].state() == Qt::TouchPointReleased) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << touchPoints[i].id() << "released";
- itemForTouchPointId.remove(touchPoints[i].id());
- if (touchPoints[i].id() == touchMouseId)
- touchMouseId = -1;
- touchMouseIdCandidates.remove(touchPoints[i].id());
- }
- }
+ if (event->asPointerMouseEvent()) {
+ deliverMouseEvent(event->asPointerMouseEvent());
+ } else if (event->asPointerTouchEvent()) {
+ deliverTouchEvent(event->asPointerTouchEvent());
+ } else {
+ Q_ASSERT(false);
}
- if (event->type() == QEvent::TouchEnd) {
- if (!itemForTouchPointId.isEmpty()) {
- qWarning() << "No release received for" << itemForTouchPointId.size()
- << "touch points over" << itemForTouchPointId.begin().value()
- << "on touch end.";
- itemForTouchPointId.clear();
- }
- }
+ event->reset(nullptr);
- --touchRecursionGuard;
+ --pointerEventRecursionGuard;
}
-// This function recurses and sends the events to the individual items
-bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints,
- QSet<int> *acceptedNewPoints, QHash<QQuickItem *,
- QList<QTouchEvent::TouchPoint> > *updatedPoints, QSet<QQuickItem *> *hasFiltered)
+// check if item or any of its child items contain the point
+// FIXME: should this be iterative instead of recursive?
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const
{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
+ QVector<QQuickItem *> targets;
+ auto itemPrivate = QQuickItemPrivate::get(item);
+ QPointF itemPos = item->mapFromScene(scenePos);
+ // if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- for (int i=0; i<newPoints.count(); i++) {
- QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (!item->contains(p))
- return false;
- }
+ if (!item->contains(itemPos))
+ return targets;
}
- // Check if our children want the event (or parts of it)
- // This is the only point where touch event delivery recurses!
+ // recurse for children
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
- if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled)
+ auto childPrivate = QQuickItemPrivate::get(child);
+ if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered))
- return true;
+ targets << pointerTargets(child, scenePos, false);
}
- // None of the children accepted the event, so check the given item itself.
- // First, construct matchingPoints as a list of TouchPoints which the
- // given item might be interested in. Any newly-pressed point which is
- // inside the item's bounds will be interesting, and also any updated point
- // which was already accepted by that item when it was first pressed.
- // (A point which was already accepted is effectively "grabbed" by the item.)
-
- // set of IDs of "interesting" new points
- QSet<int> matchingNewPoints;
- // set of points which this item has previously accepted, for starters
- QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
- // now add the new points which are inside this item's bounds
- if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
- for (int i = 0; i < newPoints.count(); i++) {
- if (acceptedNewPoints->contains(newPoints[i].id()))
- continue;
- QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (item->contains(p)) {
- matchingNewPoints.insert(newPoints[i].id());
- matchingPoints << newPoints[i];
- }
+ if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) {
+ // add this item last - children take precedence
+ targets << item;
+ }
+ return targets;
+}
+
+// return the joined lists
+// list1 has priority, common items come last
+QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
+{
+ QVector<QQuickItem *> targets = list1;
+ // start at the end of list2
+ // if item not in list, append it
+ // if item found, move to next one, inserting before the last found one
+ int insertPosition = targets.length();
+ for (int i = list2.length() - 1; i >= 0; --i) {
+ int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition);
+ if (newInsertPosition >= 0) {
+ Q_ASSERT(newInsertPosition <= insertPosition);
+ insertPosition = newInsertPosition;
}
+ // check for duplicates, only insert if the item isn't there already
+ if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition))
+ targets.insert(insertPosition, list2.at(i));
}
- // If there are no matching new points, and the existing points are all stationary,
- // there's no need to send an event to this item. This is required by a test in
- // tst_qquickwindow::touchEvent_basic:
- // a single stationary press on an item shouldn't cause an event
- if (matchingNewPoints.isEmpty()) {
- bool stationaryOnly = true;
-
- foreach (const QTouchEvent::TouchPoint &tp, matchingPoints) {
- if (tp.state() != Qt::TouchPointStationary) {
- stationaryOnly = false;
- break;
+ return targets;
+}
+
+void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
+{
+ qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
+
+ QSet<QQuickItem *> hasFiltered;
+ if (event->isPressEvent())
+ deliverPressEvent(event, &hasFiltered);
+ if (!event->allPointsAccepted())
+ deliverUpdatedTouchPoints(event, &hasFiltered);
+
+ // Remove released points from itemForTouchPointId
+ bool allReleased = true;
+ int pointCount = event->pointCount();
+ for (int i = 0; i < pointCount; ++i) {
+ QQuickEventPoint *point = event->point(i);
+ if (point->state() == QQuickEventPoint::Released) {
+ int id = point->pointId();
+ qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released";
+ point->setGrabber(nullptr);
+ if (id == touchMouseId) {
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
}
+ } else {
+ allReleased = false;
}
+ }
- if (stationaryOnly)
- matchingPoints.clear();
+ if (allReleased && !event->grabbers().isEmpty()) {
+ qWarning() << "No release received for some grabbers" << event->grabbers();
+ event->clearGrabbers();
}
+}
- if (!matchingPoints.isEmpty()) {
- // Now we know this item might be interested in the event. Copy and send it, but
- // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
- deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered);
+// Deliver touch points to existing grabbers
+bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+{
+ const auto grabbers = event->grabbers();
+ for (auto grabber : grabbers)
+ deliverMatchingPointsToItem(grabber, event, hasFiltered);
+
+ return false;
+}
+
+// Deliver newly pressed touch points
+bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered)
+{
+ const QVector<QPointF> points = event->unacceptedPressedPointScenePositions();
+ QVector<QQuickItem *> targetItems;
+ for (QPointF point: points) {
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
}
- // record the fact that this item has been visited already
- updatedPoints->remove(item);
+ for (QQuickItem *item: targetItems) {
+ deliverMatchingPointsToItem(item, event, hasFiltered);
+ if (event->allPointsAccepted())
+ break;
+ }
- // recursion is done only if ALL touch points have been delivered
- return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
+ return event->allPointsAccepted();
}
-// touchEventForItemBounds has no means to generate a touch event that contains
-// only the points that are relevant for this item. Thus the need for
-// matchingPoints to already be that set of interesting points.
-// They are all pre-transformed, too.
-bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem *> *hasFiltered)
+bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered)
{
- QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
- touchEvent.data()->setTarget(item);
- bool touchEventAccepted = false;
+ Q_Q(QQuickWindow);
+
+ // TODO: unite this mouse point delivery with the synthetic mouse event below
+ if (auto event = pointerEvent->asPointerMouseEvent()) {
+ if (item->acceptedMouseButtons() & event->button()) {
+ auto point = event->point(0);
+ if (point->isAccepted())
+ return false;
+
+ // The only reason to already have a mouse grabber here is
+ // synthetic events - flickable sends one when setPressDelay is used.
+ auto oldMouseGrabber = q->mouseGrabberItem();
+ QPointF localPos = item->mapFromScene(point->scenePos());
+ Q_ASSERT(item->contains(localPos)); // transform is checked already
+ QMouseEvent *me = event->asMouseEvent(localPos);
+ me->accept();
+ q->sendEvent(item, me);
+ if (me->isAccepted()) {
+ auto mouseGrabber = q->mouseGrabberItem();
+ if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
+ item->mouseUngrabEvent();
+ } else {
+ item->grabMouse();
+ }
+ point->setAccepted(true);
+ }
+ return me->isAccepted();
+ }
+ return false;
+ }
+
+ QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent();
+ if (!event)
+ return false;
+
+ QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item));
+ if (!touchEvent)
+ return false;
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.
@@ -2236,109 +2313,53 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv
// 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;
- foreach (int id, matchingNewPoints)
- acceptedNewPoints->insert(id);
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ event->pointById(point.id())->setAccepted();
+ }
return true;
}
- // Since it can change in sendEvent, update itemForTouchPointId now
- foreach (int id, matchingNewPoints) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item;
- itemForTouchPointId[id] = item;
- }
-
// Deliver the touch event to the given item
qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
QCoreApplication::sendEvent(item, touchEvent.data());
- touchEventAccepted = touchEvent->isAccepted();
+ 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 (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
// send mouse event
- event->setAccepted(translateTouchToMouse(item, touchEvent.data()));
- if (event->isAccepted()) {
- touchEventAccepted = true;
- }
+ if (deliverTouchAsMouse(item, event))
+ eventAccepted = true;
}
- if (touchEventAccepted) {
+ if (eventAccepted) {
// If the touch was accepted (regardless by whom or in what form),
- // update acceptedNewPoints.
- foreach (int id, matchingNewPoints)
- acceptedNewPoints->insert(id);
+ // update accepted new points.
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ auto pointerEventPoint = event->pointById(point.id());
+ pointerEventPoint->setAccepted();
+ if (point.state() == Qt::TouchPointPressed)
+ pointerEventPoint->setGrabber(item);
+ }
} else {
// But if the event was not accepted then we know this item
// will not be interested in further updates for those touchpoint IDs either.
- foreach (int id, matchingNewPoints)
- if (itemForTouchPointId[id] == item) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated";
- itemForTouchPointId.remove(id);
- }
- }
-
- return touchEventAccepted;
-}
-
-QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
-{
- const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
- QList<QTouchEvent::TouchPoint> pointsInBounds;
- // if all points are stationary, the list of points should be empty to signal a no-op
- if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
- for (int i = 0; i < touchPoints.count(); ++i) {
- const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
- if (tp.state() == Qt::TouchPointPressed) {
- QPointF p = target->mapFromScene(tp.scenePos());
- if (target->contains(p))
- pointsInBounds.append(tp);
- } else {
- pointsInBounds.append(tp);
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ 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);
+ }
}
}
- transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
}
- QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
- touchEvent->setTarget(target);
- return touchEvent;
-}
-
-QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
-{
- Qt::TouchPointStates eventStates;
- for (int i=0; i<newPoints.count(); i++)
- eventStates |= newPoints[i].state();
- // if all points have the same state, set the event type accordingly
- QEvent::Type eventType = event.type();
- switch (eventStates) {
- case Qt::TouchPointPressed:
- eventType = QEvent::TouchBegin;
- break;
- case Qt::TouchPointReleased:
- eventType = QEvent::TouchEnd;
- break;
- default:
- eventType = QEvent::TouchUpdate;
- break;
- }
-
- QTouchEvent *touchEvent = new QTouchEvent(eventType);
- touchEvent->setWindow(event.window());
- touchEvent->setTarget(event.target());
- touchEvent->setDevice(event.device());
- touchEvent->setModifiers(event.modifiers());
- touchEvent->setTouchPoints(newPoints);
- touchEvent->setTouchPointStates(eventStates);
- touchEvent->setTimestamp(event.timestamp());
- touchEvent->accept();
- return touchEvent;
+ return eventAccepted;
}
#ifndef QT_NO_DRAGANDDROP
void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
{
- Q_Q(QQuickWindow);
grabber->resetTarget();
QQuickDragGrabber::iterator grabItem = grabber->begin();
if (grabItem != grabber->end()) {
@@ -2354,7 +2375,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
e->mouseButtons(),
e->keyboardModifiers());
QQuickDropEventEx::copyActions(&translatedEvent, *e);
- q->sendEvent(**grabItem, &translatedEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
e->setAccepted(translatedEvent.isAccepted());
e->setDropAction(translatedEvent.dropAction());
grabber->setTarget(**grabItem);
@@ -2363,7 +2384,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
QDragLeaveEvent leaveEvent;
for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
return;
} else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
@@ -2379,18 +2400,18 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
moveEvent->mouseButtons(),
moveEvent->keyboardModifiers());
QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
- q->sendEvent(**grabItem, &translatedEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
++grabItem;
} else {
QDragLeaveEvent leaveEvent;
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
grabItem = grabber->release(grabItem);
}
}
return;
} else {
QDragLeaveEvent leaveEvent;
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
}
}
}
@@ -2409,7 +2430,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
{
- Q_Q(QQuickWindow);
bool accepted = false;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
@@ -2444,7 +2464,7 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
event->keyboardModifiers(),
event->type());
QQuickDropEventEx::copyActions(&translatedEvent, *event);
- q->sendEvent(item, &translatedEvent);
+ QCoreApplication::sendEvent(item, &translatedEvent);
if (event->type() == QEvent::DragEnter) {
if (translatedEvent.isAccepted()) {
grabber->grab(item);
@@ -2487,7 +2507,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
return 0;
}
- if (itemPrivate->hasCursorInChild) {
+ if (itemPrivate->subtreeCursorEnabled) {
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
@@ -2507,8 +2527,10 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
}
#endif
-bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
{
+ Q_Q(QQuickWindow);
+
if (!target)
return false;
@@ -2517,8 +2539,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) {
hasFiltered->insert(target);
- QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
- if (!targetEvent->touchPoints().isEmpty()) {
+ QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true));
+ if (targetEvent) {
if (target->childMouseEventFilter(item, targetEvent.data())) {
qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target;
QVector<int> touchIds;
@@ -2527,9 +2549,10 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
for (int i = 0; i < touchPointCount; ++i)
touchIds.append(targetEvent->touchPoints().at(i).id());
target->grabTouchPoints(touchIds);
- if (mouseGrabberItem) {
- mouseGrabberItem->ungrabMouse();
+ if (q->mouseGrabberItem()) {
+ q->mouseGrabberItem()->ungrabMouse();
touchMouseId = -1;
+ touchMouseDevice = nullptr;
}
filtered = true;
}
@@ -2541,12 +2564,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
switch (tp.state()) {
case Qt::TouchPointPressed:
t = QEvent::MouseButtonPress;
- if (touchMouseId == -1) {
- // We don't want to later filter touches as a mouse event if they were pressed
- // while a touchMouseId was already active.
- // Remember this touch as a potential to become the touchMouseId.
- touchMouseIdCandidates.insert(tp.id());
- }
break;
case Qt::TouchPointReleased:
t = QEvent::MouseButtonRelease;
@@ -2559,18 +2576,20 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
}
// Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
- if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) {
+ if (touchMouseId == -1 || touchMouseId == tp.id()) {
// targetEvent is already transformed wrt local position, velocity, etc.
- QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event, item, false));
+
+ // FIXME: remove asTouchEvent!!!
+ QScopedPointer<QMouseEvent> 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;
- itemForTouchPointId[tp.id()] = target;
touchMouseId = tp.id();
+ touchMouseDevice = event->device();
+ touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target);
target->grabMouse();
}
- touchMouseIdCandidates.clear();
filtered = true;
}
// Only one event can be filtered as a mouse event.
@@ -2597,6 +2616,7 @@ bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem
hasFiltered->insert(target);
if (target->childMouseEventFilter(item, event))
filtered = true;
+ qCDebug(DBG_MOUSE_TARGET) << target << "childMouseEventFilter ->" << filtered;
}
return sendFilteredMouseEvent(target->parentItem(), item, event, hasFiltered) || filtered;
@@ -2617,6 +2637,15 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold)
+{
+ QStyleHints *styleHints = qApp->styleHints();
+ bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
+ qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y();
+ overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
+ return overThreshold;
+}
+
/*!
\qmlproperty list<Object> Window::data
\default
@@ -2714,8 +2743,13 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo
/*!
Propagates an event \a e to a QQuickItem \a item on the window.
+ Use \l QCoreApplication::sendEvent() directly instead.
+
The return value is currently not used.
+
+ \deprecated
*/
+// ### Qt6: remove
bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
{
Q_D(QQuickWindow);
@@ -2737,9 +2771,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
QCoreApplication::sendEvent(item, e);
}
break;
- case QEvent::ShortcutOverride:
- QCoreApplication::sendEvent(item, e);
- break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
@@ -2753,41 +2784,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
}
}
break;
- case QEvent::UngrabMouse: {
- QSet<QQuickItem *> hasFiltered;
- if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) {
- e->accept();
- item->mouseUngrabEvent();
- }
- }
- break;
-#ifndef QT_NO_WHEELEVENT
- case QEvent::Wheel:
-#endif
-#ifndef QT_NO_DRAGANDDROP
- case QEvent::DragEnter:
- case QEvent::DragMove:
- case QEvent::DragLeave:
- case QEvent::Drop:
-#endif
- case QEvent::FocusIn:
- case QEvent::FocusOut:
- case QEvent::HoverEnter:
- case QEvent::HoverLeave:
- case QEvent::HoverMove:
- case QEvent::TouchCancel:
- QCoreApplication::sendEvent(item, e);
- break;
- case QEvent::TouchBegin:
- case QEvent::TouchUpdate:
- case QEvent::TouchEnd: {
- QSet<QQuickItem*> hasFiltered;
- QTouchEvent *ev = static_cast<QTouchEvent *>(e);
- qCDebug(DBG_TOUCH) << " - sendEvent for " << ev << " to " << item->parentItem() << " and " << item;
- d->sendFilteredTouchEvent(item->parentItem(), item, ev, &hasFiltered);
- }
- break;
default:
+ QCoreApplication::sendEvent(item, e);
break;
}
@@ -3148,7 +3146,7 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
<< itemPriv->paintNode;
nodes.removeAll(0);
- Q_ASSERT(nodes.first() == itemPriv->itemNodeInstance);
+ Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance);
for (int i=1; i<nodes.size(); ++i) {
QSGNode *n = nodes.at(i);
// Failing this means we messed up reparenting
@@ -3183,10 +3181,10 @@ void QQuickWindow::maybeUpdate()
void QQuickWindow::cleanupSceneGraph()
{
Q_D(QQuickWindow);
-
+#ifndef QT_NO_OPENGL
delete d->vaoHelper;
d->vaoHelper = 0;
-
+#endif
if (!d->renderer)
return;
@@ -3209,17 +3207,30 @@ void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
}
/*!
- Returns the opengl context used for rendering.
+ Returns the OpenGL context used for rendering.
- If the scene graph is not ready, this function will return 0.
+ If the scene graph is not ready, or the scene graph is not using OpenGL,
+ this function will return null.
+
+ \note If using a scene graph adaptation other than OpenGL this
+ function will return nullptr.
\sa sceneGraphInitialized(), sceneGraphInvalidated()
*/
QOpenGLContext *QQuickWindow::openglContext() const
{
+#ifndef QT_NO_OPENGL
Q_D(const QQuickWindow);
- return d->context ? d->context->openglContext() : 0;
+ if (d->context && d->context->isValid()) {
+ QSGRendererInterface *rif = d->context->sceneGraphContext()->rendererInterface(d->context);
+ if (rif && rif->graphicsApi() == QSGRendererInterface::OpenGL) {
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(d->context);
+ return openglRenderContext->openglContext();
+ }
+ }
+#endif
+ return nullptr;
}
/*!
@@ -3234,7 +3245,9 @@ bool QQuickWindow::isSceneGraphInitialized() const
/*!
\fn void QQuickWindow::frameSwapped()
- This signal is emitted when the frame buffers have been swapped.
+ This signal is emitted when a frame has been queued for presenting. With
+ vertical synchronization enabled the signal is emitted at most once per
+ vsync interval in a continuously animating scene.
This signal will be emitted from the scene graph rendering thread.
*/
@@ -3255,14 +3268,14 @@ bool QQuickWindow::isSceneGraphInitialized() const
This signal is emitted when the scene graph has been invalidated.
- This signal implies that the opengl rendering context used
+ This signal implies that the graphics rendering context used
has been invalidated and all user resources tied to that context
should be released.
- The OpenGL context of this window will be bound when this function
- is called. The only exception is if the native OpenGL has been
- destroyed outside Qt's control, for instance through
- EGL_CONTEXT_LOST.
+ In the case of the default OpenGL adaptation the context of this
+ window will be bound when this function is called. The only exception
+ is if the native OpenGL has been destroyed outside Qt's control,
+ for instance through EGL_CONTEXT_LOST.
This signal will be emitted from the scene graph rendering thread.
*/
@@ -3273,7 +3286,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
This signal is emitted when an \a error occurred during scene graph initialization.
Applications should connect to this signal if they wish to handle errors,
- like OpenGL context creation failures, in a custom way. When no slot is
+ like graphics context creation failures, in a custom way. When no slot is
connected to the signal, the behavior will be different: Quick will print
the \a message, or show a message box, and terminate the application.
@@ -3335,13 +3348,17 @@ bool QQuickWindow::isSceneGraphInitialized() const
The corresponding handler is \c onClosing.
*/
-
+#ifndef QT_NO_OPENGL
/*!
Sets the render target for this window to be \a fbo.
The specified fbo must be created in the context of the window
or one that shares with it.
+ \note
+ This function only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\warning
This function can only be called from the thread doing
the rendering.
@@ -3364,7 +3381,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
d->renderTargetSize = QSize();
}
}
-
+#endif
/*!
\overload
@@ -3374,6 +3391,10 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
The specified FBO must be created in the context of the window
or one that shares with it.
+ \note
+ This function only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\warning
This function can only be called from the thread doing
the rendering.
@@ -3415,19 +3436,23 @@ QSize QQuickWindow::renderTargetSize() const
-
+#ifndef QT_NO_OPENGL
/*!
Returns the render target for this window.
The default is to render to the surface of the window, in which
case the render target is 0.
+
+ \note
+ This function will return nullptr when not using the OpenGL scene
+ graph adaptation.
*/
QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
{
Q_D(const QQuickWindow);
return d->renderTarget;
}
-
+#endif
/*!
Grabs the contents of the window and returns it as an image.
@@ -3444,33 +3469,42 @@ QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
QImage QQuickWindow::grabWindow()
{
Q_D(QQuickWindow);
- if (!isVisible() && !d->context->openglContext()) {
- if (!handle() || !size().isValid()) {
- qWarning("QQuickWindow::grabWindow: window must be created and have a valid size");
- return QImage();
- }
+ if (!isVisible() && !d->renderControl) {
+ if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
+ return d->windowManager->grab(this);
+ }
- QOpenGLContext context;
- context.setFormat(requestedFormat());
- context.setShareContext(qt_gl_global_share_context());
- context.create();
- context.makeCurrent(this);
- d->context->initialize(&context);
+#ifndef QT_NO_OPENGL
+ if (!isVisible() && !d->renderControl) {
+ auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context);
+ if (!openglRenderContext->openglContext()) {
+ if (!handle() || !size().isValid()) {
+ qWarning("QQuickWindow::grabWindow: window must be created and have a valid size");
+ return QImage();
+ }
- d->polishItems();
- d->syncSceneGraph();
- d->renderSceneGraph(size());
+ QOpenGLContext context;
+ context.setFormat(requestedFormat());
+ context.setShareContext(qt_gl_global_share_context());
+ context.create();
+ context.makeCurrent(this);
+ d->context->initialize(&context);
- bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255;
- QImage image = qt_gl_read_framebuffer(size() * effectiveDevicePixelRatio(), alpha, alpha);
- d->cleanupNodesOnShutdown();
- d->context->invalidate();
- context.doneCurrent();
+ d->polishItems();
+ d->syncSceneGraph();
+ d->renderSceneGraph(size());
- return image;
- }
+ bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255;
+ QImage image = qt_gl_read_framebuffer(size() * effectiveDevicePixelRatio(), alpha, alpha);
+ d->cleanupNodesOnShutdown();
+ d->context->invalidate();
+ context.doneCurrent();
+ return image;
+ }
+ }
+#endif
if (d->renderControl)
return d->renderControl->grab();
else if (d->windowManager)
@@ -3512,7 +3546,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
mipmapping enabled.
\value TextureOwnsGLTexture The texture object owns the texture id and
- will delete the GL texture when the texture object is deleted.
+ will delete the OpenGL texture when the texture object is deleted.
\value TextureCanUseAtlas The image can be uploaded into a texture atlas.
@@ -3527,7 +3561,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This enum describes the error in a sceneGraphError() signal.
- \value ContextNotAvailable OpenGL context creation failed. This typically means that
+ \value ContextNotAvailable graphics context creation failed. This typically means that
no suitable OpenGL implementation was found, for example because no graphics drivers
are installed and so no OpenGL 2 support is present. On mobile and embedded boards
that use OpenGL ES such an error is likely to indicate issues in the windowing system
@@ -3544,7 +3578,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal can be used to do any preparation required before calls to
QQuickItem::updatePaintNode().
- The GL context used for rendering the scene graph will be bound at this point.
+ The OpenGL context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
@@ -3565,15 +3599,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal can be used to do preparation required after calls to
QQuickItem::updatePaintNode(), while the GUI thread is still locked.
- The GL context used for rendering the scene graph will be bound at this point.
+ The graphics context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for afterSynchronizing leaves the GL
- context in the same state as it was when the signal handler was entered. Failing to
- do so can result in the scene not rendering properly.
+ \warning When using the OpenGL adaptation, make sure that a signal handler for
+ afterSynchronizing leaves the OpenGL context in the same state as it was when the
+ signal handler was entered. Failing to do so can result in the scene not rendering
+ properly.
\since 5.3
\sa resetOpenGLState()
@@ -3585,16 +3620,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal is emitted before the scene starts rendering.
Combined with the modes for clearing the background, this option
- can be used to paint using raw GL under QML content.
+ can be used to paint using raw OpenGL under QML content.
- The GL context used for rendering the scene graph will be bound
+ The OpenGL context used for rendering the scene graph will be bound
at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for beforeRendering leaves the GL
+ \warning Make very sure that a signal handler for beforeRendering leaves the OpenGL
context in the same state as it was when the signal handler was entered. Failing to
do so can result in the scene not rendering properly.
@@ -3606,16 +3641,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal is emitted after the scene has completed rendering, before swapbuffers is called.
- This signal can be used to paint using raw GL on top of QML content,
+ This signal can be used to paint using raw OpenGL on top of QML content,
or to do screen scraping of the current frame buffer.
- The GL context used for rendering the scene graph will be bound at this point.
+ The OpenGL context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for afterRendering() leaves the GL
+ \warning Make very sure that a signal handler for afterRendering() leaves the OpenGL
context in the same state as it was when the signal handler was entered. Failing to
do so can result in the scene not rendering properly.
@@ -3650,6 +3685,10 @@ QQmlIncubationController *QQuickWindow::incubationController() const
until after the QQuickWindow::sceneGraphInitialize() has been
emitted.
+ \note
+ This signal will only be emmited when using the default OpenGL scene
+ graph adaptation.
+
\since 5.3
*/
@@ -3662,15 +3701,15 @@ QQmlIncubationController *QQuickWindow::incubationController() const
Applications may use this signal to release resources, but should be
prepared to reinstantiated them again fast. The scene graph and the
- OpenGL context are not released at this time.
+ graphics context are not released at this time.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the GL
- context in the same state as it was when the signal handler was entered. Failing to
- do so can result in the scene not rendering properly.
+ \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the
+ graphics context in the same state as it was when the signal handler was entered.
+ Failing to do so can result in the scene not rendering properly.
\sa sceneGraphInvalidated(), resetOpenGLState()
\since 5.3
@@ -3681,7 +3720,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
Sets whether the scene graph rendering of QML should clear the color buffer
before it starts rendering to \a enabled.
- By disabling clearing of the color buffer, it is possible to do GL painting
+ By disabling clearing of the color buffer, it is possible to render OpengGL content
under the scene graph.
The color buffer is cleared by default.
@@ -3722,7 +3761,8 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
alpha channel, the corresponding texture will have an alpha channel.
The caller of the function is responsible for deleting the returned texture.
- The actual GL texture will be deleted when the texture object is deleted.
+ For example whe using the OpenGL adaptation the actual OpenGL texture will
+ be deleted when the texture object is deleted.
When \a options contains TextureCanUseAtlas, the engine may put the image
into a texture atlas. Textures in an atlas need to rely on
@@ -3739,9 +3779,9 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
texture which can use mipmap filtering. Mipmapped textures can not be in
an atlas.
- The returned texture will be using \c GL_TEXTURE_2D as texture target and
- \c GL_RGBA as internal format. Reimplement QSGTexture to create textures
- with different parameters.
+ When using the OpenGL adaptation, the returned texture will be using
+ \c GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format.
+ Reimplement QSGTexture to create textures with different parameters.
\warning This function will return 0 if the scene graph has not yet been
initialized.
@@ -3760,7 +3800,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
{
Q_D(const QQuickWindow);
- if (!d->context)
+ if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid()
return 0;
uint flags = 0;
if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas;
@@ -3772,7 +3812,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
/*!
- Creates a new QSGTexture object from an existing GL texture \a id and \a size.
+ Creates a new QSGTexture object from an existing OpenGL texture \a id and \a size.
The caller of the function is responsible for deleting the returned texture.
@@ -3783,15 +3823,18 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
Use \a options to customize the texture attributes. The TextureUsesAtlas
option is ignored.
- \warning This function will return 0 if the scenegraph has not yet been
- initialized.
+ \warning This function will return null if the scenegraph has not yet been
+ initialized or OpenGL is not in use.
+
+ \note This function only has an effect when using the default OpenGL scene graph
+ adpation.
\sa sceneGraphInitialized(), QSGTexture
*/
QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
{
- Q_D(const QQuickWindow);
- if (d->context && d->context->openglContext()) {
+#ifndef QT_NO_OPENGL
+ if (openglContext()) {
QSGPlainTexture *texture = new QSGPlainTexture();
texture->setTextureId(id);
texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
@@ -3799,6 +3842,11 @@ QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, Create
texture->setTextureSize(size);
return texture;
}
+#else
+ Q_UNUSED(id)
+ Q_UNUSED(size)
+ Q_UNUSED(options)
+#endif
return 0;
}
@@ -3868,7 +3916,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
{
QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
}
-
+#ifndef QT_NO_OPENGL
/*!
\since 5.2
@@ -3886,6 +3934,9 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
buffer. The depth and stencil buffer might be clobbered by the scene
graph renderer. Clear these manually on demand.
+ \note This function only has an effect when using the default OpenGL scene graph
+ adpation.
+
\sa QQuickWindow::beforeRendering()
*/
void QQuickWindow::resetOpenGLState()
@@ -3940,7 +3991,7 @@ void QQuickWindow::resetOpenGLState()
QOpenGLFramebufferObject::bindDefault();
}
-
+#endif
/*!
\qmlproperty string Window::title
@@ -4349,7 +4400,7 @@ void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
jobs->clear();
renderJobMutex.unlock();
- foreach (QRunnable *r, jobList) {
+ for (QRunnable *r : qAsConst(jobList)) {
r->run();
delete r;
}
@@ -4379,6 +4430,130 @@ qreal QQuickWindow::effectiveDevicePixelRatio() const
return w ? w->devicePixelRatio() : devicePixelRatio();
}
+/*!
+ \return the current renderer interface. The value is always valid and is never null.
+
+ \note This function can be called at any time after constructing the
+ QQuickWindow, even while isSceneGraphInitialized() is still false. However,
+ some renderer interface functions, in particular
+ QSGRendererInterface::getResource() will not be functional until the
+ scenegraph is up and running. Backend queries, like
+ QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(),
+ will always be functional on the other hand.
+
+ \note The ownership of the returned pointer stays with Qt. The returned
+ instance may or may not be shared between different QQuickWindow instances,
+ depending on the scenegraph backend in use. Therefore applications are
+ expected to query the interface object for each QQuickWindow instead of
+ reusing the already queried pointer.
+
+ \sa QSGRenderNode, QSGRendererInterface
+
+ \since 5.8
+ */
+QSGRendererInterface *QQuickWindow::rendererInterface() const
+{
+ Q_D(const QQuickWindow);
+
+ // no context validity check - it is essential to be able to return a
+ // renderer interface instance before scenegraphInitialized() is emitted
+ // (depending on the backend, that can happen way too late for some of the
+ // rif use cases, like examining the graphics api or shading language in
+ // use)
+
+ return d->context->sceneGraphContext()->rendererInterface(d->context);
+}
+
+/*!
+ Requests a Qt Quick scenegraph backend for the specified graphics \a api.
+ Backends can either be built-in or be installed in form of dynamically
+ loaded plugins.
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. It cannot be changed afterwards.
+
+ If \a backend is invalid or an error occurs, the default backend (OpenGL or
+ software, depending on the Qt configuration) is used.
+
+ \since 5.8
+ */
+void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
+{
+ switch (api) {
+ case QSGRendererInterface::Software:
+ setSceneGraphBackend(QStringLiteral("software"));
+ break;
+ case QSGRendererInterface::Direct3D12:
+ setSceneGraphBackend(QStringLiteral("d3d12"));
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ Requests the specified Qt Quick scenegraph \a backend. Backends can either
+ be built-in or be installed in form of dynamically loaded plugins.
+
+ \overload
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. It cannot be changed afterwards.
+
+ If \a backend is invalid or an error occurs, the default backend (OpenGL or
+ software, depending on the Qt configuration) is used.
+
+ \note Calling this function is equivalent to setting the
+ \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this
+ API is safer to use in applications that spawn other processes as there is
+ no need to worry about environment inheritance.
+
+ \since 5.8
+ */
+void QQuickWindow::setSceneGraphBackend(const QString &backend)
+{
+ QSGContext::setBackend(backend);
+}
+
+/*!
+ Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
+
+ \since 5.8
+ \sa QSGRectangleNode
+ */
+QSGRectangleNode *QQuickWindow::createRectangleNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr;
+}
+
+/*!
+ Creates a simple image node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleTextureNode directly.
+
+ \since 5.8
+ \sa QSGImageNode
+ */
+QSGImageNode *QQuickWindow::createImageNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr;
+}
+
+/*!
+ Creates a nine patch node. When the scenegraph is not initialized, the return value is null.
+
+ \since 5.8
+ */
+QSGNinePatchNode *QQuickWindow::createNinePatchNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr;
+}
+
#include "moc_qquickwindow.cpp"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 1024147bb4..cfadadec2d 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -41,6 +41,7 @@
#define QQUICKWINDOW_H
#include <QtQuick/qtquickglobal.h>
+#include <QtQuick/qsgrendererinterface.h>
#include <QtCore/qmetatype.h>
#include <QtGui/qopengl.h>
#include <QtGui/qwindow.h>
@@ -60,6 +61,9 @@ class QQmlIncubationController;
class QInputMethodEvent;
class QQuickCloseEvent;
class QQuickRenderControl;
+class QSGRectangleNode;
+class QSGImageNode;
+class QSGNinePatchNode;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -110,16 +114,16 @@ public:
bool sendEvent(QQuickItem *, QEvent *);
QImage grabWindow();
-
+#ifndef QT_NO_OPENGL
void setRenderTarget(QOpenGLFramebufferObject *fbo);
QOpenGLFramebufferObject *renderTarget() const;
-
+#endif
void setRenderTarget(uint fboId, const QSize &size);
uint renderTargetId() const;
QSize renderTargetSize() const;
-
+#ifndef QT_NO_OPENGL
void resetOpenGLState();
-
+#endif
QQmlIncubationController *incubationController() const;
#ifndef QT_NO_ACCESSIBILITY
@@ -153,6 +157,15 @@ public:
qreal effectiveDevicePixelRatio() const;
+ QSGRendererInterface *rendererInterface() const;
+
+ static void setSceneGraphBackend(QSGRendererInterface::GraphicsApi api);
+ static void setSceneGraphBackend(const QString &backend);
+
+ QSGRectangleNode *createRectangleNode() const;
+ QSGImageNode *createImageNode() const;
+ QSGNinePatchNode *createNinePatchNode() const;
+
Q_SIGNALS:
void frameSwapped();
Q_REVISION(2) void openglContextCreated(QOpenGLContext *context);
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 7d2b1af29b..3328eb65c4 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
#include "qquickwindow.h"
+#include "qquickevents_p_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -68,13 +69,19 @@
QT_BEGIN_NAMESPACE
-//Make it easy to identify and customize the root item if needed
-
+class QOpenGLVertexArrayObjectHelper;
class QQuickAnimatorController;
-class QSGRenderLoop;
-class QQuickRenderControl;
class QQuickDragGrabber;
+class QQuickItemPrivate;
+class QQuickPointerDevice;
+class QQuickRenderControl;
+class QQuickWindowIncubationController;
+class QQuickWindowPrivate;
+class QQuickWindowRenderLoop;
+class QSGRenderLoop;
+class QTouchEvent;
+//Make it easy to identify and customize the root item if needed
class QQuickRootItem : public QQuickItem
{
Q_OBJECT
@@ -85,15 +92,6 @@ public Q_SLOTS:
void setHeight(int h) {QQuickItem::setHeight(qreal(h));}
};
-class QQuickItemPrivate;
-class QQuickWindowPrivate;
-
-class QTouchEvent;
-class QQuickWindowRenderLoop;
-class QQuickWindowIncubationController;
-
-class QOpenGLVertexArrayObjectHelper;
-
class Q_QUICK_PRIVATE_EXPORT QQuickCustomRenderStage
{
public:
@@ -127,7 +125,6 @@ public:
void deliverKeyEvent(QKeyEvent *e);
// Keeps track of the item currently receiving mouse events
- QQuickItem *mouseGrabberItem;
#ifndef QT_NO_CURSOR
QQuickItem *cursorItem;
#endif
@@ -135,18 +132,19 @@ public:
QQuickDragGrabber *dragGrabber;
#endif
int touchMouseId;
+ QQuickPointerDevice *touchMouseDevice;
bool checkIfDoubleClicked(ulong newPressEventTimestamp);
ulong touchMousePressTimestamp;
// Mouse positions are saved in widget coordinates
QPointF lastMousePosition;
- bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event);
+ bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent);
void translateTouchEvent(QTouchEvent *touchEvent);
void setMouseGrabber(QQuickItem *grabber);
- static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
+ void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids);
+ void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true);
static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0);
- bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *);
- bool deliverMouseEvent(QMouseEvent *);
+ void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent);
bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *);
#ifndef QT_NO_WHEELEVENT
bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
@@ -154,21 +152,33 @@ public:
#ifndef QT_NO_GESTURES
bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *);
#endif
- bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
- QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered);
- void deliverTouchEvent(QTouchEvent *);
- void reallyDeliverTouchEvent(QTouchEvent *);
- bool deliverTouchCancelEvent(QTouchEvent *);
+
+ // entry point of events to the window
+ void handleTouchEvent(QTouchEvent *);
+ void handleMouseEvent(QMouseEvent *);
+ bool compressTouchEvent(QTouchEvent *);
+ void flushFrameSynchronousEvents();
void deliverDelayedTouchEvent();
- void flushDelayedTouchEvent();
+
+ // delivery of pointer events:
+ QQuickPointerEvent *pointerEventInstance(QEvent *ev);
+ void deliverPointerEvent(QQuickPointerEvent *);
+ void deliverTouchEvent(QQuickPointerTouchEvent *);
+ bool deliverTouchCancelEvent(QTouchEvent *);
+ bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *);
+ bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered);
+ bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered);
+ bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered);
+
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const;
+ QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
+
+ // hover delivery
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
- bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered);
- QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent);
- QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints);
- bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
bool clearHover(ulong timestamp = 0);
+
#ifndef QT_NO_DRAGANDDROP
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *);
@@ -233,7 +243,8 @@ public:
QQuickRenderControl *renderControl;
QQuickAnimatorController *animationController;
QScopedPointer<QTouchEvent> delayedTouch;
- int touchRecursionGuard;
+
+ int pointerEventRecursionGuard;
QQuickCustomRenderStage *customRenderStage;
QColor clearColor;
@@ -254,15 +265,12 @@ public:
QOpenGLVertexArrayObjectHelper *vaoHelper;
- // Keeps track of which touch point (int) was last accepted by which item
- QHash<int, QQuickItem *> itemForTouchPointId;
- QSet<int> touchMouseIdCandidates;
-
mutable QQuickWindowIncubationController *incubationController;
static bool defaultAlphaBuffer;
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);
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -288,22 +296,6 @@ private:
static void cleanupNodesOnShutdown(QQuickItem *);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
-
-public:
- QQuickCloseEvent()
- : _accepted(true) {}
-
- bool isAccepted() { return _accepted; }
- void setAccepted(bool accepted) { _accepted = accepted; }
-
-private:
- bool _accepted;
-};
-
class QQuickWindowQObjectCleanupJob : public QRunnable
{
public:
@@ -321,6 +313,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowPrivate::FocusOptions)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCloseEvent)
-
#endif // QQUICKWINDOW_P_H
diff --git a/src/quick/items/shaders/sprite.frag b/src/quick/items/shaders/sprite.frag
deleted file mode 100644
index e1fcb0f006..0000000000
--- a/src/quick/items/shaders/sprite.frag
+++ /dev/null
@@ -1,12 +0,0 @@
-uniform sampler2D _qt_texture;
-uniform lowp float qt_Opacity;
-
-varying highp vec4 fTexS;
-varying lowp float progress;
-
-void main()
-{
- gl_FragColor = mix(texture2D(_qt_texture, fTexS.xy),
- texture2D(_qt_texture, fTexS.zw),
- progress) * qt_Opacity;
-} \ No newline at end of file
diff --git a/src/quick/items/shaders/sprite.vert b/src/quick/items/shaders/sprite.vert
deleted file mode 100644
index fc826f60b4..0000000000
--- a/src/quick/items/shaders/sprite.vert
+++ /dev/null
@@ -1,23 +0,0 @@
-attribute highp vec2 vPos;
-attribute highp vec2 vTex;
-
-uniform highp vec3 animData;// w,h(premultiplied of anim), interpolation progress
-uniform highp vec4 animPos;//x,y, x,y (two frames for interpolation)
-
-uniform highp mat4 qt_Matrix;
-
-varying highp vec4 fTexS;
-varying lowp float progress;
-
-void main()
-{
- progress = animData.z;
-
- // Calculate frame location in texture
- fTexS.xy = animPos.xy + vTex.xy * animData.xy;
-
- // Next frame is also passed, for interpolation
- fTexS.zw = animPos.zw + vTex.xy * animData.xy;
-
- gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1);
-} \ No newline at end of file
diff --git a/src/quick/items/shaders/sprite_core.frag b/src/quick/items/shaders/sprite_core.frag
deleted file mode 100644
index c1087a8754..0000000000
--- a/src/quick/items/shaders/sprite_core.frag
+++ /dev/null
@@ -1,16 +0,0 @@
-#version 150 core
-
-in vec4 fTexS;
-in float progress;
-
-out vec4 fragColor;
-
-uniform sampler2D _qt_texture;
-uniform float qt_Opacity;
-
-void main()
-{
- fragColor = mix(texture(_qt_texture, fTexS.xy),
- texture(_qt_texture, fTexS.zw),
- progress) * qt_Opacity;
-} \ No newline at end of file
diff --git a/src/quick/items/shaders/sprite_core.vert b/src/quick/items/shaders/sprite_core.vert
deleted file mode 100644
index 5027bf03fc..0000000000
--- a/src/quick/items/shaders/sprite_core.vert
+++ /dev/null
@@ -1,24 +0,0 @@
-#version 150 core
-
-in vec2 vPos;
-in vec2 vTex;
-
-out vec4 fTexS;
-out float progress;
-
-uniform vec3 animData; // w,h(premultiplied of anim), interpolation progress
-uniform vec4 animPos; // x,y, x,y (two frames for interpolation)
-uniform mat4 qt_Matrix;
-
-void main()
-{
- progress = animData.z;
-
- // Calculate frame location in texture
- fTexS.xy = animPos.xy + vTex.xy * animData.xy;
-
- // Next frame is also passed, for interpolation
- fTexS.zw = animPos.zw + vTex.xy * animData.xy;
-
- gl_Position = qt_Matrix * vec4(vPos.x, vPos.y, 0, 1);
-} \ No newline at end of file