diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-02-16 01:03:22 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-02-16 01:03:22 +0100 |
commit | 7d80264fa7fc71bc68ed091ff8ba97bdd7970dc4 (patch) | |
tree | e3d6878189a6079dd61cde0b20f7f10cc4d1ffb0 | |
parent | 27c0e9d709aba97bd522fd3e53a53c4ff3c4d71b (diff) | |
parent | fee0fcfef08a05ed4ba9369d2352c876b514d69c (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I51cb42d253a83c0e6a76946c37cf1ff7c7cac150
73 files changed, 1391 insertions, 547 deletions
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 618c1340e6..8512b22fbd 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -555,6 +555,7 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen protected: void beginFunctionBodyHook() override; + bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; } Reference fallbackNameLookup(const QString &name) override; private: diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 75212b841c..56ff15a78c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2385,7 +2385,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co Reference r = Reference::fromName(this, name); r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global); - if (!r.global && m_globalNames.contains(name)) + if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name)) r.global = true; return r; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 3f96afc7c2..4d7001fe64 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -561,8 +561,10 @@ protected: Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); - // Hook provided to implement QML lookup semantics + // Hooks provided to implement QML lookup semantics + virtual bool canAccelerateGlobalLookups() const { return true; } virtual Reference fallbackNameLookup(const QString &name); + virtual void beginFunctionBodyHook() {} void emitReturn(const Reference &expr); diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index d5d97f8284..dd810d9d70 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -187,13 +187,6 @@ PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int ar return Address(StackPointerRegister, offset * PointerSize); } -JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::inArgStackAddress(int arg) -{ - int offset = arg - ArgInRegCount; - Q_ASSERT(offset >= 0); - return Address(FramePointerRegister, -(offset + 1) * PointerSize); -} - void PlatformAssemblerCommon::passAccumulatorAsArg(int arg) { #ifndef QT_NO_DEBUG @@ -342,10 +335,13 @@ void PlatformAssemblerCommon::tailCallRuntime(const char *functionName, const vo void PlatformAssemblerCommon::setTailCallArg(RegisterID src, int arg) { - if (arg < ArgInRegCount) + if (arg < ArgInRegCount) { move(src, registerForArg(arg)); - else - storePtr(src, inArgStackAddress(arg)); + } else { + // We never write to the incoming arguments space on the stack, and the tail call runtime + // method has the same signature as the jitted function, so it is safe for us to just reuse + // the arguments that we got in. + } } JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::jsAlloca(int slotCount) diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h index 3e70457bd8..d3d7eedae2 100644 --- a/src/qml/jit/qv4assemblercommon_p.h +++ b/src/qml/jit/qv4assemblercommon_p.h @@ -711,7 +711,6 @@ public: private: void passAccumulatorAsArg_internal(int arg, bool doPush); static Address argStackAddress(int arg); - static Address inArgStackAddress(int arg); private: const Value* constantTable; diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 3cf22d82e5..203f1f424f 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -201,7 +201,7 @@ Chunk *MemorySegment::allocate(size_t size) // chunk allocated for one huge allocation Q_ASSERT(availableBytes >= size); pageReservation.commit(base, size); - allocatedMap = ~static_cast<quintptr>(0); + allocatedMap = ~static_cast<quint64>(0); return base; } size_t requiredChunks = (size + sizeof(Chunk) - 1)/sizeof(Chunk); diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 9ac2100eab..c519429d48 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -70,8 +70,10 @@ void QQmlApplicationEnginePrivate::cleanUp() void QQmlApplicationEnginePrivate::init() { Q_Q(QQmlApplicationEngine); - q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); - q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); + q->connect(q, &QQmlApplicationEngine::quit, QCoreApplication::instance(), + &QCoreApplication::quit, Qt::QueuedConnection); + q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), + &QCoreApplication::exit, Qt::QueuedConnection); #if QT_CONFIG(translation) QTranslator* qtTranslator = new QTranslator; if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 99031e1e74..465a342129 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -64,6 +64,7 @@ static char file_string[] = "file"; #if defined(Q_OS_ANDROID) static char assets_string[] = "assets"; +static char content_string[] = "content"; #endif class QQmlFilePrivate; @@ -452,6 +453,8 @@ bool QQmlFile::isSynchronous(const QUrl &url) #if defined(Q_OS_ANDROID) } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) { return true; + } else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(content_string), Qt::CaseInsensitive)) { + return true; #endif } else { @@ -492,7 +495,10 @@ bool QQmlFile::isSynchronous(const QString &url) return url.length() >= 8 /* assets:/ */ && url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) && url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/'); - + } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) { + return url.length() >= 9 /* content:/ */ && + url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) && + url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); } #endif @@ -556,7 +562,10 @@ bool QQmlFile::isLocalFile(const QString &url) return url.length() >= 8 /* assets:/ */ && url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) && url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/'); - + } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) { + return url.length() >= 9 /* content:/ */ && + url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) && + url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); } #endif @@ -580,6 +589,8 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url) if (url.authority().isEmpty()) return url.toString(); return QString(); + } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) { + return url.toString(); } #endif @@ -618,6 +629,8 @@ QString QQmlFile::urlToLocalFileOrQrc(const QString& url) #if defined(Q_OS_ANDROID) else if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) { return url; + } else if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive)) { + return url; } #endif diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index fc48957bcb..b508a66f84 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1821,6 +1821,11 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) // assets resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); + } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && + path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { + // content url + QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); + return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); } #endif @@ -1878,6 +1883,11 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) // assets resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)); return fileInfo.isFile(); + } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && + path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { + // content url + QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)); + return fileInfo.isFile(); } #endif @@ -1913,7 +1923,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) bool isResource = path.at(0) == QLatin1Char(':'); #if defined(Q_OS_ANDROID) - isResource = isResource || path.startsWith(QLatin1String("assets:/")); + isResource = isResource || path.startsWith(QLatin1String("assets:/")) || path.startsWith(QLatin1String("content:/")); #endif if (isResource) { diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 57bbf7465d..48cc77bc3d 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1060,7 +1060,11 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { ctxt = new QQmlContextData; ctxt->setParent(cacheItem->contextData, /*stronglyReferencedByParent*/true); - ctxt->contextObject = proxy->proxiedObject(); + QObject *proxied = proxy->proxiedObject(); + ctxt->contextObject = proxied; + // We don't own the proxied object. We need to clear it if it goes away. + QObject::connect(proxied, &QObject::destroyed, + cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed); } } @@ -2009,6 +2013,17 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisI return QV4::Encode((int)thisItem->groupIndex(Compositor::Group(flag))); } +void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObject) +{ + if (!contextData) + return; + + for (QQmlContextData *ctxt = contextData->childContexts; ctxt; ctxt = ctxt->nextChild) { + if (ctxt->contextObject == childContextObject) + ctxt->contextObject = nullptr; + } +} + //--------------------------------------------------------------------------- diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 2d6fdf228e..5e480f4df6 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -106,6 +106,7 @@ public: void referenceObject() { ++objectRef; } bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } + void childContextObjectDestroyed(QObject *childContextObject); bool isReferenced() const { return scriptRef diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 56180f2dc5..9a73726797 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -61,6 +61,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qeventloop.h> #include <QtCore/qtextstream.h> +#include <QtCore/qtimer.h> #include <QtGui/qtextdocument.h> #include <stdio.h> #include <QtGui/QGuiApplication> @@ -646,7 +647,10 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch << "Test '" << QDir::toNativeSeparators(path) << "' window not active after requestActivate()."; } if (view.isExposed()) { - QTestRootObject::instance()->setWindowShown(true); + // Defer property update until event loop has started + QTimer::singleShot(0, []() { + QTestRootObject::instance()->setWindowShown(true); + }); } else { qWarning().nospace() << "Test '" << QDir::toNativeSeparators(path) << "' window was never exposed! " diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index de19a927a0..546f3011ec 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -1548,7 +1548,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::Function if (value->as<Object>()) { QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>(); if (color.isValid()) { - r->d()->context()->state.fillStyle = color; + r->d()->context()->state.strokeStyle = color; r->d()->context()->buffer()->setStrokeStyle(color); r->d()->context()->m_strokeStyle.set(scope.engine, value); } else { @@ -1559,7 +1559,12 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::Function r->d()->context()->m_strokeStyle.set(scope.engine, value); r->d()->context()->state.strokePatternRepeatX = style->d()->patternRepeatX; r->d()->context()->state.strokePatternRepeatY = style->d()->patternRepeatY; - + } else if (!style && r->d()->context()->state.strokeStyle != QBrush(QColor())) { + // If there is no style object, then ensure that the strokeStyle is at least + // QColor in case it was previously set + r->d()->context()->state.strokeStyle = QBrush(QColor()); + r->d()->context()->buffer()->setStrokeStyle(r->d()->context()->state.strokeStyle); + r->d()->context()->m_strokeStyle.set(scope.engine, value); } } } else if (value->isString()) { diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 2a59e50304..81d019a26d 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -2460,6 +2460,11 @@ QString QQuickListView::currentSection() const if both the velocity and duration are set, the animation will use whichever gives the shorter duration. + The move velocity and duration properties are used to control movement due + to index changes; for example, when incrementCurrentIndex() is called. When + the user flicks a ListView, the velocity from the flick is used to control + the movement instead. + To set only one property, the other can be set to \c -1. For example, if you only want to animate the duration and not velocity, use the following code: diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 0b5bebb7ba..28871e8068 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -150,6 +150,11 @@ must call \l forceLayout. This informs TableView that it needs to use the provider functions again to recalculate and update the layout. + Since Qt 5.13, if you want to hide a specific column, you can return \c 0 from the + \l columnWidthProvider for that column. Likewise, you can return 0 from the + \l rowHeightProvider to hide a row. If you return a negative number, TableView + will fall back to calculate the size based on the delegate items. + \note The size of a row or column should be a whole number to avoid sub-pixel alignment of items. @@ -217,7 +222,9 @@ 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. + Since Qt 5.13, if you want to hide a specific row, you can return \c 0 height for + that row. If you return a negative number, TableView will fall back to + calculate the height based on the delegate items. \sa columnWidthProvider, {Row heights and column widths} */ @@ -230,7 +237,9 @@ 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. + Since Qt 5.13, if you want to hide a specific column, you can return \c 0 width for + that column. If you return a negative number, TableView will fall back to + calculate the width based on the delegate items. \sa rowHeightProvider, {Row heights and column widths} */ @@ -376,12 +385,41 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle") #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 const int kEdgeIndexNotSet = -2; +static const int kEdgeIndexAtEnd = -3; 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::EdgeRange::EdgeRange() + : startIndex(kEdgeIndexNotSet) + , endIndex(kEdgeIndexNotSet) + , size(0) +{} + +bool QQuickTableViewPrivate::EdgeRange::containsIndex(Qt::Edge edge, int index) +{ + if (startIndex == kEdgeIndexNotSet) + return false; + + if (endIndex == kEdgeIndexAtEnd) { + switch (edge) { + case Qt::LeftEdge: + case Qt::TopEdge: + return index <= startIndex; + case Qt::RightEdge: + case Qt::BottomEdge: + return index >= startIndex; + } + } + + const int s = std::min(startIndex, endIndex); + const int e = std::max(startIndex, endIndex); + return index >= s && index <= e; +} + QQuickTableViewPrivate::QQuickTableViewPrivate() : QQuickFlickablePrivate() { @@ -448,6 +486,118 @@ QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const return QPoint(column, row); } +int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) +{ + return int(log2(float(edge))); +} + +void QQuickTableViewPrivate::clearEdgeSizeCache() +{ + cachedColumnWidth.startIndex = kEdgeIndexNotSet; + cachedRowHeight.startIndex = kEdgeIndexNotSet; + + for (Qt::Edge edge : allTableEdges) + cachedNextVisibleEdgeIndex[edgeToArrayIndex(edge)].startIndex = kEdgeIndexNotSet; +} + +int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) +{ + // Find the next column (or row) around the loaded table that is + // visible, and should be loaded next if the content item moves. + int startIndex = -1; + switch (edge) { + case Qt::LeftEdge: startIndex = loadedColumns.firstKey() - 1; break; + case Qt::RightEdge: startIndex = loadedColumns.lastKey() + 1; break; + case Qt::TopEdge: startIndex = loadedRows.firstKey() - 1; break; + case Qt::BottomEdge: startIndex = loadedRows.lastKey() + 1; break; + } + + return nextVisibleEdgeIndex(edge, startIndex); +} + +int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) +{ + // First check if we have already searched for the first visible index + // after the given startIndex recently, and if so, return the cached result. + // The cached result is valid if startIndex is inside the range between the + // startIndex and the first visible index found after it. + auto &cachedResult = cachedNextVisibleEdgeIndex[edgeToArrayIndex(edge)]; + if (cachedResult.containsIndex(edge, startIndex)) + return cachedResult.endIndex; + + // Search for the first column (or row) in the direction of edge that is + // visible, starting from the given column (startIndex). + int foundIndex = kEdgeIndexNotSet; + int testIndex = startIndex; + + switch (edge) { + case Qt::LeftEdge: { + forever { + if (testIndex < 0) { + foundIndex = kEdgeIndexAtEnd; + break; + } + + if (!isColumnHidden(testIndex)) { + foundIndex = testIndex; + break; + } + + --testIndex; + } + break; } + case Qt::RightEdge: { + forever { + if (testIndex > tableSize.width() - 1) { + foundIndex = kEdgeIndexAtEnd; + break; + } + + if (!isColumnHidden(testIndex)) { + foundIndex = testIndex; + break; + } + + ++testIndex; + } + break; } + case Qt::TopEdge: { + forever { + if (testIndex < 0) { + foundIndex = kEdgeIndexAtEnd; + break; + } + + if (!isRowHidden(testIndex)) { + foundIndex = testIndex; + break; + } + + --testIndex; + } + break; } + case Qt::BottomEdge: { + forever { + if (testIndex > tableSize.height() - 1) { + foundIndex = kEdgeIndexAtEnd; + break; + } + + if (!isRowHidden(testIndex)) { + foundIndex = testIndex; + break; + } + + ++testIndex; + } + break; } + } + + cachedResult.startIndex = startIndex; + cachedResult.endIndex = foundIndex; + return foundIndex; +} + void QQuickTableViewPrivate::updateContentWidth() { Q_Q(QQuickTableView); @@ -458,31 +608,13 @@ void QQuickTableViewPrivate::updateContentWidth() return; } - const qreal thresholdBeforeAdjust = 0.1; - int currentRightColumn = rightColumn(); - - 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); - } - } + const int nextColumn = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge); + const int columnsRemaining = nextColumn == kEdgeIndexAtEnd ? 0 : tableSize.width() - nextColumn; + const qreal remainingColumnWidths = columnsRemaining * averageEdgeSize.width(); + const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); + const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; + const qreal estimatedWidth = loadedTableOuterRect.right() + estimatedRemainingWidth; + q->QQuickFlickable::setContentWidth(estimatedWidth); } void QQuickTableViewPrivate::updateContentHeight() @@ -495,31 +627,13 @@ void QQuickTableViewPrivate::updateContentHeight() return; } - const qreal thresholdBeforeAdjust = 0.1; - int currentBottomRow = bottomRow(); - - 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); - } - } + const int nextRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge); + const int rowsRemaining = nextRow == kEdgeIndexAtEnd ? 0 : tableSize.height() - nextRow; + const qreal remainingRowHeights = rowsRemaining * averageEdgeSize.height(); + const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); + const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing; + const qreal estimatedHeight = loadedTableOuterRect.bottom() + estimatedRemainingHeight; + q->QQuickFlickable::setContentHeight(estimatedHeight); } void QQuickTableViewPrivate::enforceTableAtOrigin() @@ -531,24 +645,36 @@ void QQuickTableViewPrivate::enforceTableAtOrigin() bool layoutNeeded = false; const qreal flickMargin = 50; - if (leftColumn() == 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(leftColumn() == 0 ? 0 : flickMargin); - layoutNeeded = true; + const bool noMoreColumns = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd; + const bool noMoreRows = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd; + + if (noMoreColumns) { + if (!qFuzzyIsNull(loadedTableOuterRect.left())) { + // There are no more columns, but the table rect + // is not at origin. So we move it there. + loadedTableOuterRect.moveLeft(0); + layoutNeeded = true; + } + } else { + if (loadedTableOuterRect.left() <= 0) { + // The table rect is at origin, or outside. But we still have + // more visible columns to the left. So we need to make some + // space so that they can be flicked in. + loadedTableOuterRect.moveLeft(flickMargin); + layoutNeeded = true; + } } - if (topRow() == 0 && loadedTableOuterRect.y() > 0) { - loadedTableOuterRect.moveTop(0); - layoutNeeded = true; - } else if (loadedTableOuterRect.y() < 0) { - loadedTableOuterRect.moveTop(topRow() == 0 ? 0 : flickMargin); - layoutNeeded = true; + if (noMoreRows) { + if (!qFuzzyIsNull(loadedTableOuterRect.top())) { + loadedTableOuterRect.moveTop(0); + layoutNeeded = true; + } + } else { + if (loadedTableOuterRect.top() <= 0) { + loadedTableOuterRect.moveTop(flickMargin); + layoutNeeded = true; + } } if (layoutNeeded) { @@ -559,12 +685,12 @@ void QQuickTableViewPrivate::enforceTableAtOrigin() void QQuickTableViewPrivate::updateAverageEdgeSize() { - int bottomCell = bottomRow(); - int rightCell = rightColumn(); - qreal accRowSpacing = bottomCell * cellSpacing.height(); - qreal accColumnSpacing = rightCell * cellSpacing.width(); - averageEdgeSize.setHeight((loadedTableOuterRect.bottom() - accRowSpacing) / (bottomCell + 1)); - averageEdgeSize.setWidth((loadedTableOuterRect.right() - accColumnSpacing) / (rightCell + 1)); + const int loadedRowCount = loadedRows.count(); + const int loadedColumnCount = loadedColumns.count(); + const qreal accRowSpacing = (loadedRowCount - 1) * cellSpacing.height(); + const qreal accColumnSpacing = (loadedColumnCount - 1) * cellSpacing.width(); + averageEdgeSize.setHeight((loadedTableOuterRect.height() - accRowSpacing) / loadedRowCount); + averageEdgeSize.setWidth((loadedTableOuterRect.width() - accColumnSpacing) / loadedColumnCount); } void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() @@ -577,32 +703,82 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() loadedTableInnerRect = QRectF(topLeftRect.bottomRight(), bottomRightRect.topLeft()); } +void QQuickTableViewPrivate::forceLayout() +{ + columnRowPositionsInvalid = true; + clearEdgeSizeCache(); + RebuildOptions rebuildOptions = 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. + 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; + } + + // 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; + rebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (row == topRow()) + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow); + break; + } + + if (rebuildOptions) + scheduleRebuildTable(rebuildOptions); + + if (polishing) { + qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!"; + q_func()->polish(); + return; + } + + updatePolish(); +} + void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest() { if (loadRequest.edge() == Qt::Edge(0)) { // No edge means we're loading the top-left item - loadedColumns.insert(loadRequest.firstCell().x(), 0); - loadedRows.insert(loadRequest.firstCell().y(), 0); + loadedColumns.insert(loadRequest.column(), 0); + loadedRows.insert(loadRequest.row(), 0); return; } switch (loadRequest.edge()) { case Qt::LeftEdge: case Qt::RightEdge: - loadedColumns.insert(loadRequest.firstCell().x(), 0); + loadedColumns.insert(loadRequest.column(), 0); break; case Qt::TopEdge: case Qt::BottomEdge: - loadedRows.insert(loadRequest.firstCell().y(), 0); + loadedRows.insert(loadRequest.row(), 0); 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); @@ -734,54 +910,16 @@ void QQuickTableViewPrivate::unloadItem(const QPoint &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)); - } -} - -int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) -{ - switch (edge) { - case Qt::LeftEdge: - return leftColumn() - 1; - case Qt::RightEdge: - return rightColumn() + 1; - case Qt::TopEdge: - return topRow() - 1; - case Qt::BottomEdge: - return bottomRow() + 1; - } - return -1; -} - bool QQuickTableViewPrivate::canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const { switch (tableEdge) { case Qt::LeftEdge: - if (leftColumn() == 0) - return false; return loadedTableOuterRect.left() > fillRect.left() + cellSpacing.width(); case Qt::RightEdge: - if (rightColumn() == tableSize.width() - 1) - return false; return loadedTableOuterRect.right() < fillRect.right() - cellSpacing.width(); case Qt::TopEdge: - if (topRow() == 0) - return false; return loadedTableOuterRect.top() > fillRect.top() + cellSpacing.height(); case Qt::BottomEdge: - if (bottomRow() == tableSize.height() - 1) - return false; return loadedTableOuterRect.bottom() < fillRect.bottom() - cellSpacing.height(); } @@ -817,9 +955,14 @@ bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF Qt::Edge QQuickTableViewPrivate::nextEdgeToLoad(const QRectF rect) { for (Qt::Edge edge : allTableEdges) { - if (canLoadTableEdge(edge, rect)) - return edge; + if (!canLoadTableEdge(edge, rect)) + continue; + const int nextIndex = nextVisibleEdgeIndexAroundLoadedTable(edge); + if (nextIndex == kEdgeIndexAtEnd) + continue; + return edge; } + return Qt::Edge(0); } @@ -892,100 +1035,152 @@ void QQuickTableViewPrivate::calculateTableSize() emit q->rowsChanged(); } -qreal QQuickTableViewPrivate::resolveColumnWidth(int column) -{ - qreal columnWidth = -1; +qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column) +{ + // Return the column width specified by the application, or go + // through the loaded items and calculate it as a fallback. For + // layouting, the width can never be zero (or negative), as this + // can lead us to be stuck in an infinite loop trying to load and + // fill out the empty viewport space with empty columns. + const qreal explicitColumnWidth = getColumnWidth(column); + if (explicitColumnWidth >= 0) + return explicitColumnWidth; + + // 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. + qreal columnWidth = sizeHintForColumn(column); + + if (qIsNaN(columnWidth) || columnWidth <= 0) { + if (!layoutWarningIssued) { + layoutWarningIssued = true; + qmlWarning(q_func()) << "the delegate's implicitHeight needs to be greater than zero"; + } + columnWidth = kDefaultRowHeight; + } - 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; + return columnWidth; +} + +qreal QQuickTableViewPrivate::getRowLayoutHeight(int row) +{ + // Return the row height specified by the application, or go + // through the loaded items and calculate it as a fallback. For + // layouting, the height can never be zero (or negative), as this + // can lead us to be stuck in an infinite loop trying to load and + // fill out the empty viewport space with empty rows. + const qreal explicitRowHeight = getRowHeight(row); + if (explicitRowHeight >= 0) + return explicitRowHeight; + + // 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. + qreal 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; +} + +qreal QQuickTableViewPrivate::getColumnWidth(int column) +{ + // Return the width of the given column, if explicitly set. Return 0 if the column + // is hidden, and -1 if the width is not set (which means that the width should + // instead be calculated from the implicit size of the delegate items. This function + // can be overridden by e.g HeaderView to provide the column widths by other means. + const int noExplicitColumnWidth = -1; + + if (cachedColumnWidth.startIndex == column) + return cachedColumnWidth.size; + + if (columnWidthProvider.isUndefined()) + return noExplicitColumnWidth; + + qreal columnWidth = noExplicitColumnWidth; + + if (columnWidthProvider.isCallable()) { + auto const columnAsArgument = QJSValueList() << QJSValue(column); + columnWidth = columnWidthProvider.call(columnAsArgument).toNumber(); + if (qIsNaN(columnWidth) || columnWidth < 0) + columnWidth = noExplicitColumnWidth; } 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; + if (!layoutWarningIssued) { + layoutWarningIssued = true; + qmlWarning(q_func()) << "columnWidthProvider doesn't contain a function"; } + columnWidth = noExplicitColumnWidth; } + cachedColumnWidth.startIndex = column; + cachedColumnWidth.size = columnWidth; return columnWidth; } -qreal QQuickTableViewPrivate::resolveRowHeight(int row) +qreal QQuickTableViewPrivate::getRowHeight(int row) { - qreal rowHeight = -1; + // Return the height of the given row, if explicitly set. Return 0 if the row + // is hidden, and -1 if the height is not set (which means that the height should + // instead be calculated from the implicit size of the delegate items. This function + // can be overridden by e.g HeaderView to provide the row heights by other means. + const int noExplicitRowHeight = -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; - } + if (cachedRowHeight.startIndex == row) + return cachedRowHeight.size; + + if (rowHeightProvider.isUndefined()) + return noExplicitRowHeight; + + qreal rowHeight = noExplicitRowHeight; + + if (rowHeightProvider.isCallable()) { + auto const rowAsArgument = QJSValueList() << QJSValue(row); + rowHeight = rowHeightProvider.call(rowAsArgument).toNumber(); + if (qIsNaN(rowHeight) || rowHeight < 0) + rowHeight = noExplicitRowHeight; } 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; + if (!layoutWarningIssued) { + layoutWarningIssued = true; + qmlWarning(q_func()) << "rowHeightProvider doesn't contain a function"; } + rowHeight = noExplicitRowHeight; } + cachedRowHeight.startIndex = row; + cachedRowHeight.size = rowHeight; return rowHeight; } +bool QQuickTableViewPrivate::isColumnHidden(int column) +{ + // A column is hidden if the width is explicit set to zero (either by + // using a columnWidthProvider, or by overriding getColumnWidth()). + return qFuzzyIsNull(getColumnWidth(column)); +} + +bool QQuickTableViewPrivate::isRowHidden(int row) +{ + // A row is hidden if the height is explicit set to zero (either by + // using a rowHeightProvider, or by overriding getRowHeight()). + return qFuzzyIsNull(getRowHeight(row)); +} + void QQuickTableViewPrivate::relayoutTable() { + clearEdgeSizeCache(); relayoutTableItems(); syncLoadedTableRectFromLoadedTable(); enforceTableAtOrigin(); - contentSizeBenchMarkPoint = QPoint(-1, -1); updateContentWidth(); updateContentHeight(); // Return back to updatePolish to loadAndUnloadVisibleEdges() @@ -1004,7 +1199,7 @@ void QQuickTableViewPrivate::relayoutTableItems() for (auto c = loadedColumns.cbegin(); c != loadedColumns.cend(); ++c) { const int column = c.key(); // Adjust the geometry of all cells in the current column - const qreal width = resolveColumnWidth(column); + const qreal width = getColumnLayoutWidth(column); for (auto r = loadedRows.cbegin(); r != loadedRows.cend(); ++r) { const int row = r.key(); @@ -1015,13 +1210,14 @@ void QQuickTableViewPrivate::relayoutTableItems() item->setGeometry(geometry); } - nextColumnX += width + cellSpacing.width(); + if (width > 0) + nextColumnX += width + cellSpacing.width(); } for (auto r = loadedRows.cbegin(); r != loadedRows.cend(); ++r) { const int row = r.key(); // Adjust the geometry of all cells in the current row - const qreal height = resolveRowHeight(row); + const qreal height = getRowLayoutHeight(row); for (auto c = loadedColumns.cbegin(); c != loadedColumns.cend(); ++c) { const int column = c.key(); @@ -1032,7 +1228,8 @@ void QQuickTableViewPrivate::relayoutTableItems() item->setGeometry(geometry); } - nextRowY += height + cellSpacing.height(); + if (height > 0) + nextRowY += height + cellSpacing.height(); } if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) { @@ -1049,68 +1246,82 @@ void QQuickTableViewPrivate::relayoutTableItems() void QQuickTableViewPrivate::layoutVerticalEdge(Qt::Edge tableEdge) { - int column = (tableEdge == Qt::LeftEdge) ? leftColumn() : rightColumn(); - QPoint neighbourDirection = (tableEdge == Qt::LeftEdge) ? kRight : kLeft; - qreal width = resolveColumnWidth(column); + int columnThatNeedsLayout; + int neighbourColumn; + qreal columnX; + qreal columnWidth; + + if (tableEdge == Qt::LeftEdge) { + columnThatNeedsLayout = leftColumn(); + neighbourColumn = loadedColumns.keys().value(1); + columnWidth = getColumnLayoutWidth(columnThatNeedsLayout); + const auto neighbourItem = loadedTableItem(QPoint(neighbourColumn, topRow())); + columnX = neighbourItem->geometry().left() - cellSpacing.width() - columnWidth; + } else { + columnThatNeedsLayout = rightColumn(); + neighbourColumn = loadedColumns.keys().value(loadedColumns.count() - 2); + columnWidth = getColumnLayoutWidth(columnThatNeedsLayout); + const auto neighbourItem = loadedTableItem(QPoint(neighbourColumn, topRow())); + columnX = neighbourItem->geometry().right() + cellSpacing.width(); + } for (auto r = loadedRows.cbegin(); r != loadedRows.cend(); ++r) { const int row = r.key(); - auto fxTableItem = loadedTableItem(QPoint(column, row)); - auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection); + auto fxTableItem = loadedTableItem(QPoint(columnThatNeedsLayout, row)); + auto const neighbourItem = loadedTableItem(QPoint(neighbourColumn, row)); + const qreal rowY = neighbourItem->geometry().y(); + const qreal rowHeight = neighbourItem->geometry().height(); - 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->setGeometry(QRectF(columnX, rowY, columnWidth, rowHeight)); fxTableItem->setVisible(true); - qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry(); + qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(columnThatNeedsLayout, row) << fxTableItem->geometry(); } } void QQuickTableViewPrivate::layoutHorizontalEdge(Qt::Edge tableEdge) { - int row = (tableEdge == Qt::TopEdge) ? topRow() : bottomRow(); - QPoint neighbourDirection = (tableEdge == Qt::TopEdge) ? kDown : kUp; - qreal height = resolveRowHeight(row); + int rowThatNeedsLayout; + int neighbourRow; + qreal rowY; + qreal rowHeight; + + if (tableEdge == Qt::TopEdge) { + rowThatNeedsLayout = topRow(); + neighbourRow = loadedRows.keys().value(1); + rowHeight = getRowLayoutHeight(rowThatNeedsLayout); + const auto neighbourItem = loadedTableItem(QPoint(leftColumn(), neighbourRow)); + rowY = neighbourItem->geometry().top() - cellSpacing.height() - rowHeight; + } else { + rowThatNeedsLayout = bottomRow(); + neighbourRow = loadedRows.keys().value(loadedRows.count() - 2); + rowHeight = getRowLayoutHeight(rowThatNeedsLayout); + const auto neighbourItem = loadedTableItem(QPoint(leftColumn(), neighbourRow)); + rowY = neighbourItem->geometry().bottom() + cellSpacing.height(); + } for (auto c = loadedColumns.cbegin(); c != loadedColumns.cend(); ++c) { const int column = c.key(); - 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(); + auto fxTableItem = loadedTableItem(QPoint(column, rowThatNeedsLayout)); + auto const neighbourItem = loadedTableItem(QPoint(column, neighbourRow)); + const qreal columnX = neighbourItem->geometry().x(); + const qreal columnWidth = neighbourItem->geometry().width(); - geometry.moveTop(top); - geometry.moveLeft(neighbourItem->geometry().left()); - - fxTableItem->setGeometry(geometry); + fxTableItem->setGeometry(QRectF(columnX, rowY, columnWidth, rowHeight)); fxTableItem->setVisible(true); - qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry(); + qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, rowThatNeedsLayout) << fxTableItem->geometry(); } } void QQuickTableViewPrivate::layoutTopLeftItem() { - const QPoint cell = loadRequest.firstCell(); + const QPoint cell(loadRequest.column(), loadRequest.row()); auto topLeftItem = loadedTableItem(cell); auto item = topLeftItem->item; item->setPosition(loadRequest.startPosition()); - item->setSize(QSizeF(resolveColumnWidth(cell.x()), resolveRowHeight(cell.y()))); + item->setSize(QSizeF(getColumnLayoutWidth(cell.x()), getRowLayoutHeight(cell.y()))); topLeftItem->setVisible(true); qCDebug(lcTableViewDelegateLifecycle) << "geometry:" << topLeftItem->geometry(); } @@ -1135,29 +1346,6 @@ void QQuickTableViewPrivate::layoutTableEdgeFromLoadRequest() } } -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(), ""); @@ -1183,9 +1371,22 @@ void QQuickTableViewPrivate::processLoadRequest() syncLoadedTableRectFromLoadedTable(); if (rebuildState == RebuildState::Done) { - enforceTableAtOrigin(); - updateContentWidth(); - updateContentHeight(); + // Loading of this edge was not done as a part of a rebuild, but + // instead as an incremental build after e.g a flick. + switch (loadRequest.edge()) { + case Qt::LeftEdge: + case Qt::TopEdge: + enforceTableAtOrigin(); + break; + case Qt::RightEdge: + updateAverageEdgeSize(); + updateContentWidth(); + break; + case Qt::BottomEdge: + updateAverageEdgeSize(); + updateContentHeight(); + break; + } drainReusePoolAfterLoadRequest(); } @@ -1206,7 +1407,7 @@ void QQuickTableViewPrivate::processRebuildTable() if (rebuildState == RebuildState::VerifyTable) { if (loadedItems.isEmpty()) { - qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate"; + qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model, all rows or columns hidden, or no delegate"; rebuildState = RebuildState::Done; return; } @@ -1230,14 +1431,14 @@ void QQuickTableViewPrivate::processRebuildTable() && reusableFlag == QQmlTableInstanceModel::Reusable); if (rebuildState == RebuildState::PreloadColumns) { - if (preload && rightColumn() < tableSize.width() - 1) + if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) != kEdgeIndexAtEnd) loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested); if (!moveToNextRebuildState()) return; } if (rebuildState == RebuildState::PreloadRows) { - if (preload && bottomRow() < tableSize.height() - 1) + if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) != kEdgeIndexAtEnd) loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested); if (!moveToNextRebuildState()) return; @@ -1265,19 +1466,26 @@ bool QQuickTableViewPrivate::moveToNextRebuildState() return true; } -void QQuickTableViewPrivate::beginRebuildTable() +QPoint QQuickTableViewPrivate::calculateNewTopLeft() { - if (loadRequest.isActive()) - cancelLoadRequest(); + const int firstVisibleLeft = nextVisibleEdgeIndex(Qt::RightEdge, 0); + const int firstVisibleTop = nextVisibleEdgeIndex(Qt::BottomEdge, 0); - calculateTableSize(); + return QPoint(firstVisibleLeft, firstVisibleTop); +} - QPoint topLeft; - QPointF topLeftPos; +void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos) +{ + if (tableSize.isEmpty()) { + releaseLoadedItems(QQmlTableInstanceModel::NotReusable); + topLeft = QPoint(kEdgeIndexAtEnd, kEdgeIndexAtEnd); + return; + } if (rebuildOptions & RebuildOption::All) { qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All"; releaseLoadedItems(QQmlTableInstanceModel::NotReusable); + topLeft = calculateNewTopLeft(); } else if (rebuildOptions & RebuildOption::ViewportOnly) { qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly"; releaseLoadedItems(reusableFlag); @@ -1301,13 +1509,27 @@ void QQuickTableViewPrivate::beginRebuildTable() } else { Q_TABLEVIEW_UNREACHABLE(rebuildOptions); } +} + +void QQuickTableViewPrivate::beginRebuildTable() +{ + calculateTableSize(); + + QPoint topLeft; + QPointF topLeftPos; + calculateTopLeft(topLeft, topLeftPos); loadedColumns.clear(); loadedRows.clear(); loadedTableOuterRect = QRect(); loadedTableInnerRect = QRect(); - contentSizeBenchMarkPoint = QPoint(-1, -1); columnRowPositionsInvalid = false; + clearEdgeSizeCache(); + + if (topLeft.x() == kEdgeIndexAtEnd || topLeft.y() == kEdgeIndexAtEnd) { + // No visible columns or rows, so nothing to load + return; + } loadInitialTopLeftItem(topLeft, topLeftPos); loadAndUnloadVisibleEdges(); @@ -1333,12 +1555,6 @@ void QQuickTableViewPrivate::loadInitialTopLeftItem(const QPoint &cell, const QP { Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), ""); - if (tableSize.isEmpty()) - return; - - if (model->count() == 0) - return; - if (tableModel && !tableModel->delegate()) return; @@ -1356,18 +1572,25 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) case Qt::LeftEdge: case Qt::RightEdge: { const int column = edge == Qt::LeftEdge ? leftColumn() : rightColumn(); - unloadItems(QLine(column, topRow(), column, bottomRow())); + for (auto r = loadedRows.cbegin(); r != loadedRows.cend(); ++r) + unloadItem(QPoint(column, r.key())); loadedColumns.remove(column); + syncLoadedTableRectFromLoadedTable(); + updateAverageEdgeSize(); + updateContentWidth(); break; } case Qt::TopEdge: case Qt::BottomEdge: { const int row = edge == Qt::TopEdge ? topRow() : bottomRow(); - unloadItems(QLine(leftColumn(), row, rightColumn(), row)); + for (auto c = loadedColumns.cbegin(); c != loadedColumns.cend(); ++c) + unloadItem(QPoint(c.key(), row)); loadedRows.remove(row); + syncLoadedTableRectFromLoadedTable(); + updateAverageEdgeSize(); + updateContentHeight(); break; } } - syncLoadedTableRectFromLoadedTable(); qCDebug(lcTableViewDelegateLifecycle) << tableLayoutToString(); } @@ -1375,20 +1598,10 @@ void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMo { const int edgeIndex = nextVisibleEdgeIndexAroundLoadedTable(edge); qCDebug(lcTableViewDelegateLifecycle) << edge << edgeIndex; - QLine cellsToLoad; - - switch (edge) { - case Qt::LeftEdge: - case Qt::RightEdge: - cellsToLoad = QLine(edgeIndex, topRow(), edgeIndex, bottomRow()); - break; - case Qt::TopEdge: - case Qt::BottomEdge: - cellsToLoad = QLine(leftColumn(), edgeIndex, rightColumn(), edgeIndex); - break; - } - loadRequest.begin(cellsToLoad, edge, incubationMode); + const QList<int> visibleCells = edge & (Qt::LeftEdge | Qt::RightEdge) + ? loadedRows.keys() : loadedColumns.keys(); + loadRequest.begin(edge, edgeIndex, visibleCells, incubationMode); processLoadRequest(); } @@ -1529,8 +1742,12 @@ void QQuickTableViewPrivate::updatePolish() if (loadedItems.isEmpty()) return; - if (columnRowPositionsInvalid) + if (columnRowPositionsInvalid) { relayoutTable(); + updateAverageEdgeSize(); + updateContentWidth(); + updateContentHeight(); + } loadAndUnloadVisibleEdges(); } @@ -1967,16 +2184,7 @@ void QQuickTableView::setContentHeight(qreal 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(); + d_func()->forceLayout(); } QQuickTableViewAttached *QQuickTableView::qmlAttachedProperties(QObject *obj) diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index ed6f8026a2..9bea8040dc 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -88,54 +88,52 @@ public: 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; + Q_ASSERT(!m_active); + m_active = true; + m_edge = Qt::Edge(0); + m_mode = incubationMode; + m_edgeIndex = cell.x(); + m_visibleCellsInEdge.clear(); + m_visibleCellsInEdge.append(cell.y()); + m_currentIndex = 0; + m_startPos = pos; qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString(); } - void begin(const QLine cellsToLoad, Qt::Edge edgeToLoad, QQmlIncubator::IncubationMode incubationMode) + void begin(Qt::Edge edgeToLoad, int edgeIndex, const QList<int> visibleCellsInEdge, 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; + Q_ASSERT(!m_active); + m_active = true; + m_edge = edgeToLoad; + m_edgeIndex = edgeIndex; + m_visibleCellsInEdge = visibleCellsInEdge; + m_mode = incubationMode; + m_currentIndex = 0; qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString(); } - inline void markAsDone() { active = false; } - inline bool isActive() { return active; } + inline void markAsDone() { m_active = false; } + inline bool isActive() { return m_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 QPoint currentCell() { return cellAt(m_currentIndex); } + inline bool hasCurrentCell() { return m_currentIndex < m_visibleCellsInEdge.count(); } + inline void moveToNextCell() { ++m_currentIndex; } - inline bool atBeginning() { return currentIndex == 0; } - inline bool hasCurrentCell() { return currentIndex < cellCount; } - inline void moveToNextCell() { ++currentIndex; } + inline Qt::Edge edge() { return m_edge; } + inline int row() { return cellAt(0).y(); } + inline int column() { return cellAt(0).x(); } + inline QQmlIncubator::IncubationMode incubationMode() { return m_mode; } - inline Qt::Edge edge() { return tableEdge; } - inline QQmlIncubator::IncubationMode incubationMode() { return mode; } - - inline QPointF startPosition() { return startPos; } + inline QPointF startPosition() { return m_startPos; } QString toString() { QString str; QDebug dbg(&str); dbg.nospace() << "TableSectionLoadRequest(" << "edge:" - << tableEdge << " cells:" << tableCells << " incubation:"; + << m_edge << ", edgeIndex:" << m_edgeIndex << ", incubation:"; - switch (mode) { + switch (m_mode) { case QQmlIncubator::Asynchronous: dbg << "Asynchronous"; break; @@ -151,22 +149,31 @@ public: } 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); + Qt::Edge m_edge = Qt::Edge(0); + QList<int> m_visibleCellsInEdge; + int m_edgeIndex = 0; + int m_currentIndex = 0; + bool m_active = false; + QQmlIncubator::IncubationMode m_mode = QQmlIncubator::AsynchronousIfNested; + QPointF m_startPos; + + inline QPoint cellAt(int index) { + return !m_edge || (m_edge & (Qt::LeftEdge | Qt::RightEdge)) + ? QPoint(m_edgeIndex, m_visibleCellsInEdge[index]) + : QPoint(m_visibleCellsInEdge[index], m_edgeIndex); } }; + class EdgeRange { + public: + EdgeRange(); + bool containsIndex(Qt::Edge edge, int index); + + int startIndex; + int endIndex; + qreal size; + }; + enum class RebuildState { Begin = 0, LoadInitalTable, @@ -234,7 +241,6 @@ public: TableEdgeLoadRequest loadRequest; - QPoint contentSizeBenchMarkPoint = QPoint(-1, -1); QSizeF cellSpacing = QSizeF(0, 0); QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable; @@ -248,6 +254,10 @@ public: QJSValue rowHeightProvider; QJSValue columnWidthProvider; + EdgeRange cachedNextVisibleEdgeIndex[4]; + EdgeRange cachedColumnWidth; + EdgeRange cachedRowHeight; + // 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 @@ -281,8 +291,13 @@ public: qreal sizeHintForRow(int row); void calculateTableSize(); - qreal resolveColumnWidth(int column); - qreal resolveRowHeight(int row); + inline bool isColumnHidden(int column); + inline bool isRowHidden(int row); + + qreal getColumnLayoutWidth(int column); + qreal getRowLayoutHeight(int row); + qreal getColumnWidth(int column); + qreal getRowHeight(int row); inline int topRow() const { return loadedRows.firstKey(); } inline int bottomRow() const { return loadedRows.lastKey(); } @@ -300,12 +315,16 @@ public: void updateContentWidth(); void updateContentHeight(); void updateAverageEdgeSize(); + void forceLayout(); void enforceTableAtOrigin(); void syncLoadedTableRectFromLoadedTable(); void syncLoadedTableFromLoadRequest(); + int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex); int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge); + inline int edgeToArrayIndex(Qt::Edge edge); + void clearEdgeSizeCache(); bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const; bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const; @@ -316,7 +335,6 @@ public: 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); @@ -324,18 +342,17 @@ public: 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(); + QPoint calculateNewTopLeft(); + void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos); void beginRebuildTable(); void layoutAfterLoadingInitialTable(); diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index dd7fbccff5..9e447d40ac 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -643,7 +643,7 @@ QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QT // Appending the elide character may push the line over the maximum width // in which case the elided text will need to be elided. QFontMetricsF metrics(layout.font()); - if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth) + if (metrics.horizontalAdvance(elideChar) + line.naturalTextWidth() >= lineWidth) elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth); } return elideText; diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 3a12ad6ba5..6b4b118eb7 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2689,6 +2689,12 @@ void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event) q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), q, SLOT(q_updateAlignment())); #endif + if (event->reason() != Qt::ActiveWindowFocusReason + && event->reason() != Qt::PopupFocusReason + && control->textCursor().hasSelection() + && !persistentSelection) + q->deselect(); + emit q->editingFinished(); } } diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 792aa31a88..d84932b8d0 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -1007,8 +1007,8 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText break; }; - QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height()); - qreal xoff = fontMetrics.width(QLatin1Char(' ')); + QSizeF size(fontMetrics.horizontalAdvance(listItemBullet), fontMetrics.height()); + qreal xoff = fontMetrics.horizontalAdvance(QLatin1Char(' ')); if (block.textDirection() == Qt::LeftToRight) xoff = -xoff - size.width(); setPosition(pos + QPointF(xoff, 0)); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f517b5b3e9..485c0dfea7 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4630,7 +4630,7 @@ void QQuickWindow::resetOpenGLState() Item or Window within which it was declared, you can remove that relationship by setting \c transientParent to \c null: - \l qml + \qml import QtQuick.Window 2.13 Window { @@ -4640,7 +4640,7 @@ void QQuickWindow::resetOpenGLState() visible: true } } - \qml + \endqml In order to cause the window to be centered above its transient parent by default, depending on the window manager, it may also be necessary to set diff --git a/src/quick/util/qquickfontmetrics.cpp b/src/quick/util/qquickfontmetrics.cpp index f1278c366f..42b3038c48 100644 --- a/src/quick/util/qquickfontmetrics.cpp +++ b/src/quick/util/qquickfontmetrics.cpp @@ -287,7 +287,7 @@ qreal QQuickFontMetrics::lineWidth() const */ qreal QQuickFontMetrics::advanceWidth(const QString &text) const { - return m_metrics.width(text); + return m_metrics.horizontalAdvance(text); } /*! diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index 5e1aaf121e..7d545cdb2f 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -675,7 +675,7 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri { qreal imgWidth = 0.0; QFontMetricsF fm(layout.font()); - const qreal spaceWidth = fm.width(QChar::Nbsp); + const qreal spaceWidth = fm.horizontalAdvance(QChar::Nbsp); const bool trailingSpace = textOut.endsWith(space); if (!updateImagePositions) { diff --git a/src/quick/util/qquicktextmetrics.cpp b/src/quick/util/qquicktextmetrics.cpp index 81088b5cd6..959980b267 100644 --- a/src/quick/util/qquicktextmetrics.cpp +++ b/src/quick/util/qquicktextmetrics.cpp @@ -186,7 +186,7 @@ void QQuickTextMetrics::setElideWidth(qreal elideWidth) */ qreal QQuickTextMetrics::advanceWidth() const { - return m_metrics.width(m_text); + return m_metrics.horizontalAdvance(m_text); } /*! diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 -Subproject 3c69133cc419840c1be34638039cd8c48a7ef58 +Subproject 6b0c42c63c2492bd0a7a96d3179d122b5f71793 diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml new file mode 100644 index 0000000000..3d67c958bb --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/delayedExit.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 + +QtObject { + id: root + property Timer t: Timer { interval: 1; running: true; onTriggered: Qt.exit(0); } + property Connections c: Connections { + target: Qt.application + onAboutToQuit: console.log("End"); + } + Component.onCompleted: console.log("Start: " + Qt.application.arguments[1]); +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qml b/tests/auto/qml/qqmlapplicationengine/testapp/delayedQuit.qml index c75485a7f7..c75485a7f7 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.qml +++ b/tests/auto/qml/qqmlapplicationengine/testapp/delayedQuit.qml diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml new file mode 100644 index 0000000000..46634f3f51 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/immediateExit.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + Component.onCompleted: { + console.log("End: " + Qt.application.arguments[1]); + Qt.exit(0) + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml b/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml new file mode 100644 index 0000000000..1da9d1201a --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/immediateQuit.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + Component.onCompleted: { + console.log("End: " + Qt.application.arguments[1]); + Qt.quit() + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp index a57889fe86..be0d98a2df 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp @@ -32,6 +32,6 @@ int main (int argc, char *argv[]) { QCoreApplication app(argc, argv); - QQmlApplicationEngine e(QUrl("qrc:///main.qml")); + QQmlApplicationEngine e(QUrl(QString("qrc:///") + argv[1])); return app.exec(); } diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc index 5f6483ac33..82b695bbd8 100644 --- a/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc @@ -1,5 +1,8 @@ <RCC> <qresource prefix="/"> - <file>main.qml</file> + <file>immediateQuit.qml</file> + <file>immediateExit.qml</file> + <file>delayedQuit.qml</file> + <file>delayedExit.qml</file> </qresource> </RCC> diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index daeb9b5455..ce654dc45e 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -46,6 +46,7 @@ private slots: void initTestCase(); void basicLoading(); void testNonResolvedPath(); + void application_data(); void application(); void applicationProperties(); void removeObjectsWhenDestroyed(); @@ -111,35 +112,56 @@ void tst_qqmlapplicationengine::testNonResolvedPath() } } +void tst_qqmlapplicationengine::application_data() +{ + QTest::addColumn<QByteArray>("qmlFile"); + QTest::addColumn<QByteArray>("expectedStdErr"); + + QTest::newRow("delayed quit") << QByteArray("delayedQuit.qml") + << QByteArray("qml: Start: delayedQuit.qml\nqml: End\n"); + QTest::newRow("delayed exit") << QByteArray("delayedExit.qml") + << QByteArray("qml: Start: delayedExit.qml\nqml: End\n"); + QTest::newRow("immediate quit") << QByteArray("immediateQuit.qml") + << QByteArray("qml: End: immediateQuit.qml\n"); + QTest::newRow("immediate exit") << QByteArray("immediateExit.qml") + << QByteArray("qml: End: immediateExit.qml\n"); +} + void tst_qqmlapplicationengine::application() { /* This test batches together some tests about running an external application written with QQmlApplicationEngine. The application tests the following functionality which is easier to do by watching a separate process: - -Loads relative paths from the working directory - -quits when quit is called - -emits aboutToQuit after quit is called - -has access to application command line arguments + - Loads relative paths from the working directory + - Quits when quit is called + - Exits when exit is called + - Emits aboutToQuit after quit is called + - Has access to application command line arguments Note that checking the output means that on builds with extra debugging, this might fail with a false positive. Also the testapp is automatically built and installed in shadow builds, so it does NOT use testData */ + + QFETCH(QByteArray, qmlFile); + QFETCH(QByteArray, expectedStdErr); + #if QT_CONFIG(process) QDir::setCurrent(buildDir); QProcess *testProcess = new QProcess(this); QStringList args; - args << QLatin1String("testData"); + args << qmlFile; // QML file passed as an argument is going to be run by testapp. testProcess->start(QLatin1String("testapp/testapp"), args); QVERIFY(testProcess->waitForFinished(5000)); QCOMPARE(testProcess->exitCode(), 0); - QByteArray test_stdout = testProcess->readAllStandardOutput(); - QByteArray test_stderr = testProcess->readAllStandardError(); - QByteArray test_stderr_target("qml: Start: testData\nqml: End\n"); + QByteArray testStdOut = testProcess->readAllStandardOutput(); + QByteArray testStdErr = testProcess->readAllStandardError(); #ifdef Q_OS_WIN - test_stderr_target.replace('\n', QByteArray("\r\n")); + expectedStdErr.replace('\n', QByteArray("\r\n")); #endif - QCOMPARE(test_stdout, QByteArray("")); - QVERIFY(QString(test_stderr).endsWith(QString(test_stderr_target))); + QCOMPARE(testStdOut, QByteArray("")); + QVERIFY2(QString(testStdErr).endsWith(QString(expectedStdErr)), + QByteArray("\nExpected ending:\n") + expectedStdErr + + QByteArray("\nActual output:\n") + testStdErr); delete testProcess; QDir::setCurrent(srcDir); #else // process diff --git a/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml b/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml new file mode 100644 index 0000000000..80e459966b --- /dev/null +++ b/tests/auto/qml/qqmlconnections/data/override-proxy-type.qml @@ -0,0 +1,13 @@ +import QtQml 2.12 +import test.proxy 1.0 + +Proxy { + property int testEnum: 0; + id: proxy + property Connections connections: Connections { + target: proxy + onSomeSignal: testEnum = Proxy.EnumValue; + } + + Component.onCompleted: someSignal() +} diff --git a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp index 8ef00f8080..dc29363fcf 100644 --- a/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp +++ b/tests/auto/qml/qqmlconnections/tst_qqmlconnections.cpp @@ -55,6 +55,7 @@ private slots: void disabledAtStart(); void clearImplicitTarget(); void onWithoutASignal(); + void noAcceleratedGlobalLookup(); private: QQmlEngine engine; @@ -407,6 +408,30 @@ void tst_qqmlconnections::onWithoutASignal() QVERIFY(item == nullptr); // should parse error, and not give us an item (or crash). } +class Proxy : public QObject +{ + Q_OBJECT +public: + enum MyEnum { EnumValue = 20, AnotherEnumValue }; + Q_ENUM(MyEnum) + +signals: + void someSignal(); +}; + +void tst_qqmlconnections::noAcceleratedGlobalLookup() +{ + qRegisterMetaType<Proxy::MyEnum>(); + qmlRegisterType<Proxy>("test.proxy", 1, 0, "Proxy"); + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("override-proxy-type.qml")); + QVERIFY(c.isReady()); + QScopedPointer<QObject> object(c.create()); + const QVariant val = object->property("testEnum"); + QCOMPARE(val.type(), QMetaType::Int); + QCOMPARE(val.toInt(), int(Proxy::EnumValue)); +} + QTEST_MAIN(tst_qqmlconnections) #include "tst_qqmlconnections.moc" diff --git a/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml b/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml new file mode 100644 index 0000000000..86fdd920ed --- /dev/null +++ b/tests/auto/qml/qqmlpropertycache/data/passQGadget.qml @@ -0,0 +1,12 @@ +import QtQml 2.2 + +QtObject { + property var result; + + property Connections connections: Connections { + target: emitter + onEmitGadget: function(gadget) { + result = gadget.someProperty; + } + } +} diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 5abda7b854..07237c9157 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -50,6 +50,7 @@ private slots: void signalHandlers(); void signalHandlersDerived(); void passForeignEnums(); + void passQGadget(); void metaObjectSize_data(); void metaObjectSize(); void metaObjectChecksum(); @@ -362,6 +363,44 @@ void tst_qqmlpropertycache::passForeignEnums() Q_DECLARE_METATYPE(MyEnum::Option1) Q_DECLARE_METATYPE(MyEnum::ShortEnum) +QT_BEGIN_NAMESPACE +class SimpleGadget +{ + Q_GADGET + Q_PROPERTY(bool someProperty READ someProperty) +public: + bool someProperty() const { return true; } +}; + +// Avoids NeedsCreation and NeedsDestruction flags +Q_DECLARE_TYPEINFO(SimpleGadget, Q_PRIMITIVE_TYPE); +QT_END_NAMESPACE + +class GadgetEmitter : public QObject +{ + Q_OBJECT +signals: + void emitGadget(SimpleGadget); +}; + +void tst_qqmlpropertycache::passQGadget() +{ + qRegisterMetaType<SimpleGadget>(); + + GadgetEmitter emitter; + engine.rootContext()->setContextProperty("emitter", &emitter); + QQmlComponent component(&engine, testFile("passQGadget.qml")); + QVERIFY(component.isReady()); + + QScopedPointer<QObject> obj(component.create(engine.rootContext())); + QVariant before = obj->property("result"); + QVERIFY(before.isNull()); + emit emitter.emitGadget(SimpleGadget()); + QVariant after = obj->property("result"); + QCOMPARE(QMetaType::Type(after.type()), QMetaType::Bool); + QVERIFY(after.toBool()); +} + class TestClass : public QObject { Q_OBJECT diff --git a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp index cf2ac4a830..f4ed051e1f 100644 --- a/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp +++ b/tests/auto/quick/pointerhandlers/flickableinterop/tst_flickableinterop.cpp @@ -741,7 +741,7 @@ void tst_FlickableInterop::touchAndDragHandlerOnFlickable() QQuickTouchUtils::flush(window); } if (!(buttonDragHandler && !pressDelay)) - QVERIFY(flickable->contentY() >= dragThreshold); + QTRY_VERIFY(flickable->contentY() >= dragThreshold); if (buttonTapHandler) QCOMPARE(buttonTapHandler->isPressed(), false); touchSeq.release(1, p1, window).commit(); diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp index feb356a7d5..2b6482465c 100644 --- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp @@ -188,7 +188,10 @@ public: QQuickPointerHandler::handlePointerEventImpl(event); if (!enabled()) return; - ++eventCount; + if (event->isPressEvent()) + ++pressEventCount; + if (event->isReleaseEvent()) + ++releaseEventCount; EventItem *item = qmlobject_cast<EventItem *>(target()); if (!item) { event->point(0)->setGrabberPointerHandler(this); @@ -218,7 +221,8 @@ public: static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePosition())); } - int eventCount = 0; + int pressEventCount = 0; + int releaseEventCount = 0; }; class tst_PointerHandlers : public QQmlDataTest @@ -646,9 +650,9 @@ void tst_PointerHandlers::handlerInWindow() QPoint p1(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(handler->eventCount, 1); + QTRY_COMPARE(handler->pressEventCount, 1); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(handler->eventCount, 2); + QTRY_COMPARE(handler->releaseEventCount, 1); } void tst_PointerHandlers::dynamicCreationInWindow() @@ -670,9 +674,9 @@ void tst_PointerHandlers::dynamicCreationInWindow() QPoint p1(20, 20); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(handler->eventCount, 1); + QTRY_COMPARE(handler->pressEventCount, 1); QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); - QTRY_COMPARE(handler->eventCount, 2); + QTRY_COMPARE(handler->releaseEventCount, 1); } QTEST_MAIN(tst_PointerHandlers) diff --git a/tests/auto/quick/qquickanimations/BLACKLIST b/tests/auto/quick/qquickanimations/BLACKLIST deleted file mode 100644 index e011db46b7..0000000000 --- a/tests/auto/quick/qquickanimations/BLACKLIST +++ /dev/null @@ -1,6 +0,0 @@ -# QTBUG-45466 QTBUG-29062 -[simpleProperty] -osx-10.9 developer-build -[simplePath] -windows gcc developer-build - diff --git a/tests/auto/quick/qquickbehaviors/BLACKLIST b/tests/auto/quick/qquickbehaviors/BLACKLIST deleted file mode 100644 index 9be4da176d..0000000000 --- a/tests/auto/quick/qquickbehaviors/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[currentValue] -windows diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml index 22803a19ce..a3f1ab0a9b 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_strokeStyle.qml @@ -3,6 +3,8 @@ import QtQuick 2.0 CanvasTestCase { id:testCase name: "strokeStyle" + property color anotherColor: "#0000ff" + property color emptyColor function init_data() { return testData("2d"); } function test_default(row) { var canvas = createCanvasObject(row); @@ -46,4 +48,37 @@ CanvasTestCase { comparePixel(ctx,0,0,255,255,255,255); canvas.destroy() } + function test_colorFromObjectToString(row) { + var canvas = createCanvasObject(row); + var ctx = canvas.getContext('2d'); + + ctx.reset(); + ctx.strokeStyle = anotherColor + ctx.strokeStyle = "red"; + compare(ctx.strokeStyle, "#ff0000"); + + ctx.strokeStyle = anotherColor + ctx.strokeStyle = "black"; + compare(ctx.strokeStyle, "#000000"); + + ctx.strokeStyle = "white"; + ctx.strokeStyle = anotherColor + compare(ctx.strokeStyle, "#0000ff"); + canvas.destroy() + } + function test_withInvalidColor(row) { + var canvas = createCanvasObject(row); + var ctx = canvas.getContext('2d'); + + ctx.reset(); + ctx.strokeStyle = emptyColor + compare(ctx.strokeStyle, "#000000"); + ctx.strokeStyle = "red"; + compare(ctx.strokeStyle, "#ff0000"); + ctx.strokeStyle = emptyColor + compare(ctx.strokeStyle, "#000000"); + ctx.strokeStyle = anotherColor; + compare(ctx.strokeStyle, "#0000ff"); + canvas.destroy() + } } diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST deleted file mode 100644 index cc91754e68..0000000000 --- a/tests/auto/quick/qquickflickable/BLACKLIST +++ /dev/null @@ -1,7 +0,0 @@ -# QTBUG-26696 -[rebound] -osx -[stopAtBounds] -windows developer-build -[returnToBounds] -osx diff --git a/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp b/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp index 6e516f51e1..ef61c45225 100644 --- a/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp +++ b/tests/auto/quick/qquickfontmetrics/tst_quickfontmetrics.cpp @@ -127,7 +127,7 @@ void tst_QuickFontMetrics::functions() QFontMetricsF expected = QFontMetricsF(QFont()); QCOMPARE(metrics.elidedText(text, mode, width, flags), expected.elidedText(text, mode, width, flags)); - QCOMPARE(metrics.advanceWidth(text), expected.width(text)); + QCOMPARE(metrics.advanceWidth(text), expected.horizontalAdvance(text)); QCOMPARE(metrics.boundingRect(text), expected.boundingRect(text)); QCOMPARE(metrics.tightBoundingRect(text), expected.tightBoundingRect(text)); } diff --git a/tests/auto/quick/qquickgridview/BLACKLIST b/tests/auto/quick/qquickgridview/BLACKLIST deleted file mode 100644 index 9eb9940aa5..0000000000 --- a/tests/auto/quick/qquickgridview/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[snapOneRow:horizontal, right to left] -windows diff --git a/tests/auto/quick/qquickimage/BLACKLIST b/tests/auto/quick/qquickimage/BLACKLIST deleted file mode 100644 index d15fae1b67..0000000000 --- a/tests/auto/quick/qquickimage/BLACKLIST +++ /dev/null @@ -1,4 +0,0 @@ -# QTBUG-65978 -[nullPixmapPaint] -b2qt - diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 15aea4be19..e22d52294f 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -1,5 +1,3 @@ -[QTBUG_38209] -* [enforceRange_withoutHighlight] osx #QTBUG-53863 diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 558ca2e759..52d1458a53 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -43,31 +43,6 @@ #include <QtGui/QScreen> #include <qpa/qwindowsysteminterface.h> -// Initialize view, set Url, center in available geometry, move mouse away if desired -static bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage) -{ - v.setBaseSize(QSize(240,320)); - v.setSource(url); - while (v.status() == QQuickView::Loading) - QTest::qWait(10); - if (v.status() != QQuickView::Ready) { - foreach (const QQmlError &e, v.errors()) - errorMessage->append(e.toString().toLocal8Bit() + '\n'); - return false; - } - const QRect screenGeometry = v.screen()->availableGeometry(); - const QSize size = v.size(); - const QPoint offset = QPoint(size.width() / 2, size.height() / 2); - v.setFramePosition(screenGeometry.center() - offset); -#if QT_CONFIG(cursor) // Get the cursor out of the way. - if (moveMouseOut) - QCursor::setPos(v.geometry().topRight() + QPoint(100, 100)); -#else - Q_UNUSED(moveMouseOut) -#endif - return true; -} - class CircleMask : public QObject { Q_OBJECT @@ -235,7 +210,7 @@ void tst_QQuickMouseArea::dragProperties() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragproperties.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragproperties.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -333,7 +308,7 @@ void tst_QQuickMouseArea::resetDrag() QQuickView window; QByteArray errorMessage; window.rootContext()->setContextProperty("haveTarget", QVariant(true)); - QVERIFY2(initView(window, testFileUrl("dragreset.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragreset.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -363,7 +338,7 @@ void tst_QQuickMouseArea::dragging() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -428,7 +403,7 @@ void tst_QQuickMouseArea::dragSmoothed() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -482,7 +457,7 @@ void tst_QQuickMouseArea::dragThreshold() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -540,7 +515,7 @@ void tst_QQuickMouseArea::invalidDrag() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -589,7 +564,7 @@ void tst_QQuickMouseArea::cancelDragging() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -644,7 +619,7 @@ void tst_QQuickMouseArea::availableDistanceLessThanDragThreshold() { QQuickView view; QByteArray errorMessage; - QVERIFY2(initView(view, testFileUrl("availableDistanceLessThanDragThreshold.qml"), true, &errorMessage), + QVERIFY2(QQuickTest::initView(view, testFileUrl("availableDistanceLessThanDragThreshold.qml"), true, &errorMessage), errorMessage.constData()); view.show(); view.requestActivate(); @@ -672,7 +647,7 @@ void tst_QQuickMouseArea::setDragOnPressed() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("setDragOnPressed.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("setDragOnPressed.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -716,7 +691,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnClick() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("updateMousePosOnClick.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("updateMousePosOnClick.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -744,7 +719,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnResize() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("updateMousePosOnResize.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("updateMousePosOnResize.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -780,7 +755,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() // We handle onPressAndHold, therefore no onClicked QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("clickandhold.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("clickandhold.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -812,7 +787,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() // We do not handle onPressAndHold, therefore we get onClicked QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("noclickandhold.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("noclickandhold.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -835,7 +810,7 @@ void tst_QQuickMouseArea::onMousePressRejected() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("rejectEvent.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("rejectEvent.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -882,7 +857,7 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("pressedCanceled.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("pressedCanceled.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -955,7 +930,7 @@ void tst_QQuickMouseArea::doubleClick() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -992,7 +967,7 @@ void tst_QQuickMouseArea::clickTwice() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("clicktwice.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("clicktwice.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1028,7 +1003,7 @@ void tst_QQuickMouseArea::invalidClick() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("doubleclick.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1061,7 +1036,7 @@ void tst_QQuickMouseArea::pressedOrdering() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("pressedOrdering.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("pressedOrdering.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1087,7 +1062,7 @@ void tst_QQuickMouseArea::preventStealing() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("preventstealing.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("preventstealing.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1161,7 +1136,7 @@ void tst_QQuickMouseArea::clickThrough() //With no handlers defined click, doubleClick and PressAndHold should propagate to those with handlers QScopedPointer<QQuickView> window(new QQuickView); QByteArray errorMessage; - QVERIFY2(initView(*window.data(), testFileUrl("clickThrough.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("clickThrough.qml"), true, &errorMessage), errorMessage.constData()); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != nullptr); @@ -1195,7 +1170,7 @@ void tst_QQuickMouseArea::clickThrough() window.reset(new QQuickView); //With handlers defined click, doubleClick and PressAndHold should propagate only when explicitly ignored - QVERIFY2(initView(*window.data(), testFileUrl("clickThrough2.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("clickThrough2.qml"), true, &errorMessage), errorMessage.constData()); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != nullptr); @@ -1269,7 +1244,7 @@ void tst_QQuickMouseArea::clickThrough() window.reset(new QQuickView); //QTBUG-34368 - Shouldn't propagate to disabled mouse areas - QVERIFY2(initView(*window.data(), testFileUrl("qtbug34368.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("qtbug34368.qml"), true, &errorMessage), errorMessage.constData()); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != nullptr); @@ -1291,7 +1266,7 @@ void tst_QQuickMouseArea::clickThrough() window.reset(new QQuickView); //QTBUG-49100 - QVERIFY2(initView(*window.data(), testFileUrl("qtbug49100.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(*window.data(), testFileUrl("qtbug49100.qml"), true, &errorMessage), errorMessage.constData()); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); QVERIFY(window->rootObject() != nullptr); @@ -1306,7 +1281,7 @@ void tst_QQuickMouseArea::hoverPosition() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("hoverPosition.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverPosition.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1325,7 +1300,7 @@ void tst_QQuickMouseArea::hoverPropagation() //QTBUG-18175, to behave like GV did. QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("hoverPropagation.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverPropagation.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1352,7 +1327,7 @@ void tst_QQuickMouseArea::hoverVisible() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("hoverVisible.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverVisible.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1380,7 +1355,7 @@ void tst_QQuickMouseArea::hoverAfterPress() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("hoverAfterPress.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("hoverAfterPress.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1406,7 +1381,7 @@ void tst_QQuickMouseArea::subtreeHoverEnabled() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("qtbug54019.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("qtbug54019.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1426,7 +1401,7 @@ void tst_QQuickMouseArea::disableAfterPress() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1529,7 +1504,7 @@ void tst_QQuickMouseArea::onWheel() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("wheel.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("wheel.qml"), true, &errorMessage), errorMessage.constData()); QQuickItem *root = window.rootObject(); QVERIFY(root != nullptr); @@ -1573,7 +1548,7 @@ void tst_QQuickMouseArea::transformedMouseArea() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("transformedMouseArea.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("transformedMouseArea.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject() != nullptr); @@ -1681,7 +1656,7 @@ void tst_QQuickMouseArea::pressedMultipleButtons() QQuickView view; QByteArray errorMessage; - QVERIFY2(initView(view, testFileUrl("simple.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(view, testFileUrl("simple.qml"), true, &errorMessage), errorMessage.constData()); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(view.rootObject() != nullptr); @@ -1712,7 +1687,7 @@ void tst_QQuickMouseArea::changeAxis() { QQuickView view; QByteArray errorMessage; - QVERIFY2(initView(view, testFileUrl("changeAxis.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(view, testFileUrl("changeAxis.qml"), true, &errorMessage), errorMessage.constData()); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QTRY_VERIFY(view.rootObject() != nullptr); @@ -1805,7 +1780,7 @@ void tst_QQuickMouseArea::moveAndReleaseWithoutPress() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("moveAndReleaseWithoutPress.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("moveAndReleaseWithoutPress.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -1845,7 +1820,7 @@ void tst_QQuickMouseArea::nestedStopAtBounds() QQuickView view; QByteArray errorMessage; - QVERIFY2(initView(view, testFileUrl("nestedStopAtBounds.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(view, testFileUrl("nestedStopAtBounds.qml"), true, &errorMessage), errorMessage.constData()); view.show(); view.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -1898,7 +1873,7 @@ void tst_QQuickMouseArea::nestedFlickableStopAtBounds() { QQuickView view; QByteArray errorMessage; - QVERIFY2(initView(view, testFileUrl("nestedFlickableStopAtBounds.qml"), false, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(view, testFileUrl("nestedFlickableStopAtBounds.qml"), false, &errorMessage), errorMessage.constData()); view.show(); view.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -2000,7 +1975,7 @@ void tst_QQuickMouseArea::containsPress() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("containsPress.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("containsPress.qml"), true, &errorMessage), errorMessage.constData()); window.show(); window.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -2053,7 +2028,7 @@ void tst_QQuickMouseArea::ignoreBySource() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("ignoreBySource.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("ignoreBySource.qml"), true, &errorMessage), errorMessage.constData()); window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); QVERIFY(window.rootObject()); @@ -2194,7 +2169,7 @@ void tst_QQuickMouseArea::pressAndHold() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("pressAndHold.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("pressAndHold.qml"), true, &errorMessage), errorMessage.constData()); window.show(); window.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -2239,7 +2214,7 @@ void tst_QQuickMouseArea::pressOneAndTapAnother() QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("twoMouseAreas.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("twoMouseAreas.qml"), true, &errorMessage), errorMessage.constData()); window.show(); window.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -2292,7 +2267,7 @@ void tst_QQuickMouseArea::mask() { QQuickView window; QByteArray errorMessage; - QVERIFY2(initView(window, testFileUrl("mask.qml"), true, &errorMessage), errorMessage.constData()); + QVERIFY2(QQuickTest::initView(window, testFileUrl("mask.qml"), true, &errorMessage), errorMessage.constData()); window.show(); window.requestActivate(); QVERIFY(QTest::qWaitForWindowExposed(&window)); diff --git a/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml b/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml new file mode 100644 index 0000000000..b11cb1476c --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/hiderowsandcolumns.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Window 2.3 + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + property var rowsToHide + property var columnsToHide + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + columnWidthProvider: function(column) { + if (columnsToHide.includes(column)) + return 0; + } + rowHeightProvider: function(row) { + if (rowsToHide.includes(row)) + return 0; + } + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + color: "lightgray" + border.width: 1 + implicitWidth: 50 + implicitHeight: 50 + Text { + anchors.centerIn: parent + text: column + "," + row + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml b/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml index 32d1fc9d0d..1e35d65bcd 100644 --- a/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml +++ b/tests/auto/quick/qquicktableview/data/usefaultyrowcolumnprovider.qml @@ -56,16 +56,14 @@ Item { delegate: tableViewDelegate columnSpacing: 1 rowSpacing: 1 - columnWidthProvider: function(column) { } - rowHeightProvider: function(row) { return 0 } + columnWidthProvider: function(column) { return "notAValidValue" } + rowHeightProvider: function(row) { return "notAValidValue" } } Component { id: tableViewDelegate Rectangle { objectName: "tableViewDelegate" - implicitWidth: 20 - implicitHeight: 20 color: "lightgray" border.width: 1 Text { diff --git a/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml b/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml index 04d12f8d20..e9f01b6abf 100644 --- a/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml +++ b/tests/auto/quick/qquicktableview/data/userowcolumnprovider.qml @@ -46,6 +46,8 @@ Item { property alias tableView: tableView property Component delegate: tableViewDelegate + property bool returnNegativeColumnWidth: false + property bool returnNegativeRowHeight: false TableView { id: tableView @@ -56,8 +58,16 @@ Item { delegate: tableViewDelegate columnSpacing: 1 rowSpacing: 1 - columnWidthProvider: function(column) { return column + 10 } - rowHeightProvider: function(row) { return row + 10 } + columnWidthProvider: function(column) { + if (returnNegativeColumnWidth) + return -1 + return column + 10 + } + rowHeightProvider: function(row) { + if (returnNegativeRowHeight) + return -1 + return row + 10 + } } Component { diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index d2ec9948c9..e263427b59 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -111,10 +111,12 @@ private slots: void checkDelegateWithAnchors(); void checkColumnWidthProvider(); void checkColumnWidthProviderInvalidReturnValues(); + void checkColumnWidthProviderNegativeReturnValue(); void checkColumnWidthProviderNotCallable(); void checkRowHeightWithoutProvider(); void checkRowHeightProvider(); void checkRowHeightProviderInvalidReturnValues(); + void checkRowHeightProviderNegativeReturnValue(); void checkRowHeightProviderNotCallable(); void checkForceLayoutFunction(); void checkContentWidthAndHeight(); @@ -157,6 +159,8 @@ private slots: void checkRebuildViewportOnly(); void useDelegateChooserWithoutDefault(); void checkTableviewInsideAsyncLoader(); + void hideRowsAndColumns_data(); + void hideRowsAndColumns(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -373,7 +377,7 @@ void tst_QQuickTableView::checkColumnWidthProviderInvalidReturnValues() tableView->setModel(model); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Provider.*valid")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero")); WAIT_UNTIL_POLISHED; @@ -381,6 +385,23 @@ void tst_QQuickTableView::checkColumnWidthProviderInvalidReturnValues() QCOMPARE(fxItem->item->width(), kDefaultColumnWidth); } +void tst_QQuickTableView::checkColumnWidthProviderNegativeReturnValue() +{ + // Check that we fall back to use the implicit width of the delegate + // items if the columnWidthProvider return a negative number. + LOAD_TABLEVIEW("userowcolumnprovider.qml"); + + auto model = TestModelAsVariant(10, 10); + view->rootObject()->setProperty("returnNegativeColumnWidth", true); + + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + for (auto fxItem : tableViewPrivate->loadedItems) + QCOMPARE(fxItem->item->width(), 20); +} + void tst_QQuickTableView::checkColumnWidthProviderNotCallable() { // Check that we fall back to use default columns widths, if you @@ -453,7 +474,7 @@ void tst_QQuickTableView::checkRowHeightProviderInvalidReturnValues() tableView->setModel(model); - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Provider.*valid")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero")); WAIT_UNTIL_POLISHED; @@ -461,6 +482,23 @@ void tst_QQuickTableView::checkRowHeightProviderInvalidReturnValues() QCOMPARE(fxItem->item->height(), kDefaultRowHeight); } +void tst_QQuickTableView::checkRowHeightProviderNegativeReturnValue() +{ + // Check that we fall back to use the implicit height of the delegate + // items if the rowHeightProvider return a negative number. + LOAD_TABLEVIEW("userowcolumnprovider.qml"); + + auto model = TestModelAsVariant(10, 10); + view->rootObject()->setProperty("returnNegativeRowHeight", true); + + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + for (auto fxItem : tableViewPrivate->loadedItems) + QCOMPARE(fxItem->item->height(), 20); +} + void tst_QQuickTableView::checkRowHeightProviderNotCallable() { // Check that we fall back to use default row heights, if you @@ -538,6 +576,8 @@ void tst_QQuickTableView::checkContentWidthAndHeight() const qreal expectedSizeInit = (tableSize * cellSizeSmall) + ((tableSize - 1) * spacing); QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); + QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); + QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); // Flick in 5 more rows and columns, but not so far that we start loading in // the ones that are bigger. Loading in more rows and columns of the same @@ -548,6 +588,8 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); + QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); + QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); // Flick to row and column 20 (smallCellCount), since there the row and // column sizes increases with 100. Check that TableView then adjusts @@ -562,6 +604,11 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QVERIFY(tableViewPrivate->rebuildScheduled); WAIT_UNTIL_POLISHED; + // Check that the average cell size is now matching the + // large cells since they fill up the whole view. + QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeLarge); + QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeLarge); + const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge); const int columnCount = smallCellCount + largeSizeCellCountInView; QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount); @@ -571,41 +618,47 @@ void tst_QQuickTableView::checkContentWidthAndHeight() const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge; const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength; - const qreal averageCellSize = lengthAfterFlick / columnCount; - const qreal expectedSizeHalf = (tableSize * averageCellSize) + accumulatedSpacing; - - QCOMPARE(tableView->contentWidth(), expectedSizeHalf); - QCOMPARE(tableView->contentHeight(), expectedSizeHalf); + // Check that loadedTableOuterRect has been calculated correct thus far + const qreal spacingAfterFlick = (smallCellCount + largeSizeCellCountInView - 1) * spacing; + QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), flickTo + spacing); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), lengthAfterFlick + spacingAfterFlick); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), flickTo + spacing); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), lengthAfterFlick + spacingAfterFlick); + + // At this point, we should have the exact content width/height set, because + // TableView knows where the large cells start in the viewport, and how many + // columns that remain in the model. It will assume that the rest of the the + // columns have the same average size as the ones currently inside the viewport. + const qreal expectedContentSize = (smallCellCount * cellSizeSmall) + (largeCellCount * cellSizeLarge) + accumulatedSpacing; + QCOMPARE(tableView->contentWidth(), expectedContentSize); + QCOMPARE(tableView->contentHeight(), expectedContentSize); // Flick to the end (row/column 100, and overshoot a bit), and // check that we then end up with the exact content width/height. const qreal secondHalfLength = largeCellCount * cellSizeLarge; const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing; - - // If we flick more than one page at a time, tableview will jump to the new - // position and rebuild the table without loading the edges in-between. Which - // row and column that ends up as new top-left is then based on a prediction, and - // therefore unreliable. To avoid this to happen (which will also affect the - // reported size of the table), we flick to the end position in smaller chuncks. - QVERIFY(!tableViewPrivate->polishScheduled); - QVERIFY(!tableViewPrivate->rebuildScheduled); - int pages = qCeil((expectedFullSize - tableView->contentX()) / tableView->width()); - for (int i = 0; i < pages; i++) { - tableView->setContentX(tableView->contentX() + tableView->width() - 1); - tableView->setContentY(tableView->contentY() + tableView->height() - 1); - QVERIFY(!tableViewPrivate->rebuildScheduled); - } + const qreal overshoot = 100; + const qreal endPosX = expectedFullSize - tableView->width() + overshoot; + const qreal endPosY = expectedFullSize - tableView->height() + overshoot; + tableView->setContentX(endPosX); + tableView->setContentY(endPosY); QCOMPARE(tableView->contentWidth(), expectedFullSize); QCOMPARE(tableView->contentHeight(), expectedFullSize); - // Flick back to start. Since we know the actual table - // size, contentWidth/Height shouldn't change. + // Flick back to start tableView->setContentX(0); tableView->setContentY(0); - QCOMPARE(tableView->contentWidth(), expectedFullSize); - QCOMPARE(tableView->contentHeight(), expectedFullSize); + // Since we move the viewport more than a page, tableview + // will jump to the new position and do a rebuild. + QVERIFY(tableViewPrivate->polishScheduled); + QVERIFY(tableViewPrivate->rebuildScheduled); + WAIT_UNTIL_POLISHED; + + // We should now have the same content width/height as when we started + QCOMPARE(tableView->contentWidth(), expectedSizeInit); + QCOMPARE(tableView->contentHeight(), expectedSizeInit); } void tst_QQuickTableView::checkPageFlicking() @@ -2001,6 +2054,92 @@ void tst_QQuickTableView::checkTableviewInsideAsyncLoader() QVERIFY(height > 0); }; +#define INT_LIST(indices) QVariant::fromValue(QList<int>() << indices) + +void tst_QQuickTableView::hideRowsAndColumns_data() +{ + QTest::addColumn<QVariant>("rowsToHide"); + QTest::addColumn<QVariant>("columnsToHide"); + + const auto emptyList = QVariant::fromValue(QList<int>()); + + // Hide rows + QTest::newRow("first") << INT_LIST(0) << emptyList; + QTest::newRow("middle 1") << INT_LIST(1) << emptyList; + QTest::newRow("middle 3") << INT_LIST(3) << emptyList; + QTest::newRow("last") << INT_LIST(4) << emptyList; + + QTest::newRow("subsequent 0,1") << INT_LIST(0 << 1) << emptyList; + QTest::newRow("subsequent 1,2") << INT_LIST(1 << 2) << emptyList; + QTest::newRow("subsequent 3,4") << INT_LIST(3 << 4) << emptyList; + + QTest::newRow("all but first") << INT_LIST(1 << 2 << 3 << 4) << emptyList; + QTest::newRow("all but last") << INT_LIST(0 << 1 << 2 << 3) << emptyList; + QTest::newRow("all but middle") << INT_LIST(0 << 1 << 3 << 4) << emptyList; + + // Hide columns + QTest::newRow("first") << emptyList << INT_LIST(0); + QTest::newRow("middle 1") << emptyList << INT_LIST(1); + QTest::newRow("middle 3") << emptyList << INT_LIST(3); + QTest::newRow("last") << emptyList << INT_LIST(4); + + QTest::newRow("subsequent 0,1") << emptyList << INT_LIST(0 << 1); + QTest::newRow("subsequent 1,2") << emptyList << INT_LIST(1 << 2); + QTest::newRow("subsequent 3,4") << emptyList << INT_LIST(3 << 4); + + QTest::newRow("all but first") << emptyList << INT_LIST(1 << 2 << 3 << 4); + QTest::newRow("all but last") << emptyList << INT_LIST(0 << 1 << 2 << 3); + QTest::newRow("all but middle") << emptyList << INT_LIST(0 << 1 << 3 << 4); + + // Hide both rows and columns at the same time + QTest::newRow("first") << INT_LIST(0) << INT_LIST(0); + QTest::newRow("middle 1") << INT_LIST(1) << INT_LIST(1); + QTest::newRow("middle 3") << INT_LIST(3) << INT_LIST(3); + QTest::newRow("last") << INT_LIST(4) << INT_LIST(4); + + QTest::newRow("subsequent 0,1") << INT_LIST(0 << 1) << INT_LIST(0 << 1); + QTest::newRow("subsequent 1,2") << INT_LIST(1 << 2) << INT_LIST(1 << 2); + QTest::newRow("subsequent 3,4") << INT_LIST(3 << 4) << INT_LIST(3 << 4); + + QTest::newRow("all but first") << INT_LIST(1 << 2 << 3 << 4) << INT_LIST(1 << 2 << 3 << 4); + QTest::newRow("all but last") << INT_LIST(0 << 1 << 2 << 3) << INT_LIST(0 << 1 << 2 << 3); + QTest::newRow("all but middle") << INT_LIST(0 << 1 << 3 << 4) << INT_LIST(0 << 1 << 3 << 4); + + // Hide all rows and columns + QTest::newRow("all") << INT_LIST(0 << 1 << 2 << 3 << 4) << INT_LIST(0 << 1 << 2 << 3 << 4); +} + +void tst_QQuickTableView::hideRowsAndColumns() +{ + // Check that you can hide the first row (corner case) + // and that we load the other columns as expected. + QFETCH(QVariant, rowsToHide); + QFETCH(QVariant, columnsToHide); + LOAD_TABLEVIEW("hiderowsandcolumns.qml"); + + const QList<int> rowsToHideList = qvariant_cast<QList<int>>(rowsToHide); + const QList<int> columnsToHideList = qvariant_cast<QList<int>>(columnsToHide); + const int modelSize = 5; + auto model = TestModelAsVariant(modelSize, modelSize); + view->rootObject()->setProperty("rowsToHide", rowsToHide); + view->rootObject()->setProperty("columnsToHide", columnsToHide); + + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + const int expectedRowCount = modelSize - rowsToHideList.count(); + const int expectedColumnCount = modelSize - columnsToHideList.count(); + QCOMPARE(tableViewPrivate->loadedRows.count(), expectedRowCount); + QCOMPARE(tableViewPrivate->loadedColumns.count(), expectedColumnCount); + + for (const int row : tableViewPrivate->loadedRows.keys()) + QVERIFY(!rowsToHideList.contains(row)); + + for (const int column : tableViewPrivate->loadedColumns.keys()) + QVERIFY(!columnsToHideList.contains(column)); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index f32da44daa..ce2a8eb257 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -1262,6 +1262,34 @@ void tst_qquicktextedit::persistentSelection() edit->setFocus(true); QCOMPARE(edit->property("selected").toString(), QLatin1String("ell")); + // QTBUG-50587 (persistentSelection with readOnly) + edit->setReadOnly(true); + + edit->setPersistentSelection(false); + QCOMPARE(edit->persistentSelection(), false); + QCOMPARE(spy.count(), 2); + + edit->select(1, 4); + QCOMPARE(edit->property("selected").toString(), QLatin1String("ell")); + + edit->setFocus(false); + QCOMPARE(edit->property("selected").toString(), QString()); + + edit->setFocus(true); + QCOMPARE(edit->property("selected").toString(), QString()); + + edit->setPersistentSelection(true); + QCOMPARE(edit->persistentSelection(), true); + QCOMPARE(spy.count(), 3); + + edit->select(1, 4); + QCOMPARE(edit->property("selected").toString(), QLatin1String("ell")); + + edit->setFocus(false); + QCOMPARE(edit->property("selected").toString(), QLatin1String("ell")); + + edit->setFocus(true); + QCOMPARE(edit->property("selected").toString(), QLatin1String("ell")); } void tst_qquicktextedit::selectionOnFocusOut() diff --git a/tests/auto/quick/qquicktextinput/BLACKLIST b/tests/auto/quick/qquicktextinput/BLACKLIST deleted file mode 100644 index e9f4f11c58..0000000000 --- a/tests/auto/quick/qquicktextinput/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# QTBUG-41895 -[tripleClickSelectsAll] -windows diff --git a/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml b/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml new file mode 100644 index 0000000000..44c157b824 --- /dev/null +++ b/tests/auto/quick/qquickvisualdatamodel/data/externalManagedModel.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tests of the QtQuick module 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.Window 2.2 +import QtQuick 2.6 +import QtQml.Models 2.11 +import example 1.0 + +Window { + visible: true + property bool running: rebuildTimer.running + ListView { + anchors.fill: parent + model: delegateModel + } + + DelegateModel { + id: delegateModel + model: objectsProvider.objects + delegate: Item {} + } + + Timer { + id: rebuildTimer + running: true + repeat: true + interval: 1 + + property int count: 0 + onTriggered: { + objectsProvider.rebuild(); + if (++count === 10) + running = false; + } + } + + ObjectsProvider { + id: objectsProvider + } +} diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 1b8654ecdd..fac8283e2c 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -431,6 +431,7 @@ private slots: void asynchronousMove_data(); void asynchronousCancel(); void invalidContext(); + void externalManagedModel(); private: template <int N> void groups_verify( @@ -4234,6 +4235,73 @@ void tst_qquickvisualdatamodel::invalidContext() QVERIFY(!item); } +class ObjectsProvider : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<QObject> objects READ objects NOTIFY objectsChanged) + +public: + explicit ObjectsProvider(QObject *parent = nullptr) : QObject(parent) {} + + Q_INVOKABLE void rebuild() + { + for (auto old: m_objects) + old->deleteLater(); + + m_objects.clear(); + emit objectsChanged(); + + const int size = std::rand() & 0xff; + for (int i = 0; i < size; ++i) { + auto newElement = new QObject(this); + QQmlEngine::setObjectOwnership(newElement, QQmlEngine::CppOwnership); + m_objects.push_back(newElement); + } + emit objectsChanged(); + } + + Q_INVOKABLE QQmlListProperty<QObject> objects() + { + return QQmlListProperty<QObject>(this, nullptr, &ObjectsProvider::listLength, + &ObjectsProvider::listAt); + } + + static int listLength(QQmlListProperty<QObject> *property) + { + auto objectsProvider = qobject_cast<ObjectsProvider*>(property->object); + return objectsProvider ? objectsProvider->m_objects.length() : 0; + } + + static QObject* listAt(QQmlListProperty<QObject> *property, int index) + { + auto objectsProvider = qobject_cast<ObjectsProvider*>(property->object); + return objectsProvider ? objectsProvider->m_objects.at(index) : nullptr; + } + +signals: + void objectsChanged(); + +private: + QList<QObject *> m_objects; +}; + +void tst_qquickvisualdatamodel::externalManagedModel() +{ + qmlRegisterType<ObjectsProvider>("example", 1, 0, "ObjectsProvider"); + + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("externalManagedModel.qml")); + QVERIFY(component.isReady()); + + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); + + QVERIFY(object->property("running").toBool()); + + // Make sure it runs to completion without crashing. + QTRY_VERIFY(!object->property("running").toBool()); +} + QTEST_MAIN(tst_qquickvisualdatamodel) #include "tst_qquickvisualdatamodel.moc" diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 3bfa23173e..5631ffe047 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -38,6 +38,7 @@ #include <private/qquickwindow_p.h> #include <private/qquickitemview_p_p.h> +QT_BEGIN_NAMESPACE QQuickView *QQuickViewTestUtil::createView() { @@ -451,3 +452,32 @@ namespace QQuickTouchUtils { } } + +namespace QQuickTest { + // Initialize view, set Url, center in available geometry, move mouse away if desired + bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage) + { + v.setBaseSize(QSize(240,320)); + v.setSource(url); + while (v.status() == QQuickView::Loading) + QTest::qWait(10); + if (v.status() != QQuickView::Ready) { + foreach (const QQmlError &e, v.errors()) + errorMessage->append(e.toString().toLocal8Bit() + '\n'); + return false; + } + const QRect screenGeometry = v.screen()->availableGeometry(); + const QSize size = v.size(); + const QPoint offset = QPoint(size.width() / 2, size.height() / 2); + v.setFramePosition(screenGeometry.center() - offset); + #if QT_CONFIG(cursor) // Get the cursor out of the way. + if (moveMouseOut) + QCursor::setPos(v.geometry().topRight() + QPoint(100, 100)); + #else + Q_UNUSED(moveMouseOut) + #endif + return true; + } +} + +QT_END_NAMESPACE diff --git a/tests/auto/quick/shared/viewtestutil.h b/tests/auto/quick/shared/viewtestutil.h index 04e1771ef8..5e725fdf11 100644 --- a/tests/auto/quick/shared/viewtestutil.h +++ b/tests/auto/quick/shared/viewtestutil.h @@ -37,6 +37,8 @@ QT_FORWARD_DECLARE_CLASS(QQuickView) QT_FORWARD_DECLARE_CLASS(QQuickItemViewPrivate) QT_FORWARD_DECLARE_CLASS(FxViewItem) +QT_BEGIN_NAMESPACE + namespace QQuickViewTestUtil { QQuickView *createView(); @@ -185,6 +187,12 @@ namespace QQuickTouchUtils { void flush(QQuickWindow *window); } +namespace QQuickTest { + bool initView(QQuickView &v, const QUrl &url, bool moveMouseOut, QByteArray *errorMessage); +} + +QT_END_NAMESPACE + Q_DECLARE_METATYPE(QQuickViewTestUtil::QaimModel*) Q_DECLARE_METATYPE(QQuickViewTestUtil::ListChange) Q_DECLARE_METATYPE(QList<QQuickViewTestUtil::ListChange>) diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_height.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_height.qml index c4321d25bb..1e2885baed 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_height.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_height.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_width.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_width.qml index 328ff40008..437f10dd97 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_width.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_borders_exceed_width.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml index 804567cf19..12c600c4af 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_smoothed.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml index b10554ad66..c3d4e5b018 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml index 73cc53ed2b..5d645902ec 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_border_overlap.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml index 8356f02614..153b77d642 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_negative_borders.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml index 9213589648..687a54ee80 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_borders.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml index 615abdee20..70bd5c0f14 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_bottom.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml index 58ed4d44dc..98d2b867b8 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_center.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml index 7e0045bf24..64cd9d30cc 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_left.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml index 04c2d021f0..6d63475cfd 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml index 5210bab321..6d2193633a 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_right_left.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml index 2e89496c92..c3c8dfb7f4 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml index 4388601f13..a32827cfc3 100644 --- a/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml +++ b/tests/manual/scenegraph_lancelot/data/borderimages/borderimage_rotated_unsmoothed_no_top_bottom.qml @@ -4,7 +4,7 @@ Rectangle { width: 320 height: 480 - color: "red" + color: "#C0FEFE" Item { x: 80 diff --git a/tools/qmleasing/mainwindow.cpp b/tools/qmleasing/mainwindow.cpp index c1a87642a5..679b4c0b91 100644 --- a/tools/qmleasing/mainwindow.cpp +++ b/tools/qmleasing/mainwindow.cpp @@ -82,7 +82,7 @@ MainWindow::MainWindow(QWidget *parent) : splineEditor->setPreset(ui_properties.comboBox->currentText()); QVBoxLayout *groupBoxLayout = new QVBoxLayout(ui_properties.groupBox); - groupBoxLayout->setMargin(0); + groupBoxLayout->setContentsMargins(QMargins()); ui_properties.groupBox->setLayout(groupBoxLayout); groupBoxLayout->addWidget(splineEditor->pointListWidget()); diff --git a/tools/qmleasing/segmentproperties.cpp b/tools/qmleasing/segmentproperties.cpp index f37527f863..c61feef9a4 100644 --- a/tools/qmleasing/segmentproperties.cpp +++ b/tools/qmleasing/segmentproperties.cpp @@ -33,7 +33,7 @@ SegmentProperties::SegmentProperties(QWidget *parent) : QWidget(parent), m_splineEditor(nullptr), m_blockSignals(false) { QVBoxLayout *layout = new QVBoxLayout(this); - layout->setMargin(0); + layout->setContentsMargins(QMargins()); layout->setSpacing(2); setLayout(layout); { diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp index 2a6081903f..69850dc7a1 100644 --- a/tools/qmleasing/splineeditor.cpp +++ b/tools/qmleasing/splineeditor.cpp @@ -524,7 +524,7 @@ void SplineEditor::setupPointListWidget() m_pointListWidget->setWidget(new QWidget(m_pointListWidget)); QVBoxLayout *layout = new QVBoxLayout(m_pointListWidget->widget()); - layout->setMargin(0); + layout->setContentsMargins(QMargins()); layout->setSpacing(2); m_pointListWidget->widget()->setLayout(layout); |