diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2012-04-11 14:56:22 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@nokia.com> | 2012-04-11 16:05:03 +0200 |
commit | a896d4b39ec3d45ba708d9b36ea9c864b1df2136 (patch) | |
tree | 45cfe153cce6114c2c76c48dc0bdabde2a8cf3e3 /src/quick | |
parent | 24fb8dc27eddfdd62bd2c3a6e863cbf433762cd6 (diff) | |
parent | 65bfc35429e845cf6b76d58107360a1360a654fc (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')
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; } } |