aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/qquickdrag.cpp17
-rw-r--r--src/quick/items/qquickflickable.cpp1
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickimagebase.cpp20
-rw-r--r--src/quick/items/qquickitem.cpp2
-rw-r--r--src/quick/items/qquickitemview.cpp68
-rw-r--r--src/quick/items/qquickitemview_p_p.h9
-rw-r--r--src/quick/items/qquicklistview.cpp20
-rw-r--r--src/quick/items/qquickpalettecolorprovider.cpp1
-rw-r--r--src/quick/items/qquickpathview.cpp22
-rw-r--r--src/quick/items/qquickpathview_p_p.h5
-rw-r--r--src/quick/items/qquickshadereffect.cpp15
-rw-r--r--src/quick/items/qquickstateoperations.cpp24
-rw-r--r--src/quick/items/qquicktableview.cpp94
-rw-r--r--src/quick/items/qquicktext.cpp6
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquicktextedit.cpp42
-rw-r--r--src/quick/items/qquicktextedit_p_p.h3
-rw-r--r--src/quick/items/qquicktextinput.cpp13
-rw-r--r--src/quick/items/qquicktextinput_p_p.h4
-rw-r--r--src/quick/items/qquicktextnode.cpp2
-rw-r--r--src/quick/items/qquicktextnode_p.h6
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp2
-rw-r--r--src/quick/items/qquickwindow.cpp78
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.
*/
/*!