aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2012-04-11 14:56:22 +0200
committerLars Knoll <lars.knoll@nokia.com>2012-04-11 16:05:03 +0200
commita896d4b39ec3d45ba708d9b36ea9c864b1df2136 (patch)
tree45cfe153cce6114c2c76c48dc0bdabde2a8cf3e3 /src/quick
parent24fb8dc27eddfdd62bd2c3a6e863cbf433762cd6 (diff)
parent65bfc35429e845cf6b76d58107360a1360a654fc (diff)
Merge remote-tracking branch 'origin/master' into api_changes
Conflicts: src/qml/debugger/qqmlprofilerservice_p.h src/qml/qml/qqmlboundsignal.cpp src/qml/qml/v4/qv4bindings.cpp src/quick/items/qquickshadereffect.cpp src/quick/particles/qquickcustomparticle.cpp src/quick/qtquick2.cpp Change-Id: Ia9c6517035ae912fa75e77473a452bd3383def56
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp682
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h22
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp30
-rw-r--r--src/quick/items/qquickcanvas.cpp17
-rw-r--r--src/quick/items/qquickcanvas.h2
-rw-r--r--src/quick/items/qquickflickable.cpp3
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp12
-rw-r--r--src/quick/items/qquickitem.cpp53
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h2
-rw-r--r--src/quick/items/qquicklistview.cpp11
-rw-r--r--src/quick/items/qquickloader.cpp81
-rw-r--r--src/quick/items/qquickloader_p_p.h7
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp2
-rw-r--r--src/quick/items/qquickpathview.cpp143
-rw-r--r--src/quick/items/qquickpathview_p.h7
-rw-r--r--src/quick/items/qquickpathview_p_p.h9
-rw-r--r--src/quick/items/qquickshadereffect.cpp960
-rw-r--r--src/quick/items/qquickshadereffect_p.h69
-rw-r--r--src/quick/items/qquickshadereffectnode.cpp210
-rw-r--r--src/quick/items/qquickshadereffectnode_p.h52
-rw-r--r--src/quick/items/qquickspriteengine.cpp3
-rw-r--r--src/quick/items/qquicktext.cpp9
-rw-r--r--src/quick/items/qquicktextcontrol.cpp74
-rw-r--r--src/quick/items/qquicktextcontrol_p.h4
-rw-r--r--src/quick/items/qquicktextcontrol_p_p.h4
-rw-r--r--src/quick/items/qquicktextedit.cpp12
-rw-r--r--src/quick/items/qquicktextinput.cpp82
-rw-r--r--src/quick/items/qquicktextinput_p_p.h9
-rw-r--r--src/quick/items/qquicktextnode.cpp22
-rw-r--r--src/quick/items/qquickvisualdatamodel.cpp2
-rw-r--r--src/quick/items/qquickwindowmanager.cpp2
-rw-r--r--src/quick/items/qquickwindowmodule_p.h4
-rw-r--r--src/quick/particles/qquickcustomparticle.cpp387
-rw-r--r--src/quick/particles/qquickcustomparticle_p.h48
-rw-r--r--src/quick/particles/qquickparticleemitter_p.h3
-rw-r--r--src/quick/particles/qquickparticlesmodule_p.h10
-rw-r--r--src/quick/qtquick2.cpp6
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp25
-rw-r--r--src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h11
-rw-r--r--src/quick/scenegraph/util/qsgpainternode.cpp5
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp4
-rw-r--r--src/quick/util/qquickconnections.cpp5
-rw-r--r--src/quick/util/qquickpropertychanges.cpp124
44 files changed, 1836 insertions, 1395 deletions
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index a605b9ce6d..6d8d9b852b 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -47,7 +47,6 @@
#include <QtQuick/private/qquickshadereffectsource_p.h>
#include <QtGui/qopenglframebufferobject.h>
-#include <QtCore/qdebug.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qquicksvgparser_p.h>
#include <private/qquickpath_p.h>
@@ -185,7 +184,7 @@ QColor qt_color_from_string(v8::Local<v8::Value> name)
if (*p != ')') return QColor();
if (isRgb)
return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
- else
+ else if (isHsl)
return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
}
return QColor();
@@ -484,8 +483,6 @@ static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
CHECK_CONTEXT(r)
r->context->reset();
- r->context->m_path = QPainterPath();
- r->context->m_path.setFillRule(Qt::WindingFill);
return args.This();
}
@@ -551,15 +548,8 @@ static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- if (args.Length() == 1) {
- qreal angle = args[0]->NumberValue();
- if (!qIsFinite(angle))
- return args.This();
-
- r->context->state.matrix.rotate(DEGREES(angle));
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
-
+ if (args.Length() == 1)
+ r->context->rotate(args[0]->NumberValue());
return args.This();
}
@@ -583,17 +573,8 @@ static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 2) {
- qreal x, y;
- x = args[0]->NumberValue();
- y = args[1]->NumberValue();
- if (!qIsFinite(x) || !qIsFinite(y))
- return args.This();
-
- r->context->state.matrix.scale(x, y);
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
-
+ if (args.Length() == 2)
+ r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
return args.This();
}
@@ -636,25 +617,13 @@ static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 6) {
- qreal a = args[0]->NumberValue();
- qreal b = args[1]->NumberValue();
- qreal c = args[2]->NumberValue();
- qreal d = args[3]->NumberValue();
- qreal e = args[4]->NumberValue();
- qreal f = args[5]->NumberValue();
-
- if (!qIsFinite(a)
- || !qIsFinite(b)
- || !qIsFinite(c)
- || !qIsFinite(d)
- || !qIsFinite(e)
- || !qIsFinite(f))
- return args.This();
-
- r->context->state.matrix = QTransform(a, b, c, d, e, f);
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
+ if (args.Length() == 6)
+ r->context->setTransform( args[0]->NumberValue()
+ , args[1]->NumberValue()
+ , args[2]->NumberValue()
+ , args[3]->NumberValue()
+ , args[4]->NumberValue()
+ , args[5]->NumberValue());
return args.This();
}
@@ -675,25 +644,13 @@ static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 6) {
- qreal a = args[0]->NumberValue();
- qreal b = args[1]->NumberValue();
- qreal c = args[2]->NumberValue();
- qreal d = args[3]->NumberValue();
- qreal e = args[4]->NumberValue();
- qreal f = args[5]->NumberValue();
-
- if (!qIsFinite(a)
- || !qIsFinite(b)
- || !qIsFinite(c)
- || !qIsFinite(d)
- || !qIsFinite(e)
- || !qIsFinite(f))
- return args.This();
-
- r->context->state.matrix *= QTransform(a, b, c, d, e, f);
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
+ if (args.Length() == 6)
+ r->context->transform( args[0]->NumberValue()
+ , args[1]->NumberValue()
+ , args[2]->NumberValue()
+ , args[3]->NumberValue()
+ , args[4]->NumberValue()
+ , args[5]->NumberValue());
return args.This();
}
@@ -713,17 +670,8 @@ static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 2) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y))
- return args.This();
-
- r->context->state.matrix.translate(x, y);
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
-
+ if (args.Length() == 2)
+ r->context->translate(args[0]->NumberValue(), args[1]->NumberValue());
return args.This();
}
@@ -739,8 +687,7 @@ static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- r->context->state.matrix = QTransform();
- r->context->buffer()->updateMatrix(r->context->state.matrix);
+ r->context->setTransform(1, 0, 0, 1, 0, 0);
return args.This();
}
@@ -755,16 +702,9 @@ static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- if (args.Length() == 2) {
- qreal sh = args[0]->NumberValue();
- qreal sv = args[1]->NumberValue();
+ if (args.Length() == 2)
+ r->context->shear(args[0]->NumberValue(), args[1]->NumberValue());
- if (!qIsFinite(sh) || !qIsFinite(sv))
- return args.This();
-
- r->context->state.matrix.shear(sh, sv);
- r->context->buffer()->updateMatrix(r->context->state.matrix);
- }
return args.This();
}
// compositing
@@ -1596,17 +1536,11 @@ static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 4) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
- r->context->buffer()->clearRect(x, y, w, h);
- }
+ if (args.Length() == 4)
+ r->context->clearRect(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue());
return args.This();
}
@@ -1621,18 +1555,8 @@ static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- if (args.Length() == 4) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
- r->context->buffer()->fillRect(x, y, w, h);
- }
-
+ if (args.Length() == 4)
+ r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
return args.This();
}
@@ -1651,18 +1575,8 @@ static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
- if (args.Length() == 4) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
- r->context->buffer()->strokeRect(x, y, w, h);
- }
+ if (args.Length() == 4)
+ r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
return args.This();
}
@@ -1687,15 +1601,8 @@ static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
antiClockwise = args[5]->BooleanValue();
qreal radius = args[2]->NumberValue();
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal sa = args[3]->NumberValue();
- qreal ea = args[4]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(sa) || !qIsFinite(ea))
- return args.This();
- if (radius < 0)
+ if (qIsFinite(radius) && radius < 0)
V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
r->context->arc(args[0]->NumberValue(),
@@ -1734,25 +1641,17 @@ static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
-
if (args.Length() == 5) {
- qreal x1 = args[0]->NumberValue();
- qreal y1 = args[1]->NumberValue();
- qreal x2 = args[2]->NumberValue();
- qreal y2 = args[3]->NumberValue();
-
- if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2))
- return args.This();
-
qreal radius = args[4]->NumberValue();
- if (radius < 0)
+
+ if (qIsFinite(radius) && radius < 0)
V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
+
r->context->arcTo(args[0]->NumberValue(),
args[1]->NumberValue(),
args[2]->NumberValue(),
args[3]->NumberValue(),
- args[4]->NumberValue());
+ radius);
}
return args.This();
@@ -1845,14 +1744,7 @@ static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- QPainterPath clipPath = r->context->m_path;
- clipPath.closeSubpath();
- if (!r->context->state.clipPath.isEmpty())
- r->context->state.clipPath = clipPath.intersected(r->context->state.clipPath);
- else
- r->context->state.clipPath = clipPath;
- r->context->buffer()->clip(r->context->state.clipPath);
-
+ r->context->clip();
return args.This();
}
@@ -1887,9 +1779,7 @@ static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r);
-
- r->context->buffer()->fill(r->context->m_path);
-
+ r->context->fill();
return args.This();
}
@@ -1975,19 +1865,8 @@ static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
- if (args.Length() == 4) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
- r->context->rect(x, y, w, h);
- }
-
+ if (args.Length() == 4)
+ r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
return args.This();
}
@@ -2002,23 +1881,13 @@ static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
- if (args.Length() == 6) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
- qreal xr = args[4]->NumberValue();
- qreal yr = args[5]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
- if (!qIsFinite(xr) || !qIsFinite(yr))
- V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "roundedRect(): Invalid arguments");
-
- r->context->roundedRect(x, y, w, h, xr, yr);
- }
-
+ if (args.Length() == 6)
+ r->context->roundedRect(args[0]->NumberValue()
+ , args[1]->NumberValue()
+ , args[2]->NumberValue()
+ , args[3]->NumberValue()
+ , args[4]->NumberValue()
+ , args[5]->NumberValue());
return args.This();
}
@@ -2036,18 +1905,8 @@ static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
CHECK_CONTEXT(r)
- if (args.Length() == 4) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- qreal w = args[2]->NumberValue();
- qreal h = args[3]->NumberValue();
-
- if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
- return args.This();
-
-
- r->context->ellipse(x, y, w, h);
- }
+ if (args.Length() == 4)
+ r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
return args.This();
}
@@ -2089,9 +1948,7 @@ static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
- r->context->buffer()->stroke(r->context->m_path);
-
+ r->context->stroke();
return args.This();
}
@@ -2108,13 +1965,8 @@ static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
CHECK_CONTEXT(r)
bool pointInPath = false;
- if (args.Length() == 2) {
- qreal x = args[0]->NumberValue();
- qreal y = args[1]->NumberValue();
- if (!qIsFinite(x) || !qIsFinite(y))
- return v8::Boolean::New(false);
- pointInPath = r->context->isPointInPath(x, y);
- }
+ if (args.Length() == 2)
+ pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue());
return v8::Boolean::New(pointInPath);
}
@@ -2331,14 +2183,8 @@ static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
CHECK_CONTEXT(r)
QV8Engine *engine = V8ENGINE();
- if (args.Length() == 3) {
- qreal x = args[1]->NumberValue();
- qreal y = args[2]->NumberValue();
- if (!qIsFinite(x) || !qIsFinite(y))
- return args.This();
- QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
- r->context->buffer()->stroke(textPath);
- }
+ if (args.Length() == 3)
+ r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false);
return args.This();
}
/*!
@@ -2450,6 +2296,10 @@ static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
if (!args.Length())
return args.This();
+ //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
+ if (!r->context->state.invertibleCTM)
+ return args.This();
+
QImage image;
if (args[0]->IsString()) {
QUrl url(engine->toString(args[0]->ToString()));
@@ -2868,16 +2718,221 @@ static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &ar
return args.This();
}
+void QQuickContext2D::scale(qreal x, qreal y)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return;
+
+ QTransform newTransform = state.matrix;
+ newTransform.scale(x, y);
+
+ if (!newTransform.isInvertible()) {
+ state.invertibleCTM = false;
+ return;
+ }
+
+ state.matrix = newTransform;
+ buffer()->updateMatrix(state.matrix);
+ m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path);
+}
+
+void QQuickContext2D::rotate(qreal angle)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(angle))
+ return;
+
+ QTransform newTransform =state.matrix;
+ newTransform.rotate(DEGREES(angle));
+
+ if (!newTransform.isInvertible()) {
+ state.invertibleCTM = false;
+ return;
+ }
+
+ state.matrix = newTransform;
+ buffer()->updateMatrix(state.matrix);
+ m_path = QTransform().rotate(-DEGREES(angle)).map(m_path);
+}
+
+void QQuickContext2D::shear(qreal h, qreal v)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(h) || !qIsFinite(v))
+ return ;
+
+ QTransform newTransform = state.matrix;
+ newTransform.shear(h, v);
+
+ if (!newTransform.isInvertible()) {
+ state.invertibleCTM = false;
+ return;
+ }
+
+ state.matrix = newTransform;
+ buffer()->updateMatrix(state.matrix);
+ m_path = QTransform().shear(-h, -v).map(m_path);
+}
+
+void QQuickContext2D::translate(qreal x, qreal y)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return ;
+
+ QTransform newTransform = state.matrix;
+ newTransform.translate(x, y);
+
+ if (!newTransform.isInvertible()) {
+ state.invertibleCTM = false;
+ return;
+ }
+
+ state.matrix = newTransform;
+ buffer()->updateMatrix(state.matrix);
+ m_path = QTransform().translate(-x, -y).map(m_path);
+}
+
+void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
+ return;
+
+ QTransform transform(a, b, c, d, e, f);
+ QTransform newTransform = state.matrix * transform;
+
+ if (!newTransform.isInvertible()) {
+ state.invertibleCTM = false;
+ return;
+ }
+ state.matrix = newTransform;
+ buffer()->updateMatrix(state.matrix);
+ m_path = transform.inverted().map(m_path);
+}
+
+void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
+{
+ if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
+ return;
+
+ QTransform ctm = state.matrix;
+ if (!ctm.isInvertible())
+ return;
+
+ state.matrix = ctm.inverted() * state.matrix;
+ m_path = ctm.map(m_path);
+ state.invertibleCTM = true;
+ transform(a, b, c, d, e, f);
+}
+
+void QQuickContext2D::fill()
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!m_path.elementCount())
+ return;
+
+ m_path.setFillRule(state.fillRule);
+ buffer()->fill(m_path);
+}
+
+void QQuickContext2D::clip()
+{
+ if (!state.invertibleCTM)
+ return;
+
+ QPainterPath clipPath = m_path;
+ clipPath.closeSubpath();
+ if (!state.clipPath.isEmpty())
+ state.clipPath = clipPath.intersected(state.clipPath);
+ else
+ state.clipPath = clipPath;
+ buffer()->clip(state.clipPath);
+}
+
+void QQuickContext2D::stroke()
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!m_path.elementCount())
+ return;
+
+ buffer()->stroke(m_path);
+}
+
+void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return;
+
+ buffer()->fillRect(x, y, w, h);
+}
+
+void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return;
+
+ buffer()->strokeRect(x, y, w, h);
+}
+
+void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return;
+
+ buffer()->clearRect(x, y, w, h);
+}
+
+void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
+{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return;
+
+ QPainterPath textPath = createTextGlyphs(x, y, text);
+ if (fill)
+ buffer()->fill(textPath);
+ else
+ buffer()->stroke(textPath);
+}
+
void QQuickContext2D::beginPath()
{
+ if (!m_path.elementCount())
+ return;
m_path = QPainterPath();
- m_path.setFillRule(state.fillRule);
}
void QQuickContext2D::closePath()
{
- if (m_path.isEmpty())
+ if (!m_path.elementCount())
return;
QRectF boundRect = m_path.boundingRect();
@@ -2889,29 +2944,53 @@ void QQuickContext2D::closePath()
void QQuickContext2D::moveTo( qreal x, qreal y)
{
+ if (!state.invertibleCTM)
+ return;
+
//FIXME: moveTo should not close the previous subpath
- m_path.moveTo(state.matrix.map(QPointF(x, y)));
+ m_path.moveTo(QPointF(x, y));
}
void QQuickContext2D::lineTo( qreal x, qreal y)
{
- m_path.lineTo(state.matrix.map(QPointF(x, y)));
+ if (!state.invertibleCTM)
+ return;
+
+ QPointF pt(x, y);
+
+ if (!m_path.elementCount())
+ m_path.moveTo(pt);
+ else if (m_path.currentPosition() != pt)
+ m_path.lineTo(pt);
}
void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
qreal x, qreal y)
{
- m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
- state.matrix.map(QPointF(x, y)));
+ if (!state.invertibleCTM)
+ return;
+
+ if (!m_path.elementCount())
+ m_path.moveTo(QPointF(cpx, cpy));
+
+ QPointF pt(x, y);
+ if (m_path.currentPosition() != pt)
+ m_path.quadTo(QPointF(cpx, cpy), pt);
}
void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
qreal cp2x, qreal cp2y,
qreal x, qreal y)
{
- m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
- state.matrix.map(QPointF(cp2x, cp2y)),
- state.matrix.map(QPointF(x, y)));
+ if (!state.invertibleCTM)
+ return;
+
+ if (!m_path.elementCount())
+ m_path.moveTo(QPointF(cp1x, cp1y));
+
+ QPointF pt(x, y);
+ if (m_path.currentPosition() != pt)
+ m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
}
void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
@@ -2969,69 +3048,100 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu
if ((sa < ea) && ((ea - sa) > Q_PI))
anticlockwise = true;
- arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false);
+ arc(p.x(), p.y(), radius, sa, ea, anticlockwise);
}
void QQuickContext2D::arcTo(qreal x1, qreal y1,
qreal x2, qreal y2,
qreal radius)
{
- QPointF st = state.matrix.map(QPointF(x1, y1));
- QPointF end = state.matrix.map(QPointF(x2, y2));
+ if (!state.invertibleCTM)
+ return;
- if (!m_path.elementCount()) {
+ if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2) || !qIsFinite(radius))
+ return;
+
+ QPointF st(x1, y1);
+ QPointF end(x2, y2);
+
+ if (!m_path.elementCount())
m_path.moveTo(st);
- } else if (st == m_path.currentPosition() || st == end || !radius) {
- m_path.lineTo(st);
- } else {
+ else if (st == m_path.currentPosition() || st == end || !radius)
+ lineTo(x1, y1);
+ else
addArcTo(st, end, radius);
- }
-}
+ }
-void QQuickContext2D::rect(qreal x, qreal y,
- qreal w, qreal h)
+void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
{
- m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h)));
+ if (!state.invertibleCTM)
+ return;
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return;
+
+ if (!w && !h) {
+ m_path.moveTo(x, y);
+ return;
+ }
+ m_path.addRect(x, y, w, h);
}
void QQuickContext2D::roundedRect(qreal x, qreal y,
qreal w, qreal h,
qreal xr, qreal yr)
{
- QPainterPath path;
- path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
- m_path.addPath(state.matrix.map(path));
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h) || !qIsFinite(xr) || !qIsFinite(yr))
+ return;
+
+ if (!w && !h) {
+ m_path.moveTo(x, y);
+ return;
+ }
+ m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
}
void QQuickContext2D::ellipse(qreal x, qreal y,
qreal w, qreal h)
{
- QPainterPath path;
- path.addEllipse(x, y, w, h);
- m_path.addPath(state.matrix.map(path));
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return;
+
+ if (!w && !h) {
+ m_path.moveTo(x, y);
+ return;
+ }
+
+ m_path.addEllipse(x, y, w, h);
}
void QQuickContext2D::text(const QString& str, qreal x, qreal y)
{
+ if (!state.invertibleCTM)
+ return;
+
QPainterPath path;
path.addText(x, y, state.font, str);
- m_path.addPath(state.matrix.map(path));
+ m_path.addPath(path);
}
-void QQuickContext2D::arc(qreal xc,
- qreal yc,
- qreal radius,
- qreal sar,
- qreal ear,
- bool antiClockWise,
- bool transform)
+void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
{
+ if (!state.invertibleCTM)
+ return;
+
+ if (!qIsFinite(xc) || !qIsFinite(yc) || !qIsFinite(sar) || !qIsFinite(ear) || !qIsFinite(radius))
+ return;
+
+ if (sar == ear)
+ return;
+
- if (transform) {
- QPointF point = state.matrix.map(QPointF(xc, yc));
- xc = point.x();
- yc = point.y();
- }
//### HACK
// In Qt we don't switch the coordinate system for degrees
@@ -3068,17 +3178,14 @@ void QQuickContext2D::arc(qreal xc,
qFuzzyCompare(qAbs(span), 360))) {
span += ea - sa;
}
- if (!m_path.elementCount())
- m_path.moveTo(xs, ys);
}
-
- if (transform) {
- QPointF currentPos = m_path.currentPosition();
- QPointF startPos = QPointF(xc + radius * qCos(sar),
- yc - radius * qSin(sar));
- if (currentPos != startPos)
- m_path.lineTo(startPos);
+ // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
+ if (!m_path.elementCount())
+ m_path.arcMoveTo(xs, ys, width, height, sa);
+ else if (!radius) {
+ m_path.lineTo(xc, yc);
+ return;
}
m_path.arcTo(xs, ys, width, height, sa, span);
@@ -3142,9 +3249,59 @@ QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString&
}
+static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
+{
+ // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
+ return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
+}
+
+static inline bool withinRange(qreal p, qreal a, qreal b)
+{
+ return (p >= a && p <= b) || (p >= b && p <= a);
+}
+
bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
{
- return m_path.contains(QPointF(x, y));
+ if (!state.invertibleCTM)
+ return false;
+
+ if (!m_path.elementCount())
+ return false;
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return false;
+
+ QPointF point(x, y);
+ QTransform ctm = state.matrix;
+ QPointF p = ctm.inverted().map(point);
+ if (!qIsFinite(p.x()) || !qIsFinite(p.y()))
+ return false;
+
+ const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
+
+ bool contains = m_path.contains(p);
+
+ if (!contains) {
+ // check whether the point is on the border
+ QPolygonF border = m_path.toFillPolygon();
+
+ QPointF p1 = border.at(0);
+ QPointF p2;
+
+ for (int i = 1; i < border.size(); ++i) {
+ p2 = border.at(i);
+ if (areCollinear(p, p1, p2)
+ // Once we know that the points are collinear we
+ // only need to check one of the coordinates
+ && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
+ withinRange(p.x(), p1.x(), p2.x()) :
+ withinRange(p.y(), p1.y(), p2.y()))) {
+ return true;
+ }
+ p1 = p2;
+ }
+ }
+ return contains;
}
QQuickContext2D::QQuickContext2D(QObject *parent)
@@ -3405,7 +3562,9 @@ void QQuickContext2D::popState()
if (newState.shadowOffsetY != state.shadowOffsetY)
buffer()->setShadowOffsetY(newState.shadowOffsetY);
+ m_path = state.matrix.map(m_path);
state = newState;
+ m_path = state.matrix.inverted().map(m_path);
}
void QQuickContext2D::pushState()
{
@@ -3417,6 +3576,8 @@ void QQuickContext2D::reset()
QQuickContext2D::State newState;
newState.matrix = QTransform();
+ m_path = QPainterPath();
+
QPainterPath defaultClipPath;
QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
@@ -3431,6 +3592,7 @@ void QQuickContext2D::reset()
newState.fillPatternRepeatY = false;
newState.strokePatternRepeatX = false;
newState.strokePatternRepeatY = false;
+ newState.invertibleCTM = true;
newState.fillRule = Qt::WindingFill;
newState.globalAlpha = 1.0;
newState.lineWidth = 1;
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 3230881134..4112d4ebf0 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -116,6 +116,7 @@ public:
, fillPatternRepeatY(false)
, strokePatternRepeatX(false)
, strokePatternRepeatY(false)
+ , invertibleCTM(true)
, fillRule(Qt::WindingFill)
, globalAlpha(1.0)
, lineWidth(1)
@@ -141,6 +142,7 @@ public:
bool fillPatternRepeatY:1;
bool strokePatternRepeatX:1;
bool strokePatternRepeatY:1;
+ bool invertibleCTM:1;
Qt::FillRule fillRule;
qreal globalAlpha;
qreal lineWidth;
@@ -180,7 +182,23 @@ public:
void pushState();
void reset();
- // path API
+ void fill();
+ void clip();
+ void stroke();
+ void fillRect(qreal x, qreal y, qreal w, qreal h);
+ void strokeRect(qreal x, qreal y, qreal w, qreal h);
+ void clearRect(qreal x, qreal y, qreal w, qreal h);
+ void drawText(const QString& text, qreal x, qreal y, bool fill);
+
+ //Transform APIs
+ void scale(qreal x, qreal y);
+ void rotate(qreal angle);
+ void shear(qreal h, qreal v);
+ void translate(qreal x, qreal y);
+ void transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f);
+ void setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f);
+
+ // Path APIs
void beginPath();
void closePath();
void moveTo(qreal x, qreal y);
@@ -195,7 +213,7 @@ public:
void text(const QString& str, qreal x, qreal y);
void arc(qreal x, qreal y, qreal radius,
qreal startAngle, qreal endAngle,
- bool anticlockwise, bool transform=true);
+ bool anticlockwise);
void addArcTo(const QPointF& p1, const QPointF& p2, float radius);
bool isPointInPath(qreal x, qreal y) const;
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 591fc216a4..f6b9a1afeb 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -236,7 +236,7 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
reset();
- QTransform originMatrix = p->transform();
+ QTransform originMatrix = p->worldTransform();
QPen pen = makePen(state);
setPainterState(p, state, pen);
@@ -247,7 +247,7 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
case QQuickContext2D::UpdateMatrix:
{
state.matrix = takeMatrix();
- p->setTransform(state.matrix * originMatrix);
+ p->setWorldTransform(state.matrix * originMatrix);
break;
}
case QQuickContext2D::ClearRect:
@@ -303,36 +303,42 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
state.strokeStyle = takeStrokeStyle();
state.strokePatternRepeatX = takeBool();
state.strokePatternRepeatY = takeBool();
- pen.setBrush(state.strokeStyle);
- p->setPen(pen);
+ QPen nPen = p->pen();
+ nPen.setBrush(state.strokeStyle);
+ p->setPen(nPen);
break;
}
case QQuickContext2D::LineWidth:
{
state.lineWidth = takeLineWidth();
- pen.setWidth(state.lineWidth);
- p->setPen(pen);
+ QPen nPen = p->pen();
+
+ nPen.setWidthF(state.lineWidth);
+ p->setPen(nPen);
break;
}
case QQuickContext2D::LineCap:
{
state.lineCap = takeLineCap();
- pen.setCapStyle(state.lineCap);
- p->setPen(pen);
+ QPen nPen = p->pen();
+ nPen.setCapStyle(state.lineCap);
+ p->setPen(nPen);
break;
}
case QQuickContext2D::LineJoin:
{
state.lineJoin = takeLineJoin();
- pen.setJoinStyle(state.lineJoin);
- p->setPen(pen);
+ QPen nPen = p->pen();
+ nPen.setJoinStyle(state.lineJoin);
+ p->setPen(nPen);
break;
}
case QQuickContext2D::MiterLimit:
{
state.miterLimit = takeMiterLimit();
- pen.setMiterLimit(state.miterLimit);
- p->setPen(pen);
+ QPen nPen = p->pen();
+ nPen.setMiterLimit(state.miterLimit);
+ p->setPen(nPen);
break;
}
case QQuickContext2D::TextAlign:
diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp
index 2e2c8725aa..3f08c8f056 100644
--- a/src/quick/items/qquickcanvas.cpp
+++ b/src/quick/items/qquickcanvas.cpp
@@ -55,7 +55,6 @@
#include <private/qguiapplication_p.h>
#include <QtGui/QInputMethod>
-#include <QtGui/QCursor>
#include <private/qabstractanimation_p.h>
@@ -123,10 +122,12 @@ private:
bool m_eventSent;
};
+#ifndef QT_NO_ACCESSIBILITY
QAccessibleInterface *QQuickCanvas::accessibleRoot() const
{
return QAccessible::queryAccessibleInterface(const_cast<QQuickCanvas*>(this));
}
+#endif
/*
@@ -154,6 +155,10 @@ have a scope focused item), and the other items will have their focus cleared.
// #define TOUCH_DEBUG
// #define DIRTY_DEBUG
+#ifdef FOCUS_DEBUG
+void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
+#endif
+
QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
: transformNode(0)
{
@@ -952,7 +957,7 @@ bool QQuickCanvasPrivate::clearHover()
if (hoverItems.isEmpty())
return false;
- QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
+ QPointF pos = QGuiApplicationPrivate::lastCursorPosition;; // ### refactor: q->mapFromGlobal(QCursor::pos());
bool accepted = false;
foreach (QQuickItem* item, hoverItems)
@@ -1087,8 +1092,12 @@ bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()),
event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
- if (QQuickMouseEventEx::extended(event))
- me.setVelocity(QQuickMouseEventEx::extended(event)->velocity());
+ QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event);
+ if (eventEx) {
+ me.setVelocity(eventEx->velocity());
+ me.setCapabilities(eventEx->capabilities());
+ }
+ me.setTimestamp(event->timestamp());
me.accept();
q->sendEvent(mouseGrabberItem, &me);
event->setAccepted(me.isAccepted());
diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h
index 787bb7e3c7..396bc2b8ff 100644
--- a/src/quick/items/qquickcanvas.h
+++ b/src/quick/items/qquickcanvas.h
@@ -102,7 +102,9 @@ public:
QQmlIncubationController *incubationController() const;
+#ifndef QT_NO_ACCESSIBILITY
virtual QAccessibleInterface *accessibleRoot() const;
+#endif
// Scene graph specific functions
QSGTexture *createTextureFromImage(const QImage &image) const;
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 62d0e4aa41..75c9919e34 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1108,7 +1108,8 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
d->clearDelayedPress();
d->handleMouseReleaseEvent(event);
event->accept();
- ungrabMouse();
+ if (canvas() && canvas()->mouseGrabberItem() == this)
+ ungrabMouse();
} else {
QQuickItem::mouseReleaseEvent(event);
}
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 427be42312..1de8e0ab73 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -47,12 +47,24 @@ QT_BEGIN_NAMESPACE
void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickImplicitSizeItem);
+ for (int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QQuickItemPrivate::ImplicitWidth) {
+ change.listener->itemImplicitWidthChanged(q);
+ }
+ }
emit q->implicitWidthChanged();
}
void QQuickImplicitSizeItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickImplicitSizeItem);
+ for (int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QQuickItemPrivate::ImplicitHeight) {
+ change.listener->itemImplicitHeightChanged(q);
+ }
+ }
emit q->implicitHeightChanged();
}
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 1cf10df2f7..ffaec540a2 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -52,8 +52,8 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlinfo.h>
#include <QtGui/qpen.h>
-#include <QtGui/qcursor.h>
#include <QtGui/qguiapplication.h>
+#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qinputmethod.h>
#include <QtCore/qdebug.h>
#include <QtCore/qcoreevent.h>
@@ -75,6 +75,26 @@
QT_BEGIN_NAMESPACE
+#ifdef FOCUS_DEBUG
+void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1);
+void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth)
+{
+ qWarning()
+ << QByteArray(depth, '\t').constData()
+ << (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ')
+ << item->hasFocus()
+ << item->hasActiveFocus()
+ << item->isFocusScope()
+ << item;
+ foreach (QQuickItem *child, item->childItems()) {
+ printFocusTree(
+ child,
+ item->isFocusScope() || !scope ? item : scope,
+ item->isFocusScope() || !scope ? depth + 1 : depth);
+ }
+}
+#endif
+
static void QQuickItem_parentNotifier(QObject *o, intptr_t, QQmlNotifier **n)
{
QQuickItemPrivate *d = QQuickItemPrivate::get(static_cast<QQuickItem *>(o));
@@ -1613,9 +1633,6 @@ void QQuickItemPrivate::setAccessibleFlagAndListener()
if (item->d_func()->isAccessible)
break; // already set - grandparents should have the flag set as well.
- if (item->canvas() && item->canvas()->rootItem() == item)
- break; // don't add a listener to the canvas root item
-
item->d_func()->isAccessible = true;
item = item->d_func()->parentItem;
}
@@ -1939,6 +1956,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
if (d->canvas) {
QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem,
QQuickCanvasPrivate::DontChangeFocusProperty);
+ if (scopeFocusedItem != this)
+ QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true);
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, false);
}
@@ -1990,7 +2009,10 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
- if (scopeItem->scopedFocusItem()) {
+ if (QQuickItemPrivate::get(scopeItem)->subFocusItem
+ || (!scopeItem->isFocusScope() && scopeItem->hasFocus())) {
+ if (scopeFocusedItem != this)
+ QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, false);
QQuickItemPrivate::get(scopeFocusedItem)->focus = false;
emit scopeFocusedItem->focusChanged(false);
} else {
@@ -4145,7 +4167,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
for (int ii = 0; ii < childItems.count(); ++ii) {
QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
- flags & QQuickItem::ItemIsFocusScope ? q : scope, newEffectiveEnable);
+ (flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
if (canvas && scope && effectiveEnable && focus) {
@@ -4509,6 +4531,12 @@ void QQuickItem::resetWidth()
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
+ for (int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QQuickItemPrivate::ImplicitWidth) {
+ change.listener->itemImplicitWidthChanged(q);
+ }
+ }
emit q->implicitWidthChanged();
}
@@ -4631,6 +4659,12 @@ void QQuickItem::resetHeight()
void QQuickItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickItem);
+ for (int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QQuickItemPrivate::ImplicitHeight) {
+ change.listener->itemImplicitHeightChanged(q);
+ }
+ }
emit q->implicitHeightChanged();
}
@@ -4781,6 +4815,7 @@ void QQuickItem::setFocus(bool focus)
QVarLengthArray<QQuickItem *, 20> changed;
QQuickItem *oldSubFocusItem = QQuickItemPrivate::get(scope)->subFocusItem;
if (oldSubFocusItem) {
+ QQuickItemPrivate::get(oldSubFocusItem)->updateSubFocusItem(scope, false);
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
changed << oldSubFocusItem;
}
@@ -4850,7 +4885,7 @@ bool QQuickItem::isUnderMouse() const
if (!d->canvas)
return false;
- QPoint cursorPos = QCursor::pos();
+ QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos))))
return true;
return false;
@@ -5669,10 +5704,11 @@ void QQuickItemLayer::activateEffect()
Q_ASSERT(m_effectComponent);
Q_ASSERT(!m_effect);
- QObject *created = m_effectComponent->create();
+ QObject *created = m_effectComponent->beginCreate(m_effectComponent->creationContext());
m_effect = qobject_cast<QQuickItem *>(created);
if (!m_effect) {
qWarning("Item: layer.effect is not a QML Item.");
+ m_effectComponent->completeCreate();
delete created;
return;
}
@@ -5683,6 +5719,7 @@ void QQuickItemLayer::activateEffect()
}
m_effect->setVisible(m_item->isVisible());
m_effect->setProperty(m_name, qVariantFromValue<QObject *>(m_effectSource));
+ m_effectComponent->completeCreate();
}
void QQuickItemLayer::deactivateEffect()
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 01e8b4d335..89c09ed015 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -302,6 +302,8 @@ public:
Parent = 0x20,
Children = 0x40,
Rotation = 0x80,
+ ImplicitWidth = 0x100,
+ ImplicitHeight = 0x200
};
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 3a5c25f5f5..cbdfb2b18b 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -74,6 +74,8 @@ public:
virtual void itemChildRemoved(QQuickItem *, QQuickItem *) {}
virtual void itemParentChanged(QQuickItem *, QQuickItem *) {}
virtual void itemRotationChanged(QQuickItem *) {}
+ virtual void itemImplicitWidthChanged(QQuickItem *) {}
+ virtual void itemImplicitHeightChanged(QQuickItem *) {}
virtual QQuickAnchorsPrivate *anchorPrivate() { return 0; }
};
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 6f33545185..1cc2637fe6 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -577,7 +577,7 @@ void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
if (sectionCriteria && sectionCriteria->delegate()) {
- if (item->attached->m_prevSection != item->attached->m_section)
+ if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
updateInlineSection(static_cast<FxListItemSG*>(item));
}
}
@@ -962,7 +962,7 @@ void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
{
if (!sectionCriteria || !sectionCriteria->delegate())
return;
- if (listItem->attached->m_prevSection != listItem->attached->m_section
+ if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
&& (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
|| (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
if (!listItem->section()) {
@@ -1022,7 +1022,7 @@ void QQuickListViewPrivate::updateStickySections()
if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
if (!currentSectionItem) {
currentSectionItem = getSectionItem(currentSection);
- } else if (currentStickySection != currentSection) {
+ } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
context->setContextProperty(QLatin1String("section"), currentSection);
}
@@ -1055,7 +1055,7 @@ void QQuickListViewPrivate::updateStickySections()
if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
if (!nextSectionItem) {
nextSectionItem = getSectionItem(nextSection);
- } else if (nextStickySection != nextSection) {
+ } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
context->setContextProperty(QLatin1String("section"), nextSection);
}
@@ -2040,6 +2040,9 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
sections, etc. for an address book)
\endlist
+ A case insensitive comparison is used when determining section
+ boundaries.
+
\c section.delegate holds the delegate component for each section.
\c section.labelPositioning determines whether the current and/or
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index f41ba44943..864e03bf43 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -52,9 +52,11 @@
QT_BEGIN_NAMESPACE
+static const QQuickItemPrivate::ChangeTypes watchedChanges
+ = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
+
QQuickLoaderPrivate::QQuickLoaderPrivate()
: item(0), component(0), itemContext(0), incubator(0), updatingSize(false),
- itemWidthValid(false), itemHeightValid(false),
active(true), loadingFromSource(false), asynchronous(false)
{
}
@@ -69,16 +71,23 @@ QQuickLoaderPrivate::~QQuickLoaderPrivate()
void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (resizeItem == item) {
- if (!updatingSize && newGeometry.width() != oldGeometry.width())
- itemWidthValid = true;
- if (!updatingSize && newGeometry.height() != oldGeometry.height())
- itemHeightValid = true;
+ if (resizeItem == item)
_q_updateSize(false);
- }
QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
}
+void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
+{
+ Q_Q(QQuickLoader);
+ q->setImplicitWidth(getImplicitWidth());
+}
+
+void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *)
+{
+ Q_Q(QQuickLoader);
+ q->setImplicitHeight(getImplicitHeight());
+}
+
void QQuickLoaderPrivate::clear()
{
Q_Q(QQuickLoader);
@@ -103,7 +112,7 @@ void QQuickLoaderPrivate::clear()
if (item) {
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+ p->removeItemChangeListener(this, watchedChanges);
// We can't delete immediately because our item may have triggered
// the Loader to load a different item.
@@ -117,14 +126,32 @@ void QQuickLoaderPrivate::clear()
void QQuickLoaderPrivate::initResize()
{
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
- // We may override the item's size, so we need to remember
- // whether the item provided its own valid size.
- itemWidthValid = p->widthValid;
- itemHeightValid = p->heightValid;
+ p->addItemChangeListener(this, watchedChanges);
_q_updateSize();
}
+qreal QQuickLoaderPrivate::getImplicitWidth() const
+{
+ Q_Q(const QQuickLoader);
+ // If the Loader has a valid width then Loader has set an explicit width on the
+ // item, and we want the item's implicitWidth. If the Loader's width has
+ // not been set then its implicitWidth is the width of the item.
+ if (item)
+ return q->widthValid() ? item->implicitWidth() : item->width();
+ return QQuickImplicitSizeItemPrivate::getImplicitWidth();
+}
+
+qreal QQuickLoaderPrivate::getImplicitHeight() const
+{
+ Q_Q(const QQuickLoader);
+ // If the Loader has a valid height then Loader has set an explicit height on the
+ // item, and we want the item's implicitHeight. If the Loader's height has
+ // not been set then its implicitHeight is the height of the item.
+ if (item)
+ return q->heightValid() ? item->implicitHeight() : item->height();
+ return QQuickImplicitSizeItemPrivate::getImplicitHeight();
+}
+
/*!
\qmlclass Loader QQuickLoader
\inqmlmodule QtQuick 2
@@ -245,7 +272,7 @@ QQuickLoader::~QQuickLoader()
Q_D(QQuickLoader);
if (d->item) {
QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
- p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+ p->removeItemChangeListener(d, watchedChanges);
}
}
@@ -282,9 +309,16 @@ void QQuickLoader::setActive(bool newVal)
loadFromSourceComponent();
}
} else {
+ // cancel any current incubation
+ if (d->incubator) {
+ d->incubator->clear();
+ delete d->itemContext;
+ d->itemContext = 0;
+ }
+
if (d->item) {
QQuickItemPrivate *p = QQuickItemPrivate::get(d->item);
- p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+ p->removeItemChangeListener(d, watchedChanges);
// We can't delete immediately because our item may have triggered
// the Loader to load a different item.
@@ -553,6 +587,14 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj)
QQuickItem *item = qobject_cast<QQuickItem*>(obj);
if (item) {
+ // If the item doesn't have an explicit size, but the Loader
+ // does, then set the item's size now before bindings are
+ // evaluated, otherwise we will end up resizing the item
+ // later and triggering any affected bindings/anchors.
+ if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
+ item->setWidth(q->width());
+ if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
+ item->setHeight(q->height());
QQml_setParent_noEvent(itemContext, obj);
QQml_setParent_noEvent(item, q);
item->setParentItem(q);
@@ -607,7 +649,8 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
emit q->sourceComponentChanged();
emit q->statusChanged();
emit q->progressChanged();
- emit q->loaded();
+ if (status == QQmlIncubator::Ready)
+ emit q->loaded();
disposeInitialPropertyValues(); // cleanup
}
@@ -812,15 +855,13 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
updatingSize = true;
- qreal iWidth = !itemWidthValid ? item->implicitWidth() : item->width();
- qreal iHeight = !itemHeightValid ? item->implicitHeight() : item->height();
- q->setImplicitSize(iWidth, iHeight);
-
if (loaderGeometryChanged && q->widthValid())
item->setWidth(q->width());
if (loaderGeometryChanged && q->heightValid())
item->setHeight(q->height());
+ q->setImplicitSize(getImplicitWidth(), getImplicitHeight());
+
updatingSize = false;
}
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 1ad7756ed8..0978911315 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -87,6 +87,8 @@ public:
~QQuickLoaderPrivate();
void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
+ void itemImplicitWidthChanged(QQuickItem *);
+ void itemImplicitHeightChanged(QQuickItem *);
void clear();
void initResize();
void load();
@@ -97,6 +99,9 @@ public:
QUrl resolveSourceUrl(QQmlV8Function *args);
v8::Handle<v8::Object> extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error);
+ virtual qreal getImplicitWidth() const;
+ virtual qreal getImplicitHeight() const;
+
QUrl source;
QQuickItem *item;
QQmlComponent *component;
@@ -105,8 +110,6 @@ public:
v8::Persistent<v8::Object> initialPropertyValues;
v8::Persistent<v8::Object> qmlGlobalForIpv;
bool updatingSize: 1;
- bool itemWidthValid : 1;
- bool itemHeightValid : 1;
bool active : 1;
bool loadingFromSource : 1;
bool asynchronous : 1;
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 110cc6ad7a..2ba7b80748 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -236,7 +236,7 @@ void QQuickTouchPoint::setSceneY(qreal sceneY)
\list
\li setting \c touchPoints to provide touch point objects with properties that can be bound to
- \li using the onTouchUpdated or onTouchPointsPressed, onTouchPointsUpdated and onTouchPointsReleased handlers
+ \li using the onTouchUpdated or onPressed, onUpdated and onReleased handlers
\endlist
While a MultiPointTouchArea \e can take exclusive ownership of certain touch points, it is also possible to have
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 962dddafc9..836943c478 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -58,13 +58,13 @@
// The number of samples to use in calculating the velocity of a flick
#ifndef QML_FLICK_SAMPLEBUFFER
-#define QML_FLICK_SAMPLEBUFFER 3
+#define QML_FLICK_SAMPLEBUFFER 1
#endif
// The number of samples to discard when calculating the flick velocity.
// Touch panels often produce inaccurate results as the finger is lifted.
#ifndef QML_FLICK_DISCARDSAMPLES
-#define QML_FLICK_DISCARDSAMPLES 1
+#define QML_FLICK_DISCARDSAMPLES 0
#endif
// The default maximum velocity of a flick.
@@ -114,8 +114,8 @@ void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &va
}
QQuickPathViewPrivate::QQuickPathViewPrivate()
- : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0)
- , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0)
+ : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0)
+ , offset(0.0), offsetAdj(0.0), mappedRange(1.0)
, stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
, moving(false), flicking(false), requestedOnPath(false), inRequest(false)
@@ -127,7 +127,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, highlightPosition(0)
, highlightRangeStart(0), highlightRangeEnd(0)
, highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
- , highlightMoveDuration(300), modelCount(0)
+ , highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
{
}
@@ -139,7 +139,7 @@ void QQuickPathViewPrivate::init()
q->setFlag(QQuickItem::ItemIsFocusScope);
q->setFiltersChildMouseEvents(true);
FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked()))
- lastPosTime.invalidate();
+ timer.invalidate();
FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding()))
}
@@ -266,7 +266,8 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
if (model && index >= 0 && index < modelCount) {
qreal start = 0.0;
- if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange)
+ if (haveHighlightRange && (highlightRangeMode != QQuickPathView::NoHighlightRange
+ || snapMode != QQuickPathView::NoSnap))
start = highlightRangeStart;
qreal globalPos = index + offset;
globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
@@ -698,7 +699,7 @@ void QQuickPathView::setCurrentIndex(int idx)
if (d->modelCount) {
d->createCurrentItem();
if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
- d->snapToCurrent();
+ d->snapToIndex(d->currentIndex);
d->currentItemOffset = d->positionOfIndex(d->currentIndex);
d->updateHighlight();
}
@@ -889,7 +890,7 @@ void QQuickPathView::setPreferredHighlightBegin(qreal start)
if (d->highlightRangeStart == start || start < 0 || start > 1.0)
return;
d->highlightRangeStart = start;
- d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
refill();
emit preferredHighlightBeginChanged();
}
@@ -906,7 +907,7 @@ void QQuickPathView::setPreferredHighlightEnd(qreal end)
if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
return;
d->highlightRangeEnd = end;
- d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
refill();
emit preferredHighlightEndChanged();
}
@@ -923,10 +924,12 @@ void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode)
if (d->highlightRangeMode == mode)
return;
d->highlightRangeMode = mode;
- d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd;
if (d->haveHighlightRange) {
d->regenerate();
- d->snapToCurrent();
+ int index = d->highlightRangeMode != NoHighlightRange ? d->currentIndex : d->calcCurrentIndex();
+ if (index >= 0)
+ d->snapToIndex(index);
}
emit highlightRangeModeChanged();
}
@@ -1175,6 +1178,41 @@ void QQuickPathView::setPathItemCount(int i)
emit pathItemCountChanged();
}
+/*!
+ \qmlproperty enumeration QtQuick2::PathView::snapMode
+
+ This property determines how the items will settle following a drag or flick.
+ The possible values are:
+
+ \list
+ \li PathView.NoSnap (default) - the items stop anywhere along the path.
+ \li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin.
+ \li PathView.SnapOneItem - the items settle no more than one item away from the item nearest
+ \l preferredHighlightBegin at the time the press is released. This mode is particularly
+ useful for moving one page at a time.
+ \endlist
+
+ \c snapMode does not affect the \l currentIndex. To update the
+ \l currentIndex as the view is moved, set \l highlightRangeMode
+ to \c PathView.StrictlyEnforceRange (default for PathView).
+
+ \sa highlightRangeMode
+*/
+QQuickPathView::SnapMode QQuickPathView::snapMode() const
+{
+ Q_D(const QQuickPathView);
+ return d->snapMode;
+}
+
+void QQuickPathView::setSnapMode(SnapMode mode)
+{
+ Q_D(QQuickPathView);
+ if (mode == d->snapMode)
+ return;
+ d->snapMode = mode;
+ emit snapModeChanged();
+}
+
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
qreal samples = qMin(path->path().length()/5, qreal(500.0));
@@ -1236,6 +1274,15 @@ qreal QQuickPathViewPrivate::calcVelocity() const
return velocity;
}
+qint64 QQuickPathViewPrivate::computeCurrentTime(QInputEvent *event)
+{
+ if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1) {
+ return event->timestamp();
+ }
+
+ return QQuickItemPrivate::elapsed(timer);
+}
+
void QQuickPathView::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickPathView);
@@ -1277,9 +1324,8 @@ void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
else
stealMouse = false;
- lastElapsed = 0;
- lastDist = 0;
- QQuickItemPrivate::start(lastPosTime);
+ QQuickItemPrivate::start(timer);
+ lastPosTime = computeCurrentTime(event);
tl.clear();
}
@@ -1299,7 +1345,7 @@ void QQuickPathView::mouseMoveEvent(QMouseEvent *event)
void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
{
Q_Q(QQuickPathView);
- if (!interactive || !lastPosTime.isValid() || !model || !modelCount)
+ if (!interactive || !timer.isValid() || !model || !modelCount)
return;
qreal newPc;
@@ -1308,10 +1354,10 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
QPointF delta = pathPoint - startPoint;
if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) {
stealMouse = true;
- startPc = newPc;
}
}
+ qint64 currentTimestamp = computeCurrentTime(event);
if (stealMouse) {
moveReason = QQuickPathViewPrivate::Mouse;
qreal diff = (newPc - startPc)*modelCount*mappedRange;
@@ -1323,10 +1369,9 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
else if (diff < -modelCount/2)
diff += modelCount;
- lastElapsed = QQuickItemPrivate::restart(lastPosTime);
- lastDist = diff;
- startPc = newPc;
- addVelocitySample(diff / (qreal(lastElapsed) / 1000.));
+ qint64 elapsed = currentTimestamp - lastPosTime;
+ if (elapsed > 0)
+ addVelocitySample(diff / (qreal(elapsed) / 1000.));
}
if (!moving) {
moving = true;
@@ -1334,6 +1379,8 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event)
emit q->movementStarted();
}
}
+ startPc = newPc;
+ lastPosTime = currentTimestamp;
}
void QQuickPathView::mouseReleaseEvent(QMouseEvent *event)
@@ -1353,8 +1400,8 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
Q_Q(QQuickPathView);
stealMouse = false;
q->setKeepMouseGrab(false);
- if (!interactive || !lastPosTime.isValid() || !model || !modelCount) {
- lastPosTime.invalidate();
+ if (!interactive || !timer.isValid() || !model || !modelCount) {
+ timer.invalidate();
if (!tl.isActive())
q->movementEnding();
return;
@@ -1364,7 +1411,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
qreal count = modelCount*mappedRange;
qreal pixelVelocity = (path->path().length()/count) * velocity;
if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
- if (qAbs(pixelVelocity) > maximumFlickVelocity) {
+ if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
velocity = maxVel / (path->path().length()/count);
@@ -1373,14 +1420,24 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
qreal v2 = velocity*velocity;
qreal accel = deceleration/10;
qreal dist = 0;
- if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
- // + 0.25 to encourage moving at least one item in the flick direction
- dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
- // round to nearest item.
- if (velocity > 0.)
- dist = qRound(dist + offset) - offset;
- else
- dist = qRound(dist - offset) + offset;
+ if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
+ || snapMode != QQuickPathView::NoSnap)) {
+ if (snapMode == QQuickPathView::SnapOneItem) {
+ // encourage snapping one item in direction of motion
+ if (velocity > 0.)
+ dist = qRound(0.5 + offset) - offset;
+ else
+ dist = qRound(0.5 - offset) + offset;
+ } else {
+ // + 0.25 to encourage moving at least one item in the flick direction
+ dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
+
+ // round to nearest item.
+ if (velocity > 0.)
+ dist = qRound(dist + offset) - offset;
+ else
+ dist = qRound(dist - offset) + offset;
+ }
// Calculate accel required to stop on item boundary
if (dist <= 0.) {
dist = 0.;
@@ -1405,7 +1462,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
fixOffset();
}
- lastPosTime.invalidate();
+ timer.invalidate();
if (!tl.isActive())
q->movementEnding();
}
@@ -1441,8 +1498,8 @@ bool QQuickPathView::sendMouseEvent(QMouseEvent *event)
grabMouse();
return d->stealMouse;
- } else if (d->lastPosTime.isValid()) {
- d->lastPosTime.invalidate();
+ } else if (d->timer.isValid()) {
+ d->timer.invalidate();
d->fixOffset();
}
if (event->type() == QEvent::MouseButtonRelease)
@@ -1476,7 +1533,7 @@ void QQuickPathView::mouseUngrabEvent()
// fix our state
d->stealMouse = false;
setKeepMouseGrab(false);
- d->lastPosTime.invalidate();
+ d->timer.invalidate();
d->fixOffset();
if (!d->tl.isActive())
movementEnding();
@@ -1557,7 +1614,8 @@ void QQuickPathView::refill()
if (d->items.count() < count) {
int idx = qRound(d->modelCount - d->offset) % d->modelCount;
qreal startPos = 0.0;
- if (d->haveHighlightRange && d->highlightRangeMode != QQuickPathView::NoHighlightRange)
+ if (d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange
+ || d->snapMode != QQuickPathView::NoSnap))
startPos = d->highlightRangeStart;
if (d->firstIndex >= 0) {
startPos = d->positionOfIndex(d->firstIndex);
@@ -1833,22 +1891,23 @@ void QQuickPathViewPrivate::fixOffset()
{
Q_Q(QQuickPathView);
if (model && items.count()) {
- if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) {
+ if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange
+ || snapMode != QQuickPathView::NoSnap)) {
int curr = calcCurrentIndex();
- if (curr != currentIndex)
+ if (curr != currentIndex && highlightRangeMode == QQuickPathView::StrictlyEnforceRange)
q->setCurrentIndex(curr);
else
- snapToCurrent();
+ snapToIndex(curr);
}
}
}
-void QQuickPathViewPrivate::snapToCurrent()
+void QQuickPathViewPrivate::snapToIndex(int index)
{
if (!model || modelCount <= 0)
return;
- qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
+ qreal targetOffset = qmlMod(modelCount - index, modelCount);
if (offset == targetOffset)
return;
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index 8b15e85b8a..2c0c106113 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -83,8 +83,10 @@ class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged)
Q_ENUMS(HighlightRangeMode)
+ Q_ENUMS(SnapMode)
public:
QQuickPathView(QQuickItem *parent=0);
@@ -144,6 +146,10 @@ public:
int pathItemCount() const;
void setPathItemCount(int);
+ enum SnapMode { NoSnap, SnapToItem, SnapOneItem };
+ SnapMode snapMode() const;
+ void setSnapMode(SnapMode mode);
+
static QQuickPathViewAttached *qmlAttachedProperties(QObject *);
public Q_SLOTS:
@@ -176,6 +182,7 @@ Q_SIGNALS:
void movementEnded();
void flickStarted();
void flickEnded();
+ void snapModeChanged();
protected:
virtual void updatePolish();
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index ac74e9a568..3285b40036 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -122,10 +122,11 @@ public:
void setAdjustedOffset(qreal offset);
void regenerate();
void updateItem(QQuickItem *, qreal);
- void snapToCurrent();
+ void snapToIndex(int index);
QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const;
void addVelocitySample(qreal v);
qreal calcVelocity() const;
+ qint64 computeCurrentTime(QInputEvent *event);
QQuickPath *path;
int currentIndex;
@@ -133,8 +134,6 @@ public:
qreal currentItemOffset;
qreal startPc;
QPointF startPoint;
- qreal lastDist;
- int lastElapsed;
qreal offset;
qreal offsetAdj;
qreal mappedRange;
@@ -149,7 +148,8 @@ public:
bool flicking : 1;
bool requestedOnPath : 1;
bool inRequest : 1;
- QElapsedTimer lastPosTime;
+ QElapsedTimer timer;
+ qint64 lastPosTime;
QPointF lastPos;
qreal dragMargin;
qreal deceleration;
@@ -180,6 +180,7 @@ public:
int highlightMoveDuration;
int modelCount;
QPODVector<qreal,10> velocityBuffer;
+ QQuickPathView::SnapMode snapMode;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index e232746417..1549057ef8 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -88,6 +88,446 @@ 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)
+ {
+ 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 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 (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 *>(qVariantValue<QObject *>(d.value));
+ if (source) {
+ if (item->canvas())
+ QQuickItemPrivate::get(source)->derefCanvas();
+ 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());
+ QByteArray signalName("2");
+ signalName.append(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 *>(qVariantValue<QObject *>(d.value));
+ if (source) {
+ if (item->canvas())
+ QQuickItemPrivate::get(source)->refCanvas(item->canvas());
+ 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, typeLength, nameIndex, nameLength;
+ const char *s = code.constData();
+ VariableQualifier decl;
+ while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
+ nameIndex, nameLength)) != -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;
+
+ 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 {
+ 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).second;
+ if (t) {
+ QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ }
+ }
+ material->textureProviders.clear();
+
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ const UniformData &d = uniformData[shaderType].at(i);
+ // First make room in the textureProviders array. Set to proper value further down.
+ if (d.specialType == UniformData::Sampler)
+ material->textureProviders.append(qMakePair(d.name, (QSGTextureProvider *)0));
+ }
+ material->uniforms[shaderType] = uniformData[shaderType];
+ }
+ 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;
+ Q_ASSERT(material->textureProviders.at(index).first == d.name);
+ QSGTextureProvider *oldProvider = material->textureProviders.at(index).second;
+ QSGTextureProvider *newProvider = 0;
+ QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<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].second = newProvider;
+ }
+ ++index;
+ }
+ }
+ Q_ASSERT(index == material->textureProviders.size());
+ }
+}
+
+void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas)
+{
+ // See comment in QQuickShaderEffectCommon::propertyChanged().
+ if (canvas) {
+ 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 *>(qVariantValue<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->refCanvas(canvas);
+ }
+ }
+ }
+ } 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 *>(qVariantValue<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->derefCanvas();
+ }
+ }
+ }
+ }
+}
+
+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 && qVariantCanConvert<QObject *>(d.value)) {
+ if (qVariantValue<QObject *>(d.value) == object)
+ d.value = QVariant();
+ }
+ }
+ }
+}
+
+
+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 *>(qVariantValue<QObject *>(d.value));
+ if (source) {
+ if (item->canvas())
+ QQuickItemPrivate::get(source)->derefCanvas();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ d.value = item->property(d.name.constData());
+
+ source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value));
+ if (source) {
+ // 'source' needs a canvas to get a scene graph node. It usually gets one through its
+ // parent, but if the source item is "inline" rather than a reference -- i.e.
+ // "property variant source: Image { }" instead of "property variant source: foo" -- it
+ // will not get a parent. In those cases, 'source' should get the canvas from 'item'.
+ if (item->canvas())
+ QQuickItemPrivate::get(source)->refCanvas(item->canvas());
+ 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;
+ }
+}
+
+
/*!
\qmlclass ShaderEffect QQuickShaderEffect
\inqmlmodule QtQuick 2
@@ -187,18 +627,21 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
, m_cullMode(NoCulling)
, m_status(Uncompiled)
, m_blending(true)
- , m_dirtyData(true)
- , m_programDirty(true)
+ , m_dirtyUniforms(true)
+ , m_dirtyUniformValues(true)
+ , m_dirtyTextureProviders(true)
+ , m_dirtyProgram(true)
+ , m_dirtyParseLog(true)
, m_dirtyMesh(true)
, m_dirtyGeometry(true)
- , m_complete(false)
{
setFlag(QQuickItem::ItemHasContents);
}
QQuickShaderEffect::~QQuickShaderEffect()
{
- reset();
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
}
/*!
@@ -211,11 +654,16 @@ QQuickShaderEffect::~QQuickShaderEffect()
void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
{
- if (m_source.fragmentCode.constData() == code.constData())
+ if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
return;
- m_source.fragmentCode = code;
+ m_common.source.sourceCode[Key::FragmentShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+
+ if (isComponentComplete())
+ m_common.updateShader(this, Key::FragmentShader);
+
update();
- m_complete = false;
if (m_status != Uncompiled) {
m_status = Uncompiled;
emit statusChanged();
@@ -234,11 +682,16 @@ void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
void QQuickShaderEffect::setVertexShader(const QByteArray &code)
{
- if (m_source.vertexCode.constData() == code.constData())
+ if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
return;
- m_source.vertexCode = code;
+ m_common.source.sourceCode[Key::VertexShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+
+ if (isComponentComplete())
+ m_common.updateShader(this, Key::VertexShader);
+
update();
- m_complete = false;
if (m_status != Uncompiled) {
m_status = Uncompiled;
emit statusChanged();
@@ -316,6 +769,7 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
}
m_dirtyMesh = true;
+ m_dirtyParseLog = true;
update();
emit meshChanged();
}
@@ -343,6 +797,34 @@ void QQuickShaderEffect::setCullMode(CullMode face)
emit cullModeChanged();
}
+QString QQuickShaderEffect::parseLog()
+{
+ if (m_dirtyParseLog) {
+ m_common.updateParseLog(m_mesh != 0);
+ m_dirtyParseLog = false;
+ }
+ return m_common.parseLog;
+}
+
+bool QQuickShaderEffect::event(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(this, (shaderType << 16) | i, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ update();
+ }
+ }
+ }
+ }
+ return QQuickItem::event(event);
+}
+
/*!
\qmlproperty enumeration QtQuick2::ShaderEffect::status
@@ -371,19 +853,6 @@ void QQuickShaderEffect::setCullMode(CullMode face)
\sa status
*/
-void QQuickShaderEffect::changeSource(int index)
-{
- Q_ASSERT(index >= 0 && index < m_sources.size());
- QVariant v = property(m_sources.at(index).name.constData());
- setSource(v, index);
-}
-
-void QQuickShaderEffect::updateData()
-{
- m_dirtyData = true;
- update();
-}
-
void QQuickShaderEffect::updateGeometry()
{
m_dirtyGeometry = true;
@@ -392,7 +861,7 @@ void QQuickShaderEffect::updateGeometry()
void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
{
- m_log = m_parseLog + log;
+ m_log = parseLog() + log;
m_status = Status(status);
emit logChanged();
emit statusChanged();
@@ -400,341 +869,17 @@ void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
void QQuickShaderEffect::sourceDestroyed(QObject *object)
{
- for (int i = 0; i < m_sources.size(); ++i) {
- SourceData &source = m_sources[i];
- if (object == source.sourceObject)
- source.sourceObject = 0;
- }
-}
-
-void QQuickShaderEffect::setSource(const QVariant &var, int index)
-{
- Q_ASSERT(index >= 0 && index < m_sources.size());
-
- SourceData &source = m_sources[index];
-
- if (source.sourceObject) {
- if (canvas())
- QQuickItemPrivate::get(source.sourceObject)->derefCanvas();
- disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
- }
-
- source.sourceObject = 0;
- if (var.isNull()) {
- return;
- } else if (!qVariantCanConvert<QObject *>(var)) {
- qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
- return;
- }
-
- QObject *obj = qVariantValue<QObject *>(var);
- if (!obj)
- return;
- QQuickItem *item = qobject_cast<QQuickItem *>(obj);
- if (!item || !item->isTextureProvider()) {
- qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
- source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
- return;
- }
-
- source.sourceObject = item;
-
- if (item) {
- // 'item' needs a canvas to get a scene graph node. It usually gets one through its
- // parent, but if the source item is "inline" rather than a reference -- i.e.
- // "property variant source: Image { }" instead of "property variant source: foo" -- it
- // will not get a parent. In those cases, 'item' should get the canvas from 'this'.
- if (canvas())
- QQuickItemPrivate::get(item)->refCanvas(canvas());
- connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
- }
-}
-
-void QQuickShaderEffect::disconnectPropertySignals()
-{
- disconnect(this, 0, this, SLOT(updateData()));
- for (int i = 0; i < m_sources.size(); ++i) {
- SourceData &source = m_sources[i];
- disconnect(this, 0, source.mapper, 0);
- disconnect(source.mapper, 0, this, 0);
- }
-}
-
-void QQuickShaderEffect::connectPropertySignals()
-{
- QSet<QByteArray>::const_iterator it;
- for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
- int pi = metaObject()->indexOfProperty(it->constData());
- if (pi >= 0) {
- QMetaProperty mp = metaObject()->property(pi);
- if (!mp.hasNotifySignal())
- qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData());
- QByteArray signalName("2");
- signalName.append(mp.notifySignal().methodSignature());
- connect(this, signalName, this, SLOT(updateData()));
- } else {
- // If the source is set via a dynamic property, like the layer is, then we need this check
- // to disable the warning.
- if (property(it->constData()).isValid())
- continue;
- qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
- }
- }
- for (int i = 0; i < m_sources.size(); ++i) {
- SourceData &source = m_sources[i];
- int pi = metaObject()->indexOfProperty(source.name.constData());
- if (pi >= 0) {
- QMetaProperty mp = metaObject()->property(pi);
- QByteArray signalName("2");
- signalName.append(mp.notifySignal().methodSignature());
- connect(this, signalName, source.mapper, SLOT(map()));
- source.mapper->setMapping(this, i);
- connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(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 (property(source.name.constData()).isValid())
- continue;
- qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
- }
- }
-}
-
-
-void QQuickShaderEffect::ensureCompleted()
-{
- if (!m_complete) {
- reset();
- updateProperties();
- m_complete = true;
- }
-}
-
-
-void QQuickShaderEffect::reset()
-{
- disconnectPropertySignals();
-
- m_source.attributeNames.clear();
- m_source.uniformNames.clear();
- m_source.respectsOpacity = false;
- m_source.respectsMatrix = false;
- m_source.className = metaObject()->className();
-
- for (int i = 0; i < m_sources.size(); ++i) {
- const SourceData &source = m_sources.at(i);
- delete source.mapper;
- if (source.sourceObject) {
- if (canvas())
- QQuickItemPrivate::get(source.sourceObject)->derefCanvas();
- disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
- }
- }
- m_sources.clear();
- m_log.clear();
- m_parseLog.clear();
- m_programDirty = true;
- m_dirtyMesh = true;
-}
-
-void QQuickShaderEffect::updateProperties()
-{
- if (m_source.vertexCode.isEmpty()) {
- m_source.attributeNames.append(QByteArray(qt_position_attribute_name));
- m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name));
- m_source.respectsMatrix = true;
- } else {
- lookThroughShaderCode(m_source.vertexCode);
- }
- if (m_source.fragmentCode.isEmpty()) {
- m_source.respectsOpacity = true;
- QByteArray name("source");
- m_source.uniformNames.insert(name);
- SourceData d;
- d.mapper = new QSignalMapper;
- d.name = name;
- d.sourceObject = 0;
- m_sources.append(d);
- } else {
- lookThroughShaderCode(m_source.fragmentCode);
- }
-
- if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) {
- m_parseLog += QLatin1String("Warning: Missing reference to \'");
- m_parseLog += QLatin1String(qt_position_attribute_name);
- m_parseLog += QLatin1String("\'.\n");
- }
- if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) {
- m_parseLog += QLatin1String("Warning: Missing reference to \'");
- m_parseLog += QLatin1String(qt_texcoord_attribute_name);
- m_parseLog += QLatin1String("\'.\n");
- }
- if (!m_source.respectsMatrix) {
- m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n");
- }
- if (!m_source.respectsOpacity) {
- m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n");
- }
-
- for (int i = 0; i < m_sources.size(); ++i) {
- QVariant v = property(m_sources.at(i).name);
- setSource(v, i);
- }
-
- connectPropertySignals();
+ m_common.sourceDestroyed(object);
}
-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)
- {
- 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 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 (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;
- }
-}
-void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code)
+void QQuickShaderEffect::propertyChanged(int mappedId)
{
- int index = 0;
- int typeIndex, typeLength, nameIndex, nameLength;
- const char *s = code.constData();
- VariableQualifier decl;
- while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
- nameIndex, nameLength)) != -1)
- {
- if (decl == AttributeQualifier) {
- m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength));
- } else {
- Q_ASSERT(decl == UniformQualifier);
-
- const int matLen = sizeof("qt_Matrix") - 1;
- const int opLen = sizeof("qt_Opacity") - 1;
- const int sampLen = sizeof("sampler2D") - 1;
-
- if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
- m_source.respectsMatrix = true;
- } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
- m_source.respectsOpacity = true;
- } else {
- QByteArray name(s + nameIndex, nameLength);
- m_source.uniformNames.insert(name);
- if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) {
- SourceData d;
- d.mapper = new QSignalMapper;
- d.name = name;
- d.sourceObject = 0;
- m_sources.append(d);
- }
- }
- }
- }
+ bool textureProviderChanged;
+ m_common.propertyChanged(this, mappedId, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ update();
}
void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
@@ -747,10 +892,8 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
{
QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
- ensureCompleted();
-
// In the case of a bad vertex shader, don't try to create a node...
- if (m_source.attributeNames.isEmpty()) {
+ if (m_common.attributes.isEmpty()) {
if (node)
delete node;
return 0;
@@ -758,14 +901,14 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
if (!node) {
node = new QQuickShaderEffectNode;
- m_programDirty = true;
- m_dirtyData = true;
+ 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)));
}
- QQuickShaderEffectMaterial *material = node->shaderMaterial();
-
if (m_dirtyMesh) {
node->setGeometry(0);
m_dirtyMesh = false;
@@ -778,11 +921,11 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
QRectF rect(0, 0, width(), height());
QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
- geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
+ geometry = mesh->updateGeometry(geometry, m_common.attributes, rect);
if (!geometry) {
QString log = mesh->log();
if (!log.isNull()) {
- m_log = m_parseLog;
+ m_log = parseLog();
m_log += QLatin1String("*** Mesh ***\n");
m_log += log;
m_status = Error;
@@ -799,18 +942,7 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
m_dirtyGeometry = false;
}
- if (m_programDirty) {
- QQuickShaderEffectProgram s = m_source;
- if (s.fragmentCode.isEmpty())
- s.fragmentCode = qt_default_fragment_code;
- if (s.vertexCode.isEmpty())
- s.vertexCode = qt_default_vertex_code;
- s.className = metaObject()->className();
-
- material->setProgramSource(s);
- node->markDirty(QSGNode::DirtyMaterial);
- m_programDirty = false;
- }
+ QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
// Update blending
if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
@@ -818,66 +950,48 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
node->markDirty(QSGNode::DirtyMaterial);
}
- if (int(material->cullMode()) != int(m_cullMode)) {
- material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode));
+ if (int(material->cullMode) != int(m_cullMode)) {
+ material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
node->markDirty(QSGNode::DirtyMaterial);
}
- if (m_dirtyData) {
- QVector<QPair<QByteArray, QVariant> > values;
- QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
- const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders();
+ if (m_dirtyProgram) {
+ Key s = m_common.source;
+ if (s.sourceCode[Key::FragmentShader].isEmpty())
+ s.sourceCode[Key::FragmentShader] = qt_default_fragment_code;
+ if (s.sourceCode[Key::VertexShader].isEmpty())
+ s.sourceCode[Key::VertexShader] = qt_default_vertex_code;
+ s.className = metaObject()->className();
- for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
- it != m_source.uniformNames.end(); ++it) {
- values.append(qMakePair(*it, property(*it)));
- }
- for (int i = 0; i < oldTextures.size(); ++i) {
- QSGTextureProvider *t = oldTextures.at(i).second;
- if (t) {
- disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- }
- for (int i = 0; i < m_sources.size(); ++i) {
- const SourceData &source = m_sources.at(i);
- QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
- textures.append(qMakePair(source.name, t));
- if (t) {
- Q_ASSERT_X(t->thread() == QThread::currentThread(),
- "QQuickShaderEffect::updatePaintNode",
- "Texture provider must belong to the rendering thread");
- connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- connect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- }
- material->setUniforms(values);
- material->setTextureProviders(textures);
+ material->setProgramSource(s);
+ material->attributes = m_common.attributes;
node->markDirty(QSGNode::DirtyMaterial);
- m_dirtyData = false;
+ 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;
}
return node;
}
+void QQuickShaderEffect::componentComplete()
+{
+ m_common.updateShader(this, Key::VertexShader);
+ m_common.updateShader(this, Key::FragmentShader);
+ QQuickItem::componentComplete();
+}
+
void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
{
- if (change == QQuickItem::ItemSceneChange) {
- // See comment in QQuickShaderEffect::setSource().
- if (value.canvas) {
- for (int i = 0; i < m_sources.size(); ++i) {
- if (m_sources.at(i).sourceObject)
- QQuickItemPrivate::get(m_sources.at(i).sourceObject)->refCanvas(value.canvas);
- }
- } else {
- for (int i = 0; i < m_sources.size(); ++i) {
- if (m_sources.at(i).sourceObject)
- QQuickItemPrivate::get(m_sources.at(i).sourceObject)->derefCanvas();
- }
- }
- }
+ if (change == QQuickItem::ItemSceneChange)
+ m_common.updateCanvas(value.canvas);
QQuickItem::itemChange(change, value);
}
-
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index db1e4e78c1..32f12fad30 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -62,6 +62,34 @@ class QSGContext;
class QSignalMapper;
class QQuickCustomMaterialShader;
+// Common class for QQuickShaderEffect and QQuickCustomParticle.
+struct 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 updateCanvas(QQuickCanvas *canvas);
+
+ // 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 Q_AUTOTEST_EXPORT QQuickShaderEffect : public QQuickItem
{
Q_OBJECT
@@ -93,10 +121,10 @@ public:
QQuickShaderEffect(QQuickItem *parent = 0);
~QQuickShaderEffect();
- QByteArray fragmentShader() const { return m_source.fragmentCode; }
+ QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
void setFragmentShader(const QByteArray &code);
- QByteArray vertexShader() const { return m_source.vertexCode; }
+ QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
void setVertexShader(const QByteArray &code);
bool blending() const { return m_blending; }
@@ -111,8 +139,9 @@ public:
QString log() const { return m_log; }
Status status() const { return m_status; }
- void ensureCompleted();
- QString parseLog() { return m_parseLog; }
+ QString parseLog();
+
+ virtual bool event(QEvent *);
Q_SIGNALS:
void fragmentShaderChanged();
@@ -126,27 +155,22 @@ Q_SIGNALS:
protected:
virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ virtual void componentComplete();
virtual void itemChange(ItemChange change, const ItemChangeData &value);
private Q_SLOTS:
- void changeSource(int index);
- void updateData();
void updateGeometry();
void updateLogAndStatus(const QString &log, int status);
void sourceDestroyed(QObject *object);
+ void propertyChanged(int mappedId);
private:
friend class QQuickCustomMaterialShader;
friend class QQuickShaderEffectNode;
- void setSource(const QVariant &var, int index);
- void disconnectPropertySignals();
- void connectPropertySignals();
- void reset();
- void updateProperties();
- void lookThroughShaderCode(const QByteArray &code);
+ typedef QQuickShaderEffectMaterialKey Key;
+ typedef QQuickShaderEffectMaterial::UniformData UniformData;
- QQuickShaderEffectProgram m_source;
QSize m_meshResolution;
QQuickShaderEffectMesh *m_mesh;
QQuickGridMesh m_defaultMesh;
@@ -154,23 +178,16 @@ private:
QString m_log;
Status m_status;
- struct SourceData
- {
- QSignalMapper *mapper;
- QQuickItem *sourceObject;
- QByteArray name;
- };
- QVector<SourceData> m_sources;
- QString m_parseLog;
+ QQuickShaderEffectCommon m_common;
uint m_blending : 1;
- uint m_dirtyData : 1;
-
- uint m_programDirty : 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_complete : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp
index c4b91844e0..a3cadb97a2 100644
--- a/src/quick/items/qquickshadereffectnode.cpp
+++ b/src/quick/items/qquickshadereffectnode.cpp
@@ -60,7 +60,6 @@ protected:
friend class QQuickShaderEffectNode;
virtual void compile();
- virtual void initialize();
virtual const char *vertexShader() const;
virtual const char *fragmentShader() const;
@@ -70,17 +69,15 @@ protected:
QString m_log;
bool m_compiled;
- QVector<int> m_uniformLocs;
- int m_opacityLoc;
- int m_matrixLoc;
- uint m_textureIndicesSet;
+ QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ uint m_initialized : 1;
};
QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
: m_key(key)
, m_attributes(attributes)
, m_compiled(false)
- , m_textureIndicesSet(false)
+ , m_initialized(false)
{
for (int i = 0; i < attributes.count(); ++i)
m_attributeNames.append(attributes.at(i).constData());
@@ -104,24 +101,25 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
: QQuickShaderEffect::Error);
}
- if (!m_textureIndicesSet) {
- for (int i = 0; i < material->m_textures.size(); ++i)
- program()->setUniformValue(material->m_textures.at(i).first.constData(), i);
- m_textureIndicesSet = true;
- }
+ if (!m_initialized) {
+ for (int i = 0; i < material->textureProviders.size(); ++i)
+ program()->setUniformValue(material->textureProviders.at(i).first.constData(), i);
- if (m_uniformLocs.size() != material->m_uniformValues.size()) {
- m_uniformLocs.reserve(material->m_uniformValues.size());
- for (int i = 0; i < material->m_uniformValues.size(); ++i) {
- const QByteArray &name = material->m_uniformValues.at(i).first;
- m_uniformLocs.append(program()->uniformLocation(name.constData()));
+ for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::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) {
+ const QByteArray &name = material->uniforms[shaderType].at(i).name;
+ m_uniformLocs[shaderType].append(program()->uniformLocation(name.constData()));
+ }
}
+ m_initialized = true;
}
QOpenGLFunctions *functions = state.context()->functions();
- for (int i = material->m_textures.size() - 1; i >= 0; --i) {
+ for (int i = material->textureProviders.size() - 1; i >= 0; --i) {
functions->glActiveTexture(GL_TEXTURE0 + i);
- if (QSGTextureProvider *provider = material->m_textures.at(i).second) {
+ if (QSGTextureProvider *provider = material->textureProviders.at(i).second) {
if (QSGTexture *texture = provider->texture()) {
texture->bind();
continue;
@@ -131,57 +129,67 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
glBindTexture(GL_TEXTURE_2D, 0);
}
- if (material->m_source.respectsOpacity)
- program()->setUniformValue(m_opacityLoc, state.opacity());
-
- for (int i = 0; i < material->m_uniformValues.count(); ++i) {
- const QVariant &v = material->m_uniformValues.at(i).second;
-
- switch (v.type()) {
- case QMetaType::QColor:
- program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast<QColor>(v)));
- break;
- case QMetaType::Float:
- program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<float>(v));
- break;
- case QMetaType::Double:
- program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast<double>(v));
- break;
- case QMetaType::QTransform:
- program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QTransform>(v));
- break;
- case QMetaType::Int:
- program()->setUniformValue(m_uniformLocs.at(i), v.toInt());
- break;
- case QMetaType::Bool:
- program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool()));
- break;
- case QMetaType::QSize:
- case QMetaType::QSizeF:
- program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF());
- break;
- case QMetaType::QPoint:
- case QMetaType::QPointF:
- program()->setUniformValue(m_uniformLocs.at(i), v.toPointF());
- break;
- case QMetaType::QRect:
- case QMetaType::QRectF:
- {
- QRectF r = v.toRectF();
- program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height());
+ for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
+ const QQuickShaderEffectMaterial::UniformData &d = material->uniforms[shaderType].at(i);
+ int loc = m_uniformLocs[shaderType].at(i);
+
+ if (d.specialType == QQuickShaderEffectMaterial::UniformData::Opacity) {
+ program()->setUniformValue(loc, state.opacity());
+ } if (d.specialType == QQuickShaderEffectMaterial::UniformData::Matrix) {
+ if (state.isMatrixDirty())
+ program()->setUniformValue(loc, state.combinedMatrix());
+ } else {
+ switch (d.value.type()) {
+ case QMetaType::QColor:
+ program()->setUniformValue(loc, qt_premultiply_color(qvariant_cast<QColor>(d.value)));
+ break;
+ case QMetaType::Float:
+ program()->setUniformValue(loc, qvariant_cast<float>(d.value));
+ break;
+ case QMetaType::Double:
+ program()->setUniformValue(loc, (float) qvariant_cast<double>(d.value));
+ break;
+ case QMetaType::QTransform:
+ program()->setUniformValue(loc, qvariant_cast<QTransform>(d.value));
+ break;
+ case QMetaType::Int:
+ program()->setUniformValue(loc, d.value.toInt());
+ break;
+ case QMetaType::Bool:
+ program()->setUniformValue(loc, GLint(d.value.toBool()));
+ break;
+ case QMetaType::QSize:
+ case QMetaType::QSizeF:
+ program()->setUniformValue(loc, d.value.toSizeF());
+ break;
+ case QMetaType::QPoint:
+ case QMetaType::QPointF:
+ program()->setUniformValue(loc, d.value.toPointF());
+ break;
+ case QMetaType::QRect:
+ case QMetaType::QRectF:
+ {
+ QRectF r = d.value.toRectF();
+ program()->setUniformValue(loc, r.x(), r.y(), r.width(), r.height());
+ }
+ break;
+ case QMetaType::QVector3D:
+ program()->setUniformValue(loc, qvariant_cast<QVector3D>(d.value));
+ break;
+ case QMetaType::QVector4D:
+ program()->setUniformValue(loc, qvariant_cast<QVector4D>(d.value));
+ break;
+ default:
+ break;
+ }
}
- break;
- case QMetaType::QVector3D:
- program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QVector3D>(v));
- break;
- default:
- break;
}
}
const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect);
- if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) {
- switch (material->cullMode()) {
+ if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) {
+ switch (material->cullMode) {
case QQuickShaderEffectMaterial::FrontFaceCulling:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
@@ -195,9 +203,6 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
break;
}
}
-
- if ((state.isMatrixDirty()) && material->m_source.respectsMatrix)
- program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
}
char const *const *QQuickCustomMaterialShader::attributeNames() const
@@ -277,38 +282,42 @@ void QQuickCustomMaterialShader::compile()
}
}
-void QQuickCustomMaterialShader::initialize()
-{
- m_opacityLoc = program()->uniformLocation("qt_Opacity");
- m_matrixLoc = program()->uniformLocation("qt_Matrix");
-}
-
const char *QQuickCustomMaterialShader::vertexShader() const
{
- return m_key.vertexCode.constData();
+ return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData();
}
const char *QQuickCustomMaterialShader::fragmentShader() const
{
- return m_key.fragmentCode.constData();
+ return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData();
}
bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const
{
- return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className;
+ if (className != other.className)
+ return false;
+ for (int shaderType = 0; shaderType < ShaderTypeCount; ++shaderType) {
+ if (sourceCode[shaderType] != other.sourceCode[shaderType])
+ return false;
+ }
+ return true;
}
uint qHash(const QQuickShaderEffectMaterialKey &key)
{
- return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className));
+ uint hash = qHash((void *)key.className);
+ typedef QQuickShaderEffectMaterialKey Key;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ hash = hash * 31337 + qHash(key.sourceCode[shaderType]);
+ return hash;
}
QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap;
QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
- : m_cullMode(NoCulling)
+ : cullMode(NoCulling)
, m_node(node)
, m_emittedLogChanged(false)
{
@@ -322,7 +331,7 @@ QSGMaterialType *QQuickShaderEffectMaterial::type() const
QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const
{
- return new QQuickCustomMaterialShader(m_source, m_source.attributeNames);
+ return new QQuickCustomMaterialShader(m_source, attributes);
}
int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const
@@ -330,17 +339,7 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const
return this - static_cast<const QQuickShaderEffectMaterial *>(other);
}
-void QQuickShaderEffectMaterial::setCullMode(QQuickShaderEffectMaterial::CullMode face)
-{
- m_cullMode = face;
-}
-
-QQuickShaderEffectMaterial::CullMode QQuickShaderEffectMaterial::cullMode() const
-{
- return m_cullMode;
-}
-
-void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgram &source)
+void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source)
{
m_source = source;
m_emittedLogChanged = false;
@@ -351,25 +350,10 @@ void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgra
}
}
-void QQuickShaderEffectMaterial::setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues)
-{
- m_uniformValues = uniformValues;
-}
-
-void QQuickShaderEffectMaterial::setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures)
-{
- m_textures = textures;
-}
-
-const QVector<QPair<QByteArray, QSGTextureProvider *> > &QQuickShaderEffectMaterial::textureProviders() const
-{
- return m_textures;
-}
-
void QQuickShaderEffectMaterial::updateTextures() const
{
- for (int i = 0; i < m_textures.size(); ++i) {
- if (QSGTextureProvider *provider = m_textures.at(i).second) {
+ for (int i = 0; i < textureProviders.size(); ++i) {
+ if (QSGTextureProvider *provider = textureProviders.at(i).second) {
if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(provider->texture()))
texture->updateTexture();
}
@@ -378,18 +362,16 @@ void QQuickShaderEffectMaterial::updateTextures() const
void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
{
- for (int i = 0; i < m_textures.size(); ++i) {
- if (provider == m_textures.at(i).second)
- m_textures[i].second = 0;
+ for (int i = 0; i < textureProviders.size(); ++i) {
+ if (provider == textureProviders.at(i).second)
+ textureProviders[i].second = 0;
}
}
QQuickShaderEffectNode::QQuickShaderEffectNode()
- : m_material(this)
{
QSGNode::setFlag(UsePreprocess, true);
- setMaterial(&m_material);
#ifdef QML_RUNTIME_TESTING
description = QLatin1String("shadereffect");
@@ -407,8 +389,8 @@ void QQuickShaderEffectNode::markDirtyTexture()
void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object)
{
- Q_ASSERT(qobject_cast<QSGTextureProvider *>(object));
- m_material.invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
+ Q_ASSERT(material());
+ static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
}
void QQuickShaderEffectNode::preprocess()
diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h
index e22d2de9e2..1bbce86426 100644
--- a/src/quick/items/qquickshadereffectnode_p.h
+++ b/src/quick/items/qquickshadereffectnode_p.h
@@ -55,8 +55,14 @@ QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
struct QQuickShaderEffectMaterialKey {
- QByteArray vertexCode;
- QByteArray fragmentCode;
+ enum ShaderType
+ {
+ VertexShader,
+ FragmentShader,
+ ShaderTypeCount
+ };
+
+ QByteArray sourceCode[ShaderTypeCount];
const char *className;
bool operator == (const QQuickShaderEffectMaterialKey &other) const;
@@ -64,24 +70,21 @@ struct QQuickShaderEffectMaterialKey {
uint qHash(const QQuickShaderEffectMaterialKey &key);
-// TODO: Implement support for multisampling.
-struct QQuickShaderEffectProgram : public QQuickShaderEffectMaterialKey
-{
- QQuickShaderEffectProgram() : respectsOpacity(false), respectsMatrix(false) {}
-
- QVector<QByteArray> attributeNames;
- QSet<QByteArray> uniformNames;
-
- uint respectsOpacity : 1;
- uint respectsMatrix : 1;
-};
-
class QQuickCustomMaterialShader;
class QQuickShaderEffectNode;
class QQuickShaderEffectMaterial : public QSGMaterial
{
public:
+ struct UniformData
+ {
+ enum SpecialType { None, Sampler, Opacity, Matrix };
+
+ QByteArray name;
+ QVariant value;
+ SpecialType specialType;
+ };
+
enum CullMode
{
NoCulling,
@@ -94,13 +97,12 @@ public:
virtual QSGMaterialShader *createShader() const;
virtual int compare(const QSGMaterial *other) const;
- void setCullMode(CullMode face);
- CullMode cullMode() const;
+ QVector<QByteArray> attributes;
+ QVector<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ QVector<QPair<QByteArray, QSGTextureProvider *> > textureProviders;
+ CullMode cullMode;
- void setProgramSource(const QQuickShaderEffectProgram &);
- void setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues);
- void setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures);
- const QVector<QPair<QByteArray, QSGTextureProvider *> > &textureProviders() const;
+ void setProgramSource(const QQuickShaderEffectMaterialKey &source);
void updateTextures() const;
void invalidateTextureProvider(QSGTextureProvider *provider);
@@ -114,11 +116,8 @@ protected:
// one. To guarantee that the type pointer is unique, the type object must live as long as
// there are any CustomMaterialShaders of that type.
QSharedPointer<QSGMaterialType> m_type;
+ QQuickShaderEffectMaterialKey m_source;
- QQuickShaderEffectProgram m_source;
- QVector<QPair<QByteArray, QVariant> > m_uniformValues;
- QVector<QPair<QByteArray, QSGTextureProvider *> > m_textures;
- CullMode m_cullMode;
QQuickShaderEffectNode *m_node;
bool m_emittedLogChanged;
@@ -137,17 +136,12 @@ public:
virtual void preprocess();
- QQuickShaderEffectMaterial *shaderMaterial() { return &m_material; }
-
Q_SIGNALS:
void logAndStatusChanged(const QString &, int status);
private Q_SLOTS:
void markDirtyTexture();
void textureProviderDestroyed(QObject *object);
-
-private:
- QQuickShaderEffectMaterial m_material;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index d4ddbc400d..900cb84de8 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -389,6 +389,7 @@ QImage QQuickSpriteEngine::assembledImage()
else
qmlInfo(state) << "SpriteEngine: Animations too large to fit in one texture, pushed over the edge by:" << state->source().toLocalFile();
qmlInfo(state) << "SpriteEngine: Your texture max size today is " << maxSize;
+ return QImage();
}
state->m_generatedCount = rowsNeeded;
h += state->frameHeight() * rowsNeeded;
@@ -407,7 +408,7 @@ QImage QQuickSpriteEngine::assembledImage()
QPainter p(&image);
int y = 0;
foreach (QQuickSprite* state, m_sprites){
- QImage img(state->source().toLocalFile());
+ QImage img(state->m_pix.image());
int frameWidth = state->m_frameWidth;
int frameHeight = state->m_frameHeight;
if (img.height() == frameHeight && img.width() < maxSize){//Simple case
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index e1a28a466a..24dd10ac9f 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -748,7 +748,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
QTextLine line;
int visibleCount = 0;
bool elide;
- bool widthChanged;
qreal height = 0;
QString elideText;
bool once = true;
@@ -776,7 +775,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
bool truncateHeight = false;
truncated = false;
elide = false;
- widthChanged = false;
int characterCount = 0;
int unwrappedLineCount = 1;
int maxLineCount = maximumLineCount();
@@ -911,10 +909,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
const qreal oldWidth = lineWidth;
lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
- if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) {
- widthChanged = true;
+ if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
continue;
- }
}
// If the next needs to be elided and there's an abbreviated string available
@@ -2363,9 +2359,6 @@ void QQuickText::componentComplete()
QQuickItem::componentComplete();
if (d->updateOnComponentComplete)
d->updateLayout();
-
- // Enable accessibility for text items.
- d->setAccessibleFlagAndListener();
}
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index eefe938467..739b5f859b 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -44,6 +44,7 @@
#ifndef QT_NO_TEXTCONTROL
+#include <qcoreapplication.h>
#include <qfont.h>
#include <qpainter.h>
#include <qevent.h>
@@ -107,11 +108,12 @@ QQuickTextControlPrivate::QQuickTextControlPrivate()
ignoreAutomaticScrollbarAdjustement(false),
overwriteMode(false),
acceptRichText(true),
- hideCursor(false),
+ cursorVisible(false),
hasFocus(false),
isEnabled(true),
hadSelectionOnMousePress(false),
- wordSelectionEnabled(false)
+ wordSelectionEnabled(false),
+ hasImState(false)
{}
bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
@@ -292,6 +294,8 @@ void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString &
{
Q_Q(QQuickTextControl);
+ cancelPreedit();
+
// for use when called from setPlainText. we may want to re-use the currently
// set char format then.
const QTextCharFormat charFormatForInsertion = cursor.charFormat();
@@ -1441,13 +1445,16 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
QList<QTextLayout::FormatRange> overrides;
const int oldPreeditCursor = preeditCursor;
preeditCursor = e->preeditString().length();
- hideCursor = false;
+ hasImState = !e->preeditString().isEmpty();
+ cursorVisible = true;
for (int i = 0; i < e->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = e->attributes().at(i);
if (a.type == QInputMethodEvent::Cursor) {
+ hasImState = true;
preeditCursor = a.start;
- hideCursor = !a.length;
+ cursorVisible = a.length != 0;
} else if (a.type == QInputMethodEvent::TextFormat) {
+ hasImState = true;
QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
if (f.isValid()) {
QTextLayout::FormatRange o;
@@ -1514,6 +1521,37 @@ void QQuickTextControlPrivate::focusEvent(QFocusEvent *e)
}
}
+bool QQuickTextControl::hasImState() const
+{
+ Q_D(const QQuickTextControl);
+ return d->hasImState;
+}
+
+bool QQuickTextControl::cursorVisible() const
+{
+ Q_D(const QQuickTextControl);
+ return d->cursorVisible;
+}
+
+void QQuickTextControl::setCursorVisible(bool visible)
+{
+ Q_D(QQuickTextControl);
+ d->cursorVisible = visible;
+ d->setBlinkingCursorEnabled(d->cursorVisible
+ && (d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard)));
+}
+
+QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const
+{
+ Q_D(const QQuickTextControl);
+ int cursorPos = hitTest(pos, Qt::FuzzyHit);
+ if (cursorPos == -1)
+ cursorPos = 0;
+ QTextCursor c(d->doc);
+ c.setPosition(cursorPos);
+ return c;
+}
+
QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const
{
Q_D(const QQuickTextControl);
@@ -1727,21 +1765,31 @@ bool QQuickTextControlPrivate::isPreediting() const
void QQuickTextControlPrivate::commitPreedit()
{
- if (!isPreediting())
+ Q_Q(QQuickTextControl);
+
+ if (!hasImState)
return;
qApp->inputMethod()->commit();
- if (!isPreediting())
+ if (!hasImState)
return;
- cursor.beginEditBlock();
- preeditCursor = 0;
- QTextBlock block = cursor.block();
- QTextLayout *layout = block.layout();
- layout->setPreeditArea(-1, QString());
- layout->clearAdditionalFormats();
- cursor.endEditBlock();
+ QInputMethodEvent event;
+ QCoreApplication::sendEvent(q->parent(), &event);
+}
+
+void QQuickTextControlPrivate::cancelPreedit()
+{
+ Q_Q(QQuickTextControl);
+
+ if (!hasImState)
+ return;
+
+ qApp->inputMethod()->reset();
+
+ QInputMethodEvent event;
+ QCoreApplication::sendEvent(q->parent(), &event);
}
void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index 9e3fc90eb1..be3f7f7ccf 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -98,6 +98,10 @@ public:
QString toHtml() const;
#endif
+ bool hasImState() const;
+ bool cursorVisible() const;
+ void setCursorVisible(bool visible);
+ QTextCursor cursorForPosition(const QPointF &pos) const;
QRectF cursorRect(const QTextCursor &cursor) const;
QRectF cursorRect() const;
QRectF selectionRect(const QTextCursor &cursor) const;
diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h
index c5a39cc759..3a10f007be 100644
--- a/src/quick/items/qquicktextcontrol_p_p.h
+++ b/src/quick/items/qquicktextcontrol_p_p.h
@@ -127,6 +127,7 @@ public:
bool isPreediting() const;
void commitPreedit();
+ void cancelPreedit();
QPointF trippleClickPoint;
QPointF mousePressPos;
@@ -155,11 +156,12 @@ public:
bool ignoreAutomaticScrollbarAdjustement : 1;
bool overwriteMode : 1;
bool acceptRichText : 1;
- bool hideCursor : 1; // used to hide the cursor in the preedit area
+ bool cursorVisible : 1; // used to hide the cursor in the preedit area
bool hasFocus : 1;
bool isEnabled : 1;
bool hadSelectionOnMousePress : 1;
bool wordSelectionEnabled : 1;
+ bool hasImState : 1;
void _q_copyLink();
void _q_updateBlock(const QTextBlock &);
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 4fa5233b9a..f727c54322 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -871,10 +871,9 @@ void QQuickTextEdit::setCursorVisible(bool on)
if (d->cursorVisible == on)
return;
d->cursorVisible = on;
- QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
if (!on && !d->persistentSelection)
d->control->setCursorIsFocusIndicator(true);
- d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
+ d->control->setCursorVisible(on);
emit cursorVisibleChanged(d->cursorVisible);
}
@@ -1536,15 +1535,18 @@ void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
Q_D(QQuickTextEdit);
const bool wasComposing = isInputMethodComposing();
d->control->processEvent(event, QPointF(0, -d->yoff));
+ setCursorVisible(d->control->cursorVisible());
if (wasComposing != isInputMethodComposing())
emit inputMethodComposingChanged();
}
void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
{
+ Q_D(QQuickTextEdit);
if (change == ItemActiveFocusHasChanged) {
setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus());
-
+ QFocusEvent focusEvent(value.boolValue ? QEvent::FocusIn : QEvent::FocusOut);
+ d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
if (value.boolValue) {
q_updateAlignment();
connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
@@ -1729,9 +1731,7 @@ bool QQuickTextEdit::canRedo() const
bool QQuickTextEdit::isInputMethodComposing() const
{
Q_D(const QQuickTextEdit);
- if (QTextLayout *layout = d->control->textCursor().block().layout())
- return layout->preeditAreaText().length() > 0;
- return false;
+ return d->control->hasImState();
}
void QQuickTextEditPrivate::init()
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index f2da67bae7..5aa01d27f3 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -45,6 +45,7 @@
#include <private/qqmlglobal_p.h>
+#include <QtCore/qcoreapplication.h>
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QTextBoundaryFinder>
@@ -62,10 +63,6 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
-static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY;
-#endif
-
/*!
\qmlclass TextInput QQuickTextInput
\inqmlmodule QtQuick 2
@@ -128,8 +125,8 @@ void QQuickTextInput::setText(const QString &s)
Q_D(QQuickTextInput);
if (s == text())
return;
- if (d->composeMode())
- qApp->inputMethod()->reset();
+
+ d->cancelPreedit();
d->internalSetText(s, -1, false);
}
@@ -678,9 +675,7 @@ QRectF QQuickTextInput::cursorRectangle() const
{
Q_D(const QQuickTextInput);
- int c = d->m_cursor;
- if (d->m_preeditCursor != -1)
- c += d->m_preeditCursor;
+ int c = d->m_cursor + d->m_preeditCursor;
if (d->m_echoMode == NoEcho)
c = 0;
QTextLine l = d->m_textLayout.lineForTextPosition(c);
@@ -1402,7 +1397,7 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
{
Q_D(QQuickTextInput);
- const bool wasComposing = d->preeditAreaText().length() > 0;
+ const bool wasComposing = d->hasImState;
if (d->m_readOnly) {
ev->ignore();
} else {
@@ -1411,7 +1406,7 @@ void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
if (!ev->isAccepted())
QQuickImplicitSizeItem::inputMethodEvent(ev);
- if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
+ if (wasComposing != d->hasImState)
emit inputMethodComposingChanged();
}
@@ -1929,11 +1924,11 @@ void QQuickTextInput::redo()
void QQuickTextInput::insert(int position, const QString &text)
{
Q_D(QQuickTextInput);
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
- if (d->m_echoMode == QQuickTextInput::Password)
- d->m_passwordEchoTimer.start(qt_passwordEchoDelay, this);
-#endif
-
+ if (d->m_echoMode == QQuickTextInput::Password) {
+ int delay = qGuiApp->styleHints()->passwordMaskDelay();
+ if (delay > 0)
+ d->m_passwordEchoTimer.start(delay, this);
+ }
if (position < 0 || position > d->m_text.length())
return;
@@ -2484,11 +2479,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
if (change == ItemActiveFocusHasChanged) {
bool hasFocus = value.boolValue;
setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus()
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) {
-#else
- if (!hasFocus && d->m_passwordEchoEditing) {
-#endif
d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
}
@@ -2521,7 +2512,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
bool QQuickTextInput::isInputMethodComposing() const
{
Q_D(const QQuickTextInput);
- return d->preeditAreaText().length() > 0;
+ return d->hasImState;
}
void QQuickTextInputPrivate::init()
@@ -2660,7 +2651,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
if (m_echoMode == QQuickTextInput::Password) {
str.fill(m_passwordCharacter);
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.length()) {
int cursor = m_cursor - 1;
QChar uc = m_text.at(cursor);
@@ -2673,7 +2663,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
str[cursor - 1] = uc;
}
}
-#endif
} else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
str.fill(m_passwordCharacter);
}
@@ -2830,18 +2819,31 @@ void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
*/
void QQuickTextInputPrivate::commitPreedit()
{
- if (!composeMode())
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
return;
qApp->inputMethod()->commit();
- if (!composeMode())
+ if (!hasImState)
+ return;
+
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
+}
+
+void QQuickTextInputPrivate::cancelPreedit()
+{
+ Q_Q(QQuickTextInput);
+
+ if (!hasImState)
return;
- m_preeditCursor = 0;
- m_textLayout.setPreeditArea(-1, QString());
- m_textLayout.clearAdditionalFormats();
- updateLayout();
+ qApp->inputMethod()->reset();
+
+ QInputMethodEvent ev;
+ QCoreApplication::sendEvent(q, &ev);
}
/*!
@@ -3123,15 +3125,19 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
m_textLayout.setPreeditArea(m_cursor, event->preeditString());
#endif //QT_NO_IM
const int oldPreeditCursor = m_preeditCursor;
+ const bool oldCursorVisible = cursorVisible;
m_preeditCursor = event->preeditString().length();
- m_hideCursor = false;
+ hasImState = !event->preeditString().isEmpty();
+ cursorVisible = true;
QList<QTextLayout::FormatRange> formats;
for (int i = 0; i < event->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::Cursor) {
+ hasImState = true;
m_preeditCursor = a.start;
- m_hideCursor = !a.length;
+ cursorVisible = a.length != 0;
} else if (a.type == QInputMethodEvent::TextFormat) {
+ hasImState = true;
QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
if (f.isValid()) {
QTextLayout::FormatRange o;
@@ -3154,6 +3160,9 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
if (isGettingInput)
finishChange(priorState);
+ if (cursorVisible != oldCursorVisible)
+ emit q->cursorVisibleChanged(cursorVisible);
+
if (selectionChange) {
emit q->selectionChanged();
q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
@@ -3333,11 +3342,12 @@ void QQuickTextInputPrivate::addCommand(const Command &cmd)
*/
void QQuickTextInputPrivate::internalInsert(const QString &s)
{
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
Q_Q(QQuickTextInput);
- if (m_echoMode == QQuickTextInput::Password)
- m_passwordEchoTimer.start(qt_passwordEchoDelay, q);
-#endif
+ if (m_echoMode == QQuickTextInput::Password) {
+ int delay = qGuiApp->styleHints()->passwordMaskDelay();
+ if (delay > 0)
+ m_passwordEchoTimer.start(delay, q);
+ }
if (hasSelectedText())
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
if (m_maskData) {
@@ -3962,11 +3972,9 @@ void QQuickTextInput::timerEvent(QTimerEvent *event)
d->m_blinkStatus = !d->m_blinkStatus;
d->updateType = QQuickTextInputPrivate::UpdatePaintNode;
update();
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
} else if (event->timerId() == d->m_passwordEchoTimer.timerId()) {
d->m_passwordEchoTimer.stop();
d->updateDisplayText();
-#endif
}
}
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 3bd34b2661..1bc2cf548b 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -115,7 +115,7 @@ public:
, selectPressed(false)
, textLayoutDirty(true)
, persistentSelection(false)
- , m_hideCursor(false)
+ , hasImState(false)
, m_separator(0)
, m_readOnly(0)
, m_textDirty(0)
@@ -202,9 +202,7 @@ public:
QColor selectionColor;
QColor selectedTextColor;
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
QBasicTimer m_passwordEchoTimer;
-#endif
int lastSelectionStart;
int lastSelectionEnd;
int m_cursor;
@@ -247,7 +245,7 @@ public:
bool selectPressed:1;
bool textLayoutDirty:1;
bool persistentSelection:1;
- bool m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
+ bool hasImState : 1;
bool m_separator : 1;
bool m_readOnly : 1;
bool m_textDirty : 1;
@@ -321,6 +319,7 @@ public:
#endif
void commitPreedit();
+ void cancelPreedit();
Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
@@ -378,9 +377,7 @@ public:
void updatePasswordEchoEditing(bool editing);
void cancelPasswordEchoTimer() {
-#ifdef QT_GUI_PASSWORD_ECHO_DELAY
m_passwordEchoTimer.stop();
-#endif
}
Qt::LayoutDirection layoutDirection() const {
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index 5be4963809..81340d6e66 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -254,9 +254,6 @@ namespace {
decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration);
qreal ascent = glyphRun.rawFont().ascent();
- // ### QTBUG-22919 The bounding rect returned by QGlyphRun appears to start on the
- // baseline, move it by the ascent so all bounding rects are at baseline - ascent.
- searchRect.translate(0, -ascent);
insert(binaryTree, BinaryTreeNode(glyphRun, selectionState, searchRect, decorations,
textColor, backgroundColor, position, ascent));
}
@@ -1219,12 +1216,6 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex
int textPos = block.position();
QTextBlock::iterator blockIterator = block.begin();
- if (blockIterator.atEnd() && preeditLength) {
- engine.setPosition(blockPosition);
- textPos = engine.addText(block, block.charFormat(), textColor, colorChanges,
- textPos, textPos + preeditLength,
- selectionStart, selectionEnd);
- }
while (!blockIterator.atEnd()) {
QTextFragment fragment = blockIterator.fragment();
@@ -1275,6 +1266,19 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex
++blockIterator;
}
+ if (preeditLength >= 0 && textPos <= block.position() + preeditPosition) {
+ engine.setPosition(blockPosition);
+ textPos = block.position() + preeditPosition;
+ QTextLine line = block.layout()->lineForTextPosition(preeditPosition);
+ if (!engine.currentLine().isValid()
+ || line.lineNumber() != engine.currentLine().lineNumber()) {
+ engine.setCurrentLine(line);
+ }
+ textPos = engine.addText(block, block.charFormat(), textColor, colorChanges,
+ textPos, textPos + preeditLength,
+ selectionStart, selectionEnd);
+ }
+
engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed
++it;
}
diff --git a/src/quick/items/qquickvisualdatamodel.cpp b/src/quick/items/qquickvisualdatamodel.cpp
index 88d46c60ee..cea83969f3 100644
--- a/src/quick/items/qquickvisualdatamodel.cpp
+++ b/src/quick/items/qquickvisualdatamodel.cpp
@@ -803,7 +803,7 @@ QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index
}
if (cacheItem->incubationTask) {
- if (!asynchronous) {
+ if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) {
// previously requested async - now needed immediately
cacheItem->incubationTask->forceCompletion();
}
diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp
index bac5cc7582..d075d3b64b 100644
--- a/src/quick/items/qquickwindowmanager.cpp
+++ b/src/quick/items/qquickwindowmanager.cpp
@@ -1221,7 +1221,7 @@ void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas)
maybeUpdate(canvas);
}
-void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *canvas)
+void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *)
{
}
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index 72fd2b32bd..156ccec127 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -42,14 +42,14 @@
#ifndef QQUICKWINDOWMODULE_H
#define QQUICKWINDOWMODULE_H
-#include <qqml.h>
+#include <private/qtquickglobal_p.h>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
-class QQuickWindowModule
+class Q_QUICK_PRIVATE_EXPORT QQuickWindowModule
{
public:
static void defineModule();
diff --git a/src/quick/particles/qquickcustomparticle.cpp b/src/quick/particles/qquickcustomparticle.cpp
index 8f75672e32..7d27e48c6b 100644
--- a/src/quick/particles/qquickcustomparticle.cpp
+++ b/src/quick/particles/qquickcustomparticle.cpp
@@ -130,29 +130,27 @@ struct PlainVertices {
QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
: QQuickParticlePainter(parent)
- , m_dirtyData(true)
- , m_material(0)
- , m_rootNode(0)
+ , m_dirtyUniforms(true)
+ , m_dirtyUniformValues(true)
+ , m_dirtyTextureProviders(true)
+ , m_dirtyProgram(true)
{
setFlag(QQuickItem::ItemHasContents);
}
-class QQuickShaderEffectMaterialObject : public QObject, public QQuickShaderEffectMaterial { };
-
void QQuickCustomParticle::sceneGraphInvalidated()
{
m_nodes.clear();
- m_rootNode = 0;
}
QQuickCustomParticle::~QQuickCustomParticle()
{
- if (m_material)
- m_material->deleteLater();
}
void QQuickCustomParticle::componentComplete()
{
+ m_common.updateShader(this, Key::FragmentShader);
+ updateVertexShader();
reset();
QQuickParticlePainter::componentComplete();
}
@@ -170,10 +168,12 @@ void QQuickCustomParticle::componentComplete()
void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
{
- if (m_source.fragmentCode.constData() == code.constData())
+ if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
return;
- m_source.fragmentCode = code;
+ m_common.source.sourceCode[Key::FragmentShader] = code;
+ m_dirtyProgram = true;
if (isComponentComplete()) {
+ m_common.updateShader(this, Key::FragmentShader);
reset();
}
emit fragmentShaderChanged();
@@ -222,236 +222,108 @@ void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
void QQuickCustomParticle::setVertexShader(const QByteArray &code)
{
- if (m_source.vertexCode.constData() == code.constData())
+ if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
return;
- m_source.vertexCode = code;
+ m_common.source.sourceCode[Key::VertexShader] = code;
+
+ m_dirtyProgram = true;
if (isComponentComplete()) {
+ updateVertexShader();
reset();
}
emit vertexShaderChanged();
}
-void QQuickCustomParticle::reset()
+void QQuickCustomParticle::updateVertexShader()
{
- disconnectPropertySignals();
-
- m_source.attributeNames.clear();
- m_source.uniformNames.clear();
- m_source.respectsOpacity = false;
- m_source.respectsMatrix = false;
- m_source.className = metaObject()->className();
-
- for (int i = 0; i < m_sources.size(); ++i) {
- const SourceData &source = m_sources.at(i);
- delete source.mapper;
- if (source.item && source.item->parentItem() == this)
- source.item->setParentItem(0);
- }
- m_sources.clear();
-
- QQuickParticlePainter::reset();
- m_pleaseReset = true;
- update();
+ m_common.disconnectPropertySignals(this, Key::VertexShader);
+ qDeleteAll(m_common.signalMappers[Key::VertexShader]);
+ m_common.uniformData[Key::VertexShader].clear();
+ m_common.signalMappers[Key::VertexShader].clear();
+ m_common.attributes.clear();
+ m_common.attributes.append("qt_ParticlePos");
+ m_common.attributes.append("qt_ParticleTex");
+ m_common.attributes.append("qt_ParticleData");
+ m_common.attributes.append("qt_ParticleVec");
+ m_common.attributes.append("qt_ParticleR");
+
+ UniformData d;
+ d.name = "qt_Matrix";
+ d.specialType = UniformData::Matrix;
+ m_common.uniformData[Key::VertexShader].append(d);
+ m_common.signalMappers[Key::VertexShader].append(0);
+
+ d.name = "qt_Timestamp";
+ d.specialType = UniformData::None;
+ m_common.uniformData[Key::VertexShader].append(d);
+ m_common.signalMappers[Key::VertexShader].append(0);
+
+ const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
+ if (!code.isEmpty())
+ m_common.lookThroughShaderCode(this, Key::VertexShader, code);
+
+ m_common.connectPropertySignals(this, Key::VertexShader);
}
-
-void QQuickCustomParticle::changeSource(int index)
-{
- Q_ASSERT(index >= 0 && index < m_sources.size());
- QVariant v = property(m_sources.at(index).name.constData());
- setSource(v, index);
-}
-
-void QQuickCustomParticle::updateData()
+void QQuickCustomParticle::reset()
{
- m_dirtyData = true;
+ QQuickParticlePainter::reset();
update();
}
-void QQuickCustomParticle::setSource(const QVariant &var, int index)
-{
- Q_ASSERT(index >= 0 && index < m_sources.size());
-
- SourceData &source = m_sources[index];
-
- source.item = 0;
- if (var.isNull()) {
- return;
- } else if (!qVariantCanConvert<QObject *>(var)) {
- qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
- return;
- }
-
- QObject *obj = qVariantValue<QObject *>(var);
- source.item = qobject_cast<QQuickItem *>(obj);
- if (!source.item || !source.item->isTextureProvider()) {
- qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]",
- source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className());
- return;
- }
-
- // TODO: Copy better solution in QQuickShaderEffect when they find it.
- // 'source.item' needs a canvas to get a scenegraph node.
- // The easiest way to make sure it gets a canvas is to
- // make it a part of the same item tree as 'this'.
- if (source.item && source.item->parentItem() == 0) {
- source.item->setParentItem(this);
- source.item->setVisible(false);
- }
-}
-
-void QQuickCustomParticle::disconnectPropertySignals()
-{
- disconnect(this, 0, this, SLOT(updateData()));
- for (int i = 0; i < m_sources.size(); ++i) {
- SourceData &source = m_sources[i];
- disconnect(this, 0, source.mapper, 0);
- disconnect(source.mapper, 0, this, 0);
- }
-}
-
-void QQuickCustomParticle::connectPropertySignals()
-{
- QSet<QByteArray>::const_iterator it;
- for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
- int pi = metaObject()->indexOfProperty(it->constData());
- if (pi >= 0) {
- QMetaProperty mp = metaObject()->property(pi);
- if (!mp.hasNotifySignal())
- qWarning("QQuickCustomParticle: property '%s' does not have notification method!", it->constData());
- QByteArray signalName("2");
- signalName.append(mp.notifySignal().methodSignature());
- connect(this, signalName, this, SLOT(updateData()));
- } else {
- qWarning("QQuickCustomParticle: '%s' does not have a matching property!", it->constData());
- }
- }
- for (int i = 0; i < m_sources.size(); ++i) {
- SourceData &source = m_sources[i];
- int pi = metaObject()->indexOfProperty(source.name.constData());
- if (pi >= 0) {
- QMetaProperty mp = metaObject()->property(pi);
- QByteArray signalName("2");
- signalName.append(mp.notifySignal().methodSignature());
- connect(this, signalName, source.mapper, SLOT(map()));
- source.mapper->setMapping(this, i);
- connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
- } else {
- qWarning("QQuickCustomParticle: '%s' does not have a matching source!", source.name.constData());
- }
- }
-}
-
-void QQuickCustomParticle::updateProperties()
-{
- QByteArray vertexCode = m_source.vertexCode;
- QByteArray fragmentCode = m_source.fragmentCode;
- if (vertexCode.isEmpty())
- vertexCode = qt_particles_default_vertex_code;
- if (fragmentCode.isEmpty())
- fragmentCode = qt_particles_default_fragment_code;
- vertexCode = qt_particles_template_vertex_code + vertexCode;
-
- m_source.attributeNames.clear();
- m_source.attributeNames << "qt_ParticlePos"
- << "qt_ParticleTex"
- << "qt_ParticleData"
- << "qt_ParticleVec"
- << "qt_ParticleR";
-
- lookThroughShaderCode(vertexCode);
- lookThroughShaderCode(fragmentCode);
-
- if (!m_source.respectsMatrix)
- qWarning("QQuickCustomParticle: Missing reference to \'qt_Matrix\'.");
- if (!m_source.respectsOpacity)
- qWarning("QQuickCustomParticle: Missing reference to \'qt_Opacity\'.");
-
- for (int i = 0; i < m_sources.size(); ++i) {
- QVariant v = property(m_sources.at(i).name);
- setSource(v, i);
- }
-
- connectPropertySignals();
-}
-
-void QQuickCustomParticle::lookThroughShaderCode(const QByteArray &code)
-{
- // Regexp for matching attributes and uniforms.
- // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name>
- static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)"));
- Q_ASSERT(re.isValid());
-
- int pos = -1;
-
- QString wideCode = QString::fromLatin1(code.constData(), code.size());
-
- while ((pos = re.indexIn(wideCode, pos + 1)) != -1) {
- QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute
- QByteArray type = re.cap(2).toLatin1(); // type
- QByteArray name = re.cap(3).toLatin1(); // variable name
-
- if (decl == "attribute") {
- if (!m_source.attributeNames.contains(name))
- qWarning() << "Custom Particle: Unknown attribute " << name;
- } else {
- Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert
-
- if (name == "qt_Matrix") {
- m_source.respectsMatrix = true;
- } else if (name == "qt_Opacity") {
- m_source.respectsOpacity = true;
- } else if (name == "qt_Timestamp") {
- //Not strictly necessary
- } else {
- m_source.uniformNames.insert(name);
- if (type == "sampler2D") {
- SourceData d;
- d.mapper = new QSignalMapper;
- d.name = name;
- d.item = 0;
- m_sources.append(d);
- }
- }
- }
- }
-}
-
QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- Q_UNUSED(oldNode);
+ QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
if (m_pleaseReset){
-
- //delete m_material;//Shader effect item doesn't regen material?
-
- delete m_rootNode;//Automatically deletes children
- m_rootNode = 0;
+ delete rootNode;//Automatically deletes children
+ rootNode = 0;
m_nodes.clear();
m_pleaseReset = false;
- m_dirtyData = false;
+ m_dirtyProgram = true;
}
if (m_system && m_system->isRunning() && !m_system->isPaused()){
- prepareNextFrame();
- if (m_rootNode) {
+ rootNode = prepareNextFrame(rootNode);
+ if (rootNode)
update();
- foreach (QSGGeometryNode* node, m_nodes)
- node->markDirty(QSGNode::DirtyGeometry);//done in buildData?
- }
}
- return m_rootNode;
+ return rootNode;
}
-void QQuickCustomParticle::prepareNextFrame(){
- if (!m_rootNode)
- m_rootNode = buildCustomNodes();
- if (!m_rootNode)
- return;
+QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
+{
+ if (!rootNode)
+ rootNode = buildCustomNodes();
+
+ if (!rootNode)
+ return 0;
+
+ if (m_dirtyProgram) {
+ QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
+ Q_ASSERT(material);
+
+ Key s = m_common.source;
+ if (s.sourceCode[Key::FragmentShader].isEmpty())
+ s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code;
+ if (s.sourceCode[Key::VertexShader].isEmpty())
+ s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code;
+ s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader];
+ s.className = metaObject()->className();
+
+ material->setProgramSource(s);
+ material->attributes = m_common.attributes;
+ foreach (QQuickShaderEffectNode* node, m_nodes)
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ m_dirtyProgram = false;
+ m_dirtyUniforms = true;
+ }
m_lastTime = m_system->systemSync(this) / 1000.;
- if (m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive.
- buildData();
+ if (true) //Currently this is how we update timestamp... potentially over expensive.
+ buildData(rootNode);
+ return rootNode;
}
QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
@@ -468,20 +340,13 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
return 0;
}
- updateProperties();
-
- QQuickShaderEffectProgram s = m_source;
- if (s.fragmentCode.isEmpty())
- s.fragmentCode = qt_particles_default_fragment_code;
- if (s.vertexCode.isEmpty())
- s.vertexCode = qt_particles_default_vertex_code;
+ if (m_groups.isEmpty())
+ return 0;
- if (!m_material) {
- m_material = new QQuickShaderEffectMaterialObject;
- }
+ QQuickShaderEffectNode *rootNode = 0;
+ QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
+ m_dirtyProgram = true;
- s.vertexCode = qt_particles_template_vertex_code + s.vertexCode;
- m_material->setProgramSource(s);
foreach (const QString &str, m_groups){
int gIdx = m_system->groupIds[str];
int count = m_system->groupData[gIdx]->size();
@@ -489,8 +354,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
m_nodes.insert(gIdx, node);
- node->setMaterial(m_material);
- node->markDirty(QSGNode::DirtyMaterial);
+ node->setMaterial(material);
//Create Particle Geometry
int vCount = count * 4;
@@ -498,6 +362,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount);
g->setDrawingMode(GL_TRIANGLES);
node->setGeometry(g);
+ node->setFlag(QSGNode::OwnsGeometry, true);
PlainVertex *vertices = (PlainVertex *) g->vertexData();
for (int p=0; p < count; ++p) {
commit(gIdx, p);
@@ -526,48 +391,46 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
indices += 6;
}
}
- foreach (QQuickShaderEffectNode* node, m_nodes){
- if (node == *(m_nodes.begin()))
- continue;
- (*(m_nodes.begin()))->appendChildNode(node);
- }
- return *(m_nodes.begin());
+ QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin();
+ rootNode = it.value();
+ rootNode->setFlag(QSGNode::OwnsMaterial, true);
+ for (++it; it != m_nodes.end(); ++it)
+ rootNode->appendChildNode(it.value());
+
+ return rootNode;
}
+void QQuickCustomParticle::sourceDestroyed(QObject *object)
+{
+ m_common.sourceDestroyed(object);
+}
-void QQuickCustomParticle::buildData()
+void QQuickCustomParticle::propertyChanged(int mappedId)
{
- if (!m_rootNode)
+ bool textureProviderChanged;
+ m_common.propertyChanged(this, mappedId, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ update();
+}
+
+
+void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
+{
+ if (!rootNode)
return;
- const QByteArray timestampName("qt_Timestamp");
- QVector<QPair<QByteArray, QVariant> > values;
- QVector<QPair<QByteArray, QSGTextureProvider *> > textures;
- const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = m_material->textureProviders();
- for (int i = 0; i < oldTextures.size(); ++i) {
- QSGTextureProvider *t = oldTextures.at(i).second;
- if (t)
- foreach (QQuickShaderEffectNode* node, m_nodes)
- disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- }
- for (int i = 0; i < m_sources.size(); ++i) {
- const SourceData &source = m_sources.at(i);
- QSGTextureProvider *t = source.item->textureProvider();
- textures.append(qMakePair(source.name, t));
- if (t)
- foreach (QQuickShaderEffectNode* node, m_nodes)
- connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
- }
- for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
- it != m_source.uniformNames.end(); ++it) {
- values.append(qMakePair(*it, property(*it)));
+ 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 == "qt_Timestamp")
+ m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
+ }
}
- values.append(qMakePair(timestampName, QVariant(m_lastTime)));
- m_material->setUniforms(values);
- m_material->setTextureProviders(textures);
- m_dirtyData = false;
+ m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()),
+ m_dirtyUniforms, true, m_dirtyTextureProviders);
foreach (QQuickShaderEffectNode* node, m_nodes)
node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
}
void QQuickCustomParticle::initialize(int gIdx, int pIdx)
@@ -599,4 +462,12 @@ void QQuickCustomParticle::commit(int gIdx, int pIdx)
}
}
+void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == QQuickItem::ItemSceneChange)
+ m_common.updateCanvas(value.canvas);
+ QQuickParticlePainter::itemChange(change, value);
+}
+
+
QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickcustomparticle_p.h b/src/quick/particles/qquickcustomparticle_p.h
index e04ac704d0..f689091268 100644
--- a/src/quick/particles/qquickcustomparticle_p.h
+++ b/src/quick/particles/qquickcustomparticle_p.h
@@ -43,6 +43,7 @@
#define CUSTOM_PARTICLE_H
#include "qquickparticlepainter_p.h"
#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickshadereffect_p.h>
#include <QSignalMapper>
QT_BEGIN_HEADER
@@ -65,53 +66,50 @@ public:
explicit QQuickCustomParticle(QQuickItem* parent=0);
~QQuickCustomParticle();
- QByteArray fragmentShader() const { return m_source.fragmentCode; }
+ QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
void setFragmentShader(const QByteArray &code);
- QByteArray vertexShader() const { return m_source.vertexCode; }
+ QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
void setVertexShader(const QByteArray &code);
-public Q_SLOTS:
- void updateData();
- void changeSource(int);
+
Q_SIGNALS:
void fragmentShaderChanged();
void vertexShaderChanged();
+
protected:
virtual void initialize(int gIdx, int pIdx);
virtual void commit(int gIdx, int pIdx);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
- void prepareNextFrame();
- void setSource(const QVariant &var, int index);
- void disconnectPropertySignals();
- void connectPropertySignals();
+ QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode);
void reset();
void resize(int oldCount, int newCount);
- void updateProperties();
- void lookThroughShaderCode(const QByteArray &code);
virtual void componentComplete();
QQuickShaderEffectNode *buildCustomNodes();
- void performPendingResize();
void sceneGraphInvalidated();
+ void itemChange(ItemChange change, const ItemChangeData &value);
+
+private Q_SLOTS:
+ void sourceDestroyed(QObject *object);
+ void propertyChanged(int mappedId);
private:
- void buildData();
-
- bool m_dirtyData;
- QQuickShaderEffectProgram m_source;
- struct SourceData
- {
- QSignalMapper *mapper;
- QPointer<QQuickItem> item;
- QByteArray name;
- };
- QVector<SourceData> m_sources;
- QQuickShaderEffectMaterialObject *m_material;
- QQuickShaderEffectNode* m_rootNode;
+ typedef QQuickShaderEffectMaterialKey Key;
+ typedef QQuickShaderEffectMaterial::UniformData UniformData;
+
+ void buildData(QQuickShaderEffectNode *rootNode);
+ void updateVertexShader();
+
+ QQuickShaderEffectCommon m_common;
+
QHash<int, QQuickShaderEffectNode*> m_nodes;
qreal m_lastTime;
+ uint m_dirtyUniforms : 1;
+ uint m_dirtyUniformValues : 1;
+ uint m_dirtyTextureProviders : 1;
+ uint m_dirtyProgram : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/particles/qquickparticleemitter_p.h b/src/quick/particles/qquickparticleemitter_p.h
index 70adcff34c..eb9e1fd591 100644
--- a/src/quick/particles/qquickparticleemitter_p.h
+++ b/src/quick/particles/qquickparticleemitter_p.h
@@ -178,7 +178,8 @@ public slots:
{
if (m_system != arg) {
m_system = arg;
- m_system->registerParticleEmitter(this);
+ if (m_system)
+ m_system->registerParticleEmitter(this);
emit systemChanged(arg);
}
}
diff --git a/src/quick/particles/qquickparticlesmodule_p.h b/src/quick/particles/qquickparticlesmodule_p.h
index b7cf09919e..23a40488cf 100644
--- a/src/quick/particles/qquickparticlesmodule_p.h
+++ b/src/quick/particles/qquickparticlesmodule_p.h
@@ -39,16 +39,16 @@
**
****************************************************************************/
-#ifndef QQuickPARTICLESMODULE_H
-#define QQuickPARTICLESMODULE_H
+#ifndef QQUICKPARTICLESMODULE_H
+#define QQUICKPARTICLESMODULE_H
-#include <qqml.h>
+#include <private/qtquickglobal_p.h>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
-class QQuickParticlesModule
+class Q_QUICK_PRIVATE_EXPORT QQuickParticlesModule
{
public:
static void defineModule();
@@ -58,4 +58,4 @@ QT_END_NAMESPACE
QT_END_HEADER
-#endif // QQuickPARTICLESMODULE_H
+#endif // QQUICKPARTICLESMODULE_H
diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp
index 16cf2198a2..8a0c05618a 100644
--- a/src/quick/qtquick2.cpp
+++ b/src/quick/qtquick2.cpp
@@ -44,14 +44,12 @@
#include <private/qquickutilmodule_p.h>
#include <private/qquickvaluetypes_p.h>
#include <private/qquickitemsmodule_p.h>
-#include <private/qquickparticlesmodule_p.h>
-#include <private/qquickwindowmodule_p.h>
-#include <private/qquickapplication_p.h>
#include <private/qqmlenginedebugservice_p.h>
#include <private/qqmldebugstatesdelegate_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlcontext_p.h>
+#include <private/qquickapplication_p.h>
#include <QtQuick/private/qquickpropertychanges_p.h>
#include <QtQuick/private/qquickstate_p.h>
#include <qqmlproperty.h>
@@ -176,8 +174,6 @@ void QQmlQtQuick2Module::defineModule()
QQuickUtilModule::defineModule();
QQmlEnginePrivate::defineModule();
QQuickItemsModule::defineModule();
- QQuickParticlesModule::defineModule();
- QQuickWindowModule::defineModule();
qmlRegisterUncreatableType<QQuickApplication>("QtQuick",2,0,"Application", QQuickApplication::tr("Application is an abstract class"));
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
index cd5aaaedd7..dd9db4e904 100644
--- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp
@@ -86,11 +86,11 @@ QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteAr
connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)),
this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)),
Qt::DirectConnection);
- connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
- this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+ connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
+ this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
Qt::DirectConnection);
- connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
- this, SLOT(reportItemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)),
+ connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
+ this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)),
Qt::DirectConnection);
connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)),
this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)),
@@ -537,7 +537,7 @@ void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
while (it != textureContentForBuffer.constEnd()) {
Texture texture;
texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key());
- texture.size = it.value().size;
+ texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key());
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_)
saveTexture(texture.textureId, texture.size.width(), texture.size.height());
@@ -557,7 +557,6 @@ void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs()
void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId,
void *bufferId,
- const QSize &bufferSize,
const QVector<quint32> &itemIds,
const QVector<QPoint> &positions)
{
@@ -568,8 +567,8 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &ca
return;
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
- qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs, bufferSize: %dx%d)",
- cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height());
+ qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)",
+ cacheId.constData(), itemIds.size());
#endif
for (int i=0; i<itemIds.size(); ++i) {
@@ -581,11 +580,11 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &ca
}
if (requestedItemsInList)
- reportItemsUpdated(cacheId, bufferId, bufferSize, itemIds, positions);
+ reportItemsUpdated(cacheId, bufferId,itemIds, positions);
}
void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId,
- void *bufferId, const QSize &bufferSize,
+ void *bufferId,
const QVector<quint32> &itemIds,
const QVector<QPoint> &positions)
{
@@ -597,19 +596,17 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cach
Q_ASSERT(itemIds.size() == positions.size());
#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG)
- qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs, bufferSize: %dx%d)",
- cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height());
+ qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)",
+ cacheId.constData(), itemIds.size());
#endif
for (int i=0; i<itemIds.size(); ++i) {
if (m_requestedGlyphs.contains(itemIds.at(i))) {
PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)];
void *oldBuffer = pendingGlyph.buffer;
- Q_ASSERT(bufferSize.height() >= pendingGlyph.bufferSize.height());
pendingGlyph.buffer = bufferId;
pendingGlyph.position = positions.at(i);
- pendingGlyph.bufferSize = bufferSize;
m_sharedGraphicsCache->referenceBuffer(bufferId);
if (oldBuffer != 0)
diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
index 19844bbda4..2d43246bb0 100644
--- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h
@@ -77,10 +77,13 @@ Q_SIGNALS:
private Q_SLOTS:
void reportItemsMissing(const QByteArray &cacheId, const QVector<quint32> &itemIds);
void reportItemsAvailable(const QByteArray &cacheId,
- void *bufferId, const QSize &bufferSize,
- const QVector<quint32> &itemIds, const QVector<QPoint> &positions);
- void reportItemsUpdated(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize,
- const QVector<quint32> &itemIds, const QVector<QPoint> &positions);
+ void *bufferId,
+ const QVector<quint32> &itemIds,
+ const QVector<QPoint> &positions);
+ void reportItemsUpdated(const QByteArray &cacheId,
+ void *bufferId,
+ const QVector<quint32> &itemIds,
+ const QVector<QPoint> &positions);
void reportItemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds);
private:
diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp
index 1ea64f6205..87a54d3124 100644
--- a/src/quick/scenegraph/util/qsgpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgpainternode.cpp
@@ -73,6 +73,10 @@ QSGPainterTexture::QSGPainterTexture()
}
+#ifdef QT_OPENGL_ES
+extern void qsg_swizzleBGRAToRGBA(QImage *image);
+#endif
+
void QSGPainterTexture::bind()
{
if (m_dirty_rect.isNull()) {
@@ -91,6 +95,7 @@ void QSGPainterTexture::bind()
int h = m_dirty_rect.height();
#ifdef QT_OPENGL_ES
+ qsg_swizzleBGRAToRGBA(&subImage);
glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h,
GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
#else
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 24b92fa98f..7be38ff109 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -402,7 +402,7 @@ QSGPlainTexture::~QSGPlainTexture()
}
#ifdef QT_OPENGL_ES
-static void swizzleBGRAToRGBA(QImage *image)
+void qsg_swizzleBGRAToRGBA(QImage *image)
{
const int width = image->width();
const int height = image->height();
@@ -500,7 +500,7 @@ void QSGPlainTexture::bind()
updateBindOptions(m_dirty_bind_options);
#ifdef QT_OPENGL_ES
- swizzleBGRAToRGBA(&tmp);
+ qsg_swizzleBGRAToRGBA(&tmp);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits());
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits());
diff --git a/src/quick/util/qquickconnections.cpp b/src/quick/util/qquickconnections.cpp
index acc9738f2c..27b66ba38f 100644
--- a/src/quick/util/qquickconnections.cpp
+++ b/src/quick/util/qquickconnections.cpp
@@ -280,10 +280,9 @@ void QQuickConnections::connectSignals()
location = ddata->outerContext->urlString;
}
- QQmlExpression *expression = ctxtdata ?
- QQmlExpressionPrivate::create(ctxtdata, 0, script, true, location, line, column) : 0;
+ QQmlBoundSignalExpression *expression = ctxtdata ?
+ new QQmlBoundSignalExpression(ctxtdata, 0, script, true, location, line, column) : 0;
signal->setExpression(expression);
- signal->addToObject();
d->boundsignals += signal;
} else {
if (!d->ignoreUnknownSignals)
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index 8b0818c96c..4bff006d9b 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -55,6 +55,7 @@
#include <private/qqmlproperty_p.h>
#include <private/qqmlcontext_p.h>
#include <private/qquickstate_p_p.h>
+#include <private/qqmlboundsignal_p.h>
#include <QtCore/qdebug.h>
@@ -139,7 +140,7 @@ class QQuickReplaceSignalHandler : public QQuickActionEvent
{
public:
QQuickReplaceSignalHandler() : expression(0), reverseExpression(0),
- rewindExpression(0), ownedExpression(0) {}
+ rewindExpression(0), ownedExpression(0), ownedExpressionWatcher(0) {}
~QQuickReplaceSignalHandler() {
delete ownedExpression;
}
@@ -147,22 +148,35 @@ public:
virtual EventType type() const { return SignalHandler; }
QQmlProperty property;
- QQmlExpression *expression;
- QQmlExpression *reverseExpression;
- QQmlExpression *rewindExpression;
- QQmlGuard<QQmlExpression> ownedExpression;
+ QQmlBoundSignalExpression *expression;
+ QQmlBoundSignalExpression *reverseExpression;
+ QQmlBoundSignalExpression *rewindExpression;
+ QQmlBoundSignalExpression *ownedExpression;
+ QQmlAbstractExpression::DeleteWatcher *ownedExpressionWatcher; // TODO: refactor the ownership impl.
virtual void execute(Reason) {
ownedExpression = QQmlPropertyPrivate::setSignalExpression(property, expression);
- if (ownedExpression == expression)
+ if (ownedExpression == expression) {
+ delete ownedExpressionWatcher;
+ ownedExpressionWatcher = 0;
ownedExpression = 0;
+ } else if (ownedExpression) {
+ delete ownedExpressionWatcher;
+ ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression);
+ }
}
virtual bool isReversable() { return true; }
virtual void reverse(Reason) {
ownedExpression = QQmlPropertyPrivate::setSignalExpression(property, reverseExpression);
- if (ownedExpression == reverseExpression)
+ if (ownedExpression == reverseExpression) {
+ delete ownedExpressionWatcher;
+ ownedExpressionWatcher = 0;
ownedExpression = 0;
+ } else if (ownedExpression) {
+ delete ownedExpressionWatcher;
+ ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression);
+ }
}
virtual void saveOriginals() {
@@ -181,6 +195,8 @@ public:
if (rsh->ownedExpression == reverseExpression) {
ownedExpression = rsh->ownedExpression;
rsh->ownedExpression = 0;
+ delete ownedExpressionWatcher;
+ ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression);
}
}
@@ -225,11 +241,17 @@ public:
public:
ExpressionChange(const QString &_name,
QQmlBinding::Identifier _id,
- QQmlExpression *_expr)
- : name(_name), id(_id), expression(_expr) {}
+ const QString& _expr,
+ const QUrl &_url,
+ int _line,
+ int _column)
+ : name(_name), id(_id), expression(_expr), url(_url), line(_line), column(_column) {}
QString name;
QQmlBinding::Identifier id;
- QQmlExpression *expression;
+ QString expression;
+ QUrl url;
+ int line;
+ int column;
};
QList<QPair<QString, QVariant> > properties;
@@ -334,20 +356,36 @@ void QQuickPropertyChangesPrivate::decode()
QQmlProperty prop = property(name); //### better way to check for signal property?
if (prop.type() & QQmlProperty::SignalProperty) {
- QQmlExpression *expression = new QQmlExpression(qmlContext(q), object, data.toString());
+ QString expression = data.toString();
+ QUrl url = QUrl();
+ int line = -1;
+ int column = -1;
+
QQmlData *ddata = QQmlData::get(q);
- if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
- expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber, ddata->columnNumber);
+ if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) {
+ url = ddata->outerContext->url;
+ line = ddata->lineNumber;
+ column = ddata->columnNumber;
+ }
+
QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
handler->property = prop;
- handler->expression = expression;
+ handler->expression = new QQmlBoundSignalExpression(QQmlContextData::get(qmlContext(q)), object, expression, false, url.toString(), line, column);
signalReplacements << handler;
- } else if (isScript) {
- QQmlExpression *expression = new QQmlExpression(qmlContext(q), object, data.toString());
+ } else if (isScript) { // binding
+ QString expression = data.toString();
+ QUrl url = QUrl();
+ int line = -1;
+ int column = -1;
+
QQmlData *ddata = QQmlData::get(q);
- if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
- expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber, ddata->columnNumber);
- expressions << ExpressionChange(name, id, expression);
+ if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) {
+ url = ddata->outerContext->url;
+ line = ddata->lineNumber;
+ column = ddata->columnNumber;
+ }
+
+ expressions << ExpressionChange(name, id, expression, url, line, column);
} else {
properties << qMakePair(name, data);
}
@@ -374,8 +412,6 @@ QQuickPropertyChanges::QQuickPropertyChanges()
QQuickPropertyChanges::~QQuickPropertyChanges()
{
Q_D(QQuickPropertyChanges);
- for(int ii = 0; ii < d->expressions.count(); ++ii)
- delete d->expressions.at(ii).expression;
for(int ii = 0; ii < d->signalReplacements.count(); ++ii)
delete d->signalReplacements.at(ii);
}
@@ -460,7 +496,8 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
for (int ii = 0; ii < d->expressions.count(); ++ii) {
- const QString &property = d->expressions.at(ii).name;
+ QQuickPropertyChangesPrivate::ExpressionChange e = d->expressions.at(ii);
+ const QString &property = e.name;
QQmlProperty prop = d->property(property);
if (prop.isValid()) {
@@ -471,16 +508,18 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
a.specifiedObject = d->object;
a.specifiedProperty = property;
+ QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this), e.url.toString(), e.column) : 0;
+ if (!newBinding)
+ newBinding = new QQmlBinding(e.expression, false, object(), QQmlContextData::get(qmlContext(this)), e.url.toString(), e.line, e.column);
+
if (d->isExplicit) {
- a.toValue = d->expressions.at(ii).expression->evaluate();
+ // in this case, we don't want to assign a binding, per se,
+ // so we evaluate the expression and assign the result.
+ // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
+ // so that we can avoid creating then destroying the binding in this case.
+ a.toValue = newBinding->evaluate();
+ newBinding->destroy();
} else {
- QQmlExpression *e = d->expressions.at(ii).expression;
-
- QQmlBinding::Identifier id = d->expressions.at(ii).id;
- QQmlBinding *newBinding = id != QQmlBinding::Invalid ? QQmlBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0;
- if (!newBinding)
- newBinding = new QQmlBinding(e->expression(), false, object(), QQmlContextData::get(qmlContext(this)),
- e->sourceFile(), e->lineNumber(), e->columnNumber());
newBinding->setTarget(prop);
a.toBinding = QQmlAbstractBinding::getPointer(newBinding);
a.deletableToBinding = true;
@@ -635,14 +674,14 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
while (expressionIterator.hasNext()) {
- const ExpressionEntry &entry = expressionIterator.next();
+ ExpressionEntry &entry = expressionIterator.next();
if (entry.name == name) {
- entry.expression->setExpression(expression);
+ entry.expression = expression;
if (state() && state()->isStateActive()) {
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name));
if (oldBinding) {
- QQmlPropertyPrivate::setBinding(d->property(name), 0);
- oldBinding->destroy();
+ QQmlPropertyPrivate::setBinding(d->property(name), 0);
+ oldBinding->destroy();
}
QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this));
@@ -653,8 +692,8 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
}
}
- QQmlExpression *newExpression = new QQmlExpression(qmlContext(this), d->object, expression);
- expressionIterator.insert(ExpressionEntry(name, QQmlBinding::Invalid, newExpression));
+ // adding a new expression.
+ expressionIterator.insert(ExpressionEntry(name, QQmlBinding::Invalid, expression, QUrl(), -1, -1));
if (state() && state()->isStateActive()) {
if (hadValue) {
@@ -675,11 +714,14 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
action.specifiedObject = object();
action.specifiedProperty = name;
-
+ QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this));
if (d->isExplicit) {
- action.toValue = newExpression->evaluate();
+ // don't assign the binding, merely evaluate the expression.
+ // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
+ // so that we can avoid creating then destroying the binding in this case.
+ action.toValue = newBinding->evaluate();
+ newBinding->destroy();
} else {
- QQmlBinding *newBinding = new QQmlBinding(newExpression->expression(), object(), qmlContext(this));
newBinding->setTarget(d->property(name));
action.toBinding = QQmlAbstractBinding::getPointer(newBinding);
action.deletableToBinding = true;
@@ -714,7 +756,7 @@ QVariant QQuickPropertyChanges::property(const QString &name) const
while (expressionIterator.hasNext()) {
const ExpressionEntry &entry = expressionIterator.next();
if (entry.name == name) {
- return QVariant(entry.expression->expression());
+ return QVariant(entry.expression);
}
}
@@ -773,7 +815,7 @@ QString QQuickPropertyChanges::expression(const QString &name) const
while (expressionIterator.hasNext()) {
const ExpressionEntry &entry = expressionIterator.next();
if (entry.name == name) {
- return entry.expression->expression();
+ return entry.expression;
}
}