diff options
Diffstat (limited to 'src/quick/items')
23 files changed, 624 insertions, 355 deletions
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h index ffb01563ed..dcfa863553 100644 --- a/src/quick/items/context2d/qquickcanvascontext_p.h +++ b/src/quick/items/context2d/qquickcanvascontext_p.h @@ -56,10 +56,14 @@ QT_REQUIRE_CONFIG(quick_canvas); #include <QtQuick/qquickitem.h> -#include <QtQml/private/qv4value_p.h> +#include <QtQml/private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE +namespace QV4 { + struct ExecutionEngine; +} + class QQuickCanvasItem; class QSGLayer; diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index 1972a9a507..11e1ec3fb2 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -67,6 +67,7 @@ class QQuickCanvasContext; class QQuickCanvasItemPrivate; class QQuickPixmap; +class QQmlV4Function; class QQuickCanvasPixmap : public QQmlRefCount { diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index ea1354725f..d769c4396b 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(quick_canvas); #include <QtCore/qqueue.h> #include <QtCore/QWaitCondition> -#include <private/qv4value_p.h> #include <private/qv4persistent_p.h> //#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose! @@ -78,6 +77,10 @@ QT_REQUIRE_CONFIG(quick_canvas); QT_BEGIN_NAMESPACE +namespace QV4 { + struct ExecutionEngine; +} + class QQuickContext2DCommandBuffer; class QQuickContext2DTexture; class QQuickPixmap; diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 0b593c0089..730bbe4404 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -406,9 +406,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role) m_state.focusable = true; break; case QAccessible::StaticText: - if (!m_stateExplicitlySet.readOnly) { + if (!m_stateExplicitlySet.readOnly) m_state.readOnly = true; - } + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; break; default: break; diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index e17a28d07e..a84af80ee9 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -73,11 +73,11 @@ class QQuickDragGrabber class Item : public QQmlGuard<QQuickItem> { public: - Item(QQuickItem *item) : QQmlGuard<QQuickItem>(item) {} + Item(QQuickItem *item) : QQmlGuard<QQuickItem>(Item::objectDestroyedImpl, item) {} QIntrusiveListNode node; - protected: - void objectDestroyed(QQuickItem *) override { delete this; } + private: + static void objectDestroyedImpl(QQmlGuardImpl *guard) { delete static_cast<Item *>(guard); } }; typedef QIntrusiveList<Item, &Item::node> ItemList; diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h index 41f682fe57..3cf91cdc8f 100644 --- a/src/quick/items/qquickimplicitsizeitem_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p.h @@ -51,8 +51,8 @@ // We mean it. // -#include "qquickpainteditem.h" #include <private/qtquickglobal_p.h> +#include <QtQuick/qquickitem.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 197d891b2a..2fdb0f00c8 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -477,6 +477,7 @@ private: friend class QSGRenderer; friend class QAccessibleQuickItem; friend class QQuickAccessibleAttached; + friend class QQuickAnchorChanges; Q_DISABLE_COPY(QQuickItem) Q_DECLARE_PRIVATE(QQuickItem) }; diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index bde39d99bd..ea1aa7b0c9 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -1041,9 +1041,15 @@ void QQuickLoaderPrivate::createComponent() const QQmlComponent::CompilationMode mode = asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous; - QQmlContext *context = qmlContext(q); - component.setObject(new QQmlComponent( - context->engine(), context->resolvedUrl(source), mode, q), q); + if (QQmlContext *context = qmlContext(q)) { + if (QQmlEngine *engine = context->engine()) { + component.setObject(new QQmlComponent( + engine, context->resolvedUrl(source), mode, q), q); + return; + } + } + + qmlWarning(q) << "createComponent: Cannot find a QML engine."; } #include <moc_qquickloader_p.cpp> diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h index 1d60f2845f..c6216ccc7c 100644 --- a/src/quick/items/qquickloader_p.h +++ b/src/quick/items/qquickloader_p.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QQuickLoaderPrivate; +class QQmlV4Function; class Q_QUICK_PRIVATE_EXPORT QQuickLoader : public QQuickImplicitSizeItem { Q_OBJECT diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index b178803c7d..dac9e2cb59 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -56,12 +56,14 @@ #include "qquickitemchangelistener_p.h" #include <qqmlincubator.h> -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> +#include <private/qv4persistent_p.h> QT_BEGIN_NAMESPACE class QQuickLoaderPrivate; +class QQmlV4Function; class QQuickLoaderIncubator : public QQmlIncubator { public: diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 1f690f1e85..5d29408b06 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -331,6 +331,7 @@ bool QQuickRenderControl::initialize() params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio(); params.maybeSurface = d->window; renderContext->initialize(¶ms); + d->initialized = true; } else { qWarning("QRhi is only compatible with default adaptation"); return false; diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h index 38e46b10d4..8f16940ea3 100644 --- a/src/quick/items/qquickrendercontrol.h +++ b/src/quick/items/qquickrendercontrol.h @@ -81,7 +81,7 @@ public: QQuickWindow *window() const; protected: - QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr); + explicit QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr); Q_SIGNALS: void renderRequested(); diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index 54517e1ed6..719c682769 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -482,12 +482,12 @@ void QQuickParentChangePrivate::reverseRewindHelper(const std::unique_ptr<QQuick { if (!target || !snapshot) return; - auto targetPriv = QQuickItemPrivate::get(target); + // leave existing bindings alive; new bindings are applied in applyBindings - targetPriv->x.setValueBypassingBindings(snapshot->x); - targetPriv->y.setValueBypassingBindings(snapshot->y); - targetPriv->width.setValueBypassingBindings(snapshot->width); - targetPriv->height.setValueBypassingBindings(snapshot->height); + // setPosition and setSize update the geometry without invalidating bindings + target->setPosition(QPointF(snapshot->x, snapshot->y)); + target->setSize(QSizeF(snapshot->width, snapshot->height)); + target->setScale(snapshot->scale); target->setRotation(snapshot->rotation); target->setParentItem(snapshot->parent); @@ -1094,6 +1094,7 @@ void QQuickAnchorChanges::reverse() QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask; QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask; + const QRectF oldGeometry(d->target->position(), d->target->size()); bool stateSetWidth = (stateHAnchors && stateHAnchors != QQuickAnchors::LeftAnchor && stateHAnchors != QQuickAnchors::RightAnchor && @@ -1102,8 +1103,11 @@ void QQuickAnchorChanges::reverse() origHAnchors != QQuickAnchors::LeftAnchor && origHAnchors != QQuickAnchors::RightAnchor && origHAnchors != QQuickAnchors::HCenterAnchor); - if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) - d->target->setWidth(d->origWidth.value); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth && !qt_is_nan(d->origWidth)) { + targetPrivate->widthValidFlag = true; + if (targetPrivate->width != d->origWidth) + targetPrivate->width.setValueBypassingBindings(d->origWidth); + } bool stateSetHeight = (stateVAnchors && stateVAnchors != QQuickAnchors::TopAnchor && @@ -1115,14 +1119,23 @@ void QQuickAnchorChanges::reverse() origVAnchors != QQuickAnchors::BottomAnchor && origVAnchors != QQuickAnchors::VCenterAnchor && origVAnchors != QQuickAnchors::BaselineAnchor); - if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) - d->target->setHeight(d->origHeight.value); + if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) { + targetPrivate->heightValidFlag = true; + if (targetPrivate->height != d->origHeight) + targetPrivate->height.setValueBypassingBindings(d->origHeight); + } - if (stateHAnchors && !origHAnchors) - d->target->setX(d->origX); + if (stateHAnchors && !origHAnchors && !qt_is_nan(d->origX) && d->origX != targetPrivate->x) + targetPrivate->x.setValueBypassingBindings(d->origX); - if (stateVAnchors && !origVAnchors) - d->target->setY(d->origY); + if (stateVAnchors && !origVAnchors && !qt_is_nan(d->origY) && d->origY != targetPrivate->y) + targetPrivate->y.setValueBypassingBindings(d->origY); + + const QRectF newGeometry(d->target->position(), d->target->size()); + if (newGeometry != oldGeometry) { + targetPrivate->dirty(QQuickItemPrivate::Position); + d->target->geometryChange(newGeometry, oldGeometry); + } } QQuickStateActionEvent::EventType QQuickAnchorChanges::type() const @@ -1316,15 +1329,31 @@ void QQuickAnchorChanges::rewind() return; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + const QRectF oldGeometry(d->target->position(), d->target->size()); + + // Restore previous values (but not previous bindings, i.e. anchors). + // Also, don't drop any new bindings. + if (!qt_is_nan(d->rewindX) && d->rewindX != targetPrivate->x) + targetPrivate->x.setValueBypassingBindings(d->rewindX); + if (!qt_is_nan(d->rewindY) && d->rewindY != targetPrivate->y) + targetPrivate->y.setValueBypassingBindings(d->rewindY); + + if (targetPrivate->widthValid() && !qt_is_nan(d->rewindWidth)) { + targetPrivate->widthValidFlag = true; + if (d->rewindWidth != targetPrivate->width) + targetPrivate->width.setValueBypassingBindings(d->rewindWidth); + } - //restore previous values (but not previous bindings, i.e. anchors) - d->target->setX(d->rewindX); - d->target->setY(d->rewindY); - if (targetPrivate->widthValid()) { - d->target->setWidth(d->rewindWidth); + if (targetPrivate->heightValid() && !qt_is_nan(d->rewindHeight)) { + targetPrivate->heightValidFlag = true; + if (d->rewindHeight != targetPrivate->height) + targetPrivate->height.setValueBypassingBindings(d->rewindHeight); } - if (targetPrivate->heightValid()) { - d->target->setHeight(d->rewindHeight); + + const QRectF newGeometry(d->target->position(), d->target->size()); + if (newGeometry != oldGeometry) { + targetPrivate->dirty(QQuickItemPrivate::Position); + d->target->geometryChange(newGeometry, oldGeometry); } } diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 4ab8c954d1..adde42321c 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -556,8 +556,8 @@ /*! \qmlmethod Point QtQuick::TableView::cellAtPos(point position, bool includeSpacing) - Returns the cell at the given \a position in the view. If no cell intersects with - \a position, the return value will be \c point(-1, -1). + Returns the cell at the given \a position in the view. If no \l {isRowLoaded()}{loaded} + cell intersects with \a position, the return value will be \c point(-1, -1). If \a includeSpacing is set to \c true, a cell's bounding box will be considered to include half the adjacent \l rowSpacing and \l columnSpacing on each side. The @@ -643,6 +643,66 @@ */ /*! + \qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column) + \since 6.4 + + Returns the \l QModelIndex that maps to \a row and \a column in the view. + + \a row and \a column should be the row and column in the view (table row and + table column), and not a row and column in the model. + + \sa rowAtIndex(), columnAtIndex() +*/ + +/*! + \qmlmethod QModelIndex QtQuick::TableView::modelIndex(point cell) + \since 6.4 + + Convenience function for doing: + \code + modelIndex(cell.y, cell.x) + \endcode + + A cell is simply a \l point that combines row and column into + a single type. Note that \c point.x will map to the column, and + \c point.y will map to the row. +*/ + +/*! + \qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex) + \since 6.4 + + Returns the row in the view that maps to \a modelIndex in the model. + + \sa columnAtIndex(), modelIndex() +*/ + +/*! + \qmlmethod int QtQuick::TableView::columnAtIndex(QModelIndex modelIndex) + \since 6.4 + + Returns the column in the view that maps to \a modelIndex in the model. + + \sa rowAtIndex(), modelIndex() +*/ + +/*! + \qmlmethod point QtQuick::TableView::cellAtIndex(QModelIndex modelIndex) + \since 6.4 + + Returns the cell in the view that maps to \a modelIndex in the model. + Convenience function for doing: + + \code + Qt.point(columnAtIndex(modelIndex), rowAtIndex(modelIndex)) + \endcode + + A cell is simply a \l point that combines row and column into + a single type. Note that \c point.x will map to the column, and + \c point.y will map to the row. +*/ + +/*! \qmlattachedproperty TableView QtQuick::TableView::view This attached property holds the view that manages the delegate instance. @@ -688,10 +748,9 @@ 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; -static const char* kRequiredProperty = "_qt_isrequiredpropery_selected"; +static const char* kRequiredProperties = "_qt_tableview_requiredpropertymask"; +static const char* kRequiredProperty_selected = "selected"; const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0); const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0); @@ -782,6 +841,40 @@ void QQuickTableViewPrivate::dumpTable() const qWarning() << "Window capture saved to:" << path; } +void QQuickTableViewPrivate::setRequiredProperty(const char *property, + const QVariant &value, int serializedModelIndex, QObject *object, bool init) +{ + if (!qobject_cast<QQmlTableInstanceModel *>(model)) { + // TableView only supports using required properties when backed by + // a QQmlTableInstanceModel. This is almost always the case, except + // if you assign it an ObjectModel or a DelegateModel (which are really + // not supported by TableView, it expects a QAIM). + return; + } + + // Attaching a property list to the delegate item is just a + // work-around until QMetaProperty::isRequired() works (QTBUG-98846). + const QString propertyName = QString::fromUtf8(property); + + if (init) { + const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value); + if (wasRequired) { + QStringList propertyList = object->property(kRequiredProperties).toStringList(); + object->setProperty(kRequiredProperties, propertyList << propertyName); + } + } else { + const QStringList propertyList = object->property(kRequiredProperties).toStringList(); + if (!propertyList.contains(propertyName)) { + // We only write to properties that are required + return; + } + const auto metaObject = object->metaObject(); + const int propertyIndex = metaObject->indexOfProperty(property); + const auto metaProperty = metaObject->property(propertyIndex); + metaProperty.write(object, value); + } +} + QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const { return const_cast<QQuickTableView *>(q_func())->contentItem(); @@ -993,7 +1086,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c const bool outsideBottom = pos.y() >= viewportRect.bottom() - 1; if (outsideLeft) { - const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd; + const bool firstColumnLoaded = atTableEnd(Qt::LeftEdge); const qreal remainingDist = viewportRect.left() - loadedTableOuterRect.left(); if (remainingDist > 0 || !firstColumnLoaded) { qreal stepX = step.width(); @@ -1003,7 +1096,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c dist.setWidth(pos.x() - viewportRect.left() - 1); } } else if (outsideRight) { - const bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd; + const bool lastColumnLoaded = atTableEnd(Qt::RightEdge); const qreal remainingDist = loadedTableOuterRect.right() - viewportRect.right(); if (remainingDist > 0 || !lastColumnLoaded) { qreal stepX = step.width(); @@ -1015,7 +1108,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c } if (outsideTop) { - const bool firstRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd; + const bool firstRowLoaded = atTableEnd(Qt::TopEdge); const qreal remainingDist = viewportRect.top() - loadedTableOuterRect.top(); if (remainingDist > 0 || !firstRowLoaded) { qreal stepY = step.height(); @@ -1025,7 +1118,7 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c dist.setHeight(pos.y() - viewportRect.top() - 1); } } else if (outsideBottom) { - const bool lastRowLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd; + const bool lastRowLoaded = atTableEnd(Qt::BottomEdge); const qreal remainingDist = loadedTableOuterRect.bottom() - viewportRect.bottom(); if (remainingDist > 0 || !lastRowLoaded) { qreal stepY = step.height(); @@ -1081,14 +1174,13 @@ int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex) { // Convert QModelIndex to cell index. A cell index is just an // integer representation of a cell instead of using a QPoint. - if (modelIndex.parent().isValid()) { - // TableView only uses the root items of the model + const QPoint cell = q_func()->cellAtIndex(modelIndex); + if (!cellIsValid(cell)) return -1; - } - return modelIndexAtCell(QPoint(modelIndex.column(), modelIndex.row())); + return modelIndexAtCell(cell); } -int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) +int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) const { return int(log2(float(edge))); } @@ -1102,7 +1194,7 @@ void QQuickTableViewPrivate::clearEdgeSizeCache() cachedNextVisibleEdgeIndex[edgeToArrayIndex(edge)].startIndex = kEdgeIndexNotSet; } -int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) +int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const { // Find the next column (or row) around the loaded table that is // visible, and should be loaded next if the content item moves. @@ -1117,7 +1209,7 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) return nextVisibleEdgeIndex(edge, startIndex); } -int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) +int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const { // First check if we have already searched for the first visible index // after the given startIndex recently, and if so, return the cached result. @@ -1200,28 +1292,6 @@ int QQuickTableViewPrivate::nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) return foundIndex; } -bool QQuickTableViewPrivate::allColumnsLoaded() -{ - // Returns true if all the columns in the model (that are not - // hidden by the columnWidthProvider) are currently loaded and visible. - const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd; - if (!firstColumnLoaded) - return false; - bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) == kEdgeIndexAtEnd; - return lastColumnLoaded; -} - -bool QQuickTableViewPrivate::allRowsLoaded() -{ - // Returns true if all the rows in the model (that are not hidden - // by the columnWidthProvider) are currently loaded and visible. - const bool firstColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd; - if (!firstColumnLoaded) - return false; - bool lastColumnLoaded = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) == kEdgeIndexAtEnd; - return lastColumnLoaded; -} - void QQuickTableViewPrivate::updateContentWidth() { // Note that we actually never really know what the content size / size of the full table will @@ -1941,6 +2011,13 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column) return columnWidth; } +qreal QQuickTableViewPrivate::getEffectiveRowY(int row) const +{ + // Return y pos of row after layout + Q_TABLEVIEW_ASSERT(loadedRows.contains(row), row); + return loadedTableItem(QPoint(leftColumn(), row))->geometry().y(); +} + qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const { // Return row height after layout @@ -1948,6 +2025,13 @@ qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const return loadedTableItem(QPoint(leftColumn(), row))->geometry().height(); } +qreal QQuickTableViewPrivate::getEffectiveColumnX(int column) const +{ + // Return x pos of column after layout + Q_TABLEVIEW_ASSERT(loadedColumns.contains(column), column); + return loadedTableItem(QPoint(column, topRow()))->geometry().x(); +} + qreal QQuickTableViewPrivate::getEffectiveColumnWidth(int column) const { // Return column width after layout @@ -1989,7 +2073,7 @@ qreal QQuickTableViewPrivate::getRowLayoutHeight(int row) return rowHeight; } -qreal QQuickTableViewPrivate::getColumnWidth(int column) +qreal QQuickTableViewPrivate::getColumnWidth(int column) const { // 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 @@ -2030,7 +2114,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) return columnWidth; } -qreal QQuickTableViewPrivate::getRowHeight(int row) +qreal QQuickTableViewPrivate::getRowHeight(int row) const { // 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 @@ -2071,14 +2155,14 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) return rowHeight; } -bool QQuickTableViewPrivate::isColumnHidden(int column) +bool QQuickTableViewPrivate::isColumnHidden(int column) const { // 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) +bool QQuickTableViewPrivate::isRowHidden(int row) const { // A row is hidden if the height is explicit set to zero (either by // using a rowHeightProvider, or by overriding getRowHeight()). @@ -2336,18 +2420,25 @@ void QQuickTableViewPrivate::processRebuildTable() return; } + if (rebuildState == RebuildState::CancelOvershoot) { + cancelOvershootAfterLayout(); + loadAndUnloadVisibleEdges(); + if (!moveToNextRebuildState()) + return; + } + const bool preload = (rebuildOptions & RebuildOption::All && reusableFlag == QQmlTableInstanceModel::Reusable); if (rebuildState == RebuildState::PreloadColumns) { - if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge) != kEdgeIndexAtEnd) + if (preload && !atTableEnd(Qt::RightEdge)) loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested); if (!moveToNextRebuildState()) return; } if (rebuildState == RebuildState::PreloadRows) { - if (preload && nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge) != kEdgeIndexAtEnd) + if (preload && !atTableEnd(Qt::BottomEdge)) loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested); if (!moveToNextRebuildState()) return; @@ -2585,12 +2676,14 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() relayoutTableItems(); syncLoadedTableRectFromLoadedTable(); - if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) { + const bool allColumnsLoaded = atTableEnd(Qt::LeftEdge) && atTableEnd(Qt::RightEdge); + if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded) { updateAverageColumnWidth(); updateContentWidth(); } - if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) { + const bool allRowsLoaded = atTableEnd(Qt::TopEdge) && atTableEnd(Qt::BottomEdge); + if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded) { updateAverageRowHeight(); updateContentHeight(); } @@ -2670,6 +2763,32 @@ void QQuickTableViewPrivate::adjustViewportYAccordingToAlignment() syncViewportRect(); } +void QQuickTableViewPrivate::cancelOvershootAfterLayout() +{ + Q_Q(QQuickTableView); + + // Note: we only want to cancel overshoot from a rebuild if we're supposed to position + // the view on a specific cell. The app is allowed to overshoot by setting contentX and + // contentY manually. Also, if this view is a sync child, we should always stay in sync + // with the syncView, so then we don't do anything. + const bool positionVertically = rebuildOptions.testFlag(RebuildOption::PositionViewAtRow); + const bool positionHorizontally = rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn); + const bool cancelVertically = positionVertically && !syncVertically; + const bool cancelHorizontally = positionHorizontally && !syncHorizontally; + + if (cancelHorizontally && !qFuzzyIsNull(q->horizontalOvershoot())) { + qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot horizontally:" << q->horizontalOvershoot(); + setLocalViewportX(q->horizontalOvershoot() < 0 ? -q->minXExtent() : -q->maxXExtent()); + syncViewportRect(); + } + + if (cancelVertically && !qFuzzyIsNull(q->verticalOvershoot())) { + qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot vertically:" << q->verticalOvershoot(); + setLocalViewportY(q->verticalOvershoot() < 0 ? -q->minYExtent() : -q->maxYExtent()); + syncViewportRect(); + } +} + void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) { Q_Q(QQuickTableView); @@ -2728,7 +2847,7 @@ void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMo processLoadRequest(); } -void QQuickTableViewPrivate::loadAndUnloadVisibleEdges() +void QQuickTableViewPrivate::loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode) { // Unload table edges that have been moved outside the visible part of the // table (including buffer area), and load new edges that has been moved inside. @@ -2765,7 +2884,7 @@ void QQuickTableViewPrivate::loadAndUnloadVisibleEdges() if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) { tableModified = true; - loadEdge(edge, QQmlIncubator::AsynchronousIfNested); + loadEdge(edge, incubationMode); if (loadRequest.isActive()) return; } @@ -2970,11 +3089,10 @@ bool QQuickTableViewPrivate::selectedInSelectionModel(const QPoint &cell) const if (!model) return false; - const QModelIndex modelIndex = model->index(cell.y(), cell.x()); - return selectionModel->isSelected(modelIndex); + return selectionModel->isSelected(q_func()->modelIndex(cell)); } -void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const +void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) { const auto &selectedIndexes = selected.indexes(); const auto &deselectedIndexes = deselected.indexes(); @@ -2984,40 +3102,25 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti setSelectedOnDelegateItem(deselectedIndexes.at(i), false); } -void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems() const -{ - for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) { - const QPoint cell = cellAtModelIndex(*it); - const bool selected = selectedInSelectionModel(cell); - setSelectedOnDelegateItem(loadedTableItem(cell)->item, selected); - } -} - -void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const +void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) { const int cellIndex = modelIndexToCellIndex(modelIndex); if (!loadedItems.contains(cellIndex)) return; const QPoint cell = cellAtModelIndex(cellIndex); - setSelectedOnDelegateItem(loadedTableItem(cell)->item, select); + QQuickItem *item = loadedTableItem(cell)->item; + setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false); } -void QQuickTableViewPrivate::setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const +void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems() { - if (!delegateItem->property(kRequiredProperty).toBool()) { - // We only assign to "selected" if it's a required property. Otherwise - // we assume (for backwards compatibility) that the property is used - // by the delegate for something else. - // Note: kRequiredProperty is a work-around until QMetaProperty::isRequired() works. - return; + for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) { + const int cellIndex = *it; + const QPoint cell = cellAtModelIndex(cellIndex); + const bool selected = selectedInSelectionModel(cell); + QQuickItem *item = loadedTableItem(cell)->item; + setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), cellIndex, item, false); } - - // Note that several delegates might be in use (in case of a DelegateChooser), and - // the delegate can also change. So we cannot cache the propertyIndex. - const auto metaObject = delegateItem->metaObject(); - const int propertyIndex = metaObject->indexOfProperty("selected"); - const auto metaProperty = metaObject->property(propertyIndex); - metaProperty.write(delegateItem, QVariant::fromValue(select)); } void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*) @@ -3048,14 +3151,7 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object) const QPoint cell = cellAtModelIndex(modelIndex); const bool selected = selectedInSelectionModel(cell); - - if (qobject_cast<QQmlTableInstanceModel *>(model)) { - const bool wasRequired = model->setRequiredProperty(modelIndex, QStringLiteral("selected"), selected); - if (wasRequired) { - // Work-around until QMetaProperty::isRequired() works - item->setProperty(kRequiredProperty, true); - } - } + setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, true); if (auto attached = getAttachedObject(object)) attached->setView(q); @@ -3071,10 +3167,9 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object) void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object) { - auto item = static_cast<QQuickItem*>(object); const QPoint cell = cellAtModelIndex(modelIndex); const bool selected = selectedInSelectionModel(cell); - setSelectedOnDelegateItem(item, selected); + setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, false); if (auto attached = getAttachedObject(object)) emit attached->reused(); @@ -3886,27 +3981,29 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing) { Q_D(const QQuickTableView); - if (!boundingRect().contains(position)) + const QPointF localPos = mapToItem(d->contentItem, position); + if (!d->loadedTableOuterRect.contains(localPos)) return QPoint(-1, -1); const qreal hSpace = d->cellSpacing.width(); const qreal vSpace = d->cellSpacing.height(); - qreal currentColumnEnd = d->loadedTableOuterRect.x() - contentX(); - qreal currentRowEnd = d->loadedTableOuterRect.y() - contentY(); + qreal currentColumnEnd = d->loadedTableOuterRect.x(); + qreal currentRowEnd = d->loadedTableOuterRect.y(); + int foundColumn = -1; int foundRow = -1; for (const int column : d->loadedColumns) { currentColumnEnd += d->getEffectiveColumnWidth(column); - if (position.x() < currentColumnEnd) { + if (localPos.x() < currentColumnEnd) { foundColumn = column; break; } currentColumnEnd += hSpace; - if (!includeSpacing && position.x() < currentColumnEnd) { + if (!includeSpacing && localPos.x() < currentColumnEnd) { // Hit spacing return QPoint(-1, -1); - } else if (includeSpacing && position.x() < currentColumnEnd - (hSpace / 2)) { + } else if (includeSpacing && localPos.x() < currentColumnEnd - (hSpace / 2)) { foundColumn = column; break; } @@ -3914,16 +4011,16 @@ QPoint QQuickTableView::cellAtPos(const QPointF &position, bool includeSpacing) for (const int row : d->loadedRows) { currentRowEnd += d->getEffectiveRowHeight(row); - if (position.y() < currentRowEnd) { + if (localPos.y() < currentRowEnd) { foundRow = row; break; } currentRowEnd += vSpace; - if (!includeSpacing && position.y() < currentRowEnd) { + if (!includeSpacing && localPos.y() < currentRowEnd) { // Hit spacing return QPoint(-1, -1); } - if (includeSpacing && position.y() < currentRowEnd - (vSpace / 2)) { + if (includeSpacing && localPos.y() < currentRowEnd - (vSpace / 2)) { foundRow = row; break; } @@ -4000,6 +4097,41 @@ qreal QQuickTableView::implicitRowHeight(int row) const return d->sizeHintForRow(row); } +QModelIndex QQuickTableView::modelIndex(const QPoint &cell) const +{ + Q_D(const QQuickTableView); + if (cell.x() < 0 || cell.x() >= columns() || cell.y() < 0 || cell.y() >= rows()) + return {}; + + auto const qaim = d->model->abstractItemModel(); + if (!qaim) + return {}; + + return qaim->index(cell.y(), cell.x()); +} + +QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const +{ + if (!index.isValid() || index.parent().isValid()) + return {-1, -1}; + return {index.column(), index.row()}; +} + +QModelIndex QQuickTableView::modelIndex(int row, int column) const +{ + return modelIndex({column, row}); +} + +int QQuickTableView::rowAtIndex(const QModelIndex &index) const +{ + return cellAtIndex(index).y(); +} + +int QQuickTableView::columnAtIndex(const QModelIndex &index) const +{ + return cellAtIndex(index).x(); +} + void QQuickTableView::forceLayout() { d_func()->forceLayout(); @@ -4092,9 +4224,9 @@ void QQuickTableSectionSizeProvider::setSize(int section, qreal size) } // return -1.0 if no valid explicit size retrieved -qreal QQuickTableSectionSizeProvider::size(int section) +qreal QQuickTableSectionSizeProvider::size(int section) const { - Q_D(QQuickTableSectionSizeProvider); + Q_D(const QQuickTableSectionSizeProvider); auto it = d->hash.find(section); if (it != d->hash.end()) return *it; diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index 40a0fae7c1..830bef6c68 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -156,6 +156,12 @@ public: Q_REVISION(6, 2) Q_INVOKABLE qreal implicitColumnWidth(int column) const; Q_REVISION(6, 2) Q_INVOKABLE qreal implicitRowHeight(int row) const; + Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(const QPoint &cell) const; + Q_REVISION(6, 4) Q_INVOKABLE virtual QPoint cellAtIndex(const QModelIndex &index) const; + Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const; + Q_REVISION(6, 4) Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const; + Q_REVISION(6, 4) Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const; + static QQuickTableViewAttached *qmlAttachedProperties(QObject *); Q_SIGNALS: diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index bbab9f6a25..5bbdd07528 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -71,6 +71,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle) static const qreal kDefaultRowHeight = 50; static const qreal kDefaultColumnWidth = 50; +static const int kEdgeIndexNotSet = -2; +static const int kEdgeIndexAtEnd = -3; class FxTableItem; class QQuickTableSectionSizeProviderPrivate; @@ -81,7 +83,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableSectionSizeProvider : public QObject { public: QQuickTableSectionSizeProvider(QObject *parent=nullptr); void setSize(int section, qreal size); - qreal size(int section); + qreal size(int section) const; bool resetSize(int section); void resetAll(); @@ -202,6 +204,7 @@ public: VerifyTable, LayoutTable, LoadAndUnloadAfterLayout, + CancelOvershoot, PreloadColumns, PreloadRows, MovePreloadedItemsToPool, @@ -276,7 +279,7 @@ public: QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable; bool blockItemCreatedCallback = false; - bool layoutWarningIssued = false; + mutable bool layoutWarningIssued = false; bool polishing = false; bool syncVertically = false; bool syncHorizontally = false; @@ -295,9 +298,9 @@ public: QQuickTableSectionSizeProvider rowHeights; QQuickTableSectionSizeProvider columnWidths; - EdgeRange cachedNextVisibleEdgeIndex[4]; - EdgeRange cachedColumnWidth; - EdgeRange cachedRowHeight; + mutable EdgeRange cachedNextVisibleEdgeIndex[4]; + mutable EdgeRange cachedColumnWidth; + mutable 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 @@ -358,14 +361,16 @@ public: QSize calculateTableSize(); void updateTableSize(); - inline bool isColumnHidden(int column); - inline bool isRowHidden(int row); + inline bool isColumnHidden(int column) const; + inline bool isRowHidden(int row) const; qreal getColumnLayoutWidth(int column); qreal getRowLayoutHeight(int row); - qreal getColumnWidth(int column); - qreal getRowHeight(int row); + qreal getColumnWidth(int column) const; + qreal getRowHeight(int row) const; + qreal getEffectiveRowY(int row) const; qreal getEffectiveRowHeight(int row) const; + qreal getEffectiveColumnX(int column) const; qreal getEffectiveColumnWidth(int column) const; int topRow() const { return *loadedRows.cbegin(); } @@ -396,11 +401,11 @@ public: void syncLoadedTableFromLoadRequest(); void shiftLoadedTableRect(const QPointF newPosition); - int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex); - int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge); - bool allColumnsLoaded(); - bool allRowsLoaded(); - inline int edgeToArrayIndex(Qt::Edge edge); + int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex) const; + int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge) const; + inline bool atTableEnd(Qt::Edge edge) const { return nextVisibleEdgeIndexAroundLoadedTable(edge) == kEdgeIndexAtEnd; } + inline bool atTableEnd(Qt::Edge edge, int startIndex) const { return nextVisibleEdgeIndex(edge, startIndex) == kEdgeIndexAtEnd; } + inline int edgeToArrayIndex(Qt::Edge edge) const; void clearEdgeSizeCache(); bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const; @@ -421,7 +426,7 @@ public: void unloadItem(const QPoint &cell); void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode); void unloadEdge(Qt::Edge edge); - void loadAndUnloadVisibleEdges(); + void loadAndUnloadVisibleEdges(QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested); void drainReusePoolAfterLoadRequest(); void processLoadRequest(); @@ -433,6 +438,7 @@ public: void layoutAfterLoadingInitialTable(); void adjustViewportXAccordingToAlignment(); void adjustViewportYAccordingToAlignment(); + void cancelOvershootAfterLayout(); void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options); @@ -473,10 +479,9 @@ public: void syncViewportPosRecursive(); bool selectedInSelectionModel(const QPoint &cell) const; - void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const; - void updateSelectedOnAllDelegateItems() const; - void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const; - void setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const; + void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected); + void updateSelectedOnAllDelegateItems(); + void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select); void fetchMoreData(); @@ -486,6 +491,11 @@ public: inline QString tableLayoutToString() const; void dumpTable() const; + void setRequiredProperty(const char *property, + const QVariant &value, + int serializedModelIndex, + QObject *object, bool init); + // QQuickSelectable QQuickItem *selectionPointerHandlerTarget() const override; void setSelectionStartPos(const QPointF &pos) override; diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index f5dca6e0f5..66baa7ed7c 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -772,11 +772,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) const bool pixelSize = font.pixelSize() != -1; QString layoutText = layout.text(); - int largeFont = pixelSize ? font.pixelSize() : font.pointSize(); - int smallFont = fontSizeMode() != QQuickText::FixedSize - ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont) - : largeFont; - int scaledFontSize = largeFont; + const qreal minimumSize = pixelSize + ? static_cast<qreal>(minimumPixelSize()) + : minimumPointSize(); + qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF(); + qreal smallFont = fontSizeMode() != QQuickText::FixedSize + ? qMin<qreal>(minimumSize, largeFont) + : largeFont; + qreal scaledFontSize = largeFont; + const qreal sizeFittingThreshold(0.01); bool widthChanged = false; widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit); @@ -805,7 +809,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) if (pixelSize) scaledFont.setPixelSize(scaledFontSize); else - scaledFont.setPointSize(scaledFontSize); + scaledFont.setPointSizeF(scaledFontSize); if (layout.font() != scaledFont) layout.setFont(scaledFont); } @@ -1069,40 +1073,45 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) if (!horizontalFit && !verticalFit) break; + // Can't find a better fit + if (qFuzzyCompare(smallFont, largeFont)) + break; + // Try and find a font size that better fits the dimensions of the element. if (horizontalFit) { if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) { widthExceeded = true; - largeFont = scaledFontSize - 1; - if (smallFont > largeFont) - break; + largeFont = scaledFontSize; + scaledFontSize = (smallFont + largeFont) / 2; - if (pixelSize) - scaledFont.setPixelSize(scaledFontSize); - else - scaledFont.setPointSize(scaledFontSize); + continue; } else if (!verticalFit) { smallFont = scaledFontSize; - if (smallFont == largeFont) + + // Check to see if the current scaledFontSize is acceptable + if ((largeFont - smallFont) < sizeFittingThreshold) break; - scaledFontSize = (smallFont + largeFont + 1) / 2; + + scaledFontSize = (smallFont + largeFont) / 2; } } if (verticalFit) { if (truncateHeight || unelidedRect.height() > maxHeight) { heightExceeded = true; - largeFont = scaledFontSize - 1; - if (smallFont > largeFont) - break; + largeFont = scaledFontSize; + scaledFontSize = (smallFont + largeFont) / 2; } else { smallFont = scaledFontSize; - if (smallFont == largeFont) + + // Check to see if the current scaledFontSize is acceptable + if ((largeFont - smallFont) < sizeFittingThreshold) break; - scaledFontSize = (smallFont + largeFont + 1) / 2; + + scaledFontSize = (smallFont + largeFont) / 2; } } } diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp index 271df508de..e6cf4a8305 100644 --- a/src/quick/items/qquicktreeview.cpp +++ b/src/quick/items/qquicktreeview.cpp @@ -149,126 +149,129 @@ \note this function will not affect the model, only the visual representation in the view. - \sa collapse(), isExpanded() + \sa collapse(), isExpanded(), expandRecursively() */ /*! - \qmlmethod QtQuick::TreeView::collapse(row) + \qmlmethod QtQuick::TreeView::expandRecursively(row = -1, depth = -1) + \since 6.4 - Collapses the tree node at the given \a row in the view. + Expands the tree node at the given \a row in the view recursively down to + \a depth. \a depth should be relative to the depth of \a row. If + \a depth is \c -1, the tree will be expanded all the way down to all leaves. + + For a model that has more than one root, you can also call this function + with \a row equal to \c -1. This will expand all roots. Hence, calling + expandRecursively(-1, -1), or simply expandRecursively(), will expand + all nodes in the model. \a row should be the row in the view (table row), and not a row in the model. - \note this function will not affect the model, only - the visual representation in the view. + \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more} data. + \note This function will not affect the model, only the visual representation in the view. + \warning If the model contains a large number of items, this function will + take some time to execute. - \sa expand(), isExpanded() + \sa collapseRecursively(), expand(), collapse(), isExpanded(), depth() */ /*! - \qmlmethod QtQuick::TreeView::toggleExpanded(row) + \qmlmethod QtQuick::TreeView::expandToIndex(QModelIndex index) + \since 6.4 - Toggles if the tree node at the given \a row should be expanded. - This is a convenience for doing: + Expands the tree from the given model \a index, and recursively all the way up + to the root. The result will be that the delegate item that represents \a index + becomes visible in the view (unless it ends up outside the viewport). To + ensure that the row ends up visible in the viewport, you can do: \code - if (isExpanded(row)) - collapse(row) - else - expand(row) + expandToIndex(index) + forceLayout() + positionViewAtRow(rowAtIndex(index), Qt.AlignVCenter) \endcode - \a row should be the row in the view (table row), and not a row in the model. + \sa expand(), expandRecursively() */ /*! - \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(row, column) - - Returns the \l QModelIndex that maps to \a row and \a column in the view. - - \a row and \a column should be the row and column in the view (table row and - table column), and not a row and column in the model. - - The assigned model, which is a tree model, is converted to a flat table - model internally so that it can be shown in a TableView (which TreeView - inherits). This function can be used whenever you need to know which - index in the tree model maps to the given row and column in the view. + \qmlmethod QtQuick::TreeView::collapse(row) - \sa rowAtIndex(), columnAtIndex() -*/ + Collapses the tree node at the given \a row in the view. -/*! - \qmlmethod QModelIndex QtQuick::TreeView::modelIndex(point cell) + \a row should be the row in the view (table row), and not a row in the model. - Convenience function for doing: - \code - modelIndex(cell.y, cell.x) - \endcode + \note this function will not affect the model, only + the visual representation in the view. - A cell is simply a \l point that combines row and column into - a single type. Note that \c point.x will map to the column, and - \c point.y will map to the row. + \sa expand(), isExpanded() */ /*! - \qmlmethod int QtQuick::TreeView::rowAtIndex(modelIndex) - - Returns the row in the view that maps to \a modelIndex in the model. + \qmlmethod QtQuick::TreeView::collapseRecursively(row = -1) + \since 6.4 - The assigned model, which is a tree model, is converted to a flat table - model internally so that it can be shown in a TableView (which TreeView - inherits). This function can be used whenever you need to know which - row in the view the given model index maps to. + Collapses the tree node at the given \a row in the view recursively down to + all leaves. - \note \a modelIndex must be a \l QModelIndex. + For a model has more than one root, you can also call this function + with \a row equal to \c -1. This will collapse all roots. Hence, calling + collapseRecursively(-1), or simply collapseRecursively(), will collapse + all nodes in the model. - \sa columnAtIndex(), modelIndex() -*/ - -/*! - \qmlmethod int QtQuick::TreeView::columnAtIndex(modelIndex) - - Returns the column in the view that maps to \a modelIndex in the model. - - The assigned model, which is a tree model, is converted to a flat table - model internally so that it can be shown in a TableView (which TreeView - inherits). This function can be used whenever you need to know which - column in the view the given model index maps to. + \a row should be the row in the view (table row), and not a row in the model. - \note \a modelIndex must be a \l QModelIndex. + \note this function will not affect the model, only + the visual representation in the view. - \sa rowAtIndex(), modelIndex() + \sa expandRecursively(), expand(), collapse(), isExpanded(), depth() */ /*! - \qmlmethod point QtQuick::TreeView::cellAtIndex(modelIndex) + \qmlmethod QtQuick::TreeView::toggleExpanded(row) - Convenience function for doing: + Toggles if the tree node at the given \a row should be expanded. + This is a convenience for doing: - \c {Qt.point(columnAtIndex(}\a {modelIndex}\c{), rowAtIndex(}\a {modelIndex}\c{))} + \code + if (isExpanded(row)) + collapse(row) + else + expand(row) + \endcode - A cell is simply a \l point that combines row and column into - a single type. Note that \c point.x will map to the column, and - \c point.y will map to the row. + \a row should be the row in the view (table row), and not a row in the model. */ /*! - \qmlsignal QtQuick::TreeView::expanded(row) + \qmlsignal QtQuick::TreeView::expanded(row, depth) This signal is emitted when a \a row is expanded in the view. + \a row and \a depth will be equal to the arguments given to the call + that caused the expansion to happen (\l expand() or \l expandRecursively()). + In case of \l expand(), \a depth will always be \c 1. + In case of \l expandToIndex(), \a depth will be the depth of the + target index. + + \note when a row is expanded recursively, the expanded signal will + only be emitted for that one row, and not for its descendants. \sa collapsed(), expand(), collapse(), toggleExpanded() */ /*! - \qmlsignal QtQuick::TreeView::collapsed(row) + \qmlsignal QtQuick::TreeView::collapsed(row, recursively) This signal is emitted when a \a row is collapsed in the view. + \a row will be equal to the argument given to the call that caused + the collapse to happen (\l collapse() or \l collapseRecursively()). + If the row was collapsed recursively, \a recursively will be \c true. + + \note when a row is collapsed recursively, the collapsed signal will + only be emitted for that one row, and not for its descendants. \sa expanded(), expand(), collapse(), toggleExpanded() */ -static const char* kRequiredProperties = "_qt_treeview_requiredpropertymask"; // Hard-code the tree column to be 0 for now static const int kTreeColumn = 0; @@ -343,32 +346,6 @@ void QQuickTreeViewPrivate::dataChangedCallback( } } -void QQuickTreeViewPrivate::setRequiredProperty(const char *property, - const QVariant &value, int serializedModelIndex, QObject *object, bool init) -{ - // Attaching a property list to the delegate item is just a - // work-around until QMetaProperty::isRequired() works! - const QString propertyName = QString::fromUtf8(property); - - if (init) { - const bool wasRequired = model->setRequiredProperty(serializedModelIndex, propertyName, value); - if (wasRequired) { - QStringList propertyList = object->property(kRequiredProperties).toStringList(); - object->setProperty(kRequiredProperties, propertyList << propertyName); - } - } else { - const QStringList propertyList = object->property(kRequiredProperties).toStringList(); - if (!propertyList.contains(propertyName)) { - // We only write to properties that are required - return; - } - const auto metaObject = object->metaObject(); - const int propertyIndex = metaObject->indexOfProperty(property); - const auto metaProperty = metaObject->property(propertyIndex); - metaProperty.write(object, value); - } -} - void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject *object, bool init) { Q_Q(QQuickTreeView); @@ -420,22 +397,97 @@ bool QQuickTreeView::isExpanded(int row) const void QQuickTreeView::expand(int row) { + if (row >= 0) + expandRecursively(row, 1); +} + +void QQuickTreeView::expandRecursively(int row, int depth) +{ Q_D(QQuickTreeView); - if (row < 0 || row >= d->m_treeModelToTableModel.rowCount()) + if (row >= d->m_treeModelToTableModel.rowCount()) + return; + if (row < 0 && row != -1) + return; + if (depth == 0 || depth < -1) + return; + + auto expandRowRecursively = [=](int startRow) { + d->m_treeModelToTableModel.expandRecursively(startRow, depth); + // Update the expanded state of the startRow. The descendant rows that gets + // expanded will get the correct state set from initItem/itemReused instead. + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, startRow); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false); + } + }; + + if (row >= 0) { + // Expand only one row recursively + const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row); + if (isExpanded && depth == 1) + return; + expandRowRecursively(row); + } else { + // Expand all root nodes recursively + const auto model = d->m_treeModelToTableModel.model(); + for (int r = 0; r < model->rowCount(); ++r) { + const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0)); + if (rootRow != -1) + expandRowRecursively(rootRow); + } + } + + emit expanded(row, depth); +} + +void QQuickTreeView::expandToIndex(const QModelIndex &index) +{ + Q_D(QQuickTreeView); + + if (!index.isValid()) { + qmlWarning(this) << "index is not valid: " << index; return; + } - if (d->m_treeModelToTableModel.isExpanded(row)) + if (index.model() != d->m_treeModelToTableModel.model()) { + qmlWarning(this) << "index doesn't belong to correct model: " << index; return; + } - d->m_treeModelToTableModel.expandRow(row); + if (rowAtIndex(index) != -1) { + // index is already visible + return; + } - for (int c = leftColumn(); c <= rightColumn(); ++c) { - const QPoint treeNodeCell(c, row); - if (const auto item = itemAtCell(treeNodeCell)) - d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false); + int depth = 1; + QModelIndex parent = index.parent(); + int row = rowAtIndex(parent); + + while (parent.isValid()) { + if (row != -1) { + // The node is already visible, since it maps to a row in the table! + d->m_treeModelToTableModel.expandRow(row); + + // Update the state of the already existing delegate item + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, row); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false); + } + + // When we hit a node that is visible, we know that all other nodes + // up to the parent have to be visible as well, so we can stop. + break; + } else { + d->m_treeModelToTableModel.expand(parent); + parent = parent.parent(); + row = rowAtIndex(parent); + depth++; + } } - emit expanded(row); + emit expanded(row, depth); } void QQuickTreeView::collapse(int row) @@ -455,7 +507,42 @@ void QQuickTreeView::collapse(int row) d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false); } - emit collapsed(row); + emit collapsed(row, false); +} + +void QQuickTreeView::collapseRecursively(int row) +{ + Q_D(QQuickTreeView); + if (row >= d->m_treeModelToTableModel.rowCount()) + return; + if (row < 0 && row != -1) + return; + + auto collapseRowRecursive = [=](int startRow) { + // Always collapse descendants recursively, + // even if the top row itself is already collapsed. + d->m_treeModelToTableModel.collapseRecursively(startRow); + // Update the expanded state of the (still visible) startRow + for (int c = leftColumn(); c <= rightColumn(); ++c) { + const QPoint treeNodeCell(c, startRow); + if (const auto item = itemAtCell(treeNodeCell)) + d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false); + } + }; + + if (row >= 0) { + collapseRowRecursive(row); + } else { + // Collapse all root nodes recursively + const auto model = d->m_treeModelToTableModel.model(); + for (int r = 0; r < model->rowCount(); ++r) { + const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0)); + if (rootRow != -1) + collapseRowRecursive(rootRow); + } + } + + emit collapsed(row, true); } void QQuickTreeView::toggleExpanded(int row) @@ -466,32 +553,22 @@ void QQuickTreeView::toggleExpanded(int row) expand(row); } -QModelIndex QQuickTreeView::modelIndex(int row, int column) const +QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const { Q_D(const QQuickTreeView); - const QModelIndex tableIndex = d->m_treeModelToTableModel.index(row, column); + const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x()); return d->m_treeModelToTableModel.mapToModel(tableIndex); } -QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const -{ - return modelIndex(cell.y(), cell.x()); -} - -int QQuickTreeView::rowAtIndex(const QModelIndex &index) const -{ - return d_func()->m_treeModelToTableModel.mapFromModel(index).row(); -} - -int QQuickTreeView::columnAtIndex(const QModelIndex &index) const -{ - return d_func()->m_treeModelToTableModel.mapFromModel(index).column(); -} - QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const { const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index); return QPoint(tableIndex.column(), tableIndex.row()); } +QModelIndex QQuickTreeView::modelIndex(int row, int column) const +{ + return modelIndex({column, row}); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h index c3e4b5a38f..870b29d329 100644 --- a/src/quick/items/qquicktreeview_p.h +++ b/src/quick/items/qquicktreeview_p.h @@ -75,15 +75,17 @@ public: Q_INVOKABLE void collapse(int row); Q_INVOKABLE void toggleExpanded(int row); - Q_INVOKABLE QModelIndex modelIndex(int row, int column) const; - Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const; - Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const; - Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const; - Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const; + Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1); + Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1); + Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index); + + Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override; + Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override; + Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override; signals: - void expanded(int row); - void collapsed(int row); + void expanded(int row, int depth); + void collapsed(int row, bool recursively); private: Q_DISABLE_COPY(QQuickTreeView) diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h index db974aefc9..47d87d2ac1 100644 --- a/src/quick/items/qquicktreeview_p_p.h +++ b/src/quick/items/qquicktreeview_p_p.h @@ -77,7 +77,6 @@ public: const QModelIndex &bottomRight, const QVector<int> &roles); - void setRequiredProperty(const char *property, const QVariant &value, int serializedModelIndex, QObject *object, bool init); void updateRequiredProperties(int serializedModelIndex, QObject *object, bool init); public: diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h index 1bc266bbbf..7018a84395 100644 --- a/src/quick/items/qquickview_p.h +++ b/src/quick/items/qquickview_p.h @@ -60,17 +60,12 @@ #include <QtCore/QWeakPointer> #include <QtQml/qqmlengine.h> -#include <private/qv4object_p.h> #include "qquickwindow_p.h" #include "qquickitemchangelistener_p.h" QT_BEGIN_NAMESPACE -namespace QV4 { -struct ExecutionEngine; -} - class QQmlContext; class QQmlError; class QQuickItem; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 156ecdaadb..f68848444e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -428,10 +428,10 @@ static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) void QQuickWindow::physicalDpiChanged() { Q_D(QQuickWindow); - const qreal newPixelRatio = screen()->devicePixelRatio(); - if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio)) + const qreal newPixelRatio = effectiveDevicePixelRatio(); + if (qFuzzyCompare(newPixelRatio, d->lastReportedItemDevicePixelRatio)) return; - d->devicePixelRatio = newPixelRatio; + d->lastReportedItemDevicePixelRatio = newPixelRatio; if (d->contentItem) updatePixelRatioHelper(d->contentItem, newPixelRatio); } @@ -514,7 +514,6 @@ void QQuickWindowPrivate::ensureCustomRenderTarget() redirect.renderTargetDirty = false; redirect.rt.reset(rhi); - redirect.devicePixelRatio = customRenderTarget.devicePixelRatio(); // a default constructed QQuickRenderTarget means no redirection if (customRenderTarget.isNull()) @@ -535,11 +534,6 @@ void QQuickWindowPrivate::syncSceneGraph() ensureCustomRenderTarget(); - // Calculate the dpr the same way renderSceneGraph() will. - qreal devicePixelRatio = q->effectiveDevicePixelRatio(); - if (redirect.rt.renderTarget && !QQuickRenderControl::renderWindowFor(q)) - devicePixelRatio = redirect.devicePixelRatio; - QRhiCommandBuffer *cb = nullptr; if (rhi) { if (redirect.commandBuffer) @@ -547,7 +541,7 @@ void QQuickWindowPrivate::syncSceneGraph() else cb = swapchain->currentFrameCommandBuffer(); } - context->prepareSync(devicePixelRatio, cb, graphicsConfig); + context->prepareSync(q->effectiveDevicePixelRatio(), cb, graphicsConfig); animationController->beforeNodeSync(); @@ -646,36 +640,21 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa const bool flipY = rhi ? !rhi->isYUpInNDC() : false; if (flipY) matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; + const qreal devicePixelRatio = q->effectiveDevicePixelRatio(); - if (redirect.rt.renderTarget) { - const QSize pixelSize = redirect.rt.renderTarget->pixelSize(); - QRect rect(QPoint(0, 0), pixelSize); - renderer->setDeviceRect(rect); - renderer->setViewportRect(rect); - if (QQuickRenderControl::renderWindowFor(q)) { - renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags); - renderer->setDevicePixelRatio(devicePixelRatio); - } else { - const QSizeF logicalSize = pixelSize / redirect.devicePixelRatio; - renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags); - renderer->setDevicePixelRatio(redirect.devicePixelRatio); - } - } else { - QSize pixelSize; - QSizeF logicalSize; - if (surfaceSize.isEmpty()) { - pixelSize = size * devicePixelRatio; - logicalSize = size; - } else { - pixelSize = surfaceSize; - logicalSize = QSizeF(surfaceSize) / devicePixelRatio; - } - QRect rect(QPoint(0, 0), pixelSize); - renderer->setDeviceRect(rect); - renderer->setViewportRect(rect); - renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags); - renderer->setDevicePixelRatio(devicePixelRatio); - } + QSize pixelSize; + if (redirect.rt.renderTarget) + pixelSize = redirect.rt.renderTarget->pixelSize(); + else if (surfaceSize.isEmpty()) + pixelSize = size * devicePixelRatio; + else + pixelSize = surfaceSize; + QSizeF logicalSize = QSizeF(pixelSize) / devicePixelRatio; + + renderer->setDevicePixelRatio(devicePixelRatio); + renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize)); + renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize)); + renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), logicalSize), matrixFlags); if (rhi) { context->renderNextRhiFrame(renderer); @@ -705,7 +684,7 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa QQuickWindowPrivate::QQuickWindowPrivate() : contentItem(nullptr) , dirtyItemList(nullptr) - , devicePixelRatio(0) + , lastReportedItemDevicePixelRatio(0) , context(nullptr) , renderer(nullptr) , windowManager(nullptr) @@ -770,7 +749,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) Q_ASSERT(windowManager || renderControl); if (QScreen *screen = q->screen()) { - devicePixelRatio = screen->devicePixelRatio(); + lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio(); // if the screen changes, then QQuickWindow::handleScreenChanged disconnects // and connects to the new screen physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged, @@ -1326,6 +1305,8 @@ QQuickItem *QQuickWindow::contentItem() const \brief The item which currently has active focus or \c null if there is no item with active focus. + + \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick} */ QQuickItem *QQuickWindow::activeFocusItem() const { @@ -3736,21 +3717,31 @@ void QQuickWindow::runJobsAfterSwap() } /*! - * Returns the device pixel ratio for this window. - * - * This is different from QWindow::devicePixelRatio() in that it supports - * redirected rendering via QQuickRenderControl. When using a - * QQuickRenderControl, the QQuickWindow is often not created, meaning it is - * never shown and there is no underlying native window created in the - * windowing system. As a result, querying properties like the device pixel - * ratio cannot give correct results. Use this function instead. - * - * \sa QWindow::devicePixelRatio() + Returns the device pixel ratio for this window. + + This is different from QWindow::devicePixelRatio() in that it supports + redirected rendering via QQuickRenderControl and QQuickRenderTarget. When + using a QQuickRenderControl, the QQuickWindow is often not fully created, + meaning it is never shown and there is no underlying native window created + in the windowing system. As a result, querying properties like the device + pixel ratio cannot give correct results. This function takes into account + both QQuickRenderControl::renderWindowFor() and + QQuickRenderTarget::devicePixelRatio(). When no redirection is in effect, + the result is same as QWindow::devicePixelRatio(). + + \sa QQuickRenderControl, QQuickRenderTarget, setRenderTarget(), QWindow::devicePixelRatio() */ qreal QQuickWindow::effectiveDevicePixelRatio() const { + Q_D(const QQuickWindow); QWindow *w = QQuickRenderControl::renderWindowFor(const_cast<QQuickWindow *>(this)); - return w ? w->devicePixelRatio() : devicePixelRatio(); + if (w) + return w->devicePixelRatio(); + + if (!d->customRenderTarget.isNull()) + return d->customRenderTarget.devicePixelRatio(); + + return devicePixelRatio(); } /*! diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 0e69d63239..0fd09a1e33 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -193,7 +193,7 @@ public: QVector<QQuickItem *> itemsToPolish; - qreal devicePixelRatio; + qreal lastReportedItemDevicePixelRatio; QMetaObject::Connection physicalDpiChangedConnection; void updateDirtyNodes(); @@ -228,7 +228,6 @@ public: struct Redirect { QRhiCommandBuffer *commandBuffer = nullptr; QQuickWindowRenderTarget rt; - qreal devicePixelRatio = 1.0; bool renderTargetDirty = false; } redirect; |