aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp2
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp192
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h5
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp26
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h14
-rw-r--r--src/quick/items/items.pri10
-rw-r--r--src/quick/items/qquickanchors_p.h1
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp11
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h2
-rw-r--r--src/quick/items/qquickborderimage.cpp26
-rw-r--r--src/quick/items/qquickevents.cpp214
-rw-r--r--src/quick/items/qquickevents_p_p.h183
-rw-r--r--src/quick/items/qquickflickable.cpp104
-rw-r--r--src/quick/items/qquickflickable_p.h19
-rw-r--r--src/quick/items/qquickflickable_p_p.h2
-rw-r--r--src/quick/items/qquickgridview.cpp35
-rw-r--r--src/quick/items/qquickitem.cpp68
-rw-r--r--src/quick/items/qquickitem.h1
-rw-r--r--src/quick/items/qquickitem_p.h24
-rw-r--r--src/quick/items/qquickitemsmodule.cpp48
-rw-r--r--src/quick/items/qquickitemview.cpp115
-rw-r--r--src/quick/items/qquickitemview_p_p.h37
-rw-r--r--src/quick/items/qquickitemviewfxitem.cpp165
-rw-r--r--src/quick/items/qquickitemviewfxitem_p_p.h108
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp2
-rw-r--r--src/quick/items/qquicklistview.cpp56
-rw-r--r--src/quick/items/qquickloader.cpp19
-rw-r--r--src/quick/items/qquickloader_p_p.h3
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp3
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h4
-rw-r--r--src/quick/items/qquickpainteditem.h1
-rw-r--r--src/quick/items/qquickpositioners.cpp24
-rw-r--r--src/quick/items/qquickrectangle.cpp121
-rw-r--r--src/quick/items/qquickrectangle_p.h17
-rw-r--r--src/quick/items/qquickrectangle_p_p.h2
-rw-r--r--src/quick/items/qquickrendercontrol.cpp9
-rw-r--r--src/quick/items/qquickscalegrid.cpp4
-rw-r--r--src/quick/items/qquickscalegrid_p_p.h12
-rw-r--r--src/quick/items/qquickscreen.cpp40
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp5
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h7
-rw-r--r--src/quick/items/qquickspriteengine_p.h2
-rw-r--r--src/quick/items/qquicktableview.cpp2034
-rw-r--r--src/quick/items/qquicktableview_p.h173
-rw-r--r--src/quick/items/qquicktableview_p_p.h388
-rw-r--r--src/quick/items/qquicktext.cpp51
-rw-r--r--src/quick/items/qquicktext_p.h12
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquickwindow.cpp258
-rw-r--r--src/quick/items/qquickwindow.h1
-rw-r--r--src/quick/items/qquickwindow_p.h10
51 files changed, 4004 insertions, 667 deletions
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index eee64c9663..06dddabb65 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -735,7 +735,7 @@ void QQuickCanvasItem::updatePolish()
for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
function = it.value().value();
- jsCall->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
+ jsCall->args[0] = QV4::Value::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
function->call(jsCall);
}
}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 2c115f4d3c..32336b5baf 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -563,6 +563,8 @@ struct QQuickJSContext2D : public QV4::Object
static QV4::ReturnedValue method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
@@ -596,7 +598,7 @@ public:
static QV4::Heap::QQuickJSContext2DPrototype *create(QV4::ExecutionEngine *engine)
{
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocObject<QQuickJSContext2DPrototype>());
+ QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocate<QQuickJSContext2DPrototype>());
o->defineDefaultProperty(QStringLiteral("quadraticCurveTo"), method_quadraticCurveTo, 0);
o->defineDefaultProperty(QStringLiteral("restore"), method_restore, 0);
@@ -641,6 +643,8 @@ public:
o->defineDefaultProperty(QStringLiteral("createLinearGradient"), method_createLinearGradient, 0);
o->defineDefaultProperty(QStringLiteral("strokeRect"), method_strokeRect, 0);
o->defineDefaultProperty(QStringLiteral("closePath"), method_closePath, 0);
+ o->defineDefaultProperty(QStringLiteral("setLineDash"), method_setLineDash, 0);
+ o->defineDefaultProperty(QStringLiteral("getLineDash"), method_getLineDash, 0);
o->defineAccessorProperty(QStringLiteral("canvas"), QQuickJSContext2DPrototype::method_get_canvas, nullptr);
return o->d();
@@ -690,6 +694,8 @@ public:
static QV4::ReturnedValue method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -908,8 +914,8 @@ struct QQuickJSContext2DPixelData : public QV4::Object
V4_OBJECT2(QQuickJSContext2DPixelData, QV4::Object)
V4_NEEDS_DESTROY
- static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty);
+ static bool virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, Value *receiver);
static QV4::ReturnedValue proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -938,7 +944,7 @@ struct QQuickJSContext2DImageData : public QV4::Object
void QV4::Heap::QQuickJSContext2DImageData::init()
{
Object::init();
- pixelData = QV4::Primitive::undefinedValue();
+ pixelData = QV4::Value::undefinedValue();
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
@@ -954,19 +960,19 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
{
QV4::Scope scope(v4);
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DPixelData>());
+ QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DPixelData>());
QV4::ScopedObject p(scope, ed->pixelArrayProto.value());
- pixelData->setPrototype(p);
+ pixelData->setPrototypeOf(p);
if (image.isNull()) {
*pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
pixelData->d()->image->fill(0x00000000);
} else {
- Q_ASSERT(image.width()== qRound(w * image.devicePixelRatio()) && image.height() == qRound(h * image.devicePixelRatio()));
+ Q_ASSERT(image.width()== qRound(w * image.devicePixelRatioF()) && image.height() == qRound(h * image.devicePixelRatioF()));
*pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
}
- QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DImageData>());
+ QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
imageData->d()->pixelData = pixelData.asReturnedValue();
return imageData.asReturnedValue();
}
@@ -1406,7 +1412,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionOb
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if (value->as<Object>()) {
QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
@@ -1461,7 +1467,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(const QV4::FunctionObj
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if ((value->isString() && value->toQString() == QLatin1String("WindingFill"))
|| (value->isInt32() && value->integerValue() == Qt::WindingFill)) {
@@ -1515,7 +1521,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::Function
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if (value->as<Object>()) {
QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
@@ -1582,9 +1588,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const
}
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
RETURN_RESULT(*gradient);
}
@@ -1634,9 +1640,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
RETURN_RESULT(*gradient);
}
@@ -1678,9 +1684,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QConicalGradient(x, y, angle);
RETURN_RESULT(*gradient);
}
@@ -1738,7 +1744,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::F
CHECK_CONTEXT(r)
if (argc >= 2) {
- QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QColor color = scope.engine->toVariant(argv[0], qMetaTypeId<QColor>()).value<QColor>();
if (color.isValid()) {
@@ -1963,6 +1969,122 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionO
RETURN_UNDEFINED();
}
+/*!
+ \qmlmethod array QtQuick::Context2D::getLineDash()
+ \since QtQuick 2.11
+ Returns an array of qreals representing the dash pattern of the line.
+
+ \sa setLineDash()
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ const QVector<qreal> pattern = r->d()->context->state.lineDash;
+ QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject(pattern.size()));
+ array->arrayReserve(pattern.size());
+ for (int i = 0; i < pattern.size(); i++)
+ array->put(i, QV4::Value::fromDouble(pattern[i]));
+
+ array->setArrayLengthUnchecked(pattern.size());
+
+ RETURN_RESULT(*array);
+}
+
+/*!
+ \qmlmethod QtQuick::Context2D::setLineDash(array pattern)
+ \since QtQuick 2.11
+ Sets the dash pattern to the given pattern
+
+ \a pattern a list of numbers that specifies distances to alternately draw a line and a gap.
+
+ If the number of elements in the array is odd, the elements of the array get copied
+ and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25].
+
+ \table 100%
+ \row
+ \li \inlineimage qml-item-canvas-lineDash.png
+ \li
+ \code
+ var space = 4
+ ctx.setLineDash([1, space, 3, space, 9, space, 27, space, 9, space])
+ ...
+ ctx.stroke();
+ \endcode
+ \endtable
+
+ \sa setLineDash
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ if (!argc)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedArrayObject array(scope, argv[0]);
+ if (!array)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedValue v(scope);
+ const uint arrayLength = array->getLength();
+ QVector<qreal> dashes;
+ dashes.reserve(arrayLength);
+ for (uint i = 0; i < arrayLength; ++i) {
+ v = array->get(i);
+ const double number = v->toNumber();
+
+ if (!qt_is_finite(number) || (number < 0))
+ RETURN_UNDEFINED();
+
+ dashes.append(v->toNumber());
+ }
+ if (dashes.size() % 2 != 0) {
+ dashes += dashes;
+ }
+
+ r->d()->context->state.lineDash = dashes;
+ r->d()->context->buffer()->setLineDash(dashes);
+
+ RETURN_UNDEFINED();
+}
+
+/*!
+ \qmlproperty real QtQuick::Context2D::lineDashOffset
+ \since QtQuick 2.11
+
+ Holds the current line dash offset
+ The default line dash ofset value is 0
+ */
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ RETURN_RESULT(QV4::Encode(r->d()->context->state.lineDashOffset));
+}
+
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ const qreal offset = argc ? argv[0].toNumber() : -1;
+
+ if (qt_is_finite(offset) && offset != r->d()->context->state.lineDashOffset) {
+ r->d()->context->state.lineDashOffset = offset;
+ r->d()->context->buffer()->setLineDashOffset(offset);
+ }
+ RETURN_UNDEFINED();
+}
+
+
// shadows
/*!
\qmlproperty real QtQuick::Context2D::shadowBlur
@@ -2096,7 +2218,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_path(const QV4::FunctionObject
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
r->d()->context->beginPath();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, value);
if (!!qobjectWrapper) {
@@ -2593,7 +2715,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::F
bool pointInPath = false;
if (argc >= 2)
pointInPath = r->d()->context->isPointInPath(argv[0].toNumber(), argv[1].toNumber());
- RETURN_RESULT(QV4::Primitive::fromBoolean(pointInPath).asReturnedValue());
+ RETURN_RESULT(QV4::Value::fromBoolean(pointInPath).asReturnedValue());
}
QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
@@ -2652,7 +2774,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QFont font = qt_font_from_string(s->toQString(), r->d()->context->state.font);
@@ -2704,7 +2826,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionOb
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QString textAlign = s->toQString();
@@ -2771,7 +2893,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::Functio
QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QString textBaseline = s->toQString();
@@ -2858,7 +2980,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::Fun
uint width = fm.width(argv[0].toQStringNoThrow());
QV4::ScopedObject tm(scope, scope.engine->newObject());
tm->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
- QV4::ScopedValue(scope, QV4::Primitive::fromDouble(width)));
+ QV4::ScopedValue(scope, QV4::Value::fromDouble(width)));
RETURN_RESULT(*tm);
}
RETURN_UNDEFINED();
@@ -3130,8 +3252,12 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::Funct
RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
}
-QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
+QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
{
+ if (!id.isArrayIndex())
+ return QV4::Object::virtualGet(m, id, receiver, hasProperty);
+
+ uint index = id.asArrayIndex();
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
@@ -3156,19 +3282,24 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
return QV4::Encode(qAlpha(*pixel));
}
}
+
if (hasProperty)
*hasProperty = false;
return QV4::Encode::undefined();
}
-bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value)
+bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
{
+ if (!id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
if (scope.hasException())
return false;
+ uint index = id.asArrayIndex();
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
const int v = value.toInt32();
@@ -4303,6 +4434,7 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4)
proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth);
proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign);
proto->defineAccessorProperty(QStringLiteral("shadowBlur"), QQuickJSContext2D::method_get_shadowBlur, QQuickJSContext2D::method_set_shadowBlur);
+ proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), QQuickJSContext2D::method_get_lineDashOffset, QQuickJSContext2D::method_set_lineDashOffset);
contextPrototype = proto;
proto = scope.engine->newObject();
@@ -4366,6 +4498,10 @@ void QQuickContext2D::popState()
if (newState.shadowOffsetY != state.shadowOffsetY)
buffer()->setShadowOffsetY(newState.shadowOffsetY);
+
+ if (newState.lineDash != state.lineDash)
+ buffer()->setLineDash(newState.lineDash);
+
m_path = state.matrix.map(m_path);
state = newState;
m_path = state.matrix.inverted().map(m_path);
@@ -4399,9 +4535,9 @@ void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
QQuickContext2DEngineData *ed = engineData(engine);
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocObject<QQuickJSContext2D>());
+ QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
QV4::ScopedObject p(scope, ed->contextPrototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
wrapper->d()->context = this;
m_v4value = wrapper;
}
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 4cc07027b1..1ece6796f3 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -112,6 +112,8 @@ public:
LineWidth,
LineCap,
LineJoin,
+ LineDash,
+ LineDashOffset,
MiterLimit,
ShadowOffsetX,
ShadowOffsetY,
@@ -142,6 +144,7 @@ public:
, lineWidth(1)
, lineCap(Qt::FlatCap)
, lineJoin(Qt::MiterJoin)
+ , lineDashOffset(0)
, miterLimit(10)
, shadowOffsetX(0)
, shadowOffsetY(0)
@@ -170,6 +173,8 @@ public:
qreal lineWidth;
Qt::PenCapStyle lineCap;
Qt::PenJoinStyle lineJoin;
+ QVector<qreal> lineDash;
+ qreal lineDashOffset;
qreal miterLimit;
qreal shadowOffsetX;
qreal shadowOffsetY;
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 30895d9b0e..55ebbe907c 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -192,6 +192,10 @@ QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
pen.setJoinStyle(state.lineJoin);
pen.setMiterLimit(state.miterLimit);
pen.setBrush(state.strokeStyle);
+ if (!state.lineDash.isEmpty()) {
+ pen.setDashPattern(state.lineDash);
+ }
+ pen.setDashOffset(state.lineDashOffset);
return pen;
}
@@ -361,6 +365,28 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
p->setPen(nPen);
break;
}
+ case QQuickContext2D::LineDash:
+ {
+ const qreal count = takeReal();
+ QVector<qreal> pattern;
+ pattern.reserve(count);
+ for (uint i = 0; i < count; i++) {
+ pattern.append(takeReal());
+ }
+ state.lineDash = pattern;
+ QPen nPen = p->pen();
+ nPen.setDashPattern(pattern);
+ p->setPen(nPen);
+ break;
+ }
+ case QQuickContext2D::LineDashOffset:
+ {
+ state.lineDashOffset = takeReal();
+ QPen nPen = p->pen();
+ nPen.setDashOffset(state.lineDashOffset);
+ p->setPen(nPen);
+ break;
+ }
case QQuickContext2D::MiterLimit:
{
state.miterLimit = takeMiterLimit();
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
index 2a1ac7304e..c3e844c929 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -201,6 +201,20 @@ public:
ints << join;
}
+ inline void setLineDash(const QVector<qreal> &pattern)
+ {
+ commands << QQuickContext2D::LineDash;
+ reals << pattern.length();
+ for (qreal r : pattern)
+ reals << r;
+ }
+
+ inline void setLineDashOffset( qreal offset)
+ {
+ commands << QQuickContext2D::LineDashOffset;
+ reals << offset;
+ }
+
inline void setMiterLimit( qreal limit)
{
commands << QQuickContext2D::MiterLimit;
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 1acb3b5265..e649b5429d 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -122,9 +122,11 @@ qtConfig(quick-gridview) {
qtConfig(quick-itemview) {
HEADERS += \
+ $$PWD/qquickitemviewfxitem_p_p.h \
$$PWD/qquickitemview_p.h \
$$PWD/qquickitemview_p_p.h
SOURCES += \
+ $$PWD/qquickitemviewfxitem.cpp \
$$PWD/qquickitemview.cpp
}
@@ -142,6 +144,14 @@ qtConfig(quick-listview) {
$$PWD/qquicklistview.cpp
}
+qtConfig(quick-tableview) {
+ HEADERS += \
+ $$PWD/qquicktableview_p.h \
+ $$PWD/qquicktableview_p_p.h
+ SOURCES += \
+ $$PWD/qquicktableview.cpp
+}
+
qtConfig(quick-pathview) {
HEADERS += \
$$PWD/qquickpathview_p.h \
diff --git a/src/quick/items/qquickanchors_p.h b/src/quick/items/qquickanchors_p.h
index 931b963534..c7995cb7a8 100644
--- a/src/quick/items/qquickanchors_p.h
+++ b/src/quick/items/qquickanchors_p.h
@@ -108,6 +108,7 @@ public:
Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor
};
Q_DECLARE_FLAGS(Anchors, Anchor)
+ Q_FLAG(Anchors)
QQuickAnchorLine left() const;
void setLeft(const QQuickAnchorLine &edge);
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 603d5a3120..18adb4e992 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -268,6 +268,16 @@ QT_BEGIN_NAMESPACE
Stops, then starts the sprite animation.
*/
+/*!
+ \qmlsignal QtQuick::AnimatedSprite::finished()
+ \since 5.12
+
+ This signal is emitted when the sprite has finished animating.
+
+ It is not emitted when running is set to \c false, nor for sprites whose
+ \l loops property is set to \c AnimatedSprite.Infinite.
+*/
+
QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
QQuickItem(*(new QQuickAnimatedSpritePrivate), parent)
{
@@ -806,6 +816,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
frameAt = 0;
d->m_running = false;
emit runningChanged(false);
+ emit finished();
maybeUpdate();
}
} else {
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index d7e60b909c..ff59591c9f 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -135,6 +135,8 @@ Q_SIGNALS:
void loopsChanged(int arg);
void currentFrameChanged(int arg);
+ Q_REVISION(12) void finished();
+
public Q_SLOTS:
void start();
void stop();
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index d49829096f..e4e766a129 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -591,10 +591,18 @@ void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border,
*innerTargetRect = *targetRect;
if (border) {
- *innerSourceRect = QRectF(border->left() * devicePixelRatio / qreal(sourceSize.width()),
- border->top() * devicePixelRatio / qreal(sourceSize.height()),
- qMax<qreal>(0, sourceSize.width() - (border->right() + border->left()) * devicePixelRatio) / sourceSize.width(),
- qMax<qreal>(0, sourceSize.height() - (border->bottom() + border->top()) * devicePixelRatio) / sourceSize.height());
+ qreal borderLeft = border->left() * devicePixelRatio;
+ qreal borderRight = border->right() * devicePixelRatio;
+ qreal borderTop = border->top() * devicePixelRatio;
+ qreal borderBottom = border->bottom() * devicePixelRatio;
+ if (borderLeft + borderRight > sourceSize.width() && borderLeft < sourceSize.width())
+ borderRight = sourceSize.width() - borderLeft;
+ if (borderTop + borderBottom > sourceSize.height() && borderTop < sourceSize.height())
+ borderBottom = sourceSize.height() - borderTop;
+ *innerSourceRect = QRectF(QPointF(borderLeft / qreal(sourceSize.width()),
+ borderTop * devicePixelRatio / qreal(sourceSize.height())),
+ QPointF((sourceSize.width() - borderRight) / qreal(sourceSize.width()),
+ (sourceSize.height() - borderBottom) / qreal(sourceSize.height()))),
*innerTargetRect = QRectF(border->left(),
border->top(),
qMax<qreal>(0, targetSize.width() - (border->right() + border->left())),
@@ -604,14 +612,16 @@ void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border,
qreal hTiles = 1;
qreal vTiles = 1;
const QSizeF innerTargetSize = innerTargetRect->size() * devicePixelRatio;
- if (innerSourceRect->width() != 0
- && horizontalTileMode != QQuickBorderImage::Stretch) {
+ if (innerSourceRect->width() <= 0)
+ hTiles = 0;
+ else if (horizontalTileMode != QQuickBorderImage::Stretch) {
hTiles = innerTargetSize.width() / qreal(innerSourceRect->width() * sourceSize.width());
if (horizontalTileMode == QQuickBorderImage::Round)
hTiles = qCeil(hTiles);
}
- if (innerSourceRect->height() != 0
- && verticalTileMode != QQuickBorderImage::Stretch) {
+ if (innerSourceRect->height() <= 0)
+ vTiles = 0;
+ else if (verticalTileMode != QQuickBorderImage::Stretch) {
vTiles = innerTargetSize.height() / qreal(innerSourceRect->height() * sourceSize.height());
if (verticalTileMode == QQuickBorderImage::Round)
vTiles = qCeil(vTiles);
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index d4aba31e28..31c56b7cb7 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -642,6 +642,11 @@ QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d)
return dev;
}
+const QTouchDevice *QQuickPointerDevice::qTouchDevice() const
+{
+ return g_touchDevices->key(const_cast<QQuickPointerDevice *>(this));
+}
+
QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
{
return g_touchDevices->values();
@@ -901,7 +906,7 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b
if (grabber) {
grabber->onGrabChanged(grabber, GrabExclusive, this);
for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
- if (passiveGrabber != grabber)
+ if (!passiveGrabber.isNull() && passiveGrabber != grabber)
passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
}
}
@@ -1291,7 +1296,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
- m_mousePoint->clearPassiveGrabbers();
+ m_point->clearPassiveGrabbers();
Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
@@ -1305,13 +1310,13 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
default:
break;
}
- m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ m_point->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
return this;
}
-void QQuickPointerMouseEvent::localize(QQuickItem *target)
+void QQuickSinglePointEvent::localize(QQuickItem *target)
{
- m_mousePoint->localizePosition(target);
+ m_point->localizePosition(target);
}
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
@@ -1412,35 +1417,54 @@ QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event)
break;
}
quint64 deviceId = QTouchDevicePrivate::get(const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected
- m_gesturePoint->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
+ m_point->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
return this;
}
-
-void QQuickPointerNativeGestureEvent::localize(QQuickItem *target)
-{
- m_gesturePoint->localizePosition(target);
-}
#endif // QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
+QQuickEventPoint *QQuickSinglePointEvent::point(int i) const
+{
if (i == 0)
- return m_mousePoint;
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
- if (i >= 0 && i < m_pointCount)
- return m_touchPoints.at(i);
- return nullptr;
+QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event)
+{
+ m_event = static_cast<QInputEvent*>(event);
+ if (!event)
+ return this;
+#if QT_CONFIG(wheelevent)
+ if (event->type() == QEvent::Wheel) {
+ auto ev = static_cast<QWheelEvent*>(event);
+ m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
+ // m_button = Qt::NoButton;
+ m_pressedButtons = ev->buttons();
+ m_angleDelta = QVector2D(ev->angleDelta());
+ m_pixelDelta = QVector2D(ev->pixelDelta());
+ m_phase = ev->phase();
+ m_synthSource = ev->source();
+ m_inverted = ev->inverted();
+
+ m_point->reset(Qt::TouchPointMoved, ev->posF(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ }
+#endif
+ // TODO else if (event->type() == QEvent::Scroll) ...
+ return this;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::point(int i) const {
- if (i == 0)
- return m_gesturePoint;
+void QQuickPointerScrollEvent::localize(QQuickItem *target)
+{
+ m_point->localizePosition(target);
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const
+{
+ if (i >= 0 && i < m_pointCount)
+ return m_touchPoints.at(i);
return nullptr;
}
-#endif // QT_CONFIG(gestures)
QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
: QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
@@ -1454,17 +1478,19 @@ QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
return static_cast<QQuickPointerEvent *>(parent());
}
-bool QQuickPointerMouseEvent::allPointsAccepted() const {
- return m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allPointsAccepted() const
+{
+ return m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
- return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allUpdatedPointsAccepted() const
+{
+ return m_point->state() == QQuickEventPoint::Pressed || m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allPointsGrabbed() const
+bool QQuickSinglePointEvent::allPointsGrabbed() const
{
- return m_mousePoint->exclusiveGrabber() != nullptr;
+ return m_point->exclusiveGrabber() != nullptr;
}
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
@@ -1477,10 +1503,10 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
/*!
Returns the exclusive grabber of this event, if any, in a vector.
*/
-QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
+QVector<QObject *> QQuickSinglePointEvent::exclusiveGrabbers() const
{
QVector<QObject *> result;
- if (QObject *grabber = m_mousePoint->exclusiveGrabber())
+ if (QObject *grabber = m_point->exclusiveGrabber())
result << grabber;
return result;
}
@@ -1488,17 +1514,18 @@ QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
/*!
Remove all passive and exclusive grabbers of this event, without notifying.
*/
-void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabberItem(nullptr);
- m_mousePoint->clearPassiveGrabbers();
+void QQuickSinglePointEvent::clearGrabbers() const
+{
+ m_point->setGrabberItem(nullptr);
+ m_point->clearPassiveGrabbers();
}
/*!
Returns whether the given \a handler is the exclusive grabber of this event.
*/
-bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+bool QQuickSinglePointEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
{
- return m_mousePoint->exclusiveGrabber() == handler;
+ return handler && (m_point->exclusiveGrabber() == handler);
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -1526,7 +1553,8 @@ bool QQuickPointerMouseEvent::isReleaseEvent() const
return me && me->type() == QEvent::MouseButtonRelease;
}
-bool QQuickPointerTouchEvent::allPointsAccepted() const {
+bool QQuickPointerTouchEvent::allPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
return false;
@@ -1534,7 +1562,8 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
@@ -1571,7 +1600,8 @@ QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
Remove all passive and exclusive grabbers of all touchpoints in this event,
without notifying.
*/
-void QQuickPointerTouchEvent::clearGrabbers() const {
+void QQuickPointerTouchEvent::clearGrabbers() const
+{
for (auto point: m_touchPoints) {
point->setGrabberItem(nullptr);
point->clearPassiveGrabbers();
@@ -1629,7 +1659,8 @@ QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() cons
If the touchpoint cannot be found, this returns nullptr.
Ownership of the event is NOT transferred to the caller.
*/
-QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const {
+QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const
+{
const QTouchEvent::TouchPoint *p = touchPointById(pointID);
if (!p)
return nullptr;
@@ -1669,33 +1700,6 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
}
#if QT_CONFIG(gestures)
-/*!
- Returns the exclusive grabber of this event, if any, in a vector.
-*/
-QVector<QObject *> QQuickPointerNativeGestureEvent::exclusiveGrabbers() const
-{
- QVector<QObject *> result;
- if (QObject *grabber = m_gesturePoint->exclusiveGrabber())
- result << grabber;
- return result;
-}
-
-/*!
- Remove all passive and exclusive grabbers of this event, without notifying.
-*/
-void QQuickPointerNativeGestureEvent::clearGrabbers() const {
- m_gesturePoint->setGrabberItem(nullptr);
- m_gesturePoint->clearPassiveGrabbers();
-}
-
-/*!
- Returns whether the given \a handler is the exclusive grabber of this event.
-*/
-bool QQuickPointerNativeGestureEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
-{
- return m_gesturePoint->exclusiveGrabber() == handler;
-}
-
bool QQuickPointerNativeGestureEvent::isPressEvent() const
{
return type() == Qt::BeginNativeGesture;
@@ -1729,6 +1733,39 @@ qreal QQuickPointerNativeGestureEvent::value() const
#endif // QT_CONFIG(gestures)
/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are placed
+ on the touchpad and scrolling begins. On other devices where this
+ information is not available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isPressEvent() const
+{
+ return phase() == Qt::ScrollBegin;
+}
+
+/*!
+ Returns true when the scroll event has Qt::ScrollUpdate phase, or when the
+ phase is unknown. Some multi-touch-capable touchpads and trackpads provide
+ phase information; whereas ordinary mouse wheels and other types of
+ trackpads do not, and in such cases this is always true.
+*/
+bool QQuickPointerScrollEvent::isUpdateEvent() const
+{
+ return phase() == Qt::ScrollUpdate || phase() == Qt::NoScrollPhase;
+}
+
+/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are lifted
+ from the touchpad. On other devices where this information is not
+ available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isReleaseEvent() const
+{
+ return phase() == Qt::ScrollEnd;
+}
+
+/*!
\internal
Returns a pointer to the QQuickEventPoint which has the \a pointId as
\l {QQuickEventPoint::pointId}{pointId}.
@@ -1736,13 +1773,15 @@ qreal QQuickPointerNativeGestureEvent::value() const
\fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
- if (m_mousePoint && pointId == m_mousePoint->pointId())
- return m_mousePoint;
+QQuickEventPoint *QQuickSinglePointEvent::pointById(int pointId) const
+{
+ if (m_point && pointId == m_point->pointId())
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const
+{
auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
[pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
if (it != m_touchPoints.constEnd())
@@ -1750,21 +1789,14 @@ QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
return nullptr;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::pointById(int pointId) const {
- if (m_gesturePoint && pointId == m_gesturePoint->pointId())
- return m_gesturePoint;
- return nullptr;
-}
-#endif
-
/*!
\internal
Returns a pointer to the original TouchPoint which has the same
\l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
QTouchEvent, and if that point is found. Otherwise, returns nullptr.
*/
-const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const {
+const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const
+{
const QTouchEvent *ev = asTouchEvent();
if (!ev)
return nullptr;
@@ -1875,24 +1907,10 @@ QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
return static_cast<QTouchEvent *>(m_event);
}
-#if QT_CONFIG(gestures)
-bool QQuickPointerNativeGestureEvent::allPointsAccepted() const {
- return m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allUpdatedPointsAccepted() const {
- return m_gesturePoint->state() == QQuickEventPoint::Pressed || m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allPointsGrabbed() const
-{
- return m_gesturePoint->exclusiveGrabber() != nullptr;
-}
-#endif // QT_CONFIG(gestures)
-
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
if (!dev) {
@@ -1914,7 +1932,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickPointerEvent(";
@@ -1933,7 +1952,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index b2642735f2..d1a8bbd901 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -69,6 +69,7 @@ class QQuickPointerMouseEvent;
#if QT_CONFIG(gestures)
class QQuickPointerNativeGestureEvent;
#endif
+class QQuickPointerScrollEvent;
class QQuickPointerTabletEvent;
class QQuickPointerTouchEvent;
class QQuickPointerHandler;
@@ -76,12 +77,12 @@ class QQuickPointerHandler;
class QQuickKeyEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(int key READ key)
- Q_PROPERTY(QString text READ text)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat)
- Q_PROPERTY(int count READ count)
- Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode)
+ Q_PROPERTY(int key READ key CONSTANT)
+ Q_PROPERTY(QString text READ text CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT)
+ Q_PROPERTY(int count READ count CONSTANT)
+ Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
@@ -124,16 +125,16 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(int button READ button)
- Q_PROPERTY(int buttons READ buttons)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(int source READ source REVISION 7)
- Q_PROPERTY(bool wasHeld READ wasHeld)
- Q_PROPERTY(bool isClick READ isClick)
+ Q_PROPERTY(qreal x READ x CONSTANT)
+ Q_PROPERTY(qreal y READ y CONSTANT)
+ Q_PROPERTY(int button READ button CONSTANT)
+ Q_PROPERTY(int buttons READ buttons CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(int source READ source CONSTANT REVISION 7)
+ Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT)
+ Q_PROPERTY(bool isClick READ isClick CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- Q_REVISION(11) Q_PROPERTY(int flags READ flags)
+ Q_PROPERTY(int flags READ flags CONSTANT REVISION 11)
public:
QQuickMouseEvent()
@@ -192,13 +193,13 @@ private:
class QQuickWheelEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(QPoint angleDelta READ angleDelta)
- Q_PROPERTY(QPoint pixelDelta READ pixelDelta)
- Q_PROPERTY(int buttons READ buttons)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(bool inverted READ inverted)
+ Q_PROPERTY(qreal x READ x CONSTANT)
+ Q_PROPERTY(qreal y READ y CONSTANT)
+ Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT)
+ Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT)
+ Q_PROPERTY(int buttons READ buttons CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(bool inverted READ inverted CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
@@ -258,15 +259,15 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent)
- Q_PROPERTY(QPointF position READ position)
- Q_PROPERTY(QPointF scenePosition READ scenePosition)
- Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
- Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
- Q_PROPERTY(State state READ state)
- Q_PROPERTY(int pointId READ pointId)
- Q_PROPERTY(qreal timeHeld READ timeHeld)
- Q_PROPERTY(QVector2D velocity READ velocity)
+ Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent CONSTANT)
+ Q_PROPERTY(QPointF position READ position CONSTANT)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition CONSTANT)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition CONSTANT)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition CONSTANT)
+ Q_PROPERTY(State state READ state CONSTANT)
+ Q_PROPERTY(int pointId READ pointId CONSTANT)
+ Q_PROPERTY(qreal timeHeld READ timeHeld CONSTANT)
+ Q_PROPERTY(QVector2D velocity READ velocity CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber)
@@ -280,7 +281,7 @@ public:
Q_DECLARE_FLAGS(States, State)
Q_FLAG(States)
- enum GrabState {
+ enum GrabTransition {
GrabPassive = 0x01,
UngrabPassive = 0x02,
CancelGrabPassive = 0x03,
@@ -289,7 +290,7 @@ public:
UngrabExclusive = 0x20,
CancelGrabExclusive = 0x30,
};
- Q_ENUM(GrabState)
+ Q_ENUM(GrabTransition)
QQuickEventPoint(QQuickPointerEvent *parent);
@@ -385,10 +386,10 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPointerDevice *device READ device)
- Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
- Q_PROPERTY(Qt::MouseButtons button READ button)
- Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
+ Q_PROPERTY(QQuickPointerDevice *device READ device CONSTANT)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(Qt::MouseButtons button READ button CONSTANT)
+ Q_PROPERTY(Qt::MouseButtons buttons READ buttons CONSTANT)
public:
QQuickPointerEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
@@ -419,16 +420,18 @@ public: // helpers for C++ only (during event delivery)
#if QT_CONFIG(gestures)
virtual QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() { return nullptr; }
#endif
+ virtual QQuickPointerScrollEvent *asPointerScrollEvent() { return nullptr; }
virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; }
virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; }
virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
#if QT_CONFIG(gestures)
virtual const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const { return nullptr; }
#endif
+ virtual const QQuickPointerScrollEvent *asPointerScrollEvent() const { return nullptr; }
virtual bool allPointsAccepted() const = 0;
virtual bool allUpdatedPointsAccepted() const = 0;
virtual bool allPointsGrabbed() const = 0;
- bool isAccepted() { return m_event->isAccepted(); }
+ bool isAccepted() { return m_event ? m_event->isAccepted() : false; }
void setAccepted(bool accepted) { if (m_event) m_event->setAccepted(accepted); }
QVector<QPointF> unacceptedPressedPointScenePositions() const;
@@ -439,7 +442,7 @@ public: // helpers for C++ only (during event delivery)
virtual void clearGrabbers() const = 0;
virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0;
- ulong timestamp() const { return m_event->timestamp(); }
+ ulong timestamp() const { return m_event ? m_event->timestamp() : 0; }
protected:
QQuickPointerDevice *m_device;
@@ -452,21 +455,14 @@ protected:
Q_DISABLE_COPY(QQuickPointerEvent)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointEvent : public QQuickPointerEvent
{
Q_OBJECT
public:
- QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { }
+ QQuickSinglePointEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickPointerEvent(parent, device), m_point(new QQuickEventPoint(this)) { }
- QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
- bool isPressEvent() const override;
- bool isDoubleClickEvent() const override;
- bool isUpdateEvent() const override;
- bool isReleaseEvent() const override;
- QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
- const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
int pointCount() const override { return 1; }
QQuickEventPoint *point(int i) const override;
QQuickEventPoint *pointById(int pointId) const override;
@@ -477,10 +473,28 @@ public:
void clearGrabbers() const override;
bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
- QMouseEvent *asMouseEvent(const QPointF& localPos) const;
+protected:
+ QQuickEventPoint *m_point;
-private:
- QQuickEventPoint *m_mousePoint;
+ Q_DISABLE_COPY(QQuickSinglePointEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ bool isDoubleClickEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
+ const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
+
+ QMouseEvent *asMouseEvent(const QPointF& localPos) const;
Q_DISABLE_COPY(QQuickPointerMouseEvent)
};
@@ -528,7 +542,7 @@ private:
};
#if QT_CONFIG(gestures)
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickSinglePointEvent
{
Q_OBJECT
Q_PROPERTY(Qt::NativeGestureType type READ type CONSTANT)
@@ -536,34 +550,65 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPoin
public:
QQuickPointerNativeGestureEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_gesturePoint(new QQuickEventPoint(this)) { }
+ : QQuickSinglePointEvent(parent, device) { }
QQuickPointerEvent *reset(QEvent *) override;
- void localize(QQuickItem *target) override;
bool isPressEvent() const override;
bool isUpdateEvent() const override;
bool isReleaseEvent() const override;
QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() override { return this; }
const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const override { return this; }
- int pointCount() const override { return 1; }
- QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(int pointId) const override;
- bool allPointsAccepted() const override;
- bool allUpdatedPointsAccepted() const override;
- bool allPointsGrabbed() const override;
- QVector<QObject *> exclusiveGrabbers() const override;
- void clearGrabbers() const override;
- bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
Qt::NativeGestureType type() const;
qreal value() const;
-private:
- QQuickEventPoint *m_gesturePoint;
-
Q_DISABLE_COPY(QQuickPointerNativeGestureEvent)
};
#endif // QT_CONFIG(gestures)
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerScrollEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector2D angleDelta READ angleDelta CONSTANT)
+ Q_PROPERTY(QVector2D pixelDelta READ pixelDelta CONSTANT)
+ Q_PROPERTY(bool hasAngleDelta READ hasAngleDelta CONSTANT)
+ Q_PROPERTY(bool hasPixelDelta READ hasPixelDelta CONSTANT)
+ Q_PROPERTY(bool inverted READ isInverted CONSTANT)
+
+public:
+ QQuickPointerScrollEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
+ bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerScrollEvent *asPointerScrollEvent() override { return this; }
+ const QQuickPointerScrollEvent *asPointerScrollEvent() const override { return this; }
+ QVector2D angleDelta() const { return m_angleDelta; }
+ QVector2D pixelDelta() const { return m_pixelDelta; }
+ bool hasAngleDelta() const { return !angleDelta().isNull(); }
+ bool hasPixelDelta() const { return !pixelDelta().isNull(); }
+ bool isInverted() const { return m_inverted; }
+ Qt::ScrollPhase phase() const { return m_phase; }
+
+private:
+ // TODO add QQuickPointerDevice source() whenever QInputEvent is extended to have a source device
+ // then maybe Qt::MouseEventSource synthSource() will be obsolete... that's why it's not public now
+ Qt::MouseEventSource synthSource() const { return m_synthSource; }
+
+private:
+ QVector2D m_angleDelta;
+ QVector2D m_pixelDelta;
+ Qt::ScrollPhase m_phase = Qt::NoScrollPhase;
+ Qt::MouseEventSource m_synthSource = Qt::MouseEventNotSynthesized;
+ bool m_inverted = false;
+
+ friend class QQuickWindowPrivate;
+
+ Q_DISABLE_COPY(QQuickPointerScrollEvent)
+};
+
// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject
@@ -589,7 +634,6 @@ public:
AllDevices = 0x7FFF
};
Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
- Q_ENUM(DeviceType)
Q_FLAG(DeviceTypes)
enum PointerType : qint16 {
@@ -601,7 +645,6 @@ public:
AllPointerTypes = 0x7FFF
};
Q_DECLARE_FLAGS(PointerTypes, PointerType)
- Q_ENUM(PointerType)
Q_FLAG(PointerTypes)
enum CapabilityFlag : qint16 {
@@ -618,7 +661,6 @@ public:
YTilt = 0x1000
};
Q_DECLARE_FLAGS(Capabilities, CapabilityFlag)
- Q_ENUM(CapabilityFlag)
Q_FLAG(Capabilities)
DeviceType type() const { return m_deviceType; }
@@ -629,6 +671,7 @@ public:
int buttonCount() const { return m_buttonCount; }
QString name() const { return m_name; }
QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+ const QTouchDevice *qTouchDevice() const;
static QQuickPointerDevice *touchDevice(const QTouchDevice *d);
static QList<QQuickPointerDevice *> touchDevices();
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index cc35630fff..85045be411 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -69,10 +69,6 @@ static const int FlickThreshold = 15;
// will ensure the Flickable retains the grab on consecutive flicks.
static const int RetainGrabVelocity = 100;
-#ifdef Q_OS_OSX
-static const int MovementEndingTimerInterval = 100;
-#endif
-
// Currently std::round can't be used on Android when using ndk g++, so
// use C version instead. We could just define two versions of Round, one
// for float and one for double, but then only one of them would be used
@@ -248,6 +244,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, stealMouse(false), pressed(false)
, scrollingPhase(false), interactive(true), calcVelocity(false)
, pixelAligned(false)
+ , syncDrag(false)
, lastPosTime(-1)
, lastPressTime(0)
, deceleration(QML_FLICK_DEFAULTDECELERATION)
@@ -508,7 +505,8 @@ static bool fuzzyLessThanOrEqualTo(qreal a, qreal b)
void QQuickFlickablePrivate::updateBeginningEnd()
{
Q_Q(QQuickFlickable);
- bool atBoundaryChange = false;
+ bool atXBeginningChange = false, atXEndChange = false;
+ bool atYBeginningChange = false, atYEndChange = false;
// Vertical
const qreal maxyextent = -q->maxYExtent();
@@ -519,11 +517,11 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != vData.atBeginning) {
vData.atBeginning = atBeginning;
- atBoundaryChange = true;
+ atYBeginningChange = true;
}
if (atEnd != vData.atEnd) {
vData.atEnd = atEnd;
- atBoundaryChange = true;
+ atYEndChange = true;
}
// Horizontal
@@ -535,11 +533,11 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != hData.atBeginning) {
hData.atBeginning = atBeginning;
- atBoundaryChange = true;
+ atXBeginningChange = true;
}
if (atEnd != hData.atEnd) {
hData.atEnd = atEnd;
- atBoundaryChange = true;
+ atXEndChange = true;
}
if (vData.extentsChanged) {
@@ -560,8 +558,16 @@ void QQuickFlickablePrivate::updateBeginningEnd()
}
}
- if (atBoundaryChange)
+ if (atXEndChange || atYEndChange || atXBeginningChange || atYBeginningChange)
emit q->isAtBoundaryChanged();
+ if (atXEndChange)
+ emit q->atXEndChanged();
+ if (atXBeginningChange)
+ emit q->atXBeginningChanged();
+ if (atYEndChange)
+ emit q->atYEndChanged();
+ if (atYBeginningChange)
+ emit q->atYBeginningChanged();
if (visibleArea)
visibleArea->updateVisible();
@@ -984,6 +990,33 @@ void QQuickFlickable::setPixelAligned(bool align)
}
}
+/*!
+ \qmlproperty bool QtQuick::Flickable::synchronousDrag
+ \since 5.12
+
+ If this property is set to true, then when the mouse or touchpoint moves
+ far enough to begin dragging the content, the content will jump, such that
+ the content pixel which was under the cursor or touchpoint when pressed
+ remains under that point.
+
+ The default is \c false, which provides a smoother experience (no jump)
+ at the cost that some of the drag distance is "lost" at the beginning.
+*/
+bool QQuickFlickable::synchronousDrag() const
+{
+ Q_D(const QQuickFlickable);
+ return d->syncDrag;
+}
+
+void QQuickFlickable::setSynchronousDrag(bool v)
+{
+ Q_D(QQuickFlickable);
+ if (v != d->syncDrag) {
+ d->syncDrag = v;
+ emit synchronousDragChanged();
+ }
+}
+
qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event) const
{
if (0 != event->timestamp())
@@ -1100,7 +1133,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!vMoved)
vData.dragStartOffset = dy;
- qreal newY = dy + vData.pressPos - vData.dragStartOffset;
+ qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
// Recalculate bounds in case margins have changed, but use the content
// size estimate taken at the start of the drag in case the drag causes
// the estimate to be altered
@@ -1176,7 +1209,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!hMoved)
hData.dragStartOffset = dx;
- qreal newX = dx + hData.pressPos - hData.dragStartOffset;
+ qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
const qreal minX = hData.dragMinBound + hData.startMargin;
const qreal maxX = hData.dragMaxBound - hData.endMargin;
if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
@@ -1280,7 +1313,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
{
Q_Q(QQuickFlickable);
- if (!interactive || lastPosTime == -1)
+ if (!interactive || lastPosTime == -1 || event->buttons() == Qt::NoButton)
return;
qint64 currentTimestamp = computeCurrentTime(event);
@@ -1460,23 +1493,24 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
case Qt::ScrollUpdate:
if (d->scrollingPhase)
d->pressed = true;
-#ifdef Q_OS_MACOS
- // TODO eliminate this timer when ScrollMomentum has been added
- d->movementEndingTimer.start(MovementEndingTimerInterval, this);
-#endif
+ break;
+ case Qt::ScrollMomentum:
+ d->pressed = false;
+ d->scrollingPhase = false;
+ d->draggingEnding();
+ event->accept();
+ d->lastPosTime = -1;
break;
case Qt::ScrollEnd:
- // TODO most of this should be done at transition to ScrollMomentum phase,
- // then do what the movementEndingTimer triggers at transition to ScrollEnd phase
d->pressed = false;
d->scrollingPhase = false;
d->draggingEnding();
event->accept();
returnToBounds();
d->lastPosTime = -1;
-#ifdef Q_OS_MACOS
- d->movementEndingTimer.start(MovementEndingTimerInterval, this);
-#endif
+ d->stealMouse = false;
+ if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
+ movementEnding(true, true);
return;
}
@@ -1672,14 +1706,6 @@ void QQuickFlickable::timerEvent(QTimerEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
}
- } else if (event->timerId() == d->movementEndingTimer.timerId()) {
- d->movementEndingTimer.stop();
- if (!d->scrollingPhase) {
- d->pressed = false;
- d->stealMouse = false;
- if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
- movementEnding(true, true);
- }
}
}
@@ -1821,8 +1847,8 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
d->vData.velocity = yVelocity;
d->hData.vTime = d->vData.vTime = d->timeline.time();
- bool flickedX = d->flickX(xVelocity);
- bool flickedY = d->flickY(yVelocity);
+ const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(xVelocity);
+ const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(yVelocity);
if (flickedX)
d->hMoved = true;
@@ -2540,7 +2566,7 @@ void QQuickFlickablePrivate::draggingStarting()
void QQuickFlickablePrivate::draggingEnding()
{
Q_Q(QQuickFlickable);
- bool wasDragging = hData.dragging || vData.dragging;
+ const bool wasDragging = hData.dragging || vData.dragging;
if (hData.dragging) {
hData.dragging = false;
emit q->draggingHorizontallyChanged();
@@ -2549,12 +2575,14 @@ void QQuickFlickablePrivate::draggingEnding()
vData.dragging = false;
emit q->draggingVerticallyChanged();
}
- if (wasDragging && !hData.dragging && !vData.dragging) {
- emit q->draggingChanged();
- emit q->dragEnded();
+ if (wasDragging) {
+ if (!hData.dragging && !vData.dragging) {
+ emit q->draggingChanged();
+ emit q->dragEnded();
+ }
+ hData.inRebound = false;
+ vData.inRebound = false;
}
- hData.inRebound = false;
- vData.inRebound = false;
}
bool QQuickFlickablePrivate::isViewMoving() const
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 939e3af698..b7c4fa5b67 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -98,14 +98,15 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged)
- Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged)
+ Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY atXEndChanged)
+ Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY atYEndChanged)
+ Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY atXBeginningChanged)
+ Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY atYBeginningChanged)
Q_PROPERTY(QQuickFlickableVisibleArea *visibleArea READ visibleArea CONSTANT)
Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
+ Q_PROPERTY(bool synchronousDrag READ synchronousDrag WRITE setSynchronousDrag NOTIFY synchronousDragChanged REVISION 12)
Q_PROPERTY(qreal horizontalOvershoot READ horizontalOvershoot NOTIFY horizontalOvershootChanged REVISION 9)
Q_PROPERTY(qreal verticalOvershoot READ verticalOvershoot NOTIFY verticalOvershootChanged REVISION 9)
@@ -213,6 +214,9 @@ public:
bool pixelAligned() const;
void setPixelAligned(bool align);
+ bool synchronousDrag() const;
+ void setSynchronousDrag(bool v);
+
qreal horizontalOvershoot() const;
qreal verticalOvershoot() const;
@@ -259,9 +263,16 @@ Q_SIGNALS:
void dragStarted();
void dragEnded();
void pixelAlignedChanged();
+ Q_REVISION(12) void synchronousDragChanged();
Q_REVISION(9) void horizontalOvershootChanged();
Q_REVISION(9) void verticalOvershootChanged();
+ // The next four signals should be marked as Q_REVISION(12). See QTBUG-71243
+ void atXEndChanged();
+ void atYEndChanged();
+ void atXBeginningChanged();
+ void atYBeginningChanged();
+
protected:
bool childMouseEventFilter(QQuickItem *, QEvent *) override;
void mousePressEvent(QMouseEvent *event) override;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 08f069e830..c538cf7878 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -221,8 +221,8 @@ public:
bool interactive : 1;
bool calcVelocity : 1;
bool pixelAligned : 1;
+ bool syncDrag : 1;
QElapsedTimer timer;
- QBasicTimer movementEndingTimer;
qint64 lastPosTime;
qint64 lastPressTime;
QPointF lastPos;
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index f2fa66332c..272d4a4df5 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -417,11 +417,11 @@ qreal QQuickGridViewPrivate::contentXForPosition(qreal pos) const
if (flow == QQuickGridView::FlowLeftToRight) {
// vertical scroll
if (q->effectiveLayoutDirection() == Qt::LeftToRight) {
- return 0;
+ return -q->leftMargin();
} else {
qreal colSize = cellWidth;
- int columns = q->width()/colSize;
- return -q->width() + (cellWidth * columns);
+ int columns = (q->width() - q->leftMargin() - q->rightMargin()) / colSize;
+ return -q->width() + q->rightMargin() + (cellWidth * columns);
}
} else {
// horizontal scroll
@@ -444,16 +444,18 @@ qreal QQuickGridViewPrivate::contentYForPosition(qreal pos) const
} else {
// horizontal scroll
if (verticalLayoutDirection == QQuickItemView::TopToBottom)
- return 0;
+ return -q->topMargin();
else
- return -q->height();
+ return -q->height() + q->bottomMargin();
}
}
void QQuickGridViewPrivate::resetColumns()
{
Q_Q(QQuickGridView);
- qreal length = flow == QQuickGridView::FlowLeftToRight ? q->width() : q->height();
+ qreal length = flow == QQuickGridView::FlowLeftToRight
+ ? q->width() - q->leftMargin() - q->rightMargin()
+ : q->height() - q->topMargin() - q->bottomMargin();
columns = qMax(1, qFloor(length / colSize()));
}
@@ -914,7 +916,7 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
// if we've been dragged < rowSize()/2 then bias towards the next row
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
bias = rowSize()/2;
@@ -925,13 +927,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1013,7 +1015,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1030,7 +1032,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1309,13 +1311,14 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
- \qmlproperty model QtQuick::GridView::model
- This property holds the model providing data for the grid.
+ \qmlproperty model QtQuick::GridView::model
+ This property holds the model providing data for the grid.
The model provides the set of data that is used to create the items
- in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
- or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
- used, it must be a subclass of \l QAbstractItemModel or a simple list.
+ in the view. Models can be created directly in QML using \l ListModel,
+ \l XmlListModel, \l DelegateModel, or \l ObjectModel, or provided by C++
+ model classes. If a C++ model class is used, it must be a subclass of
+ \l QAbstractItemModel or a simple list.
\sa {qml-data-models}{Data Models}
*/
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 154e3241b1..064406ee3c 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -67,6 +67,7 @@
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#include <QtQuick/private/qquickhoverhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <private/qv4engine_p.h>
@@ -356,7 +357,7 @@ void QQuickItemKeyFilter::componentComplete()
\qmltype KeyNavigation
\instantiates QQuickKeyNavigationAttached
\inqmlmodule QtQuick
- \ingroup qtquick-input
+ \ingroup qtquick-input-handlers
\brief Supports key navigation by arrow keys.
Key-based user interfaces commonly allow the use of arrow keys to navigate between
@@ -815,7 +816,7 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
\qmltype Keys
\instantiates QQuickKeysAttached
\inqmlmodule QtQuick
- \ingroup qtquick-input
+ \ingroup qtquick-input-handlers
\brief Provides key handling to Items.
All visual primitives support key handling via the Keys
@@ -2453,20 +2454,19 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- if (QObject *acc = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(item, false)) {
- int role = acc->property("role").toInt();
- if (role == QAccessible::EditableText
- || role == QAccessible::Table
- || role == QAccessible::List
- || role == QAccessible::SpinBox) {
- return true;
- } else if (role == QAccessible::ComboBox) {
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item);
+ QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
+ return true;
+ } else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
+ if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item))
return iface->state().editable;
- }
}
#endif
+ QVariant editable = item->property("editable");
+ if (editable.isValid())
+ return editable.toBool();
+
QVariant readonly = item->property("readOnly");
if (readonly.isValid() && !readonly.toBool() && item->property("text").isValid())
return true;
@@ -2565,8 +2565,19 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
from = item->parentItem();
}
bool skip = false;
- QQuickItem * startItem = item;
- QQuickItem * firstFromItem = from;
+
+ QQuickItem *startItem = item;
+ // Protect from endless loop:
+ // If we start on an invisible item we will not find it again.
+ // If there is no other item which can become the focus item, we have a forever loop,
+ // since the protection only works if we encounter the first item again.
+ while (startItem && !startItem->isVisible()) {
+ startItem = startItem->parentItem();
+ }
+ if (!startItem)
+ return item;
+
+ QQuickItem *firstFromItem = from;
QQuickItem *current = item;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
@@ -4069,8 +4080,6 @@ void QQuickItem::mouseDoubleClickEvent(QMouseEvent *)
/*!
This event handler can be reimplemented in a subclass to be notified
when a mouse ungrab event has occurred on this item.
-
- \input item.qdocinc accepting-events
*/
void QQuickItem::mouseUngrabEvent()
{
@@ -4080,8 +4089,6 @@ void QQuickItem::mouseUngrabEvent()
/*!
This event handler can be reimplemented in a subclass to be notified
when a touch ungrab event has occurred on this item.
-
- \input item.qdocinc accepting-events
*/
void QQuickItem::touchUngrabEvent()
{
@@ -5201,6 +5208,17 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
}
+bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if (handler->wantsEventPoint(point))
+ return true;
+ }
+ return false;
+}
+
/*!
\internal
Deliver the \a event to all PointerHandlers which are in the pre-determined
@@ -7404,6 +7422,8 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
return; // nope! sorry, something else wants it kept on.
+ if (otherChildPrivate->hasHoverHandlers())
+ return; // nope! sorry, we have pointer handlers which are interested.
}
}
@@ -8177,6 +8197,16 @@ bool QQuickItemPrivate::hasPointerHandlers() const
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();
}
+bool QQuickItemPrivate::hasHoverHandlers() const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *h : extra->pointerHandlers)
+ if (qmlobject_cast<QQuickHoverHandler *>(h))
+ return true;
+ return false;
+}
+
#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
@@ -8762,7 +8792,7 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
{
- return (engine->memoryManager->allocObject<QQuickItemWrapper>(q_func()))->asReturnedValue();
+ return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index cfdb2ad5b7..6f601e0872 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -163,6 +163,7 @@ public:
// Remember to increment the size of QQuickItemPrivate::flags
};
Q_DECLARE_FLAGS(Flags, Flag)
+ Q_FLAG(Flags)
enum ItemChange {
ItemChildAddedChange, // value.item
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 0204ac07c5..11b47114cf 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -280,6 +280,7 @@ public:
QQuickItemLayer *layer() const;
bool hasPointerHandlers() const;
+ bool hasHoverHandlers() const;
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -330,12 +331,26 @@ public:
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
struct ChangeListener {
- ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = nullptr) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {}
- ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {}
+ using ChangeTypes = QQuickItemPrivate::ChangeTypes;
+
+ ChangeListener(QQuickItemChangeListener *l = nullptr, ChangeTypes t = nullptr)
+ : listener(l)
+ , types(t)
+ , gTypes(QQuickGeometryChange::All)
+ {}
+
+ ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt)
+ : listener(l)
+ , types(Geometry)
+ , gTypes(gt)
+ {}
+
+ bool operator==(const ChangeListener &other) const
+ { return listener == other.listener && types == other.types; }
+
QQuickItemChangeListener *listener;
- QQuickItemPrivate::ChangeTypes types;
+ ChangeTypes types;
QQuickGeometryChange gTypes; //NOTE: not used for ==
- bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
struct ExtraData {
@@ -582,6 +597,7 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ bool anyPointerHandlerWants(QQuickEventPoint *point) const;
virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
virtual void setVisible(bool visible);
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 51a91e1f7a..a20150e3b9 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -64,6 +64,9 @@
#if QT_CONFIG(quick_pathview)
#include "qquickpathview_p.h"
#endif
+#if QT_CONFIG(quick_tableview)
+#include "qquicktableview_p.h"
+#endif
#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
#endif
@@ -113,6 +116,12 @@
#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#include "handlers/qquickdraghandler_p.h"
+#include "handlers/qquickhoverhandler_p.h"
+#include "handlers/qquickpinchhandler_p.h"
+#include "handlers/qquickpointhandler_p.h"
+#include "handlers/qquicktaphandler_p.h"
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
QT_END_NAMESPACE
@@ -135,6 +144,9 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
win->setTransientParent(parentItem->window());
return QQmlPrivate::Parented;
}
+ } else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
+ handler->setParent(parent);
+ return QQmlPrivate::Parented;
}
return QQmlPrivate::IncompatibleObject;
} else if (QQuickWindow *parentWindow = qmlobject_cast<QQuickWindow *>(parent)) {
@@ -422,6 +434,42 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickAnimatedImage, 11>(uri, 2, 11,"AnimatedImage");
#endif
qmlRegisterType<QQuickItem, 11>(uri, 2, 11,"Item");
+ qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable");
+
+ // classes related to Input Handlers which are newly exposed since 5.12
+ qmlRegisterUncreatableType<QQuickPointerEvent>(uri, 2, 12, "PointerEvent",
+ QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickPointerMouseEvent>(uri, 2, 12, "PointerMouseEvent",
+ QQuickPointerHandler::tr("PointerMouseEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickPointerTouchEvent>(uri, 2, 12, "PointerTouchEvent",
+ QQuickPointerHandler::tr("PointerTouchEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickEventPoint>(uri, 2, 12, "EventPoint",
+ QQuickPointerHandler::tr("EventPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickEventTouchPoint>(uri, 2, 12, "EventTouchPoint",
+ QQuickPointerHandler::tr("EventTouchPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickPointerDevice>(uri, 2, 12, "PointerDevice",
+ QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent"));
+
+ // Input Handlers are part of QtQuick, not a separate module, since 5.12
+ qmlRegisterUncreatableType<QQuickPointerHandler>(uri, 2, 12, "PointerHandler",
+ QQuickPointerHandler::tr("PointerHandler is an abstract base class"));
+ qmlRegisterType<QQuickPointHandler>(uri, 2, 12, "PointHandler");
+ qmlRegisterType<QQuickDragHandler>(uri, 2, 12, "DragHandler");
+ qmlRegisterUncreatableType<QQuickDragAxis>(uri, 2, 12, "DragAxis",
+ QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler"));
+ qmlRegisterType<QQuickHoverHandler>(uri, 2, 12, "HoverHandler");
+ qmlRegisterType<QQuickPinchHandler>(uri, 2, 12, "PinchHandler");
+ qmlRegisterType<QQuickTapHandler>(uri, 2, 12, "TapHandler");
+ qRegisterMetaType<QQuickHandlerPoint>();
+
+ // The rest of the 5.12 revisions
+ qmlRegisterType<QQuickAnimatedSprite, 12>("QtQuick", 2, 12, "AnimatedSprite");
+ qmlRegisterType<QQuickGradient, 12>(uri, 2, 12, "Gradient");
+ qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable");
+ qmlRegisterType<QQuickText, 12>(uri, 2, 12, "Text");
+#if QT_CONFIG(quick_tableview)
+ qmlRegisterType<QQuickTableView>(uri, 2, 12, "TableView");
+#endif
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index f2e055e874..8dafc16cf4 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickitemview_p_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQml/QQmlInfo>
#include "qplatformdefs.h"
@@ -52,117 +53,14 @@ Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
#endif
FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemViewAttached *attached)
- : item(i)
+ : QQuickItemViewFxItem(i, own, QQuickItemViewPrivate::get(v))
, view(v)
- , transitionableItem(nullptr)
, attached(attached)
- , ownItem(own)
- , releaseAfterTransition(false)
- , trackGeom(false)
{
if (attached) // can be null for default components (see createComponentItem)
attached->setView(view);
}
-FxViewItem::~FxViewItem()
-{
- delete transitionableItem;
- if (ownItem && item) {
- trackGeometry(false);
- item->setParentItem(nullptr);
- item->deleteLater();
- item = nullptr;
- }
-}
-
-qreal FxViewItem::itemX() const
-{
- return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
-}
-
-qreal FxViewItem::itemY() const
-{
- return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
-}
-
-void FxViewItem::moveTo(const QPointF &pos, bool immediate)
-{
- if (transitionableItem)
- transitionableItem->moveTo(pos, immediate);
- else if (item)
- item->setPosition(pos);
-}
-
-void FxViewItem::setVisible(bool visible)
-{
- if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
- return;
- if (item)
- QQuickItemPrivate::get(item)->setCulled(!visible);
-}
-
-void FxViewItem::trackGeometry(bool track)
-{
- if (track) {
- if (!trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = true;
- }
- } else {
- if (trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = false;
- }
- }
-}
-
-QQuickItemViewTransitioner::TransitionType FxViewItem::scheduledTransitionType() const
-{
- return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
-}
-
-bool FxViewItem::transitionScheduledOrRunning() const
-{
- return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
-}
-
-bool FxViewItem::transitionRunning() const
-{
- return transitionableItem ? transitionableItem->transitionRunning() : false;
-}
-
-bool FxViewItem::isPendingRemoval() const
-{
- return transitionableItem ? transitionableItem->isPendingRemoval() : false;
-}
-
-void FxViewItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
-{
- if (!transitioner)
- return;
- if (!transitionableItem)
- transitionableItem = new QQuickItemViewTransitionableItem(item);
- transitioner->transitionNextReposition(transitionableItem, type, asTarget);
-}
-
-bool FxViewItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
-{
- return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
-}
-
-void FxViewItem::startTransition(QQuickItemViewTransitioner *transitioner)
-{
- if (transitionableItem)
- transitionableItem->startTransition(transitioner, index);
-}
-
-
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
: active(false)
{
@@ -931,6 +829,8 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition)
return;
+ Q_Q(QQuickItemView);
+ q->cancelFlick();
applyPendingChanges();
const int modelCount = model->count();
int idx = qMax(qMin(index, modelCount - 1), 0);
@@ -1285,6 +1185,7 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
{
Q_D(QQuickItemView);
if (reset) {
+ cancelFlick();
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
d->moveReason = QQuickItemViewPrivate::SetIndex;
@@ -1329,6 +1230,12 @@ void QQuickItemView::trackedPositionChanged()
Q_D(QQuickItemView);
if (!d->trackedItem || !d->currentItem)
return;
+
+ if (d->inLayout) {
+ polish();
+ return;
+ }
+
if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index e250cf0ccb..ea5b5df9c6 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -56,6 +56,7 @@
QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemview_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include "qquickitemviewtransition_p.h"
#include "qquickflickable_p_p.h"
#include <QtQml/private/qqmlobjectmodel_p.h>
@@ -65,47 +66,13 @@ QT_REQUIRE_CONFIG(quick_itemview);
QT_BEGIN_NAMESPACE
-
-class Q_AUTOTEST_EXPORT FxViewItem
+class Q_AUTOTEST_EXPORT FxViewItem : public QQuickItemViewFxItem
{
public:
FxViewItem(QQuickItem *, QQuickItemView *, bool own, QQuickItemViewAttached *attached);
- virtual ~FxViewItem();
-
- qreal itemX() const;
- qreal itemY() const;
- inline qreal itemWidth() const { return item ? item->width() : 0; }
- inline qreal itemHeight() const { return item ? item->height() : 0; }
-
- void moveTo(const QPointF &pos, bool immediate);
- void setVisible(bool visible);
- void trackGeometry(bool track);
-
- QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
- bool transitionScheduledOrRunning() const;
- bool transitionRunning() const;
- bool isPendingRemoval() const;
-
- void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
- bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
- void startTransition(QQuickItemViewTransitioner *transitioner);
-
- // these are positions and sizes along the current direction of scrolling/flicking
- virtual qreal position() const = 0;
- virtual qreal endPosition() const = 0;
- virtual qreal size() const = 0;
- virtual qreal sectionSize() const = 0;
-
- virtual bool contains(qreal x, qreal y) const = 0;
- QPointer<QQuickItem> item;
QQuickItemView *view;
- QQuickItemViewTransitionableItem *transitionableItem;
QQuickItemViewAttached *attached;
- int index;
- bool ownItem;
- bool releaseAfterTransition;
- bool trackGeom;
};
diff --git a/src/quick/items/qquickitemviewfxitem.cpp b/src/quick/items/qquickitemviewfxitem.cpp
new file mode 100644
index 0000000000..f9c65967ea
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemviewfxitem_p_p.h"
+#include "qquickitem_p.h"
+#include "qquickitemview_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener* changeListener)
+ : item(item)
+ , ownItem(ownItem)
+ , changeListener(changeListener)
+ , transitionableItem(nullptr)
+ , releaseAfterTransition(false)
+ , trackGeom(false)
+{
+}
+
+QQuickItemViewFxItem::~QQuickItemViewFxItem()
+{
+ delete transitionableItem;
+ if (ownItem && item) {
+ trackGeometry(false);
+ item->setParentItem(0);
+ item->deleteLater();
+ }
+}
+
+qreal QQuickItemViewFxItem::itemX() const
+{
+ return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
+}
+
+qreal QQuickItemViewFxItem::itemY() const
+{
+ return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
+}
+
+void QQuickItemViewFxItem::moveTo(const QPointF &pos, bool immediate)
+{
+ if (transitionableItem)
+ transitionableItem->moveTo(pos, immediate);
+ else if (item)
+ item->setPosition(pos);
+}
+
+void QQuickItemViewFxItem::setVisible(bool visible)
+{
+ if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
+ return;
+ if (item)
+ QQuickItemPrivate::get(item)->setCulled(!visible);
+}
+
+void QQuickItemViewFxItem::trackGeometry(bool track)
+{
+ if (track) {
+ if (!trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = true;
+ }
+ } else {
+ if (trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->removeItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = false;
+ }
+ }
+}
+
+QRectF QQuickItemViewFxItem::geometry() const
+{
+ return QRectF(item->position(), item->size());
+}
+
+void QQuickItemViewFxItem::setGeometry(const QRectF &geometry)
+{
+ item->setPosition(geometry.topLeft());
+ item->setSize(geometry.size());
+}
+
+QQuickItemViewTransitioner::TransitionType QQuickItemViewFxItem::scheduledTransitionType() const
+{
+ return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
+}
+
+bool QQuickItemViewFxItem::transitionScheduledOrRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
+}
+
+bool QQuickItemViewFxItem::transitionRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionRunning() : false;
+}
+
+bool QQuickItemViewFxItem::isPendingRemoval() const
+{
+ return transitionableItem ? transitionableItem->isPendingRemoval() : false;
+}
+
+void QQuickItemViewFxItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
+{
+ if (!transitioner)
+ return;
+ if (!transitionableItem)
+ transitionableItem = new QQuickItemViewTransitionableItem(item);
+ transitioner->transitionNextReposition(transitionableItem, type, asTarget);
+}
+
+bool QQuickItemViewFxItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
+{
+ return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
+}
+
+void QQuickItemViewFxItem::startTransition(QQuickItemViewTransitioner *transitioner)
+{
+ if (transitionableItem)
+ transitionableItem->startTransition(transitioner, index);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quick/items/qquickitemviewfxitem_p_p.h b/src/quick/items/qquickitemviewfxitem_p_p.h
new file mode 100644
index 0000000000..48ffe248bc
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem_p_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFXVIEWITEM_P_P_H
+#define QQUICKFXVIEWITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickitemviewtransition_p.h>
+
+QT_REQUIRE_CONFIG(quick_itemview);
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickItemViewFxItem
+{
+public:
+ QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener *changeListener);
+ virtual ~QQuickItemViewFxItem();
+
+ qreal itemX() const;
+ qreal itemY() const;
+ inline qreal itemWidth() const { return item ? item->width() : 0; }
+ inline qreal itemHeight() const { return item ? item->height() : 0; }
+
+ void moveTo(const QPointF &pos, bool immediate);
+ void setVisible(bool visible);
+ void trackGeometry(bool track);
+
+ QRectF geometry() const;
+ void setGeometry(const QRectF &geometry);
+
+ QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
+ bool transitionScheduledOrRunning() const;
+ bool transitionRunning() const;
+ bool isPendingRemoval() const;
+
+ void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
+ bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
+ void startTransition(QQuickItemViewTransitioner *transitioner);
+
+ // these are positions and sizes along the current direction of scrolling/flicking
+ virtual qreal position() const = 0;
+ virtual qreal endPosition() const = 0;
+ virtual qreal size() const = 0;
+ virtual qreal sectionSize() const = 0;
+
+ virtual bool contains(qreal x, qreal y) const = 0;
+
+ int index = -1;
+ QPointer<QQuickItem> item;
+ bool ownItem;
+ QQuickItemChangeListener *changeListener;
+ QQuickItemViewTransitionableItem *transitionableItem;
+ bool releaseAfterTransition;
+ bool trackGeom;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFXVIEWITEM_P_P_H
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index c06dcce0d9..0fde0beb75 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -500,6 +500,8 @@ void QQuickItemViewTransitionableItem::startTransition(QQuickItemViewTransitione
}
if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
+ if (transition)
+ transition->cancel();
delete transition;
transition = new QQuickItemViewTransitionJob;
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 19033e03f1..908801ce1c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -515,8 +515,8 @@ QString QQuickListViewPrivate::sectionAt(int modelIndex)
qreal QQuickListViewPrivate::snapPosAt(qreal pos)
{
- if (FxViewItem *snapItem = snapItemAt(pos))
- return snapItem->position();
+ if (FxListItemSG *snapItem = static_cast<FxListItemSG*>(snapItemAt(pos)))
+ return snapItem->itemPosition();
if (visibleItems.count()) {
qreal firstPos = (*visibleItems.constBegin())->position();
qreal endPos = (*(--visibleItems.constEnd()))->position();
@@ -530,22 +530,35 @@ qreal QQuickListViewPrivate::snapPosAt(qreal pos)
FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
{
+ const qreal velocity = orient == QQuickListView::Vertical ? vData.velocity : hData.velocity;
FxViewItem *snapItem = nullptr;
+ FxViewItem *prevItem = nullptr;
qreal prevItemSize = 0;
for (FxViewItem *item : qAsConst(visibleItems)) {
if (item->index == -1)
continue;
- qreal itemTop = item->position();
- if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
+
+ const FxListItemSG *listItem = static_cast<FxListItemSG *>(item);
+ qreal itemTop = listItem->position();
+ qreal itemSize = listItem->size();
+ if (highlight && itemTop >= pos && listItem->endPosition() <= pos + highlight->size())
return item;
+ if (listItem->section() && velocity > 0) {
+ if (itemTop + listItem->sectionSize() / 2 >= pos && itemTop - prevItemSize / 2 < pos)
+ snapItem = prevItem;
+ itemTop = listItem->itemPosition();
+ itemSize = listItem->itemSize();
+ }
+
// Middle of item and spacing (i.e. the middle of the distance between this item and the next
- qreal halfwayToNextItem = itemTop + (item->size()+spacing) / 2;
+ qreal halfwayToNextItem = itemTop + (itemSize+spacing) / 2;
qreal halfwayToPrevItem = itemTop - (prevItemSize+spacing) / 2;
if (halfwayToNextItem >= pos && halfwayToPrevItem < pos)
snapItem = item;
- prevItemSize = item->size();
+ prevItemSize = listItem->itemSize();
+ prevItem = item;
}
return snapItem;
}
@@ -1501,7 +1514,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
bias = averageSize/2;
@@ -1512,13 +1525,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1530,15 +1543,15 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
} else {
if (isContentFlowReversed())
- pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
+ pos = qMax(qMin(-static_cast<FxListItemSG*>(topItem)->itemPosition() + highlightRangeStart - size(), -maxExtent), -minExtent);
else
- pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
+ pos = qMax(qMin(static_cast<FxListItemSG*>(topItem)->itemPosition() - highlightRangeStart, -maxExtent), -minExtent);
}
} else if (bottomItem && isInBounds) {
if (isContentFlowReversed())
- pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
+ pos = qMax(qMin(-static_cast<FxListItemSG*>(bottomItem)->itemPosition() + highlightRangeEnd - size(), -maxExtent), -minExtent);
else
- pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
+ pos = qMax(qMin(static_cast<FxListItemSG*>(bottomItem)->itemPosition() - highlightRangeEnd, -maxExtent), -minExtent);
} else {
QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
return;
@@ -1568,7 +1581,10 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
timeline.reset(data.move);
if (viewPos != position()) {
if (fixupMode != Immediate) {
- timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
+ if (fixupMode == ExtentChanged && data.fixingUp)
+ timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::OutQuad), fixupDuration/2);
+ else
+ timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
data.fixingUp = true;
} else {
timeline.set(data.move, -viewPos);
@@ -1599,7 +1615,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1616,7 +1632,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1982,7 +1998,7 @@ QQuickListView::~QQuickListView()
The model provides the set of data that is used to create the items
in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
- or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
+ or \l ObjectModel, or provided by C++ model classes. If a C++ model class is
used, it must be a subclass of \l QAbstractItemModel or a simple list.
\sa {qml-data-models}{Data Models}
@@ -2521,7 +2537,11 @@ void QQuickListView::setHighlightResizeDuration(int duration)
the view.
\li ListView.SnapOneItem - the view settles no more than one item away from the first
visible item at the time the mouse button is released. This mode is particularly
- useful for moving one page at a time.
+ useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
+ show a stronger affinity to neighboring items when movement occurs. For example, a
+ short drag that snaps back to the current item with SnapToItem might snap to a
+ neighboring item with SnapOneItem.
+
\endlist
\c snapMode does not affect the \l currentIndex. To update the
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index b5ae41daef..d0e29c204e 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -54,7 +54,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
QQuickLoaderPrivate::QQuickLoaderPrivate()
- : item(nullptr), object(nullptr), component(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
+ : item(nullptr), object(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
active(true), loadingFromSource(false), asynchronous(false)
{
}
@@ -111,11 +111,10 @@ void QQuickLoaderPrivate::clear()
QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
q, SIGNAL(progressChanged()));
component->deleteLater();
- component = nullptr;
+ component.setObject(nullptr, q);
} else if (component) {
- component = nullptr;
+ component.setObject(nullptr, q);
}
- componentStrongReference.clear();
source = QUrl();
if (item) {
@@ -441,7 +440,7 @@ void QQuickLoader::loadFromSource()
if (isComponentComplete()) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
d->load();
}
}
@@ -484,11 +483,7 @@ void QQuickLoader::setSourceComponent(QQmlComponent *comp)
d->clear();
- d->component = comp;
- if (comp) {
- if (QQmlData *ddata = QQmlData::get(comp))
- d->componentStrongReference = ddata->jsWrapper;
- }
+ d->component.setObject(comp, this);
d->loadingFromSource = false;
if (d->active)
@@ -832,7 +827,7 @@ void QQuickLoader::componentComplete()
if (d->loadingFromSource) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
}
d->load();
}
@@ -1007,7 +1002,7 @@ QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV4Function *args)
QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error)
{
QV4::Scope scope(args->v4engine());
- QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue valuemap(scope, QV4::Encode::undefined());
if (args->length() >= 2) {
QV4::ScopedValue v(scope, (*args)[1]);
if (!v->isObject() || v->as<QV4::ArrayObject>()) {
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 7492527401..349b5c6c06 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -103,8 +103,7 @@ public:
QUrl source;
QQuickItem *item;
QObject *object;
- QQmlComponent *component;
- QV4::PersistentValue componentStrongReference; // To ensure GC doesn't delete components created by Qt.createComponent
+ QQmlStrongJSQObjectReference<QQmlComponent> component;
QQmlContext *itemContext;
QQuickLoaderIncubator *incubator;
QV4::PersistentValue initialPropertyValues;
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index b1438d7541..3eca535a67 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -610,6 +610,9 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
else { // QEvent::MouseButtonPress
addTouchPoint(me);
started = true;
+ _mouseQpaTouchPoint.setStartPos(me->localPos());
+ _mouseQpaTouchPoint.setStartScenePos(me->windowPos());
+ _mouseQpaTouchPoint.setStartScreenPos(me->screenPos());
_mouseQpaTouchPoint.setState(Qt::TouchPointPressed);
}
touchPoints << _mouseQpaTouchPoint;
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index f1550b4ac6..634ea1c2e2 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -185,8 +185,8 @@ private:
class QQuickGrabGestureEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints)
- Q_PROPERTY(qreal dragThreshold READ dragThreshold)
+ Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT)
+ Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT)
public:
QQuickGrabGestureEvent() : _dragThreshold(QGuiApplication::styleHints()->startDragDistance()) {}
diff --git a/src/quick/items/qquickpainteditem.h b/src/quick/items/qquickpainteditem.h
index b057f4295d..ddc1fd99d9 100644
--- a/src/quick/items/qquickpainteditem.h
+++ b/src/quick/items/qquickpainteditem.h
@@ -71,6 +71,7 @@ public:
FastFBOResizing = 0x1
};
Q_DECLARE_FLAGS(PerformanceHints, PerformanceHint)
+ Q_FLAG(PerformanceHints)
void update(const QRect &rect = QRect());
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index 55bafd9f83..512f59d799 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -900,11 +900,7 @@ void QQuickPositionerAttached::setIsLastItem(bool isLastItem)
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{Qt Quick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Column::spacing
@@ -1089,11 +1085,7 @@ void QQuickColumn::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Row::spacing
@@ -1378,11 +1370,7 @@ void QQuickRow::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty qreal QtQuick::Grid::spacing
@@ -1974,11 +1962,7 @@ void QQuickGrid::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Flow::spacing
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index bf030b9d80..5e217dcd0c 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -40,9 +40,13 @@
#include "qquickrectangle_p.h"
#include "qquickrectangle_p_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <private/qqmlmetatype_p.h>
+
#include <QtGui/qpixmapcache.h>
#include <QtCore/qmath.h>
#include <QtCore/qmetaobject.h>
@@ -219,10 +223,11 @@ void QQuickGradientStop::updateGradient()
of solid color fills or images. Consider using gradients for static items
in a user interface.
- In Qt 5.0, only vertical, linear gradients can be applied to items. If you
- need to apply different orientations of gradients, a combination of rotation
- and clipping will need to be applied to the relevant items. This can
- introduce additional performance requirements for your application.
+ Since Qt 5.12, vertical and horizontal linear gradients can be applied to items.
+ If you need to apply angled gradients, a combination of rotation and clipping
+ can be applied to the relevant items. Alternatively, consider using
+ QtQuick.Shapes::LinearGradient or QtGraphicalEffects::LinearGradient. These
+ approaches can all introduce additional performance requirements for your application.
The use of animations involving gradient stops may not give the desired
result. An alternative way to animate gradients is to use pre-generated
@@ -255,6 +260,28 @@ QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
return QQmlListProperty<QQuickGradientStop>(this, m_stops);
}
+/*!
+ \qmlproperty enumeration QtQuick::Gradient::orientation
+ \since 5.12
+
+ Set this property to define the direction of the gradient.
+ \list
+ \li Gradient.Vertical - a vertical gradient
+ \li Gradient.Horizontal - a horizontal gradient
+ \endlist
+
+ The default is Gradient.Vertical.
+*/
+void QQuickGradient::setOrientation(Orientation orientation)
+{
+ if (m_orientation == orientation)
+ return;
+
+ m_orientation = orientation;
+ emit orientationChanged();
+ emit updated();
+}
+
QGradientStops QQuickGradient::gradientStops() const
{
QGradientStops stops;
@@ -370,11 +397,11 @@ QQuickPen *QQuickRectangle::border()
}
/*!
- \qmlproperty Gradient QtQuick::Rectangle::gradient
+ \qmlproperty any QtQuick::Rectangle::gradient
The gradient to use to fill the rectangle.
- This property allows for the construction of simple vertical gradients.
+ This property allows for the construction of simple vertical or horizontal gradients.
Other gradients may be formed by adding rotation to the rectangle.
\div {class="float-left"}
@@ -384,37 +411,66 @@ QQuickPen *QQuickRectangle::border()
\snippet qml/rectangle/rectangle-gradient.qml rectangles
\clearfloat
+ The property also accepts gradient presets from QGradient::Preset. Note however
+ that due to Rectangle only supporting simple vertical or horizontal gradients,
+ any preset with an unsupported angle will revert to the closest representation.
+
+ \snippet qml/rectangle/rectangle-gradient.qml presets
+ \clearfloat
+
If both a gradient and a color are specified, the gradient will be used.
\sa Gradient, color
*/
-QQuickGradient *QQuickRectangle::gradient() const
+QJSValue QQuickRectangle::gradient() const
{
Q_D(const QQuickRectangle);
return d->gradient;
}
-void QQuickRectangle::setGradient(QQuickGradient *gradient)
+void QQuickRectangle::setGradient(const QJSValue &gradient)
{
Q_D(QQuickRectangle);
- if (d->gradient == gradient)
+ if (d->gradient.equals(gradient))
return;
- static int updatedSignalIdx = -1;
- if (updatedSignalIdx < 0)
- updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
+
+ static int updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
if (d->doUpdateSlotIdx < 0)
d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()");
- if (d->gradient)
- QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
- d->gradient = gradient;
- if (d->gradient)
- QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (auto oldGradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject()))
+ QMetaObject::disconnect(oldGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (gradient.isQObject()) {
+ if (auto newGradient = qobject_cast<QQuickGradient*>(gradient.toQObject())) {
+ d->gradient = gradient;
+ QMetaObject::connect(newGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+ } else {
+ qmlWarning(this) << "Can't assign "
+ << QQmlMetaType::prettyTypeName(gradient.toQObject()) << " to gradient property";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNumber() || gradient.isString()) {
+ QGradient preset(gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() != QGradient::NoGradient) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "No such gradient preset '" << gradient.toString() << "'";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNull() || gradient.isUndefined()) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "Unknown gradient type. Expected int, string, or Gradient";
+ d->gradient = QJSValue();
+ }
+
update();
}
void QQuickRectangle::resetGradient()
{
- setGradient(nullptr);
+ setGradient(QJSValue());
}
/*!
@@ -510,10 +566,35 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
rectangle->setAntialiasing(antialiasing());
QGradientStops stops;
- if (d->gradient) {
- stops = d->gradient->gradientStops();
+ bool vertical = true;
+ if (d->gradient.isQObject()) {
+ auto gradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject());
+ Q_ASSERT(gradient);
+ stops = gradient->gradientStops();
+ vertical = gradient->orientation() == QQuickGradient::Vertical;
+ } else if (d->gradient.isNumber() || d->gradient.isString()) {
+ QGradient preset(d->gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() == QGradient::LinearGradient) {
+ auto linearGradient = static_cast<QLinearGradient&>(preset);
+ const QPointF start = linearGradient.start();
+ const QPointF end = linearGradient.finalStop();
+ vertical = qAbs(start.y() - end.y()) >= qAbs(start.x() - end.x());
+ stops = linearGradient.stops();
+ if ((vertical && start.y() > end.y()) || (!vertical && start.x() > end.x())) {
+ // QSGInternalRectangleNode doesn't support stops in the wrong order,
+ // so we need to manually reverse them here.
+ QGradientStops reverseStops;
+ for (auto it = stops.rbegin(); it != stops.rend(); ++it) {
+ auto stop = *it;
+ stop.first = 1 - stop.first;
+ reverseStops.append(stop);
+ }
+ stops = reverseStops;
+ }
+ }
}
rectangle->setGradientStops(stops);
+ rectangle->setGradientVertical(vertical);
rectangle->update();
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index c07ad835fb..d56a03d22d 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -119,24 +119,35 @@ class Q_QUICK_PRIVATE_EXPORT QQuickGradient : public QObject
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QQuickGradientStop> stops READ stops)
+ Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged REVISION 12)
Q_CLASSINFO("DefaultProperty", "stops")
+ Q_ENUMS(QGradient::Preset)
public:
QQuickGradient(QObject *parent=nullptr);
~QQuickGradient() override;
+ enum Orientation { Vertical = Qt::Vertical,
+ Horizontal = Qt::Horizontal };
+ Q_ENUM(Orientation)
+
QQmlListProperty<QQuickGradientStop> stops();
+ Orientation orientation() const { return m_orientation; }
+ void setOrientation(Orientation orientation);
+
QGradientStops gradientStops() const;
Q_SIGNALS:
void updated();
+ void orientationChanged();
private:
void doUpdate();
private:
QList<QQuickGradientStop *> m_stops;
+ Orientation m_orientation = Vertical;
friend class QQuickRectangle;
friend class QQuickGradientStop;
};
@@ -147,7 +158,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
- Q_PROPERTY(QQuickGradient *gradient READ gradient WRITE setGradient RESET resetGradient)
+ Q_PROPERTY(QJSValue gradient READ gradient WRITE setGradient RESET resetGradient)
Q_PROPERTY(QQuickPen * border READ border CONSTANT)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
public:
@@ -158,8 +169,8 @@ public:
QQuickPen *border();
- QQuickGradient *gradient() const;
- void setGradient(QQuickGradient *gradient);
+ QJSValue gradient() const;
+ void setGradient(const QJSValue &gradient);
void resetGradient();
qreal radius() const;
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 3c1aaf7661..c7c5293f9b 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -73,7 +73,7 @@ public:
}
QColor color;
- QQuickGradient *gradient;
+ QJSValue gradient;
QQuickPen *pen;
qreal radius;
static int doUpdateSlotIdx;
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index fb1c6366f2..025acefec2 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -187,7 +187,6 @@ void QQuickRenderControlPrivate::windowDestroyed()
{
if (window) {
rc->invalidate();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = nullptr;
@@ -313,9 +312,6 @@ bool QQuickRenderControl::sync()
void QQuickRenderControl::invalidate()
{
Q_D(QQuickRenderControl);
- if (!d->initialized)
- return;
-
if (!d->window)
return;
@@ -323,6 +319,9 @@ void QQuickRenderControl::invalidate()
cd->fireAboutToStop();
cd->cleanupNodesOnShutdown();
+ if (!d->initialized)
+ return;
+
// We must invalidate since the context can potentially be destroyed by the
// application right after returning from this function. Invalidating is
// also essential to allow a subsequent initialize() to succeed.
@@ -392,6 +391,7 @@ QImage QQuickRenderControl::grab()
grabContent.setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
}
#endif
+#if QT_CONFIG(thread)
} else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
cd->polishItems();
@@ -409,6 +409,7 @@ QImage QQuickRenderControl::grab()
render();
softwareRenderer->setCurrentPaintDevice(prevDev);
}
+#endif
} else {
qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
}
diff --git a/src/quick/items/qquickscalegrid.cpp b/src/quick/items/qquickscalegrid.cpp
index d7a0f1b681..23f179be1d 100644
--- a/src/quick/items/qquickscalegrid.cpp
+++ b/src/quick/items/qquickscalegrid.cpp
@@ -66,6 +66,7 @@ void QQuickScaleGrid::setLeft(int pos)
{
if (_left != pos) {
_left = pos;
+ emit leftBorderChanged();
emit borderChanged();
}
}
@@ -74,6 +75,7 @@ void QQuickScaleGrid::setTop(int pos)
{
if (_top != pos) {
_top = pos;
+ emit topBorderChanged();
emit borderChanged();
}
}
@@ -82,6 +84,7 @@ void QQuickScaleGrid::setRight(int pos)
{
if (_right != pos) {
_right = pos;
+ emit rightBorderChanged();
emit borderChanged();
}
}
@@ -90,6 +93,7 @@ void QQuickScaleGrid::setBottom(int pos)
{
if (_bottom != pos) {
_bottom = pos;
+ emit bottomBorderChanged();
emit borderChanged();
}
}
diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h
index 5752f61e3f..f5187a8eea 100644
--- a/src/quick/items/qquickscalegrid_p_p.h
+++ b/src/quick/items/qquickscalegrid_p_p.h
@@ -65,10 +65,10 @@ class Q_AUTOTEST_EXPORT QQuickScaleGrid : public QObject
{
Q_OBJECT
- Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged)
- Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged)
- Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged)
- Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged)
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged)
public:
QQuickScaleGrid(QObject *parent=nullptr);
@@ -90,6 +90,10 @@ public:
Q_SIGNALS:
void borderChanged();
+ void leftBorderChanged();
+ void topBorderChanged();
+ void rightBorderChanged();
+ void bottomBorderChanged();
private:
int _left;
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index aea7e44a65..b057fd9d8f 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -396,24 +396,24 @@ void QQuickScreenInfo::setWrappedScreen(QScreen *screen)
if (!oldScreen || screen->devicePixelRatio() != oldScreen->devicePixelRatio())
emit devicePixelRatioChanged();
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(widthChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(heightChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(virtualXChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(virtualYChanged()));
- connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)),
- this, SIGNAL(orientationChanged()));
- connect(screen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)),
- this, SIGNAL(primaryOrientationChanged()));
- connect(screen, SIGNAL(virtualGeometryChanged(QRect)),
- this, SIGNAL(desktopGeometryChanged()));
- connect(screen, SIGNAL(logicalDotsPerInchChanged(qreal)),
- this, SIGNAL(logicalPixelDensityChanged()));
- connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
- this, SIGNAL(pixelDensityChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(widthChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(heightChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(virtualXChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(virtualYChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(orientationChanged(Qt::ScreenOrientation)),
+ this, QQuickScreenInfo, SIGNAL(orientationChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)),
+ this, QQuickScreenInfo, SIGNAL(primaryOrientationChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(virtualGeometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(desktopGeometryChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(logicalDotsPerInchChanged(qreal)),
+ this, QQuickScreenInfo, SIGNAL(logicalPixelDensityChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(physicalDotsPerInchChanged(qreal)),
+ this, QQuickScreenInfo, SIGNAL(pixelDensityChanged()));
}
QScreen *QQuickScreenInfo::wrappedScreen() const
@@ -473,11 +473,11 @@ int QQuickScreenAttached::angleBetween(int a, int b)
void QQuickScreenAttached::windowChanged(QQuickWindow* c)
{
if (m_window)
- disconnect(m_window, SIGNAL(screenChanged(QScreen*)), this, SLOT(screenChanged(QScreen*)));
+ qmlobject_disconnect(m_window, QQuickWindow, SIGNAL(screenChanged(QScreen*)), this, QQuickScreenAttached, SLOT(screenChanged(QScreen*)));
m_window = c;
screenChanged(c ? c->screen() : nullptr);
if (c)
- connect(c, SIGNAL(screenChanged(QScreen*)), this, SLOT(screenChanged(QScreen*)));
+ qmlobject_connect(c, QQuickWindow, SIGNAL(screenChanged(QScreen*)), this, QQuickScreenAttached, SLOT(screenChanged(QScreen*)));
}
void QQuickScreenAttached::screenChanged(QScreen *screen)
diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp
index 77b7cbc78d..804f548d21 100644
--- a/src/quick/items/qquickshadereffectmesh.cpp
+++ b/src/quick/items/qquickshadereffectmesh.cpp
@@ -64,6 +64,11 @@ QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
{
}
+QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
/*!
\qmltype GridMesh
\instantiates QQuickGridMesh
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 5d6641429a..62a9798e40 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -72,7 +72,7 @@ const char *qtTexCoordAttributeName();
class QSGGeometry;
class QRectF;
-class QQuickShaderEffectMesh : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMesh : public QObject
{
Q_OBJECT
public:
@@ -87,9 +87,12 @@ public:
Q_SIGNALS:
// Emitted when the geometry needs to be updated.
void geometryChanged();
+
+protected:
+ QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent = nullptr);
};
-class QQuickGridMesh : public QQuickShaderEffectMesh
+class Q_QUICK_PRIVATE_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
{
Q_OBJECT
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index da917683b6..d3944b4620 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -109,7 +109,7 @@ public:
virtual int variedDuration() const
{
- return qMax(qreal(0.0) , m_duration
+ return qMax(0.0 , m_duration
+ (m_durationVariation * QRandomGenerator::global()->bounded(2.0))
- m_durationVariation);
}
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
new file mode 100644
index 0000000000..44c16e2d7e
--- /dev/null
+++ b/src/quick/items/qquicktableview.cpp
@@ -0,0 +1,2034 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktableview_p.h"
+#include "qquicktableview_p_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qdir.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+/*!
+ \qmltype TableView
+ \instantiates QQuickTableView
+ \inqmlmodule QtQuick
+ \ingroup qtquick-views
+ \inherits Flickable
+ \brief Provides a table view of items provided by the model.
+
+ A TableView has a \l model that defines the data to be displayed, and a
+ \l delegate that defines how the data should be displayed.
+
+ TableView inherits \l Flickable. This means that while the model can have
+ any number of rows and columns, only a subsection of the table is usually
+ visible inside the viewport. As soon as you flick, new rows and columns
+ enter the viewport, while old ones exit and are removed from the viewport.
+ The rows and columns that move out are reused for building the rows and columns
+ that move into the viewport. As such, the TableView support models of any
+ size without affecting performance.
+
+ A TableView displays data from models created from built-in QML types
+ such as ListModel and XmlListModel, which populates the first column only
+ in a TableView. To create models with multiple columns, create a model in
+ C++ that inherits QAbstractItemModel, and expose it to QML.
+
+ \section1 Example Usage
+
+ The following example shows how to create a model from C++ with multiple
+ columns:
+
+ \snippet qml/tableview/tablemodel.cpp 0
+
+ And then how to use it from QML:
+
+ \snippet qml/tableview/tablemodel.qml 0
+
+ \section1 Reusing items
+
+ TableView recycles delegate items by default, instead of instantiating from
+ the \l delegate whenever new rows and columns are flicked into view. This
+ can give a huge performance boost, depending on the complexity of the
+ delegate.
+
+ When an item is flicked out, it moves to the \e{reuse pool}, which is an
+ internal cache of unused items. When this happens, the \l TableView::pooled
+ signal is emitted to inform the item about it. Likewise, when the item is
+ moved back from the pool, the \l TableView::reused signal is emitted.
+
+ Any item properties that come from the model are updated when the
+ item is reused. This includes \c index, \c row, and \c column, but also
+ any model roles.
+
+ \note Avoid storing any state inside a delegate. If you do, reset it
+ manually on receiving the \l TableView::reused signal.
+
+ If an item has timers or animations, consider pausing them on receiving
+ the \l TableView::pooled signal. That way you avoid using the CPU resources
+ for items that are not visible. Likewise, if an item has resources that
+ cannot be reused, they could be freed up.
+
+ If you don't want to reuse items or if the \l delegate cannot support it,
+ you can set the \l reuseItems property to \c false.
+
+ \note While an item is in the pool, it might still be alive and respond
+ to connected signals and bindings.
+
+ The following example shows a delegate that animates a spinning rectangle. When
+ it is pooled, the animation is temporarily paused:
+
+ \snippet qml/tableview/reusabledelegate.qml 0
+
+ \section1 Row heights and column widths
+
+ When a new column is flicked into view, TableView will determine its width
+ by calling the \l columnWidthProvider function. TableView itself will never
+ store row height or column width, as it's designed to support large models
+ containing any number of rows and columns. Instead, it will ask the
+ application whenever it needs to know.
+
+ TableView uses the largest \c implicitWidth among the items as the column
+ width, unless the \l columnWidthProvider property is explicitly set. Once
+ the column width is found, all other items in the same column are resized
+ to this width, even if new items that are flicked in later have larger
+ \c implicitWidth. Setting an explicit \l width on an item is ignored and
+ overwritten.
+
+ \note The calculated width of a column is discarded when it is flicked out
+ of the viewport, and is recalculated if the column is flicked back in. The
+ calculation is always based on the items that are visible when the column
+ is flicked in. This means that it can end up different each time, depending
+ on which row you're at when the column enters. You should therefore have the
+ same \c implicitWidth for all items in a column, or set
+ \l columnWidthProvider. The same logic applies for the row height
+ calculation.
+
+ If you change the values that a \l rowHeightProvider or a
+ \l columnWidthProvider return for rows and columns inside the viewport, you
+ must call \l forceLayout. This informs TableView that it needs to use the
+ provider functions again to recalculate and update the layout.
+
+ \note The size of a row or column should be a whole number to avoid
+ sub-pixel alignment of items.
+
+ The following example shows how to set a simple \c columnWidthProvider
+ together with a timer that modifies the values the function returns. When
+ the array is modified, \l forceLayout is called to let the changes
+ take effect:
+
+ \snippet qml/tableview/tableviewwithprovider.qml 0
+
+ \section1 Overlays and underlays
+
+ Tableview inherits \l Flickable. And when new items are instantiated from the
+ delegate, it will parent them to the \l{Flickable::}{contentItem}
+ with a \c z value equal to \c 1. You can add your own items inside the
+ Tableview, as child items of the Flickable. By controlling their \c z
+ value, you can make them be on top of or underneath the table items.
+
+ Here is an example that shows how to add some text on top of the table, that
+ moves together with the table as you flick:
+
+ \snippet qml/tableview/tableviewwithheader.qml 0
+*/
+
+/*!
+ \qmlproperty int QtQuick::TableView::rows
+
+ This property holds the number of rows in the table. This is
+ equal to the number of rows in the model.
+
+ This property is read only.
+*/
+
+/*!
+ \qmlproperty int QtQuick::TableView::columns
+
+ This property holds the number of columns in the table. This is
+ equal to the number of columns in the model. If the model is
+ a list, columns will be 1.
+
+ This property is read only.
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::rowSpacing
+
+ This property holds the spacing between the rows.
+
+ The default value is 0.
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::columnSpacing
+
+ This property holds the spacing between the columns.
+
+ The default value is 0.
+*/
+
+/*!
+ \qmlproperty var QtQuick::TableView::rowHeightProvider
+
+ This property can hold a function that returns the row height for each row
+ in the model. When assigned, it will be called whenever TableView needs to
+ know the height of a specific row. The function takes one argument, \c row,
+ for which the TableView needs to know the height.
+
+ \note The height of a row must always be greater than \c 0.
+
+ \sa columnWidthProvider, {Row heights and column widths}
+*/
+
+/*!
+ \qmlproperty var QtQuick::TableView::columnWidthProvider
+
+ This property can hold a function that returns the column width for each
+ column in the model. When assigned, it is called whenever TableView needs
+ to know the width of a specific column. The function takes one argument,
+ \c column, for which the TableView needs to know the width.
+
+ \note The width of a column must always be greater than \c 0.
+
+ \sa rowHeightProvider, {Row heights and column widths}
+*/
+
+/*!
+ \qmlproperty model QtQuick::TableView::model
+ This property holds the model that provides data for the table.
+
+ The model provides the set of data that is used to create the items
+ in the view. Models can be created directly in QML using \l ListModel,
+ \l XmlListModel or \l ObjectModel, or provided by a custom C++ model
+ class. If it is a C++ model, it must be a subclass of \l QAbstractItemModel
+ or a simple list.
+
+ \sa {qml-data-models}{Data Models}
+*/
+
+/*!
+ \qmlproperty Component QtQuick::TableView::delegate
+
+ The delegate provides a template defining each cell item instantiated by the
+ view. The model index is exposed as an accessible \c index property. The same
+ applies to \c row and \c column. Properties of the model are also available
+ depending upon the type of \l {qml-data-models}{Data Model}.
+
+ A delegate should specify its size using \l implicitWidth and \l implicitHeight.
+ The TableView lays out the items based on that information. Explicit \l width or
+ \l height settings are ignored and overwritten.
+
+ \note Delegates are instantiated as needed and may be destroyed at any time.
+ They are also reused if the \l reuseItems property is set to \c true. You
+ should therefore avoid storing state information in the delegates.
+
+ \sa {Row heights and column widths}, {Reusing items}
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TableView::reuseItems
+
+ This property holds whether or not items instantiated from the \l delegate
+ should be reused. If set to \c false, any currently pooled items
+ are destroyed.
+
+ \sa {Reusing items}, TableView::pooled, TableView::reused
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::contentWidth
+
+ This property holds the width of the \l contentView, which is also
+ the width of the table (including margins). As a TableView cannot
+ always know the exact width of the table without loading all columns
+ in the model, the \c contentWidth is usually an estimated width based on
+ the columns it has seen so far. This estimate is recalculated whenever
+ new columns are flicked into view, which means that the content width
+ can change dynamically.
+
+ If you know up front what the width of the table will be, assign a value
+ to \c contentWidth explicitly, to avoid unnecessary calculations and
+ updates to the TableView.
+
+ \sa contentHeight
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::contentHeight
+
+ This property holds the height of the \l contentView, which is also
+ the height of the table (including margins). As a TableView cannot
+ always know the exact height of the table without loading all rows
+ in the model, the \c contentHeight is usually an estimated height
+ based on the rows it has seen so far. This estimate is recalculated
+ whenever new rows are flicked into view, which means that the content height
+ can change dynamically.
+
+ If you know up front what the height of the table will be, assign a
+ value to \c contentHeight explicitly, to avoid unnecessary calculations and
+ updates to the TableView.
+
+ \sa contentWidth
+*/
+
+/*!
+ \qmlmethod real QtQuick::TableView::forceLayout
+
+ Responding to changes in the model are batched so that they are handled
+ only once per frame. This means the TableView delays showing any changes
+ while a script is being run. The same is also true when changing
+ properties such as \l rowSpacing or \l leftMargin.
+
+ This method forces the TableView to immediately update the layout so
+ that any recent changes take effect.
+
+ Calling this function re-evaluates the size and position of each visible
+ row and column. This is needed if the functions assigned to
+ \l rowHeightProvider or \l columnWidthProvider return different values than
+ what is already assigned.
+*/
+
+/*!
+ \qmlattachedproperty TableView QtQuick::TableView::view
+
+ This attached property holds the view that manages the delegate instance.
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::TableView::pooled
+
+ This signal is emitted after an item has been added to the reuse
+ pool. You can use it to pause ongoing timers or animations inside
+ the item, or free up resources that cannot be reused.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, reused
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::TableView::reused
+
+ This signal is emitted after an item has been reused. At this point, the
+ item has been taken out of the pool and placed inside the content view,
+ and the model properties such as index, row, and column have been updated.
+
+ Other properties that are not provided by the model does not change when an item
+ is reused. You should avoid storing any state inside a delegate, but if you do,
+ manually reset that state on receiving this signal.
+
+ This signal is emitted when the item is reused, and not the first time the
+ item is created.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, pooled
+*/
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
+
+#define Q_TABLEVIEW_UNREACHABLE(output) { dumpTable(); qWarning() << "output:" << output; Q_UNREACHABLE(); }
+#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
+
+static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
+
+static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge)
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ return QLine(rect.topLeft(), rect.bottomLeft());
+ case Qt::RightEdge:
+ return QLine(rect.topRight(), rect.bottomRight());
+ case Qt::TopEdge:
+ return QLine(rect.topLeft(), rect.topRight());
+ case Qt::BottomEdge:
+ return QLine(rect.bottomLeft(), rect.bottomRight());
+ }
+ return QLine();
+}
+
+static QRect expandedRect(const QRect &rect, Qt::Edge edge, int increment)
+{
+ switch (edge) {
+ case Qt::LeftEdge:
+ return rect.adjusted(-increment, 0, 0, 0);
+ case Qt::RightEdge:
+ return rect.adjusted(0, 0, increment, 0);
+ case Qt::TopEdge:
+ return rect.adjusted(0, -increment, 0, 0);
+ case Qt::BottomEdge:
+ return rect.adjusted(0, 0, 0, increment);
+ }
+ return QRect();
+}
+
+const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0);
+const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0);
+const QPoint QQuickTableViewPrivate::kUp = QPoint(0, -1);
+const QPoint QQuickTableViewPrivate::kDown = QPoint(0, 1);
+
+QQuickTableViewPrivate::QQuickTableViewPrivate()
+ : QQuickFlickablePrivate()
+{
+}
+
+QQuickTableViewPrivate::~QQuickTableViewPrivate()
+{
+ releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
+ if (tableModel)
+ delete tableModel;
+}
+
+QString QQuickTableViewPrivate::tableLayoutToString() const
+{
+ return QString(QLatin1String("table cells: (%1,%2) -> (%3,%4), item count: %5, table rect: %6,%7 x %8,%9"))
+ .arg(loadedTable.topLeft().x()).arg(loadedTable.topLeft().y())
+ .arg(loadedTable.bottomRight().x()).arg(loadedTable.bottomRight().y())
+ .arg(loadedItems.count())
+ .arg(loadedTableOuterRect.x())
+ .arg(loadedTableOuterRect.y())
+ .arg(loadedTableOuterRect.width())
+ .arg(loadedTableOuterRect.height());
+}
+
+void QQuickTableViewPrivate::dumpTable() const
+{
+ auto listCopy = loadedItems.values();
+ std::stable_sort(listCopy.begin(), listCopy.end(),
+ [](const FxTableItem *lhs, const FxTableItem *rhs)
+ { return lhs->index < rhs->index; });
+
+ qWarning() << QStringLiteral("******* TABLE DUMP *******");
+ for (int i = 0; i < listCopy.count(); ++i)
+ qWarning() << static_cast<FxTableItem *>(listCopy.at(i))->cell;
+ qWarning() << tableLayoutToString();
+
+ const QString filename = QStringLiteral("QQuickTableView_dumptable_capture.png");
+ const QString path = QDir::current().absoluteFilePath(filename);
+ if (q_func()->window() && q_func()->window()->grabWindow().save(path))
+ qWarning() << "Window capture saved to:" << path;
+}
+
+QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
+{
+ QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
+ return static_cast<QQuickTableViewAttached *>(attachedObject);
+}
+
+int QQuickTableViewPrivate::modelIndexAtCell(const QPoint &cell) const
+{
+ int availableRows = tableSize.height();
+ int modelIndex = cell.y() + (cell.x() * availableRows);
+ Q_TABLEVIEW_ASSERT(modelIndex < model->count(),
+ "modelIndex:" << modelIndex << "cell:" << cell << "count:" << model->count());
+ return modelIndex;
+}
+
+QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const
+{
+ int availableRows = tableSize.height();
+ Q_TABLEVIEW_ASSERT(availableRows > 0, availableRows);
+ int column = int(modelIndex / availableRows);
+ int row = modelIndex % availableRows;
+ return QPoint(column, row);
+}
+
+void QQuickTableViewPrivate::updateContentWidth()
+{
+ Q_Q(QQuickTableView);
+
+ if (explicitContentWidth.isValid()) {
+ // Don't calculate contentWidth when it
+ // was set explicitly by the application.
+ return;
+ }
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentRightColumn = loadedTable.right();
+
+ if (currentRightColumn > contentSizeBenchMarkPoint.x()) {
+ contentSizeBenchMarkPoint.setX(currentRightColumn);
+
+ const qreal spacing = currentRightColumn * cellSpacing.width();
+ qreal currentWidth = loadedTableOuterRect.right();
+ const qreal averageCellWidth = (currentWidth - spacing) / (currentRightColumn + 1);
+ qreal estimatedWidth = (tableSize.width() * (averageCellWidth + cellSpacing.width())) - cellSpacing.width();
+
+ if (currentRightColumn >= tableSize.width() - 1) {
+ // We are at the last column, and can set the exact width
+ if (!qFuzzyCompare(currentWidth, q->implicitWidth()))
+ q->QQuickFlickable::setContentWidth(currentWidth);
+ } else if (currentWidth >= q->implicitWidth()) {
+ // We are at the estimated width, but there are still more columns
+ q->QQuickFlickable::setContentWidth(estimatedWidth);
+ } else {
+ // Only set a new width if the new estimate is substantially different
+ qreal diff = 1 - (estimatedWidth / q->implicitWidth());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->QQuickFlickable::setContentWidth(estimatedWidth);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::updateContentHeight()
+{
+ Q_Q(QQuickTableView);
+
+ if (explicitContentHeight.isValid()) {
+ // Don't calculate contentHeight when it
+ // was set explicitly by the application.
+ return;
+ }
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentBottomRow = loadedTable.bottom();
+
+ if (currentBottomRow > contentSizeBenchMarkPoint.y()) {
+ contentSizeBenchMarkPoint.setY(currentBottomRow);
+
+ const qreal spacing = currentBottomRow * cellSpacing.height();
+ qreal currentHeight = loadedTableOuterRect.bottom();
+ const qreal averageCellHeight = (currentHeight - spacing) / (currentBottomRow + 1);
+ qreal estimatedHeight = (tableSize.height() * (averageCellHeight + cellSpacing.height())) - cellSpacing.height();
+
+ if (currentBottomRow >= tableSize.height() - 1) {
+ // We are at the last row, and can set the exact height
+ if (!qFuzzyCompare(currentHeight, q->implicitHeight()))
+ q->QQuickFlickable::setContentHeight(currentHeight);
+ } else if (currentHeight >= q->implicitHeight()) {
+ // We are at the estimated height, but there are still more rows
+ q->QQuickFlickable::setContentHeight(estimatedHeight);
+ } else {
+ // Only set a new height if the new estimate is substantially different
+ qreal diff = 1 - (estimatedHeight / q->implicitHeight());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->QQuickFlickable::setContentHeight(estimatedHeight);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::enforceTableAtOrigin()
+{
+ // Gaps before the first row/column can happen if rows/columns
+ // changes size while flicking e.g because of spacing changes or
+ // changes to a column maxWidth/row maxHeight. Check for this, and
+ // move the whole table rect accordingly.
+ bool layoutNeeded = false;
+ const qreal flickMargin = 50;
+
+ if (loadedTable.x() == 0 && loadedTableOuterRect.x() > 0) {
+ // The table is at the beginning, but not at the edge of the
+ // content view. So move the table to origin.
+ loadedTableOuterRect.moveLeft(0);
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.x() < 0) {
+ // The table is outside the beginning of the content view. Move
+ // the whole table inside, and make some room for flicking.
+ loadedTableOuterRect.moveLeft(loadedTable.x() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (loadedTable.y() == 0 && loadedTableOuterRect.y() > 0) {
+ loadedTableOuterRect.moveTop(0);
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.y() < 0) {
+ loadedTableOuterRect.moveTop(loadedTable.y() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (layoutNeeded) {
+ qCDebug(lcTableViewDelegateLifecycle);
+ relayoutTableItems();
+ }
+}
+
+void QQuickTableViewPrivate::updateAverageEdgeSize()
+{
+ int bottomCell = loadedTable.bottom();
+ int rightCell = loadedTable.right();
+ qreal accRowSpacing = bottomCell * cellSpacing.height();
+ qreal accColumnSpacing = rightCell * cellSpacing.width();
+ averageEdgeSize.setHeight((loadedTableOuterRect.bottom() - accRowSpacing) / (bottomCell + 1));
+ averageEdgeSize.setWidth((loadedTableOuterRect.right() - accColumnSpacing) / (rightCell + 1));
+}
+
+void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
+{
+ QRectF topLeftRect = loadedTableItem(loadedTable.topLeft())->geometry();
+ QRectF bottomRightRect = loadedTableItem(loadedTable.bottomRight())->geometry();
+ loadedTableOuterRect = topLeftRect.united(bottomRightRect);
+ loadedTableInnerRect = QRectF(topLeftRect.bottomRight(), bottomRightRect.topLeft());
+}
+
+void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest()
+{
+ if (loadRequest.edge() == Qt::Edge(0)) {
+ // No edge means we're loading the top-left item
+ loadedTable = QRect(loadRequest.firstCell(), loadRequest.lastCell());
+ return;
+ }
+
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::TopEdge:
+ loadedTable.setTopLeft(loadRequest.firstCell());
+ break;
+ case Qt::RightEdge:
+ case Qt::BottomEdge:
+ loadedTable.setBottomRight(loadRequest.lastCell());
+ break;
+ }
+}
+
+FxTableItem *QQuickTableViewPrivate::itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const
+{
+ return loadedTableItem(fxTableItem->cell + direction);
+}
+
+FxTableItem *QQuickTableViewPrivate::loadedTableItem(const QPoint &cell) const
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ return loadedItems.value(modelIndex);
+}
+
+FxTableItem *QQuickTableViewPrivate::createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+ Q_Q(QQuickTableView);
+
+ bool ownItem = false;
+ int modelIndex = modelIndexAtCell(cell);
+
+ QObject* object = model->object(modelIndex, incubationMode);
+ if (!object) {
+ if (model->incubationStatus(modelIndex) == QQmlIncubator::Loading) {
+ // Item is incubating. Return nullptr for now, and let the table call this
+ // function again once we get a callback to itemCreatedCallback().
+ return nullptr;
+ }
+
+ qWarning() << "TableView: failed loading index:" << modelIndex;
+ object = new QQuickItem();
+ ownItem = true;
+ }
+
+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
+ if (!item) {
+ // The model could not provide an QQuickItem for the
+ // given index, so we create a placeholder instead.
+ qWarning() << "TableView: delegate is not an item:" << modelIndex;
+ model->release(object);
+ item = new QQuickItem();
+ ownItem = true;
+ } else {
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ if (anchors && anchors->activeDirections())
+ qmlWarning(item) << "TableView: detected anchors on delegate with index: " << modelIndex
+ << ". Use implicitWidth and implicitHeight instead.";
+ }
+
+ if (ownItem) {
+ // Parent item is normally set early on from initItemCallback (to
+ // allow bindings to the parent property). But if we created the item
+ // within this function, we need to set it explicit.
+ item->setImplicitWidth(kDefaultColumnWidth);
+ item->setImplicitHeight(kDefaultRowHeight);
+ item->setParentItem(q->contentItem());
+ }
+ Q_TABLEVIEW_ASSERT(item->parentItem() == q->contentItem(), item->parentItem());
+
+ FxTableItem *fxTableItem = new FxTableItem(item, q, ownItem);
+ fxTableItem->setVisible(false);
+ fxTableItem->cell = cell;
+ fxTableItem->index = modelIndex;
+ return fxTableItem;
+}
+
+FxTableItem *QQuickTableViewPrivate::loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+#ifdef QT_DEBUG
+ // Since TableView needs to work flawlessly when e.g incubating inside an async
+ // loader, being able to override all loading to async while debugging can be helpful.
+ static const bool forcedAsync = forcedIncubationMode == QLatin1String("async");
+ if (forcedAsync)
+ incubationMode = QQmlIncubator::Asynchronous;
+#endif
+
+ // Note that even if incubation mode is asynchronous, the item might
+ // be ready immediately since the model has a cache of items.
+ QBoolBlocker guard(blockItemCreatedCallback);
+ auto item = createFxTableItem(cell, incubationMode);
+ qCDebug(lcTableViewDelegateLifecycle) << cell << "ready?" << bool(item);
+ return item;
+}
+
+void QQuickTableViewPrivate::releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag) {
+ // Make a copy and clear the list of items first to avoid destroyed
+ // items being accessed during the loop (QTBUG-61294)
+ auto const tmpList = loadedItems;
+ loadedItems.clear();
+ for (FxTableItem *item : tmpList)
+ releaseItem(item, reusableFlag);
+}
+
+void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag)
+{
+ Q_Q(QQuickTableView);
+ auto item = fxTableItem->item;
+ Q_TABLEVIEW_ASSERT(item, fxTableItem->index);
+
+ if (fxTableItem->ownItem) {
+ delete item;
+ } else {
+ // Only QQmlTableInstanceModel supports reusing items
+ auto releaseFlag = tableModel ?
+ tableModel->release(item, reusableFlag) :
+ model->release(item);
+
+ if (releaseFlag != QQmlInstanceModel::Destroyed) {
+ // When items are not destroyed, it typically means that the
+ // item is reused, or that the model is an ObjectModel. If
+ // so, we just hide the item instead.
+ fxTableItem->setVisible(false);
+
+ // If the item (or a descendant) has focus, remove it, so
+ // that the item doesn't enter with focus when it's reused.
+ if (QQuickWindow *window = item->window()) {
+ const auto focusItem = qobject_cast<QQuickItem *>(window->focusObject());
+ if (focusItem) {
+ const bool hasFocus = item == focusItem || item->isAncestorOf(focusItem);
+ if (hasFocus) {
+ const auto focusChild = QQuickItemPrivate::get(q)->subFocusItem;
+ QQuickWindowPrivate::get(window)->clearFocusInScope(q, focusChild, Qt::OtherFocusReason);
+ }
+ }
+ }
+ }
+ }
+
+ delete fxTableItem;
+}
+
+void QQuickTableViewPrivate::unloadItem(const QPoint &cell)
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ releaseItem(loadedItems.take(modelIndex), reusableFlag);
+}
+
+void QQuickTableViewPrivate::unloadItems(const QLine &items)
+{
+ qCDebug(lcTableViewDelegateLifecycle) << items;
+
+ if (items.dx()) {
+ int y = items.p1().y();
+ for (int x = items.p1().x(); x <= items.p2().x(); ++x)
+ unloadItem(QPoint(x, y));
+ } else {
+ int x = items.p1().x();
+ for (int y = items.p1().y(); y <= items.p2().y(); ++y)
+ unloadItem(QPoint(x, y));
+ }
+}
+
+bool QQuickTableViewPrivate::canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.topLeft().x() == 0)
+ return false;
+ return loadedTableOuterRect.left() > fillRect.left() + cellSpacing.width();
+ case Qt::RightEdge:
+ if (loadedTable.bottomRight().x() >= tableSize.width() - 1)
+ return false;
+ return loadedTableOuterRect.right() < fillRect.right() - cellSpacing.width();
+ case Qt::TopEdge:
+ if (loadedTable.topLeft().y() == 0)
+ return false;
+ return loadedTableOuterRect.top() > fillRect.top() + cellSpacing.height();
+ case Qt::BottomEdge:
+ if (loadedTable.bottomRight().y() >= tableSize.height() - 1)
+ return false;
+ return loadedTableOuterRect.bottom() < fillRect.bottom() - cellSpacing.height();
+ }
+
+ return false;
+}
+
+bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ // Note: if there is only one row or column left, we cannot unload, since
+ // they are needed as anchor point for further layouting.
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.left() <= fillRect.left();
+ case Qt::RightEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.right() >= fillRect.right();
+ case Qt::TopEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.top() <= fillRect.top();
+ case Qt::BottomEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.bottom() >= fillRect.bottom();
+ }
+ Q_TABLEVIEW_UNREACHABLE(tableEdge);
+ return false;
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToLoad(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canLoadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToUnload(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canUnloadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+qreal QQuickTableViewPrivate::cellWidth(const QPoint& cell)
+{
+ // Using an items width directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ return cellItem->implicitWidth();
+}
+
+qreal QQuickTableViewPrivate::cellHeight(const QPoint& cell)
+{
+ // Using an items height directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ return cellItem->implicitHeight();
+}
+
+qreal QQuickTableViewPrivate::sizeHintForColumn(int column)
+{
+ // Find the widest cell in the column, and return its width
+ qreal columnWidth = 0;
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row)
+ columnWidth = qMax(columnWidth, cellWidth(QPoint(column, row)));
+
+ return columnWidth;
+}
+
+qreal QQuickTableViewPrivate::sizeHintForRow(int row)
+{
+ // Find the highest cell in the row, and return its height
+ qreal rowHeight = 0;
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column)
+ rowHeight = qMax(rowHeight, cellHeight(QPoint(column, row)));
+
+ return rowHeight;
+}
+
+void QQuickTableViewPrivate::calculateTableSize()
+{
+ // tableSize is the same as row and column count, and will always
+ // be the same as the number of rows and columns in the model.
+ Q_Q(QQuickTableView);
+ QSize prevTableSize = tableSize;
+
+ if (tableModel)
+ tableSize = QSize(tableModel->columns(), tableModel->rows());
+ else if (model)
+ tableSize = QSize(1, model->count());
+ else
+ tableSize = QSize(0, 0);
+
+ if (prevTableSize.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (prevTableSize.height() != tableSize.height())
+ emit q->rowsChanged();
+}
+
+qreal QQuickTableViewPrivate::resolveColumnWidth(int column)
+{
+ Q_TABLEVIEW_ASSERT(column >= loadedTable.left() && column <= loadedTable.right(), column);
+ qreal columnWidth = -1;
+
+ if (!columnWidthProvider.isUndefined()) {
+ if (columnWidthProvider.isCallable()) {
+ auto const columnAsArgument = QJSValueList() << QJSValue(column);
+ columnWidth = columnWidthProvider.call(columnAsArgument).toNumber();
+ if (qIsNaN(columnWidth) || columnWidth <= 0) {
+ // The column width needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling columns. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "columnWidthProvider did not return a valid width for column: " << column;
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ } else {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "columnWidthProvider doesn't contain a function";
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ } else {
+ // If columnWidthProvider is left unspecified, we just iterate over the currently visible items in
+ // the column. The downside of doing that, is that the column width will then only be based
+ // on the implicit width of the currently loaded items (which can be different depending on
+ // which row you're at when the column is flicked in). The upshot is that you don't have to
+ // bother setting columnWidthProvider for small tables, or if the implicit width doesn't vary.
+ columnWidth = sizeHintForColumn(column);
+ if (qIsNaN(columnWidth) || columnWidth <= 0) {
+ // The column width needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling columns. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "the delegate's implicitWidth needs to be greater than zero";
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ }
+
+ return columnWidth;
+}
+
+qreal QQuickTableViewPrivate::resolveRowHeight(int row)
+{
+ Q_TABLEVIEW_ASSERT(row >= loadedTable.top() && row <= loadedTable.bottom(), row);
+ qreal rowHeight = -1;
+
+ if (!rowHeightProvider.isUndefined()) {
+ if (rowHeightProvider.isCallable()) {
+ auto const rowAsArgument = QJSValueList() << QJSValue(row);
+ rowHeight = rowHeightProvider.call(rowAsArgument).toNumber();
+ if (qIsNaN(rowHeight) || rowHeight <= 0) {
+ // The row height needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling rows. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "rowHeightProvider did not return a valid height for row: " << row;
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ } else {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "rowHeightProvider doesn't contain a function";
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ } else {
+ // If rowHeightProvider is left unspecified, we just iterate over the currently visible items in
+ // the row. The downside of doing that, is that the row height will then only be based
+ // on the implicit height of the currently loaded items (which can be different depending on
+ // which column you're at when the row is flicked in). The upshot is that you don't have to
+ // bother setting rowHeightProvider for small tables, or if the implicit height doesn't vary.
+ rowHeight = sizeHintForRow(row);
+ if (qIsNaN(rowHeight) || rowHeight <= 0) {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "the delegate's implicitHeight needs to be greater than zero";
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ }
+
+ return rowHeight;
+}
+
+void QQuickTableViewPrivate::relayoutTable()
+{
+ relayoutTableItems();
+ syncLoadedTableRectFromLoadedTable();
+ enforceTableAtOrigin();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+ updateContentWidth();
+ updateContentHeight();
+ // Return back to updatePolish to loadAndUnloadVisibleEdges()
+ // since the re-layout might have caused some edges to be pushed
+ // out, while others might have been pushed in.
+}
+
+void QQuickTableViewPrivate::relayoutTableItems()
+{
+ qCDebug(lcTableViewDelegateLifecycle);
+ columnRowPositionsInvalid = false;
+
+ qreal nextColumnX = loadedTableOuterRect.x();
+ qreal nextRowY = loadedTableOuterRect.y();
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ // Adjust the geometry of all cells in the current column
+ const qreal width = resolveColumnWidth(column);
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveLeft(nextColumnX);
+ geometry.setWidth(width);
+ item->setGeometry(geometry);
+ }
+
+ nextColumnX += width + cellSpacing.width();
+ }
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ // Adjust the geometry of all cells in the current row
+ const qreal height = resolveRowHeight(row);
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveTop(nextRowY);
+ geometry.setHeight(height);
+ item->setGeometry(geometry);
+ }
+
+ nextRowY += height + cellSpacing.height();
+ }
+
+ if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ QPoint cell = QPoint(column, row);
+ qCDebug(lcTableViewDelegateLifecycle()) << "relayout item:" << cell << loadedTableItem(cell)->geometry();
+ }
+ }
+ }
+}
+
+void QQuickTableViewPrivate::layoutVerticalEdge(Qt::Edge tableEdge)
+{
+ int column = (tableEdge == Qt::LeftEdge) ? loadedTable.left() : loadedTable.right();
+ QPoint neighbourDirection = (tableEdge == Qt::LeftEdge) ? kRight : kLeft;
+ qreal width = resolveColumnWidth(column);
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(width);
+ geometry.setHeight(neighbourItem->geometry().height());
+ qreal left = tableEdge == Qt::LeftEdge ?
+ neighbourItem->geometry().left() - cellSpacing.width() - geometry.width() :
+ neighbourItem->geometry().right() + cellSpacing.width();
+
+ geometry.moveLeft(left);
+ geometry.moveTop(neighbourItem->geometry().top());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutHorizontalEdge(Qt::Edge tableEdge)
+{
+ int row = (tableEdge == Qt::TopEdge) ? loadedTable.top() : loadedTable.bottom();
+ QPoint neighbourDirection = (tableEdge == Qt::TopEdge) ? kDown : kUp;
+ qreal height = resolveRowHeight(row);
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(neighbourItem->geometry().width());
+ geometry.setHeight(height);
+ qreal top = tableEdge == Qt::TopEdge ?
+ neighbourItem->geometry().top() - cellSpacing.height() - geometry.height() :
+ neighbourItem->geometry().bottom() + cellSpacing.height();
+
+ geometry.moveTop(top);
+ geometry.moveLeft(neighbourItem->geometry().left());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutTopLeftItem()
+{
+ const QPoint cell = loadRequest.firstCell();
+ auto topLeftItem = loadedTableItem(cell);
+ auto item = topLeftItem->item;
+
+ item->setPosition(loadRequest.startPosition());
+ item->setSize(QSizeF(resolveColumnWidth(cell.x()), resolveRowHeight(cell.y())));
+ topLeftItem->setVisible(true);
+ qCDebug(lcTableViewDelegateLifecycle) << "geometry:" << topLeftItem->geometry();
+}
+
+void QQuickTableViewPrivate::layoutTableEdgeFromLoadRequest()
+{
+ if (loadRequest.edge() == Qt::Edge(0)) {
+ // No edge means we're loading the top-left item
+ layoutTopLeftItem();
+ return;
+ }
+
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::RightEdge:
+ layoutVerticalEdge(loadRequest.edge());
+ break;
+ case Qt::TopEdge:
+ case Qt::BottomEdge:
+ layoutHorizontalEdge(loadRequest.edge());
+ break;
+ }
+}
+
+void QQuickTableViewPrivate::cancelLoadRequest()
+{
+ loadRequest.markAsDone();
+ model->cancel(modelIndexAtCell(loadRequest.currentCell()));
+
+ if (rebuildScheduled) {
+ // No reason to rollback already loaded edge items
+ // since we anyway are about to reload all items.
+ return;
+ }
+
+ if (loadRequest.atBeginning()) {
+ // No items have yet been loaded, so nothing to unload
+ return;
+ }
+
+ QLine rollbackItems;
+ rollbackItems.setP1(loadRequest.firstCell());
+ rollbackItems.setP2(loadRequest.previousCell());
+ qCDebug(lcTableViewDelegateLifecycle()) << "rollback:" << rollbackItems << tableLayoutToString();
+ unloadItems(rollbackItems);
+}
+
+void QQuickTableViewPrivate::processLoadRequest()
+{
+ Q_TABLEVIEW_ASSERT(loadRequest.isActive(), "");
+
+ while (loadRequest.hasCurrentCell()) {
+ QPoint cell = loadRequest.currentCell();
+ FxTableItem *fxTableItem = loadFxTableItem(cell, loadRequest.incubationMode());
+
+ if (!fxTableItem) {
+ // Requested item is not yet ready. Just leave, and wait for this
+ // function to be called again when the item is ready.
+ return;
+ }
+
+ loadedItems.insert(modelIndexAtCell(cell), fxTableItem);
+ loadRequest.moveToNextCell();
+ }
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "all items loaded!";
+
+ syncLoadedTableFromLoadRequest();
+ layoutTableEdgeFromLoadRequest();
+ syncLoadedTableRectFromLoadedTable();
+
+ if (rebuildState == RebuildState::Done) {
+ enforceTableAtOrigin();
+ updateContentWidth();
+ updateContentHeight();
+ drainReusePoolAfterLoadRequest();
+ }
+
+ loadRequest.markAsDone();
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::processRebuildTable()
+{
+ moveToNextRebuildState();
+
+ if (rebuildState == RebuildState::LoadInitalTable) {
+ beginRebuildTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::VerifyTable) {
+ if (loadedItems.isEmpty()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ rebuildState = RebuildState::Done;
+ return;
+ }
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LayoutTable) {
+ layoutAfterLoadingInitialTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ const bool preload = (rebuildOptions & RebuildOption::All
+ && reusableFlag == QQmlTableInstanceModel::Reusable);
+
+ if (rebuildState == RebuildState::PreloadColumns) {
+ if (preload && loadedTable.right() < tableSize.width() - 1)
+ loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadRows) {
+ if (preload && loadedTable.bottom() < tableSize.height() - 1)
+ loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::MovePreloadedItemsToPool) {
+ while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
+ unloadEdge(edge);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+}
+
+bool QQuickTableViewPrivate::moveToNextRebuildState()
+{
+ if (loadRequest.isActive()) {
+ // Items are still loading async, which means
+ // that the current state is not yet done.
+ return false;
+ }
+ rebuildState = RebuildState(int(rebuildState) + 1);
+ qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ return true;
+}
+
+void QQuickTableViewPrivate::beginRebuildTable()
+{
+ if (loadRequest.isActive())
+ cancelLoadRequest();
+
+ calculateTableSize();
+
+ QPoint topLeft;
+ QPointF topLeftPos;
+
+ if (rebuildOptions & RebuildOption::All) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All";
+ releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
+ } else if (rebuildOptions & RebuildOption::ViewportOnly) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly";
+ releaseLoadedItems(reusableFlag);
+
+ if (rebuildOptions & RebuildOption::CalculateNewTopLeftRow) {
+ const int newRow = int(viewportRect.y() / (averageEdgeSize.height() + cellSpacing.height()));
+ topLeft.ry() = qBound(0, newRow, tableSize.height() - 1);
+ topLeftPos.ry() = topLeft.y() * (averageEdgeSize.height() + cellSpacing.height());
+ } else {
+ topLeft.ry() = qBound(0, loadedTable.topLeft().y(), tableSize.height() - 1);
+ topLeftPos.ry() = loadedTableOuterRect.topLeft().y();
+ }
+ if (rebuildOptions & RebuildOption::CalculateNewTopLeftColumn) {
+ const int newColumn = int(viewportRect.x() / (averageEdgeSize.width() + cellSpacing.width()));
+ topLeft.rx() = qBound(0, newColumn, tableSize.width() - 1);
+ topLeftPos.rx() = topLeft.x() * (averageEdgeSize.width() + cellSpacing.width());
+ } else {
+ topLeft.rx() = qBound(0, loadedTable.topLeft().x(), tableSize.width() - 1);
+ topLeftPos.rx() = loadedTableOuterRect.topLeft().x();
+ }
+ } else {
+ Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
+ }
+
+ loadedTable = QRect();
+ loadedTableOuterRect = QRect();
+ loadedTableInnerRect = QRect();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+ columnRowPositionsInvalid = false;
+
+ loadInitialTopLeftItem(topLeft, topLeftPos);
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+{
+ if (rowHeightProvider.isUndefined() || columnWidthProvider.isUndefined()) {
+ // Since we don't have both size providers, we need to calculate the
+ // size of each row and column based on the size of the delegate items.
+ // This couldn't be done while we were loading the initial rows and
+ // columns, since during the process, we didn't have all the items
+ // available yet for the calculation. So we do it now.
+ relayoutTable();
+ }
+
+ updateAverageEdgeSize();
+ updateContentWidth();
+ updateContentHeight();
+}
+
+void QQuickTableViewPrivate::loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos)
+{
+ Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), "");
+
+ if (tableSize.isEmpty())
+ return;
+
+ if (model->count() == 0)
+ return;
+
+ if (tableModel && !tableModel->delegate())
+ return;
+
+ // Load top-left item. After loaded, loadItemsInsideRect() will take
+ // care of filling out the rest of the table.
+ loadRequest.begin(cell, pos, QQmlIncubator::AsynchronousIfNested);
+ processLoadRequest();
+}
+
+void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
+{
+ unloadItems(rectangleEdge(loadedTable, edge));
+ loadedTable = expandedRect(loadedTable, edge, -1);
+ syncLoadedTableRectFromLoadedTable();
+ qCDebug(lcTableViewDelegateLifecycle) << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode)
+{
+ QLine cellsToLoad = rectangleEdge(expandedRect(loadedTable, edge, 1), edge);
+ loadRequest.begin(cellsToLoad, edge, incubationMode);
+ processLoadRequest();
+}
+
+void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
+{
+ // Unload table edges that have been moved outside the visible part of the
+ // table (including buffer area), and load new edges that has been moved inside.
+ // Note: an important point is that we always keep the table rectangular
+ // and without holes to reduce complexity (we never leave the table in
+ // a half-loaded state, or keep track of multiple patches).
+ // We load only one edge (row or column) at a time. This is especially
+ // important when loading into the buffer, since we need to be able to
+ // cancel the buffering quickly if the user starts to flick, and then
+ // focus all further loading on the edges that are flicked into view.
+
+ if (loadRequest.isActive()) {
+ // Don't start loading more edges while we're
+ // already waiting for another one to load.
+ return;
+ }
+
+ if (loadedItems.isEmpty()) {
+ // We need at least the top-left item to be loaded before we can
+ // start loading edges around it. Not having a top-left item at
+ // this point means that the model is empty (or no delegate).
+ return;
+ }
+
+ bool tableModified;
+
+ do {
+ tableModified = false;
+
+ if (Qt::Edge edge = nextEdgeToUnload(viewportRect)) {
+ tableModified = true;
+ unloadEdge(edge);
+ }
+
+ if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) {
+ tableModified = true;
+ loadEdge(edge, QQmlIncubator::AsynchronousIfNested);
+ if (loadRequest.isActive())
+ return;
+ }
+ } while (tableModified);
+
+}
+
+void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest()
+{
+ Q_Q(QQuickTableView);
+
+ if (reusableFlag == QQmlTableInstanceModel::NotReusable || !tableModel)
+ return;
+
+ if (!qFuzzyIsNull(q->verticalOvershoot()) || !qFuzzyIsNull(q->horizontalOvershoot())) {
+ // Don't drain while we're overshooting, since this will fill up the
+ // pool, but we expect to reuse them all once the content item moves back.
+ return;
+ }
+
+ // When loading edges, we don't want to drain the reuse pool too aggressively. Normally,
+ // all the items in the pool are reused rapidly as the content view is flicked around
+ // anyway. Even if the table is temporarily flicked to a section that contains fewer
+ // cells than what used to be (e.g if the flicked-in rows are taller than average), it
+ // still makes sense to keep all the items in circulation; Chances are, that soon enough,
+ // thinner rows are flicked back in again (meaning that we can fit more items into the
+ // view). But at the same time, if a delegate chooser is in use, the pool might contain
+ // items created from different delegates. And some of those delegates might be used only
+ // occasionally. So to avoid situations where an item ends up in the pool for too long, we
+ // call drain after each load request, but with a sufficiently large pool time. (If an item
+ // in the pool has a large pool time, it means that it hasn't been reused for an equal
+ // amount of load cycles, and should be released).
+ //
+ // We calculate an appropriate pool time by figuring out what the minimum time must be to
+ // not disturb frequently reused items. Since the number of items in a row might be higher
+ // than in a column (or vice versa), the minimum pool time should take into account that
+ // you might be flicking out a single row (filling up the pool), before you continue
+ // flicking in several new columns (taking them out again, but now in smaller chunks). This
+ // will increase the number of load cycles items are kept in the pool (poolTime), but still,
+ // we shouldn't release them, as they are still being reused frequently.
+ // To get a flexible maxValue (that e.g tolerates rows and columns being flicked
+ // in with varying sizes, causing some items not to be resued immediately), we multiply the
+ // value by 2. Note that we also add an extra +1 to the column count, because the number of
+ // visible columns will fluctuate between +1/-1 while flicking.
+ const int w = loadedTable.width();
+ const int h = loadedTable.height();
+ const int minTime = int(std::ceil(w > h ? qreal(w + 1) / h : qreal(h + 1) / w));
+ const int maxTime = minTime * 2;
+ tableModel->drainReusableItemsPool(maxTime);
+}
+
+void QQuickTableViewPrivate::scheduleRebuildTable(RebuildOptions options) {
+ if (!q_func()->isComponentComplete()) {
+ // We'll rebuild the table once complete anyway
+ return;
+ }
+
+ rebuildScheduled = true;
+ scheduledRebuildOptions |= options;
+ q_func()->polish();
+}
+
+void QQuickTableViewPrivate::invalidateColumnRowPositions() {
+ columnRowPositionsInvalid = true;
+ q_func()->polish();
+}
+
+void QQuickTableViewPrivate::updatePolish()
+{
+ // Whenever something changes, e.g viewport moves, spacing is set to a
+ // new value, model changes etc, this function will end up being called. Here
+ // we check what needs to be done, and load/unload cells accordingly.
+
+ Q_TABLEVIEW_ASSERT(!polishing, "recursive updatePolish() calls are not allowed!");
+ QBoolBlocker polishGuard(polishing, true);
+
+ if (loadRequest.isActive()) {
+ // We're currently loading items async to build a new edge in the table. We see the loading
+ // as an atomic operation, which means that we don't continue doing anything else until all
+ // items have been received and laid out. Note that updatePolish is then called once more
+ // after the loadRequest has completed to handle anything that might have occurred in-between.
+ return;
+ }
+
+ if (rebuildState != RebuildState::Done) {
+ processRebuildTable();
+ return;
+ }
+
+ syncWithPendingChanges();
+
+ if (rebuildState == RebuildState::Begin) {
+ processRebuildTable();
+ return;
+ }
+
+ if (loadedItems.isEmpty())
+ return;
+
+ if (columnRowPositionsInvalid)
+ relayoutTable();
+
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent)
+{
+ if (rebuildScheduled || rebuildState != RebuildState::Done)
+ return;
+
+ QQuickFlickablePrivate::fixup(data, minExtent, maxExtent);
+}
+
+void QQuickTableViewPrivate::createWrapperModel()
+{
+ Q_Q(QQuickTableView);
+ // When the assigned model is not an instance model, we create a wrapper
+ // model (QQmlTableInstanceModel) that keeps a pointer to both the
+ // assigned model and the assigned delegate. This model will give us a
+ // common interface to any kind of model (js arrays, QAIM, number etc), and
+ // help us create delegate instances.
+ tableModel = new QQmlTableInstanceModel(qmlContext(q));
+ model = tableModel;
+}
+
+void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*)
+{
+ if (blockItemCreatedCallback)
+ return;
+
+ qCDebug(lcTableViewDelegateLifecycle) << "item done loading:"
+ << cellAtModelIndex(modelIndex);
+
+ // Since the item we waited for has finished incubating, we can
+ // continue with the load request. processLoadRequest will
+ // ask the model for the requested item once more, which will be
+ // quick since the model has cached it.
+ processLoadRequest();
+ loadAndUnloadVisibleEdges();
+ updatePolish();
+}
+
+void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+ Q_Q(QQuickTableView);
+
+ if (auto item = qmlobject_cast<QQuickItem*>(object)) {
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ }
+
+ if (auto attached = getAttachedObject(object))
+ attached->setView(q);
+}
+
+void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->pooled();
+}
+
+void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->reused();
+}
+
+void QQuickTableViewPrivate::syncWithPendingChanges()
+{
+ // The application can change properties like the model or the delegate while
+ // we're e.g in the middle of e.g loading a new row. Since this will lead to
+ // unpredicted behavior, and possibly a crash, we need to postpone taking
+ // such assignments into effect until we're in a state that allows it.
+ Q_Q(QQuickTableView);
+ viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height());
+ syncRebuildOptions();
+ syncModel();
+ syncDelegate();
+}
+
+void QQuickTableViewPrivate::syncRebuildOptions()
+{
+ if (!rebuildScheduled)
+ return;
+
+ rebuildState = RebuildState::Begin;
+ rebuildOptions = scheduledRebuildOptions;
+ scheduledRebuildOptions = RebuildOption::None;
+ rebuildScheduled = false;
+}
+
+void QQuickTableViewPrivate::syncDelegate()
+{
+ if (tableModel && assignedDelegate == tableModel->delegate())
+ return;
+
+ if (!tableModel)
+ createWrapperModel();
+
+ tableModel->setDelegate(assignedDelegate);
+}
+
+void QQuickTableViewPrivate::syncModel()
+{
+ if (modelVariant == assignedModel)
+ return;
+
+ if (model)
+ disconnectFromModel();
+
+ modelVariant = assignedModel;
+ QVariant effectiveModelVariant = modelVariant;
+ if (effectiveModelVariant.userType() == qMetaTypeId<QJSValue>())
+ effectiveModelVariant = effectiveModelVariant.value<QJSValue>().toVariant();
+
+ const auto instanceModel = qobject_cast<QQmlInstanceModel *>(qvariant_cast<QObject*>(effectiveModelVariant));
+
+ if (instanceModel) {
+ if (tableModel) {
+ delete tableModel;
+ tableModel = nullptr;
+ }
+ model = instanceModel;
+ } else {
+ if (!tableModel)
+ createWrapperModel();
+ tableModel->setModel(effectiveModelVariant);
+ }
+
+ connectToModel();
+}
+
+void QQuickTableViewPrivate::connectToModel()
+{
+ Q_TABLEVIEW_ASSERT(model, "");
+
+ QObjectPrivate::connect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::connect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+
+ if (tableModel) {
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
+ if (auto const aim = model->abstractItemModel()) {
+ // When the model exposes a QAIM, we connect to it directly. This means that if the current model is
+ // a QQmlDelegateModel, we just ignore all the change sets it emits. In most cases, the model will instead
+ // be our own QQmlTableInstanceModel, which doesn't bother creating change sets at all. For models that are
+ // not based on QAIM (like QQmlObjectModel, QQmlListModel, javascript arrays etc), there is currently no way
+ // to modify the model at runtime without also re-setting the model on the view.
+ connect(aim, &QAbstractItemModel::rowsMoved, this, &QQuickTableViewPrivate::rowsMovedCallback);
+ connect(aim, &QAbstractItemModel::columnsMoved, this, &QQuickTableViewPrivate::columnsMovedCallback);
+ connect(aim, &QAbstractItemModel::rowsInserted, this, &QQuickTableViewPrivate::rowsInsertedCallback);
+ connect(aim, &QAbstractItemModel::rowsRemoved, this, &QQuickTableViewPrivate::rowsRemovedCallback);
+ connect(aim, &QAbstractItemModel::columnsInserted, this, &QQuickTableViewPrivate::columnsInsertedCallback);
+ connect(aim, &QAbstractItemModel::columnsRemoved, this, &QQuickTableViewPrivate::columnsRemovedCallback);
+ connect(aim, &QAbstractItemModel::modelReset, this, &QQuickTableViewPrivate::modelResetCallback);
+ connect(aim, &QAbstractItemModel::layoutChanged, this, &QQuickTableViewPrivate::layoutChangedCallback);
+ } else {
+ QObjectPrivate::connect(model, &QQmlInstanceModel::modelUpdated, this, &QQuickTableViewPrivate::modelUpdated);
+ }
+}
+
+void QQuickTableViewPrivate::disconnectFromModel()
+{
+ Q_TABLEVIEW_ASSERT(model, "");
+
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+
+ if (tableModel) {
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
+ if (auto const aim = model->abstractItemModel()) {
+ disconnect(aim, &QAbstractItemModel::rowsMoved, this, &QQuickTableViewPrivate::rowsMovedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsMoved, this, &QQuickTableViewPrivate::columnsMovedCallback);
+ disconnect(aim, &QAbstractItemModel::rowsInserted, this, &QQuickTableViewPrivate::rowsInsertedCallback);
+ disconnect(aim, &QAbstractItemModel::rowsRemoved, this, &QQuickTableViewPrivate::rowsRemovedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsInserted, this, &QQuickTableViewPrivate::columnsInsertedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsRemoved, this, &QQuickTableViewPrivate::columnsRemovedCallback);
+ disconnect(aim, &QAbstractItemModel::modelReset, this, &QQuickTableViewPrivate::modelResetCallback);
+ disconnect(aim, &QAbstractItemModel::layoutChanged, this, &QQuickTableViewPrivate::layoutChangedCallback);
+ } else {
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::modelUpdated, this, &QQuickTableViewPrivate::modelUpdated);
+ }
+}
+
+void QQuickTableViewPrivate::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ Q_UNUSED(changeSet);
+ Q_UNUSED(reset);
+
+ Q_TABLEVIEW_ASSERT(!model->abstractItemModel(), "");
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsMovedCallback(const QModelIndex &parent, int, int, const QModelIndex &, int )
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsMovedCallback(const QModelIndex &parent, int, int, const QModelIndex &, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsInsertedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsRemovedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsInsertedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsRemovedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
+{
+ Q_UNUSED(parents);
+ Q_UNUSED(hint);
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::modelResetCallback()
+{
+ scheduleRebuildTable(RebuildOption::All);
+}
+
+QQuickTableView::QQuickTableView(QQuickItem *parent)
+ : QQuickFlickable(*(new QQuickTableViewPrivate), parent)
+{
+ setFlag(QQuickItem::ItemIsFocusScope);
+}
+
+int QQuickTableView::rows() const
+{
+ return d_func()->tableSize.height();
+}
+
+int QQuickTableView::columns() const
+{
+ return d_func()->tableSize.width();
+}
+
+qreal QQuickTableView::rowSpacing() const
+{
+ return d_func()->cellSpacing.height();
+}
+
+void QQuickTableView::setRowSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(spacing) || !qt_is_finite(spacing) || spacing < 0)
+ return;
+ if (qFuzzyCompare(d->cellSpacing.height(), spacing))
+ return;
+
+ d->cellSpacing.setHeight(spacing);
+ d->invalidateColumnRowPositions();
+ emit rowSpacingChanged();
+}
+
+qreal QQuickTableView::columnSpacing() const
+{
+ return d_func()->cellSpacing.width();
+}
+
+void QQuickTableView::setColumnSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(spacing) || !qt_is_finite(spacing) || spacing < 0)
+ return;
+ if (qFuzzyCompare(d->cellSpacing.width(), spacing))
+ return;
+
+ d->cellSpacing.setWidth(spacing);
+ d->invalidateColumnRowPositions();
+ emit columnSpacingChanged();
+}
+
+QJSValue QQuickTableView::rowHeightProvider() const
+{
+ return d_func()->rowHeightProvider;
+}
+
+void QQuickTableView::setRowHeightProvider(QJSValue provider)
+{
+ Q_D(QQuickTableView);
+ if (provider.strictlyEquals(d->rowHeightProvider))
+ return;
+
+ d->rowHeightProvider = provider;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ emit rowHeightProviderChanged();
+}
+
+QJSValue QQuickTableView::columnWidthProvider() const
+{
+ return d_func()->columnWidthProvider;
+}
+
+void QQuickTableView::setColumnWidthProvider(QJSValue provider)
+{
+ Q_D(QQuickTableView);
+ if (provider.strictlyEquals(d->columnWidthProvider))
+ return;
+
+ d->columnWidthProvider = provider;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ emit columnWidthProviderChanged();
+}
+
+QVariant QQuickTableView::model() const
+{
+ return d_func()->assignedModel;
+}
+
+void QQuickTableView::setModel(const QVariant &newModel)
+{
+ Q_D(QQuickTableView);
+ if (newModel == d->assignedModel)
+ return;
+
+ d->assignedModel = newModel;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
+ emit modelChanged();
+}
+
+QQmlComponent *QQuickTableView::delegate() const
+{
+ return d_func()->assignedDelegate;
+}
+
+void QQuickTableView::setDelegate(QQmlComponent *newDelegate)
+{
+ Q_D(QQuickTableView);
+ if (newDelegate == d->assignedDelegate)
+ return;
+
+ d->assignedDelegate = newDelegate;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
+
+ emit delegateChanged();
+}
+
+bool QQuickTableView::reuseItems() const
+{
+ return bool(d_func()->reusableFlag == QQmlTableInstanceModel::Reusable);
+}
+
+void QQuickTableView::setReuseItems(bool reuse)
+{
+ Q_D(QQuickTableView);
+ if (reuseItems() == reuse)
+ return;
+
+ d->reusableFlag = reuse ? QQmlTableInstanceModel::Reusable : QQmlTableInstanceModel::NotReusable;
+
+ if (!reuse && d->tableModel) {
+ // When we're told to not reuse items, we
+ // immediately, as documented, drain the pool.
+ d->tableModel->drainReusableItemsPool(0);
+ }
+
+ emit reuseItemsChanged();
+}
+
+void QQuickTableView::setContentWidth(qreal width)
+{
+ Q_D(QQuickTableView);
+ d->explicitContentWidth = width;
+ QQuickFlickable::setContentWidth(width);
+}
+
+void QQuickTableView::setContentHeight(qreal height)
+{
+ Q_D(QQuickTableView);
+ d->explicitContentHeight = height;
+ QQuickFlickable::setContentHeight(height);
+}
+
+void QQuickTableView::forceLayout()
+{
+ Q_D(QQuickTableView);
+ d->columnRowPositionsInvalid = true;
+
+ if (d->polishing) {
+ qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!";
+ polish();
+ return;
+ }
+
+ d->updatePolish();
+}
+
+QQuickTableViewAttached *QQuickTableView::qmlAttachedProperties(QObject *obj)
+{
+ return new QQuickTableViewAttached(obj);
+}
+
+void QQuickTableView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
+
+ if (d->tableModel) {
+ // When the view changes size, we force the pool to
+ // shrink by releasing all pooled items.
+ d->tableModel->drainReusableItemsPool(0);
+ }
+
+ polish();
+}
+
+void QQuickTableView::viewportMoved(Qt::Orientations orientation)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::viewportMoved(orientation);
+
+ QQuickTableViewPrivate::RebuildOptions options = QQuickTableViewPrivate::RebuildOption::None;
+
+ // Check the viewport moved more than one page vertically
+ if (!d->viewportRect.intersects(QRectF(d->viewportRect.x(), contentY(), 1, height())))
+ options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow;
+ // Check the viewport moved more than one page horizontally
+ if (!d->viewportRect.intersects(QRectF(contentX(), d->viewportRect.y(), width(), 1)))
+ options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn;
+
+ if (options) {
+ // When the viewport has moved more than one page vertically or horizontally, we switch
+ // strategy from refilling edges around the current table to instead rebuild the table
+ // from scratch inside the new viewport. This will greatly improve performance when flicking
+ // a long distance in one go, which can easily happen when dragging on scrollbars.
+ options |= QQuickTableViewPrivate::RebuildOption::ViewportOnly;
+ d->scheduleRebuildTable(options);
+ }
+
+ if (d->rebuildScheduled) {
+ // No reason to do anything, since we're about to rebuild the whole table anyway.
+ // Besides, calling updatePolish, which will start the rebuild, can easily cause
+ // binding loops to happen since we usually end up modifying the geometry of the
+ // viewport (contentItem) as well.
+ return;
+ }
+
+ // Calling polish() will schedule a polish event. But while the user is flicking, several
+ // mouse events will be handled before we get an updatePolish() call. And the updatePolish()
+ // call will only see the last mouse position. This results in a stuttering flick experience
+ // (especially on windows). We improve on this by calling updatePolish() directly. But this
+ // has the pitfall that we open up for recursive callbacks. E.g while inside updatePolish(), we
+ // load/unload items, and emit signals. The application can listen to those signals and set a
+ // new contentX/Y on the flickable. So we need to guard for this, to avoid unexpected behavior.
+ if (d->polishing)
+ polish();
+ else
+ d->updatePolish();
+}
+
+void QQuickTableViewPrivate::_q_componentFinalized()
+{
+ // Now that all bindings are evaluated, and we know
+ // our final geometery, we can build the table.
+ qCDebug(lcTableViewDelegateLifecycle);
+ updatePolish();
+}
+
+void QQuickTableViewPrivate::registerCallbackWhenBindingsAreEvaluated()
+{
+ // componentComplete() is called on us after all static values have been assigned, but
+ // before bindings to any anchestors has been evaluated. Especially this means that
+ // if our size is bound to the parents size, it will still be empty at that point.
+ // And we cannot build the table without knowing our own size. We could wait until we
+ // got the first updatePolish() callback, but at that time, any asynchronous loaders that we
+ // might be inside have already finished loading, which means that we would load all
+ // the delegate items synchronously instead of asynchronously. We therefore add a componentFinalized
+ // function that gets called after all the bindings we rely on has been evaluated.
+ // When receiving this call, we load the delegate items (and build the table).
+ Q_Q(QQuickTableView);
+ QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(q));
+ static int finalizedIdx = -1;
+ if (finalizedIdx < 0)
+ finalizedIdx = q->metaObject()->indexOfSlot("_q_componentFinalized()");
+ engPriv->registerFinalizeCallback(q, finalizedIdx);
+}
+
+void QQuickTableView::componentComplete()
+{
+ QQuickFlickable::componentComplete();
+ d_func()->registerCallbackWhenBindingsAreEvaluated();
+}
+
+#include "moc_qquicktableview_p.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
new file mode 100644
index 0000000000..9fcd4c6c17
--- /dev/null
+++ b/src/quick/items/qquicktableview_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEW_P_H
+#define QQUICKTABLEVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+QT_REQUIRE_CONFIG(quick_tableview);
+
+#include <QtCore/qpointer.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQml/private/qqmlnullablevalue_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTableViewAttached;
+class QQuickTableViewPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int rows READ rows NOTIFY rowsChanged)
+ Q_PROPERTY(int columns READ columns NOTIFY columnsChanged)
+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
+ Q_PROPERTY(QJSValue rowHeightProvider READ rowHeightProvider WRITE setRowHeightProvider NOTIFY rowHeightProviderChanged)
+ Q_PROPERTY(QJSValue columnWidthProvider READ columnWidthProvider WRITE setColumnWidthProvider NOTIFY columnWidthProviderChanged)
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(bool reuseItems READ reuseItems WRITE setReuseItems NOTIFY reuseItemsChanged)
+ Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
+ Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentWidth NOTIFY contentHeightChanged)
+
+public:
+ QQuickTableView(QQuickItem *parent = nullptr);
+
+ int rows() const;
+ int columns() const;
+
+ qreal rowSpacing() const;
+ void setRowSpacing(qreal spacing);
+
+ qreal columnSpacing() const;
+ void setColumnSpacing(qreal spacing);
+
+ QJSValue rowHeightProvider() const;
+ void setRowHeightProvider(QJSValue provider);
+
+ QJSValue columnWidthProvider() const;
+ void setColumnWidthProvider(QJSValue provider);
+
+ QVariant model() const;
+ void setModel(const QVariant &newModel);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ bool reuseItems() const;
+ void setReuseItems(bool reuseItems);
+
+ void setContentWidth(qreal width);
+ void setContentHeight(qreal height);
+
+ Q_INVOKABLE void forceLayout();
+
+ static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
+
+Q_SIGNALS:
+ void rowsChanged();
+ void columnsChanged();
+ void rowSpacingChanged();
+ void columnSpacingChanged();
+ void rowHeightProviderChanged();
+ void columnWidthProviderChanged();
+ void modelChanged();
+ void delegateChanged();
+ void reuseItemsChanged();
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void viewportMoved(Qt::Orientations orientation) override;
+ void componentComplete() override;
+
+private:
+ Q_DISABLE_COPY(QQuickTableView)
+ Q_DECLARE_PRIVATE(QQuickTableView)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_componentFinalized())
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged)
+
+public:
+ QQuickTableViewAttached(QObject *parent)
+ : QObject(parent) {}
+
+ QQuickTableView *view() const { return m_view; }
+ void setView(QQuickTableView *newTableView) {
+ if (newTableView == m_view)
+ return;
+ m_view = newTableView;
+ Q_EMIT viewChanged();
+ }
+
+Q_SIGNALS:
+ void viewChanged();
+ void pooled();
+ void reused();
+
+private:
+ QPointer<QQuickTableView> m_view;
+
+ friend class QQuickTableViewPrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableView)
+QML_DECLARE_TYPEINFO(QQuickTableView, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKTABLEVIEW_P_H
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
new file mode 100644
index 0000000000..2ed04f8d29
--- /dev/null
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEW_P_P_H
+#define QQUICKTABLEVIEW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquicktableview_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/private/qqmltableinstancemodel_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
+
+static const qreal kDefaultRowHeight = 50;
+static const qreal kDefaultColumnWidth = 50;
+
+class FxTableItem;
+
+class Q_QML_AUTOTEST_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTableView)
+
+public:
+ class TableEdgeLoadRequest
+ {
+ // Whenever we need to load new rows or columns in the
+ // table, we fill out a TableEdgeLoadRequest.
+ // TableEdgeLoadRequest is just a struct that keeps track
+ // of which cells that needs to be loaded, and which cell
+ // the table is currently loading. The loading itself is
+ // done by QQuickTableView.
+
+ public:
+ void begin(const QPoint &cell, const QPointF &pos, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = Qt::Edge(0);
+ tableCells = QLine(cell, cell);
+ mode = incubationMode;
+ cellCount = 1;
+ currentIndex = 0;
+ startPos = pos;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
+ }
+
+ void begin(const QLine cellsToLoad, Qt::Edge edgeToLoad, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = edgeToLoad;
+ tableCells = cellsToLoad;
+ mode = incubationMode;
+ cellCount = tableCells.x2() - tableCells.x1() + tableCells.y2() - tableCells.y1() + 1;
+ currentIndex = 0;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
+ }
+
+ inline void markAsDone() { active = false; }
+ inline bool isActive() { return active; }
+
+ inline QPoint firstCell() { return tableCells.p1(); }
+ inline QPoint lastCell() { return tableCells.p2(); }
+ inline QPoint currentCell() { return cellAt(currentIndex); }
+ inline QPoint previousCell() { return cellAt(currentIndex - 1); }
+
+ inline bool atBeginning() { return currentIndex == 0; }
+ inline bool hasCurrentCell() { return currentIndex < cellCount; }
+ inline void moveToNextCell() { ++currentIndex; }
+
+ inline Qt::Edge edge() { return tableEdge; }
+ inline QQmlIncubator::IncubationMode incubationMode() { return mode; }
+
+ inline QPointF startPosition() { return startPos; }
+
+ QString toString()
+ {
+ QString str;
+ QDebug dbg(&str);
+ dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
+ << tableEdge << " cells:" << tableCells << " incubation:";
+
+ switch (mode) {
+ case QQmlIncubator::Asynchronous:
+ dbg << "Asynchronous";
+ break;
+ case QQmlIncubator::AsynchronousIfNested:
+ dbg << "AsynchronousIfNested";
+ break;
+ case QQmlIncubator::Synchronous:
+ dbg << "Synchronous";
+ break;
+ }
+
+ return str;
+ }
+
+ private:
+ Qt::Edge tableEdge = Qt::Edge(0);
+ QLine tableCells;
+ int currentIndex = 0;
+ int cellCount = 0;
+ bool active = false;
+ QQmlIncubator::IncubationMode mode = QQmlIncubator::AsynchronousIfNested;
+ QPointF startPos;
+
+ QPoint cellAt(int index)
+ {
+ int x = tableCells.p1().x() + (tableCells.dx() ? index : 0);
+ int y = tableCells.p1().y() + (tableCells.dy() ? index : 0);
+ return QPoint(x, y);
+ }
+ };
+
+ enum class RebuildState {
+ Begin = 0,
+ LoadInitalTable,
+ VerifyTable,
+ LayoutTable,
+ LoadAndUnloadAfterLayout,
+ PreloadColumns,
+ PreloadRows,
+ MovePreloadedItemsToPool,
+ Done
+ };
+
+ enum class RebuildOption {
+ None = 0,
+ ViewportOnly = 0x1,
+ CalculateNewTopLeftRow = 0x2,
+ CalculateNewTopLeftColumn = 0x4,
+ All = 0x8,
+ };
+ Q_DECLARE_FLAGS(RebuildOptions, RebuildOption)
+
+public:
+ QQuickTableViewPrivate();
+ ~QQuickTableViewPrivate() override;
+
+ static inline QQuickTableViewPrivate *get(QQuickTableView *q) { return q->d_func(); }
+
+ void updatePolish() override;
+ void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
+
+public:
+ QHash<int, FxTableItem *> loadedItems;
+
+ // model, tableModel and modelVariant all point to the same model. modelVariant
+ // is the model assigned by the user. And tableModel is the wrapper model we create
+ // around it. But if the model is an instance model directly, we cannot wrap it, so
+ // we need a pointer for that case as well.
+ QQmlInstanceModel* model = nullptr;
+ QPointer<QQmlTableInstanceModel> tableModel = nullptr;
+ QVariant modelVariant;
+
+ // When the applications assignes a new model or delegate to the view, we keep them
+ // around until we're ready to take them into use (syncWithPendingChanges).
+ QVariant assignedModel = QVariant(int(0));
+ QQmlComponent *assignedDelegate = nullptr;
+
+ // loadedTable describes the table cells that are currently loaded (from top left
+ // row/column to bottom right row/column). loadedTableOuterRect describes the actual
+ // pixels that those cells cover, and is matched agains the viewport to determine when
+ // we need to fill up with more rows/columns. loadedTableInnerRect describes the pixels
+ // that the loaded table covers if you remove one row/column on each side of the table, and
+ // is used to determine rows/columns that are no longer visible and can be unloaded.
+ QRect loadedTable;
+ QRectF loadedTableOuterRect;
+ QRectF loadedTableInnerRect;
+
+ QRectF viewportRect = QRectF(0, 0, -1, -1);
+
+ QSize tableSize;
+
+ RebuildState rebuildState = RebuildState::Done;
+ RebuildOptions rebuildOptions = RebuildOption::All;
+ RebuildOptions scheduledRebuildOptions = RebuildOption::All;
+
+ TableEdgeLoadRequest loadRequest;
+
+ QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
+ QSizeF cellSpacing = QSizeF(0, 0);
+
+ QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
+
+ bool blockItemCreatedCallback = false;
+ bool columnRowPositionsInvalid = false;
+ bool layoutWarningIssued = false;
+ bool polishing = false;
+ bool rebuildScheduled = true;
+
+ QJSValue rowHeightProvider;
+ QJSValue columnWidthProvider;
+
+ // TableView uses contentWidth/height to report the size of the table (this
+ // will e.g make scrollbars written for Flickable work out of the box). This
+ // value is continuously calculated, and will change/improve as more columns
+ // are loaded into view. At the same time, we want to open up for the
+ // possibility that the application can set the content width explicitly, in
+ // case it knows what the exact width should be from the start. We therefore
+ // override the contentWidth/height properties from QQuickFlickable, to be able
+ // to implement this combined behavior. This also lets us lazy build the table
+ // if the application needs to know the content size early on.
+ QQmlNullableValue<qreal> explicitContentWidth;
+ QQmlNullableValue<qreal> explicitContentHeight;
+
+ QSizeF averageEdgeSize;
+
+ const static QPoint kLeft;
+ const static QPoint kRight;
+ const static QPoint kUp;
+ const static QPoint kDown;
+
+#ifdef QT_DEBUG
+ QString forcedIncubationMode = qEnvironmentVariable("QT_TABLEVIEW_INCUBATION_MODE");
+#endif
+
+public:
+ QQuickTableViewAttached *getAttachedObject(const QObject *object) const;
+
+ int modelIndexAtCell(const QPoint &cell) const;
+ QPoint cellAtModelIndex(int modelIndex) const;
+
+ qreal sizeHintForColumn(int column);
+ qreal sizeHintForRow(int row);
+ void calculateTableSize();
+
+ qreal resolveColumnWidth(int column);
+ qreal resolveRowHeight(int row);
+
+ void relayoutTable();
+ void relayoutTableItems();
+
+ void layoutVerticalEdge(Qt::Edge tableEdge);
+ void layoutHorizontalEdge(Qt::Edge tableEdge);
+ void layoutTopLeftItem();
+ void layoutTableEdgeFromLoadRequest();
+
+ void updateContentWidth();
+ void updateContentHeight();
+ void updateAverageEdgeSize();
+
+ void enforceTableAtOrigin();
+ void syncLoadedTableRectFromLoadedTable();
+ void syncLoadedTableFromLoadRequest();
+
+ bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ Qt::Edge nextEdgeToLoad(const QRectF rect);
+ Qt::Edge nextEdgeToUnload(const QRectF rect);
+
+ qreal cellWidth(const QPoint &cell);
+ qreal cellHeight(const QPoint &cell);
+
+ FxTableItem *loadedTableItem(const QPoint &cell) const;
+ FxTableItem *itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const;
+ FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+ FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+
+ void releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag);
+ void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
+
+ void unloadItem(const QPoint &cell);
+ void unloadItems(const QLine &items);
+
+ void loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos);
+ void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
+ void unloadEdge(Qt::Edge edge);
+ void loadAndUnloadVisibleEdges();
+ void drainReusePoolAfterLoadRequest();
+ void cancelLoadRequest();
+ void processLoadRequest();
+
+ void processRebuildTable();
+ bool moveToNextRebuildState();
+ void beginRebuildTable();
+ void layoutAfterLoadingInitialTable();
+
+ void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
+ void invalidateColumnRowPositions();
+
+ void createWrapperModel();
+
+ void initItemCallback(int modelIndex, QObject *item);
+ void itemCreatedCallback(int modelIndex, QObject *object);
+ void itemPooledCallback(int modelIndex, QObject *object);
+ void itemReusedCallback(int modelIndex, QObject *object);
+ void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ inline void syncWithPendingChanges();
+ inline void syncDelegate();
+ inline void syncModel();
+ inline void syncRebuildOptions();
+
+ void connectToModel();
+ void disconnectFromModel();
+
+ void rowsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
+ void columnsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column);
+ void rowsInsertedCallback(const QModelIndex &parent, int begin, int end);
+ void rowsRemovedCallback(const QModelIndex &parent, int begin, int end);
+ void columnsInsertedCallback(const QModelIndex &parent, int begin, int end);
+ void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
+ void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
+ void modelResetCallback();
+
+ void _q_componentFinalized();
+ void registerCallbackWhenBindingsAreEvaluated();
+
+ inline QString tableLayoutToString() const;
+ void dumpTable() const;
+};
+
+class FxTableItem : public QQuickItemViewFxItem
+{
+public:
+ FxTableItem(QQuickItem *item, QQuickTableView *table, bool own)
+ : QQuickItemViewFxItem(item, own, QQuickTableViewPrivate::get(table))
+ {
+ }
+
+ qreal position() const override { return 0; }
+ qreal endPosition() const override { return 0; }
+ qreal size() const override { return 0; }
+ qreal sectionSize() const override { return 0; }
+ bool contains(qreal, qreal) const override { return false; }
+
+ QPoint cell;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 76720e5c5d..3cce30aaf6 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -88,6 +88,7 @@ QQuickTextPrivate::QQuickTextPrivate()
, truncated(false), hAlignImplicit(true), rightToLeftText(false)
, layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
, polishSize(false)
+ , updateSizeRecursionGuard(false)
{
implicitAntialiasing = true;
}
@@ -442,6 +443,7 @@ void QQuickTextPrivate::updateSize()
//### need to confirm cost of always setting these for richText
internalWidthUpdate = true;
+ qreal oldWidth = q->width();
qreal iWidth = -1;
if (!q->widthValid())
iWidth = size.width();
@@ -449,30 +451,45 @@ void QQuickTextPrivate::updateSize()
q->setImplicitSize(iWidth + hPadding, size.height() + vPadding);
internalWidthUpdate = false;
- if (iWidth == -1)
- q->setImplicitHeight(size.height() + vPadding);
-
- QTextBlock firstBlock = extra->doc->firstBlock();
- while (firstBlock.layout()->lineCount() == 0)
- firstBlock = firstBlock.next();
-
- QTextBlock lastBlock = extra->doc->lastBlock();
- while (lastBlock.layout()->lineCount() == 0)
- lastBlock = lastBlock.previous();
-
- if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
- QTextLine firstLine = firstBlock.layout()->lineAt(0);
- QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
- advance = QSizeF(lastLine.horizontalAdvance(),
- (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ // If the implicit width update caused a recursive change of the width,
+ // we will have skipped integral parts of the layout due to the
+ // internalWidthUpdate recursion guard. To make sure everything is up
+ // to date, we need to run a second pass over the layout when updateSize()
+ // is done.
+ if (!qFuzzyCompare(q->width(), oldWidth) && !updateSizeRecursionGuard) {
+ updateSizeRecursionGuard = true;
+ updateSize();
+ updateSizeRecursionGuard = false;
} else {
- advance = QSizeF();
+ if (iWidth == -1)
+ q->setImplicitHeight(size.height() + vPadding);
+
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ while (firstBlock.layout()->lineCount() == 0)
+ firstBlock = firstBlock.next();
+
+ QTextBlock lastBlock = extra->doc->lastBlock();
+ while (lastBlock.layout()->lineCount() == 0)
+ lastBlock = lastBlock.previous();
+
+ if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
+ QTextLine firstLine = firstBlock.layout()->lineAt(0);
+ QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ } else {
+ advance = QSizeF();
+ }
}
}
if (layedOutTextRect.size() != previousSize)
emit q->contentSizeChanged();
+ if (layedOutTextRect.width() != previousSize.width())
+ emit q->contentWidthChanged(layedOutTextRect.width());
+ if (layedOutTextRect.height() != previousSize.height())
+ emit q->contentHeightChanged(layedOutTextRect.height());
updateType = UpdatePaintNode;
q->update();
}
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index 15e989c13d..1af60051fb 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -79,10 +79,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged)
Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode?
- Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentSizeChanged)
- Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentSizeChanged)
- Q_PROPERTY(qreal paintedWidth READ contentWidth NOTIFY contentSizeChanged) // Compatibility
- Q_PROPERTY(qreal paintedHeight READ contentHeight NOTIFY contentSizeChanged)
+ Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentWidthChanged)
+ Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
+ Q_PROPERTY(qreal paintedWidth READ contentWidth NOTIFY contentWidthChanged) // Compatibility
+ Q_PROPERTY(qreal paintedHeight READ contentHeight NOTIFY contentHeightChanged)
Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged)
Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged)
Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged)
@@ -272,6 +272,10 @@ Q_SIGNALS:
void textFormatChanged(QQuickText::TextFormat textFormat);
void elideModeChanged(QQuickText::TextElideMode mode);
void contentSizeChanged();
+ // The next two signals should be marked as Q_REVISION(12). See QTBUG-71247
+ void contentWidthChanged(qreal contentWidth);
+ void contentHeightChanged(qreal contentHeight);
+
void lineHeightChanged(qreal lineHeight);
void lineHeightModeChanged(LineHeightMode mode);
void fontSizeModeChanged();
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index b0b1492d57..fd26d966c8 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -174,6 +174,7 @@ public:
bool needToUpdateLayout:1;
bool formatModifiesFontSize:1;
bool polishSize:1; // Workaround for problem with polish called after updateSize (QTBUG-42636)
+ bool updateSizeRecursionGuard:1;
static const QChar elideChar;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 53c0231f87..4f14eedd39 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -46,6 +46,7 @@
#include "qquickevents_p_p.h"
#include <private/qquickdrag_p.h>
+#include <private/qquickhoverhandler_p.h>
#include <private/qquickpointerhandler_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
@@ -88,6 +89,8 @@ Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
+Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
@@ -256,14 +259,16 @@ void QQuickWindow::hideEvent(QHideEvent *)
void QQuickWindow::focusOutEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(false, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(false, ev->reason());
}
/*! \reimp */
void QQuickWindow::focusInEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(true, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(true, ev->reason());
d->updateFocusItemTransform();
}
@@ -610,6 +615,7 @@ QQmlListProperty<QObject> QQuickWindowPrivate::data()
static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
// The touch point local position and velocity are not yet transformed.
QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers());
@@ -651,6 +657,7 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp)
bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
Q_Q(QQuickWindow);
auto device = pointerEvent->device();
@@ -767,6 +774,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids)
{
+ QQuickPointerEvent *ev = nullptr;
for (int i = 0; i < ids.count(); ++i) {
int id = ids.at(i);
if (Q_UNLIKELY(id < 0)) {
@@ -786,15 +794,26 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &
qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null";
}
+ // optimization to avoid the loop over devices below:
+ // all ids are probably from the same event, so we don't have to search
+ if (ev) {
+ auto point = ev->pointById(id);
+ if (point && point->exclusiveGrabber() != grabber) {
+ point->setExclusiveGrabber(grabber);
+ continue; // next id in the ids loop
+ }
+ }
+ // search all devices for a QQuickPointerEvent instance that is delivering the point with id
const auto touchDevices = QQuickPointerDevice::touchDevices();
for (auto device : touchDevices) {
- auto point = pointerEventInstance(device)->pointById(id);
- if (!point)
- continue;
- QObject *oldGrabber = point->exclusiveGrabber();
- if (oldGrabber == grabber)
- continue;
- point->setExclusiveGrabber(grabber);
+ QQuickPointerEvent *pev = pointerEventInstance(device);
+ auto point = pev->pointById(id);
+ if (point) {
+ ev = pev;
+ if (point->exclusiveGrabber() != grabber)
+ point->setExclusiveGrabber(grabber);
+ break; // out of touchDevices loop
+ }
}
}
}
@@ -1315,7 +1334,9 @@ QQuickWindow::~QQuickWindow()
#if QT_CONFIG(draganddrop)
delete d->dragGrabber; d->dragGrabber = nullptr;
#endif
- delete d->contentItem; d->contentItem = nullptr;
+ QQuickRootItem *root = d->contentItem;
+ d->contentItem = nullptr;
+ delete root;
qDeleteAll(d->pointerEventInstances);
d->pointerEventInstances.clear();
@@ -1540,8 +1561,20 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
- for (QQuickItem* item : qAsConst(hoverItems))
+ for (QQuickItem* item : qAsConst(hoverItems)) {
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (itemPrivate->hasPointerHandlers()) {
+ pos = q->mapFromGlobal(QCursor::pos());
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, pos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+ }
hoverItems.clear();
return accepted;
}
@@ -1571,6 +1604,8 @@ bool QQuickWindow::event(QEvent *e)
return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
break;
case QEvent::Enter: {
+ if (!d->contentItem)
+ return false;
QEnterEvent *enter = static_cast<QEnterEvent*>(e);
bool accepted = enter->isAccepted();
bool delivered = d->deliverHoverEvent(d->contentItem, enter->windowPos(), d->lastMousePosition,
@@ -1592,7 +1627,8 @@ bool QQuickWindow::event(QEvent *e)
break;
#endif
case QEvent::WindowDeactivate:
- contentItem()->windowDeactivateEvent();
+ if (d->contentItem)
+ d->contentItem->windowDeactivateEvent();
break;
case QEvent::Close: {
// TOOD Qt 6 (binary incompatible)
@@ -1617,7 +1653,7 @@ bool QQuickWindow::event(QEvent *e)
}
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
- d->deliverNativeGestureEvent(d->contentItem, static_cast<QNativeGestureEvent*>(e));
+ d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(e));
break;
#endif
case QEvent::ShortcutOverride:
@@ -1684,6 +1720,7 @@ void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuic
{
const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
+ hasFiltered.clear();
for (auto handler : passiveGrabbers) {
// a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
@@ -1723,6 +1760,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
if (grabberItem) {
bool handled = false;
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, grabberItem))
handled = true;
// if the grabber is an Item:
@@ -1747,8 +1785,10 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// release event: ungrab if no buttons are pressed anymore
if (mouseIsReleased)
removeGrabber(grabberItem, true, isDeliveringTouchAsMouse());
- } else if (auto handler = point->grabberPointerHandler()) {
+ deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent);
+ } else if (auto handler = point->grabberPointerHandler()) {
pointerEvent->localize(handler->parentItem());
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
handler->handlePointerEvent(pointerEvent);
if (mouseIsReleased)
@@ -1767,12 +1807,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
continue;
pointerEvent->localize(item);
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, item)) {
if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers
delivered = true;
@@ -1834,6 +1875,16 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
}
}
+ if (itemPrivate->hasPointerHandlers()) {
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, scenePos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (item->contains(p)) {
@@ -1886,43 +1937,57 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
return false;
}
-#if QT_CONFIG(wheelevent)
-bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
+// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
+// in the usual reverse-paint-order until propagation is stopped
+bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event)
{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ Q_ASSERT(event->pointCount() == 1);
+ QQuickEventPoint *point = event->point(0);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(event->posF());
- if (!item->contains(p))
- return false;
- }
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverWheelEvent(child, event))
- return true;
- }
-
- QPointF p = item->mapFromScene(event->posF());
-
- if (item->contains(p)) {
- QWheelEvent wheel(p, event->globalPosF(), event->pixelDelta(), event->angleDelta(), event->delta(),
- event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
- wheel.setTimestamp(event->timestamp());
- wheel.accept();
- QCoreApplication::sendEvent(item, &wheel);
- if (wheel.isAccepted()) {
- event->accept();
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ event->localize(item);
+ // Let Pointer Handlers have the first shot
+ itemPrivate->handlePointerEvent(event);
+ if (point->isAccepted())
return true;
+ QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint());
+#if QT_CONFIG(wheelevent)
+ // Let the Item have a chance to handle it
+ if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) {
+ QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(),
+ pse->buttons(), pse->modifiers(), pse->phase(),
+ pse->isInverted(), pse->synthSource());
+ wheel.setTimestamp(pse->timestamp());
+ wheel.accept();
+ QCoreApplication::sendEvent(item, &wheel);
+ if (wheel.isAccepted()) {
+ qCDebug(lcWheelTarget) << &wheel << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
+ }
+#endif
+#if QT_CONFIG(gestures)
+ if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) {
+ QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g,
+ pnge->value(), 0L, 0L); // TODO can't copy things I can't access
+ nge.accept();
+ QCoreApplication::sendEvent(item, &nge);
+ if (nge.isAccepted()) {
+ qCDebug(lcGestureTarget) << &nge << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
}
+#endif // gestures
}
- return false;
+ return false; // it wasn't handled
}
+#if QT_CONFIG(wheelevent)
/*! \reimp */
void QQuickWindow::wheelEvent(QWheelEvent *event)
{
@@ -1937,55 +2002,11 @@ void QQuickWindow::wheelEvent(QWheelEvent *event)
return;
event->ignore();
- d->deliverWheelEvent(d->contentItem, event);
+ d->deliverPointerEvent(d->pointerEventInstance(event));
d->lastWheelEventAccepted = event->isAccepted();
}
#endif // wheelevent
-#if QT_CONFIG(gestures)
-bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGestureEvent *event)
-{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- QPointF p = item->mapFromScene(event->windowPos());
- if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) && !item->contains(p))
- return false;
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverNativeGestureEvent(child, event))
- return true;
- }
-
- // Try the Item's pointer handlers first
- QQuickPointerEvent *pointerEvent = pointerEventInstance(event);
- pointerEvent->localize(item);
- if (itemPrivate->handlePointerEvent(pointerEvent, false)) {
- if (pointerEvent->allPointsAccepted()) {
- event->accept();
- return true;
- }
- }
-
- // If still not accepted, try direct delivery to the item
- if (item->contains(p)) {
- QNativeGestureEvent copy(event->gestureType(), event->device(), p, event->windowPos(), event->screenPos(),
- event->value(), 0L, 0L); // TODO can't copy things I can't access
- event->accept();
- item->event(&copy);
- if (copy.isAccepted()) {
- event->accept();
- return true;
- }
- }
-
- return false;
-}
-#endif // gestures
-
bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
@@ -2229,6 +2250,8 @@ QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointer
if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e))
continue;
#endif
+ if (eventType == QEvent::Wheel && !qobject_cast<QQuickPointerScrollEvent*>(e))
+ continue;
// Otherwise we assume there's only one event type per device.
// More disambiguation tests might need to be added above if that changes later.
if (e->device() == device)
@@ -2248,7 +2271,10 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic
// QWindowSystemInterface::handleMouseEvent() does not take a device parameter:
// we assume all mouse events come from one mouse (the "core pointer").
// So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice()
- ev = new QQuickPointerMouseEvent(q, device);
+ if (eventType == QEvent::Wheel)
+ ev = new QQuickPointerScrollEvent(q, device);
+ else
+ ev = new QQuickPointerMouseEvent(q, device);
break;
case QQuickPointerDevice::TouchPad:
case QQuickPointerDevice::TouchScreen:
@@ -2282,6 +2308,7 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) con
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
+ case QEvent::Wheel:
dev = QQuickPointerDevice::genericMouseDevice();
break;
case QEvent::TouchBegin:
@@ -2324,7 +2351,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
} else if (event->asPointerTouchEvent()) {
deliverTouchEvent(event->asPointerTouchEvent());
} else {
- Q_ASSERT(false);
+ deliverSinglePointEventUntilAccepted(event);
}
event->reset(nullptr);
@@ -2332,16 +2359,16 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
--pointerEventRecursionGuard;
}
-// check if item or any of its child items contain the point
+// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
// FIXME: should this be iterative instead of recursive?
// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
{
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
- QPointF itemPos = item->mapFromScene(scenePos);
+ QPointF itemPos = item->mapFromScene(point->scenePosition());
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
if (!item->contains(itemPos))
@@ -2355,11 +2382,15 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
+ targets << pointerTargets(child, point, checkMouseButtons, checkAcceptsTouch);
}
bool relevant = item->contains(itemPos);
- if (!(itemPrivate->hasPointerHandlers())) {
+ if (itemPrivate->hasPointerHandlers()) {
+ if (!relevant)
+ if (itemPrivate->anyPointerHandlerWants(point))
+ relevant = true;
+ } else {
if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
relevant = false;
if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
@@ -2440,6 +2471,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
// The grabber is not an item? It's a handler then. Let it have the event first.
QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
+ hasFiltered.clear();
if (sendFilteredPointerEvent(event, receiver))
done = true;
event->localize(receiver);
@@ -2469,7 +2501,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
QQuickEventPoint *point = event->point(i);
if (point->state() == QQuickEventPoint::Pressed)
continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false, false);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2494,12 +2526,20 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
int pointCount = event->pointCount();
QVector<QQuickItem *> targetItems;
bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
+ if (isTouchEvent && event->isPressEvent()) {
+ // When a second point is pressed, we're starting over with delivery, so
+ // don't let prior conception of which one is acting as a mouse interfere
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+ }
for (int i = 0; i < pointCount; ++i) {
auto point = event->point(i);
+ if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent())
+ point->clearPassiveGrabbers();
point->setAccepted(false); // because otherwise touchEventForItem will ignore it
if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
point->setGrabberPointerHandler(nullptr, true);
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), !isTouchEvent, isTouchEvent);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2512,6 +2552,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
qWarning("event went missing during delivery! (nested sendEvent() is not allowed)");
break;
}
+ hasFiltered.clear();
if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
if (event->isAccepted()) {
for (int i = 0; i < event->pointCount(); ++i)
@@ -2567,7 +2608,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
QPointF localPos = item->mapFromScene(point->scenePosition());
- Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
QCoreApplication::sendEvent(item, me);
@@ -2595,6 +2635,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
bool eventAccepted = false;
// If any parent filters the event, we're done.
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, item))
return;
@@ -2604,10 +2645,12 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
eventAccepted = touchEvent->isAccepted();
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
- if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
- // send mouse event
- if (deliverTouchAsMouse(item, ptEvent))
- eventAccepted = true;
+ if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ // send mouse event
+ if (deliverTouchAsMouse(item, ptEvent))
+ eventAccepted = true;
+ }
}
if (eventAccepted) {
@@ -2811,7 +2854,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
- hasFiltered.clear();
return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
@@ -2872,7 +2914,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event
pt->setGrabberItem(filteringParent);
}
return true;
- } else {
+ } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) {
const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i);
@@ -2971,6 +3013,12 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
+bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta)
+{
+ int threshold = qApp->styleHints()->startDragDistance();
+ return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
+}
+
/*!
\qmlproperty list<Object> Window::data
\default
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 817178fdac..79e8a11aa8 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -95,6 +95,7 @@ public:
};
Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
+ Q_FLAG(CreateTextureOptions)
enum SceneGraphError {
ContextNotAvailable = 1
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index b5e3a2c1eb..10c8477417 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -152,12 +152,7 @@ public:
bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
bool sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
-#if QT_CONFIG(wheelevent)
- bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
-#endif
-#if QT_CONFIG(gestures)
- bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *);
-#endif
+ bool deliverSinglePointEventUntilAccepted(QQuickPointerEvent *);
// entry point of events to the window
void handleTouchEvent(QTouchEvent *);
@@ -180,7 +175,7 @@ public:
void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
@@ -307,6 +302,7 @@ public:
QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
}
+ static bool dragOverThreshold(QVector2D delta);
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);