diff options
author | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2022-05-16 18:16:38 +0300 |
---|---|---|
committer | Tarja Sundqvist <tarja.sundqvist@qt.io> | 2022-05-16 18:16:38 +0300 |
commit | ae66ecf0f95c79d730190b92e641c0410d5d6896 (patch) | |
tree | 5e5ff0c1f08148a7a421581ba0907314aa90d6a3 | |
parent | 04ea6df18a2b06efd133a4f1b13c2e38817279ae (diff) | |
parent | 53086eaf2ffb5fc1c360cf13f3d87e8d5f2a7b6f (diff) |
49 files changed, 819 insertions, 164 deletions
diff --git a/.qmake.conf b/.qmake.conf index b09daef9c6..a72382f252 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ CONFIG += warning_clean DEFINES += QT_NO_LINKED_LIST DEFINES += QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 5.15.4 +MODULE_VERSION = 5.15.5 diff --git a/src/3rdparty/masm/wtf/PageBlock.cpp b/src/3rdparty/masm/wtf/PageBlock.cpp index e715ed262a..bc0e8d6f2d 100644 --- a/src/3rdparty/masm/wtf/PageBlock.cpp +++ b/src/3rdparty/masm/wtf/PageBlock.cpp @@ -64,6 +64,7 @@ inline size_t systemPageSize() #endif +inline namespace hidden { size_t pageSize() { if (!s_pageSize) @@ -78,5 +79,6 @@ size_t pageMask() s_pageMask = ~(pageSize() - 1); return s_pageMask; } +} } // namespace WTF diff --git a/src/3rdparty/masm/wtf/PageBlock.h b/src/3rdparty/masm/wtf/PageBlock.h index 09e4048239..d85c39cb33 100644 --- a/src/3rdparty/masm/wtf/PageBlock.h +++ b/src/3rdparty/masm/wtf/PageBlock.h @@ -28,8 +28,13 @@ namespace WTF { +// avoid false positive detection by apple +// by putting the function inside an inline namespace +// to obtain different name mangling +inline namespace hidden { WTF_EXPORT_PRIVATE size_t pageSize(); WTF_EXPORT_PRIVATE size_t pageMask(); +} inline bool isPageAligned(void* address) { return !(reinterpret_cast<intptr_t>(address) & (pageSize() - 1)); } inline bool isPageAligned(size_t size) { return !(size & (pageSize() - 1)); } inline bool isPowerOfTwo(size_t size) { return !(size & (size - 1)); } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 506ecb64bb..b50490e831 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -216,7 +216,8 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) return false; QV4::ScopedObject scopeObject(scope, engine()->newObject()); - if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) { + if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext || + ctxt->d()->type == QV4::Heap::ExecutionContext::Type_BlockContext) { QStringList names; Refs collectedRefs; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index 71645579c5..499f060c9c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -163,7 +163,7 @@ int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, boo for (QV4Debugger *debugger : qAsConst(m_debuggers)) debugger->addBreakPoint(fileName, lineNumber, condition); - int id = m_breakPoints.size(); + const int id = ++m_lastBreakPointId; m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition)); return id; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h index 39ac4d4dcb..43baec32d7 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -93,6 +93,7 @@ private: }; QHash<int, BreakPoint> m_breakPoints; + int m_lastBreakPointId = 0; bool m_breakOnThrow; QV4DebugServiceImpl *m_debugService; }; diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 89f99e21cd..2ad85ab910 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -448,7 +448,9 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen errors << m; } - return false; + + if (!errors.isEmpty() || !parseResult) + return false; } program = parser.ast(); Q_ASSERT(program); diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index ece2fd5fd7..ffe0ec9737 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -69,6 +69,21 @@ exposed to QML but the type itself should not be instantiable. For a quick guide to choosing the correct approach to expose C++ types to QML, see \l {Choosing the Correct Integration Method Between C++ and QML}. +\section2 Preconditions + +All the macros mentioned below are available from the \c qqml.h +header. You need to add the following code to the files using them in order to +make the macros available: + +\code +#include <QtQml/qqml.h> +\endcode + +Furthermore, your class declarations have to live in headers reachable via your +project's include path. The declarations are used to generate registration code +at compile time, and the registration code needs to include the headers that +contain the declarations. + \section2 Registering an Instantiable Object Type \b{Any QObject-derived C++ class can be registered as the definition of a diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 4e531ceb61..6f5a0c4196 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -159,11 +159,11 @@ \relates QQmlEngine Declares the enclosing type to be a singleton in QML. This only takes effect - if the type is available in QML, by having a \l QML_ELEMENT or - \l QML_NAMED_ELEMENT() macro. By default, each QQmlEngine will try to create a - singleton instance using the type's default constructor when the type is first - accessed. If there is no default constructor the singleton is initially - inaccessible. This behavior can be overridden by calling + if the type is a \l Q_OBJECT and is available in QML (by having a + \l QML_ELEMENT or \l QML_NAMED_ELEMENT() macro). By default, each QQmlEngine + will try to create a singleton instance using the type's default constructor + when the type is first accessed. If there is no default constructor the + singleton is initially inaccessible. This behavior can be overridden by calling \l qmlRegisterSingletonType() with a specific factory function or \l qmlRegisterSingletonInstance() with a specific instance for the same class and the same type namespace and version. diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc index 075b3a7646..0e06cbec8a 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc @@ -133,7 +133,7 @@ Rectangle { Connections { target: button - function onClicked(): { + function onClicked() { rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1); } } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 9ee4fdbe41..c785f8ef51 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -375,10 +375,15 @@ bool QQmlComponentPrivate::setInitialProperty(QObject *component, const QString& { QQmlProperty prop = QQmlComponentPrivate::removePropertyFromRequired(component, name, requiredProperties()); QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop); - if (!prop.isValid() || !privProp->writeValueProperty(value, {})) { + const bool isValid = prop.isValid(); + if (!isValid || !privProp->writeValueProperty(value, {})) { QQmlError error{}; error.setUrl(url); - error.setDescription(QLatin1String("Could not set property %1").arg(name)); + if (isValid) + error.setDescription(QLatin1String("Could not set initial property %1").arg(name)); + else + error.setDescription(QLatin1String("Setting initial properties failed: %2 does not have a property called %1").arg(name, + QQmlMetaType::prettyTypeName(component))); state.errors.push_back(error); return false; } else @@ -830,6 +835,14 @@ QObject *QQmlComponent::create(QQmlContext *context) properties with \a initialProperties. \a context specifies the context where the object instance is to be created. + \omit + TODO: also mention errorString() when QTBUG-93239 is fixed + \endomit + + If any of the \c initialProperties cannot be set, \l isError() will return + \c true, and the \l errors() function can be used to + get detailed information about the error(s). + \sa QQmlComponent::create \since 5.14 */ diff --git a/src/qmldebug/qqmlprofilerevent_p.h b/src/qmldebug/qqmlprofilerevent_p.h index a7e37d1964..01b2f58f16 100644 --- a/src/qmldebug/qqmlprofilerevent_p.h +++ b/src/qmldebug/qqmlprofilerevent_p.h @@ -48,6 +48,7 @@ #include <QtCore/qmetatype.h> #include <initializer_list> +#include <limits> #include <type_traits> // diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 26ded63c41..312a621029 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -1343,6 +1343,8 @@ QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incu QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) { Q_D(QQmlDelegateModel); + if (d->m_compositor.count(d->m_compositorGroup) <= index) + return QQmlIncubator::Null; Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); if (!it->inCache()) return QQmlIncubator::Null; diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index adb753e000..03f8d8918c 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -503,8 +503,11 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const return false; if (QQuickItem *par = parentItem()) { if (par->window()) { + QRect windowGeometry = par->window()->geometry(); + if (!par->window()->isTopLevel()) + windowGeometry = QRect(QWindowPrivate::get(par->window())->globalPosition(), par->window()->size()); QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint()); - if (!par->window()->geometry().contains(screenPosition)) + if (!windowGeometry.contains(screenPosition)) return false; } QPointF p = par->mapFromScene(point->scenePosition()); diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 950afaed52..7e673b3fe4 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -813,7 +813,7 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event \readonly \qmlproperty int QtQuick::EventPoint::pointId - This property holds the ID of the event, if any. + This property holds the ID of the point, if any. Touchpoints have automatically-incrementing IDs: each time the user presses a finger against the touchscreen, it will be a larger number. @@ -826,12 +826,8 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(const QTabletEvent *event \readonly \qmlproperty bool QtQuick::EventPoint::accepted - Setting \a accepted to true prevents the event from being propagated to - Items below the PointerHandler's Item. - - Generally, if the handler acts on the mouse event, then it should be - accepted so that items lower in the stacking order do not also respond to - the same event. + Indicates whether this point has been accepted during delivery thus far. + This flag cannot be usefully set from QML. */ /*! diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 497672b497..3df899d63d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2095,7 +2095,7 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) /*! \class QQuickItem::ItemChangeData \inmodule QtQuick - \brief Adds supplimentary information to the QQuickItem::itemChange() + \brief Adds supplementary information to the QQuickItem::itemChange() function. The meaning of each member of this class is defined by the change type. @@ -2125,25 +2125,31 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) /*! \variable QQuickItem::ItemChangeData::realValue - Contains supplimentary information to the QQuickItem::itemChange() function. + The numeric value that has changed: \l {QQuickItem::opacity()}{opacity}, + \l {QQuickItem::rotation()}{rotation} or + \l {QScreen::devicePixelRatio}{device pixel ratio}. \sa QQuickItem::ItemChange */ /*! \variable QQuickItem::ItemChangeData::boolValue - Contains supplimentary information to the QQuickItem::itemChange() function. + The boolean value that has changed: \l {QQuickItem::visible()}{visible}, + \l {QQuickItem::enabled()}{enabled}, \l {QQuickItem::activeFocus()}{activeFocus} + or \l {QQuickItem::antialiasing()}{antialiasing}. \sa QQuickItem::ItemChange */ /*! \variable QQuickItem::ItemChangeData::item - Contains supplimentary information to the QQuickItem::itemChange() function. + The item that has been added or removed as a \l{QQuickItem::childItems()}{child}, + or the new \l{QQuickItem::parentItem()}{parent}. \sa QQuickItem::ItemChange */ /*! \variable QQuickItem::ItemChangeData::window - Contains supplimentary information to the QQuickItem::itemChange() function. + The \l{QQuickWindow}{window} in which the item has been shown, or \c nullptr + if the item has been removed from a window. \sa QQuickItem::ItemChange */ diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index dfb56ccc00..63359a7683 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -529,8 +529,8 @@ QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &act data->interpolatorType = QMetaType::QReal; data->interpolator = d->interpolator; data->reverse = direction == Backward ? true : false; - data->fromSourced = false; - data->fromDefined = false; + data->fromIsSourced = false; + data->fromIsDefined = false; for (int ii = 0; ii < actions.count(); ++ii) { QQuickStateAction &action = actions[ii]; @@ -543,7 +543,7 @@ QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &act QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator; if (data->actions.count()) { animator->setAnimValue(data); - animator->setFromSourcedValue(&data->fromSourced); + animator->setFromIsSourcedValue(&data->fromIsSourced); } else { delete data; } @@ -864,9 +864,9 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0); data->endRotation = d->endRotation; data->reverse = direction == Backward ? true : false; - data->fromSourced = false; - data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false; - data->toDefined = d->path ? true : false; + data->fromIsSourced = false; + data->fromIsDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false; + data->toIsDefined = d->path ? true : false; int origModifiedSize = modified.count(); for (int i = 0; i < actions.count(); ++i) { @@ -885,8 +885,7 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio } } - if (target && d->path && - (modified.count() > origModifiedSize || data->toDefined)) { + if (target && d->path && (modified.count() > origModifiedSize || data->toIsDefined)) { data->target = target; data->path = d->path; data->path->invalidateSequentialHistory(); @@ -897,13 +896,13 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio // treat interruptions specially, otherwise we end up with strange paths if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) { - if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) { + if (!data->fromIsDefined && !data->toIsDefined && !prevData.painterPath.isEmpty()) { QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV); if (!prevData.anchorPoint.isNull()) pathPos -= prevData.anchorPoint; if (pathPos == data->target->position()) { //only treat as interruption if we interrupted ourself data->painterPath = prevData.painterPath; - data->toDefined = data->fromDefined = data->fromSourced = true; + data->toIsDefined = data->fromIsDefined = data->fromIsSourced = true; data->prevBez.isValid = false; data->interruptStart = prevData.currentV; data->startRotation = prevData.startRotation; @@ -913,13 +912,13 @@ QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actio } } } - pa->setFromSourcedValue(&data->fromSourced); + pa->setFromIsSourcedValue(&data->fromIsSourced); pa->setAnimValue(data); pa->setDuration(d->duration); pa->setEasingCurve(d->easingCurve); return initInstance(pa); } else { - pa->setFromSourcedValue(nullptr); + pa->setFromIsSourcedValue(nullptr); pa->setAnimValue(nullptr); delete pa; delete data; @@ -939,7 +938,7 @@ void QQuickPathAnimationUpdater::setValue(qreal v) } currentV = v; bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0)); - if (!fromSourced && (!fromDefined || !toDefined)) { + if (!fromIsSourced && (!fromIsDefined || !toIsDefined)) { qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x(); qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y(); qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x(); @@ -947,7 +946,7 @@ void QQuickPathAnimationUpdater::setValue(qreal v) prevBez.isValid = false; painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints); - fromSourced = true; + fromIsSourced = true; } qreal angle; diff --git a/src/quick/items/qquickitemanimation_p_p.h b/src/quick/items/qquickitemanimation_p_p.h index 83b9899197..351a0cb2bc 100644 --- a/src/quick/items/qquickitemanimation_p_p.h +++ b/src/quick/items/qquickitemanimation_p_p.h @@ -92,7 +92,7 @@ class QQuickPathAnimationUpdater : public QQuickBulkValueUpdater { public: QQuickPathAnimationUpdater() : path(nullptr), pathLength(0), target(nullptr), reverse(false), - fromSourced(false), fromDefined(false), toDefined(false), + fromIsSourced(false), fromIsDefined(false), toIsDefined(false), toX(0), toY(0), currentV(0), orientation(QQuickPathAnimation::Fixed), entryInterval(0), exitInterval(0) {} ~QQuickPathAnimationUpdater() {} @@ -108,9 +108,9 @@ public: QQuickItem *target; bool reverse; - bool fromSourced; - bool fromDefined; - bool toDefined; + bool fromIsSourced; + bool fromIsDefined; + bool toIsDefined; qreal toX; qreal toY; qreal currentV; diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 2b4ca9e256..010a0152e1 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2346,7 +2346,9 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc inRequest = true; - QObject* object = model->object(modelIndex, incubationMode); + // The model will run this same range check internally but produce a warning and return nullptr. + // Since we handle this result graciously in our code, we preempt this warning by checking the range ourselves. + QObject* object = modelIndex < model->count() ? model->object(modelIndex, incubationMode) : nullptr; QQuickItem *item = qmlobject_cast<QQuickItem*>(object); if (!item) { diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 0692a1da42..1a07eb5923 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -556,7 +556,10 @@ void QQuickPinchArea::updatePinch() d->inPinch = true; d->stealMouse = true; if (d->pinch && d->pinch->target()) { - d->pinchStartPos = pinch()->target()->position(); + auto targetParent = pinch()->target()->parentItem(); + d->pinchStartPos = targetParent ? + targetParent->mapToScene(pinch()->target()->position()) : + pinch()->target()->position(); d->pinchStartScale = d->pinch->target()->scale(); d->pinchStartRotation = d->pinch->target()->rotation(); d->pinch->setActive(true); @@ -604,6 +607,9 @@ void QQuickPinchArea::updatePinchTarget() s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); pinch()->target()->setScale(s); QPointF pos = d->sceneLastCenter - d->sceneStartCenter + d->pinchStartPos; + if (auto targetParent = pinch()->target()->parentItem()) + pos = targetParent->mapFromScene(pos); + if (pinch()->axis() & QQuickPinch::XAxis) { qreal x = pos.x(); if (x < pinch()->xmin()) diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index f9454fc5cb..5feec60874 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -958,43 +958,61 @@ QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilit return RebuildOption::None; } - // Go through all columns from first to last, find the columns that used - // to be hidden and not loaded, and check if they should become visible - // (and vice versa). If there is a change, we need to rebuild. RebuildOptions rebuildOptions = RebuildOption::None; - for (int column = leftColumn(); column <= rightColumn(); ++column) { - const bool wasVisibleFromBefore = loadedColumns.contains(column); - const bool isVisibleNow = !qFuzzyIsNull(getColumnWidth(column)); - if (wasVisibleFromBefore == isVisibleNow) - continue; - - // A column changed visibility. This means that it should - // either be loaded or unloaded. So we need a rebuild. - qCDebug(lcTableViewDelegateLifecycle) << "Column" << column << "changed visibility to" << isVisibleNow; + if (loadedTableOuterRect.x() == origin.x() && leftColumn() != 0) { + // Since the left column is at the origin of the viewport, but still not the first + // column in the model, we need to calculate a new left column since there might be + // columns in front of it that used to be hidden, but should now be visible (QTBUG-93264). rebuildOptions.setFlag(RebuildOption::ViewportOnly); - if (column == leftColumn()) { - // The first loaded column should now be hidden. This means that we - // need to calculate which column should now be first instead. - rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn); + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn); + } else { + // Go through all loaded columns from first to last, find the columns that used + // to be hidden and not loaded, and check if they should become visible + // (and vice versa). If there is a change, we need to rebuild. + for (int column = leftColumn(); column <= rightColumn(); ++column) { + const bool wasVisibleFromBefore = loadedColumns.contains(column); + const bool isVisibleNow = !qFuzzyIsNull(getColumnWidth(column)); + if (wasVisibleFromBefore == isVisibleNow) + continue; + + // A column changed visibility. This means that it should + // either be loaded or unloaded. So we need a rebuild. + qCDebug(lcTableViewDelegateLifecycle) << "Column" << column << "changed visibility to" << isVisibleNow; + rebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (column == leftColumn()) { + // The first loaded column should now be hidden. This means that we + // need to calculate which column should now be first instead. + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn); + } + break; } - break; } - // Go through all rows from first to last, and do the same as above - for (int row = topRow(); row <= bottomRow(); ++row) { - const bool wasVisibleFromBefore = loadedRows.contains(row); - const bool isVisibleNow = !qFuzzyIsNull(getRowHeight(row)); - if (wasVisibleFromBefore == isVisibleNow) - continue; - - // A row changed visibility. This means that it should - // either be loaded or unloaded. So we need a rebuild. - qCDebug(lcTableViewDelegateLifecycle) << "Row" << row << "changed visibility to" << isVisibleNow; + if (loadedTableOuterRect.y() == origin.y() && topRow() != 0) { + // Since the top row is at the origin of the viewport, but still not the first + // row in the model, we need to calculate a new top row since there might be + // rows in front of it that used to be hidden, but should now be visible (QTBUG-93264). rebuildOptions.setFlag(RebuildOption::ViewportOnly); - if (row == topRow()) - rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow); - break; + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow); + } else { + // Go through all loaded rows from first to last, find the rows that used + // to be hidden and not loaded, and check if they should become visible + // (and vice versa). If there is a change, we need to rebuild. + for (int row = topRow(); row <= bottomRow(); ++row) { + const bool wasVisibleFromBefore = loadedRows.contains(row); + const bool isVisibleNow = !qFuzzyIsNull(getRowHeight(row)); + if (wasVisibleFromBefore == isVisibleNow) + continue; + + // A row changed visibility. This means that it should + // either be loaded or unloaded. So we need a rebuild. + qCDebug(lcTableViewDelegateLifecycle) << "Row" << row << "changed visibility to" << isVisibleNow; + rebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (row == topRow()) + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow); + break; + } } return rebuildOptions; diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 5df5e5adcf..1d9e3761a2 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2359,6 +2359,14 @@ void QQuickTextEdit::q_textChanged() d->determineHorizontalAlignment(); d->updateDefaultTextOption(); updateSize(); + + markDirtyNodesForRange(0, d->document->characterCount(), 0); + polish(); + if (isComponentComplete()) { + d->updateType = QQuickTextEditPrivate::UpdatePaintNode; + update(); + } + emit textChanged(); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h index b80bacbaa0..af912e1a3f 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -54,6 +54,8 @@ #include <private/qsgadaptationlayer_p.h> #include <private/qsgtexturematerial_p.h> +#include <QtCore/QPointer> + QT_BEGIN_NAMESPACE namespace QSGSoftwareHelpers { @@ -132,7 +134,7 @@ private: QRectF m_innerSourceRect; QRectF m_subSourceRect; - QSGTexture *m_texture; + QPointer<QSGTexture> m_texture; QPixmap m_cachedMirroredPixmap; bool m_mirror; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 31671c8639..a0a97c779a 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -1878,7 +1878,7 @@ bool Renderer::checkOverlap(int first, int last, const Rect &bounds) { for (int i=first; i<=last; ++i) { Element *e = m_alphaRenderList.at(i); - if (!e || e->batch) + if (!e) continue; Q_ASSERT(e->boundsComputed); if (e->bounds.intersects(bounds)) @@ -1949,8 +1949,10 @@ void Renderer::prepareAlphaBatches() continue; if (ej->root != ei->root || ej->isRenderNode) break; - if (ej->batch) + if (ej->batch) { + overlapBounds |= ej->bounds; continue; + } QSGGeometryNode *gnj = ej->node; if (gnj->geometry()->vertexCount() == 0) diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp index e5e25d141b..4cf019c7f0 100644 --- a/src/quick/util/qquickanimation.cpp +++ b/src/quick/util/qquickanimation.cpp @@ -1996,7 +1996,7 @@ void QQuickPropertyAnimationPrivate::convertVariant(QVariant &variant, int type) } QQuickBulkValueAnimator::QQuickBulkValueAnimator() - : QAbstractAnimationJob(), animValue(nullptr), fromSourced(nullptr), m_duration(250) + : QAbstractAnimationJob(), animValue(nullptr), fromIsSourced(nullptr), m_duration(250) { } @@ -2026,8 +2026,8 @@ void QQuickBulkValueAnimator::updateCurrentTime(int currentTime) void QQuickBulkValueAnimator::topLevelAnimationLoopChanged() { //check for new from every top-level loop (when the top level animation is started and all subsequent loops) - if (fromSourced) - *fromSourced = false; + if (fromIsSourced) + *fromIsSourced = false; QAbstractAnimationJob::topLevelAnimationLoopChanged(); } @@ -2596,7 +2596,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v) if (v == 1.) { QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); } else { - if (!fromSourced && !fromDefined) { + if (!fromIsSourced && !fromIsDefined) { action.fromValue = action.property.read(); if (interpolatorType) { QQuickPropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); @@ -2616,7 +2616,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v) return; } wasDeleted = nullptr; - fromSourced = true; + fromIsSourced = true; } void QQuickAnimationPropertyUpdater::debugUpdater(QDebug d, int indentLevel) const @@ -2760,11 +2760,11 @@ QAbstractAnimationJob* QQuickPropertyAnimation::transition(QQuickStateActions &a data->interpolatorType = d->interpolatorType; data->interpolator = d->interpolator; data->reverse = direction == Backward ? true : false; - data->fromSourced = false; - data->fromDefined = d->fromIsDefined; + data->fromIsSourced = false; + data->fromIsDefined = d->fromIsDefined; data->actions = dataActions; animator->setAnimValue(data); - animator->setFromSourcedValue(&data->fromSourced); + animator->setFromIsSourcedValue(&data->fromIsSourced); d->actions = &data->actions; //remove this? } diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h index 8d23242b68..f785bf6623 100644 --- a/src/quick/util/qquickanimation_p_p.h +++ b/src/quick/util/qquickanimation_p_p.h @@ -135,7 +135,7 @@ public: void setAnimValue(QQuickBulkValueUpdater *value); QQuickBulkValueUpdater *getAnimValue() const { return animValue; } - void setFromSourcedValue(bool *value) { fromSourced = value; } + void setFromIsSourcedValue(bool *value) { fromIsSourced = value; } int duration() const override { return m_duration; } void setDuration(int msecs) { m_duration = msecs; } @@ -150,7 +150,7 @@ protected: private: QQuickBulkValueUpdater *animValue; - bool *fromSourced; + bool *fromIsSourced; int m_duration; QEasingCurve easing; }; @@ -311,7 +311,7 @@ public: class Q_AUTOTEST_EXPORT QQuickAnimationPropertyUpdater : public QQuickBulkValueUpdater { public: - QQuickAnimationPropertyUpdater() : interpolatorType(0), interpolator(nullptr), prevInterpolatorType(0), reverse(false), fromSourced(false), fromDefined(false), wasDeleted(nullptr) {} + QQuickAnimationPropertyUpdater() : interpolatorType(0), interpolator(nullptr), prevInterpolatorType(0), reverse(false), fromIsSourced(false), fromIsDefined(false), wasDeleted(nullptr) {} ~QQuickAnimationPropertyUpdater() override; void setValue(qreal v) override; @@ -323,8 +323,8 @@ public: QVariantAnimation::Interpolator interpolator; int prevInterpolatorType; //for generic bool reverse; - bool fromSourced; - bool fromDefined; + bool fromIsSourced; + bool fromIsDefined; bool *wasDeleted; }; diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp index d1ff78f8bc..e2a9c33adf 100644 --- a/src/quick/util/qquickanimator.cpp +++ b/src/quick/util/qquickanimator.cpp @@ -176,7 +176,7 @@ void QQuickAnimator::setTo(qreal to) Q_D(QQuickAnimator); if (to == d->to) return; - d->isToDefined = true; + d->toIsDefined = true; d->to = to; Q_EMIT toChanged(d->to); } @@ -204,7 +204,7 @@ void QQuickAnimator::setFrom(qreal from) Q_D(QQuickAnimator); if (from == d->from) return; - d->isFromDefined = true; + d->fromIsDefined = true; d->from = from; Q_EMIT fromChanged(d->from); } @@ -231,14 +231,14 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, job->setTarget(qobject_cast<QQuickItem *>(action.property.object())); - if (isFromDefined) + if (fromIsDefined) job->setFrom(from); else if (action.fromValue.isValid()) job->setFrom(action.fromValue.toReal()); else job->setFrom(action.property.read().toReal()); - if (isToDefined) + if (toIsDefined) job->setTo(to); else if (action.toValue.isValid()) job->setTo(action.toValue.toReal()); diff --git a/src/quick/util/qquickanimator_p_p.h b/src/quick/util/qquickanimator_p_p.h index b176119c70..33e202c522 100644 --- a/src/quick/util/qquickanimator_p_p.h +++ b/src/quick/util/qquickanimator_p_p.h @@ -68,8 +68,8 @@ public: , duration(250) , from(0) , to(0) - , isFromDefined(false) - , isToDefined(false) + , fromIsDefined(false) + , toIsDefined(false) { } @@ -79,8 +79,8 @@ public: qreal from; qreal to; - uint isFromDefined : 1; - uint isToDefined : 1; + uint fromIsDefined : 1; + uint toIsDefined : 1; void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified, QObject *defaultTarget); }; diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 74ee52b1d3..c39e1f5b8b 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -107,12 +107,13 @@ QT_BEGIN_NAMESPACE \li PathPolyline \li Yes \li Yes + \li No \li Yes - \li Yes - \li PathMultiLine - \li Yes + \row + \li PathMultiLine \li Yes \li Yes + \li No \li Yes \row \li PathQuad diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index 660852ba83..d531fc9205 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -46,6 +46,8 @@ #include "qquickstyledtext_p.h" #include <QQmlContext> +Q_LOGGING_CATEGORY(lcStyledText, "qt.quick.styledtext") + /* QQuickStyledText supports few tags: @@ -566,6 +568,8 @@ void QQuickStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textI textOut += QChar(34); else if (entity == QLatin1String("nbsp")) textOut += QChar(QChar::Nbsp); + else + qCWarning(lcStyledText) << "StyledText doesn't support entity" << entity; return; } else if (*ch == QLatin1Char(' ')) { QStringRef entity(&textIn, entityStart - 1, entityLength + 1); diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml new file mode 100644 index 0000000000..c3e7687831 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml @@ -0,0 +1,15 @@ +import QtQml 2.15 +Timer { + Component.onCompleted: { + console.log('0') + console.log('1') + console.log('2') + console.log('3') + console.log('4') + console.log('5') + running = true + } + + interval: 0 + onTriggered: Qt.quit() +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml new file mode 100644 index 0000000000..1715992490 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml @@ -0,0 +1,16 @@ +import QtQml 2.15 + +Timer { + Component.onCompleted: { + var a = 97 + var b = 98 + var c = 99 + let d = 100 + const e = 101 + console.log("onClicked") // Set breakpoint + running = true + } + + interval: 0 + onTriggered: Qt.quit() +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 91470e0651..43c81ee515 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -65,6 +65,8 @@ const char *STEPACTION_QMLFILE = "stepAction.qml"; const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; +const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml"; +const char *LETCONSTLOCALS_QMLFILE = "letConstLocals.qml"; #undef QVERIFY #define QVERIFY(statement) \ @@ -156,6 +158,9 @@ private slots: void encodeQmlScope(); void breakOnAnchor(); + void breakPointIds(); + void letConstLocals(); + private: ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true, bool restrictServices = false); @@ -165,10 +170,11 @@ private: void targetData(); bool waitForClientSignal(const char *signal, int timeout = 30000); void checkVersionParameters(); + int setBreakPoint(const QString &file, int sourceLine, bool enabled); + void clearBreakPoint(int id); }; - void tst_QQmlDebugJS::initTestCase() { QQmlDebugTest::initTestCase(); @@ -566,7 +572,8 @@ void tst_QQmlDebugJS::changeBreakpoint() int sourceLine2 = 37; int sourceLine1 = 38; - QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE); + QCOMPARE(init(qmlscene, file), ConnectSuccess); bool isStopped = false; QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); @@ -589,27 +596,13 @@ void tst_QQmlDebugJS::changeBreakpoint() return breakpointsHit[0].toInt(); }; - auto setBreakPoint = [&](int sourceLine, bool enabled) { - int id = -1; - auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { - id = extractBody().value("breakpoint").toInt(); - }); - - m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine, -1, enabled); - bool success = QTest::qWaitFor([&]() { return id >= 0; }); - Q_UNUSED(success); - - QObject::disconnect(connection); - return id; - }; - //The breakpoints are in a timer loop so we can set them after connect(). //Furthermore the breakpoints should be hit in the right order because setting of breakpoints //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) - const int breakpoint1 = setBreakPoint(sourceLine1, false); + const int breakpoint1 = setBreakPoint(file, sourceLine1, false); QVERIFY(breakpoint1 >= 0); - const int breakpoint2 = setBreakPoint(sourceLine2, true); + const int breakpoint2 = setBreakPoint(file, sourceLine2, true); QVERIFY(breakpoint2 >= 0); auto verifyBreakpoint = [&](int sourceLine, int breakpointId) { @@ -1026,6 +1019,96 @@ void tst_QQmlDebugJS::breakOnAnchor() QCOMPARE(breaks, 2); } +void tst_QQmlDebugJS::breakPointIds() +{ + QString file(BREAKPOINTIDS_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int breaks = 0; + int breakPointIds[] = { -1, -1, -1, -1, -1, -1}; + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + const QJsonObject body = m_client->response().body.toObject(); + QCOMPARE(body.value("sourceLine").toInt(), breaks + 4); + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + QVERIFY(breakpointsHit.size() > 0); + QCOMPARE(breakpointsHit[0].toInt(), breakPointIds[breaks]); + ++breaks; + m_client->continueDebugging(QV4DebugClient::Continue); + }); + + for (int i = 0; i < 6; ++i) + breakPointIds[i] = setBreakPoint(file, i + 4, true); + + clearBreakPoint(breakPointIds[2]); + breakPointIds[2] = setBreakPoint(file, 6, true); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); + + QCOMPARE(breaks, 6); +} + +void tst_QQmlDebugJS::letConstLocals() +{ + QString file(LETCONSTLOCALS_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + m_client->frame(); + }); + + int numScopes = 0; + QString expectedMembers = QStringLiteral("abcde"); + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + const auto value = m_client->response(); + if (value.command == QStringLiteral("frame")) { + const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray(); + for (const auto &scope : scopes) { + const auto scopeObject = scope.toObject(); + const int type = scopeObject.value("type").toInt(); + if (type == 1 || type == 4) { + m_client->scope(scopeObject.value("index").toInt()); + ++numScopes; + } + } + QVERIFY(numScopes > 0); + } else if (value.command == QStringLiteral("scope")) { + const auto props = value.body.toObject().value(QStringLiteral("object")).toObject() + .value(QStringLiteral("properties")).toArray(); + for (const auto &prop : props) { + const auto propObj = prop.toObject(); + const QString name = propObj.value(QStringLiteral("name")).toString(); + if (name == QStringLiteral("onCompleted")) + continue; + QVERIFY(name.length() == 1); + auto i = expectedMembers.indexOf(name.at(0)); + QVERIFY(i != -1); + expectedMembers.remove(i, 1); + QCOMPARE(propObj.value(QStringLiteral("type")).toString(), + QStringLiteral("number")); + QCOMPARE(propObj.value(QStringLiteral("value")).toInt(), + int(name.at(0).toLatin1())); + } + if (--numScopes == 0) { + QVERIFY(expectedMembers.isEmpty()); + m_client->continueDebugging(QV4DebugClient::Continue); + } + } + }); + + setBreakPoint(file, 10, true); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + QList<QQmlDebugClient *> tst_QQmlDebugJS::createClients() { m_client = new QV4DebugClient(m_connection); @@ -1054,6 +1137,35 @@ void tst_QQmlDebugJS::checkVersionParameters() QCOMPARE(body.value("ChangeBreakpoint").toBool(), true); } +int tst_QQmlDebugJS::setBreakPoint(const QString &file, int sourceLine, bool enabled) +{ + int id = -1; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + id = m_client->response().body.toObject().value("breakpoint").toInt(); + }); + + m_client->setBreakpoint(file, sourceLine, -1, enabled); + bool success = QTest::qWaitFor([&]() { return id >= 0; }); + Q_UNUSED(success); + + QObject::disconnect(connection); + return id; +} + +void tst_QQmlDebugJS::clearBreakPoint(int id) +{ + bool ok = false; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + ok = true; + }); + + m_client->clearBreakpoint(id); + bool success = QTest::qWaitFor([&]() { return ok; }); + Q_UNUSED(success); + + QObject::disconnect(connection); +} + QTEST_MAIN(tst_QQmlDebugJS) #include "tst_qqmldebugjs.moc" diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index e3cbeb9891..e4e7728508 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -575,21 +575,29 @@ void tst_qv4debugger::readLocals() QString script = "var f = function(a, b) {\n" " var c = a + b\n" + " let e = 'jaja'\n" + " const ff = 'nenene'\n" " var d = a - b\n" // breakpoint, c should be set, d should be undefined " return c === d\n" "}\n" "f(1, 2, 3);\n"; - debugger()->addBreakPoint("readLocals", 3); + debugger()->addBreakPoint("readLocals", 5); evaluateJavaScript(script, "readLocals"); QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 5); // locals and parameters + QCOMPARE(frame0.size(), 7); // locals and parameters QVERIFY(frame0.contains("c")); QCOMPARE(frame0.type("c"), QStringLiteral("number")); QCOMPARE(frame0.value("c").toDouble(), 3.0); QVERIFY(frame0.contains("d")); QCOMPARE(frame0.type("d"), QStringLiteral("undefined")); + QVERIFY(frame0.contains("e")); + QCOMPARE(frame0.type("e"), QStringLiteral("string")); + QCOMPARE(frame0.value("e").toString(), QStringLiteral("jaja")); + QVERIFY(frame0.contains("ff")); + QCOMPARE(frame0.type("ff"), QStringLiteral("string")); + QCOMPARE(frame0.value("ff").toString(), QStringLiteral("nenene")); } void tst_qv4debugger::readObject() diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 43cbd93396..544569de5f 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -828,7 +828,7 @@ void tst_qqmlcomponent::testSetInitialProperties() comp.createWithInitialProperties(QVariantMap { {"notThePropertiesYoureLookingFor", 42} }) }; QVERIFY(obj); - QVERIFY(comp.errorString().contains("Could not set property notThePropertiesYoureLookingFor")); + QVERIFY(comp.errorString().contains("Setting initial properties failed: Item does not have a property called notThePropertiesYoureLookingFor")); } } diff --git a/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml b/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml new file mode 100644 index 0000000000..02d737e37f --- /dev/null +++ b/tests/auto/qml/qqmldelegatemodel/data/qtbug_86017.qml @@ -0,0 +1,32 @@ +import QtQuick 2.8 +import QtQml.Models 2.1 + +DelegateModel { + id: visualModel + model: ListModel { + id: myLM + ListElement { + name: "Apple" + } + ListElement { + name: "Orange" + } + } + + filterOnGroup: "selected" + + groups: [ + DelegateModelGroup { + name: "selected" + includeByDefault: true + } + ] + + delegate: Text { + Component.onCompleted: { + DelegateModel.inPersistedItems = true + DelegateModel.inSelected = false + } + text: "item " + index + } +} diff --git a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp index 71550a50f3..9bc359d243 100644 --- a/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp +++ b/tests/auto/qml/qqmldelegatemodel/tst_qqmldelegatemodel.cpp @@ -45,6 +45,7 @@ private slots: void valueWithoutCallingObjectFirst_data(); void valueWithoutCallingObjectFirst(); void filterOnGroup_removeWhenCompleted(); + void qtbug_86017(); }; class AbstractItemModel : public QAbstractItemModel @@ -147,6 +148,20 @@ void tst_QQmlDelegateModel::filterOnGroup_removeWhenCompleted() QQmlDelegateModel *model = root->findChild<QQmlDelegateModel*>(); QVERIFY(model); QTest::qWaitFor([=]{ return model->count() == 2; } ); + +void tst_QQmlDelegateModel::qtbug_86017() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("qtbug_86017.qml")); + QScopedPointer<QObject> root(component.create()); + QVERIFY2(root, qPrintable(component.errorString())); + QTRY_VERIFY(component.isReady()); + QQmlDelegateModel *model = qobject_cast<QQmlDelegateModel*>(root.data()); + + QVERIFY(model); + QCOMPARE(model->count(), 2); + QCOMPARE(model->filterGroup(), "selected"); } QTEST_MAIN(tst_QQmlDelegateModel) diff --git a/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml b/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml new file mode 100644 index 0000000000..51943c3839 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/hangOnWarning.qml @@ -0,0 +1,3 @@ +import QtQml 2.15 + +QtObject["foobar"] {} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index de8b2ef7eb..94ecd6862a 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -334,6 +334,8 @@ private slots: void accessNullPointerPropertyCache(); void bareInlineComponent(); + void hangOnWarning(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -5841,6 +5843,16 @@ void tst_qqmllanguage::bareInlineComponent() QVERIFY(tab1Found); } +void tst_qqmllanguage::hangOnWarning() +{ + QTest::ignoreMessage(QtWarningMsg, + qPrintable(QStringLiteral("%1:3 : Ignored annotation") + .arg(testFileUrl("hangOnWarning.qml").toString()))); + QQmlComponent component(&engine, testFileUrl("hangOnWarning.qml")); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qmltest/qmltest.pro b/tests/auto/qmltest/qmltest.pro index 8ad1541cbc..0b793269e9 100644 --- a/tests/auto/qmltest/qmltest.pro +++ b/tests/auto/qmltest/qmltest.pro @@ -15,7 +15,6 @@ SUBDIRS += \ listview \ objectmodel \ pathview \ - pixel \ positioners \ qqmlbinding \ qtbug46798 \ diff --git a/tests/auto/qmltest/pixel/tst_pixel.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml index e628fed1d8..1c18133f92 100644 --- a/tests/auto/qmltest/pixel/tst_pixel.qml +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/simpleTapHandler.qml @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -26,40 +26,14 @@ ** ****************************************************************************/ -import QtQuick 2.0 -import QtTest 1.1 - -Rectangle { - id:rect - width: 40 - height: 40 - color:"red" - TestCase { - name: "Pixels" - when: windowShown - - function test_pixel() { - skip("test_pixel() is unstable, QTBUG-27671") - var img = grabImage(rect); - compare(img.pixel(20, 20), Qt.rgba(255, 0, 0, 255)); - compare(img.red(1,1), 255); - compare(img.green(1,1), 0); - compare(img.blue(1,1), 0); - compare(img.alpha(1,1), 255); - - fuzzyCompare(img.red(1,1), 254, 2); - fuzzyCompare(img.pixel(1,1), Qt.rgba(254, 0, 0, 254), 2); - fuzzyCompare(img.pixel(1,1), "#FF0201", 2); - - rect.color = "blue"; - waitForRendering(rect); - img = grabImage(rect); - compare(img.pixel(20, 20), Qt.rgba(0, 0, 255, 255)); - compare(img.red(1,1), 0); - compare(img.green(1,1), 0); - compare(img.blue(1,1), 255); - compare(img.alpha(1,1), 255); - } +import QtQuick 2.12 +Item { + id: root + width: 100 + height: 100 + property int tapCount: 0 + TapHandler { + onTapped: { ++root.tapCount } } -}
\ No newline at end of file +} diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp index 419afed3ac..52cef6248f 100644 --- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp @@ -71,15 +71,25 @@ private slots: void buttonsMultiTouch(); void componentUserBehavioralOverride(); void rightLongPressIgnoreWheel(); + void nonTopLevelParentWindow(); private: - void createView(QScopedPointer<QQuickView> &window, const char *fileName); + void createView(QScopedPointer<QQuickView> &window, const char *fileName, + QWindow *parent = nullptr); QTouchDevice *touchDevice; + void mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, + QWindow *targetWindow, QWindow *mapToWindow); }; -void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName, + QWindow *parent) { - window.reset(new QQuickView); + window.reset(new QQuickView(parent)); + if (parent) { + parent->show(); + QVERIFY(QTest::qWaitForWindowActive(parent)); + } + window->setSource(testFileUrl(fileName)); QTRY_COMPARE(window->status(), QQuickView::Ready); QQuickViewTestUtil::centerOnScreen(window.data()); @@ -90,6 +100,20 @@ void tst_TapHandler::createView(QScopedPointer<QQuickView> &window, const char * QVERIFY(window->rootObject() != nullptr); } +void tst_TapHandler::mouseEvent(QEvent::Type type, Qt::MouseButton button, const QPoint &point, + QWindow *targetWindow, QWindow *mapToWindow) +{ + QVERIFY(targetWindow); + QVERIFY(mapToWindow); + auto buttons = button; + if (type == QEvent::MouseButtonRelease) { + buttons = Qt::NoButton; + } + QMouseEvent me(type, point, mapToWindow->mapToGlobal(point), button, buttons, + Qt::KeyboardModifiers()); + QVERIFY(qApp->notify(targetWindow, &me)); +} + void tst_TapHandler::initTestCase() { // This test assumes that we don't get synthesized mouse events from QGuiApplication @@ -745,6 +769,31 @@ void tst_TapHandler::rightLongPressIgnoreWheel() QCOMPARE(tappedSpy.count(), 0); } +void tst_TapHandler::nonTopLevelParentWindow() // QTBUG-91716 +{ + QScopedPointer<QQuickWindow> parentWindowPtr(new QQuickWindow); + auto parentWindow = parentWindowPtr.get(); + parentWindow->setGeometry(400, 400, 250, 250); + + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "simpleTapHandler.qml", parentWindow); + auto window = windowPtr.get(); + window->setGeometry(10, 10, 100, 100); + + QQuickItem *root = window->rootObject(); + + auto p1 = QPoint(20, 20); + mouseEvent(QEvent::MouseButtonPress, Qt::LeftButton, p1, window, parentWindow); + mouseEvent(QEvent::MouseButtonRelease, Qt::LeftButton, p1, window, parentWindow); + + QCOMPARE(root->property("tapCount").toInt(), 1); + + QTest::touchEvent(window, touchDevice).press(0, p1, parentWindow).commit(); + QTest::touchEvent(window, touchDevice).release(0, p1, parentWindow).commit(); + + QCOMPARE(root->property("tapCount").toInt(), 2); +} + QTEST_MAIN(tst_TapHandler) #include "tst_qquicktaphandler.moc" diff --git a/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml b/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml new file mode 100644 index 0000000000..3acf67b4b6 --- /dev/null +++ b/tests/auto/quick/qquickpincharea/data/draggablePinchArea.qml @@ -0,0 +1,70 @@ +import QtQuick 2.12 + +Rectangle { + id: root + width: 600 + height: 600 + + Rectangle { + objectName: "paContainer" + width: parent.width -100 + height: parent.height - 100 + border.color: "black" + anchors.centerIn: parent + transformOrigin: Item.Center + + Rectangle { + width: 300 + height: 300 + color: "tomato" + PinchArea { + id: pa + anchors.fill: parent + pinch.target: parent + pinch.minimumScale: 0.5 + pinch.maximumScale: 2 + pinch.minimumRotation: -360 + pinch.maximumRotation: 360 + pinch.dragAxis: Pinch.XAndYAxis + pinch.minimumX: -100 + pinch.maximumX: 300 + pinch.minimumY: -100 + pinch.maximumY: 300 + } + + + Text { text: "this way up" } + } + } + + // only for touch feedback / troubleshooting + Item { + id: glassPane + z: 10000 + anchors.fill: parent + + PointHandler { + id: ph1 + target: Rectangle { + parent: glassPane + color: "green" + visible: ph1.active + x: ph1.point.position.x - width / 2 + y: ph1.point.position.y - height / 2 + width: 20; height: width; radius: width / 2 + } + } + + PointHandler { + id: ph2 + target: Rectangle { + parent: glassPane + color: "blue" + visible: ph2.active + x: ph2.point.position.x - width / 2 + y: ph2.point.position.y - height / 2 + width: 20; height: width; radius: width / 2 + } + } + } +} diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index 5b7108d96b..367df96118 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -52,6 +52,8 @@ private slots: void cancel(); void transformedPinchArea_data(); void transformedPinchArea(); + void dragTransformedPinchArea_data(); + void dragTransformedPinchArea(); private: QQuickView *createView(); @@ -586,6 +588,70 @@ void tst_QQuickPinchArea::transformedPinchArea() } } +void tst_QQuickPinchArea::dragTransformedPinchArea_data() +{ + QTest::addColumn<int>("rotation"); + QTest::addColumn<QPoint>("p1"); + QTest::addColumn<QPoint>("p2"); + QTest::addColumn<QPoint>("delta"); + + QTest::newRow("unrotated") + << 0 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 40); + QTest::newRow("20 deg") + << 20 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 40); + QTest::newRow("90 deg") + << 90 << QPoint(100, 100) << QPoint(200, 100) << QPoint(0, 40); + QTest::newRow("180 deg") + << 180 << QPoint(100, 100) << QPoint(200, 100) << QPoint(40, 0); + QTest::newRow("225 deg") + << 210 << QPoint(200, 200) << QPoint(300, 200) << QPoint(80, 80); +} + +void tst_QQuickPinchArea::dragTransformedPinchArea() // QTBUG-63673 +{ + QFETCH(int, rotation); + QFETCH(QPoint, p1); + QFETCH(QPoint, p2); + QFETCH(QPoint, delta); + const int threshold = qApp->styleHints()->startDragDistance(); + + QQuickView *view = createView(); + QScopedPointer<QQuickView> scope(view); + view->setSource(testFileUrl("draggablePinchArea.qml")); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + QVERIFY(view->rootObject()); + QQuickPinchArea *pinchArea = view->rootObject()->findChild<QQuickPinchArea*>(); + QVERIFY(pinchArea); + QQuickItem *pinchAreaTarget = pinchArea->parentItem(); + QVERIFY(pinchAreaTarget); + QQuickItem *pinchAreaContainer = pinchAreaTarget->parentItem(); + QVERIFY(pinchAreaContainer); + pinchAreaContainer->setRotation(rotation); + + QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device); + // start pinch + pinchSequence.press(1, pinchArea->mapToScene(p1).toPoint(), view) + .press(2, pinchArea->mapToScene(p2).toPoint(), view).commit(); + QQuickTouchUtils::flush(view); + pinchSequence.move(1, pinchArea->mapToScene(p1 + QPoint(threshold, threshold)).toPoint(), view) + .move(2, pinchArea->mapToScene(p2 + QPoint(threshold, threshold)).toPoint(), view).commit(); + QQuickTouchUtils::flush(view); + pinchSequence.move(1, pinchArea->mapToScene(p1 + delta).toPoint(), view) + .move(2, pinchArea->mapToScene(p2 + delta).toPoint(), view).commit(); + QQuickTouchUtils::flush(view); + QCOMPARE(pinchArea->pinch()->active(), true); + auto error = delta - QPoint(threshold, threshold) - + pinchAreaTarget->position().toPoint(); // expect 0, 0 + QVERIFY(qAbs(error.x()) <= 1); + QVERIFY(qAbs(error.y()) <= 1); + + // release pinch + pinchSequence.release(1, p1, view).release(2, p2, view).commit(); + QQuickTouchUtils::flush(view); + QCOMPARE(pinchArea->pinch()->active(), false); +} + QQuickView *tst_QQuickPinchArea::createView() { QQuickView *window = new QQuickView(nullptr); diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index d14c37d8e3..6d37fb44e3 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -169,6 +169,8 @@ private slots: void checkTableviewInsideAsyncLoader(); void hideRowsAndColumns_data(); void hideRowsAndColumns(); + void hideAndShowFirstColumn(); + void hideAndShowFirstRow(); void checkThatRevisionedPropertiesCannotBeUsedInOldImports(); void checkSyncView_rootView_data(); void checkSyncView_rootView(); @@ -2413,6 +2415,82 @@ void tst_QQuickTableView::hideRowsAndColumns() QVERIFY(!columnsToHideList.contains(column)); } +void tst_QQuickTableView::hideAndShowFirstColumn() +{ + // Check that if we hide the first column, it will move + // the second column to the origin of the viewport. Then check + // that if we show the first column again, it will reappear at + // the origin of the viewport, and as such, pushing the second + // column to the right of it. + LOAD_TABLEVIEW("hiderowsandcolumns.qml"); + + const int modelSize = 5; + auto model = TestModelAsVariant(modelSize, modelSize); + tableView->setModel(model); + + // Start by making the first column hidden + const auto columnsToHideList = QList<int>() << 0; + view->rootObject()->setProperty("columnsToHide", QVariant::fromValue(columnsToHideList)); + + WAIT_UNTIL_POLISHED; + + const int expectedColumnCount = modelSize - columnsToHideList.count(); + QCOMPARE(tableViewPrivate->loadedColumns.count(), expectedColumnCount); + QCOMPARE(tableViewPrivate->leftColumn(), 1); + QCOMPARE(tableView->contentX(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.x(), 0); + + // Make the first column in the model visible again + const auto emptyList = QList<int>(); + view->rootObject()->setProperty("columnsToHide", QVariant::fromValue(emptyList)); + tableView->forceLayout(); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableViewPrivate->loadedColumns.count(), modelSize); + QCOMPARE(tableViewPrivate->leftColumn(), 0); + QCOMPARE(tableView->contentX(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.x(), 0); +} + +void tst_QQuickTableView::hideAndShowFirstRow() +{ + // Check that if we hide the first row, it will move + // the second row to the origin of the viewport. Then check + // that if we show the first row again, it will reappear at + // the origin of the viewport, and as such, pushing the second + // row below it. + LOAD_TABLEVIEW("hiderowsandcolumns.qml"); + + const int modelSize = 5; + auto model = TestModelAsVariant(modelSize, modelSize); + tableView->setModel(model); + + // Start by making the first row hidden + const auto rowsToHideList = QList<int>() << 0; + view->rootObject()->setProperty("rowsToHide", QVariant::fromValue(rowsToHideList)); + + WAIT_UNTIL_POLISHED; + + const int expectedRowsCount = modelSize - rowsToHideList.count(); + QCOMPARE(tableViewPrivate->loadedRows.count(), expectedRowsCount); + QCOMPARE(tableViewPrivate->topRow(), 1); + QCOMPARE(tableView->contentY(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.y(), 0); + + // Make the first row in the model visible again + const auto emptyList = QList<int>(); + view->rootObject()->setProperty("rowsToHide", QVariant::fromValue(emptyList)); + tableView->forceLayout(); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(tableViewPrivate->loadedRows.count(), modelSize); + QCOMPARE(tableViewPrivate->topRow(), 0); + QCOMPARE(tableView->contentY(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.y(), 0); +} + void tst_QQuickTableView::checkThatRevisionedPropertiesCannotBeUsedInOldImports() { // Check that if you use a QQmlAdaptorModel together with a Repeater, the diff --git a/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml new file mode 100644 index 0000000000..f06be8f553 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/render_AlphaOverlapRebuild.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 + +/* + QTBUG-92984. + + Have three Image (with semi-transparency) and two semi-transparent + Rectangle elements, and so all in the alpha render list, with images that + are big enough to not get atlased. (meaning the underlying nodes never get + merged, but the nodes for the Rectangle elements might) + + Lay them out vertically below each other, with the two Rectangles on top of + the second and third Images, respectively. Then change (swap) the source + property of the Images. This triggers a rebuild in the batch renderer. + + Verify that the results are still correct, i.e. that the two Rectangle + elements do not get merged. An incorrect result would be having the third + Image rendered on top of the corresponding Rectangle due the two Rectangles + (incorrectly) being in one merged batch. The Image should always be below, + regardless of which nodes get changed, invalidated, and how batches get + rebuilt. + + The base-final sample set 1 just verifies that the Image changes from the + blueish to greenish. The important part is the second set of samples: this + checks that the red(ish) rectangle is still on top of the third Image. With + incorrect merging behavior the second final result would be the same as the + first final one (i.e. the "background" Image rendered, incorrectly, on top + of the Rectangle). + + #samples: 4 + PixelPos R G B Error-tolerance + #base: 30 115 0.24313 0.30588 0.99607 0.05 + #base: 30 124 0.847059 0.062745 0.2 0.05 + #final: 30 115 0.36078 0.99607 0.42745 0.05 + #final: 30 124 0.870588 0.2 0.0862745 0.05 +*/ + +RenderTestBase { + id: root + + property string selectedItem: "item2" + + Item { + width: 150; height: 50 + Image { + width: parent.width + objectName: "item1" + source: "widebtn1.png" + } + } + + Item { + y: 50; width: 150; height: 50 + Image { + width: parent.width + objectName: "item2" + source: selectedItem == objectName ? "widebtn2.png" : "widebtn1.png" + } + Rectangle { + anchors.fill: parent + anchors.margins: 20 + color: "red" + opacity: 0.8 + } + } + + Item { + y: 100; width: 150; height: 50 + Image { + id: img3 + width: parent.width + objectName: "item3" + source: selectedItem == objectName ? "widebtn2.png" : "widebtn1.png" + } + Rectangle { + width: parent.width + 50 + anchors.centerIn: parent + height: img3.height - 40 + color: "red" + opacity: 0.8 + } + } + + onEnterFinalStage: { + selectedItem = "item3"; + finalStageComplete = true; + } +} diff --git a/tests/auto/quick/scenegraph/data/widebtn1.png b/tests/auto/quick/scenegraph/data/widebtn1.png Binary files differnew file mode 100644 index 0000000000..1150b67a7a --- /dev/null +++ b/tests/auto/quick/scenegraph/data/widebtn1.png diff --git a/tests/auto/quick/scenegraph/data/widebtn2.png b/tests/auto/quick/scenegraph/data/widebtn2.png Binary files differnew file mode 100644 index 0000000000..40afe08363 --- /dev/null +++ b/tests/auto/quick/scenegraph/data/widebtn2.png diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 12f7efb7d5..cf85e262c0 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -402,7 +402,9 @@ void tst_SceneGraph::render_data() << "render_StackingOrder.qml" << "render_ImageFiltering.qml" << "render_bug37422.qml" - << "render_OpacityThroughBatchRoot.qml"; + << "render_OpacityThroughBatchRoot.qml" + << "render_AlphaOverlapRebuild.qml"; + if (!m_brokenMipmapSupport) files << "render_Mipmap.qml"; |