diff options
Diffstat (limited to 'src/quick/items')
24 files changed, 306 insertions, 151 deletions
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp index ebc1f33cde..66718b42ed 100644 --- a/src/quick/items/qquickdrag.cpp +++ b/src/quick/items/qquickdrag.cpp @@ -686,11 +686,18 @@ QMimeData *QQuickDragAttachedPrivate::createMimeData() const } else if (mimeType == u"text/html"_s) { mimeData->setHtml(text); } else if (mimeType == u"text/uri-list"_s) { - const QUrl url(text); - if (url.isValid()) - mimeData->setUrls({url}); - else - qmlWarning(q) << text << " is not a valid URI"; + QList<QUrl> urls; + // parse and split according to RFC2483 + const auto lines = text.split(u"\r\n"_s, Qt::SkipEmptyParts); + for (const auto &line : lines) { + const QUrl url(line); + if (url.isValid()) + urls.push_back(url); + else + qmlWarning(q) << line << " is not a valid URI"; + + } + mimeData->setUrls(urls); } else if (mimeType.startsWith(u"text/"_s)) { if (qsizetype charsetIdx = mimeType.lastIndexOf(u";charset="_s); charsetIdx != -1) { charsetIdx += sizeof(";charset=") - 1; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 5dcc712c73..77a4bef646 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1647,7 +1647,6 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->pressed = false; d->scrollingPhase = false; d->draggingEnding(); - event->accept(); returnToBounds(); d->lastPosTime = -1; d->stealMouse = false; diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 19eecad94f..8c2bca9304 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -2402,7 +2402,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch { Q_Q(QQuickGridView); - if (q->size().isEmpty()) + if (q->size().isNull()) return false; int modelIndex = change.index; diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 78dfecc42a..b4d21542f4 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -304,15 +304,17 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions) } } - d->pix.load(qmlEngine(this), - loadUrl, - d->sourceClipRect.toRect(), - (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(), - options, - (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(), - d->currentFrame, d->frameCount, - d->devicePixelRatio); - + auto engine = qmlEngine(this); + if (engine) { + d->pix.load(qmlEngine(this), + loadUrl, + d->sourceClipRect.toRect(), + (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(), + options, + (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(), + d->currentFrame, d->frameCount, + d->devicePixelRatio); + } if (d->pix.isLoading()) { if (d->progress != 0.0) { d->progress = 0.0; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index f2621f9f68..1e4af6838d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1481,7 +1481,7 @@ QQuickLayoutMirroringAttached::QQuickLayoutMirroringAttached(QObject *parent) : if (itemPrivate) itemPrivate->extra.value().layoutDirectionAttached = this; else - qmlWarning(parent) << tr("LayoutDirection attached property only works with Items and Windows"); + qmlWarning(parent) << tr("LayoutMirroring attached property only works with Items and Windows"); } QQuickLayoutMirroringAttached * QQuickLayoutMirroringAttached::qmlAttachedProperties(QObject *object) diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index fea7dba2c3..210759585b 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -10,6 +10,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle") +Q_LOGGING_CATEGORY(lcCount, "qt.quick.itemview.count") // Default cacheBuffer for all views. #ifndef QML_VIEW_DEFAULTCACHEBUFFER @@ -223,7 +224,7 @@ void QQuickItemView::setModel(const QVariant &m) this, SLOT(modelUpdated(QQmlChangeSet,bool))); if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) QObjectPrivate::connect(dataModel, &QQmlDelegateModel::delegateChanged, d, &QQuickItemViewPrivate::applyDelegateChange); - emit countChanged(); + d->emitCountChanged(); } emit modelChanged(); d->moveReason = QQuickItemViewPrivate::Other; @@ -255,7 +256,7 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate) int oldCount = dataModel->count(); dataModel->setDelegate(delegate); if (oldCount != dataModel->count()) - emit countChanged(); + d->emitCountChanged(); } emit delegateChanged(); d->delegateValidated = false; @@ -1086,8 +1087,7 @@ qreal QQuickItemViewPrivate::calculatedMaxExtent() const void QQuickItemViewPrivate::applyDelegateChange() { releaseVisibleItems(QQmlDelegateModel::NotReusable); - releaseItem(currentItem, QQmlDelegateModel::NotReusable); - currentItem = nullptr; + releaseCurrentItem(QQmlDelegateModel::NotReusable); updateSectionCriteria(); refill(); moveReason = QQuickItemViewPrivate::SetIndex; @@ -1126,6 +1126,14 @@ void QQuickItemViewPrivate::showVisibleItems() const } } +// Simplifies debugging of count. +void QQuickItemViewPrivate::emitCountChanged() +{ + Q_Q(QQuickItemView); + qCDebug(lcCount).nospace() << "about to emit countChanged for " << q << "; count changed to " << q->count(); + emit q->countChanged(); +} + void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeometry) { @@ -1225,7 +1233,7 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) d->updateTrackedItem(); } d->moveReason = QQuickItemViewPrivate::Other; - emit countChanged(); + d->emitCountChanged(); #if QT_CONFIG(quick_viewtransitions) if (d->transitioner && d->transitioner->populateTransition) d->forceLayoutPolish(); @@ -1486,7 +1494,7 @@ void QQuickItemView::componentComplete() d->fixupPosition(); } if (d->model && d->model->count()) - emit countChanged(); + d->emitCountChanged(); } @@ -1653,8 +1661,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) if (currentItem) { if (currentItem->attached) currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem, reusableFlag); - currentItem = nullptr; + releaseCurrentItem(reusableFlag); currentIndex = modelIndex; emit q->currentIndexChanged(); emit q->currentItemChanged(); @@ -1715,10 +1722,9 @@ void QQuickItemViewPrivate::clear(bool onDestruction) releasePendingTransition.clear(); #endif - auto oldCurrentItem = currentItem; - releaseItem(currentItem, QQmlDelegateModel::NotReusable); - currentItem = nullptr; - if (oldCurrentItem) + const bool hadCurrentItem = currentItem != nullptr; + releaseCurrentItem(QQmlDelegateModel::NotReusable); + if (hadCurrentItem) emit q->currentItemChanged(); createHighlight(onDestruction); trackedItem = nullptr; @@ -1763,7 +1769,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to) Q_Q(QQuickItemView); if (!model || !model->isValid() || !q->isComponentComplete()) return; - if (q->size().isEmpty() && visibleItems.isEmpty()) + if (q->size().isNull() && visibleItems.isEmpty()) return; if (!model->count()) { updateHeader(); @@ -1814,7 +1820,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to) } if (prevCount != itemCount) - emit q->countChanged(); + emitCountChanged(); } while (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()); storeFirstVisibleItemPosition(); } @@ -1861,7 +1867,20 @@ void QQuickItemViewPrivate::layout() // viewBounds contains bounds before any add/remove/move operation to the view QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height()); - if (!isValid() && !visibleItems.size()) { + // We use isNull for the size check, because isEmpty returns true + // if either dimension is negative, but apparently we support negative-sized + // views (see tst_QQuickListView::resizeView). + if ((!isValid() && !visibleItems.size()) || q->size().isNull()) { + if (q->size().isNull() && hasPendingChanges()) { + // count() refers to the number of items in the model, not in the view + // (which is why we don't emit for the !visibleItems.size() case). + // If there are pending model changes, emit countChanged in order to + // support the use case of QTBUG-129165, where visible is bound to count > 0 + // and the ListView is in a layout with Layout.preferredHeight bound to + // contentHeight. This ensures that a hidden ListView will become visible. + emitCountChanged(); + } + clear(); setPosition(contentStartOffset()); updateViewport(); @@ -2121,10 +2140,9 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult if (currentChanges.currentRemoved && currentItem) { if (currentItem->item && currentItem->attached) currentItem->attached->setIsCurrentItem(false); - auto oldCurrentItem = currentItem; - releaseItem(currentItem, reusableFlag); - currentItem = nullptr; - if (oldCurrentItem) + const bool hadCurrentItem = currentItem != nullptr; + releaseCurrentItem(reusableFlag); + if (hadCurrentItem) emit q->currentItemChanged(); } if (!currentIndexCleared) @@ -2137,7 +2155,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult updateSections(); if (prevItemCount != itemCount) - emit q->countChanged(); + emitCountChanged(); if (!visibleAffected && viewportChanged) updateViewport(); @@ -2491,9 +2509,15 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu return flags != QQmlInstanceModel::Referenced; } -QQuickItem *QQuickItemViewPrivate::createHighlightItem() const +QQuickItem *QQuickItemViewPrivate::createHighlightItem() { - return createComponentItem(highlightComponent, 0.0, true); + QQuickItem *item = nullptr; + if (!inRequest) { + inRequest = true; + item = createComponentItem(highlightComponent, 0.0, true); + inRequest = false; + } + return item; } QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault) const diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 301ff6f326..c1188ac4d7 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -145,9 +145,14 @@ public: void mirrorChange() override; FxViewItem *createItem(int modelIndex,QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested); + bool releaseCurrentItem(QQmlInstanceModel::ReusableFlag reusableFlag) + { + auto oldCurrentItem = std::exchange(currentItem, nullptr); + return releaseItem(oldCurrentItem, reusableFlag); + } virtual bool releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag); - QQuickItem *createHighlightItem() const; + QQuickItem *createHighlightItem(); QQuickItem *createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault = false) const; virtual void initializeComponentItem(QQuickItem *) const; @@ -224,6 +229,8 @@ public: releaseItem(item, reusableFlag); } + void emitCountChanged(); + virtual QQuickItemViewAttached *getAttachedObject(const QObject *) const { return nullptr; } QPointer<QQmlInstanceModel> model; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 41c345bd66..92b66ca5ed 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -1797,6 +1797,19 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); return; } + // If we have the CurrentLabelAtStart flag set, then we need to consider + // the section size while calculating the position + if (sectionCriteria + && (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) + && currentSectionItem) { + auto sectionSize = (orient == QQuickListView::Vertical) ? currentSectionItem->height() + : currentSectionItem->width(); + if (isContentFlowReversed()) + pos += sectionSize; + else + pos -= sectionSize; + } + pos = qBound(-minExtent, pos, -maxExtent); qreal dist = qAbs(data.move + pos); @@ -3645,7 +3658,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch int modelIndex = change.index; int count = change.count; - if (q->size().isEmpty() && visibleItems.isEmpty()) + if (q->size().isNull() && visibleItems.isEmpty()) return false; qreal tempPos = isContentFlowReversed() ? -position()-size() : position(); @@ -3773,7 +3786,10 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch continue; } - visibleItems.insert(index, item); + if (index < visibleItems.size()) + visibleItems.insert(index, item); + else // special case of appending an item to the model - as above + visibleItems.append(item); if (index == 0) insertResult->changedFirstItem = true; if (change.isMove()) { diff --git a/src/quick/items/qquickpalettecolorprovider.cpp b/src/quick/items/qquickpalettecolorprovider.cpp index 2d36ff01c5..a9dfe45092 100644 --- a/src/quick/items/qquickpalettecolorprovider.cpp +++ b/src/quick/items/qquickpalettecolorprovider.cpp @@ -116,6 +116,7 @@ bool QQuickPaletteColorProvider::doInheritPalette(const QPalette &palette) { auto inheritedMask = m_requestedPalette.isAllocated() ? m_requestedPalette->resolveMask() | palette.resolveMask() : palette.resolveMask(); + // If a palette was set on this item, it should always win over the palette to be inherited from. QPalette parentPalette = m_requestedPalette.isAllocated() ? m_requestedPalette->resolve(palette) : palette; parentPalette.setResolveMask(inheritedMask); diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index ee2bf7ac39..9e99ec0601 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -206,10 +206,7 @@ QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType() void QQuickPathViewPrivate::clear() { - if (currentItem) { - releaseItem(currentItem); - currentItem = nullptr; - } + releaseCurrentItem(); for (QQuickItem *p : std::as_const(items)) releaseItem(p); @@ -724,14 +721,13 @@ void QQuickPathView::setCurrentIndex(int idx) ? ((idx % d->modelCount) + d->modelCount) % d->modelCount : 0; if (d->model && (idx != d->currentIndex || !d->currentItem)) { - if (d->currentItem) { + const bool hadCurrentItem = d->currentItem != nullptr; + const int oldCurrentIdx = d->currentIndex; + if (hadCurrentItem) { if (QQuickPathViewAttached *att = d->attached(d->currentItem)) att->setIsCurrentItem(false); - d->releaseItem(d->currentItem); + d->releaseCurrentItem(); } - int oldCurrentIdx = d->currentIndex; - QQuickItem *oldCurrentItem = d->currentItem; - d->currentItem = nullptr; d->moveReason = QQuickPathViewPrivate::SetIndex; d->currentIndex = idx; if (d->modelCount) { @@ -743,7 +739,7 @@ void QQuickPathView::setCurrentIndex(int idx) } if (oldCurrentIdx != d->currentIndex) emit currentIndexChanged(); - if (oldCurrentItem != d->currentItem) + if (hadCurrentItem) emit currentItemChanged(); } } @@ -2003,6 +1999,7 @@ void QQuickPathView::refill() startPos = d->highlightRangeStart; // With no items, then "end" is just off the top so we populate via append endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount; + endIdx = qMax(-1, endIdx); // endIdx shouldn't be smaller than -1 endPos = d->positionOfIndex(endIdx); } //Append @@ -2185,8 +2182,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) } else if (d->currentItem) { if (QQuickPathViewAttached *att = d->attached(d->currentItem)) att->setIsCurrentItem(true); - d->releaseItem(d->currentItem); - d->currentItem = nullptr; + d->releaseCurrentItem(); } d->currentIndex = qMin(r.index, d->modelCount - r.count - 1); currentChanged = true; @@ -2334,7 +2330,7 @@ void QQuickPathViewPrivate::updateCurrent() if (currentItem) { if (QQuickPathViewAttached *att = attached(currentItem)) att->setIsCurrentItem(false); - releaseItem(currentItem); + releaseCurrentItem(); } int oldCurrentIndex = currentIndex; currentIndex = idx; diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 61c0b2ce62..65df3b6be1 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -67,6 +67,11 @@ public: } QQuickItem *getItem(int modelIndex, qreal z = 0, bool async=false); + void releaseCurrentItem() + { + auto oldCurrentItem = std::exchange(currentItem, nullptr); + releaseItem(oldCurrentItem); + } void releaseItem(QQuickItem *item); QQuickPathViewAttached *attached(QQuickItem *item); QQmlOpenMetaObjectType *attachedType(); diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 2a6d40c8aa..40a2942041 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -538,6 +538,14 @@ QQuickShaderEffect::~QQuickShaderEffect() { Q_D(QQuickShaderEffect); d->inDestructor = true; + + for (int i = 0; i < QQuickShaderEffectPrivate::NShader; ++i) { + d->disconnectSignals(QQuickShaderEffectPrivate::Shader(i)); + d->clearMappers(QQuickShaderEffectPrivate::Shader(i)); + } + + delete d->m_mgr; + d->m_mgr = nullptr; } /*! @@ -835,12 +843,7 @@ QQuickShaderEffectPrivate::QQuickShaderEffectPrivate() QQuickShaderEffectPrivate::~QQuickShaderEffectPrivate() { - for (int i = 0; i < NShader; ++i) { - disconnectSignals(Shader(i)); - clearMappers(Shader(i)); - } - - delete m_mgr; + Q_ASSERT(m_mgr == nullptr); } void QQuickShaderEffectPrivate::setFragmentShader(const QUrl &fileUrl) diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index 1c8bee7253..16127e5a35 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -1055,17 +1055,24 @@ void QQuickAnchorChanges::reverse() //restore any absolute geometry changed by the state's anchors QQuickAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Vertical_Mask; QQuickAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Vertical_Mask; + QQuickAnchors::Anchors resetVAnchors = d->anchorSet->d_func()->resetAnchors & QQuickAnchors::Vertical_Mask; QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask; QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask; + QQuickAnchors::Anchors resetHAnchors = d->anchorSet->d_func()->resetAnchors & QQuickAnchors::Horizontal_Mask; const QRectF oldGeometry(d->target->position(), d->target->size()); bool stateSetWidth = (stateHAnchors && stateHAnchors != QQuickAnchors::LeftAnchor && stateHAnchors != QQuickAnchors::RightAnchor && stateHAnchors != QQuickAnchors::HCenterAnchor); - // in case of an additive AnchorChange, we _did_ end up modifying the width - stateSetWidth |= ((stateHAnchors & QQuickAnchors::LeftAnchor) && (origHAnchors & QQuickAnchors::RightAnchor)) || - ((stateHAnchors & QQuickAnchors::RightAnchor) && (origHAnchors & QQuickAnchors::LeftAnchor)); + // in case of an additive AnchorChange, we _did_ end up modifying the width, unless opposite + // edge was set to undefined in state + stateSetWidth |= ((stateHAnchors & QQuickAnchors::LeftAnchor) + && (origHAnchors & QQuickAnchors::RightAnchor) + && !(resetHAnchors & QQuickAnchors::RightAnchor)) + || ((stateHAnchors & QQuickAnchors::RightAnchor) + && (origHAnchors & QQuickAnchors::LeftAnchor) + && !(resetHAnchors & QQuickAnchors::LeftAnchor)); bool origSetWidth = (origHAnchors && origHAnchors != QQuickAnchors::LeftAnchor && origHAnchors != QQuickAnchors::RightAnchor && @@ -1081,9 +1088,14 @@ void QQuickAnchorChanges::reverse() stateVAnchors != QQuickAnchors::BottomAnchor && stateVAnchors != QQuickAnchors::VCenterAnchor && stateVAnchors != QQuickAnchors::BaselineAnchor); - // in case of an additive AnchorChange, we _did_ end up modifying the height - stateSetHeight |= ((stateVAnchors & QQuickAnchors::TopAnchor) && (origVAnchors & QQuickAnchors::BottomAnchor)) || - ((stateVAnchors & QQuickAnchors::BottomAnchor) && (origVAnchors & QQuickAnchors::TopAnchor)); + // in case of an additive AnchorChange, we _did_ end up modifying the height, unless opposite + // edge was set to undefined in state + stateSetHeight |= ((stateVAnchors & QQuickAnchors::TopAnchor) + && (origVAnchors & QQuickAnchors::BottomAnchor) + && !(resetVAnchors & QQuickAnchors::BottomAnchor)) + || ((stateVAnchors & QQuickAnchors::BottomAnchor) + && (origVAnchors & QQuickAnchors::TopAnchor) + && !(resetVAnchors & QQuickAnchors::TopAnchor)); bool origSetHeight = (origVAnchors && origVAnchors != QQuickAnchors::TopAnchor && origVAnchors != QQuickAnchors::BottomAnchor && diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 390d246170..1b564df125 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1011,32 +1011,26 @@ \qmlmethod real QtQuick::TableView::implicitColumnWidth(int column) \since 6.2 - Returns the implicit width of the given \a column. If the - column is not loaded (and therefore not visible), the return value - will be \c -1. + Returns the implicit width of the given \a column. This is the largest + \l implicitWidth found among the currently \l{isRowLoaded()}{loaded} + delegate items inside that column. - The implicit width of a column is the largest implicitWidth - found among the currently loaded delegate items inside that column. - Widths returned by the \l columnWidthProvider will not be taken - into account. + If the \a column is not loaded (and therefore not visible), the return value is \c -1. - \sa columnWidthProvider, columnWidth(), isColumnLoaded(), {Row heights and column widths} + \sa columnWidth(), isRowLoaded(), {Row heights and column widths} */ /*! \qmlmethod real QtQuick::TableView::implicitRowHeight(int row) \since 6.2 - Returns the implicit height of the given \a row. If the - row is not loaded (and therefore not visible), the return value - will be \c -1. + Returns the implicit height of the given \a row. This is the largest + \l implicitHeight found among the currently \l{isColumnLoaded()}{loaded} + delegate items inside that row. - The implicit height of a row is the largest implicitHeight - found among the currently loaded delegate items inside that row. - Heights returned by the \l rowHeightProvider will not be taken - into account. + If the \a row is not loaded (and therefore not visible), the return value is \c -1. - \sa rowHeightProvider, rowHeight(), isRowLoaded(), {Row heights and column widths} + \sa rowHeight(), isColumnLoaded(), {Row heights and column widths} */ /*! @@ -1202,13 +1196,15 @@ Convenience function for doing: \code - modelIndex(cell.y, cell.x) + index(cell.y, cell.x) \endcode A cell is simply a \l point that combines row and column into a single type. \note \c {point.x} will map to the column, and \c {point.y} will map to the row. + + \sa index() */ /*! @@ -2155,11 +2151,13 @@ void QQuickTableViewPrivate::updateExtents() const int nextTopRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge); const int nextBottomRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge); + QPointF prevOrigin = origin; + QSizeF prevEndExtent = endExtent; + if (syncHorizontally) { const auto syncView_d = syncView->d_func(); origin.rx() = syncView_d->origin.x(); endExtent.rwidth() = syncView_d->endExtent.width(); - hData.markExtentsDirty(); } else if (nextLeftColumn == kEdgeIndexAtEnd) { // There are no more columns to load on the left side of the table. // In that case, we ensure that the origin match the beginning of the table. @@ -2176,7 +2174,6 @@ void QQuickTableViewPrivate::updateExtents() } } origin.rx() = loadedTableOuterRect.left(); - hData.markExtentsDirty(); } else if (loadedTableOuterRect.left() <= origin.x() + cellSpacing.width()) { // The table rect is at the origin, or outside, but we still have more // visible columns to the left. So we try to guesstimate how much space @@ -2186,7 +2183,6 @@ void QQuickTableViewPrivate::updateExtents() const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; origin.rx() = loadedTableOuterRect.left() - estimatedRemainingWidth; - hData.markExtentsDirty(); } else if (nextRightColumn == kEdgeIndexAtEnd) { // There are no more columns to load on the right side of the table. // In that case, we ensure that the end of the content view match the end of the table. @@ -2204,7 +2200,6 @@ void QQuickTableViewPrivate::updateExtents() } } endExtent.rwidth() = loadedTableOuterRect.right() - q->contentWidth(); - hData.markExtentsDirty(); } else if (loadedTableOuterRect.right() >= q->contentWidth() + endExtent.width() - cellSpacing.width()) { // The right-most column is outside the end of the content view, and we // still have more visible columns in the model. This can happen if the application @@ -2215,14 +2210,12 @@ void QQuickTableViewPrivate::updateExtents() const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; const qreal pixelsOutsideContentWidth = loadedTableOuterRect.right() - q->contentWidth(); endExtent.rwidth() = pixelsOutsideContentWidth + estimatedRemainingWidth; - hData.markExtentsDirty(); } if (syncVertically) { const auto syncView_d = syncView->d_func(); origin.ry() = syncView_d->origin.y(); endExtent.rheight() = syncView_d->endExtent.height(); - vData.markExtentsDirty(); } else if (nextTopRow == kEdgeIndexAtEnd) { // There are no more rows to load on the top side of the table. // In that case, we ensure that the origin match the beginning of the table. @@ -2239,7 +2232,6 @@ void QQuickTableViewPrivate::updateExtents() } } origin.ry() = loadedTableOuterRect.top(); - vData.markExtentsDirty(); } else if (loadedTableOuterRect.top() <= origin.y() + cellSpacing.height()) { // The table rect is at the origin, or outside, but we still have more // visible rows at the top. So we try to guesstimate how much space @@ -2249,7 +2241,6 @@ void QQuickTableViewPrivate::updateExtents() const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing; origin.ry() = loadedTableOuterRect.top() - estimatedRemainingHeight; - vData.markExtentsDirty(); } else if (nextBottomRow == kEdgeIndexAtEnd) { // There are no more rows to load on the bottom side of the table. // In that case, we ensure that the end of the content view match the end of the table. @@ -2267,7 +2258,6 @@ void QQuickTableViewPrivate::updateExtents() } } endExtent.rheight() = loadedTableOuterRect.bottom() - q->contentHeight(); - vData.markExtentsDirty(); } else if (loadedTableOuterRect.bottom() >= q->contentHeight() + endExtent.height() - cellSpacing.height()) { // The bottom-most row is outside the end of the content view, and we // still have more visible rows in the model. This can happen if the application @@ -2278,7 +2268,6 @@ void QQuickTableViewPrivate::updateExtents() const qreal estimatedRemainingHeight = remainingRowHeigts + remainingSpacing; const qreal pixelsOutsideContentHeight = loadedTableOuterRect.bottom() - q->contentHeight(); endExtent.rheight() = pixelsOutsideContentHeight + estimatedRemainingHeight; - vData.markExtentsDirty(); } if (tableMovedHorizontally || tableMovedVertically) { @@ -2299,12 +2288,23 @@ void QQuickTableViewPrivate::updateExtents() } } - if (hData.minExtentDirty || vData.minExtentDirty) { - qCDebug(lcTableViewDelegateLifecycle) << "move origin and endExtent to:" << origin << endExtent; + if (prevOrigin != origin || prevEndExtent != endExtent) { + if (prevOrigin != origin) + qCDebug(lcTableViewDelegateLifecycle) << "move origin to:" << origin; + if (prevEndExtent != endExtent) + qCDebug(lcTableViewDelegateLifecycle) << "move endExtent to:" << endExtent; // updateBeginningEnd() will let the new extents take effect. This will also change the // visualArea of the flickable, which again will cause any attached scrollbars to adjust // the position of the handle. Note the latter will cause the viewport to move once more. + hData.markExtentsDirty(); + vData.markExtentsDirty(); updateBeginningEnd(); + if (!q->isMoving()) { + // When we adjust the extents, the viewport can sometimes be left suspended in an + // overshooted state. It will bounce back again once the user clicks inside the + // viewport. But this comes across as a bug, so returnToBounds explicitly. + q->returnToBounds(); + } } } @@ -4318,11 +4318,18 @@ void QQuickTableViewPrivate::syncSyncView() q->setRightMargin(syncView->rightMargin()); updateContentWidth(); - if (syncView->leftColumn() != q->leftColumn()) { - // The left column is no longer the same as the left - // column in syncView. This requires a rebuild. - scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn; - scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (scheduledRebuildOptions & RebuildOption::LayoutOnly) { + if (syncView->leftColumn() != q->leftColumn() + || syncView->d_func()->loadedTableOuterRect.left() != loadedTableOuterRect.left()) { + // The left column is no longer the same, or at the same pos, as the left column in + // syncView. This can happen if syncView did a relayout that caused its left column + // to be resized so small that it ended up outside the viewport. It can also happen + // if the syncView loaded and unloaded columns after the relayout. We therefore need + // to sync our own left column and pos to be the same, which we do by rebuilding the + // whole viewport instead of just doing a plain LayoutOnly. + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn; + scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + } } } @@ -4333,11 +4340,18 @@ void QQuickTableViewPrivate::syncSyncView() q->setBottomMargin(syncView->bottomMargin()); updateContentHeight(); - if (syncView->topRow() != q->topRow()) { - // The top row is no longer the same as the top - // row in syncView. This requires a rebuild. - scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow; - scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + if (scheduledRebuildOptions & RebuildOption::LayoutOnly) { + if (syncView->topRow() != q->topRow() + || syncView->d_func()->loadedTableOuterRect.top() != loadedTableOuterRect.top()) { + // The top row is no longer the same, or at the same pos, as the top row in + // syncView. This can happen if syncView did a relayout that caused its top row + // to be resized so small that it ended up outside the viewport. It can also happen + // if the syncView loaded and unloaded rows after the relayout. We therefore need + // to sync our own top row and pos to be the same, which we do by rebuilding the + // whole viewport instead of just doing a plain LayoutOnly. + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow; + scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly); + } } } @@ -4724,7 +4738,7 @@ void QQuickTableViewPrivate::syncViewportRect() auto syncChild_d = syncChild->d_func(); if (syncChild_d->syncHorizontally) w = qMax(w, syncChild->width()); - if (syncChild_d->syncHorizontally) + if (syncChild_d->syncVertically) h = qMax(h, syncChild->height()); } diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index f618a55760..e5976acb1e 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -62,6 +62,7 @@ QQuickTextPrivate::QQuickTextPrivate() , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false) , polishSize(false) , updateSizeRecursionGuard(false) + , containsUnscalableGlyphs(false) { implicitAntialiasing = true; } @@ -1697,7 +1698,7 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value) break; case ItemDevicePixelRatioHasChanged: - if (d->renderType == NativeRendering) { + if (d->containsUnscalableGlyphs) { // Native rendering optimizes for a given pixel grid, so its results must not be scaled. // Text layout code respects the current device pixel ratio automatically, we only need // to rerun layout after the ratio changed. @@ -2495,6 +2496,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data Q_D(QQuickText); if (d->text.isEmpty()) { + d->containsUnscalableGlyphs = false; delete oldNode; return nullptr; } @@ -2553,6 +2555,8 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data } } + d->containsUnscalableGlyphs = node->containsUnscalableGlyphs(); + // The font caches have now been initialized on the render thread, so they have to be // invalidated before we can use them from the main thread again. invalidateFontCaches(); diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 9870197c31..8e48f93281 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -143,6 +143,7 @@ public: bool formatModifiesFontSize:1; bool polishSize:1; // Workaround for problem with polish called after updateSize (QTBUG-42636) bool updateSizeRecursionGuard:1; + bool containsUnscalableGlyphs:1; static const QChar elideChar; static const int largeTextSizeThreshold; diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index f27b537302..4471d6e4b9 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -707,6 +707,7 @@ void QQuickTextEdit::setHAlign(HAlignment align) if (d->setHAlign(align, true) && isComponentComplete()) { d->updateDefaultTextOption(); updateSize(); + updateWholeDocument(); } } @@ -814,6 +815,7 @@ void QQuickTextEditPrivate::mirrorChange() if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) { updateDefaultTextOption(); q->updateSize(); + q->updateWholeDocument(); emit q->effectiveHorizontalAlignmentChanged(); } } @@ -1512,7 +1514,7 @@ void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) Q_UNUSED(value); switch (change) { case ItemDevicePixelRatioHasChanged: - if (d->renderType == NativeRendering) { + if (d->containsUnscalableGlyphs) { // Native rendering optimizes for a given pixel grid, so its results must not be scaled. // Text layout code respects the current device pixel ratio automatically, we only need // to rerun layout after the ratio changed. @@ -2138,6 +2140,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * return oldNode; } + d->containsUnscalableGlyphs = false; if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) { delete oldNode; oldNode = nullptr; @@ -2216,14 +2219,17 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * d->firstBlockInViewport = -1; d->firstBlockPastViewport = -1; + int frameCount = -1; while (!frames.isEmpty()) { QTextFrame *textFrame = frames.takeFirst(); + ++frameCount; + if (frameCount > 0) + firstDirtyPos = 0; + qCDebug(lcVP) << "frame" << frameCount << textFrame + << "from" << positionToRectangle(textFrame->firstPosition()).topLeft() + << "to" << positionToRectangle(textFrame->lastPosition()).bottomRight(); frames.append(textFrame->childFrames()); frameDecorationsEngine.addFrameDecorations(d->document, textFrame); - - if (textFrame->lastPosition() < firstDirtyPos - || textFrame->firstPosition() >= firstCleanNode.startPos()) - continue; resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); if (textFrame->firstPosition() > textFrame->lastPosition() @@ -2307,16 +2313,25 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * } break; // skip rest of blocks in this frame } - if (inView && !block.text().isEmpty() && coveredRegion.isValid()) + if (inView && !block.text().isEmpty() && coveredRegion.isValid()) { d->renderedRegion = d->renderedRegion.united(coveredRegion); + // In case we're going to visit more (nested) frames after this, ensure that we + // don't omit any blocks that fit within the region that we claim as fully rendered. + if (!frames.isEmpty()) + viewport = viewport.united(d->renderedRegion); + } } } bool createdNodeInView = false; if (inView) { if (!engine.hasContents()) { - if (node && !node->parent()) - d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); + if (node) { + d->containsUnscalableGlyphs = d->containsUnscalableGlyphs + || node->containsUnscalableGlyphs(); + if (!node->parent()) + d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); + } node = d->createTextNode(); createdNodeInView = true; updateNodeTransform(node, nodeOffset); @@ -2331,6 +2346,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position()); if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) { currentNodeSize = 0; + d->containsUnscalableGlyphs = d->containsUnscalableGlyphs + || node->containsUnscalableGlyphs(); if (!node->parent()) d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); if (!createdNodeInView) @@ -2341,8 +2358,12 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * ++it; } // loop over blocks in frame } - if (Q_LIKELY(node && !node->parent())) - d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); + if (Q_LIKELY(node)) { + d->containsUnscalableGlyphs = d->containsUnscalableGlyphs + || node->containsUnscalableGlyphs(); + if (Q_LIKELY(!node->parent())) + d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); + } } frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor()); // Now prepend the frame decorations since we want them rendered first, with the text nodes and cursor in front. @@ -2943,6 +2964,7 @@ void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engin it = textNodeMap.insert(it, TextNode(startPos, node)); ++it; root->appendChildNode(node); + ++renderedBlockCount; } QQuickTextNode *QQuickTextEditPrivate::createTextNode() diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index db07462a5a..114373c1c7 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -96,6 +96,7 @@ public: , selectByMouse(true), canPaste(false), canPasteValid(false), hAlignImplicit(true) , textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false) , hadSelection(false), markdownText(false) + , containsUnscalableGlyphs(false) { } @@ -164,6 +165,7 @@ public: int lineCount; int firstBlockInViewport = -1; // only for the autotest; can be wrong after scrolling sometimes int firstBlockPastViewport = -1; // only for the autotest + int renderedBlockCount = -1; // only for the autotest QRectF renderedRegion; enum UpdateType { @@ -202,6 +204,7 @@ public: bool selectByKeyboardSet:1; bool hadSelection : 1; bool markdownText : 1; + bool containsUnscalableGlyphs : 1; static const int largeTextSizeThreshold; }; diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index e4b7e4197b..08713eb026 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1568,6 +1568,8 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event) if (d->sendMouseEventToInputContext(event)) return; + d->hadSelectionOnMousePress = d->hasSelectedText(); + const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event); if (d->selectByMouse && (isMouse @@ -1663,8 +1665,13 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event) // On a touchscreen or with a stylus, set cursor position and focus on release, not on press; // if Flickable steals the grab in the meantime, the cursor won't move. // Check d->hasSelectedText() to keep touch-and-hold word selection working. - if (!isMouse && !d->hasSelectedText()) + // But if text was selected already on press, deselect it on release. + if (!isMouse && (!d->hasSelectedText() || d->hadSelectionOnMousePress)) d->moveCursor(d->positionAt(event->position()), false); + // On Android, after doing a long-press to start selection, we see a release event, + // even though there was no press event. So reset hadSelectionOnMousePress to avoid + // it getting stuck in true state. + d->hadSelectionOnMousePress = false; if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease()) ensureActiveFocus(Qt::MouseFocusReason); @@ -1778,7 +1785,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) Q_UNUSED(value); switch (change) { case ItemDevicePixelRatioHasChanged: - if (d->renderType == NativeRendering) { + if (d->containsUnscalableGlyphs) { // Native rendering optimizes for a given pixel grid, so its results must not be scaled. // Text layout code respects the current device pixel ratio automatically, we only need // to rerun layout after the ratio changed. @@ -1997,6 +2004,8 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData d->textLayoutDirty = false; } + d->containsUnscalableGlyphs = node->containsUnscalableGlyphs(); + invalidateFontCaches(); return node; diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 01d90f6b10..2360bf99eb 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -107,6 +107,7 @@ public: , canRedo(false) , hAlignImplicit(true) , selectPressed(false) + , hadSelectionOnMousePress(false) , textLayoutDirty(true) , persistentSelection(false) , hasImState(false) @@ -124,6 +125,7 @@ public: , inLayout(false) , requireImplicitWidth(false) , overwriteMode(false) + , containsUnscalableGlyphs(false) #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) , selectByTouchDrag(false) #endif @@ -259,6 +261,7 @@ public: bool canRedo:1; bool hAlignImplicit:1; bool selectPressed:1; + bool hadSelectionOnMousePress:1; bool textLayoutDirty:1; bool persistentSelection:1; bool hasImState : 1; @@ -276,6 +279,7 @@ public: bool inLayout:1; bool requireImplicitWidth:1; bool overwriteMode:1; + bool containsUnscalableGlyphs:1; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) bool selectByTouchDrag:1; #endif diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 2024470265..0a56cae652 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -62,6 +62,8 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun } } + m_containsUnscalableGlyphs = m_containsUnscalableGlyphs || preferNativeGlyphNode; + QSGGlyphNode *node = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode, m_renderTypeQuality); node->setOwnerElement(m_ownerElement); diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h index 0538336311..3098c86bb2 100644 --- a/src/quick/items/qquicktextnode_p.h +++ b/src/quick/items/qquicktextnode_p.h @@ -75,6 +75,11 @@ public: void setRenderTypeQuality(int renderTypeQuality) { m_renderTypeQuality = renderTypeQuality; } int renderTypeQuality() const { return m_renderTypeQuality; } + bool containsUnscalableGlyphs() const + { + return m_containsUnscalableGlyphs; + } + QPair<int, int> renderedLineRange() const { return { m_firstLineInViewport, m_firstLinePastViewport }; } private: @@ -85,6 +90,7 @@ private: int m_renderTypeQuality; int m_firstLineInViewport = -1; int m_firstLinePastViewport = -1; + bool m_containsUnscalableGlyphs = false; friend class QQuickTextEdit; friend class QQuickTextEditPrivate; diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 6463628879..5aac0e3260 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -143,6 +143,8 @@ int QQuickTextNodeEngine::addText(const QTextBlock &block, while (textPos < fragmentEnd) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); + if (!line.isValid()) + break; if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 4606c1231e..9de90f8d82 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -42,6 +42,7 @@ #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qsgdefaultrendercontext_p.h> +#include <private/qsgsoftwarerenderer_p.h> #if QT_CONFIG(opengl) #include <private/qopengl_p.h> #include <QOpenGLContext> @@ -286,37 +287,31 @@ struct PolishLoopDetector if (itemsToPolish.size() > itemsRemainingBeforeUpdatePolish) { // Detected potential polish loop. ++numPolishLoopsInSequence; - if (numPolishLoopsInSequence >= 1000) { + if (numPolishLoopsInSequence == 10000) { + // We have looped 10,000 times without actually reducing the list of items to + // polish, give up for now. + // This is not a fix, just a remedy so that the application can be somewhat + // responsive. + numPolishLoopsInSequence = 0; + return true; + } + if (numPolishLoopsInSequence >= 1000 && numPolishLoopsInSequence < 1005) { // Start to warn about polish loop after 1000 consecutive polish loops - if (numPolishLoopsInSequence == 100000) { - // We have looped 100,000 times without actually reducing the list of items to - // polish, give up for now. - // This is not a fix, just a remedy so that the application can be somewhat - // responsive. - numPolishLoopsInSequence = 0; - return true; - } else if (numPolishLoopsInSequence < 1005) { - // Show the 5 next items involved in the polish loop. - // (most likely they will be the same 5 items...) - QQuickItem *guiltyItem = itemsToPolish.last(); - qmlWarning(item) << "possible QQuickItem::polish() loop"; - - auto typeAndObjectName = [](QQuickItem *item) { - QString typeName = QQmlMetaType::prettyTypeName(item); - QString objName = item->objectName(); - if (!objName.isNull()) - return QLatin1String("%1(%2)").arg(typeName, objName); - return typeName; - }; - - qmlWarning(guiltyItem) << typeAndObjectName(guiltyItem) - << " called polish() inside updatePolish() of " << typeAndObjectName(item); - - if (numPolishLoopsInSequence == 1004) - // Enough warnings. Reset counter in order to speed things up and re-detect - // more loops - numPolishLoopsInSequence = 0; - } + // Show the 5 next items involved in the polish loop. + // (most likely they will be the same 5 items...) + QQuickItem *guiltyItem = itemsToPolish.last(); + qmlWarning(item) << "possible QQuickItem::polish() loop"; + + auto typeAndObjectName = [](QQuickItem *item) { + QString typeName = QQmlMetaType::prettyTypeName(item); + QString objName = item->objectName(); + if (!objName.isNull()) + return QLatin1String("%1(%2)").arg(typeName, objName); + return typeName; + }; + + qmlWarning(guiltyItem) << typeAndObjectName(guiltyItem) + << " called polish() inside updatePolish() of " << typeAndObjectName(item); } } else { numPolishLoopsInSequence = 0; @@ -519,6 +514,7 @@ void QQuickWindowPrivate::syncSceneGraph() { Q_Q(QQuickWindow); + const bool wasRtDirty = redirect.renderTargetDirty; ensureCustomRenderTarget(); QRhiCommandBuffer *cb = nullptr; @@ -540,7 +536,7 @@ void QQuickWindowPrivate::syncSceneGraph() invalidateFontData(contentItem); } - if (!renderer) { + if (Q_UNLIKELY(!renderer)) { forceUpdate(contentItem); QSGRootNode *rootNode = new QSGRootNode; @@ -550,6 +546,10 @@ void QQuickWindowPrivate::syncSceneGraph() : QSGRendererInterface::RenderMode2DNoDepthBuffer; renderer = context->createRenderer(renderMode); renderer->setRootNode(rootNode); + } else if (Q_UNLIKELY(wasRtDirty) + && q->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { + auto softwareRenderer = static_cast<QSGSoftwareRenderer *>(renderer); + softwareRenderer->markDirty(); } updateDirtyNodes(); @@ -914,6 +914,22 @@ void QQuickWindowPrivate::cleanup(QSGNode *n) // The confirmExitPopup allows user to save or discard the document, // or to cancel the closing. \endcode + + \section1 Styling + + As with all visual types in Qt Quick, Window supports + \l {palette}{palettes}. However, as with types like \l Text, Window does + not use palettes by default. For example, to change the background color + of the window when the operating system's theme changes, the \l color must + be set: + + \snippet qml/windowPalette.qml declaration-and-color + \codeline + \snippet qml/windowPalette.qml text-item + \snippet qml/windowPalette.qml closing-brace + + Use \l {ApplicationWindow} (and \l {Label}) from \l {Qt Quick Controls} + instead of Window to get automatic styling. */ /*! |
