aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp2
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp109
-rw-r--r--src/quick/items/qquickaccessibleattached_p.h42
-rw-r--r--src/quick/items/qquickborderimage.cpp26
-rw-r--r--src/quick/items/qquickdrag.cpp2
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickimage.cpp79
-rw-r--r--src/quick/items/qquickimagebase.cpp98
-rw-r--r--src/quick/items/qquickimagebase_p.h5
-rw-r--r--src/quick/items/qquickimagebase_p_p.h11
-rw-r--r--src/quick/items/qquickitem.cpp38
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/items/qquickitemanimation.cpp5
-rw-r--r--src/quick/items/qquicklistview.cpp2
-rw-r--r--src/quick/items/qquickmousearea.cpp1
-rw-r--r--src/quick/items/qquickpalettecolorprovider.cpp1
-rw-r--r--src/quick/items/qquickselectable_p.h2
-rw-r--r--src/quick/items/qquicktableview.cpp671
-rw-r--r--src/quick/items/qquicktableview_p.h8
-rw-r--r--src/quick/items/qquicktableview_p_p.h119
-rw-r--r--src/quick/items/qquicktext.cpp153
-rw-r--r--src/quick/items/qquicktext_p_p.h7
-rw-r--r--src/quick/items/qquicktextdocument.cpp30
-rw-r--r--src/quick/items/qquicktextedit.cpp15
-rw-r--r--src/quick/items/qquicktextinput.cpp14
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp14
-rw-r--r--src/quick/items/qquickwindow.cpp51
-rw-r--r--src/quick/items/qquickwindow_p.h1
-rw-r--r--src/quick/items/qquickwindowcontainer.cpp1
-rw-r--r--src/quick/items/qquickwindowmodule.cpp31
-rw-r--r--src/quick/items/qquickwindowmodule_p_p.h2
31 files changed, 1306 insertions, 238 deletions
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 6ae5914970..d78bd040c2 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -1206,7 +1206,7 @@ QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
This signal is emitted when the \a region needs to be rendered. If a context
is active it can be referenced from the context property.
- This signal can be triggered by markdirty(), requestPaint() or by changing
+ This signal can be triggered by markDirty(), requestPaint() or by changing
the current canvas window.
*/
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 865fb8bf11..bd24d1b1de 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -120,6 +120,15 @@ QT_BEGIN_NAMESPACE
\endtable
*/
+/*!
+ \qmlproperty string QtQuick::Accessible::id
+
+ This property sets an identifier for the object.
+ It can be used to provide stable identifiers to UI tests.
+ By default, the identifier is set to the ID of the QML object.
+ If the ID is not set the default of \l QAccessible::Identifier is used.
+*/
+
/*! \qmlproperty bool QtQuick::Accessible::focusable
\brief This property holds whether this item is focusable.
@@ -301,15 +310,19 @@ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent)
: QObject(parent), m_role(QAccessible::NoRole)
{
Q_ASSERT(parent);
- if (!item()) {
- qmlWarning(parent) << "Accessible must be attached to an Item";
- return;
- }
-
// Enable accessibility for items with accessible content. This also
- // enables accessibility for the ancestors of souch items.
- item()->d_func()->setAccessible();
- QAccessibleEvent ev(item(), QAccessible::ObjectCreated);
+ // enables accessibility for the ancestors of such items.
+ auto item = qobject_cast<QQuickItem *>(parent);
+ if (item) {
+ item->d_func()->setAccessible();
+ } else {
+ const QLatin1StringView className(QQmlData::ensurePropertyCache(parent)->firstCppMetaObject()->className());
+ if (className != QLatin1StringView("QQuickAction")) {
+ qmlWarning(parent) << "Accessible must be attached to an Item or an Action";
+ return;
+ }
+ }
+ QAccessibleEvent ev(parent, QAccessible::ObjectCreated);
QAccessible::updateAccessibility(&ev);
if (const QMetaObject *pmo = parent->metaObject()) {
@@ -423,13 +436,15 @@ QQuickAccessibleAttached *QQuickAccessibleAttached::qmlAttachedProperties(QObjec
bool QQuickAccessibleAttached::ignored() const
{
- return item() ? !item()->d_func()->isAccessible : false;
+ auto item = qobject_cast<QQuickItem *>(parent());
+ return item ? !item->d_func()->isAccessible : false;
}
void QQuickAccessibleAttached::setIgnored(bool ignored)
{
- if (this->ignored() != ignored && item()) {
- item()->d_func()->isAccessible = !ignored;
+ auto item = qobject_cast<QQuickItem *>(parent());
+ if (item && this->ignored() != ignored) {
+ item->d_func()->isAccessible = !ignored;
emit ignoredChanged();
}
}
@@ -457,8 +472,13 @@ bool QQuickAccessibleAttached::doAction(const QString &actionName)
sig = &sigPreviousPage;
else if (actionName == QAccessibleActionInterface::nextPageAction())
sig = &sigNextPage;
- if (sig && isSignalConnected(*sig))
- return sig->invoke(this);
+ if (sig && isSignalConnected(*sig)) {
+ bool ret = false;
+ if (m_proxying)
+ ret = sig->invoke(m_proxying);
+ ret |= sig->invoke(this);
+ return ret;
+ }
return false;
}
@@ -497,6 +517,69 @@ QString QQuickAccessibleAttached::stripHtml(const QString &html)
#endif
}
+void QQuickAccessibleAttached::setProxying(QQuickAccessibleAttached *proxying)
+{
+ if (proxying == m_proxying)
+ return;
+
+ const QMetaObject &mo = staticMetaObject;
+ if (m_proxying) {
+ // We disconnect all signals from the proxy into this object
+ auto mo = m_proxying->metaObject();
+ auto propertyCache = QQmlData::ensurePropertyCache(m_proxying);
+ for (int signalIndex = propertyCache->signalOffset();
+ signalIndex < propertyCache->signalCount(); ++signalIndex) {
+ const QMetaMethod m = mo->method(propertyCache->signal(signalIndex)->coreIndex());
+ Q_ASSERT(m.methodType() == QMetaMethod::Signal);
+ if (m.methodType() != QMetaMethod::Signal)
+ continue;
+
+ disconnect(m_proxying, m, this, m);
+ }
+ }
+
+ m_proxying = proxying;
+
+ if (m_proxying) {
+ // We connect all signals from the proxy into this object
+ auto propertyCache = QQmlData::ensurePropertyCache(m_proxying);
+ auto mo = m_proxying->metaObject();
+ for (int signalIndex = propertyCache->signalOffset();
+ signalIndex < propertyCache->signalCount(); ++signalIndex) {
+ const QMetaMethod m = mo->method(propertyCache->signal(signalIndex)->coreIndex());
+ Q_ASSERT(m.methodType() == QMetaMethod::Signal);
+ connect(proxying, m, this, m);
+ }
+ }
+
+ // We check all properties
+ for (int prop = mo.propertyOffset(); prop < mo.propertyCount(); ++prop) {
+ const QMetaProperty p = mo.property(prop);
+ if (!p.hasNotifySignal()) {
+ continue;
+ }
+
+ const QMetaMethod signal = p.notifySignal();
+ if (signal.parameterCount() == 0)
+ signal.invoke(this);
+ else
+ signal.invoke(this, Q_ARG(bool, p.read(this).toBool()));
+ }
+}
+
+/*!
+ * \since 6.8
+ * Issues an announcement event with a \a message with priority \a priority.
+ *
+ * \sa QAccessibleAnnouncementEvent
+ */
+void QQuickAccessibleAttached::announce(const QString &message, QAccessible::AnnouncementPriority priority)
+{
+ QAccessibleAnnouncementEvent event(parent(), message);
+ event.setPriority(priority);
+ QAccessible::updateAccessibility(&event);
+}
+
QT_END_NAMESPACE
#include "moc_qquickaccessibleattached_p.cpp"
diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h
index 92d1307a9a..3ec8fcab98 100644
--- a/src/quick/items/qquickaccessibleattached_p.h
+++ b/src/quick/items/qquickaccessibleattached_p.h
@@ -30,9 +30,11 @@ QT_BEGIN_NAMESPACE
#define STATE_PROPERTY(P) \
Q_PROPERTY(bool P READ P WRITE set_ ## P NOTIFY P ## Changed FINAL) \
- bool P() const { return m_state.P ; } \
+ bool P() const { return m_proxying && !m_stateExplicitlySet.P ? m_proxying->P() : m_state.P ; } \
void set_ ## P(bool arg) \
{ \
+ if (m_proxying) \
+ m_proxying->set_##P(arg);\
m_stateExplicitlySet.P = true; \
if (m_state.P == arg) \
return; \
@@ -51,6 +53,7 @@ class Q_QUICK_EXPORT QQuickAccessibleAttached : public QObject
Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged FINAL)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL)
+ Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged REVISION(6, 8) FINAL)
Q_PROPERTY(bool ignored READ ignored WRITE setIgnored NOTIFY ignoredChanged FINAL)
QML_NAMED_ELEMENT(Accessible)
@@ -84,6 +87,8 @@ public:
QString name() const {
if (m_state.passwordEdit)
return QString();
+ if (m_proxying)
+ return m_proxying->name();
return m_name;
}
@@ -99,9 +104,15 @@ public:
}
void setNameImplicitly(const QString &name);
- QString description() const { return m_description; }
+ QString description() const {
+ return !m_descriptionExplicitlySet && m_proxying ? m_proxying->description() : m_description;
+ }
void setDescription(const QString &description)
{
+ if (!m_descriptionExplicitlySet && m_proxying) {
+ disconnect(m_proxying, &QQuickAccessibleAttached::descriptionChanged, this, &QQuickAccessibleAttached::descriptionChanged);
+ }
+ m_descriptionExplicitlySet = true;
if (m_description != description) {
m_description = description;
Q_EMIT descriptionChanged();
@@ -110,6 +121,17 @@ public:
}
}
+ QString id() const { return m_id; }
+ void setId(const QString &id)
+ {
+ if (m_id != id) {
+ m_id = id;
+ Q_EMIT idChanged();
+ QAccessibleEvent ev(parent(), QAccessible::IdentifierChanged);
+ QAccessible::updateAccessibility(&ev);
+ }
+ }
+
// Factory function
static QQuickAccessibleAttached *qmlAttachedProperties(QObject *obj);
@@ -143,6 +165,13 @@ public:
if (att && (role == QAccessible::NoRole || att->role() == role)) {
break;
}
+ if (auto action = object->property("action").value<QObject *>(); action) {
+ QQuickAccessibleAttached *att = QQuickAccessibleAttached::attachedProperties(action);
+ if (att && (role == QAccessible::NoRole || att->role() == role)) {
+ object = action;
+ break;
+ }
+ }
object = object->parent();
}
return object;
@@ -154,6 +183,9 @@ public:
void availableActions(QStringList *actions) const;
Q_REVISION(6, 2) Q_INVOKABLE static QString stripHtml(const QString &html);
+ void setProxying(QQuickAccessibleAttached *proxying);
+
+ Q_REVISION(6, 8) Q_INVOKABLE void announce(const QString &message, QAccessible::AnnouncementPriority priority = QAccessible::AnnouncementPriority::Polite);
public Q_SLOTS:
void valueChanged() {
@@ -171,6 +203,7 @@ Q_SIGNALS:
void roleChanged();
void nameChanged();
void descriptionChanged();
+ void idChanged();
void ignoredChanged();
void pressAction();
void toggleAction();
@@ -184,14 +217,15 @@ Q_SIGNALS:
void nextPageAction();
private:
- QQuickItem *item() const { return qobject_cast<QQuickItem*>(parent()); }
-
QAccessible::Role m_role;
QAccessible::State m_state;
QAccessible::State m_stateExplicitlySet;
QString m_name;
bool m_nameExplicitlySet = false;
QString m_description;
+ bool m_descriptionExplicitlySet = false;
+ QQuickAccessibleAttached* m_proxying = nullptr;
+ QString m_id;
static QMetaMethod sigPress;
static QMetaMethod sigToggle;
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index 40daf518d3..004f0491bd 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -400,11 +400,16 @@ void QQuickBorderImage::requestFinished()
{
Q_D(QQuickBorderImage);
- const QSize impsize = d->pix.implicitSize();
+ if (d->pendingPix != d->currentPix) {
+ std::swap(d->pendingPix, d->currentPix);
+ d->pendingPix->clear(this); // Clear the old image
+ }
+
+ const QSize impsize = d->currentPix->implicitSize();
setImplicitSize(impsize.width() / d->devicePixelRatio, impsize.height() / d->devicePixelRatio);
- if (d->pix.isError()) {
- qmlWarning(this) << d->pix.error();
+ if (d->currentPix->isError()) {
+ qmlWarning(this) << d->currentPix->error();
d->setStatus(Error);
d->setProgress(0);
} else {
@@ -416,8 +421,8 @@ void QQuickBorderImage::requestFinished()
d->oldSourceSize = sourceSize();
emit sourceSizeChanged();
}
- if (d->frameCount != d->pix.frameCount()) {
- d->frameCount = d->pix.frameCount();
+ if (d->frameCount != d->currentPix->frameCount()) {
+ d->frameCount = d->currentPix->frameCount();
emit frameCountChanged();
}
@@ -507,7 +512,7 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
{
Q_D(QQuickBorderImage);
- QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
+ QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window());
if (!texture || width() <= 0 || height() <= 0) {
delete oldNode;
@@ -532,7 +537,7 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
QRectF innerSourceRect;
QRectF subSourceRect;
d->calculateRects(d->border,
- QSize(d->pix.width(), d->pix.height()), QSizeF(width(), height()),
+ QSize(d->currentPix->width(), d->currentPix->height()), QSizeF(width(), height()),
d->horizontalTileMode, d->verticalTileMode, d->devicePixelRatio,
&targetRect, &innerTargetRect,
&innerSourceRect, &subSourceRect);
@@ -577,6 +582,13 @@ void QQuickBorderImage::pixmapChange()
frameCount is the number of frames in the image. Most images have only one frame.
*/
+/*!
+ \qmlproperty bool QtQuick::BorderImage::retainWhileLoading
+ \since 6.8
+
+ \include qquickimage.cpp qml-image-retainwhileloading
+ */
+
QT_END_NAMESPACE
#include "moc_qquickborderimage_p.cpp"
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index dbe443b2b3..47064ad433 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -362,7 +362,7 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
}
/*!
- \qmlattachedproperty QUrl QtQuick::Drag::imageSourceSize
+ \qmlattachedproperty size QtQuick::Drag::imageSourceSize
\since 6.8
This property holds the size of the image that will be used to represent
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 25ef44ecb0..e67d80b2d6 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1382,7 +1382,7 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
\qmlproperty int QtQuick::GridView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
*/
/*!
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 3aaa59819c..3efe082332 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -218,7 +218,7 @@ QQuickImage::~QQuickImage()
void QQuickImagePrivate::setImage(const QImage &image)
{
Q_Q(QQuickImage);
- pix.setImage(image);
+ currentPix->setImage(image);
q->pixmapChange();
q->update();
}
@@ -226,7 +226,7 @@ void QQuickImagePrivate::setImage(const QImage &image)
void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
{
Q_Q(QQuickImage);
- pix.setPixmap(pixmap);
+ currentPix->setPixmap(pixmap);
q->pixmapChange();
q->update();
}
@@ -605,12 +605,12 @@ void QQuickImage::updatePaintedGeometry()
Q_D(QQuickImage);
if (d->fillMode == PreserveAspectFit) {
- if (!d->pix.width() || !d->pix.height()) {
+ if (!d->currentPix->width() || !d->currentPix->height()) {
setImplicitSize(0, 0);
return;
}
- const qreal pixWidth = d->pix.width() / d->devicePixelRatio;
- const qreal pixHeight = d->pix.height() / d->devicePixelRatio;
+ const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
+ const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
const qreal w = widthValid() ? width() : pixWidth;
const qreal widthScale = w / pixWidth;
const qreal h = heightValid() ? height() : pixHeight;
@@ -627,10 +627,10 @@ void QQuickImage::updatePaintedGeometry()
setImplicitSize(iWidth, iHeight);
} else if (d->fillMode == PreserveAspectCrop) {
- if (!d->pix.width() || !d->pix.height())
+ if (!d->currentPix->width() || !d->currentPix->height())
return;
- const qreal pixWidth = d->pix.width() / d->devicePixelRatio;
- const qreal pixHeight = d->pix.height() / d->devicePixelRatio;
+ const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
+ const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
qreal widthScale = width() / pixWidth;
qreal heightScale = height() / pixHeight;
if (widthScale < heightScale) {
@@ -642,8 +642,8 @@ void QQuickImage::updatePaintedGeometry()
d->paintedHeight = heightScale * pixHeight;
d->paintedWidth = widthScale * pixWidth;
} else if (d->fillMode == Pad) {
- d->paintedWidth = d->pix.width() / d->devicePixelRatio;
- d->paintedHeight = d->pix.height() / d->devicePixelRatio;
+ d->paintedWidth = d->currentPix->width() / d->devicePixelRatio;
+ d->paintedHeight = d->currentPix->height() / d->devicePixelRatio;
} else {
d->paintedWidth = width();
d->paintedHeight = height();
@@ -685,7 +685,7 @@ QSGTextureProvider *QQuickImage::textureProvider() const
dd->provider = new QQuickImageTextureProvider;
dd->provider->m_smooth = d->smooth;
dd->provider->m_mipmap = d->mipmap;
- dd->provider->updateTexture(d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window()));
+ dd->provider->updateTexture(d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window()));
}
return d->provider;
@@ -711,7 +711,7 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
Q_D(QQuickImage);
- QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->pix.textureFactory(), window());
+ QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window());
// Copy over the current texture state into the texture provider...
if (d->provider) {
@@ -736,8 +736,8 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
- qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width() / d->devicePixelRatio;
- qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height() / d->devicePixelRatio;
+ qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->currentPix->width() / d->devicePixelRatio;
+ qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->currentPix->height() / d->devicePixelRatio;
int xOffset = 0;
if (d->hAlign == QQuickImage::AlignHCenter)
@@ -754,36 +754,36 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
switch (d->fillMode) {
case Stretch:
targetRect = QRectF(0, 0, width(), height());
- sourceRect = d->pix.rect();
+ sourceRect = d->currentPix->rect();
break;
case PreserveAspectFit:
targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
- sourceRect = d->pix.rect();
+ sourceRect = d->currentPix->rect();
break;
case PreserveAspectCrop: {
targetRect = QRectF(0, 0, width(), height());
- qreal wscale = width() / qreal(d->pix.width());
- qreal hscale = height() / qreal(d->pix.height());
+ qreal wscale = width() / qreal(d->currentPix->width());
+ qreal hscale = height() / qreal(d->currentPix->height());
if (wscale > hscale) {
- int src = (hscale / wscale) * qreal(d->pix.height());
+ int src = (hscale / wscale) * qreal(d->currentPix->height());
int y = 0;
if (d->vAlign == QQuickImage::AlignVCenter)
- y = qCeil((d->pix.height() - src) / 2.);
+ y = qCeil((d->currentPix->height() - src) / 2.);
else if (d->vAlign == QQuickImage::AlignBottom)
- y = qCeil(d->pix.height() - src);
- sourceRect = QRectF(0, y, d->pix.width(), src);
+ y = qCeil(d->currentPix->height() - src);
+ sourceRect = QRectF(0, y, d->currentPix->width(), src);
} else {
- int src = (wscale / hscale) * qreal(d->pix.width());
+ int src = (wscale / hscale) * qreal(d->currentPix->width());
int x = 0;
if (d->hAlign == QQuickImage::AlignHCenter)
- x = qCeil((d->pix.width() - src) / 2.);
+ x = qCeil((d->currentPix->width() - src) / 2.);
else if (d->hAlign == QQuickImage::AlignRight)
- x = qCeil(d->pix.width() - src);
- sourceRect = QRectF(x, 0, src, d->pix.height());
+ x = qCeil(d->currentPix->width() - src);
+ sourceRect = QRectF(x, 0, src, d->currentPix->height());
}
}
break;
@@ -797,13 +797,13 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
case TileHorizontally:
targetRect = QRectF(0, 0, width(), height());
- sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
+ sourceRect = QRectF(-xOffset, 0, width(), d->currentPix->height());
hWrap = QSGTexture::Repeat;
break;
case TileVertically:
targetRect = QRectF(0, 0, width(), height());
- sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
+ sourceRect = QRectF(0, -yOffset, d->currentPix->width(), height());
vWrap = QSGTexture::Repeat;
break;
@@ -817,8 +817,8 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
break;
}
- qreal nsWidth = (hWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->pix.width() / d->devicePixelRatio : d->pix.width();
- qreal nsHeight = (vWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->pix.height() / d->devicePixelRatio : d->pix.height();
+ qreal nsWidth = (hWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->width() / d->devicePixelRatio : d->currentPix->width();
+ qreal nsHeight = (vWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->height() / d->devicePixelRatio : d->currentPix->height();
QRectF nsrect(sourceRect.x() / nsWidth,
sourceRect.y() / nsHeight,
sourceRect.width() / nsWidth,
@@ -967,6 +967,25 @@ void QQuickImage::setMipmap(bool use)
frameCount is the number of frames in the image. Most images have only one frame.
*/
+/*!
+ \qmlproperty bool QtQuick::Image::retainWhileLoading
+ \since 6.8
+
+//! [qml-image-retainwhileloading]
+ This property defines the behavior when the \l source property is changed and loading happens
+ asynchronously. This is the case when the \l asynchronous property is set to \c true, or if the
+ image is not on the local file system.
+
+ If \c retainWhileLoading is \c false (the default), the old image is discarded immediately, and
+ the component is cleared while the new image is being loaded. If set to \c true, the old image
+ is retained and remains visible until the new one is ready.
+
+ Enabling this property can avoid flickering in cases where loading the new image takes a long
+ time. It comes at the cost of some extra memory use for double buffering while the new image is
+ being loaded.
+//! [qml-image-retainwhileloading]
+ */
+
QT_END_NAMESPACE
#include "moc_qquickimage_p_p.cpp"
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index ab827379fb..b00baf7f7d 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -15,16 +15,7 @@
QT_BEGIN_NAMESPACE
-bool isScalableImageFormat(const QUrl &url)
-{
- if (url.scheme() == QLatin1String("image"))
- return true;
-
- const QString stringUrl = url.path(QUrl::PrettyDecoded);
- return stringUrl.endsWith(QLatin1String("svg"))
- || stringUrl.endsWith(QLatin1String("svgz"))
- || stringUrl.endsWith(QLatin1String("pdf"));
-}
+using namespace Qt::Literals::StringLiterals;
// This function gives derived classes the chance set the devicePixelRatio
// if they're not happy with our implementation of it.
@@ -33,7 +24,7 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
// QQuickImageProvider and SVG and PDF can generate a high resolution image when
// sourceSize is set. If sourceSize is not set then the provider default size will
// be used, as usual.
- const bool setDevicePixelRatio = isScalableImageFormat(url);
+ const bool setDevicePixelRatio = QQuickPixmap::isScalableImageFormat(url);
if (setDevicePixelRatio)
devicePixelRatio = targetDevicePixelRatio;
@@ -144,7 +135,7 @@ QSize QQuickImageBase::sourceSize() const
int width = d->sourcesize.width();
int height = d->sourcesize.height();
- return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height());
+ return QSize(width != -1 ? width : d->currentPix->width(), height != -1 ? height : d->currentPix->height());
}
void QQuickImageBase::resetSourceSize()
@@ -197,7 +188,7 @@ void QQuickImageBase::setCache(bool cache)
QImage QQuickImageBase::image() const
{
Q_D(const QQuickImageBase);
- return d->pix.image();
+ return d->currentPix->image();
}
void QQuickImageBase::setMirror(bool mirror)
@@ -243,7 +234,7 @@ bool QQuickImageBase::mirrorVertically() const
void QQuickImageBase::setCurrentFrame(int frame)
{
Q_D(QQuickImageBase);
- if (frame == d->currentFrame || frame < 0 || (isComponentComplete() && frame >= d->pix.frameCount()))
+ if (frame == d->currentFrame || frame < 0 || (isComponentComplete() && frame >= d->currentPix->frameCount()))
return;
d->currentFrame = frame;
@@ -273,7 +264,8 @@ int QQuickImageBase::frameCount() const
void QQuickImageBase::loadEmptyUrl()
{
Q_D(QQuickImageBase);
- d->pix.clear(this);
+ d->currentPix->clear(this);
+ d->pendingPix->clear(this);
d->setProgress(0);
d->status = Null; // do not emit statusChanged until after setImplicitSize
setImplicitSize(0, 0); // also called in QQuickImageBase::pixmapChange, but not QQuickImage/QQuickBorderImage overrides
@@ -299,7 +291,7 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
options |= QQuickPixmap::Asynchronous;
if (d->cache)
options |= QQuickPixmap::Cache;
- d->pix.clear(this);
+ d->pendingPix->clear(this);
QUrl loadUrl = url;
const QQmlContext *context = qmlContext(this);
if (context)
@@ -310,7 +302,7 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
d->devicePixelRatio = 1.0;
bool updatedDevicePixelRatio = false;
if (d->sourcesize.isValid()
- || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
+ || (QQuickPixmap::isScalableImageFormat(d->url) && d->url.scheme() != "image"_L1)) {
updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
}
@@ -324,16 +316,16 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
d->status = Null; // reset status, no emit
- 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);
+ d->pendingPix->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->pendingPix->isLoading()) {
d->setProgress(0);
d->setStatus(Loading);
@@ -346,9 +338,10 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()");
}
- d->pix.connectFinished(this, thisRequestFinished);
- d->pix.connectDownloadProgress(this, thisRequestProgress);
- update(); //pixmap may have invalidated texture, updatePaintNode needs to be called before the next repaint
+ d->pendingPix->connectFinished(this, thisRequestFinished);
+ d->pendingPix->connectDownloadProgress(this, thisRequestProgress);
+ if (!d->retainWhileLoading)
+ update(); //pixmap may have invalidated texture, updatePaintNode needs to be called before the next repaint
} else {
requestFinished();
}
@@ -369,10 +362,15 @@ void QQuickImageBase::load()
void QQuickImageBase::requestFinished()
{
Q_D(QQuickImageBase);
+ if (d->pendingPix != d->currentPix
+ && d->pendingPix->status() != QQuickPixmap::Null
+ && d->pendingPix->status() != QQuickPixmap::Loading) {
+ std::swap(d->pendingPix, d->currentPix);
+ d->pendingPix->clear(this); // Clear the old image
+ }
- if (d->pix.isError()) {
- qmlWarning(this) << d->pix.error();
- d->pix.clear(this);
+ if (d->currentPix->isError()) {
+ qmlWarning(this) << d->currentPix->error();
d->status = Error;
d->setProgress(0);
} else {
@@ -391,12 +389,12 @@ void QQuickImageBase::requestFinished()
d->oldAutoTransform = autoTransform();
emitAutoTransformBaseChanged();
}
- if (d->frameCount != d->pix.frameCount()) {
- d->frameCount = d->pix.frameCount();
+ if (d->frameCount != d->currentPix->frameCount()) {
+ d->frameCount = d->currentPix->frameCount();
emit frameCountChanged();
}
- if (d->colorSpace != d->pix.colorSpace()) {
- d->colorSpace = d->pix.colorSpace();
+ if (d->colorSpace != d->currentPix->colorSpace()) {
+ d->colorSpace = d->currentPix->colorSpace();
emit colorSpaceChanged();
}
@@ -439,7 +437,7 @@ void QQuickImageBase::componentComplete()
void QQuickImageBase::pixmapChange()
{
Q_D(QQuickImageBase);
- setImplicitSize(d->pix.width() / d->devicePixelRatio, d->pix.height() / d->devicePixelRatio);
+ setImplicitSize(d->currentPix->width() / d->devicePixelRatio, d->currentPix->height() / d->devicePixelRatio);
}
void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio)
@@ -479,7 +477,7 @@ bool QQuickImageBase::autoTransform() const
{
Q_D(const QQuickImageBase);
if (d->providerOptions.autoTransform() == QQuickImageProviderOptions::UsePluginDefaultTransform)
- return d->pix.autoTransform() == QQuickImageProviderOptions::ApplyTransform;
+ return d->currentPix->autoTransform() == QQuickImageProviderOptions::ApplyTransform;
return d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform;
}
@@ -509,6 +507,30 @@ void QQuickImageBase::setColorSpace(const QColorSpace &colorSpace)
emit colorSpaceChanged();
}
+bool QQuickImageBase::retainWhileLoading() const
+{
+ Q_D(const QQuickImageBase);
+ return d->retainWhileLoading;
+}
+
+void QQuickImageBase::setRetainWhileLoading(bool retainWhileLoading)
+{
+ Q_D(QQuickImageBase);
+ if (d->retainWhileLoading == retainWhileLoading)
+ return;
+
+ d->retainWhileLoading = retainWhileLoading;
+ if (d->retainWhileLoading) {
+ if (d->currentPix == &d->pix1)
+ d->pendingPix = &d->pix2;
+ else
+ d->pendingPix = &d->pix1;
+ } else {
+ d->pendingPix->clear();
+ d->pendingPix = d->currentPix;
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickimagebase_p.cpp"
diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h
index 08f9abbc63..5a33db43b8 100644
--- a/src/quick/items/qquickimagebase_p.h
+++ b/src/quick/items/qquickimagebase_p.h
@@ -33,6 +33,7 @@ class Q_QUICK_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged)
Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged)
Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged REVISION(6, 2))
+ Q_PROPERTY(bool retainWhileLoading READ retainWhileLoading WRITE setRetainWhileLoading NOTIFY retainWhileLoadingChanged REVISION(6, 8))
Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged REVISION(2, 14))
Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged REVISION(2, 14))
Q_PROPERTY(QColorSpace colorSpace READ colorSpace WRITE setColorSpace NOTIFY colorSpaceChanged REVISION(2, 15))
@@ -94,6 +95,9 @@ public:
QColorSpace colorSpace() const;
virtual void setColorSpace(const QColorSpace &colorSpace);
+ bool retainWhileLoading() const;
+ void setRetainWhileLoading(bool retain);
+
static void resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio);
// Use a virtual rather than a signal->signal to avoid the huge
@@ -113,6 +117,7 @@ Q_SIGNALS:
Q_REVISION(2, 15) void sourceClipRectChanged();
Q_REVISION(2, 15) void colorSpaceChanged();
Q_REVISION(6, 2) void mirrorVerticallyChanged();
+ Q_REVISION(6, 8) void retainWhileLoadingChanged();
protected:
void loadEmptyUrl();
diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h
index 3cbb9facb0..d53712b779 100644
--- a/src/quick/items/qquickimagebase_p_p.h
+++ b/src/quick/items/qquickimagebase_p_p.h
@@ -33,8 +33,11 @@ public:
cache(true),
mirrorHorizontally(false),
mirrorVertically(false),
- oldAutoTransform(false)
+ oldAutoTransform(false),
+ retainWhileLoading(false)
{
+ pendingPix = &pix1;
+ currentPix = &pix1;
}
virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio);
@@ -43,7 +46,10 @@ public:
void setProgress(qreal value);
QUrl url;
- QQuickPixmap pix;
+ QQuickPixmap *pendingPix = nullptr;
+ QQuickPixmap *currentPix = nullptr;
+ QQuickPixmap pix1;
+ QQuickPixmap pix2;
QSize sourcesize;
QSize oldSourceSize;
QRectF sourceClipRect;
@@ -61,6 +67,7 @@ public:
bool mirrorHorizontally: 1;
bool mirrorVertically : 1;
bool oldAutoTransform : 1;
+ bool retainWhileLoading : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 4c26708168..e7b2a31f04 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2470,12 +2470,32 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
*/
bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
{
- QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward);
+ QQuickWindow *window = item->window();
+ const bool wrap = !window || window->isTopLevel();
+
+ QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap);
if (next == item)
return false;
- next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
+ const auto reason = forward ? Qt::TabFocusReason : Qt::BacktabFocusReason;
+
+ if (!wrap && !next) {
+ // Focus chain wrapped and we are not top-level window
+ // Give focus to parent window
+ Q_ASSERT(window);
+ Q_ASSERT(window->parent());
+
+
+ qt_window_private(window->parent())->setFocusToTarget(
+ forward ? QWindowPrivate::FocusTarget::Next
+ : QWindowPrivate::FocusTarget::Prev,
+ reason);
+ window->parent()->requestActivate();
+ return true;
+ }
+
+ next->forceActiveFocus(reason);
return true;
}
@@ -2524,7 +2544,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star
return nullptr;
}
-QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward)
+QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap)
{
Q_ASSERT(item);
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward;
@@ -2618,6 +2638,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
}
current = parent;
} else if (hasChildren) {
+ if (!wrap && (forward || firstFromItem != from)) {
+ qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
+ << "Focus chain about to wrap.";
+ // If focus chain wraps, we should give the parent window
+ // a chance to get focus, so we should stop here
+ return nullptr;
+ }
+
// Wrap around after checking all items forward
if (forward) {
current = firstChild;
@@ -9289,12 +9317,12 @@ void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFil
bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
// if there's no exclusive grabber, look for passive grabbers during filtering
if (isFiltering && !pointGrabber) {
- auto pg = event->passiveGrabbers(p);
+ const auto pg = event->passiveGrabbers(p);
if (!pg.isEmpty()) {
// It seems unlikely to have multiple passive grabbers of one eventpoint with different grandparents.
// So hopefully if we start from one passive grabber and go up the parent chain from there,
// we will find any filtering parent items that exist.
- auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.first());
+ auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.constFirst());
if (handler)
pointGrabber = handler->parentItem();
}
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index c2e014b72d..bb238904ca 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -575,7 +575,7 @@ public:
static bool focusNextPrev(QQuickItem *item, bool forward);
static QQuickItem *nextTabChildItem(const QQuickItem *item, int start);
static QQuickItem *prevTabChildItem(const QQuickItem *item, int start);
- static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward);
+ static QQuickItem *nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap = true);
static bool canAcceptTabFocus(QQuickItem *item);
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 8bccc7f4cb..11b2642748 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -993,9 +993,8 @@ QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPriv
QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
{
if (animationTemplate && pathUpdater()) {
- QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
- animationTemplate->activeAnimations.find(pathUpdater()->target);
- if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ auto it = animationTemplate->activeAnimations.constFind(pathUpdater()->target);
+ if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
animationTemplate->activeAnimations.erase(it);
}
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 3fde95e213..6b185b4e7c 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2411,7 +2411,7 @@ QQuickListView::~QQuickListView()
/*!
\qmlproperty int QtQuick::ListView::count
- This property holds the number of items in the view.
+ This property holds the number of items in the model.
*/
/*!
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 9c83183eb7..a6edfc4754 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -1284,6 +1284,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
return me.isAccepted();
}
+ Q_UNUSED(source)
return false;
}
diff --git a/src/quick/items/qquickpalettecolorprovider.cpp b/src/quick/items/qquickpalettecolorprovider.cpp
index af6f0ef7a5..f01fa22bdd 100644
--- a/src/quick/items/qquickpalettecolorprovider.cpp
+++ b/src/quick/items/qquickpalettecolorprovider.cpp
@@ -162,6 +162,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/qquickselectable_p.h b/src/quick/items/qquickselectable_p.h
index dbee64c49e..65434be490 100644
--- a/src/quick/items/qquickselectable_p.h
+++ b/src/quick/items/qquickselectable_p.h
@@ -37,7 +37,7 @@ public:
virtual void normalizeSelection() = 0;
virtual QRectF selectionRectangle() const = 0;
- virtual QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) = 0;
+ virtual QSizeF scrollTowardsPoint(const QPointF &pos, const QSizeF &step) = 0;
virtual void setCallback(std::function<void(CallBackFlag)> func) = 0;
};
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 6203f6f3c1..c7bd050327 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -6,11 +6,13 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdir.h>
+#include <QtCore/qmimedata.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <QtQmlModels/private/qqmldelegatemodel_p_p.h>
#include <QtQml/private/qqmlincubator_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtQml/qqmlinfo.h>
+#include <QtQuick/qquickitemgrabresult.h>
#include <QtQuick/private/qquickflickable_p_p.h>
#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
@@ -467,6 +469,9 @@
\li required property bool current - \c true if the delegate is \l {Keyboard navigation}{current.}
\li required property bool selected - \c true if the delegate is \l {Selecting items}{selected.}
\li required property bool editing - \c true if the delegate is being \l {Editing cells}{edited.}
+ \li required property bool containsDrag - \c true if a column or row is currently being dragged
+ over this delegate. This property is only supported for HorizontalHeaderView and
+ VerticalHeaderView. (since Qt 6.8)
\endlist
The following example shows how to use these properties:
@@ -944,6 +949,48 @@
*/
/*!
+ \qmlmethod QtQuick::TableView::moveColumn(int source, int destination)
+ \since 6.8
+
+ Moves a column from the \a source to the \a destination position.
+
+ \note If a syncView is set, the sync view will control the internal index mapping for
+ column reordering. Therefore, in that case, a call to this function will be forwarded to
+ the sync view instead.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::clearColumnReordering()
+ \since 6.8
+
+ Resets any previously applied column reordering.
+
+ \note If a syncView is set, a call to this function will be forwarded to
+ corresponding view item and reset the column ordering.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::moveRow(int source, int destination)
+ \since 6.8
+
+ Moves a row from the \a source to the \a destination position.
+
+ \note If a syncView is set, the sync view will control the internal index mapping for
+ row reordering. Therefore, in that case, a call to this function will be forwarded to
+ the sync view instead.
+*/
+
+/*!
+ \qmlmethod QtQuick::TableView::clearRowReordering()
+ \since 6.8
+
+ Resets any previously applied row reordering.
+
+ \note If a syncView is set, a call to this function will be forwarded to
+ the corresponding view item and reset the row ordering.
+*/
+
+/*!
\qmlmethod Item QtQuick::TableView::itemAtCell(point cell)
Returns the delegate item at \a cell if loaded, otherwise \c null.
@@ -1339,6 +1386,24 @@
*/
/*!
+ \qmlsignal QtQuick::TableView::columnMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
+ \since 6.8
+
+ This signal is emitted when a column is moved. The column's logical index is specified by
+ \a logicalIndex, the old index by \a oldVisualIndex, and the new index position by
+ \a newVisualIndex.
+*/
+
+/*!
+ \qmlsignal QtQuick::TableView::rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
+ \since 6.8
+
+ This signal is emitted when a row is moved. The row's logical index is specified by
+ \a logicalIndex, the old index by \a oldVisualIndex, and the new index position by
+ \a newVisualIndex.
+*/
+
+/*!
\qmlattachedproperty TableView QtQuick::TableView::view
This attached property holds the view that manages the delegate instance.
@@ -1459,6 +1524,7 @@ static const char* kRequiredProperties = "_qt_tableview_requiredpropertymask";
static const char* kRequiredProperty_selected = "selected";
static const char* kRequiredProperty_current = "current";
static const char* kRequiredProperty_editing = "editing";
+static const char* kRequiredProperty_containsDrag = "containsDrag";
QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildState state)
{
@@ -1849,28 +1915,43 @@ void QQuickTableViewPrivate::updateSelection(const QRect &oldSelection, const QR
{
const QModelIndex startIndex = qaim->index(newRect.y(), newRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() + newRect.height(), newRect.x() + newRect.width());
- select = QItemSelection(startIndex, endIndex);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ select.append(QItemSelection(logicalModelIndex, logicalModelIndex));
+ }
}
// Unselect cells in the new minus old rects
if (oldRect.x() < newRect.x()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), newRect.x() - 1);
- deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
} else if (oldRect.x() + oldRect.width() > newRect.x() + newRect.width()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), newRect.x() + newRect.width() + 1);
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ for (auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
}
if (oldRect.y() < newRect.y()) {
const QModelIndex startIndex = qaim->index(oldRect.y(), oldRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() - 1, oldRect.x() + oldRect.width());
- deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
} else if (oldRect.y() + oldRect.height() > newRect.y() + newRect.height()) {
const QModelIndex startIndex = qaim->index(newRect.y() + newRect.height() + 1, oldRect.x());
const QModelIndex endIndex = qaim->index(oldRect.y() + oldRect.height(), oldRect.x() + oldRect.width());
- deselect.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ for (const auto &modelIndex : QItemSelection(startIndex, endIndex).indexes()) {
+ const QModelIndex &logicalModelIndex = qaim->index(logicalRowIndex(modelIndex.row()), logicalColumnIndex(modelIndex.column()));
+ deselect.merge(QItemSelection(logicalModelIndex, logicalModelIndex), QItemSelectionModel::Select);
+ }
}
if (selectionFlag == QItemSelectionModel::Select) {
@@ -1976,7 +2057,7 @@ QRect QQuickTableViewPrivate::selection() const
return QRect(selectionStartCell.x(), selectionStartCell.y(), w, h);
}
-QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step)
+QSizeF QQuickTableViewPrivate::scrollTowardsPoint(const QPointF &pos, const QSizeF &step)
{
Q_Q(QQuickTableView);
@@ -2085,14 +2166,14 @@ QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const
}
}
-int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex) const
+int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex, bool visualIndex) const
{
// Convert QModelIndex to cell index. A cell index is just an
// integer representation of a cell instead of using a QPoint.
const QPoint cell = q_func()->cellAtIndex(modelIndex);
if (!cellIsValid(cell))
return -1;
- return modelIndexAtCell(cell);
+ return modelIndexAtCell(visualIndex ? cell : QPoint(modelIndex.column(), modelIndex.row()));
}
int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) const
@@ -2647,7 +2728,9 @@ FxTableItem *QQuickTableViewPrivate::createFxTableItem(const QPoint &cell, QQmlI
Q_Q(QQuickTableView);
bool ownItem = false;
- int modelIndex = modelIndexAtCell(cell);
+
+ int modelIndex = modelIndexAtCell(isTransposed ? QPoint(logicalRowIndex(cell.x()), logicalColumnIndex(cell.y())) :
+ QPoint(logicalColumnIndex(cell.x()), logicalRowIndex(cell.y())));
QObject* object = model->object(modelIndex, incubationMode);
if (!object) {
@@ -3000,7 +3083,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
const int noExplicitColumnWidth = -1;
- if (cachedColumnWidth.startIndex == column)
+ if (cachedColumnWidth.startIndex == logicalColumnIndex(column))
return cachedColumnWidth.size;
if (syncHorizontally)
@@ -3019,7 +3102,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
qreal columnWidth = noExplicitColumnWidth;
if (columnWidthProvider.isCallable()) {
- auto const columnAsArgument = QJSValueList() << QJSValue(column);
+ auto const columnAsArgument = QJSValueList() << QJSValue(logicalColumnIndex(column));
columnWidth = columnWidthProvider.call(columnAsArgument).toNumber();
if (qIsNaN(columnWidth) || columnWidth < 0)
columnWidth = noExplicitColumnWidth;
@@ -3031,7 +3114,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
columnWidth = noExplicitColumnWidth;
}
- cachedColumnWidth.startIndex = column;
+ cachedColumnWidth.startIndex = logicalColumnIndex(column);
cachedColumnWidth.size = columnWidth;
return columnWidth;
}
@@ -3046,7 +3129,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
const int noExplicitRowHeight = -1;
- if (cachedRowHeight.startIndex == row)
+ if (cachedRowHeight.startIndex == logicalRowIndex(row))
return cachedRowHeight.size;
if (syncVertically)
@@ -3065,7 +3148,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
qreal rowHeight = noExplicitRowHeight;
if (rowHeightProvider.isCallable()) {
- auto const rowAsArgument = QJSValueList() << QJSValue(row);
+ auto const rowAsArgument = QJSValueList() << QJSValue(logicalRowIndex(row));
rowHeight = rowHeightProvider.call(rowAsArgument).toNumber();
if (qIsNaN(rowHeight) || rowHeight < 0)
rowHeight = noExplicitRowHeight;
@@ -3077,7 +3160,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
rowHeight = noExplicitRowHeight;
}
- cachedRowHeight.startIndex = row;
+ cachedRowHeight.startIndex = logicalRowIndex(row);
cachedRowHeight.size = rowHeight;
return rowHeight;
}
@@ -4288,11 +4371,13 @@ void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
item->setZ(1);
const QPoint cell = cellAtModelIndex(modelIndex);
- const bool current = currentInSelectionModel(cell);
- const bool selected = selectedInSelectionModel(cell);
+ const QPoint visualCell = QPoint(visualColumnIndex(cell.x()), visualRowIndex(cell.y()));
+ const bool current = currentInSelectionModel(visualCell);
+ const bool selected = selectedInSelectionModel(visualCell);
setRequiredProperty(kRequiredProperty_current, QVariant::fromValue(current), modelIndex, object, true);
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, true);
setRequiredProperty(kRequiredProperty_editing, QVariant::fromValue(false), modelIndex, item, true);
+ setRequiredProperty(kRequiredProperty_containsDrag, QVariant::fromValue(false), modelIndex, item, true);
if (auto attached = getAttachedObject(object))
attached->setView(q);
@@ -4309,11 +4394,13 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
{
const QPoint cell = cellAtModelIndex(modelIndex);
- const bool current = currentInSelectionModel(cell);
- const bool selected = selectedInSelectionModel(cell);
+ const QPoint visualCell = QPoint(visualColumnIndex(cell.x()), visualRowIndex(cell.y()));
+ const bool current = currentInSelectionModel(visualCell);
+ const bool selected = selectedInSelectionModel(visualCell);
setRequiredProperty(kRequiredProperty_current, QVariant::fromValue(current), modelIndex, object, false);
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(selected), modelIndex, object, false);
// Note: the edit item will never be reused, so no reason to set kRequiredProperty_editing
+ setRequiredProperty(kRequiredProperty_containsDrag, QVariant::fromValue(false), modelIndex, object, false);
if (auto item = qobject_cast<QQuickItem*>(object))
QQuickItemPrivate::get(item)->setCulled(false);
@@ -4896,6 +4983,7 @@ void QQuickTableViewPrivate::init()
hoverHandler = new QQuickTableViewHoverHandler(q);
resizeHandler = new QQuickTableViewResizeHandler(q);
+
hoverHandler->setEnabled(resizableRows || resizableColumns);
resizeHandler->setEnabled(resizableRows || resizableColumns);
@@ -5668,6 +5756,10 @@ void QQuickTableView::setSyncView(QQuickTableView *view)
if (d->assignedSyncView == view)
return;
+ // Clear existing index mapping information maintained
+ // in the current view
+ d->clearIndexMapping();
+
d->assignedSyncView = view;
d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
@@ -6012,6 +6104,169 @@ void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode,
}
#endif
+void QQuickTableView::moveColumn(int source, int destination)
+{
+ Q_D(QQuickTableView);
+ d->moveSection(source, destination, Qt::Horizontal);
+}
+
+void QQuickTableView::moveRow(int source, int destination)
+{
+ Q_D(QQuickTableView);
+ d->moveSection(source, destination, Qt::Vertical);
+}
+
+void QQuickTableViewPrivate::moveSection(int source, int destination, Qt::Orientations orientation)
+{
+ Q_Q(QQuickTableView);
+
+ if (source < 0 || destination < 0 ||
+ (orientation == Qt::Horizontal &&
+ (source >= tableSize.width() || destination >= tableSize.width())) ||
+ (orientation == Qt::Vertical &&
+ (source >= tableSize.height() || destination >= tableSize.height())))
+ return;
+
+ if (source == destination)
+ return;
+
+ if (m_sectionState != SectionState::Moving) {
+ m_sectionState = SectionState::Moving;
+ if (syncView)
+ syncView->d_func()->moveSection(source, destination, orientation);
+ else {
+ // Initialize the visual and logical index mapping
+ initializeIndexMapping();
+
+ // Set current index mapping according to moving rows or columns
+ SectionData *visualIndex = nullptr;
+ SectionData *logicalIndex = nullptr;
+
+ if (orientation == Qt::Horizontal) {
+ visualIndex = visualIndices[0].data();
+ logicalIndex = logicalIndices[0].data();
+ } else if (orientation == Qt::Vertical) {
+ visualIndex = visualIndices[1].data();
+ logicalIndex = logicalIndices[1].data();
+ }
+
+ const int logical = logicalIndex[source].index;
+ int visual = source;
+
+ if (destination > source) {
+ while (visual < destination) {
+ SectionData &visualData = visualIndex[logicalIndex[visual + 1].index];
+ SectionData &logicalData = logicalIndex[visual];
+ visualData.prevIndex = visualData.index;
+ visualData.index = visual;
+ logicalData.prevIndex = logicalData.index;
+ logicalData.index = logicalIndex[visual + 1].index;
+ ++visual;
+ }
+ } else {
+ while (visual > destination) {
+ SectionData &visualData = visualIndex[logicalIndex[visual - 1].index];
+ SectionData &logicalData = logicalIndex[visual];
+ visualData.prevIndex = visualData.index;
+ visualData.index = visual;
+ logicalData.prevIndex = logicalData.index;
+ logicalData.index = logicalIndex[visual - 1].index;
+ --visual;
+ }
+ }
+
+ visualIndex[logical].prevIndex = visualIndex[logical].index;
+ visualIndex[logical].index = destination;
+ logicalIndex[destination].prevIndex = logicalIndex[destination].index;
+ logicalIndex[destination].index = logical;
+
+ // Trigger section move for horizontal and vertical child views
+ // Used in a case where moveSection() triggered for table view
+ for (auto syncChild : std::as_const(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ if (syncChild_d->m_sectionState != SectionState::Moving &&
+ ((syncChild_d->syncHorizontally && orientation == Qt::Horizontal) ||
+ (syncChild_d->syncVertically && orientation == Qt::Vertical)))
+ syncChild_d->moveSection(source, destination, orientation);
+ }
+ }
+
+ // Rebuild the view to reflect the section order
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+ m_sectionState = SectionState::Idle;
+
+ // Emit section moved signal for the sections moved in the view
+ const int startIndex = (source > destination) ? destination : source;
+ const int endIndex = (source > destination) ? source : destination;
+ const int mapIndex = static_cast<int>(orientation) - 1;
+ for (int index = startIndex; index <= endIndex; index++) {
+ const SectionData *logicalDataIndices = (syncView ? syncView->d_func()->logicalIndices[mapIndex].constData() : logicalIndices[mapIndex].constData());
+ const SectionData *visualDataIndices = syncView ? syncView->d_func()->visualIndices[mapIndex].constData() : visualIndices[mapIndex].constData();
+ const int prevLogicalIndex = logicalDataIndices[index].prevIndex;
+ if (orientation == Qt::Horizontal)
+ emit q->columnMoved(prevLogicalIndex, visualDataIndices[prevLogicalIndex].prevIndex, visualDataIndices[prevLogicalIndex].index);
+ else
+ emit q->rowMoved(prevLogicalIndex, visualDataIndices[prevLogicalIndex].prevIndex, visualDataIndices[prevLogicalIndex].index);
+ }
+ }
+}
+
+void QQuickTableView::clearColumnReordering()
+{
+ Q_D(QQuickTableView);
+ d->clearSection(Qt::Horizontal);
+}
+
+void QQuickTableView::clearRowReordering()
+{
+ Q_D(QQuickTableView);
+ d->clearSection(Qt::Vertical);
+}
+
+void QQuickTableViewPrivate::clearSection(Qt::Orientations orientation)
+{
+ Q_Q(QQuickTableView);
+
+ const int mapIndex = static_cast<int>(orientation) - 1;
+ const QList<SectionData> oldLogicalIndices = syncView ? syncView->d_func()->logicalIndices[mapIndex] : logicalIndices[mapIndex];
+ const QList<SectionData> oldVisualIndices = syncView ? syncView->d_func()->visualIndices[mapIndex] : visualIndices[mapIndex];;
+
+ if (syncView)
+ syncView->d_func()->clearSection(orientation);
+ else {
+ // Clear the index mapping and rebuild the table
+ logicalIndices[mapIndex].clear();
+ visualIndices[mapIndex].clear();
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+ }
+
+ // Emit section moved signal for the sections moved in the view
+ for (int index = 0; index < oldLogicalIndices.size(); index++) {
+ const SectionData *logicalDataIndices = oldLogicalIndices.constData();
+ const SectionData *visualDataIndices = oldVisualIndices.constData();
+ if (logicalDataIndices[index].index != index) {
+ const int currentIndex = logicalDataIndices[index].index;
+ if (orientation == Qt::Horizontal)
+ emit q->columnMoved(currentIndex, visualDataIndices[currentIndex].index, index);
+ else
+ emit q->rowMoved(currentIndex, visualDataIndices[currentIndex].index, index);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::setContainsDragOnDelegateItem(const QModelIndex &modelIndex, bool overlay)
+{
+ if (!modelIndex.isValid())
+ return;
+
+ const int cellIndex = modelIndexToCellIndex(modelIndex);
+ if (!loadedItems.contains(cellIndex))
+ return;
+ const QPoint cell = cellAtModelIndex(cellIndex);
+ QQuickItem *item = loadedTableItem(cell)->item;
+ setRequiredProperty(kRequiredProperty_containsDrag, QVariant::fromValue(overlay), cellIndex, item, false);
+}
+
QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
{
Q_D(const QQuickTableView);
@@ -6190,9 +6445,9 @@ void QQuickTableView::setColumnWidth(int column, qreal size)
return;
if (size < 0)
- d->explicitColumnWidths.remove(column);
+ d->explicitColumnWidths.remove(d->logicalColumnIndex(column));
else
- d->explicitColumnWidths.insert(column, size);
+ d->explicitColumnWidths.insert(d->logicalColumnIndex(column), size);
if (d->loadedItems.isEmpty())
return;
@@ -6225,7 +6480,7 @@ qreal QQuickTableView::explicitColumnWidth(int column) const
if (d->syncHorizontally)
return d->syncView->explicitColumnWidth(column);
- const auto it = d->explicitColumnWidths.constFind(column);
+ const auto it = d->explicitColumnWidths.constFind(d->logicalColumnIndex(column));
if (it != d->explicitColumnWidths.constEnd())
return *it;
return -1;
@@ -6248,9 +6503,9 @@ void QQuickTableView::setRowHeight(int row, qreal size)
return;
if (size < 0)
- d->explicitRowHeights.remove(row);
+ d->explicitRowHeights.remove(d->logicalRowIndex(row));
else
- d->explicitRowHeights.insert(row, size);
+ d->explicitRowHeights.insert(d->logicalRowIndex(row), size);
if (d->loadedItems.isEmpty())
return;
@@ -6283,7 +6538,7 @@ qreal QQuickTableView::explicitRowHeight(int row) const
if (d->syncVertically)
return d->syncView->explicitRowHeight(row);
- const auto it = d->explicitRowHeights.constFind(row);
+ const auto it = d->explicitRowHeights.constFind(d->logicalRowIndex(row));
if (it != d->explicitRowHeights.constEnd())
return *it;
return -1;
@@ -6299,14 +6554,15 @@ QModelIndex QQuickTableView::modelIndex(const QPoint &cell) const
if (!qaim)
return {};
- return qaim->index(cell.y(), cell.x());
+ return qaim->index(d->logicalRowIndex(cell.y()), d->logicalColumnIndex(cell.x()));
}
QPoint QQuickTableView::cellAtIndex(const QModelIndex &index) const
{
if (!index.isValid() || index.parent().isValid())
return {-1, -1};
- return {index.column(), index.row()};
+ Q_D(const QQuickTableView);
+ return {d->visualColumnIndex(index.column()), d->visualRowIndex(index.row())};
}
#if QT_DEPRECATED_SINCE(6, 4)
@@ -6371,7 +6627,8 @@ void QQuickTableView::edit(const QModelIndex &index)
// is currently dependent of the QQmlTableInstanceModel that was used to create the object
// in order to initialize required properties, so we need to set the editItem variable
// early on, so that we can use it in setRequiredProperty.
- d->editIndex = modelIndex(d->cellAtModelIndex(serializedModelIndex));
+ const QPoint cell = d->cellAtModelIndex(serializedModelIndex);
+ d->editIndex = modelIndex({d->visualColumnIndex(cell.x()), d->visualRowIndex(cell.y())});
d->editItem = qmlobject_cast<QQuickItem*>(object);
if (!d->editItem)
return;
@@ -6400,7 +6657,7 @@ void QQuickTableView::edit(const QModelIndex &index)
d->editModel->setModel(d->tableModel->model());
d->editModel->setDelegate(attached->editDelegate());
- const int cellIndex = d->modelIndexToCellIndex(index);
+ const int cellIndex = d->modelIndexToCellIndex(index, false);
QObject* object = d->editModel->object(cellIndex, QQmlIncubator::Synchronous);
if (!object) {
d->editIndex = QModelIndex();
@@ -6451,7 +6708,7 @@ void QQuickTableView::closeEditor()
d->editItem = nullptr;
cellItem->setZ(1);
- const int cellIndex = d->modelIndexToCellIndex(d->editIndex);
+ const int cellIndex = d->modelIndexToCellIndex(d->editIndex, false);
d->setRequiredProperty(kRequiredProperty_editing, QVariant::fromValue(false), cellIndex, cellItem, false);
// Remove the extra reference we sat on the cell item from edit()
d->model->release(cellItem, QQmlInstanceModel::NotReusable);
@@ -6669,7 +6926,6 @@ void QQuickTableView::setResizableRows(bool enabled)
}
// ----------------------------------------------
-
QQuickTableViewHoverHandler::QQuickTableViewHoverHandler(QQuickTableView *view)
: QQuickHoverHandler(view->contentItem())
{
@@ -6720,13 +6976,35 @@ void QQuickTableViewHoverHandler::handleEventPoint(QPointerEvent *event, QEventP
// ----------------------------------------------
-QQuickTableViewResizeHandler::QQuickTableViewResizeHandler(QQuickTableView *view)
+QQuickTableViewPointerHandler::QQuickTableViewPointerHandler(QQuickTableView *view)
: QQuickSinglePointHandler(view->contentItem())
{
- setMargin(5);
// Set a grab permission that stops the flickable, as well as
// any drag handler inside the delegate, from stealing the drag.
setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
+}
+
+bool QQuickTableViewPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
+{
+ if (!QQuickSinglePointHandler::wantsEventPoint(event, point))
+ return false;
+
+ // If we have a mouse wheel event then we do not want to do anything related to resizing.
+ if (event->type() == QEvent::Type::Wheel)
+ return false;
+
+ // When the user is flicking, we disable resizing, so that
+ // he doesn't start to resize by accident.
+ const auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ return !tableView->isMoving();
+}
+
+// ----------------------------------------------
+
+QQuickTableViewResizeHandler::QQuickTableViewResizeHandler(QQuickTableView *view)
+ : QQuickTableViewPointerHandler(view)
+{
+ setMargin(5);
setObjectName("tableViewResizeHandler");
}
@@ -6754,23 +7032,14 @@ void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
}
}
-bool QQuickTableViewResizeHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
-{
- if (!QQuickSinglePointHandler::wantsEventPoint(event, point))
- return false;
-
- // If we have a mouse wheel event then we do not want to do anything related to resizing.
- if (event->type() == QEvent::Type::Wheel)
- return false;
-
- // When the user is flicking, we disable resizing, so that
- // he doesn't start to resize by accident.
- auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
- return !tableView->isMoving();
-}
-
void QQuickTableViewResizeHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point)
{
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto *tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ const auto *activeHandler = tableViewPrivate->activePointerHandler();
+ if (activeHandler && !qobject_cast<const QQuickTableViewResizeHandler *>(activeHandler))
+ return;
+
// Resolve which state we're in first...
updateState(point);
// ...and act on it next
@@ -6835,6 +7104,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
// pointer handlers to do flicking, so setting an exclusive grab (together
// with grab permissions) doens't work ATM.
tableView->setFiltersChildMouseEvents(false);
+ tableViewPrivate->setActivePointerHandler(this);
break;
case DraggingStarted:
setExclusiveGrab(event, point, true);
@@ -6856,6 +7126,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
break; }
case DraggingFinished: {
tableView->setFiltersChildMouseEvents(true);
+ tableViewPrivate->setActivePointerHandler(nullptr);
#if QT_CONFIG(cursor)
tableViewPrivate->updateCursor();
#endif
@@ -6864,6 +7135,310 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
}
// ----------------------------------------------
+QQuickTableViewSectionDragHandler::QQuickTableViewSectionDragHandler(QQuickTableView *view)
+ : QQuickTableViewPointerHandler(view)
+{
+ setObjectName("tableViewDragHandler");
+}
+
+QQuickTableViewSectionDragHandler::~QQuickTableViewSectionDragHandler()
+{
+ resetDragData();
+}
+
+void QQuickTableViewSectionDragHandler::resetDragData()
+{
+ if (m_state != Listening) {
+ m_state = Listening;
+ resetSectionOverlay();
+ m_source = -1;
+ m_destination = -1;
+ if (m_grabResult.data())
+ m_grabResult.data()->disconnect();
+ if (!m_drag.isNull()) {
+ m_drag->disconnect();
+ delete m_drag;
+ }
+ if (!m_dropArea.isNull()) {
+ m_dropArea->disconnect();
+ delete m_dropArea;
+ }
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ tableView->setFiltersChildMouseEvents(true);
+ }
+}
+
+void QQuickTableViewSectionDragHandler::resetSectionOverlay()
+{
+ if (m_destination != -1) {
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto *tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ const int row = (m_sectionOrientation == Qt::Horizontal) ? 0 : m_destination;
+ const int column = (m_sectionOrientation == Qt::Horizontal) ? m_destination : 0;
+ tableViewPrivate->setContainsDragOnDelegateItem(tableView->index(row, column), false);
+ m_destination = -1;
+ }
+}
+
+void QQuickTableViewSectionDragHandler::grabSection()
+{
+ // Generate the transparent section image in pixmap
+ QPixmap pixmap(m_grabResult->image().size());
+ pixmap.fill(Qt::transparent);
+ QPainter painter(&pixmap);
+ painter.setOpacity(0.6);
+ painter.drawImage(0, 0, m_grabResult->image());
+ painter.end();
+
+ // Specify the pixmap and mime data to be as drag object
+ auto *mimeData = new QMimeData();
+ mimeData->setImageData(pixmap);
+ m_drag->setMimeData(mimeData);
+ m_drag->setPixmap(pixmap);
+}
+
+void QQuickTableViewSectionDragHandler::handleDrop(QQuickDragEvent *event)
+{
+ Q_UNUSED(event);
+
+ if (m_state == Dragging) {
+ event->setAccepted(true);
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto *tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ tableViewPrivate->moveSection(m_source, m_destination, m_sectionOrientation);
+ m_state = DraggingFinished;
+ resetSectionOverlay();
+ if (m_scrollTimer.isActive())
+ m_scrollTimer.stop();
+ }
+}
+
+void QQuickTableViewSectionDragHandler::handleDrag(QQuickDragEvent *event)
+{
+ Q_UNUSED(event);
+
+ if (m_state == Dragging) {
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ const QPoint dragItemPosition(tableView->contentX() + event->x(), tableView->contentY() + event->y());
+ const auto *sourceItem = qobject_cast<QQuickItem *>(m_drag->source());
+ const QPoint targetCell = tableView->cellAtPosition(dragItemPosition, true);
+
+ auto *tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ const int newDestination = (m_sectionOrientation == Qt::Horizontal) ? targetCell.x() : targetCell.y();
+ if (newDestination != m_destination) {
+ // Reset the overlay property in the existing model delegate item
+ resetSectionOverlay();
+ // Set the overlay property in the new model delegate item
+ const int row = (m_sectionOrientation == Qt::Horizontal) ? 0 : newDestination;
+ const int column = (m_sectionOrientation == Qt::Horizontal) ? newDestination : 0;
+ tableViewPrivate->setContainsDragOnDelegateItem(tableView->index(row, column), true);
+ m_destination = newDestination;
+ }
+
+ // Scroll header view while section item moves out of the table boundary
+ const QPoint dragItemStartPos = (m_sectionOrientation == Qt::Horizontal) ? QPoint(dragItemPosition.x() - sourceItem->width() / 2, dragItemPosition.y()) :
+ QPoint(dragItemPosition.x(), dragItemPosition.y() - sourceItem->height() / 2);
+ const QPoint dragItemEndPos = (m_sectionOrientation == Qt::Horizontal) ? QPoint(dragItemPosition.x() + sourceItem->width() / 2, dragItemPosition.y()) :
+ QPoint(dragItemPosition.x(), dragItemPosition.y() + sourceItem->height() / 2);
+ const bool useStartPos = (m_sectionOrientation == Qt::Horizontal) ? (dragItemStartPos.x() <= tableView->contentX()) : (dragItemStartPos.y() <= tableView->contentY());
+ const bool useEndPos = (m_sectionOrientation == Qt::Horizontal) ? (dragItemEndPos.x() >= tableView->width()) : (dragItemEndPos.y() >= tableView->height());
+ if (useStartPos || useEndPos) {
+ if (!m_scrollTimer.isActive()) {
+ m_dragPoint = (m_sectionOrientation == Qt::Horizontal) ? QPoint(useStartPos ? dragItemStartPos.x() : dragItemEndPos.x(), 0) :
+ QPoint(0, useStartPos ? dragItemStartPos.y() : dragItemEndPos.y());
+ m_scrollTimer.start(1);
+ }
+ } else {
+ if (m_scrollTimer.isActive())
+ m_scrollTimer.stop();
+ }
+ }
+}
+
+void QQuickTableViewSectionDragHandler::handleDragDropAction(Qt::DropAction action)
+{
+ // Reset the overlay property in the model delegate item when drag or drop
+ // happens outside specified drop area (i.e. during ignore action)
+ if (action == Qt::IgnoreAction) {
+ resetSectionOverlay();
+ if (m_scrollTimer.isActive())
+ m_scrollTimer.stop();
+ }
+}
+
+void QQuickTableViewSectionDragHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point)
+{
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto *tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ const auto *activeHandler = tableViewPrivate->activePointerHandler();
+ if (activeHandler && !qobject_cast<const QQuickTableViewSectionDragHandler *>(activeHandler))
+ return;
+
+ if (m_state == DraggingFinished)
+ resetDragData();
+
+ if (point.state() == QEventPoint::Pressed) {
+ // Reset the information in the drag handler
+ resetDragData();
+ // Activate the passive grab to get further move updates
+ setPassiveGrab(event, point, true);
+ // Disable flicking while dragging. TableView uses filtering instead of
+ // pointer handlers to do flicking, so setting an exclusive grab (together
+ // with grab permissions) doens't work ATM.
+ auto *tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ tableView->setFiltersChildMouseEvents(false);
+ m_state = Tracking;
+ } else if (point.state() == QEventPoint::Released) {
+ // Reset the information in the drag handler
+ resetDragData();
+ } else if (point.state() == QEventPoint::Updated) {
+ // Check to see that the movement can be considered as dragging
+ const qreal distX = point.position().x() - point.pressPosition().x();
+ const qreal distY = point.position().y() - point.pressPosition().y();
+ const qreal dragDist = qSqrt(distX * distX + distY * distY);
+ if (dragDist > qApp->styleHints()->startDragDistance()) {
+ switch (m_state) {
+ case Tracking: {
+ // Grab the image for dragging header
+ const QPoint cell = tableView->cellAtPosition(point.position(), true);
+ auto *item = tableView->itemAtCell(cell);
+ if (m_drag.isNull()) {
+ m_drag = new QDrag(item);
+ connect(m_drag.data(), &QDrag::actionChanged, this,
+ &QQuickTableViewSectionDragHandler::handleDragDropAction);
+ }
+ // Connect the timer for scroling
+ QObject::connect(&m_scrollTimer, &QTimer::timeout, [&]{
+ const QSizeF dist = tableViewPrivate->scrollTowardsPoint(m_dragPoint, m_step);
+ m_dragPoint.rx() += dist.width() > 0 ? m_step.width() : -m_step.width();
+ m_dragPoint.ry() += dist.height() > 0 ? m_step.height() : -m_step.height();
+ m_step = QSizeF(qAbs(dist.width() * 0.010), qAbs(dist.height() * 0.010));
+ });
+ // Set the drop area
+ if (m_dropArea.isNull()) {
+ m_dropArea = new QQuickDropArea(tableView);
+ m_dropArea->setSize(tableView->size());
+ connect(m_dropArea, &QQuickDropArea::positionChanged, this,
+ &QQuickTableViewSectionDragHandler::handleDrag);
+ connect(m_dropArea, &QQuickDropArea::dropped, this,
+ &QQuickTableViewSectionDragHandler::handleDrop);
+ }
+ // Grab the image of the section
+ m_grabResult = item->grabToImage();
+ connect(m_grabResult.data(), &QQuickItemGrabResult::ready, this,
+ &QQuickTableViewSectionDragHandler::grabSection);
+ // Update source depending on the type of orientation
+ m_source = (m_sectionOrientation == Qt::Horizontal) ? cell.x() : cell.y();
+ m_state = DraggingStarted;
+ // Set drag handler as active and it further handles section pointer events
+ tableViewPrivate->setActivePointerHandler(this);
+ }
+ break;
+
+ case DraggingStarted: {
+ if (m_drag && m_drag->mimeData()) {
+ if (auto *item = qobject_cast<QQuickItem *>(m_drag->source())) {
+ m_state = Dragging;
+ const QPointF itemPos = item->mapFromItem(tableView->contentItem(), point.position());
+ Q_UNUSED(itemPos);
+ m_drag->setHotSpot(m_sectionOrientation == Qt::Horizontal ? QPoint(item->width()/2, itemPos.y()) : QPoint(itemPos.x(), item->height()/2));
+ m_drag->exec();
+ // Reset the active handler
+ tableViewPrivate->setActivePointerHandler(nullptr);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+// ----------------------------------------------
+void QQuickTableViewPrivate::initSectionDragHandler(Qt::Orientation orientation)
+{
+ if (!sectionDragHandler) {
+ Q_Q(QQuickTableView);
+ sectionDragHandler = new QQuickTableViewSectionDragHandler(q);
+ sectionDragHandler->setSectionOrientation(orientation);
+ }
+}
+
+void QQuickTableViewPrivate::destroySectionDragHandler()
+{
+ if (sectionDragHandler)
+ delete sectionDragHandler;
+}
+
+void QQuickTableViewPrivate::initializeIndexMapping()
+{
+ auto initIndices = [](auto& visualIndex, auto& logicalIndex, int size) {
+ visualIndex.resize(size);
+ logicalIndex.resize(size);
+ for (int index = 0; index < size; ++index)
+ visualIndex[index].index = logicalIndex[index].index = index;
+ };
+
+ if (!tableSize.isEmpty()) {
+ if (visualIndices[0].size() != tableSize.width()
+ || logicalIndices[0].size() != tableSize.width())
+ initIndices(visualIndices[0], logicalIndices[0], tableSize.width());
+
+ if (visualIndices[1].size() != tableSize.height()
+ || logicalIndices[1].size() != tableSize.height())
+ initIndices(visualIndices[1], logicalIndices[1], tableSize.height());
+ }
+}
+
+void QQuickTableViewPrivate::clearIndexMapping()
+{
+ logicalIndices[0].clear();
+ visualIndices[0].clear();
+
+ logicalIndices[1].clear();
+ visualIndices[1].clear();
+}
+
+int QQuickTableViewPrivate::logicalRowIndex(const int visualIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->logicalRowIndex(visualIndex);
+ if (logicalIndices[1].isEmpty() || visualIndex < 0)
+ return visualIndex;
+ return logicalIndices[1].constData()[visualIndex].index;
+}
+
+int QQuickTableViewPrivate::logicalColumnIndex(const int visualIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->logicalColumnIndex(visualIndex);
+ if (logicalIndices[0].isEmpty() || visualIndex < 0)
+ return visualIndex;
+ return logicalIndices[0].constData()[visualIndex].index;
+}
+
+int QQuickTableViewPrivate::visualRowIndex(const int logicalIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->visualRowIndex(logicalIndex);
+ if (visualIndices[1].isEmpty() || logicalIndex < 0)
+ return logicalIndex;
+ return visualIndices[1].constData()[logicalIndex].index;
+}
+
+int QQuickTableViewPrivate::visualColumnIndex(const int logicalIndex) const
+{
+ if (syncView)
+ return syncView->d_func()->visualColumnIndex(logicalIndex);
+ if (visualIndices[0].isEmpty() || logicalIndex < 0)
+ return logicalIndex;
+ return visualIndices[0].constData()[logicalIndex].index;
+}
+
+// ----------------------------------------------
QQuickTableViewTapHandler::QQuickTableViewTapHandler(QQuickTableView *view)
: QQuickTapHandler(view->contentItem())
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index cdde6de3d6..10cc53274d 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -233,6 +233,11 @@ public:
Q_INVOKABLE void positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
#endif
+ Q_REVISION(6, 8) Q_INVOKABLE void moveColumn(int source, int destination);
+ Q_REVISION(6, 8) Q_INVOKABLE void moveRow(int source, int destination);
+ Q_REVISION(6, 8) Q_INVOKABLE void clearColumnReordering();
+ Q_REVISION(6, 8) Q_INVOKABLE void clearRowReordering();
+
static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
Q_SIGNALS:
@@ -264,6 +269,9 @@ Q_SIGNALS:
Q_REVISION(6, 5) void editTriggersChanged();
Q_REVISION(6, 5) void layoutChanged();
Q_REVISION(6, 6) void selectionModeChanged();
+ Q_REVISION(6, 8) void rowMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
+ Q_REVISION(6, 8) void columnMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
+
protected:
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 867e03485a..ea49a3309b 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -23,8 +23,8 @@
#include <QtQml/private/qqmlincubator_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
#include <QtQml/qqmlinfo.h>
+#include <QtGui/qdrag.h>
-#include <QtQuick/private/qminimalflatset_p.h>
#include <QtQuick/private/qquickflickable_p_p.h>
#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
#include <QtQuick/private/qquickanimation_p.h>
@@ -32,6 +32,9 @@
#include <QtQuick/private/qquicksinglepointhandler_p.h>
#include <QtQuick/private/qquickhoverhandler_p.h>
#include <QtQuick/private/qquicktaphandler_p.h>
+#include <QtQuick/private/qquickdroparea_p.h>
+
+#include <QtCore/private/qminimalflatset_p.h>
QT_BEGIN_NAMESPACE
@@ -67,15 +70,10 @@ protected:
void handleEventPoint(QPointerEvent *event, QEventPoint &point) override;
};
-/*! \internal
- * TableView uses QQuickTableViewResizeHandler to enable the user to resize
- * rows and columns. By using a custom pointer handler, we can get away with
- * using a single pointer handler for the whole content item, rather than
- * e.g having to split it up into multiple items with drag handlers placed
- * between the cells.
- */
-class QQuickTableViewResizeHandler : public QQuickSinglePointHandler
+class QQuickTableViewPointerHandler : public QQuickSinglePointHandler
{
+ Q_OBJECT
+
public:
enum State {
Listening, // the pointer is not being pressed between the cells
@@ -85,12 +83,28 @@ public:
DraggingFinished // dragging was finished
};
- QQuickTableViewResizeHandler(QQuickTableView *view);
- State state() { return m_state; }
- void updateState(QEventPoint &point);
- void updateDrag(QPointerEvent *event, QEventPoint &point);
+ QQuickTableViewPointerHandler(QQuickTableView *view);
State m_state = Listening;
+ State state() { return m_state; }
+
+protected:
+ bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) override;
+};
+
+/*! \internal
+ * TableView uses QQuickTableViewResizeHandler to enable the user to resize
+ * rows and columns. By using a custom pointer handler, we can get away with
+ * using a single pointer handler for the whole content item, rather than
+ * e.g having to split it up into multiple items with drag handlers placed
+ * between the cells.
+ */
+class QQuickTableViewResizeHandler : public QQuickTableViewPointerHandler
+{
+ Q_OBJECT
+
+public:
+ QQuickTableViewResizeHandler(QQuickTableView *view);
int m_row = -1;
qreal m_rowStartY = -1;
@@ -100,15 +114,54 @@ public:
qreal m_columnStartX = -1;
qreal m_columnStartWidth = -1;
+ void updateState(QEventPoint &point);
+ void updateDrag(QPointerEvent *event, QEventPoint &point);
+
friend class QQuickTableViewPrivate;
protected:
- bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) override;
void handleEventPoint(QPointerEvent *event, QEventPoint &point) override;
void onGrabChanged(QQuickPointerHandler *grabber, QPointingDevice::GrabTransition transition,
QPointerEvent *ev, QEventPoint &point) override;
};
+class QQuickTableViewSectionDragHandler : public QQuickTableViewPointerHandler
+{
+ Q_OBJECT
+
+public:
+ QQuickTableViewSectionDragHandler(QQuickTableView *view);
+ ~QQuickTableViewSectionDragHandler();
+
+ void grabSection();
+
+ void handleDrag(QQuickDragEvent *event);
+ void handleDrop(QQuickDragEvent *event);
+ void handleDragDropAction(Qt::DropAction action);
+
+ void setSectionOrientation(Qt::Orientation orientation) { m_sectionOrientation = orientation; }
+
+ friend class QQuickTableViewPrivate;
+
+protected:
+ void handleEventPoint(QPointerEvent *event, QEventPoint &point) override;
+
+private:
+ void resetDragData();
+ void resetSectionOverlay();
+
+ QSharedPointer<QQuickItemGrabResult> m_grabResult;
+ QPointer<QDrag> m_drag;
+ int m_source = -1;
+ int m_destination = -1;
+ QPointer<QQuickDropArea> m_dropArea;
+ Qt::Orientation m_sectionOrientation;
+
+ QPointF m_dragPoint;
+ QSizeF m_step = QSizeF(1, 1);
+ QTimer m_scrollTimer;
+};
+
/*! \internal
* QQuickTableViewTapHandler used to handle tap events explicitly for table view
*/
@@ -123,7 +176,6 @@ public:
friend class QQuickTableViewPrivate;
};
-
class Q_QUICK_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate, public QQuickSelectable
{
public:
@@ -240,6 +292,11 @@ public:
Done
};
+ enum class SectionState {
+ Idle = 0,
+ Moving
+ };
+
enum class RebuildOption {
None = 0,
All = 0x1,
@@ -393,6 +450,8 @@ public:
QQuickTableViewHoverHandler *hoverHandler = nullptr;
QQuickTableViewResizeHandler *resizeHandler = nullptr;
+ QQuickTableViewSectionDragHandler *sectionDragHandler = nullptr;
+ QQuickTableViewPointerHandler *activePtrHandler = nullptr;
QQmlTableInstanceModel *editModel = nullptr;
QQuickItem *editItem = nullptr;
@@ -403,6 +462,16 @@ public:
QString forcedIncubationMode = qEnvironmentVariable("QT_TABLEVIEW_INCUBATION_MODE");
#endif
+ struct SectionData {
+ int index = -1;
+ int prevIndex = -1;
+ };
+
+ QList<SectionData> visualIndices[Qt::Vertical];
+ QList<SectionData> logicalIndices[Qt::Vertical];
+
+ SectionState m_sectionState = SectionState::Idle;
+
public:
void init();
@@ -410,7 +479,7 @@ public:
int modelIndexAtCell(const QPoint &cell) const;
QPoint cellAtModelIndex(int modelIndex) const;
- int modelIndexToCellIndex(const QModelIndex &modelIndex) const;
+ int modelIndexToCellIndex(const QModelIndex &modelIndex, bool visualIndex = true) const;
inline bool cellIsValid(const QPoint &cell) const { return cell.x() != -1 && cell.y() != -1; }
qreal sizeHintForColumn(int column) const;
@@ -589,7 +658,7 @@ public:
void clearSelection() override;
void normalizeSelection() override;
QRectF selectionRectangle() const override;
- QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) override;
+ QSizeF scrollTowardsPoint(const QPointF &pos, const QSizeF &step) override;
void setCallback(std::function<void(CallBackFlag)> func) override;
void cancelSelectionTracking();
@@ -597,6 +666,22 @@ public:
virtual void updateSelection(const QRect &oldSelection, const QRect &newSelection);
QRect selection() const;
// ----------------
+
+ // Section drag handler
+ void initSectionDragHandler(Qt::Orientation orientation);
+ void destroySectionDragHandler();
+ inline void setActivePointerHandler(QQuickTableViewPointerHandler *handler) { activePtrHandler = handler; }
+ inline QQuickTableViewPointerHandler* activePointerHandler() const { return activePtrHandler; }
+ // Row/Column reordering
+ void moveSection(int source , int destination, Qt::Orientations orientation);
+ void initializeIndexMapping();
+ void clearIndexMapping();
+ void clearSection(Qt::Orientations orientation);
+ virtual int logicalRowIndex(const int visualIndex) const;
+ virtual int logicalColumnIndex(const int visualIndex) const;
+ virtual int visualRowIndex(const int logicalIndex) const;
+ virtual int visualColumnIndex(const int logicalIndex) const;
+ void setContainsDragOnDelegateItem(const QModelIndex &modelIndex, bool overlay);
};
class FxTableItem : public QQuickItemViewFxItem
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index b30084474d..f918a0da2e 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -11,7 +11,6 @@
#include <private/qqmlglobal_p.h>
#include <private/qsgadaptationlayer_p.h>
#include "qsginternaltextnode_p.h"
-#include "qquickimage_p_p.h"
#include "qquicktextutil_p.h"
#include <QtQuick/private/qsgtexture_p.h>
@@ -49,7 +48,7 @@ const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
const int QQuickTextPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD;
QQuickTextPrivate::QQuickTextPrivate()
- : fontInfo(font), elideLayout(nullptr), textLine(nullptr), lineWidth(0)
+ : fontInfo(font), lineWidth(0)
, color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
, lineCount(1), multilengthEos(-1)
, elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
@@ -82,7 +81,6 @@ QQuickTextPrivate::ExtraData::ExtraData()
, doc(nullptr)
, minimumPixelSize(12)
, minimumPointSize(12)
- , nbActiveDownloads(0)
, maximumLineCount(INT_MAX)
, renderTypeQuality(QQuickText::DefaultRenderTypeQuality)
, lineHeightValid(false)
@@ -101,9 +99,6 @@ void QQuickTextPrivate::init()
QQuickTextPrivate::~QQuickTextPrivate()
{
- delete elideLayout;
- delete textLine; textLine = nullptr;
-
if (extra.isAllocated()) {
qDeleteAll(extra->imgTags);
extra->imgTags.clear();
@@ -357,29 +352,33 @@ void QQuickText::resourceRequestFinished()
void QQuickText::imageDownloadFinished()
{
Q_D(QQuickText);
+ if (!d->extra.isAllocated())
+ return;
- (d->extra->nbActiveDownloads)--;
+ if (std::any_of(d->extra->imgTags.cbegin(), d->extra->imgTags.cend(),
+ [] (auto *image) { return image->pix && image->pix->isLoading(); })) {
+ // return if we still have any active download
+ return;
+ }
// when all the remote images have been downloaded,
// if one of the sizes was not specified at parsing time
// we use the implicit size from pixmapcache and re-layout.
- if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
- bool needToUpdateLayout = false;
- for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
- if (!img->size.isValid()) {
- img->size = img->pix->implicitSize();
- needToUpdateLayout = true;
- }
+ bool needToUpdateLayout = false;
+ for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
+ if (!img->size.isValid()) {
+ img->size = img->pix->implicitSize();
+ needToUpdateLayout = true;
}
+ }
- if (needToUpdateLayout) {
- d->textHasChanged = true;
- d->updateLayout();
- } else {
- d->updateType = QQuickTextPrivate::UpdatePaintNode;
- update();
- }
+ if (needToUpdateLayout) {
+ d->textHasChanged = true;
+ d->updateLayout();
+ } else {
+ d->updateType = QQuickTextPrivate::UpdatePaintNode;
+ update();
}
}
@@ -677,7 +676,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
Q_Q(QQuickText);
if (!textLine)
- textLine = new QQuickTextLine;
+ textLine.reset(new QQuickTextLine);
textLine->setFullLayoutTextLength(fullLayoutTextLength);
textLine->setLine(&line);
textLine->setY(height);
@@ -693,7 +692,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height,
if (lineHeight() != 1.0)
textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
- emit q->lineLaidOut(textLine);
+ emit q->lineLaidOut(textLine.get());
height += textLine->height();
}
@@ -1189,7 +1188,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (elide) {
if (!elideLayout) {
- elideLayout = new QTextLayout;
+ elideLayout.reset(new QTextLayout);
elideLayout->setCacheEnabled(true);
}
QTextEngine *engine = layout.engine();
@@ -1239,8 +1238,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (visibleCount == 1)
layout.clearLayout();
} else {
- delete elideLayout;
- elideLayout = nullptr;
+ elideLayout.reset();
}
QTextLine firstLine = visibleCount == 1 && elideLayout
@@ -1289,12 +1287,10 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
if (!image->pix) {
const QQmlContext *context = qmlContext(q);
const QUrl url = context->resolvedUrl(q->baseUrl()).resolved(image->url);
- image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size);
+ image->pix.reset(new QQuickPixmap(context->engine(), url, QRect(), image->size * devicePixelRatio()));
+
if (image->pix->isLoading()) {
image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
- if (!extra.isAllocated() || !extra->nbActiveDownloads)
- extra.value().nbActiveDownloads = 0;
- extra->nbActiveDownloads++;
} else if (image->pix->isReady()) {
if (!image->size.isValid()) {
image->size = image->pix->implicitSize();
@@ -1379,6 +1375,11 @@ void QQuickTextPrivate::updateDocumentText()
rightToLeftText = extra->doc->toPlainText().isRightToLeft();
}
+qreal QQuickTextPrivate::devicePixelRatio() const
+{
+ return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
+}
+
/*!
\qmltype Text
\instantiates QQuickText
@@ -1773,9 +1774,7 @@ QQuickText::~QQuickText()
By default, no variable axes are set.
- \note In order to use variable axes on Windows, the application has to run with either the
- FreeType or DirectWrite font databases. See the documentation for
- QGuiApplication::QGuiApplication() for more information on how to select these technologies.
+ \note On Windows, variable axes are not supported if the optional GDI font backend is in use.
\sa QFont::setVariableAxis()
//! [qml-font-variable-axes]
@@ -1849,6 +1848,53 @@ QQuickText::~QQuickText()
\sa QFont::setFeature()
//! [qml-font-features]
*/
+
+/*!
+ \qmlproperty bool QtQuick::Text::font.contextFontMerging
+ \since 6.8
+
+//! [qml-font-context-font-merging]
+ If the selected font does not contain a certain character, Qt automatically chooses a
+ similar-looking fallback font that contains the character. By default this is done on a
+ character-by-character basis.
+
+ This means that in certain uncommon cases, many different fonts may be used to represent one
+ string of text even if it's in the same script. Setting \c contextFontMerging to true will try
+ finding the fallback font that matches the largest subset of the input string instead. This
+ will be more expensive for strings where missing glyphs occur, but may give more consistent
+ results. By default, \c contextFontMerging is \c{false}.
+
+ \sa QFont::StyleStrategy
+//! [qml-font-context-font-merging]
+*/
+
+/*!
+ \qmlproperty bool QtQuick::Text::font.preferTypoLineMetrics
+ \since 6.8
+
+//! [qml-font-prefer-typo-line-metrics] For compatibility reasons, OpenType fonts contain two
+ competing sets of the vertical line metrics that provide the \l{QFontMetricsF::ascent()}{ascent},
+ \l{QFontMetricsF::descent()}{descent} and \l{QFontMetricsF::leading()}{leading} of the font. These
+ are often referred to as the
+ \l{https://learn.microsoft.com/en-us/typography/opentype/spec/os2#uswinascent}{win} (Windows)
+ metrics and the \l{https://learn.microsoft.com/en-us/typography/opentype/spec/os2#sta}{typo}
+ (typographical) metrics. While the specification recommends using the \c typo metrics for line
+ spacing, many applications prefer the \c win metrics unless the \c{USE_TYPO_METRICS} flag is set in
+ the \l{https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection}{fsSelection}
+ field of the font. For backwards-compatibility reasons, this is also the case for Qt applications.
+ This is not an issue for fonts that set the \c{USE_TYPO_METRICS} flag to indicate that the \c{typo}
+ metrics are valid, nor for fonts where the \c{win} metrics and \c{typo} metrics match up. However,
+ for certain fonts the \c{win} metrics may be larger than the preferable line spacing and the
+ \c{USE_TYPO_METRICS} flag may be unset by mistake. For such fonts, setting
+ \c{font.preferTypoLineMetrics} may give superior results.
+
+ By default, \c preferTypoLineMetrics is \c{false}.
+
+ \sa QFont::StyleStrategy
+//! [qml-font-prefer-typo-line-metrics]
+*/
+
+
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -1903,14 +1949,30 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value)
break;
case ItemDevicePixelRatioHasChanged:
- if (d->renderType == NativeRendering) {
- // 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.
- // Changes of implicit size should be minimal; they are hard to avoid.
- d->implicitWidthValid = false;
- d->implicitHeightValid = false;
- d->updateLayout();
+ {
+ bool needUpdateLayout = false;
+ if (d->renderType == NativeRendering) {
+ // 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.
+ // Changes of implicit size should be minimal; they are hard to avoid.
+ d->implicitWidthValid = false;
+ d->implicitHeightValid = false;
+ needUpdateLayout = true;
+ }
+
+ if (d->extra.isAllocated()) {
+ // check if we have scalable inline images with explicit size set, which should be reloaded
+ for (QQuickStyledTextImgTag *image : std::as_const(d->extra->visibleImgTags)) {
+ if (image->size.isValid() && QQuickPixmap::isScalableImageFormat(image->url)) {
+ image->pix.reset();
+ needUpdateLayout = true;
+ }
+ }
+ }
+
+ if (needUpdateLayout)
+ d->updateLayout();
}
break;
@@ -2751,13 +2813,12 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
node->addTextLayout(QPointF(dx, dy), &d->layout, -1, -1,0, unelidedLineCount);
if (d->elideLayout)
- node->addTextLayout(QPointF(dx, dy), d->elideLayout);
+ node->addTextLayout(QPointF(dx, dy), d->elideLayout.get());
if (d->extra.isAllocated()) {
for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
- QQuickPixmap *pix = img->pix;
- if (pix && pix->isReady())
- node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
+ if (img->pix && img->pix->isReady())
+ node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, img->size.width(), img->size.height()), img->pix->image());
}
}
}
@@ -3044,7 +3105,7 @@ QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const
if (styledText) {
QString link = anchorAt(&layout, translatedMousePos);
if (link.isEmpty() && elideLayout)
- link = anchorAt(elideLayout, translatedMousePos);
+ link = anchorAt(elideLayout.get(), translatedMousePos);
return link;
} else if (richText && extra.isAllocated() && extra->doc) {
translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), availableWidth(), q->effectiveHAlign());
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 2e54ae53a1..6dba7a7d75 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -77,7 +77,6 @@ public:
QString hoveredLink;
int minimumPixelSize;
int minimumPointSize;
- int nbActiveDownloads;
int maximumLineCount;
int renderTypeQuality;
bool lineHeightValid : 1;
@@ -96,8 +95,8 @@ public:
QFontInfo fontInfo;
QTextLayout layout;
- QTextLayout *elideLayout;
- QQuickTextLine *textLine;
+ QScopedPointer<QTextLayout> elideLayout;
+ QScopedPointer<QQuickTextLine> textLine;
qreal lineWidth;
@@ -164,6 +163,8 @@ public:
void ensureDoc();
void updateDocumentText();
+ qreal devicePixelRatio() const;
+
QRectF setupTextLayout(qreal * const baseline);
void setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset = 0);
bool isLinkActivatedConnected();
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 6443b9ef70..a1fb7adcea 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -326,13 +326,15 @@ void QQuickTextDocumentPrivate::load()
setStatus(QQuickTextDocument::Status::Loading, {});
QByteArray data = file.readAll();
doc->setBaseUrl(resolvedUrl.adjusted(QUrl::RemoveFilename));
+#if QT_CONFIG(textmarkdownreader) || QT_CONFIG(texthtmlparser)
const bool plainText = editor->textFormat() == QQuickTextEdit::PlainText;
+#endif
#if QT_CONFIG(textmarkdownreader)
if (!plainText && isMarkdown) {
doc->setMarkdown(QString::fromUtf8(data));
} else
#endif
-#ifndef QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(texthtmlparser)
if (!plainText && isHtml) {
// If a user loads an HTML file, remember the encoding.
// If the user then calls save() later, the same encoding will be used.
@@ -411,7 +413,7 @@ void QQuickTextDocumentPrivate::writeTo(const QUrl &fileUrl)
raw = doc->toMarkdown().toUtf8();
break;
#endif
-#ifndef QT_NO_TEXTHTMLPARSER
+#if QT_CONFIG(texthtmlparser)
case Qt::RichText:
if (sameUrl && encoding) {
QStringEncoder enc(*encoding);
@@ -587,10 +589,24 @@ QSizeF QQuickTextImageHandler::intrinsicSize(
{
if (format.isImageFormat()) {
QTextImageFormat imageFormat = format.toImageFormat();
- const int width = qRound(imageFormat.width());
+ int width = qRound(imageFormat.width());
const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth) && width > 0;
const int height = qRound(imageFormat.height());
const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight) && height > 0;
+ const auto maxWidth = imageFormat.maximumWidth();
+ const bool hasMaxWidth = imageFormat.hasProperty(QTextFormat::ImageMaxWidth) && maxWidth.type() != QTextLength::VariableLength;
+
+ int effectiveMaxWidth = INT_MAX;
+ if (hasMaxWidth) {
+ if (maxWidth.type() == QTextLength::PercentageLength) {
+ effectiveMaxWidth = (doc->pageSize().width() - 2 * doc->documentMargin()) * maxWidth.value(100) / 100;
+ } else {
+ effectiveMaxWidth = maxWidth.rawValue();
+ }
+
+ width = qMin(effectiveMaxWidth, width);
+ }
+
QSizeF size(width, height);
if (!hasWidth || !hasHeight) {
QVariant res = doc->resource(QTextDocument::ImageResource, QUrl(imageFormat.name()));
@@ -605,11 +621,17 @@ QSizeF QQuickTextImageHandler::intrinsicSize(
return size;
}
QSize imgSize = image.size();
+ if (imgSize.width() > effectiveMaxWidth) {
+ // image is bigger than effectiveMaxWidth, scale it down
+ imgSize.setHeight(effectiveMaxWidth * imgSize.height() / (qreal) imgSize.width());
+ imgSize.setWidth(effectiveMaxWidth);
+ }
+
if (!hasWidth) {
if (!hasHeight)
size.setWidth(imgSize.width());
else
- size.setWidth(qRound(height * (imgSize.width() / (qreal) imgSize.height())));
+ size.setWidth(qMin(effectiveMaxWidth, qRound(height * (imgSize.width() / (qreal) imgSize.height()))));
}
if (!hasHeight) {
if (!hasWidth)
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 22bbd6e05b..854c2e17fd 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -376,6 +376,20 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty bool QtQuick::TextEdit::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TextEdit::font.preferTypoLineMetrics
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-prefer-typo-line-metrics
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -862,6 +876,7 @@ void QQuickTextEdit::setHAlign(HAlignment align)
if (d->setHAlign(align, true) && isComponentComplete()) {
d->updateDefaultTextOption();
updateSize();
+ updateWholeDocument();
}
}
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index ef00451788..0826011a54 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -403,6 +403,20 @@ QString QQuickTextInputPrivate::realText() const
\include qquicktext.cpp qml-font-features
*/
+
+/*!
+ \qmlproperty bool QtQuick::TextInput::font.contextFontMerging
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-context-font-merging
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TextInput::font.preferTypoLineMetrics
+ \since 6.8
+
+ \include qquicktext.cpp qml-font-prefer-typo-line-metrics
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index fd33fa86e2..c486fece40 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -660,10 +660,14 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
if (borderStyle == QTextFrameFormat::BorderStyle_None)
return;
- addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
- -frameFormat.rightMargin() - borderWidth,
- -frameFormat.bottomMargin() - borderWidth),
- borderWidth, borderStyle, borderBrush);
+ const auto collapsed = table->format().borderCollapse();
+
+ if (!collapsed) {
+ addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(),
+ -frameFormat.rightMargin() - borderWidth,
+ -frameFormat.bottomMargin() - borderWidth),
+ borderWidth, borderStyle, borderBrush);
+ }
if (table != nullptr) {
int rows = table->rows();
int columns = table->columns();
@@ -673,7 +677,7 @@ void QQuickTextNodeEngine::addFrameDecorations(QTextDocument *document, QTextFra
QTextTableCell cell = table->cellAt(row, column);
QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell);
- addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth,
+ addBorder(cellRect.adjusted(-borderWidth, -borderWidth, collapsed ? -borderWidth : 0, collapsed ? -borderWidth : 0), borderWidth,
borderStyle, borderBrush);
}
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index b8c7878e04..eb969e7476 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -956,6 +956,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.
*/
/*!
@@ -1853,6 +1869,41 @@ void QQuickWindowPrivate::clearFocusObject()
da->clearFocusObject();
}
+void QQuickWindowPrivate::setFocusToTarget(FocusTarget target, Qt::FocusReason reason)
+{
+ if (!contentItem)
+ return;
+
+ QQuickItem *newFocusItem = nullptr;
+ switch (target) {
+ case FocusTarget::First:
+ case FocusTarget::Last: {
+ const bool forward = (target == FocusTarget::First);
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(contentItem, forward);
+ if (newFocusItem) {
+ const auto *itemPriv = QQuickItemPrivate::get(newFocusItem);
+ if (itemPriv->subFocusItem && itemPriv->flags & QQuickItem::ItemIsFocusScope)
+ clearFocusInScope(newFocusItem, itemPriv->subFocusItem, reason);
+ }
+ break;
+ }
+ case FocusTarget::Next:
+ case FocusTarget::Prev: {
+ const auto da = deliveryAgentPrivate();
+ Q_ASSERT(da);
+ QQuickItem *focusItem = da->focusTargetItem() ? da->focusTargetItem() : contentItem;
+ bool forward = (target == FocusTarget::Next);
+ newFocusItem = QQuickItemPrivate::nextPrevItemInTabFocusChain(focusItem, forward);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (newFocusItem)
+ newFocusItem->forceActiveFocus(reason);
+}
+
/*!
\qmlproperty list<QtObject> Window::data
\qmldefault
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 1b1b12de65..8ba4e56515 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -146,6 +146,7 @@ public:
#endif
void clearFocusObject() override;
+ void setFocusToTarget(FocusTarget, Qt::FocusReason) override;
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
diff --git a/src/quick/items/qquickwindowcontainer.cpp b/src/quick/items/qquickwindowcontainer.cpp
index 839a30330d..55806356f6 100644
--- a/src/quick/items/qquickwindowcontainer.cpp
+++ b/src/quick/items/qquickwindowcontainer.cpp
@@ -21,7 +21,6 @@ using namespace Qt::StringLiterals;
\inqmlmodule QtQuick
\ingroup qtquick-visual
\inherits Item
- \instantiates QQuickItem
\since 6.7
\preliminary
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index c4a978dec8..e539880452 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -35,7 +35,15 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow
: QQuickWindow(dd, parent)
{
connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged);
- connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged);
+ connect(this, &QWindow::visibilityChanged, this, [&]{
+ Q_D(QQuickWindowQmlImpl);
+ // Update the window's actual visibility and turn off visibilityExplicitlySet,
+ // so that future applyWindowVisibility() calls do not apply both window state
+ // and visible state, unless setVisibility() is called again by the user.
+ d->visibility = QWindow::visibility();
+ d->visibilityExplicitlySet = false;
+ emit QQuickWindowQmlImpl::visibilityChanged(d->visibility);
+ });
connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
// We shadow the x and y properties, so that we can re-map them in case
@@ -110,6 +118,7 @@ void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
{
Q_D(QQuickWindowQmlImpl);
d->visibility = visibility;
+ d->visibilityExplicitlySet = true;
if (d->componentComplete)
applyWindowVisibility();
}
@@ -190,8 +199,8 @@ void QQuickWindowQmlImpl::applyWindowVisibility()
Q_ASSERT(d->componentComplete);
- const bool visible = d->visibility == AutomaticVisibility
- ? d->visible : d->visibility != Hidden;
+ const bool visible = d->visibilityExplicitlySet
+ ? d->visibility != Hidden : d->visible;
qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this;
@@ -234,20 +243,30 @@ void QQuickWindowQmlImpl::applyWindowVisibility()
}
}
- if (d->visibleExplicitlySet && ((d->visibility == Hidden && d->visible) ||
- (d->visibility > AutomaticVisibility && !d->visible))) {
+ if (d->visibleExplicitlySet && d->visibilityExplicitlySet &&
+ ((d->visibility == Hidden && d->visible) ||
+ (d->visibility > AutomaticVisibility && !d->visible))) {
// FIXME: Should we bail out in this case?
qmlWarning(this) << "Conflicting properties 'visible' and 'visibility'";
}
if (d->visibility == AutomaticVisibility) {
+ // We're either showing for the first time, with the default
+ // visibility of AutomaticVisibility, or the user has called
+ // setVisibility with AutomaticVisibility at some point, so
+ // apply both window state and visible.
if (QWindow::parent() || visualParent())
setWindowState(Qt::WindowNoState);
else
setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
QQuickWindow::setVisible(d->visible);
- } else {
+ } else if (d->visibilityExplicitlySet) {
+ // We're not AutomaticVisibility, but the user has requested
+ // an explicit visibility, so apply both window state and visible.
QQuickWindow::setVisibility(d->visibility);
+ } else {
+ // Our window state should be up to date, so only apply visible
+ QQuickWindow::setVisible(d->visible);
}
}
diff --git a/src/quick/items/qquickwindowmodule_p_p.h b/src/quick/items/qquickwindowmodule_p_p.h
index bda666e15b..227b8aa01e 100644
--- a/src/quick/items/qquickwindowmodule_p_p.h
+++ b/src/quick/items/qquickwindowmodule_p_p.h
@@ -30,6 +30,8 @@ public:
bool visible = false;
bool visibleExplicitlySet = false;
QQuickWindow::Visibility visibility = QQuickWindow::AutomaticVisibility;
+ bool visibilityExplicitlySet = false;
+
QV4::PersistentValue rootItemMarker;
QMetaObject::Connection itemParentWindowChangeListener;