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.cpp39
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h15
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp306
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h4
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp109
-rw-r--r--src/quick/items/qquickaccessibleattached_p.h46
-rw-r--r--src/quick/items/qquickanchors.cpp12
-rw-r--r--src/quick/items/qquickanchors_p.h4
-rw-r--r--src/quick/items/qquickanchors_p_p.h2
-rw-r--r--src/quick/items/qquickanimatedimage.cpp83
-rw-r--r--src/quick/items/qquickanimatedimage_p.h4
-rw-r--r--src/quick/items/qquickanimatedimage_p_p.h24
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h2
-rw-r--r--src/quick/items/qquickborderimage.cpp126
-rw-r--r--src/quick/items/qquickborderimage_p.h3
-rw-r--r--src/quick/items/qquickborderimage_p_p.h17
-rw-r--r--src/quick/items/qquickclipnode_p.h2
-rw-r--r--src/quick/items/qquickcolorgroup.cpp29
-rw-r--r--src/quick/items/qquickcolorgroup_p.h12
-rw-r--r--src/quick/items/qquickdrag.cpp86
-rw-r--r--src/quick/items/qquickdrag_p.h57
-rw-r--r--src/quick/items/qquickdrag_p_p.h4
-rw-r--r--src/quick/items/qquickdroparea.cpp13
-rw-r--r--src/quick/items/qquickdroparea_p.h44
-rw-r--r--src/quick/items/qquickevents.cpp19
-rw-r--r--src/quick/items/qquickevents_p_p.h93
-rw-r--r--src/quick/items/qquickflickable.cpp340
-rw-r--r--src/quick/items/qquickflickable_p.h4
-rw-r--r--src/quick/items/qquickflickable_p_p.h82
-rw-r--r--src/quick/items/qquickflickablebehavior_p.h7
-rw-r--r--src/quick/items/qquickflipable.cpp90
-rw-r--r--src/quick/items/qquickflipable_p.h4
-rw-r--r--src/quick/items/qquickfocusscope_p.h4
-rw-r--r--src/quick/items/qquickframebufferobject.cpp4
-rw-r--r--src/quick/items/qquickgraphicsconfiguration.cpp104
-rw-r--r--src/quick/items/qquickgraphicsconfiguration.h3
-rw-r--r--src/quick/items/qquickgraphicsconfiguration_p.h7
-rw-r--r--src/quick/items/qquickgraphicsdevice.cpp21
-rw-r--r--src/quick/items/qquickgraphicsdevice.h4
-rw-r--r--src/quick/items/qquickgraphicsdevice_p.h4
-rw-r--r--src/quick/items/qquickgraphicsinfo.cpp62
-rw-r--r--src/quick/items/qquickgraphicsinfo_p.h1
-rw-r--r--src/quick/items/qquickgridview.cpp175
-rw-r--r--src/quick/items/qquickgridview_p.h4
-rw-r--r--src/quick/items/qquickimage.cpp153
-rw-r--r--src/quick/items/qquickimage_p.h5
-rw-r--r--src/quick/items/qquickimage_p_p.h4
-rw-r--r--src/quick/items/qquickimagebase.cpp164
-rw-r--r--src/quick/items/qquickimagebase_p.h8
-rw-r--r--src/quick/items/qquickimagebase_p_p.h37
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h2
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p_p.h2
-rw-r--r--src/quick/items/qquickitem.cpp710
-rw-r--r--src/quick/items/qquickitem.h26
-rw-r--r--src/quick/items/qquickitem_p.h127
-rw-r--r--src/quick/items/qquickitemanimation.cpp13
-rw-r--r--src/quick/items/qquickitemanimation_p.h12
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h6
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp16
-rw-r--r--src/quick/items/qquickitemsmodule.cpp160
-rw-r--r--src/quick/items/qquickitemview.cpp93
-rw-r--r--src/quick/items/qquickitemview_p.h22
-rw-r--r--src/quick/items/qquickitemview_p_p.h31
-rw-r--r--src/quick/items/qquickitemviewfxitem.cpp35
-rw-r--r--src/quick/items/qquickitemviewfxitem_p_p.h10
-rw-r--r--src/quick/items/qquickitemviewtransition_p.h20
-rw-r--r--src/quick/items/qquicklistview.cpp299
-rw-r--r--src/quick/items/qquicklistview_p.h11
-rw-r--r--src/quick/items/qquickloader.cpp50
-rw-r--r--src/quick/items/qquickloader_p.h5
-rw-r--r--src/quick/items/qquickloader_p_p.h1
-rw-r--r--src/quick/items/qquickmousearea.cpp30
-rw-r--r--src/quick/items/qquickmousearea_p.h4
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp57
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h28
-rw-r--r--src/quick/items/qquickpainteditem.cpp21
-rw-r--r--src/quick/items/qquickpainteditem_p.h3
-rw-r--r--src/quick/items/qquickpalette.cpp55
-rw-r--r--src/quick/items/qquickpalette_p.h17
-rw-r--r--src/quick/items/qquickpalettecolorprovider.cpp53
-rw-r--r--src/quick/items/qquickpalettecolorprovider_p.h3
-rw-r--r--src/quick/items/qquickpaletteproviderprivatebase_p.h32
-rw-r--r--src/quick/items/qquickpathview.cpp130
-rw-r--r--src/quick/items/qquickpathview_p.h10
-rw-r--r--src/quick/items/qquickpathview_p_p.h10
-rw-r--r--src/quick/items/qquickpincharea.cpp2
-rw-r--r--src/quick/items/qquickpincharea_p.h39
-rw-r--r--src/quick/items/qquickpositioners.cpp66
-rw-r--r--src/quick/items/qquickpositioners_p.h34
-rw-r--r--src/quick/items/qquickpositioners_p_p.h7
-rw-r--r--src/quick/items/qquickrectangle.cpp280
-rw-r--r--src/quick/items/qquickrectangle_p.h40
-rw-r--r--src/quick/items/qquickrectangle_p_p.h20
-rw-r--r--src/quick/items/qquickrendercontrol.cpp118
-rw-r--r--src/quick/items/qquickrendercontrol.h5
-rw-r--r--src/quick/items/qquickrendercontrol_p.h4
-rw-r--r--src/quick/items/qquickrendertarget.cpp1165
-rw-r--r--src/quick/items/qquickrendertarget.h31
-rw-r--r--src/quick/items/qquickrendertarget_p.h25
-rw-r--r--src/quick/items/qquickrepeater_p.h4
-rw-r--r--src/quick/items/qquickrhiitem.cpp1158
-rw-r--r--src/quick/items/qquickrhiitem.h124
-rw-r--r--src/quick/items/qquickrhiitem_p.h101
-rw-r--r--src/quick/items/qquickscalegrid_p_p.h14
-rw-r--r--src/quick/items/qquickscreen.cpp2
-rw-r--r--src/quick/items/qquickscreen_p.h42
-rw-r--r--src/quick/items/qquickselectable_p.h13
-rw-r--r--src/quick/items/qquickshadereffect.cpp48
-rw-r--r--src/quick/items/qquickshadereffect_p.h2
-rw-r--r--src/quick/items/qquickshadereffect_p_p.h1
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h8
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp32
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h2
-rw-r--r--src/quick/items/qquicksprite_p.h2
-rw-r--r--src/quick/items/qquickspriteengine_p.h14
-rw-r--r--src/quick/items/qquickspritesequence_p.h2
-rw-r--r--src/quick/items/qquickstateoperations.cpp54
-rw-r--r--src/quick/items/qquickstateoperations_p.h24
-rw-r--r--src/quick/items/qquicktableview.cpp1360
-rw-r--r--src/quick/items/qquicktableview_p.h45
-rw-r--r--src/quick/items/qquicktableview_p_p.h148
-rw-r--r--src/quick/items/qquicktext.cpp642
-rw-r--r--src/quick/items/qquicktext_p.h26
-rw-r--r--src/quick/items/qquicktext_p_p.h15
-rw-r--r--src/quick/items/qquicktextcontrol.cpp55
-rw-r--r--src/quick/items/qquicktextcontrol_p.h1
-rw-r--r--src/quick/items/qquicktextcontrol_p_p.h1
-rw-r--r--src/quick/items/qquicktextdocument.cpp660
-rw-r--r--src/quick/items/qquicktextdocument.h43
-rw-r--r--src/quick/items/qquicktextdocument_p.h94
-rw-r--r--src/quick/items/qquicktextedit.cpp837
-rw-r--r--src/quick/items/qquicktextedit_p.h30
-rw-r--r--src/quick/items/qquicktextedit_p_p.h130
-rw-r--r--src/quick/items/qquicktextinput.cpp329
-rw-r--r--src/quick/items/qquicktextinput_p.h10
-rw-r--r--src/quick/items/qquicktextinput_p_p.h6
-rw-r--r--src/quick/items/qquicktextinterface_p.h2
-rw-r--r--src/quick/items/qquicktextnode_p.h95
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp44
-rw-r--r--src/quick/items/qquicktextnodeengine_p.h7
-rw-r--r--src/quick/items/qquicktextutil_p.h2
-rw-r--r--src/quick/items/qquicktranslate_p.h10
-rw-r--r--src/quick/items/qquicktreeview.cpp115
-rw-r--r--src/quick/items/qquicktreeview_p.h16
-rw-r--r--src/quick/items/qquicktreeview_p_p.h2
-rw-r--r--src/quick/items/qquickview.cpp71
-rw-r--r--src/quick/items/qquickview.h2
-rw-r--r--src/quick/items/qquickview_p.h5
-rw-r--r--src/quick/items/qquickwindow.cpp762
-rw-r--r--src/quick/items/qquickwindow.h14
-rw-r--r--src/quick/items/qquickwindow_p.h63
-rw-r--r--src/quick/items/qquickwindowattached_p.h16
-rw-r--r--src/quick/items/qquickwindowcontainer.cpp590
-rw-r--r--src/quick/items/qquickwindowcontainer_p.h79
-rw-r--r--src/quick/items/qquickwindowmodule.cpp524
-rw-r--r--src/quick/items/qquickwindowmodule_p.h26
-rw-r--r--src/quick/items/qquickwindowmodule_p_p.h15
-rw-r--r--src/quick/items/qsginternaltextnode.cpp (renamed from src/quick/items/qquicktextnode.cpp)157
-rw-r--r--src/quick/items/qsginternaltextnode_p.h199
159 files changed, 11769 insertions, 3512 deletions
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 0eb4fe40d3..d78bd040c2 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -9,7 +9,7 @@
#include <private/qquickcontext2dtexture_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <QtGui/QGuiApplication>
#include <qsgtextureprovider.h>
@@ -252,7 +252,7 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
QPainter instead of the more expensive and likely less performing
JavaScript and Context2D approach.
- \sa Context2D, QQuickPaintedItem
+ \sa Context2D, QQuickPaintedItem, {Qt Quick Examples - Pointer Handlers}
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -444,9 +444,8 @@ void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
\qmlproperty enumeration QtQuick::Canvas::renderTarget
Holds the current canvas render target.
- \list
- \li Canvas.Image - render to an in memory image buffer.
- \endlist
+ \value Canvas.Image Render to an in-memory image buffer.
+ \value Canvas.FramebufferObject As of Qt 6.0, this value is ignored.
This hint is supplied along with renderStrategy to the graphics context to
determine the method of rendering. A renderStrategy, renderTarget or a
@@ -480,11 +479,9 @@ void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
\qmlproperty enumeration QtQuick::Canvas::renderStrategy
Holds the current canvas rendering strategy.
- \list
- \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
- \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
- \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
- \endlist
+ \value Canvas.Immediate context will perform graphics commands immediately in the main UI thread.
+ \value Canvas.Threaded context will defer graphics commands to a private rendering thread.
+ \value Canvas.Cooperative context will defer graphics commands to the applications global render thread.
This hint is supplied along with renderTarget to the graphics context to
determine the method of rendering. A renderStrategy, renderTarget or a
@@ -813,7 +810,7 @@ QSGTextureProvider *QQuickCanvasItem::textureProvider() const
*/
-void QQuickCanvasItem::getContext(QQmlV4Function *args)
+void QQuickCanvasItem::getContext(QQmlV4FunctionPtr args)
{
Q_D(QQuickCanvasItem);
@@ -857,7 +854,7 @@ void QQuickCanvasItem::getContext(QQmlV4Function *args)
scene.
*/
-void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args)
+void QQuickCanvasItem::requestAnimationFrame(QQmlV4FunctionPtr args)
{
QV4::Scope scope(args->v4engine());
QV4::ScopedFunctionObject f(scope, (*args)[0]);
@@ -886,7 +883,7 @@ void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args)
This function will cancel the animation callback referenced by \a handle.
*/
-void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV4Function *args)
+void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV4FunctionPtr args)
{
QV4::Scope scope(args->v4engine());
QV4::ScopedValue v(scope, (*args)[0]);
@@ -961,12 +958,12 @@ bool QQuickCanvasItem::save(const QString &filename, const QSizeF &imageSize) co
return toImage(QRectF(QPointF(0, 0), imageSize)).save(url.toLocalFile());
}
-QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url, QSizeF sourceSize)
{
Q_D(QQuickCanvasItem);
QUrl fullPathUrl = d->baseUrl.resolved(url);
if (!d->pixmaps.contains(fullPathUrl)) {
- loadImage(url);
+ loadImage(url, sourceSize);
}
return d->pixmaps.value(fullPathUrl);
}
@@ -980,7 +977,7 @@ QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& ur
*/
/*!
- \qmlmethod QtQuick::Canvas::loadImage(url image)
+ \qmlmethod QtQuick::Canvas::loadImage(url image, size sourceSize = undefined)
Loads the given \a image asynchronously.
@@ -989,10 +986,14 @@ QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& ur
\note Only loaded images can be painted on the Canvas item.
+ If \a sourceSize is specified, the image will be scaled to that size during loading. This is
+ useful for loading scalable (vector) images (eg. SVGs) at their intended display size. This
+ parameter was introduced in Qt 6.7.
+
\sa unloadImage(), imageLoaded(), isImageLoaded(),
Context2D::createImageData(), Context2D::drawImage()
*/
-void QQuickCanvasItem::loadImage(const QUrl& url)
+void QQuickCanvasItem::loadImage(const QUrl& url, QSizeF sourceSize)
{
Q_D(QQuickCanvasItem);
QUrl fullPathUrl = d->baseUrl.resolved(url);
@@ -1004,6 +1005,8 @@ void QQuickCanvasItem::loadImage(const QUrl& url)
pix->load(qmlEngine(this)
, fullPathUrl
+ , QRect()
+ , sourceSize.toSize()
, QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
if (pix->isLoading())
pix->connectFinished(this, SIGNAL(imageLoaded()));
@@ -1203,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/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index d7821a53ba..300039743d 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -31,9 +31,8 @@ class QQuickCanvasContext;
class QQuickCanvasItemPrivate;
class QQuickPixmap;
-class QQmlV4Function;
-class QQuickCanvasPixmap : public QQmlRefCount
+class QQuickCanvasPixmap final : public QQmlRefCounted<QQuickCanvasPixmap>
{
public:
QQuickCanvasPixmap(const QImage& image);
@@ -110,17 +109,17 @@ public:
QImage toImage(const QRectF& rect = QRectF()) const;
- Q_INVOKABLE void getContext(QQmlV4Function *args);
+ Q_INVOKABLE void getContext(QQmlV4FunctionPtr args);
- Q_INVOKABLE void requestAnimationFrame(QQmlV4Function *args);
- Q_INVOKABLE void cancelRequestAnimationFrame(QQmlV4Function *args);
+ Q_INVOKABLE void requestAnimationFrame(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void cancelRequestAnimationFrame(QQmlV4FunctionPtr args);
Q_INVOKABLE void requestPaint();
Q_INVOKABLE void markDirty(const QRectF& dirtyRect = QRectF());
Q_INVOKABLE bool save(const QString &filename, const QSizeF &imageSize = QSizeF()) const;
Q_INVOKABLE QString toDataURL(const QString& type = QLatin1String("image/png")) const;
- QQmlRefPointer<QQuickCanvasPixmap> loadedPixmap(const QUrl& url);
+ QQmlRefPointer<QQuickCanvasPixmap> loadedPixmap(const QUrl& url, QSizeF sourceSize = QSizeF());
bool isTextureProvider() const override;
QSGTextureProvider *textureProvider() const override;
@@ -139,7 +138,7 @@ Q_SIGNALS:
void imageLoaded();
public Q_SLOTS:
- void loadImage(const QUrl& url);
+ void loadImage(const QUrl& url, QSizeF sourceSize = QSizeF());
void unloadImage(const QUrl& url);
bool isImageLoaded(const QUrl& url) const;
bool isImageLoading(const QUrl& url) const;
@@ -186,6 +185,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCanvasItem)
-
#endif //QQUICKCANVASITEM_P_H
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b9354b0ac2..1a6391a270 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -44,6 +44,8 @@
#include <private/qsgdefaultrendercontext_p.h>
+#include <QtCore/qpointer.h>
+
#include <cmath>
#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
#include <ctype.h>
@@ -96,7 +98,7 @@ QT_BEGIN_NAMESPACE
THROW_GENERIC_ERROR("Not a Context2D object");
#define qClamp(val, min, max) qMin(qMax(val, min), max)
#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
-Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
+Q_QUICK_EXPORT QColor qt_color_from_string(const QV4::Value &name)
{
QByteArray str = name.toQString().toUtf8();
@@ -932,7 +934,7 @@ void QV4::Heap::QQuickJSContext2DImageData::init()
DEFINE_OBJECT_VTABLE(QQuickJSContext2DImageData);
-static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, const QImage& image)
+static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, QImage&& image)
{
QV4::Scope scope(v4);
QQuickContext2DEngineData *ed = engineData(scope.engine);
@@ -946,7 +948,7 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
} else {
// After qtbase 88e56d0932a3615231adf40d5ae033e742d72c33, the image size can be off by one.
Q_ASSERT(qAbs(image.width() - qRound(w * image.devicePixelRatio())) <= 1 && qAbs(image.height() - qRound(h * image.devicePixelRatio())) <= 1);
- *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
+ *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? std::move(image) : std::move(image).convertToFormat(QImage::Format_ARGB32);
}
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
@@ -1289,29 +1291,81 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_globalAlpha(const QV4::Function
/*!
\qmlproperty string QtQuick::Context2D::globalCompositeOperation
- Holds the current the current composition operation, from the list below:
- \list
- \li source-atop - A atop B. Display the source image wherever both images are opaque.
- Display the destination image wherever the destination image is opaque but the source image is transparent.
- Display transparency elsewhere.
- \li source-in - A in B. Display the source image wherever both the source image and destination image are opaque.
- Display transparency elsewhere.
- \li source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
- Display transparency elsewhere.
- \li source-over - (default) A over B. Display the source image wherever the source image is opaque.
- Display the destination image elsewhere.
- \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
- \li destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
- \li destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
- \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
- \li lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
- \li copy - A (B is ignored). Display the source image instead of the destination image.
- \li xor - A xor B. Exclusive OR of the source image and destination image.
- \endlist
-
- Additionally, this property also accepts the compositon modes listed in QPainter::CompositionMode. According to the W3C standard, these
- extension composition modes are provided as "vendorName-operationName" syntax, for example: QPainter::CompositionMode_Exclusion is provided as
- "qt-exclusion".
+ Holds the current the current composition operation. Allowed operations are:
+
+ \value "source-atop"
+ QPainter::CompositionMode_SourceAtop
+ A atop B. Display the source image wherever both images are opaque.
+ Display the destination image wherever the destination image is opaque
+ but the source image is transparent. Display transparency elsewhere.
+ \value "source-in"
+ QPainter::CompositionMode_SourceIn
+ A in B. Display the source image wherever both the source image and
+ destination image are opaque. Display transparency elsewhere.
+ \value "source-out"
+ QPainter::CompositionMode_SourceOut
+ A out B. Display the source image wherever the source image is opaque
+ and the destination image is transparent. Display transparency elsewhere.
+ \value "source-over"
+ QPainter::CompositionMode_SourceOver (default)
+ A over B. Display the source image wherever the source image is opaque.
+ Display the destination image elsewhere.
+ \value "destination-atop"
+ QPainter::CompositionMode_DestinationAtop
+ B atop A. Same as \c source-atop but using the destination image instead
+ of the source image and vice versa.
+ \value "destination-in"
+ QPainter::CompositionMode_DestinationIn
+ B in A. Same as \c source-in but using the destination image instead of
+ the source image and vice versa.
+ \value "destination-out"
+ QPainter::CompositionMode_DestinationOut
+ B out A. Same as \c source-out but using the destination image instead
+ of the source image and vice versa.
+ \value "destination-over"
+ QPainter::CompositionMode_DestinationOver
+ B over A. Same as \c source-over but using the destination image
+ instead of the source image and vice versa.
+ \value "lighter"
+ QPainter::CompositionMode_Plus
+ A plus B. Display the sum of the source image and destination image,
+ with color values approaching \c 255 (100%) as a limit.
+ \value "copy"
+ QPainter::CompositionMode_Source
+ A (B is ignored). Display the source image instead of the destination image.
+ \value "xor"
+ QPainter::CompositionMode_Xor
+ A xor B. Exclusive OR of the source image and destination image.
+ \value "qt-clear"
+ QPainter::CompositionMode_Clear
+ \value "qt-destination"
+ QPainter::CompositionMode_Destination
+ \value "qt-multiply"
+ QPainter::CompositionMode_Multiply
+ \value "qt-screen"
+ QPainter::CompositionMode_Screen
+ \value "qt-overlay"
+ QPainter::CompositionMode_Overlay
+ \value "qt-darken"
+ QPainter::CompositionMode_Darken
+ \value "qt-lighten"
+ QPainter::CompositionMode_Lighten
+ \value "qt-color-dodge"
+ QPainter::CompositionMode_ColorDodge
+ \value "qt-color-burn"
+ QPainter::CompositionMode_ColorBurn
+ \value "qt-hard-light"
+ QPainter::CompositionMode_HardLight
+ \value "qt-soft-light"
+ QPainter::CompositionMode_SoftLight
+ \value "qt-difference"
+ QPainter::CompositionMode_Difference
+ \value "qt-exclusion"
+ QPainter::CompositionMode_Exclusion
+
+ In compliance with the W3C standard, the extended composition modes beyond
+ the required modes are provided as "vendorName-operationName" syntax, for
+ example: QPainter::CompositionMode_Exclusion is provided as "qt-exclusion".
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
@@ -1422,18 +1476,19 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionOb
}
RETURN_UNDEFINED();
}
+
/*!
\qmlproperty enumeration QtQuick::Context2D::fillRule
- Holds the current fill rule used for filling shapes. The following fill rules are supported:
- \list
- \li Qt.OddEvenFill
- \li Qt.WindingFill
- \endlist
- Note: Unlike the QPainterPath, the Canvas API uses the winding fill as the default fill rule.
- The fillRule property is part of the context rendering state.
+ Holds the current fill rule used for filling shapes. The following fill rules are supported:
- \sa fillStyle
- */
+ \value Qt.OddEvenFill Qt::OddEvenFill
+ \value Qt.WindingFill (default) Qt::WindingFill
+
+ \note Unlike QPainterPath, the Canvas API uses the winding fill as the default fill rule.
+ The fillRule property is part of the context rendering state.
+
+ \sa fillStyle
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -1687,48 +1742,49 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
}
/*!
- \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
- This is an overloaded function.
- Returns a CanvasPattern object that uses the given \a color and \a patternMode.
- The valid pattern modes are:
- \list
- \li Qt.SolidPattern
- \li Qt.Dense1Pattern
- \li Qt.Dense2Pattern
- \li Qt.Dense3Pattern
- \li Qt.Dense4Pattern
- \li Qt.Dense5Pattern
- \li Qt.Dense6Pattern
- \li Qt.Dense7Pattern
- \li Qt.HorPattern
- \li Qt.VerPattern
- \li Qt.CrossPattern
- \li Qt.BDiagPattern
- \li Qt.FDiagPattern
- \li Qt.DiagCrossPattern
-\endlist
+ \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
+ This is an overloaded function.
+ Returns a CanvasPattern object that uses the given \a color and \a patternMode.
+ The valid pattern modes are:
+
+ \value Qt.SolidPattern Qt::SolidPattern
+ \value Qt.Dense1Pattern Qt::Dense1Pattern
+ \value Qt.Dense2Pattern Qt::Dense2Pattern
+ \value Qt.Dense3Pattern Qt::Dense3Pattern
+ \value Qt.Dense4Pattern Qt::Dense4Pattern
+ \value Qt.Dense5Pattern Qt::Dense5Pattern
+ \value Qt.Dense6Pattern Qt::Dense6Pattern
+ \value Qt.Dense7Pattern Qt::Dense7Pattern
+ \value Qt.HorPattern Qt::HorPattern
+ \value Qt.VerPattern Qt::VerPattern
+ \value Qt.CrossPattern Qt::CrossPattern
+ \value Qt.BDiagPattern Qt::BDiagPattern
+ \value Qt.FDiagPattern Qt::FDiagPattern
+ \value Qt.DiagCrossPattern Qt::DiagCrossPattern
+
\sa Qt::BrushStyle
- */
+*/
/*!
- \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
- Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
+ \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
+ Returns a CanvasPattern object that uses the given image and repeats in the
+ direction(s) given by the repetition argument.
- The \a image parameter must be a valid Image item, a valid CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
+ The \a image parameter must be a valid Image item, a valid CanvasImageData
+ object or loaded image url. If there is no image data, thus function throws an
+ INVALID_STATE_ERR exception.
- The allowed values for \a repetition are:
+ The allowed values for \a repetition are:
- \list
- \li "repeat" - both directions
- \li "repeat-x - horizontal only
- \li "repeat-y" - vertical only
- \li "no-repeat" - neither
- \endlist
+ \value "repeat" both directions
+ \value "repeat-x horizontal only
+ \value "repeat-y" vertical only
+ \value "no-repeat" neither
- If the repetition argument is empty or null, the value "repeat" is used.
+ If the repetition argument is empty or null, the value "repeat" is used.
- \sa strokeStyle
- \sa fillStyle
- */
+ \sa strokeStyle
+ \sa fillStyle
+*/
QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
{
QV4::Scope scope(b);
@@ -1792,13 +1848,20 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::F
// line styles
/*!
\qmlproperty string QtQuick::Context2D::lineCap
- Holds the current line cap style.
- The possible line cap styles are:
- \list
- \li butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
- \li round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
- \li square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
- \endlist
+ Holds the current line cap style.
+ The possible line cap styles are:
+
+ \value "butt"
+ (default) Qt::FlatCap the end of each line has a flat edge
+ perpendicular to the direction of the line.
+ \value "round"
+ Qt::RoundCap a semi-circle with the diameter equal to the width of the
+ line is added on to the end of the line.
+ \value "square"
+ Qt::SquareCap a rectangle with the length of the line width and the
+ width of half the line width, placed flat against the edge
+ perpendicular to the direction of the line.
+
Other values are ignored.
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
@@ -1848,16 +1911,18 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(const QV4::FunctionObje
/*!
\qmlproperty string QtQuick::Context2D::lineJoin
- Holds the current line join style. A join exists at any point in a subpath
- shared by two consecutive lines. When a subpath is closed, then a join also exists
- at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
+ Holds the current line join style. A join exists at any point in a subpath
+ shared by two consecutive lines. When a subpath is closed, then a join also
+ exists at its first point (equivalent to its last point) connecting the
+ first and last lines in the subpath.
The possible line join styles are:
- \list
- \li bevel - this is all that is rendered at joins.
- \li round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
- \li miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
- \endlist
+
+ \value "bevel" Qt::BevelJoin The triangular notch between the two lines is filled.
+ \value "round" Qt::RoundJoin A circular arc between the two lines is filled.
+ \value "miter" (default) Qt::MiterJoin The outer edges of the lines are extended to
+ meet at an angle, and this area is filled.
+
Other values are ignored.
*/
QV4::ReturnedValue QQuickJSContext2D::method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
@@ -2306,8 +2371,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::Func
\image qml-item-canvas-startAngle.png
- The \a anticlockwise parameter is \c true for each arc in the figure above
- because they are all drawn in the anticlockwise direction.
+ The \a anticlockwise parameter is \c false for each arc in the figure above
+ because they are all drawn in the clockwise direction.
\sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
Context Standard for arc()}
@@ -2790,19 +2855,20 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject
}
/*!
- \qmlproperty string QtQuick::Context2D::textAlign
-
- Holds the current text alignment settings.
- The possible values are:
- \list
- \li start
- \li end
- \li left
- \li right
- \li center
- \endlist
- Other values are ignored. The default value is "start".
- */
+ \qmlproperty string QtQuick::Context2D::textAlign
+
+ Holds the current text alignment settings. The possible values are:
+
+ \value "start" (default) Align to the start edge of the text (left side in
+ left-to-right text, right side in right-to-left text).
+ \value "end" Align to the end edge of the text (right side in left-to-right
+ text, left side in right-to-left text).
+ \value "left" Qt::AlignLeft
+ \value "right" Qt::AlignRight
+ \value "center" Qt::AlignHCenter
+
+ Other values are ignored.
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -2857,20 +2923,19 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionOb
}
/*!
- \qmlproperty string QtQuick::Context2D::textBaseline
-
- Holds the current baseline alignment settings.
- The possible values are:
- \list
- \li top
- \li hanging
- \li middle
- \li alphabetic
- \li ideographic
- \li bottom
- \endlist
- Other values are ignored. The default value is "alphabetic".
- */
+ \qmlproperty string QtQuick::Context2D::textBaseline
+
+ Holds the current baseline alignment settings. The possible values are:
+
+ \value "top" The top of the em square
+ \value "hanging" The hanging baseline
+ \value "middle" The middle of the em square
+ \value "alphabetic" (default) The alphabetic baseline
+ \value "ideographic" The ideographic-under baseline
+ \value "bottom" The bottom of the em square
+
+ Other values are ignored. The default value is "alphabetic".
+*/
QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@@ -3347,7 +3412,7 @@ bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id
/*!
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(CanvasImageData imageData)
- Creates a CanvasImageData object with the same dimensions as the argument.
+ Creates a CanvasImageData object with the same dimensions as the \a imageData argument.
*/
/*!
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(Url imageUrl)
@@ -3378,7 +3443,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4:
}
} else if (arg0->isString()) {
QImage image = r->d()->context()->createPixmap(QUrl(arg0->toQStringNoThrow()))->image();
- RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, image));
+ RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, std::move(image)));
}
} else if (argc == 2) {
qreal w = argv[0].toNumber();
@@ -3419,7 +3484,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::Fu
THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
QImage image = r->d()->context()->canvas()->toImage(QRectF(x, y, w, h));
- RETURN_RESULT(qt_create_image_data(w, h, scope.engine, image));
+ RETURN_RESULT(qt_create_image_data(w, h, scope.engine, std::move(image)));
}
RETURN_RESULT(QV4::Encode::null());
}
@@ -3534,7 +3599,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::Fu
\code
var gradient = ctx.createLinearGradient(0, 0, 100, 100);
gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
- gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
+ gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1)');
\endcode
*/
QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
@@ -4076,6 +4141,7 @@ static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetr
break;
case QQuickContext2D::Right:
offset = metrics.horizontalAdvance(text);
+ break;
case QQuickContext2D::Left:
default:
break;
@@ -4089,9 +4155,9 @@ void QQuickContext2D::setGrabbedImage(const QImage& grab)
m_grabbed = true;
}
-QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url, QSizeF sourceSize)
{
- return m_canvas->loadedPixmap(url);
+ return m_canvas->loadedPixmap(url, sourceSize);
}
QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 9ae769f504..50917d3ecf 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -212,7 +212,7 @@ public:
bool isPointInPath(qreal x, qreal y) const;
QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text);
- QQmlRefPointer<QQuickCanvasPixmap> createPixmap(const QUrl& url);
+ QQmlRefPointer<QQuickCanvasPixmap> createPixmap(const QUrl& url, QSizeF sourceSize = QSizeF());
QSurface *surface() const { return m_surface.data(); }
void setGrabbedImage(const QImage& grab);
@@ -239,8 +239,6 @@ public:
static QMutex mutex;
};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickContext2D)
#endif // QQUICKCONTEXT2D_P_H
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 865fb8bf11..4a5dfa4111 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 politeness \a politeness.
+ *
+ * \sa QAccessibleAnnouncementEvent
+ */
+void QQuickAccessibleAttached::announce(const QString &message, QAccessible::AnnouncementPoliteness politeness)
+{
+ QAccessibleAnnouncementEvent event(parent(), message);
+ event.setPoliteness(politeness);
+ 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 7b70bdb75c..aa15b437ce 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; \
@@ -45,12 +47,13 @@ QT_BEGIN_NAMESPACE
} \
Q_SIGNAL void P ## Changed(bool arg);
-class Q_QUICK_PRIVATE_EXPORT QQuickAccessibleAttached : public QObject
+class Q_QUICK_EXPORT QQuickAccessibleAttached : public QObject
{
Q_OBJECT
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::AnnouncementPoliteness politeness = QAccessible::AnnouncementPoliteness::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;
@@ -211,8 +245,6 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAccessibleAttached)
-
#endif // accessibility
#endif
diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp
index dfb8ce0a18..e6de648313 100644
--- a/src/quick/items/qquickanchors.cpp
+++ b/src/quick/items/qquickanchors.cpp
@@ -49,11 +49,7 @@ static inline qreal hcenter(const QQuickItem *item)
if (!QQuickAnchorsPrivate::get(anchors)->centerAligned)
return width / 2;
}
- int iw = width;
- if (iw % 2)
- return (width + 1) / 2;
- else
- return width / 2;
+ return qRound(width / 2);
}
static inline qreal vcenter(const QQuickItem *item)
@@ -63,11 +59,7 @@ static inline qreal vcenter(const QQuickItem *item)
if (!QQuickAnchorsPrivate::get(anchors)->centerAligned)
return height / 2;
}
- int ih = height;
- if (ih % 2)
- return (height + 1) / 2;
- else
- return height / 2;
+ return qRound(height / 2);
}
//local position
diff --git a/src/quick/items/qquickanchors_p.h b/src/quick/items/qquickanchors_p.h
index 1409cfee06..f33e21c5bd 100644
--- a/src/quick/items/qquickanchors_p.h
+++ b/src/quick/items/qquickanchors_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickAnchorsPrivate;
class QQuickAnchorLine;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchors : public QObject
+class Q_QUICK_EXPORT QQuickAnchors : public QObject
{
Q_OBJECT
@@ -180,6 +180,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAnchors::Anchors)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnchors)
-
#endif // QQUICKANCHORS_P_H
diff --git a/src/quick/items/qquickanchors_p_p.h b/src/quick/items/qquickanchors_p_p.h
index eebfc5c06b..12e6dc465a 100644
--- a/src/quick/items/qquickanchors_p_p.h
+++ b/src/quick/items/qquickanchors_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorLine
+class Q_QUICK_EXPORT QQuickAnchorLine
{
Q_GADGET
QML_ANONYMOUS
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index e4bc156a30..93f7dd8340 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -106,6 +106,26 @@ void QQuickAnimatedImagePrivate::clearCache()
with QQuickImageProvider.
*/
+/*!
+ \qmlproperty size QtQuick::AnimatedImage::sourceSize
+
+ This property holds the scaled width and height of the full-frame image.
+
+ Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
+ the painting of the image, this property sets the maximum number of pixels
+ stored for cached frames so that large animations do not use more
+ memory than necessary.
+
+ If the original size is larger than \c sourceSize, the image is scaled down.
+
+ The natural size of the image can be restored by setting this property to
+ \c undefined.
+
+ \note \e {Changing this property dynamically causes the image source to be reloaded,
+ potentially even from the network, if it is not in the disk cache.}
+
+ \sa Image::sourceSize
+*/
QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
: QQuickImage(*(new QQuickAnimatedImagePrivate), parent)
{
@@ -287,10 +307,7 @@ void QQuickAnimatedImage::load()
Q_D(QQuickAnimatedImage);
if (d->url.isEmpty()) {
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(0);
d->setImage(QImage());
if (sourceSize() != d->oldSourceSize) {
@@ -298,9 +315,7 @@ void QQuickAnimatedImage::load()
emit sourceSizeChanged();
}
- d->status = Null;
- emit statusChanged(d->status);
-
+ d->setStatus(Null);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
} else {
@@ -313,19 +328,18 @@ void QQuickAnimatedImage::load()
resolve2xLocalFile(resolvedUrl, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
QString lf = QQmlFile::urlToLocalFileOrQrc(loadUrl);
+ d->status = Null; // reset status, no emit
+
if (!lf.isEmpty()) {
d->setMovie(new QMovie(lf));
movieRequestFinished();
} else {
#if QT_CONFIG(qml_network)
- if (d->status != Loading) {
- d->status = Loading;
- emit statusChanged(d->status);
- }
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
+ if (d->reply)
+ return;
+
+ d->setStatus(Loading);
+ d->setProgress(0);
QNetworkRequest req(d->url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
@@ -337,27 +351,21 @@ void QQuickAnimatedImage::load()
}
}
-#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
-
void QQuickAnimatedImage::movieRequestFinished()
{
Q_D(QQuickAnimatedImage);
#if QT_CONFIG(qml_network)
if (d->reply) {
- d->redirectCount++;
- if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
- QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = d->reply->url().resolved(redirect.toUrl());
- d->reply->deleteLater();
- setSource(url);
- return;
- }
- }
+ auto movie = new QMovie(d->reply);
+ // From this point, we no longer need to handle the reply.
+ // I.e. it will be used only as a data source for QMovie,
+ // so it should live as long as the movie lives.
+ d->reply->disconnect(this);
+ d->reply->setParent(movie);
+ d->reply = nullptr;
- d->redirectCount=0;
- d->setMovie(new QMovie(d->reply));
+ d->setMovie(movie);
}
#endif
@@ -373,13 +381,8 @@ void QQuickAnimatedImage::movieRequestFinished()
emit sourceSizeChanged();
}
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
-
- d->status = Error;
- emit statusChanged(d->status);
+ d->setProgress(0);
+ d->setStatus(Error);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
@@ -392,10 +395,7 @@ void QQuickAnimatedImage::movieRequestFinished()
d->movie->setCacheMode(QMovie::CacheAll);
d->movie->setSpeed(qRound(d->speed * 100.0));
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(1);
bool pausedAtStart = d->paused;
if (d->movie && d->playing)
@@ -416,8 +416,7 @@ void QQuickAnimatedImage::movieRequestFinished()
}
}
- d->status = Ready;
- emit statusChanged(d->status);
+ d->setStatus(Ready);
if (isPlaying() != d->oldPlaying)
emit playingChanged();
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 38beb6d1c3..4d8be4068a 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QMovie;
class QQuickAnimatedImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatedImage : public QQuickImage
+class Q_QUICK_EXPORT QQuickAnimatedImage : public QQuickImage
{
Q_OBJECT
@@ -85,6 +85,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAnimatedImage)
-
#endif // QQUICKANIMATEDIMAGE_P_H
diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h
index 2b01b4365d..1bd5729b93 100644
--- a/src/quick/items/qquickanimatedimage_p_p.h
+++ b/src/quick/items/qquickanimatedimage_p_p.h
@@ -34,11 +34,7 @@ class QQuickAnimatedImagePrivate : public QQuickImagePrivate
public:
QQuickAnimatedImagePrivate()
- : playing(true), paused(false), oldPlaying(false), padding(0)
- , presetCurrentFrame(0), speed(1.0), movie(nullptr)
-#if QT_CONFIG(qml_network)
- , reply(nullptr), redirectCount(0)
-#endif
+ : playing(true), paused(false), oldPlaying(false)
{
}
@@ -46,18 +42,18 @@ public:
void setMovie(QMovie *movie);
void clearCache();
+ qreal speed = 1;
+ QMovie *movie = nullptr;
+ int presetCurrentFrame = 0;
+ QMap<int, QQuickPixmap *> frameMap;
+
+#if QT_CONFIG(qml_network)
+ QNetworkReply *reply = nullptr;
+#endif
+
bool playing : 1;
bool paused : 1;
bool oldPlaying : 1;
- unsigned padding: 29;
- int presetCurrentFrame;
- qreal speed;
- QMovie *movie;
-#if QT_CONFIG(qml_network)
- QNetworkReply *reply;
- int redirectCount;
-#endif
- QMap<int, QQuickPixmap *> frameMap;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index 26358cd7ef..8f4c6ff7fc 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -31,7 +31,7 @@ class QQuickSpriteEngine;
class QSGGeometryNode;
class QQuickAnimatedSpritePrivate;
class QSGSpriteNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnimatedSprite : public QQuickItem
+class Q_QUICK_EXPORT QQuickAnimatedSprite : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index c8ebc59356..004f0491bd 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -63,9 +63,8 @@ QT_BEGIN_NAMESPACE
\image qml-borderimage-normal-image.png
\endfloat
- An unscaled image is displayed using an Image. The \l border property is
- used to determine the parts of the image that will lie inside the unscaled corner
- areas and the parts that will be stretched horizontally and vertically.
+ For comparison, an unscaled image is displayed using a simple Image item.
+ Here we have overlaid lines to show how we'd like to break it up with BorderImage:
\snippet qml/borderimage/normal-image.qml normal image
@@ -74,12 +73,15 @@ QT_BEGIN_NAMESPACE
\image qml-borderimage-scaled.png
\endfloat
- A BorderImage is used to display the image, and it is given a size that is
+ But when a BorderImage is used to display the image, the \l border property is
+ used to determine the parts of the image that will lie inside the unscaled corner
+ areas, and the parts that will be stretched horizontally and vertically.
+ Then, you can give it a size that is
larger than the original image. Since the \l horizontalTileMode property is set to
\l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
- in regions 4 and 6 are stretched vertically.
+ in regions 4 and 6 are stretched vertically:
\snippet qml/borderimage/borderimage-scaled.qml scaled border image
@@ -92,17 +94,25 @@ QT_BEGIN_NAMESPACE
\l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
the parts of image in regions 2 and 8 are tiled so that they fill the space at the
top and bottom of the item. Similarly, the \l verticalTileMode property is set to
- \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
- 4 and 6 are tiled so that they fill the space at the left and right of the item.
+ \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, so the parts of image in regions
+ 4 and 6 are tiled to fill the space at the left and right of the item:
\snippet qml/borderimage/borderimage-tiled.qml tiled border image
\clearfloat
+ \beginfloatleft
+ \image qml-borderimage-rounded.png
+ \endfloat
+
In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
- may not be an exact multiple of the height of the corresponding regions. It can be useful
- to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
- \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
+ may not be an exact multiple of the height of the corresponding regions. If you use
+ \l{BorderImage::horizontalTileMode}{BorderImage.Round} mode, it will choose an integer
+ number of tiles and shrink them to fit:
+
+ \snippet qml/borderimage/borderimage-rounded.qml tiled border image
+
+ \clearfloat
The Border Image example in \l{Qt Quick Examples - Image Elements} shows how a BorderImage
can be used to simulate a shadow effect on a rectangular item.
@@ -149,12 +159,10 @@ QQuickBorderImage::~QQuickBorderImage()
This property describes the status of image loading. It can be one of:
- \list
- \li BorderImage.Null - no image has been set
- \li BorderImage.Ready - the image has been loaded
- \li BorderImage.Loading - the image is currently being loaded
- \li BorderImage.Error - an error occurred while loading the image
- \endlist
+ \value BorderImage.Null No image has been set
+ \value BorderImage.Ready The image has been loaded
+ \value BorderImage.Loading The image is currently being loaded
+ \value BorderImage.Error An error occurred while loading the image
\sa progress
*/
@@ -268,20 +276,19 @@ void QQuickBorderImage::load()
: d->url);
if (!lf.isEmpty()) {
QFile file(lf);
- file.open(QIODevice::ReadOnly);
- setGridScaledImage(QQuickGridScaledImage(&file));
+ if (!file.open(QIODevice::ReadOnly))
+ d->setStatus(Error);
+ else
+ setGridScaledImage(QQuickGridScaledImage(&file));
} else {
#if QT_CONFIG(qml_network)
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- d->status = Loading;
+ d->setProgress(0);
+ d->setStatus(Loading);
+
QNetworkRequest req(d->url);
d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
this, QQuickBorderImage, SLOT(sciRequestFinished()));
- emit statusChanged(d->status);
#endif
}
} else {
@@ -332,11 +339,9 @@ QQuickScaleGrid *QQuickBorderImage::border()
This property describes how to repeat or stretch the middle parts of the border image.
- \list
- \li BorderImage.Stretch - Scales the image to fit to the available area.
- \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
- \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
- \endlist
+ \value BorderImage.Stretch Scales the image to fit to the available area.
+ \value BorderImage.Repeat Tile the image until there is no more space. May crop the last image.
+ \value BorderImage.Round Like Repeat, but scales the images down to ensure that the last image is not cropped.
The default tile mode for each property is BorderImage.Stretch.
*/
@@ -376,8 +381,7 @@ void QQuickBorderImage::setGridScaledImage(const QQuickGridScaledImage& sci)
{
Q_D(QQuickBorderImage);
if (!sci.isValid()) {
- d->status = Error;
- emit statusChanged(d->status);
+ d->setStatus(Error);
} else {
QQuickScaleGrid *sg = border();
sg->setTop(sci.gridTop());
@@ -396,30 +400,29 @@ void QQuickBorderImage::requestFinished()
{
Q_D(QQuickBorderImage);
- QSize impsize = d->pix.implicitSize();
- if (d->pix.isError()) {
- d->status = Error;
- qmlWarning(this) << d->pix.error();
- if (d->progress != 0) {
- d->progress = 0;
- emit progressChanged(d->progress);
- }
- } else {
- d->status = Ready;
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ 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);
- emit statusChanged(d->status);
+
+ if (d->currentPix->isError()) {
+ qmlWarning(this) << d->currentPix->error();
+ d->setStatus(Error);
+ d->setProgress(0);
+ } else {
+ d->setStatus(Ready);
+ d->setProgress(1);
+ }
+
if (sourceSize() != d->oldSourceSize) {
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();
}
@@ -427,28 +430,14 @@ void QQuickBorderImage::requestFinished()
}
#if QT_CONFIG(qml_network)
-#define BORDERIMAGE_MAX_REDIRECT 16
-
void QQuickBorderImage::sciRequestFinished()
{
Q_D(QQuickBorderImage);
- d->redirectCount++;
- if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
- QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
- if (redirect.isValid()) {
- QUrl url = d->sciReply->url().resolved(redirect.toUrl());
- setSource(url);
- return;
- }
- }
- d->redirectCount=0;
-
if (d->sciReply->error() != QNetworkReply::NoError) {
- d->status = Error;
+ d->setStatus(Error);
d->sciReply->deleteLater();
d->sciReply = nullptr;
- emit statusChanged(d->status);
} else {
QQuickGridScaledImage sci(d->sciReply);
d->sciReply->deleteLater();
@@ -523,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;
@@ -548,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);
@@ -593,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/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h
index 92dd90b654..3c87955189 100644
--- a/src/quick/items/qquickborderimage_p.h
+++ b/src/quick/items/qquickborderimage_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickScaleGrid;
class QQuickGridScaledImage;
class QQuickBorderImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickBorderImage : public QQuickImageBase
+class Q_QUICK_EXPORT QQuickBorderImage : public QQuickImageBase
{
Q_OBJECT
@@ -77,6 +77,5 @@ private:
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBorderImage)
#endif // QQUICKBORDERIMAGE_P_H
diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h
index 82867d9385..02d5ac9067 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -31,11 +31,6 @@ class QQuickBorderImagePrivate : public QQuickImageBasePrivate
public:
QQuickBorderImagePrivate()
- : border(0), horizontalTileMode(QQuickBorderImage::Stretch),
- verticalTileMode(QQuickBorderImage::Stretch), pixmapChanged(false)
-#if QT_CONFIG(qml_network)
- , sciReply(0), redirectCount(0)
-#endif
{
}
@@ -43,7 +38,6 @@ public:
{
}
-
QQuickScaleGrid *getScaleGrid()
{
Q_Q(QQuickBorderImage);
@@ -66,15 +60,14 @@ public:
QRectF *innerSourceRect,
QRectF *subSourceRect);
- QQuickScaleGrid *border;
QUrl sciurl;
- QQuickBorderImage::TileMode horizontalTileMode;
- QQuickBorderImage::TileMode verticalTileMode;
- bool pixmapChanged : 1;
+ QQuickScaleGrid *border = nullptr;
+ QQuickBorderImage::TileMode horizontalTileMode = QQuickBorderImage::Stretch;
+ QQuickBorderImage::TileMode verticalTileMode = QQuickBorderImage::Stretch;
+ bool pixmapChanged = false;
#if QT_CONFIG(qml_network)
- QNetworkReply *sciReply;
- int redirectCount;
+ QNetworkReply *sciReply = nullptr;
#endif
};
diff --git a/src/quick/items/qquickclipnode_p.h b/src/quick/items/qquickclipnode_p.h
index 953d0da08b..37b1b1778f 100644
--- a/src/quick/items/qquickclipnode_p.h
+++ b/src/quick/items/qquickclipnode_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickDefaultClipNode : public QSGClipNode
+class Q_QUICK_EXPORT QQuickDefaultClipNode : public QSGClipNode
{
public:
QQuickDefaultClipNode(const QRectF &);
diff --git a/src/quick/items/qquickcolorgroup.cpp b/src/quick/items/qquickcolorgroup.cpp
index fd913aba7a..bc1b4dee5a 100644
--- a/src/quick/items/qquickcolorgroup.cpp
+++ b/src/quick/items/qquickcolorgroup.cpp
@@ -45,9 +45,21 @@ QT_BEGIN_NAMESPACE
base: "green"
}
\endcode
+
+ The \l Palette type exposes color groups for each QML item state.
*/
/*!
+ \qmlproperty color QtQuick::ColorGroup::accent
+ \since 6.6
+
+ A color that typically contrasts or compliments \l base, \l window, and \l
+ button colors. It usually represents the users' choice of desktop
+ personalisation. Styling of interactive components is a typical use case.
+ Unless explicitly set, it defaults to \l highlight.
+*/
+
+/*!
\qmlproperty color QtQuick::ColorGroup::alternateBase
Used as the alternate background color in item views with alternating row colors.
@@ -180,8 +192,6 @@ QT_BEGIN_NAMESPACE
Additional signal indicates that the current state of this color group
has been changed. Usually it means that one of the colors is changed.
-
- \sa Palette
*/
/*!
@@ -499,6 +509,21 @@ void QQuickColorGroup::resetPlaceholderText()
resetColor(QPalette::PlaceholderText, &QQuickColorGroup::placeholderTextChanged);
}
+QColor QQuickColorGroup::accent() const
+{
+ return color(QPalette::Accent);
+}
+
+void QQuickColorGroup::setAccent(const QColor &color)
+{
+ setColor(QPalette::Accent, color, &QQuickColorGroup::accentChanged);
+}
+
+void QQuickColorGroup::resetAccent()
+{
+ resetColor(QPalette::Accent, &QQuickColorGroup::accentChanged);
+}
+
QPalette::ColorGroup QQuickColorGroup::groupTag() const
{
return m_groupTag;
diff --git a/src/quick/items/qquickcolorgroup_p.h b/src/quick/items/qquickcolorgroup_p.h
index 54c883e1c7..202bba7ee1 100644
--- a/src/quick/items/qquickcolorgroup_p.h
+++ b/src/quick/items/qquickcolorgroup_p.h
@@ -22,12 +22,14 @@
#include <QtQml/qqml.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickPalette;
class QQuickPaletteColorProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickColorGroup : public QObject
+class Q_QUICK_EXPORT QQuickColorGroup : public QObject
{
Q_OBJECT
@@ -52,6 +54,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickColorGroup : public QObject
Q_PROPERTY(QColor windowText READ windowText WRITE setWindowText RESET resetWindowText NOTIFY windowTextChanged FINAL)
Q_PROPERTY(QColor placeholderText READ placeholderText WRITE setPlaceholderText
RESET resetPlaceholderText NOTIFY placeholderTextChanged REVISION(6, 2) FINAL)
+ Q_PROPERTY(QColor accent READ accent WRITE setAccent RESET resetAccent NOTIFY accentChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(ColorGroup)
QML_ADDED_IN_VERSION(6, 0)
@@ -144,6 +147,10 @@ public:
void setPlaceholderText(const QColor &color);
void resetPlaceholderText();
+ QColor accent() const;
+ void setAccent(const QColor &color);
+ void resetAccent();
+
QPalette::ColorGroup groupTag() const;
void setGroupTag(QPalette::ColorGroup tag);
@@ -173,6 +180,7 @@ Q_SIGNALS:
void windowChanged();
void windowTextChanged();
Q_REVISION(6, 2) void placeholderTextChanged();
+ Q_REVISION(6, 6) void accentChanged();
void changed();
@@ -197,6 +205,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColorGroup)
-
#endif // QQUICKCOLORGROUP_H
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index bdca024b1d..47064ad433 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -9,7 +9,7 @@
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qbuffer.h>
#include <QtCore/qmimedata.h>
@@ -55,7 +55,7 @@ using namespace Qt::StringLiterals;
\l {supportedActions}{drop action} chosen by the recipient of the event,
otherwise it will return Qt.IgnoreAction.
- \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
+ \sa {Qt Quick Examples - Drag and Drop}
*/
void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
@@ -354,7 +354,7 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
if (url.isEmpty()) {
d->pixmapLoader.clear();
} else {
- d->pixmapLoader.load(qmlEngine(parent()), url);
+ d->loadPixmap();
}
Q_EMIT imageSourceChanged();
@@ -362,6 +362,48 @@ void QQuickDragAttached::setImageSource(const QUrl &url)
}
/*!
+ \qmlattachedproperty size QtQuick::Drag::imageSourceSize
+ \since 6.8
+
+ This property holds the size of the image that will be used to represent
+ the data during the drag and drop operation. Changing this property after
+ the drag operation has started will have no effect.
+
+ This property sets the maximum number of pixels stored for the loaded
+ image so that large images do not use more memory than necessary.
+ See \l {QtQuick::Image::sourceSize}{Image.sourceSize} for more details.
+
+ The example below shows an SVG image rendered at one size, and re-renders
+ it at a different size for the drag image:
+
+ \snippet qml/externalDragScaledImage.qml 0
+
+ \sa imageSource, Item::grabToImage()
+*/
+
+QSize QQuickDragAttached::imageSourceSize() const
+{
+ Q_D(const QQuickDragAttached);
+ int width = d->imageSourceSize.width();
+ int height = d->imageSourceSize.height();
+ return QSize(width != -1 ? width : d->pixmapLoader.width(),
+ height != -1 ? height : d->pixmapLoader.height());
+}
+
+void QQuickDragAttached::setImageSourceSize(const QSize &size)
+{
+ Q_D(QQuickDragAttached);
+ if (d->imageSourceSize != size) {
+ d->imageSourceSize = size;
+
+ if (!d->imageSource.isEmpty())
+ d->loadPixmap();
+
+ Q_EMIT imageSourceSizeChanged();
+ }
+}
+
+/*!
\qmlattachedproperty stringlist QtQuick::Drag::keys
This property holds a list of keys that can be used by a DropArea to filter drag events.
@@ -477,11 +519,9 @@ void QQuickDragAttached::setProposedAction(Qt::DropAction action)
A drag can also be started manually using \l startDrag.
- \list
- \li Drag.None - do not start drags automatically
- \li Drag.Automatic - start drags automatically
- \li Drag.Internal (default) - start backwards compatible drags automatically
- \endlist
+ \value Drag.None do not start drags automatically
+ \value Drag.Automatic start drags automatically
+ \value Drag.Internal (default) start backwards compatible drags automatically
When using \c Drag.Automatic you should also define \l mimeData and bind the
\l active property to the active property of MouseArea : \l {MouseArea::drag.active}
@@ -540,7 +580,7 @@ void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions)
property for the started sequence.
*/
-void QQuickDragAttached::start(QQmlV4Function *args)
+void QQuickDragAttached::start(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);
if (d->inEvent) {
@@ -576,12 +616,10 @@ void QQuickDragAttached::start(QQmlV4Function *args)
The returned drop action may be one of:
- \list
- \li Qt.CopyAction Copy the data to the target
- \li Qt.MoveAction Move the data from the source to the target
- \li Qt.LinkAction Create a link from the source to the target.
- \li Qt.IgnoreAction Ignore the action (do nothing with the data).
- \endlist
+ \value Qt.CopyAction Copy the data to the target
+ \value Qt.MoveAction Move the data from the source to the target
+ \value Qt.LinkAction Create a link from the source to the target.
+ \value Qt.IgnoreAction Ignore the action (do nothing with the data).
*/
int QQuickDragAttached::drop()
@@ -705,11 +743,10 @@ QMimeData *QQuickDragAttachedPrivate::createMimeData() const
else
qmlWarning(q) << "Don't know how to encode text as " << mimeType;
} else {
- mimeData->setData(mimeType, text.toUtf8().constData());
+ mimeData->setData(mimeType, text.toUtf8());
}
} else {
- qmlWarning(q) << "Mime data contains a string, but mime type " << mimeType
- << " is not a supported text type";
+ mimeData->setData(mimeType, text.toUtf8());
}
break;
}
@@ -765,6 +802,17 @@ QMimeData *QQuickDragAttachedPrivate::createMimeData() const
return mimeData;
}
+void QQuickDragAttachedPrivate::loadPixmap()
+{
+ Q_Q(QQuickDragAttached);
+
+ QUrl loadUrl = imageSource;
+ const QQmlContext *context = qmlContext(q->parent());
+ if (context)
+ loadUrl = context->resolvedUrl(imageSource);
+ pixmapLoader.load(context ? context->engine() : nullptr, loadUrl, QRect(), q->imageSourceSize());
+}
+
Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedActions)
{
Q_Q(QQuickDragAttached);
@@ -808,7 +856,7 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
property for the started sequence.
*/
-void QQuickDragAttached::startDrag(QQmlV4Function *args)
+void QQuickDragAttached::startDrag(QQmlV4FunctionPtr args)
{
Q_D(QQuickDragAttached);
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index 806dbc7602..fa73e91485 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -119,24 +119,23 @@ private:
friend class QQuickDragAttachedPrivate;
};
-class QQmlV4Function;
class QQuickDragAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickDrag : public QObject
+class Q_QUICK_EXPORT QQuickDrag : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget)
- Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged)
- Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged)
- Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged)
- Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged)
- Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
- Q_PROPERTY(bool active READ active NOTIFY activeChanged)
- Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged)
- Q_PROPERTY(bool smoothed READ smoothed WRITE setSmoothed NOTIFY smoothedChanged)
+ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget FINAL)
+ Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged FINAL FINAL)
+ Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged FINAL)
+ Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged FINAL)
+ Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged FINAL)
+ Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged FINAL)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged FINAL)
+ Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged FINAL)
+ Q_PROPERTY(bool smoothed READ smoothed WRITE setSmoothed NOTIFY smoothedChanged FINAL)
// Note, threshold was added in QtQuick 2.2 but REVISION is not supported (or needed) for grouped
// properties See QTBUG-33179
- Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold)
+ Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold FINAL)
//### consider drag and drop
QML_NAMED_ELEMENT(Drag)
@@ -211,21 +210,23 @@ private:
};
class QQuickDragAttachedPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickDragAttached : public QObject
+class Q_QUICK_EXPORT QQuickDragAttached : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickDragAttached)
- Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
- Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource)
- Q_PROPERTY(QObject *target READ target NOTIFY targetChanged)
- Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged)
- Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged)
- Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
- Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged)
- Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged)
- Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged)
- Q_PROPERTY(QQuickDrag::DragType dragType READ dragType WRITE setDragType NOTIFY dragTypeChanged)
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource FINAL)
+ Q_PROPERTY(QObject *target READ target NOTIFY targetChanged FINAL)
+ Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged FINAL)
+ Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged FINAL)
+ // imageSourceSize is new in Qt 6.8; revision omitted because of QTBUG-33179
+ Q_PROPERTY(QSize imageSourceSize READ imageSourceSize WRITE setImageSourceSize NOTIFY imageSourceSizeChanged FINAL)
+ Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged FINAL)
+ Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged FINAL)
+ Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged FINAL)
+ Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged FINAL)
+ Q_PROPERTY(QQuickDrag::DragType dragType READ dragType WRITE setDragType NOTIFY dragTypeChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -249,6 +250,9 @@ public:
QUrl imageSource() const;
void setImageSource(const QUrl &url);
+ QSize imageSourceSize() const;
+ void setImageSourceSize(const QSize &size);
+
QStringList keys() const;
void setKeys(const QStringList &keys);
@@ -269,8 +273,8 @@ public:
bool event(QEvent *event) override;
public Q_SLOTS:
- void start(QQmlV4Function *);
- void startDrag(QQmlV4Function *);
+ void start(QQmlV4FunctionPtr);
+ void startDrag(QQmlV4FunctionPtr);
void cancel();
Q_SIGNALS:
@@ -282,6 +286,7 @@ Q_SIGNALS:
void targetChanged();
void hotSpotChanged();
void imageSourceChanged();
+ void imageSourceSizeChanged(); // new in Qt 6.8
void keysChanged();
void mimeDataChanged();
void supportedActionsChanged();
@@ -291,6 +296,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDrag)
-
#endif
diff --git a/src/quick/items/qquickdrag_p_p.h b/src/quick/items/qquickdrag_p_p.h
index 3cb122dbcc..657dfefb1d 100644
--- a/src/quick/items/qquickdrag_p_p.h
+++ b/src/quick/items/qquickdrag_p_p.h
@@ -25,7 +25,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
@@ -67,6 +67,7 @@ public:
Qt::DropAction startDrag(Qt::DropActions supportedActions);
void setTarget(QQuickItem *item);
QMimeData *createMimeData() const;
+ void loadPixmap();
QQuickDragGrabber dragGrabber;
@@ -86,6 +87,7 @@ public:
bool overrideActions : 1;
QPointF hotSpot;
QUrl imageSource;
+ QSize imageSourceSize;
QQuickPixmap pixmapLoader;
QStringList keys;
QVariantMap externalMimeData;
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index 1c865a6327..2538e0e238 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -7,6 +7,7 @@
#include <private/qv4arraybuffer_p.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qregularexpression.h>
QT_BEGIN_NAMESPACE
@@ -72,7 +73,7 @@ QQuickDropAreaPrivate::~QQuickDropAreaPrivate()
The \l drag.source property is communicated to the source of a drag event as
the recipient of a drop on the drag target.
- \sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
+ \sa {Qt Quick Examples - Drag and Drop}
*/
QQuickDropArea::QQuickDropArea(QQuickItem *parent)
@@ -353,12 +354,10 @@ void QQuickDropArea::dropEvent(QDropEvent *event)
The drop action may be one of:
- \list
- \li Qt.CopyAction Copy the data to the target.
- \li Qt.MoveAction Move the data from the source to the target.
- \li Qt.LinkAction Create a link from the source to the target.
- \li Qt.IgnoreAction Ignore the action (do nothing with the data).
- \endlist
+ \value Qt.CopyAction Copy the data to the target.
+ \value Qt.MoveAction Move the data from the source to the target.
+ \value Qt.LinkAction Create a link from the source to the target.
+ \value Qt.IgnoreAction Ignore the action (do nothing with the data).
*/
/*!
diff --git a/src/quick/items/qquickdroparea_p.h b/src/quick/items/qquickdroparea_p.h
index 35d295f5aa..e9fff0afa0 100644
--- a/src/quick/items/qquickdroparea_p.h
+++ b/src/quick/items/qquickdroparea_p.h
@@ -29,23 +29,23 @@ class QQuickDropAreaPrivate;
class QQuickDragEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(QObject *source READ source)
- Q_PROPERTY(QStringList keys READ keys)
- Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions)
- Q_PROPERTY(Qt::DropActions proposedAction READ proposedAction)
- Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction)
- Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
- Q_PROPERTY(bool hasColor READ hasColor)
- Q_PROPERTY(bool hasHtml READ hasHtml)
- Q_PROPERTY(bool hasText READ hasText)
- Q_PROPERTY(bool hasUrls READ hasUrls)
- Q_PROPERTY(QVariant colorData READ colorData)
- Q_PROPERTY(QString html READ html)
- Q_PROPERTY(QString text READ text)
- Q_PROPERTY(QList<QUrl> urls READ urls)
- Q_PROPERTY(QStringList formats READ formats)
+ Q_PROPERTY(qreal x READ x FINAL)
+ Q_PROPERTY(qreal y READ y FINAL)
+ Q_PROPERTY(QObject *source READ source FINAL)
+ Q_PROPERTY(QStringList keys READ keys FINAL)
+ Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions FINAL)
+ Q_PROPERTY(Qt::DropActions proposedAction READ proposedAction FINAL)
+ Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction FINAL)
+ Q_PROPERTY(bool accepted READ accepted WRITE setAccepted FINAL)
+ Q_PROPERTY(bool hasColor READ hasColor FINAL)
+ Q_PROPERTY(bool hasHtml READ hasHtml FINAL)
+ Q_PROPERTY(bool hasText READ hasText FINAL)
+ Q_PROPERTY(bool hasUrls READ hasUrls FINAL)
+ Q_PROPERTY(QVariant colorData READ colorData FINAL)
+ Q_PROPERTY(QString html READ html FINAL)
+ Q_PROPERTY(QString text READ text FINAL)
+ Q_PROPERTY(QList<QUrl> urls READ urls FINAL)
+ Q_PROPERTY(QStringList formats READ formats FINAL)
QML_NAMED_ELEMENT(DragEvent)
QML_UNCREATABLE("DragEvent is only meant to be created by DropArea")
QML_ADDED_IN_VERSION(2, 0)
@@ -92,9 +92,9 @@ private:
class QQuickDropAreaDrag : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x NOTIFY positionChanged)
- Q_PROPERTY(qreal y READ y NOTIFY positionChanged)
- Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged)
+ Q_PROPERTY(qreal x READ x NOTIFY positionChanged FINAL)
+ Q_PROPERTY(qreal y READ y NOTIFY positionChanged FINAL)
+ Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -117,7 +117,7 @@ private:
};
class QQuickDropAreaPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickDropArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickDropArea : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged)
@@ -161,6 +161,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDropArea)
-
#endif // QQUICKDROPAREA_P_H
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 5c722df4fe..d6d012a021 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -97,16 +97,15 @@ Item {
This property holds the keyboard modifier flags that existed immediately
before the event occurred.
- It contains a bitwise combination of:
- \list
- \li \l {Qt::NoModifier} {Qt.NoModifier} - No modifier key is pressed.
- \li \l {Qt::ShiftModifier} {Qt.ShiftModifier} - A Shift key on the keyboard is pressed.
- \li \l {Qt::ControlModifier} {Qt.ControlModifier} - A Ctrl key on the keyboard is pressed.
- \li \l {Qt::AltModifier} {Qt.AltModifier} - An Alt key on the keyboard is pressed.
- \li \l {Qt::MetaModifier} {Qt.MetaModifier} - A Meta key on the keyboard is pressed.
- \li \l {Qt::KeypadModifier} {Qt.KeypadModifier} - A keypad button is pressed.
- \li \l {Qt::GroupSwitchModifier} {Qt.GroupSwitchModifier} - X11 only. A Mode_switch key on the keyboard is pressed.
- \endlist
+ It contains a bitwise combination of numeric values (the same as in Qt::KeyboardModifier):
+
+ \value Qt.NoModifier No modifier key is pressed.
+ \value Qt.ShiftModifier} A Shift key on the keyboard is pressed.
+ \value Qt.ControlModifier A Ctrl key on the keyboard is pressed.
+ \value Qt.AltModifier An Alt key on the keyboard is pressed.
+ \value Qt.MetaModifier A Meta key on the keyboard is pressed.
+ \value Qt.KeypadModifier A keypad button is pressed.
+ \value Qt.GroupSwitchModifier X11 only. A Mode_switch key on the keyboard is pressed.
For example, to react to a Shift key + Enter key combination:
\qml
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 24f3cef6c9..f82905333b 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -36,17 +36,18 @@ class QPointerEvent;
class QMouseEvent;
class QQuickPointerHandler;
-class Q_QUICK_PRIVATE_EXPORT QQuickKeyEvent : public QObject
+class Q_QUICK_EXPORT QQuickKeyEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(int key READ key CONSTANT)
- Q_PROPERTY(QString text READ text CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT)
- Q_PROPERTY(int count READ count CONSTANT)
- Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(int key READ key CONSTANT FINAL)
+ Q_PROPERTY(QString text READ text CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+ Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT FINAL)
+ Q_PROPERTY(int count READ count CONSTANT FINAL)
+ Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(KeyEvent)
+ QML_UNCREATABLE("Should only be used by signal handlers in the Keys attached property")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -102,21 +103,23 @@ private:
bool m_autoRepeat = false;
};
-// used in Qt Location
-class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
+class Q_QUICK_EXPORT QQuickMouseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x CONSTANT)
- Q_PROPERTY(qreal y READ y CONSTANT)
- Q_PROPERTY(int button READ button CONSTANT)
- Q_PROPERTY(int buttons READ buttons CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(int source READ source CONSTANT REVISION(2, 7))
- Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT)
- Q_PROPERTY(bool isClick READ isClick CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- Q_PROPERTY(int flags READ flags CONSTANT REVISION(2, 11))
- QML_ANONYMOUS
+ Q_PROPERTY(qreal x READ x CONSTANT FINAL)
+ Q_PROPERTY(qreal y READ y CONSTANT FINAL)
+ Q_PROPERTY(int button READ button CONSTANT FINAL)
+ Q_PROPERTY(int buttons READ buttons CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+#if QT_DEPRECATED_SINCE(6, 6)
+ Q_PROPERTY(int source READ source CONSTANT REVISION(2, 7) FINAL)
+#endif
+ Q_PROPERTY(bool isClick READ isClick CONSTANT FINAL)
+ Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ Q_PROPERTY(int flags READ flags CONSTANT REVISION(2, 11) FINAL)
+ QML_NAMED_ELEMENT(MouseEvent)
+ QML_UNCREATABLE("Should only be used by mouse event signal handlers, for example in MouseArea")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -145,7 +148,9 @@ public:
int button() const { return _button; }
int buttons() const { return _buttons; }
int modifiers() const { return _modifiers; }
+#if QT_DEPRECATED_SINCE(6, 6)
int source() const { return _source; }
+#endif
bool wasHeld() const { return _wasHeld; }
bool isClick() const { return _isClick; }
@@ -153,7 +158,9 @@ public:
void setX(qreal x) { _x = x; }
void setY(qreal y) { _y = y; }
void setPosition(const QPointF &point) { _x = point.x(); _y = point.y(); }
+#if QT_DEPRECATED_SINCE(6, 6)
void setSource(Qt::MouseEventSource s) { _source = s; }
+#endif
bool isAccepted() { return _accepted; }
void setAccepted(bool accepted) { _accepted = accepted; }
@@ -172,20 +179,21 @@ private:
};
#if QT_CONFIG(wheelevent)
-class QQuickWheelEvent : public QObject
+class Q_QUICK_EXPORT QQuickWheelEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(const QPointingDevice *device READ pointingDevice CONSTANT)
- Q_PROPERTY(qreal x READ x CONSTANT)
- Q_PROPERTY(qreal y READ y CONSTANT)
- Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT)
- Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT)
- Q_PROPERTY(Qt::ScrollPhase phase READ phase CONSTANT)
- Q_PROPERTY(int buttons READ buttons CONSTANT)
- Q_PROPERTY(int modifiers READ modifiers CONSTANT)
- Q_PROPERTY(bool inverted READ inverted CONSTANT)
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(const QPointingDevice *device READ pointingDevice CONSTANT FINAL)
+ Q_PROPERTY(qreal x READ x CONSTANT FINAL)
+ Q_PROPERTY(qreal y READ y CONSTANT FINAL)
+ Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT FINAL)
+ Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT FINAL)
+ Q_PROPERTY(Qt::ScrollPhase phase READ phase CONSTANT FINAL)
+ Q_PROPERTY(int buttons READ buttons CONSTANT FINAL)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT FINAL)
+ Q_PROPERTY(bool inverted READ inverted CONSTANT FINAL)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(WheelEvent)
+ QML_UNCREATABLE("Should only be used by wheel event signal handlers, for example in MouseArea")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -231,11 +239,12 @@ private:
};
#endif
-class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
+class Q_QUICK_EXPORT QQuickCloseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- QML_ANONYMOUS
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted FINAL)
+ QML_NAMED_ELEMENT(CloseEvent)
+ QML_UNCREATABLE("Should only be used by Window's closing signal")
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -250,14 +259,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickKeyEvent)
-QML_DECLARE_TYPE(QQuickMouseEvent)
-#if QT_CONFIG(wheelevent)
-QML_DECLARE_TYPE(QQuickWheelEvent)
-#endif
-QML_DECLARE_TYPE(QQuickCloseEvent)
-QML_DECLARE_TYPE(QPointingDevice)
-QML_DECLARE_TYPE(QPointingDeviceUniqueId)
-QML_DECLARE_TYPE(QPointerEvent)
-
#endif // QQUICKEVENTS_P_P_H
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index c2f71a9438..5a9a0ff846 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -7,7 +7,7 @@
#include "qquickwindow.h"
#include "qquickwindow_p.h"
#include "qquickmousearea_p.h"
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
#include "qquickdrag_p.h"
#endif
@@ -22,39 +22,23 @@
#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qmath.h>
-#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformtheme.h>
#include <math.h>
#include <cmath>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
-Q_LOGGING_CATEGORY(lcFlickable, "qt.quick.flickable")
-Q_LOGGING_CATEGORY(lcFilter, "qt.quick.flickable.filter")
-Q_LOGGING_CATEGORY(lcReplay, "qt.quick.flickable.replay")
-Q_LOGGING_CATEGORY(lcWheel, "qt.quick.flickable.wheel")
-Q_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
+Q_STATIC_LOGGING_CATEGORY(lcFlickable, "qt.quick.flickable")
+Q_STATIC_LOGGING_CATEGORY(lcFilter, "qt.quick.flickable.filter")
+Q_STATIC_LOGGING_CATEGORY(lcReplay, "qt.quick.flickable.replay")
+Q_STATIC_LOGGING_CATEGORY(lcWheel, "qt.quick.flickable.wheel")
+Q_STATIC_LOGGING_CATEGORY(lcVel, "qt.quick.flickable.velocity")
// RetainGrabVelocity is the maxmimum instantaneous velocity that
// will ensure the Flickable retains the grab on consecutive flicks.
static const int RetainGrabVelocity = 100;
-// Currently std::round can't be used on Android when using ndk g++, so
-// use C version instead. We could just define two versions of Round, one
-// for float and one for double, but then only one of them would be used
-// and compiler would trigger a warning about unused function.
-//
-// See https://code.google.com/p/android/issues/detail?id=54418
-template<typename T>
-static T Round(T t) {
- return round(t);
-}
-template<>
-Q_DECL_UNUSED float Round<float>(float f) {
- return roundf(f);
-}
-
static qreal EaseOvershoot(qreal t) {
return qAtan(t);
}
@@ -101,7 +85,7 @@ void QQuickFlickableVisibleArea::updateVisible()
qreal pagePos = 0;
qreal pageSize = 0;
if (!qFuzzyIsNull(maxYBounds)) {
- qreal y = p->pixelAligned ? Round(p->vData.move.value()) : p->vData.move.value();
+ qreal y = p->pixelAligned ? std::round(p->vData.move.value()) : p->vData.move.value();
pagePos = (-y + flickable->minYExtent()) / maxYBounds;
pageSize = viewheight / maxYBounds;
}
@@ -120,7 +104,7 @@ void QQuickFlickableVisibleArea::updateVisible()
const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
const qreal maxXBounds = maxxextent + viewwidth;
if (!qFuzzyIsNull(maxXBounds)) {
- qreal x = p->pixelAligned ? Round(p->hData.move.value()) : p->hData.move.value();
+ qreal x = p->pixelAligned ? std::round(p->hData.move.value()) : p->hData.move.value();
pagePos = (-x + flickable->minXExtent()) / maxXBounds;
pageSize = viewwidth / maxXBounds;
} else {
@@ -249,8 +233,9 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, syncDrag(false)
, lastPosTime(-1)
, lastPressTime(0)
- , deceleration(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickDeceleration).toReal())
- , maxVelocity(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity).toReal())
+ , deceleration(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickDeceleration).toReal())
+ , wheelDeceleration(15000)
+ , maxVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
, delayedPressEvent(nullptr), pressDelay(0), fixupDuration(400)
, flickBoost(1.0), initialWheelFlickDistance(qApp->styleHints()->wheelScrollLines() * 24)
, fixupMode(Normal), vTime(0), visibleArea(nullptr)
@@ -259,6 +244,9 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, boundsMovement(QQuickFlickable::FollowBoundsBehavior)
, rebound(nullptr)
{
+ const int wheelDecelerationEnv = qEnvironmentVariableIntValue("QT_QUICK_FLICKABLE_WHEEL_DECELERATION");
+ if (wheelDecelerationEnv > 0)
+ wheelDeceleration = wheelDecelerationEnv;
}
void QQuickFlickablePrivate::init()
@@ -276,6 +264,7 @@ void QQuickFlickablePrivate::init()
q->setFlag(QQuickItem::ItemIsViewport);
QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
/*!
@@ -339,20 +328,21 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr
}
}
-bool QQuickFlickablePrivate::flickX(qreal velocity)
+bool QQuickFlickablePrivate::flickX(QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
- return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
+ return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, eventType, velocity);
}
-bool QQuickFlickablePrivate::flickY(qreal velocity)
+bool QQuickFlickablePrivate::flickY(QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
- return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
+ return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, eventType, velocity);
}
bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback,
+ QEvent::Type eventType, qreal velocity)
{
Q_Q(QQuickFlickable);
qreal maxDistance = -1;
@@ -374,13 +364,14 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt
v = maxVelocity;
}
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
+ qCDebug(lcFlickable) << "choosing deceleration" << accel << "for" << eventType;
// adjust accel so that we hit a full pixel
- qreal accel = deceleration;
qreal v2 = v * v;
qreal dist = v2 / (accel * 2.0);
if (v > 0)
dist = -dist;
- qreal target = -Round(-(data.move.value() - dist));
+ qreal target = std::round(data.move.value() - dist);
dist = -target + data.move.value();
accel = v2 / (2.0f * qAbs(dist));
@@ -502,18 +493,18 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt
} else if (data.move.value() <= maxExtent) {
resetTimeline(data);
adjustContentPos(data, maxExtent);
- } else if (-Round(-data.move.value()) != data.move.value()) {
+ } else if (-std::round(-data.move.value()) != data.move.value()) {
// We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
resetTimeline(data);
qreal val = data.move.value();
- if (std::abs(-Round(-val) - val) < 0.25) // round small differences
- val = -Round(-val);
+ if (std::abs(std::round(val) - val) < 0.25) // round small differences
+ val = std::round(val);
else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
- val = -std::floor(-val);
+ val = std::ceil(val);
else if (data.smoothVelocity.value() < 0)
- val = -std::ceil(-val);
+ val = std::floor(val);
else // otherwise round
- val = -Round(-val);
+ val = std::round(val);
timeline.set(data.move, val);
}
data.inOvershoot = false;
@@ -549,7 +540,7 @@ void QQuickFlickablePrivate::updateBeginningEnd()
// Vertical
const qreal maxyextent = -q->maxYExtent();
const qreal minyextent = -q->minYExtent();
- const qreal ypos = -vData.move.value();
+ const qreal ypos = pixelAligned ? -std::round(vData.move.value()) : -vData.move.value();
bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
@@ -569,7 +560,7 @@ void QQuickFlickablePrivate::updateBeginningEnd()
// Horizontal
const qreal maxxextent = -q->maxXExtent();
const qreal minxextent = -q->minXExtent();
- const qreal xpos = -hData.move.value();
+ const qreal xpos = pixelAligned ? -std::round(hData.move.value()) : -hData.move.value();
atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
@@ -764,8 +755,6 @@ void QQuickFlickablePrivate::updateBeginningEnd()
\snippet qml/flickableScrollbar.qml 0
\dots 8
\snippet qml/flickableScrollbar.qml 1
-
- \sa {customitems/scrollbar}{UI Components: Scrollbar Example}
*/
QQuickFlickable::QQuickFlickable(QQuickItem *parent)
: QQuickItem(*(new QQuickFlickablePrivate), parent)
@@ -1111,14 +1100,17 @@ void QQuickFlickablePrivate::handlePressEvent(QPointerEvent *event)
}
q->setKeepMouseGrab(stealMouse);
- maybeBeginDrag(computeCurrentTime(event), event->points().first().position());
+ maybeBeginDrag(computeCurrentTime(event), event->points().first().position(),
+ event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->buttons()
+ : Qt::NoButton);
}
-void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn)
+void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn, Qt::MouseButtons buttons)
{
Q_Q(QQuickFlickable);
clearDelayedPress();
- pressed = true;
+ // consider dragging only when event is left mouse button or touch event which has no button
+ pressed = buttons.testFlag(Qt::LeftButton) || (buttons == Qt::NoButton);
if (hData.transitionToBounds)
hData.transitionToBounds->stopTransition();
@@ -1382,10 +1374,14 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
const QVector2D velocity = firstPointLocalVelocity(event);
bool overThreshold = false;
- if (q->yflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
- if (q->xflick())
- overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ if (event->pointCount() == 1) {
+ if (q->yflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
+ if (q->xflick())
+ overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.x(), Qt::XAxis, firstPoint);
+ } else {
+ qCDebug(lcFilter) << q->objectName() << "ignoring multi-touch" << event;
+ }
drag(currentTimestamp, event->type(), pos, deltas, overThreshold, false, false, velocity);
}
@@ -1447,24 +1443,32 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
}
flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
- const int flickThreshold = QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickStartDistance).toInt();
+ const int flickThreshold = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickStartDistance).toInt();
+
+ bool anyPointGrabbed = event->points().constEnd() !=
+ std::find_if(event->points().constBegin(),event->points().constEnd(),
+ [q, event](const QEventPoint &point) { return event->exclusiveGrabber(point) == q; });
bool flickedVertically = false;
vVelocity *= flickBoost;
- bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(pos.y() - pressPos.y()) > flickThreshold;
+ const bool isVerticalFlickAllowed = anyPointGrabbed &&
+ q->yflick() && qAbs(vVelocity) > _q_MinimumFlickVelocity &&
+ qAbs(pos.y() - pressPos.y()) > flickThreshold;
if (isVerticalFlickAllowed) {
velocityTimeline.reset(vData.smoothVelocity);
vData.smoothVelocity.setValue(-vVelocity);
- flickedVertically = flickY(vVelocity);
+ flickedVertically = flickY(event->type(), vVelocity);
}
bool flickedHorizontally = false;
hVelocity *= flickBoost;
- bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(pos.x() - pressPos.x()) > flickThreshold;
+ const bool isHorizontalFlickAllowed = anyPointGrabbed &&
+ q->xflick() && qAbs(hVelocity) > _q_MinimumFlickVelocity &&
+ qAbs(pos.x() - pressPos.x()) > flickThreshold;
if (isHorizontalFlickAllowed) {
velocityTimeline.reset(hData.smoothVelocity);
hData.smoothVelocity.setValue(-hVelocity);
- flickedHorizontally = flickX(hVelocity);
+ flickedHorizontally = flickX(event->type(), hVelocity);
}
if (!isVerticalFlickAllowed)
@@ -1474,8 +1478,15 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
fixupX();
flickingStarted(flickedHorizontally, flickedVertically);
- if (!isViewMoving())
+ if (!isViewMoving()) {
q->movementEnding();
+ } else {
+ if (flickedVertically)
+ vMoved = true;
+ if (flickedHorizontally)
+ hMoved = true;
+ q->movementStarting();
+ }
}
void QQuickFlickable::mousePressEvent(QMouseEvent *event)
@@ -1508,13 +1519,14 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
- // Now send the release
- if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(event->point(0)))) {
- // not copying or detaching anything, so make sure we return the original event unchanged
- const auto oldPosition = event->point(0).position();
- QMutableEventPoint::setPosition(event->point(0), grabber->mapFromScene(event->scenePosition()));
+ auto &firstPoint = event->point(0);
+ if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
+ // Since we sent the delayed press to the window, we need to resend the release to the window too.
+ // We're not copying or detaching, so restore the original event position afterwards.
+ const auto oldPosition = firstPoint.position();
+ QMutableEventPoint::setPosition(firstPoint, event->scenePosition());
QCoreApplication::sendEvent(window(), event);
- QMutableEventPoint::setPosition(event->point(0), oldPosition);
+ QMutableEventPoint::setPosition(firstPoint, oldPosition);
}
// And the event has been consumed
@@ -1533,6 +1545,15 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
void QQuickFlickable::touchEvent(QTouchEvent *event)
{
Q_D(QQuickFlickable);
+
+ if (event->type() == QEvent::TouchCancel) {
+ if (d->interactive && d->wantsPointerEvent(event))
+ d->cancelInteraction();
+ else
+ QQuickItem::touchEvent(event);
+ return;
+ }
+
bool unhandled = false;
const auto &firstPoint = event->points().first();
switch (firstPoint.state()) {
@@ -1558,11 +1579,11 @@ void QQuickFlickable::touchEvent(QTouchEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
- // Now send the release
- auto &firstPoint = event->point(0);
- if (auto grabber = qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(firstPoint))) {
- const auto localPos = grabber->mapFromScene(firstPoint.scenePosition());
- QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
+ const auto &firstPoint = event->point(0);
+ if (const auto *grabber = event->exclusiveGrabber(firstPoint); grabber && grabber->isQuickItemType()) {
+ // Since we sent the delayed press to the window, we need to resend the release to the window too.
+ QScopedPointer<QPointerEvent> localizedEvent(
+ QQuickDeliveryAgentPrivate::clonePointerEvent(event, firstPoint.scenePosition()));
QCoreApplication::sendEvent(window(), localizedEvent.data());
}
@@ -1644,48 +1665,117 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
// no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
int xDelta = event->angleDelta().x();
int yDelta = event->angleDelta().y();
- // For a single "clicky" wheel event (angleDelta +/- 120),
- // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
- // The decel algo from there is
- // qreal dist = v2 / (accel * 2.0);
- // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
- // now solve for dt:
- // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
- if (!isMoving())
- elapsed = 120 / qSqrt(d->deceleration * 2 * d->initialWheelFlickDistance);
- if (yflick() && yDelta != 0) {
- qreal instVelocity = yDelta / elapsed;
- // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
- if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
- d->vData.velocityBuffer.clear();
- d->vData.addVelocitySample(instVelocity, d->maxVelocity);
- d->vData.updateVelocity();
- if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
- const bool newFlick = d->flickY(d->vData.velocity);
- if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
- d->flickingStarted(false, true);
- d->vMoved = true;
+
+ if (d->wheelDeceleration > _q_MaximumWheelDeceleration) {
+ const qreal wheelScroll = -qApp->styleHints()->wheelScrollLines() * 24;
+ // If wheelDeceleration is very large, i.e. the user or the platform does not want to have any mouse wheel
+ // acceleration behavior, we want to move a distance proportional to QStyleHints::wheelScrollLines()
+ if (yflick() && yDelta != 0) {
+ d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
+ d->vMoved = true;
+ qreal scrollPixel = (-yDelta / 120.0 * wheelScroll);
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (d->vData.move.value() >= minYExtent())
+ d->vMoved = false;
+ } else { // Backward direction (towards user)
+ if (d->vData.move.value() <= maxYExtent())
+ d->vMoved = false;
+ }
+ if (d->vMoved) {
+ if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
+ const qreal estContentPos = scrollPixel + d->vData.move.value();
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (estContentPos > minYExtent())
+ scrollPixel = minYExtent() - d->vData.move.value();
+ } else { // Backward direction (towards user)
+ if (estContentPos < maxYExtent())
+ scrollPixel = maxYExtent() - d->vData.move.value();
+ }
+ }
+ d->resetTimeline(d->vData);
movementStarting();
+ d->timeline.moveBy(d->vData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
+ d->vData.fixingUp = true;
+ d->timeline.callback(QQuickTimeLineCallback(&d->vData.move, QQuickFlickablePrivate::fixupY_callback, d));
}
event->accept();
}
- }
- if (xflick() && xDelta != 0) {
- qreal instVelocity = xDelta / elapsed;
- // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
- if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
- d->hData.velocityBuffer.clear();
- d->hData.addVelocitySample(instVelocity, d->maxVelocity);
- d->hData.updateVelocity();
- if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
- const bool newFlick = d->flickX(d->hData.velocity);
- if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
- d->flickingStarted(true, false);
- d->hMoved = true;
+ if (xflick() && xDelta != 0) {
+ d->moveReason = QQuickFlickablePrivate::Mouse; // ItemViews will set fixupMode to Immediate in fixup() without this.
+ d->hMoved = true;
+ qreal scrollPixel = (-xDelta / 120.0 * wheelScroll);
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (d->hData.move.value() >= minXExtent())
+ d->hMoved = false;
+ } else { // Backward direction (towards user)
+ if (d->hData.move.value() <= maxXExtent())
+ d->hMoved = false;
+ }
+ if (d->hMoved) {
+ if (d->boundsBehavior == QQuickFlickable::StopAtBounds) {
+ const qreal estContentPos = scrollPixel + d->hData.move.value();
+ if (scrollPixel > 0) { // Forward direction (away from user)
+ if (estContentPos > minXExtent())
+ scrollPixel = minXExtent() - d->hData.move.value();
+ } else { // Backward direction (towards user)
+ if (estContentPos < maxXExtent())
+ scrollPixel = maxXExtent() - d->hData.move.value();
+ }
+ }
+ d->resetTimeline(d->hData);
movementStarting();
+ d->timeline.moveBy(d->hData.move, scrollPixel, QEasingCurve(QEasingCurve::OutExpo), 3*d->fixupDuration/4);
+ d->hData.fixingUp = true;
+ d->timeline.callback(QQuickTimeLineCallback(&d->hData.move, QQuickFlickablePrivate::fixupX_callback, d));
}
event->accept();
}
+ } else {
+ // wheelDeceleration is set to some reasonable value: the user or the platform wants to have
+ // the classic Qt Quick mouse wheel acceleration behavior.
+ // For a single "clicky" wheel event (angleDelta +/- 120),
+ // we want flick() to end up moving a distance proportional to QStyleHints::wheelScrollLines().
+ // The decel algo from there is
+ // qreal dist = v2 / (accel * 2.0);
+ // i.e. initialWheelFlickDistance = (120 / dt)^2 / (deceleration * 2)
+ // now solve for dt:
+ // dt = 120 / sqrt(deceleration * 2 * initialWheelFlickDistance)
+ if (!isMoving())
+ elapsed = 120 / qSqrt(d->wheelDeceleration * 2 * d->initialWheelFlickDistance);
+ if (yflick() && yDelta != 0) {
+ qreal instVelocity = yDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->vData.velocity > 0) || (instVelocity > 0 && d->vData.velocity < 0))
+ d->vData.velocityBuffer.clear();
+ d->vData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->vData.updateVelocity();
+ if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
+ const bool newFlick = d->flickY(event->type(), d->vData.velocity);
+ if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
+ d->flickingStarted(false, true);
+ d->vMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ }
+ if (xflick() && xDelta != 0) {
+ qreal instVelocity = xDelta / elapsed;
+ // if the direction has changed, start over with filtering, to allow instant movement in the opposite direction
+ if ((instVelocity < 0 && d->hData.velocity > 0) || (instVelocity > 0 && d->hData.velocity < 0))
+ d->hData.velocityBuffer.clear();
+ d->hData.addVelocitySample(instVelocity, d->maxVelocity);
+ d->hData.updateVelocity();
+ if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
+ const bool newFlick = d->flickX(event->type(), d->hData.velocity);
+ if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
+ d->flickingStarted(true, false);
+ d->hMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ }
}
} else {
// use pixelDelta (probably from a trackpad): this is where we want to be on most platforms eventually
@@ -1812,7 +1902,7 @@ void QQuickFlickablePrivate::replayDelayedPress()
void QQuickFlickablePrivate::setViewportX(qreal x)
{
Q_Q(QQuickFlickable);
- qreal effectiveX = pixelAligned ? -Round(-x) : x;
+ qreal effectiveX = pixelAligned ? -std::round(-x) : x;
const qreal maxX = q->maxXExtent();
const qreal minX = q->minXExtent();
@@ -1847,7 +1937,7 @@ void QQuickFlickablePrivate::setViewportX(qreal x)
void QQuickFlickablePrivate::setViewportY(qreal y)
{
Q_Q(QQuickFlickable);
- qreal effectiveY = pixelAligned ? -Round(-y) : y;
+ qreal effectiveY = pixelAligned ? -std::round(-y) : y;
const qreal maxY = q->maxYExtent();
const qreal minY = q->minYExtent();
@@ -2015,7 +2105,7 @@ void QQuickFlickable::geometryChange(const QRectF &newGeometry, const QRectF &ol
Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
Calling this method will update the corresponding moving and flicking properties and signals,
- just like a real flick.
+ just like a real touchscreen flick.
*/
void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
@@ -2027,8 +2117,8 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
d->vData.velocity = yVelocity;
d->hData.vTime = d->vData.vTime = d->timeline.time();
- const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(xVelocity);
- const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(yVelocity);
+ const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(QEvent::TouchUpdate, xVelocity);
+ const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(QEvent::TouchUpdate, yVelocity);
if (flickedX)
d->hMoved = true;
@@ -2580,12 +2670,31 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
QQuickDeliveryAgentPrivate::isTabletEvent(event)))
return false; // don't filter hover events or wheel events, for example
Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
- qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
Q_D(QQuickFlickable);
// If a touch event contains a new press point, don't steal right away: watch the movements for a while
if (isTouch && static_cast<QTouchEvent *>(event)->touchPointStates().testFlag(QEventPoint::State::Pressed))
d->stealMouse = false;
+ // If multiple touchpoints are within bounds, don't grab: it's probably meant for multi-touch interaction in some child
+ if (event->pointCount() > 1) {
+ qCDebug(lcFilter) << objectName() << "ignoring multi-touch" << event << "for" << receiver;
+ d->stealMouse = false;
+ } else {
+ qCDebug(lcFilter) << objectName() << "filtering" << event << "for" << receiver;
+ }
+
const auto &firstPoint = event->points().first();
+
+ if (event->pointCount() == 1 && event->exclusiveGrabber(firstPoint) == this) {
+ // We have an exclusive grab (since we're e.g dragging), but at the same time, we have
+ // a child with a passive grab (which is why this filter is being called). And because
+ // of that, we end up getting the same pointer events twice; First in our own event
+ // handlers (because of the grab), then once more in here, since we filter the child.
+ // To avoid processing the event twice (e.g avoid calling handleReleaseEvent once more
+ // from below), we mark the event as filtered, and simply return.
+ event->setAccepted(true);
+ return true;
+ }
+
QPointF localPos = mapFromScene(firstPoint.scenePosition());
bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
@@ -2595,7 +2704,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
// Special case for MouseArea, try to guess what it does with the event
if (auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
bool preventStealing = mouseArea->preventStealing();
-#if QT_CONFIG(draganddrop)
+#if QT_CONFIG(quick_draganddrop)
if (mouseArea->drag() && mouseArea->drag()->target())
preventStealing = true;
#endif
@@ -2720,21 +2829,10 @@ void QQuickFlickable::setMaximumFlickVelocity(qreal v)
\qmlproperty real QtQuick::Flickable::flickDeceleration
This property holds the rate at which a flick will decelerate:
the higher the number, the faster it slows down when the user stops
- flicking via touch, touchpad or mouse wheel. For example 0.0001 is nearly
+ flicking via touch. For example 0.0001 is nearly
"frictionless", and 10000 feels quite "sticky".
The default value is platform dependent. Values of zero or less are not allowed.
-
- \note For touchpad flicking, some platforms drive Flickable directly by
- sending QWheelEvents with QWheelEvent::phase() being \c Qt::ScrollMomentum,
- after the user has released all fingers from the touchpad. In that case,
- the operating system is controlling the deceleration, and this property has
- no effect.
-
- \note For mouse wheel scrolling, and for gesture scrolling on touchpads
- that do not have a momentum phase, extremely large values of
- flickDeceleration can make Flickable very resistant to scrolling,
- especially if \l maximumFlickVelocity is too small.
*/
qreal QQuickFlickable::flickDeceleration() const
{
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index a059924ed1..3f6a0e67fb 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickFlickablePrivate;
class QQuickFlickableVisibleArea;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
+class Q_QUICK_EXPORT QQuickFlickable : public QQuickItem
{
Q_OBJECT
@@ -287,6 +287,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlickable)
-
#endif // QQUICKFLICKABLE_P_H
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 04d27c0b8e..e9f452f3d7 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -30,14 +30,11 @@
QT_BEGIN_NAMESPACE
-// Really slow flicks can be annoying.
-const qreal MinimumFlickVelocity = 75.0;
-
class QQuickFlickableVisibleArea;
class QQuickTransition;
class QQuickFlickableReboundTransition;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickFlickable)
@@ -60,21 +57,17 @@ public:
QQuickFlickablePrivate *parent;
};
+ enum MovementReason { Other, SetIndex, Mouse };
+
struct AxisData {
AxisData(QQuickFlickablePrivate *fp, void (QQuickFlickablePrivate::*func)(qreal))
: move(fp, func)
- , transitionToBounds(nullptr)
- , viewSize(-1), lastPos(0), previousDragDelta(0), velocity(0), startMargin(0), endMargin(0)
- , origin(0), overshoot(0)
- , transitionTo(0)
- , continuousFlickVelocity(0), velocityTime(), vTime(0)
, smoothVelocity(fp), atEnd(false), atBeginning(true)
, transitionToSet(false)
, fixingUp(false), inOvershoot(false), inRebound(false), moving(false), flicking(false)
, flickingWhenDragBegan(false), dragging(false), extentsChanged(false)
- , explicitValue(false), minExtentDirty(true), maxExtentDirty(true)
- , contentPositionChangedExternallyDuringDrag(false)
- , unused(0)
+ , explicitValue(false), contentPositionChangedExternallyDuringDrag(false)
+ , minExtentDirty(true), maxExtentDirty(true)
{}
~AxisData();
@@ -102,26 +95,27 @@ public:
void updateVelocity();
QQuickTimeLineValueProxy<QQuickFlickablePrivate> move;
- QQuickFlickableReboundTransition *transitionToBounds;
- qreal viewSize;
- qreal pressPos;
- qreal lastPos;
- qreal dragStartOffset;
- qreal dragMinBound;
- qreal dragMaxBound;
- qreal previousDragDelta;
- qreal velocity;
- qreal flickTarget;
- qreal startMargin;
- qreal endMargin;
- qreal origin;
- qreal overshoot;
- qreal transitionTo;
- qreal continuousFlickVelocity;
+ QQuickFlickableReboundTransition *transitionToBounds = nullptr;
+ qreal viewSize = -1;
+ qreal pressPos = 0;
+ qreal lastPos = 0;
+ qreal dragStartOffset = 0;
+ qreal dragMinBound = 0;
+ qreal dragMaxBound = 0;
+ qreal previousDragDelta = 0;
+ qreal velocity = 0;
+ qreal flickTarget = 0;
+ qreal startMargin = 0;
+ qreal endMargin = 0;
+ qreal origin = 0;
+ qreal overshoot = 0;
+ qreal transitionTo = 0;
+ qreal continuousFlickVelocity = 0;
QElapsedTimer velocityTime;
- int vTime;
+ int vTime = 0;
QQuickFlickablePrivate::Velocity smoothVelocity;
QPODVector<qreal,10> velocityBuffer;
+ // bitfield
uint atEnd : 1;
uint atBeginning : 1;
uint transitionToSet : 1;
@@ -134,16 +128,18 @@ public:
uint dragging : 1;
uint extentsChanged : 1;
uint explicitValue : 1;
+ uint contentPositionChangedExternallyDuringDrag : 1;
+ // mutable portion of bitfield
mutable uint minExtentDirty : 1;
mutable uint maxExtentDirty : 1;
- uint contentPositionChangedExternallyDuringDrag : 1;
- uint unused : 17;
+ // end bitfield
};
- bool flickX(qreal velocity);
- bool flickY(qreal velocity);
+ bool flickX(QEvent::Type eventType, qreal velocity);
+ bool flickY(QEvent::Type eventType, qreal velocity);
virtual bool flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
+ QQuickTimeLineCallback::Callback fixupCallback,
+ QEvent::Type eventType, qreal velocity);
void flickingStarted(bool flickingH, bool flickingV);
void fixupX();
@@ -184,6 +180,8 @@ public:
AxisData hData;
AxisData vData;
+ MovementReason moveReason = Other;
+
QQuickTimeLine timeline;
bool hMoved : 1;
bool vMoved : 1;
@@ -201,6 +199,7 @@ public:
QPointF pressPos;
QVector2D accumulatedWheelPixelDelta;
qreal deceleration;
+ qreal wheelDeceleration;
qreal maxVelocity;
QPointerEvent *delayedPressEvent;
QBasicTimer delayedPressTimer;
@@ -231,7 +230,8 @@ public:
void handleMoveEvent(QPointerEvent *);
void handleReleaseEvent(QPointerEvent *);
- void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn);
+ void maybeBeginDrag(qint64 currentTimestamp, const QPointF &pressPosn,
+ Qt::MouseButtons buttons = Qt::NoButton);
void drag(qint64 currentTimestamp, QEvent::Type eventType, const QPointF &localPos,
const QVector2D &deltas, bool overThreshold, bool momentum,
bool velocitySensitiveOverBounds, const QVector2D &velocity);
@@ -247,14 +247,14 @@ public:
static void data_clear(QQmlListProperty<QObject> *);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickFlickableVisibleArea : public QObject
+class Q_QUICK_EXPORT QQuickFlickableVisibleArea : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged)
- Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged)
- Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged)
- Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged)
+ Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged FINAL)
+ Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged FINAL)
+ Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged FINAL)
+ Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -284,6 +284,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlickableVisibleArea)
-
#endif // QQUICKFLICKABLE_P_P_H
diff --git a/src/quick/items/qquickflickablebehavior_p.h b/src/quick/items/qquickflickablebehavior_p.h
index d99fc988d1..ec43aaca7d 100644
--- a/src/quick/items/qquickflickablebehavior_p.h
+++ b/src/quick/items/qquickflickablebehavior_p.h
@@ -62,4 +62,11 @@
#define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
#endif
+// Really slow flicks can be annoying.
+const qreal _q_MinimumFlickVelocity = 75.0;
+
+// If QQuickFlickablePrivate::wheelDeceleration (perhaps overridden via QT_QUICK_FLICKABLE_WHEEL_DECELERATION)
+// is greater than this, we switch to proportional wheel scrolling: no "acceleration" at all.
+const qreal _q_MaximumWheelDeceleration = 14999;
+
#endif //QQUICKFLICKABLEBEHAVIOR_H
diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp
index d599618834..2e48566295 100644
--- a/src/quick/items/qquickflipable.cpp
+++ b/src/quick/items/qquickflipable.cpp
@@ -3,10 +3,12 @@
#include "qquickflipable_p.h"
#include "qquickitem_p.h"
-
+#include "qquicktranslate_p.h"
#include <QtQml/qqmlinfo.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
// XXX todo - i think this needs work and a bit of a re-think
@@ -201,9 +203,20 @@ void QQuickFlipable::updatePolish()
d->updateSide();
}
-// determination on the currently visible side of the flipable
-// has to be done on the complete scene transform to give
-// correct results.
+/*! \internal
+ Flipable must use the complete scene transform to correctly determine the
+ currently visible side.
+
+ It must also be independent of camera distance, in case the contents are
+ too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
+ whereas QQuickRotation::applyTo(QMatrix4x4*) calls
+ QMatrix4x4::projectedRotate() which by default assumes the camera distance
+ is 1024 virtual pixels. So for example if contents inside Flipable are to
+ be flipped around the y axis, and are wider than 1024*2, some of the
+ rendering goes behind the "camera". That's expected for rendering (since we
+ didn't provide API to change camera distance), but not ok for deciding when
+ to flip.
+*/
void QQuickFlipablePrivate::updateSide()
{
Q_Q(QQuickFlipable);
@@ -213,35 +226,50 @@ void QQuickFlipablePrivate::updateSide()
sideDirty = false;
- QTransform sceneTransform;
- itemToParentTransform(sceneTransform);
-
- QPointF p1(0, 0);
- QPointF p2(1, 0);
- QPointF p3(1, 1);
-
- QPointF scenep1 = sceneTransform.map(p1);
- QPointF scenep2 = sceneTransform.map(p2);
- QPointF scenep3 = sceneTransform.map(p3);
-#if 0
- p1 = q->mapToParent(p1);
- p2 = q->mapToParent(p2);
- p3 = q->mapToParent(p3);
-#endif
-
- qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) -
- (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x());
-
- wantBackYFlipped = scenep1.x() >= scenep2.x();
- wantBackXFlipped = scenep2.y() >= scenep3.y();
-
- QQuickFlipable::Side newSide;
- if (cross > 0) {
- newSide = QQuickFlipable::Back;
- } else {
- newSide = QQuickFlipable::Front;
+ QMatrix4x4 sceneTransform;
+
+ const qreal tx = x.value();
+ const qreal ty = y.value();
+ if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
+ sceneTransform.translate(tx, ty);
+
+ for (const auto *transform : std::as_const(transforms)) {
+ if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
+ // rotation is a special case: we want to call rotate() instead of projectedRotate()
+ const auto angle = rot->angle();
+ const auto axis = rot->axis();
+ if (!(qFuzzyIsNull(angle) || axis.isNull())) {
+ sceneTransform.translate(rot->origin());
+ sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
+ sceneTransform.translate(-rot->origin());
+ }
+ } else {
+ transform->applyTo(&sceneTransform);
+ }
}
+ const bool hasRotation = !qFuzzyIsNull(rotation());
+ const bool hasScale = !qFuzzyCompare(scale(), 1);
+ if (hasScale || hasRotation) {
+ QPointF tp = computeTransformOrigin();
+ sceneTransform.translate(tp.x(), tp.y());
+ if (hasScale)
+ sceneTransform.scale(scale(), scale());
+ if (hasRotation)
+ sceneTransform.rotate(rotation(), 0, 0, 1);
+ sceneTransform.translate(-tp.x(), -tp.y());
+ }
+
+ const QVector3D origin(sceneTransform.map(QPointF(0, 0)));
+ const QVector3D right = QVector3D(sceneTransform.map(QPointF(1, 0))) - origin;
+ const QVector3D top = QVector3D(sceneTransform.map(QPointF(0, 1))) - origin;
+
+ wantBackYFlipped = right.x() < 0;
+ wantBackXFlipped = top.y() < 0;
+
+ const QQuickFlipable::Side newSide =
+ QVector3D::crossProduct(top, right).z() > 0 ? QQuickFlipable::Back : QQuickFlipable::Front;
+
if (newSide != current) {
current = newSide;
if (current == QQuickFlipable::Back && back)
diff --git a/src/quick/items/qquickflipable_p.h b/src/quick/items/qquickflipable_p.h
index 78abbf740f..93f012a844 100644
--- a/src/quick/items/qquickflipable_p.h
+++ b/src/quick/items/qquickflipable_p.h
@@ -28,7 +28,7 @@ QT_REQUIRE_CONFIG(quick_flipable);
QT_BEGIN_NAMESPACE
class QQuickFlipablePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlipable : public QQuickItem
+class Q_QUICK_EXPORT QQuickFlipable : public QQuickItem
{
Q_OBJECT
@@ -71,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFlipable)
-
#endif // QQUICKFLIPABLE_P_H
diff --git a/src/quick/items/qquickfocusscope_p.h b/src/quick/items/qquickfocusscope_p.h
index 530227f8ec..17b07d6a6e 100644
--- a/src/quick/items/qquickfocusscope_p.h
+++ b/src/quick/items/qquickfocusscope_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickFocusScope : public QQuickItem
+class Q_QUICK_EXPORT QQuickFocusScope : public QQuickItem
{
Q_OBJECT
QML_NAMED_ELEMENT(FocusScope)
@@ -32,6 +32,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFocusScope)
-
#endif // QQUICKFOCUSSCOPE_P_H
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0633d01ce4..cdae6fbec7 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -8,7 +8,7 @@
#include <private/qquickitem_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QSGSimpleTextureNode>
#include <QSGRendererInterface>
@@ -81,7 +81,7 @@ public:
* and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
* classes that consume texture providers.
*
- * \sa {Scene Graph - Rendering FBOs}, {Scene Graph and Rendering}
+ * \sa {Scene Graph and Rendering}
*/
/*!
diff --git a/src/quick/items/qquickgraphicsconfiguration.cpp b/src/quick/items/qquickgraphicsconfiguration.cpp
index ec0a9981dc..1cfa160e18 100644
--- a/src/quick/items/qquickgraphicsconfiguration.cpp
+++ b/src/quick/items/qquickgraphicsconfiguration.cpp
@@ -3,10 +3,7 @@
#include "qquickgraphicsconfiguration_p.h"
#include <QCoreApplication>
-
-#if QT_CONFIG(vulkan)
-#include <QtGui/private/qrhivulkan_p.h>
-#endif
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -57,8 +54,10 @@ QT_BEGIN_NAMESPACE
Vulkan, or graphics APIs where the concept is applicable. Where some
concepts are not applicable, the related settings are simply ignored.
- Examples of functions in this category are preferredInstanceExtensions()
- and setDeviceExtensions().
+ Examples of functions in this category are setDeviceExtensions() and
+ preferredInstanceExtensions(). The latter is useful when the application
+ manages its own \l QVulkanInstance which is then associated with the
+ QQuickWindow via \l QWindow::setVulkanInstance().
\section1 Qt Quick Scene Graph Renderer Configuration
@@ -181,7 +180,8 @@ QT_BEGIN_NAMESPACE
- With pipeline cache saving enabled, Qt stores all render and compute
pipelines encountered into an MTLBinaryArchive. Saving the pipeline cache
stores the blob retrieved from the archive, with additional metadata to
- identify the device.
+ identify the device. \b{Note:} currently MTLBinaryArchive usage is disabled
+ on macOS and iOS due to various issues on some hardware and OS versions.
\li OpenGL - There is no native concept of pipelines, the "pipeline cache"
stores a collection of program binaries retrieved via
@@ -190,7 +190,7 @@ QT_BEGIN_NAMESPACE
metadata to identify the device, driver, and its version that the binaries
were retrieved from. Persistent caching of program binaries is not new in
Qt: Qt 5 already had similar functionality in QOpenGLShaderProgram, see
- \l{QOpenGLShaderProgram::addCacheableShaderFromSourceCode()}{addCacheableShaderFromSourceCode()}
+ \l{QOpenGLShaderProgram::}{addCacheableShaderFromSourceCode()}
for example. In fact that mechanism is always active in Qt 6 as well when
using Qt Quick with OpenGL. However, when using the new, graphics API
independent pipeline cache abstraction provided here, the Qt 5 era program
@@ -212,24 +212,25 @@ QT_BEGIN_NAMESPACE
HLSL shader. A good example is Qt Quick 3D, where the runtime-generated
shaders for materials imply having to deal with HLSL source code. Saving
and reloading the Qt Quick pipeline cache can therefore bring considerable
- improvements in scenes with one or more \l{QtQuick3D::}View3D items in
+ improvements in scenes with one or more \l{View3D} items in
them. A counterexample may be Qt Quick itself: as most built-in shaders for
2D content ship with DirectX bytecode generated at build time, the cache is
not going to present any significant improvements.
\endlist
- All this is independent from the shader processing performed by the \l
- QtShaderTools module and its command-line tools such as \c qsb. As an
- example, take Vulkan. Having the Vulkan-compatible GLSL source code
- compiled to SPIR-V either at offline or build time (directly via qsb or
- CMake) is good, because the expensive compilation from source form is
- avoided at run time. SPIR-V is however a vendor-independent intermediate
- format. At runtime, when constructing graphics or compute pipelines, there
- is likely another round of compilation happening, this time from the
- intermediate format to the vendor-specific instruction set of the GPU (and
- this may be dependent on certain state in the graphics pipeline and the
- render targets as well). The pipeline cache helps with this latter phase.
+ All this is independent from the shader processing performed by the
+ \l [QtShaderTools]{Qt Shader Tools} module and its command-line tools such
+ as \c qsb. As an example, take Vulkan. Having the Vulkan-compatible GLSL
+ source code compiled to SPIR-V either at offline or build time (directly
+ via qsb or CMake) is good, because the expensive compilation from source
+ form is avoided at run time. SPIR-V is however a vendor-independent
+ intermediate format. At runtime, when constructing graphics or compute
+ pipelines, there is likely another round of compilation happening, this
+ time from the intermediate format to the vendor-specific instruction set of
+ the GPU (and this may be dependent on certain state in the graphics
+ pipeline and the render targets as well). The pipeline cache helps with
+ this latter phase.
\note Many graphics API implementation employ their own persistent disk
cache transparently to the applications. Using the pipeline cache feature
@@ -572,6 +573,51 @@ bool QQuickGraphicsConfiguration::isDebugMarkersEnabled() const
}
/*!
+ When enabled, GPU timing data is collected from command buffers on
+ platforms and 3D APIs where this is supported. This data is then printed in
+ the renderer logs that can be enabled via \c{QSG_RENDER_TIMING} environment
+ variable or logging categories such as \c{qt.scenegraph.time.renderloop},
+ and may also be made visible to other modules, such as Qt Quick 3D's
+ \l DebugView item.
+
+ By default this is disabled, because collecting the data may involve
+ additional work, such as inserting timestamp queries in the command stream,
+ depending on the underlying graphics API. To enable, either call this
+ function with \a enable set to true, or set the \c{QSG_RHI_PROFILE}
+ environment variable to a non-zero value.
+
+ Graphics APIs where this can be expected to be supported are Direct 3D 11,
+ Direct 3D 12, Vulkan (as long as the underlying Vulkan implementation
+ supports timestamp queries), Metal, and OpenGL with a core or compatibility
+ profile context for version 3.3 or newer. Timestamps are not supported with
+ OpenGL ES.
+
+ \since 6.6
+
+ \sa timestampsEnabled(), setDebugMarkers()
+ */
+void QQuickGraphicsConfiguration::setTimestamps(bool enable)
+{
+ if (d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps) != enable) {
+ detach();
+ d->flags.setFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps, enable);
+ }
+}
+
+/*!
+ \return true if GPU timing collection is enabled.
+
+ By default the value is false.
+
+ \since 6.6
+ \sa setTimestamps()
+ */
+bool QQuickGraphicsConfiguration::timestampsEnabled() const
+{
+ return d->flags.testFlag(QQuickGraphicsConfigurationPrivate::EnableTimestamps);
+}
+
+/*!
Requests choosing an adapter or physical device that uses software-based
rasterization. Applicable only when the underlying API has support for
enumerating adapters (for example, Direct 3D or Vulkan), and is ignored
@@ -624,7 +670,7 @@ bool QQuickGraphicsConfiguration::prefersSoftwareDevice() const
\since 6.5
- \sa isAutomaticPipelineCacheEnbled()
+ \sa isAutomaticPipelineCacheEnabled()
*/
void QQuickGraphicsConfiguration::setAutomaticPipelineCache(bool enable)
{
@@ -779,9 +825,9 @@ QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate()
if (enableDebugLayer)
flags |= EnableDebugLayer;
- static const bool enableDebugMarkers = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
- if (enableDebugMarkers)
- flags |= EnableDebugMarkers;
+ static const bool enableProfilingRelated = qEnvironmentVariableIntValue("QSG_RHI_PROFILE");
+ if (enableProfilingRelated)
+ flags |= EnableDebugMarkers | EnableTimestamps;
static const bool preferSoftwareDevice = qEnvironmentVariableIntValue("QSG_RHI_PREFER_SOFTWARE_RENDERER");
if (preferSoftwareDevice)
@@ -801,12 +847,12 @@ QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate()
pipelineCacheLoadFile = pipelineCacheLoadFileEnv;
}
-QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate *other)
+QQuickGraphicsConfigurationPrivate::QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate &other)
: ref(1),
- deviceExtensions(other->deviceExtensions),
- flags(other->flags),
- pipelineCacheSaveFile(other->pipelineCacheSaveFile),
- pipelineCacheLoadFile(other->pipelineCacheLoadFile)
+ deviceExtensions(other.deviceExtensions),
+ flags(other.flags),
+ pipelineCacheSaveFile(other.pipelineCacheSaveFile),
+ pipelineCacheLoadFile(other.pipelineCacheLoadFile)
{
}
diff --git a/src/quick/items/qquickgraphicsconfiguration.h b/src/quick/items/qquickgraphicsconfiguration.h
index d520a4a5ab..95777a1c51 100644
--- a/src/quick/items/qquickgraphicsconfiguration.h
+++ b/src/quick/items/qquickgraphicsconfiguration.h
@@ -34,6 +34,9 @@ public:
void setDebugMarkers(bool enable);
bool isDebugMarkersEnabled() const;
+ void setTimestamps(bool enable);
+ bool timestampsEnabled() const;
+
void setPreferSoftwareDevice(bool enable);
bool prefersSoftwareDevice() const;
diff --git a/src/quick/items/qquickgraphicsconfiguration_p.h b/src/quick/items/qquickgraphicsconfiguration_p.h
index 12980d154a..08b5e6026f 100644
--- a/src/quick/items/qquickgraphicsconfiguration_p.h
+++ b/src/quick/items/qquickgraphicsconfiguration_p.h
@@ -21,20 +21,21 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickGraphicsConfigurationPrivate
+class Q_QUICK_EXPORT QQuickGraphicsConfigurationPrivate
{
public:
static QQuickGraphicsConfigurationPrivate *get(QQuickGraphicsConfiguration *p) { return p->d; }
static const QQuickGraphicsConfigurationPrivate *get(const QQuickGraphicsConfiguration *p) { return p->d; }
QQuickGraphicsConfigurationPrivate();
- QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate *other);
+ QQuickGraphicsConfigurationPrivate(const QQuickGraphicsConfigurationPrivate &other);
enum Flag {
UseDepthBufferFor2D = 0x01,
EnableDebugLayer = 0x02,
EnableDebugMarkers = 0x04,
PreferSoftwareDevice = 0x08,
- AutoPipelineCache = 0x10
+ AutoPipelineCache = 0x10,
+ EnableTimestamps = 0x20
};
Q_DECLARE_FLAGS(Flags, Flag)
diff --git a/src/quick/items/qquickgraphicsdevice.cpp b/src/quick/items/qquickgraphicsdevice.cpp
index 5bd0865660..84eeb3e69e 100644
--- a/src/quick/items/qquickgraphicsdevice.cpp
+++ b/src/quick/items/qquickgraphicsdevice.cpp
@@ -93,11 +93,14 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromOpenGLContext(QOpenGLContext *con
/*!
\return a new QQuickGraphicsDevice describing a DXGI adapter and D3D feature level.
- This factory function is suitable for Direct3D 11, particularly in
+ This factory function is suitable for Direct3D 11 and 12, particularly in
combination with OpenXR. \a adapterLuidLow and \a adapterLuidHigh together
specify a LUID, while a featureLevel specifies a \c{D3D_FEATURE_LEVEL_}
value. \a featureLevel can be set to 0 if it is not intended to be
specified, in which case the scene graph's defaults will be used.
+
+ \note With Direct 3D 12 \a featureLevel specifies the \c minimum feature
+ level passed on to D3D12CreateDevice().
*/
#if defined(Q_OS_WIN) || defined(Q_QDOC)
QQuickGraphicsDevice QQuickGraphicsDevice::fromAdapter(quint32 adapterLuidLow,
@@ -120,6 +123,10 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromAdapter(quint32 adapterLuidLow,
be a \c{ID3D11Device*}, \a context is expected to be a
\c{ID3D11DeviceContext*}.
+ It also supports Direct 3D 12, if that is the 3D API used at run time. With
+ D3D12 \a context is unused and can be set to null. \a device is expected to
+ be a \c{ID3D12Device*}.
+
\note the resulting QQuickGraphicsDevice does not own any native resources,
it merely contains references. It is the caller's responsibility to ensure
that the native resource exists as long as necessary.
@@ -146,7 +153,7 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndContext(void *device, vo
that the native resource exists as long as necessary.
*/
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndCommandQueue(MTLDevice *device,
MTLCommandQueue *commandQueue)
{
@@ -205,12 +212,14 @@ QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceObjects(VkPhysicalDevice ph
#endif
/*!
- \internal
+ \return a new QQuickGraphicsDevice referencing an existing \a rhi object.
\note Similarly to fromOpenGLContext(), the caller must be careful to only
share a QRhi (and so the underlying graphics context or device) between
QQuickWindows that are known to be compatible, not breaking the underlying
graphics API's rules when it comes to threading, pixel formats, etc.
+
+ \since 6.6
*/
QQuickGraphicsDevice QQuickGraphicsDevice::fromRhi(QRhi *rhi)
{
@@ -226,10 +235,10 @@ QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate()
{
}
-QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other)
+QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate &other)
: ref(1),
- type(other->type),
- u(other->u)
+ type(other.type),
+ u(other.u)
{
}
diff --git a/src/quick/items/qquickgraphicsdevice.h b/src/quick/items/qquickgraphicsdevice.h
index 40c8020a2d..d668f5e576 100644
--- a/src/quick/items/qquickgraphicsdevice.h
+++ b/src/quick/items/qquickgraphicsdevice.h
@@ -10,7 +10,7 @@
#include <QtGui/qvulkaninstance.h>
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
Q_FORWARD_DECLARE_OBJC_CLASS(MTLDevice);
Q_FORWARD_DECLARE_OBJC_CLASS(MTLCommandQueue);
#endif
@@ -40,7 +40,7 @@ public:
static QQuickGraphicsDevice fromDeviceAndContext(void *device, void *context);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
static QQuickGraphicsDevice fromDeviceAndCommandQueue(MTLDevice *device, MTLCommandQueue *commandQueue);
#endif
diff --git a/src/quick/items/qquickgraphicsdevice_p.h b/src/quick/items/qquickgraphicsdevice_p.h
index 74f1e3af6e..d1c3bf1284 100644
--- a/src/quick/items/qquickgraphicsdevice_p.h
+++ b/src/quick/items/qquickgraphicsdevice_p.h
@@ -21,13 +21,13 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickGraphicsDevicePrivate
+class Q_QUICK_EXPORT QQuickGraphicsDevicePrivate
{
public:
static QQuickGraphicsDevicePrivate *get(QQuickGraphicsDevice *p) { return p->d; }
static const QQuickGraphicsDevicePrivate *get(const QQuickGraphicsDevice *p) { return p->d; }
QQuickGraphicsDevicePrivate();
- QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other);
+ QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate &other);
enum class Type {
Null,
diff --git a/src/quick/items/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp
index 45c86f1b24..7b84a7eb54 100644
--- a/src/quick/items/qquickgraphicsinfo.cpp
+++ b/src/quick/items/qquickgraphicsinfo.cpp
@@ -56,16 +56,16 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property describes the graphics API that is currently in use.
The possible values are:
- \list
- \li GraphicsInfo.Unknown - the default value when no active scenegraph is associated with the item
- \li GraphicsInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
- \li GraphicsInfo.OpenVG - OpenVG
- \li GraphicsInfo.OpenGL - OpenGL or OpenGL ES on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Direct3D11 - Direct3D 11 on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Vulkan - Vulkan on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Metal - Metal on top of QRhi, a graphics abstraction layer
- \li GraphicsInfo.Null - Null (no output) on top of QRhi, a graphics abstraction layer
- \endlist
+
+ \value GraphicsInfo.Unknown the default value when no active scenegraph is associated with the item
+ \value GraphicsInfo.Software Qt Quick's software renderer based on QPainter with the raster paint engine
+ \value GraphicsInfo.OpenVG OpenVG
+ \value GraphicsInfo.OpenGL OpenGL or OpenGL ES on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Direct3D11 Direct3D 11 on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Direct3D12 Direct3D 12 on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Vulkan Vulkan on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Metal Metal on top of QRhi, a graphics abstraction layer
+ \value GraphicsInfo.Null Null (no output) on top of QRhi, a graphics abstraction layer
*/
/*!
@@ -74,12 +74,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains the shading language supported by the Qt Quick
backend the application is using.
- \list
- \li GraphicsInfo.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
- \li GraphicsInfo.GLSL - GLSL or GLSL ES
- \li GraphicsInfo.HLSL - HLSL
- \li GraphicsInfo.RhiShader - QShader
- \endlist
+ \value GraphicsInfo.UnknownShadingLanguage Not yet known due to no window and scenegraph associated
+ \value GraphicsInfo.GLSL GLSL or GLSL ES
+ \value GraphicsInfo.HLSL HLSL
+ \value GraphicsInfo.RhiShader QShader
\note The value is only up-to-date once the item is associated with a
window. Bindings relying on the value have to keep this in mind since the
@@ -100,10 +98,8 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains a bitmask of the shader compilation approaches
supported by the Qt Quick backend the application is using.
- \list
- \li GraphicsInfo.RuntimeCompilation
- \li GraphicsInfo.OfflineCompilation
- \endlist
+ \value GraphicsInfo.RuntimeCompilation
+ \value GraphicsInfo.OfflineCompilation
With OpenGL the value is GraphicsInfo.RuntimeCompilation, which corresponds
to the traditional way of using ShaderEffect. Non-OpenGL backends are
@@ -127,11 +123,9 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property contains a bitmask of the supported ways of providing shader
sources.
- \list
- \li GraphicsInfo.ShaderSourceString
- \li GraphicsInfo.ShaderSourceFile
- \li GraphicsInfo.ShaderByteCode
- \endlist
+ \value GraphicsInfo.ShaderSourceString
+ \value GraphicsInfo.ShaderSourceFile
+ \value GraphicsInfo.ShaderByteCode
With OpenGL the value is GraphicsInfo.ShaderSourceString, which corresponds
to the traditional way of inlining GLSL source code into QML. Other,
@@ -182,11 +176,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
This property holds the configured OpenGL context profile.
The possible values are:
- \list
- \li GraphicsInfo.OpenGLNoProfile (default) - OpenGL version is lower than 3.2 or OpenGL is not in use.
- \li GraphicsInfo.OpenGLCoreProfile - Functionality deprecated in OpenGL version 3.0 is not available.
- \li GraphicsInfo.OpenGLCompatibilityProfile - Functionality from earlier OpenGL versions is available.
- \endlist
+
+ \value GraphicsInfo.OpenGLNoProfile (default) OpenGL version is lower than 3.2 or OpenGL is not in use.
+ \value GraphicsInfo.OpenGLCoreProfile Functionality deprecated in OpenGL version 3.0 is not available.
+ \value GraphicsInfo.OpenGLCompatibilityProfile Functionality from earlier OpenGL versions is available.
Reusable QML components will typically use this property in bindings in order to
choose between core and non core profile compatible shader sources.
@@ -203,11 +196,10 @@ QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
other than OpenGL.
The possible values are:
- \list
- \li GraphicsInfo.SurfaceFormatUnspecified (default) - Unspecified rendering method
- \li GraphicsInfo.SurfaceFormatOpenGL - Desktop OpenGL or other graphics API
- \li GraphicsInfo.SurfaceFormatOpenGLES - OpenGL ES
- \endlist
+
+ \value GraphicsInfo.SurfaceFormatUnspecified (default) Unspecified rendering method
+ \value GraphicsInfo.SurfaceFormatOpenGL Desktop OpenGL or other graphics API
+ \value GraphicsInfo.SurfaceFormatOpenGLES OpenGL ES
\note This is applicable only to OpenGL.
diff --git a/src/quick/items/qquickgraphicsinfo_p.h b/src/quick/items/qquickgraphicsinfo_p.h
index be3bef7fa9..a18694a83b 100644
--- a/src/quick/items/qquickgraphicsinfo_p.h
+++ b/src/quick/items/qquickgraphicsinfo_p.h
@@ -55,6 +55,7 @@ public:
Vulkan = QSGRendererInterface::Vulkan,
Metal = QSGRendererInterface::Metal,
Null = QSGRendererInterface::Null,
+ Direct3D12 = QSGRendererInterface::Direct3D12,
OpenGLRhi = QSGRendererInterface::OpenGLRhi,
Direct3D11Rhi = QSGRendererInterface::Direct3D11Rhi,
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 7f45d45708..e67d80b2d6 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -162,7 +162,9 @@ public:
void setPosition(qreal pos) override;
void layoutVisibleItems(int fromModelIndex = 0) override;
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
+#if QT_CONFIG(quick_viewtransitions)
void translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) override;
+#endif
bool needsRefillForAddedOrRemovedIndex(int index) const override;
qreal headerSize() const override;
@@ -181,7 +183,7 @@ public:
void fixupPosition() override;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) override;
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
QQuickGridView::Flow flow;
qreal cellWidth;
@@ -503,8 +505,10 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << colPos << rowPos;
if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, incubationMode))))
break;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
item->setPosition(colPos, rowPos, true);
+#endif
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
visibleItems.append(item);
if (++colNum >= columns) {
@@ -538,8 +542,10 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, incubationMode))))
break;
--visibleIndex;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
item->setPosition(colPos, rowPos, true);
+#endif
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
visibleItems.prepend(item);
if (--colNum < 0) {
@@ -555,11 +561,14 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
void QQuickGridViewPrivate::removeItem(FxViewItem *item)
{
+#if QT_CONFIG(quick_viewtransitions)
if (item->transitionScheduledOrRunning()) {
qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName();
item->releaseAfterTransition = true;
releasePendingTransition.append(item);
- } else {
+ } else
+#endif
+ {
releaseItem(item, QQmlDelegateModel::NotReusable);
}
}
@@ -883,7 +892,11 @@ void QQuickGridViewPrivate::initializeCurrentItem()
FxViewItem *actualItem = visibleItem(currentIndex);
// don't reposition the item if it's about to be transitioned to another position
- if ((!actualItem || !actualItem->transitionScheduledOrRunning()))
+ if ((!actualItem
+#if QT_CONFIG(quick_viewtransitions)
+ || !actualItem->transitionScheduledOrRunning()
+#endif
+ ))
gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
}
}
@@ -995,13 +1008,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
}
bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
{
data.fixingUp = false;
moveReason = Mouse;
if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
&& snapMode == QQuickGridView::NoSnap) {
- return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
}
qreal maxDistance = 0;
qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
@@ -1051,7 +1064,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
else
v = maxVelocity;
}
- qreal accel = deceleration;
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
qreal v2 = v * v;
qreal overshootDist = 0.0;
if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
@@ -1253,23 +1266,28 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
\qmlattachedproperty bool QtQuick::GridView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
+
+ \snippet qml/gridview/gridview.qml isCurrentItem
*/
/*!
\qmlattachedproperty GridView QtQuick::GridView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate and also to the header, the footer
and the highlight delegates.
-
- \snippet qml/gridview/gridview.qml isCurrentItem
*/
/*!
\qmlattachedproperty bool QtQuick::GridView::delayRemove
+
This attached property holds whether the delegate may be destroyed. It
is attached to each instance of the delegate. The default value is false.
@@ -1364,9 +1382,53 @@ 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.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::GridView::reuseItems
+
+ This property enables you to reuse items that are instantiated
+ from the \l delegate. If set to \c false, any currently
+ pooled items are destroyed.
+
+ This property is \c false by default.
+
+ \since 5.15
+
+ \sa {Reusing items}, pooled(), reused()
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::GridView::pooled()
+
+ This signal is emitted after an item has been added to the reuse
+ pool. You can use it to pause ongoing timers or animations inside
+ the item, or free up resources that cannot be reused.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, reused()
*/
+/*!
+ \qmlattachedsignal QtQuick::GridView::reused()
+
+ This signal is emitted after an item has been reused. At this point, the
+ item has been taken out of the pool and placed inside the content view,
+ and the model properties such as \c index and \c row have been updated.
+
+ Other properties that are not provided by the model does not change when an
+ item is reused. You should avoid storing any state inside a delegate, but if
+ you do, manually reset that state on receiving this signal.
+
+ This signal is emitted when the item is reused, and not the first time the
+ item is created.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, pooled()
+*/
/*!
\qmlproperty Component QtQuick::GridView::highlight
@@ -1427,36 +1489,32 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
Valid values for \c highlightRangeMode are:
- \list
- \li GridView.ApplyRange - the view attempts to maintain the highlight within the range.
+ \value GridView.ApplyRange the view attempts to maintain the highlight within the range.
However, the highlight can move outside of the range at the ends of the view or due
to mouse interaction.
- \li GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
+ \value GridView.StrictlyEnforceRange the highlight never moves outside of the range.
The current item changes if a keyboard or mouse action would cause the highlight to move
outside of the range.
- \li GridView.NoHighlightRange - this is the default value.
- \endlist
+ \value GridView.NoHighlightRange the default value
*/
/*!
- \qmlproperty enumeration QtQuick::GridView::layoutDirection
- This property holds the layout direction of the grid.
+ \qmlproperty enumeration QtQuick::GridView::layoutDirection
+ This property holds the layout direction of the grid.
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
- dependent on the \l GridView::flow property.
- \li Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
- on the \l GridView::flow property.
- \endlist
+ \value Qt.LeftToRight (default) Items will be laid out starting in the top, left corner. The flow is
+ dependent on the \l GridView::flow property.
+ \value Qt.RightToLeft Items will be laid out starting in the top, right corner. The flow is dependent
+ on the \l GridView::flow property.
- \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
- GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
- indicates that the flow is horizontal.
+ \b Note: If GridView::flow is set to GridView.FlowLeftToRight, this is not to be confused if
+ GridView::layoutDirection is set to Qt.RightToLeft. The GridView.FlowLeftToRight flow value simply
+ indicates that the flow is horizontal.
- \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
+ \sa GridView::effectiveLayoutDirection, GridView::verticalLayoutDirection
*/
@@ -1477,10 +1535,8 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
Possible values:
- \list
- \li GridView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
- \li GridView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
- \endlist
+ \value GridView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
+ \value GridView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
\sa GridView::layoutDirection
*/
@@ -1588,10 +1644,8 @@ void QQuickGridView::setHighlightMoveDuration(int duration)
Possible values:
- \list
- \li GridView.FlowLeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
- \li GridView.FlowTopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
- \endlist
+ \value GridView.FlowLeftToRight (default) Items are laid out from left to right, and the view scrolls vertically
+ \value GridView.FlowTopToBottom Items are laid out from top to bottom, and the view scrolls horizontally
*/
QQuickGridView::Flow QQuickGridView::flow() const
{
@@ -1668,15 +1722,12 @@ void QQuickGridView::setCellHeight(qreal cellHeight)
This property determines how the view scrolling will settle following a drag or flick.
The possible values are:
- \list
- \li GridView.NoSnap (default) - the view stops anywhere within the visible area.
- \li GridView.SnapToRow - the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
- aligned with the start of the view.
- \li GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
- away from the first visible row at the time the mouse button is released.
- This mode is particularly useful for moving one page at a time.
- \endlist
-
+ \value GridView.NoSnap (default) the view stops anywhere within the visible area.
+ \value GridView.SnapToRow the view settles with a row (or column for \c GridView.FlowTopToBottom flow)
+ aligned with the start of the view.
+ \value GridView.SnapOneRow the view will settle no more than one row (or column for \c GridView.FlowTopToBottom flow)
+ away from the first visible row at the time the mouse button is released.
+ This mode is particularly useful for moving one page at a time.
*/
QQuickGridView::SnapMode QQuickGridView::snapMode() const
{
@@ -2356,6 +2407,9 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
{
Q_Q(QQuickGridView);
+ if (q->size().isEmpty())
+ return false;
+
int modelIndex = change.index;
int count = change.count;
@@ -2404,6 +2458,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
}
}
+#if QT_CONFIG(quick_viewtransitions)
// Update the indexes of the following visible items.
for (FxViewItem *item : std::as_const(visibleItems)) {
if (item->index != -1 && item->index >= modelIndex) {
@@ -2414,6 +2469,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
}
}
+#endif
int prevVisibleCount = visibleItems.size();
if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
@@ -2443,9 +2499,11 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->changedFirstItem = true;
if (!change.isMove()) {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
item->moveTo(QPointF(colPos, rowPos), true);
}
insertResult->sizeChangesBeforeVisiblePos += rowSize();
@@ -2497,13 +2555,19 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
if (change.isMove()) {
// we know this is a move target, since move displaced items that are
// shuffled into view due to a move would be added in refill()
- if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
+ if (newItem
+#if QT_CONFIG(quick_viewtransitions)
+ && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)
+#endif
+ )
movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
} else {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
item->moveTo(QPointF(colPos, rowPos), true);
}
insertResult->sizeChangesAfterVisiblePos += rowSize();
@@ -2523,6 +2587,7 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
return visibleItems.size() > prevVisibleCount;
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
{
if (!transitioner)
@@ -2562,6 +2627,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
}
}
}
+#endif
bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
{
@@ -2576,18 +2642,15 @@ bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) co
Positions the view such that the \a index is at the position specified by
\a mode:
- \list
- \li GridView.Beginning - position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
- \li GridView.Center - position item in the center of the view.
- \li GridView.End - position item at bottom (or right for horizontal orientation) of the view.
- \li GridView.Visible - if any part of the item is visible then take no action, otherwise
- bring the item into view.
- \li GridView.Contain - ensure the entire item is visible. If the item is larger than
- the view the item is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
- \li GridView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ \value GridView.Beginning position item at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
+ \value GridView.Center position item in the center of the view.
+ \value GridView.End position item at bottom (or right for horizontal orientation) of the view.
+ \value GridView.Visible if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \value GridView.Contain ensure the entire item is visible. If the item is larger than the view, the item
+ is positioned at the top (or left for \c GridView.FlowTopToBottom flow) of the view.
+ \value GridView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid if
+ \l highlightRangeMode is \c StrictlyEnforceRange or snapping is enabled via \l snapMode.
If positioning the view at the index would cause empty space to be displayed at
the beginning or end of the view, the view will be positioned at the boundary.
diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h
index b90e5e6c9e..1f9698a706 100644
--- a/src/quick/items/qquickgridview_p.h
+++ b/src/quick/items/qquickgridview_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickGridViewAttached;
class QQuickGridViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickGridView : public QQuickItemView
+class Q_QUICK_EXPORT QQuickGridView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickGridView)
@@ -101,6 +101,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickGridView)
-
#endif // QQUICKGRIDVIEW_P_H
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 3b6ddf729f..3efe082332 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -115,6 +115,43 @@ QQuickImagePrivate::QQuickImagePrivate()
convert foo.png \( +clone -alpha Extract \) -channel RGB -compose Multiply -composite foo_pm.png
\endcode
+ Do not confuse container formats, such as, \c KTX, and the format of the
+ actual texture data stored in the container file. For example, reading a
+ \c KTX file is supported on all platforms, independently of what GPU driver is
+ used at run time. However, this does not guarantee that the compressed
+ texture format, used by the data in the file, is supported at run time. For
+ example, if the KTX file contains compressed data with the format
+ \c{ETC2 RGBA8}, and the 3D graphics API implementation used at run time does not
+ support \c ETC2 compressed textures, the Image item will not display
+ anything.
+
+ \note Compressed texture format support is not under Qt's control, and it
+ is up to the application or device developer to ensure the compressed
+ texture data is provided in the appropriate format for the target
+ environment(s).
+
+ Do not assume that compressed format support is specific to a platform. It
+ may also be specific to the driver and 3D API implementation in use on that
+ particular platform. In practice, implementations of different 3D graphics
+ APIs (e.g., Vulkan and OpenGL) on the same platform (e.g., Windows) from
+ the same vendor for the same hardware may offer a different set of
+ compressed texture formats.
+
+ When targeting desktop environments (Windows, macOS, Linux) only, a general
+ recommendation is to consider using the \c{DXTn}/\c{BCn} formats since
+ these tend to have the widest support amongst the implementations of Direct
+ 3D, Vulkan, OpenGL, and Metal on these platforms. In contrast, when
+ targeting mobile or embedded devices, the \c ETC2 or \c ASTC formats are
+ likely to be a better choice since these are typically the formats
+ supported by the OpenGL ES implementations on such hardware.
+
+ An application that intends to run across desktop, mobile, and embedded
+ hardware should plan and design its use of compressed textures carefully.
+ It is highly likely that relying on a single format is not going to be
+ sufficient, and therefore the application will likely need to branch based
+ on the platform to use compressed textures in a format appropriate there,
+ or perhaps to skip using compressed textures in some cases.
+
\section1 Automatic Detection of File Extension
If the \l source URL indicates a non-existing local file or resource, the
@@ -181,22 +218,16 @@ QQuickImage::~QQuickImage()
void QQuickImagePrivate::setImage(const QImage &image)
{
Q_Q(QQuickImage);
- pix.setImage(image);
-
+ currentPix->setImage(image);
q->pixmapChange();
- status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
-
q->update();
}
void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
{
Q_Q(QQuickImage);
- pix.setPixmap(pixmap);
-
+ currentPix->setPixmap(pixmap);
q->pixmapChange();
- status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
-
q->update();
}
@@ -205,15 +236,15 @@ void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
Set this property to define what happens when the source image has a different size
than the item.
- \list
- \li Image.Stretch - the image is scaled to fit
- \li Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
- \li Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
- \li Image.Tile - the image is duplicated horizontally and vertically
- \li Image.TileVertically - the image is stretched horizontally and tiled vertically
- \li Image.TileHorizontally - the image is stretched vertically and tiled horizontally
- \li Image.Pad - the image is not transformed
- \endlist
+
+ \value Image.Stretch the image is scaled to fit
+ \value Image.PreserveAspectFit the image is scaled uniformly to fit without cropping
+ \value Image.PreserveAspectCrop the image is scaled uniformly to fill, cropping if necessary
+ \value Image.Tile the image is duplicated horizontally and vertically
+ \value Image.TileVertically the image is stretched horizontally and tiled vertically
+ \value Image.TileHorizontally the image is stretched vertically and tiled horizontally
+ \value Image.Pad the image is not transformed
+ \br
\table
@@ -348,12 +379,11 @@ qreal QQuickImage::paintedHeight() const
\readonly
This property holds the status of image loading. It can be one of:
- \list
- \li Image.Null - no image has been set
- \li Image.Ready - the image has been loaded
- \li Image.Loading - the image is currently being loaded
- \li Image.Error - an error occurred while loading the image
- \endlist
+
+ \value Image.Null No image has been set
+ \value Image.Ready The image has been loaded
+ \value Image.Loading The image is currently being loaded
+ \value Image.Error An error occurred while loading the image
Use this status to provide an update or respond to the status change in some way.
For example, you could:
@@ -458,6 +488,8 @@ qreal QQuickImage::paintedHeight() const
\note \e {Changing this property dynamically causes the image source to be reloaded,
potentially even from the network, if it is not in the disk cache.}
+
+ \sa {Qt Quick Examples - Pointer Handlers}
*/
/*!
@@ -573,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;
@@ -595,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) {
@@ -610,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();
@@ -653,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;
@@ -679,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) {
@@ -704,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)
@@ -722,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;
@@ -765,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;
@@ -785,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,
@@ -935,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/qquickimage_p.h b/src/quick/items/qquickimage_p.h
index d7a55e529b..73dd3e2eb7 100644
--- a/src/quick/items/qquickimage_p.h
+++ b/src/quick/items/qquickimage_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickImagePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase
+class Q_QUICK_EXPORT QQuickImage : public QQuickImageBase
{
Q_OBJECT
@@ -30,6 +30,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase
Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged)
Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged)
Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged)
+ Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged REVISION(2, 3))
Q_PROPERTY(bool autoTransform READ autoTransform WRITE setAutoTransform NOTIFY autoTransformChanged REVISION(2, 5))
Q_PROPERTY(QRectF sourceClipRect READ sourceClipRect WRITE setSourceClipRect RESET resetSourceClipRect NOTIFY sourceClipRectChanged REVISION(2, 15))
@@ -100,5 +101,5 @@ private:
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickImage)
+
#endif // QQUICKIMAGE_P_H
diff --git a/src/quick/items/qquickimage_p_p.h b/src/quick/items/qquickimage_p_p.h
index 7b6dc00d6f..8f910385e8 100644
--- a/src/quick/items/qquickimage_p_p.h
+++ b/src/quick/items/qquickimage_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickImageTextureProvider : public QSGTextureProvider
+class Q_QUICK_EXPORT QQuickImageTextureProvider : public QSGTextureProvider
{
Q_OBJECT
public:
@@ -38,7 +38,7 @@ public:
bool m_mipmap;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate
+class Q_QUICK_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate
{
Q_DECLARE_PUBLIC(QQuickImage)
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 78dfecc42a..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;
@@ -41,6 +32,28 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
return setDevicePixelRatio;
}
+void QQuickImageBasePrivate::setStatus(QQuickImageBase::Status value)
+{
+ Q_Q(QQuickImageBase);
+
+ if (status == value)
+ return;
+
+ status = value;
+ emit q->statusChanged(status);
+}
+
+void QQuickImageBasePrivate::setProgress(qreal value)
+{
+ Q_Q(QQuickImageBase);
+
+ if (qFuzzyCompare(progress, value))
+ return;
+
+ progress = value;
+ emit q->progressChanged(progress);
+}
+
QQuickImageBase::QQuickImageBase(QQuickItem *parent)
: QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent)
{
@@ -63,14 +76,12 @@ QQuickImageBase::Status QQuickImageBase::status() const
return d->status;
}
-
qreal QQuickImageBase::progress() const
{
Q_D(const QQuickImageBase);
return d->progress;
}
-
bool QQuickImageBase::asynchronous() const
{
Q_D(const QQuickImageBase);
@@ -124,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()
@@ -145,6 +156,7 @@ void QQuickImageBase::setSourceClipRect(const QRectF &r)
return;
d->sourceClipRect = r;
+ d->providerOptions.setSourceClipRect(r);
emit sourceClipRectChanged();
if (isComponentComplete())
load();
@@ -176,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)
@@ -222,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;
@@ -252,12 +264,10 @@ int QQuickImageBase::frameCount() const
void QQuickImageBase::loadEmptyUrl()
{
Q_D(QQuickImageBase);
- d->pix.clear(this);
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- d->status = Null;
+ 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
pixmapChange(); // This calls update() in QQuickBorderImage and QQuickImage, not in QQuickImageBase...
@@ -281,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)
@@ -292,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);
}
@@ -304,24 +314,20 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
}
}
- d->pix.load(qmlEngine(this),
- loadUrl,
- d->sourceClipRect.toRect(),
- (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(),
- options,
- (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(),
- d->currentFrame, d->frameCount,
- d->devicePixelRatio);
-
- if (d->pix.isLoading()) {
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
- if (d->status != Loading) {
- d->status = Loading;
- emit statusChanged(d->status);
- }
+ d->status = Null; // reset status, no emit
+
+ 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->pendingPix->isLoading()) {
+ d->setProgress(0);
+ d->setStatus(Loading);
static int thisRequestProgress = -1;
static int thisRequestFinished = -1;
@@ -332,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();
}
@@ -355,24 +362,25 @@ 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;
- if (d->progress != 0.0) {
- d->progress = 0.0;
- emit progressChanged(d->progress);
- }
+ d->setProgress(0);
} else {
- d->status = Ready;
- if (d->progress != 1.0) {
- d->progress = 1.0;
- emit progressChanged(d->progress);
- }
+ d->status = Ready; // do not emit statusChanged until after setImplicitSize
+ d->setProgress(1);
}
+
pixmapChange();
emit statusChanged(d->status);
+
if (sourceSize() != d->oldSourceSize) {
d->oldSourceSize = sourceSize();
emit sourceSizeChanged();
@@ -381,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();
}
@@ -396,10 +404,8 @@ void QQuickImageBase::requestFinished()
void QQuickImageBase::requestProgress(qint64 received, qint64 total)
{
Q_D(QQuickImageBase);
- if (d->status == Loading && total > 0) {
- d->progress = qreal(received)/total;
- emit progressChanged(d->progress);
- }
+ if (d->status == Loading && total > 0)
+ d->setProgress(qreal(received) / total);
}
void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value)
@@ -431,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)
@@ -471,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;
}
@@ -501,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 5ad698816d..5a33db43b8 100644
--- a/src/quick/items/qquickimagebase_p.h
+++ b/src/quick/items/qquickimagebase_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
class QQuickImageBasePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
{
Q_OBJECT
@@ -31,9 +31,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem
Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged)
- Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
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))
@@ -95,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
@@ -114,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 04113da181..d53712b779 100644
--- a/src/quick/items/qquickimagebase_p_p.h
+++ b/src/quick/items/qquickimagebase_p_p.h
@@ -18,49 +18,56 @@
#include "qquickimplicitsizeitem_p_p.h"
#include "qquickimagebase_p.h"
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
QT_BEGIN_NAMESPACE
class QNetworkReply;
-class Q_QUICK_PRIVATE_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QQuickImageBase)
public:
QQuickImageBasePrivate()
- : status(QQuickImageBase::Null),
- progress(0.0),
- devicePixelRatio(1.0),
- currentFrame(0),
- frameCount(0),
- async(false),
+ : async(false),
cache(true),
mirrorHorizontally(false),
mirrorVertically(false),
- oldAutoTransform(false)
+ oldAutoTransform(false),
+ retainWhileLoading(false)
{
+ pendingPix = &pix1;
+ currentPix = &pix1;
}
virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio);
- QQuickPixmap pix;
- QQuickImageBase::Status status;
+ void setStatus(QQuickImageBase::Status value);
+ void setProgress(qreal value);
+
QUrl url;
- qreal progress;
+ QQuickPixmap *pendingPix = nullptr;
+ QQuickPixmap *currentPix = nullptr;
+ QQuickPixmap pix1;
+ QQuickPixmap pix2;
QSize sourcesize;
QSize oldSourceSize;
- qreal devicePixelRatio;
QRectF sourceClipRect;
QQuickImageProviderOptions providerOptions;
QColorSpace colorSpace;
- int currentFrame;
- int frameCount;
+
+ int currentFrame = 0;
+ int frameCount = 0;
+ qreal progress = 0;
+ qreal devicePixelRatio = 1;
+ QQuickImageBase::Status status = QQuickImageBase::Null;
+
bool async : 1;
bool cache : 1;
bool mirrorHorizontally: 1;
bool mirrorVertically : 1;
bool oldAutoTransform : 1;
+ bool retainWhileLoading : 1;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 507ad537ac..198c82f52c 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
class QQuickImplicitSizeItemPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
+class Q_QUICK_EXPORT QQuickImplicitSizeItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h
index c1ac09f850..4510391e71 100644
--- a/src/quick/items/qquickimplicitsizeitem_p_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItemPrivate : public QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickImplicitSizeItemPrivate : public QQuickItemPrivate
{
Q_DECLARE_PUBLIC(QQuickImplicitSizeItem)
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index cc860c9ae3..6c4ab28768 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -48,6 +48,8 @@
# include <QtGui/qcursor.h>
#endif
+#include <QtCore/qpointer.h>
+
#include <algorithm>
#include <limits>
@@ -55,13 +57,9 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcMouseTarget)
-Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
-Q_DECLARE_LOGGING_CATEGORY(lcPtr)
-Q_DECLARE_LOGGING_CATEGORY(lcTransient)
Q_LOGGING_CATEGORY(lcHandlerParent, "qt.quick.handler.parent")
Q_LOGGING_CATEGORY(lcVP, "qt.quick.viewport")
-Q_LOGGING_CATEGORY(lcChangeListeners, "qt.quick.item.changelisteners")
+Q_STATIC_LOGGING_CATEGORY(lcChangeListeners, "qt.quick.item.changelisteners")
// after 100ms, a mouse/non-mouse cursor conflict is resolved in favor of the mouse handler
static const quint64 kCursorOverrideTimeout = 100;
@@ -86,6 +84,14 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = nullptr, int depth = 1
}
}
+static void setActiveFocus(QQuickItem *item, Qt::FocusReason reason)
+{
+ QQuickItemPrivate *d = QQuickItemPrivate::get(item);
+ if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
+ QQuickWindowPrivate::get(d->window)->clearFocusInScope(item, d->subFocusItem, reason);
+ item->forceActiveFocus(reason);
+}
+
/*!
\qmltype Transform
\instantiates QQuickTransform
@@ -319,10 +325,10 @@ QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickItemKeyFilter::shortcutOverride(QKeyEvent *event)
+void QQuickItemKeyFilter::shortcutOverrideEvent(QKeyEvent *event)
{
if (m_next)
- m_next->shortcutOverride(event);
+ m_next->shortcutOverrideEvent(event);
else
event->ignore();
}
@@ -568,14 +574,12 @@ void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i)
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li KeyNavigation.BeforeItem - process the key events before normal
- item key processing. If the event is used for key navigation, it will be accepted and will not
- be passed on to the item.
- \li KeyNavigation.AfterItem (default) - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the KeyNavigation attached property handler.
- \endlist
+ \value KeyNavigation.BeforeItem process the key events before normal
+ item key processing. If the event is used for key navigation, it will be accepted and
+ will not be passed on to the item.
+ \value KeyNavigation.AfterItem (default) process the key events after normal item key
+ handling. If the item accepts the key event it will not be
+ handled by the KeyNavigation attached property handler.
*/
QQuickKeyNavigationAttached::Priority QQuickKeyNavigationAttached::priority() const
{
@@ -868,14 +872,11 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li Keys.BeforeItem (default) - process the key events before normal
- item key processing. If the event is accepted it will not
- be passed on to the item.
- \li Keys.AfterItem - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the Keys attached property handler.
- \endlist
+ \value Keys.BeforeItem (default) process the key events before normal item key processing.
+ If the event is accepted, it will not be passed on to the item.
+ \value Keys.AfterItem process the key events after normal item key handling. If the item
+ accepts the key event, it will not be handled by the
+ Keys attached property handler.
\sa {Key Handling Priorities}
*/
@@ -1385,7 +1386,7 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickKeysAttached::shortcutOverride(QKeyEvent *event)
+void QQuickKeysAttached::shortcutOverrideEvent(QKeyEvent *event)
{
Q_D(QQuickKeysAttached);
QQuickKeyEvent &keyEvent = d->theKeyEvent;
@@ -1700,6 +1701,54 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
}
}
+
+bool QQuickItemPrivate::setFocusIfNeeded(QEvent::Type eventType)
+{
+ Q_Q(QQuickItem);
+ const bool setFocusOnRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ Qt::FocusPolicy policy = Qt::ClickFocus;
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::TouchBegin:
+ if (setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::MouseButtonRelease:
+ case QEvent::TouchEnd:
+ if (!setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::Wheel:
+ policy = Qt::WheelFocus;
+ break;
+ default:
+ break;
+ }
+
+ if ((focusPolicy & policy) == policy) {
+ setActiveFocus(q, Qt::MouseFocusReason);
+ return true;
+ }
+
+ return false;
+}
+
+Qt::FocusReason QQuickItemPrivate::lastFocusChangeReason() const
+{
+ return static_cast<Qt::FocusReason>(focusReason);
+}
+
+bool QQuickItemPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ if (focusReason == reason)
+ return false;
+
+ focusReason = reason;
+ return true;
+}
+
/*!
\class QQuickItem
\brief The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
@@ -2211,6 +2260,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
*/
/*!
+ \fn void QQuickItem::focusPolicyChanged(Qt::FocusPolicy)
+ \internal
+*/
+
+/*!
\fn void QQuickItem::activeFocusOnTabChanged(bool)
\internal
*/
@@ -2381,7 +2435,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole();
if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
return true;
} else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
@@ -2413,12 +2467,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;
}
@@ -2467,7 +2541,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;
@@ -2561,6 +2635,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;
@@ -2806,8 +2888,8 @@ void QQuickItem::stackBefore(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex < siblingIndex ? siblingIndex - 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
@@ -2851,8 +2933,8 @@ void QQuickItem::stackAfter(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
@@ -3050,8 +3132,8 @@ void QQuickItemPrivate::derefWindow()
paintNode = nullptr;
for (int ii = 0; ii < childItems.size(); ++ii) {
- QQuickItem *child = childItems.at(ii);
- QQuickItemPrivate::get(child)->derefWindow();
+ if (QQuickItem *child = childItems.at(ii))
+ QQuickItemPrivate::get(child)->derefWindow();
}
dirty(Window);
@@ -3063,7 +3145,7 @@ void QQuickItemPrivate::derefWindow()
/*!
-Returns a transform that maps points from window space into item space.
+ Returns a transform that maps points from window space into item space.
*/
QTransform QQuickItemPrivate::windowToItemTransform() const
{
@@ -3072,21 +3154,21 @@ QTransform QQuickItemPrivate::windowToItemTransform() const
}
/*!
-Returns a transform that maps points from item space into window space.
+ Returns a transform that maps points from item space into window space.
*/
QTransform QQuickItemPrivate::itemToWindowTransform() const
{
// item's parent must not be itself, otherwise calling itemToWindowTransform() on it is infinite recursion
Q_ASSERT(!parentItem || QQuickItemPrivate::get(parentItem) != this);
QTransform rv = parentItem ? QQuickItemPrivate::get(parentItem)->itemToWindowTransform() : QTransform();
- itemToParentTransform(rv);
+ itemToParentTransform(&rv);
return rv;
}
/*!
-Motifies \a t with this items local transform relative to its parent.
+ Modifies \a t with this item's local transform relative to its parent.
*/
-void QQuickItemPrivate::itemToParentTransform(QTransform &t) const
+void QQuickItemPrivate::itemToParentTransform(QTransform *t) const
{
/* Read the current x and y values. As this is an internal method,
we don't care about it being usable in bindings. Instead, we
@@ -3098,21 +3180,21 @@ void QQuickItemPrivate::itemToParentTransform(QTransform &t) const
qreal x = this->x.valueBypassingBindings();
qreal y = this->y.valueBypassingBindings();
if (x || y)
- t.translate(x, y);
+ t->translate(x, y);
if (!transforms.isEmpty()) {
- QMatrix4x4 m(t);
+ QMatrix4x4 m(*t);
for (int ii = transforms.size() - 1; ii >= 0; --ii)
transforms.at(ii)->applyTo(&m);
- t = m.toTransform();
+ *t = m.toTransform();
}
if (scale() != 1. || rotation() != 0.) {
QPointF tp = computeTransformOrigin();
- t.translate(tp.x(), tp.y());
- t.scale(scale(), scale());
- t.rotate(rotation());
- t.translate(-tp.x(), -tp.y());
+ t->translate(tp.x(), tp.y());
+ t->scale(scale(), scale());
+ t->rotate(rotation());
+ t->translate(-tp.x(), -tp.y());
}
}
@@ -3201,6 +3283,8 @@ QQuickItemPrivate::QQuickItemPrivate()
, maybeHasSubsceneDeliveryAgent(true)
, subtreeTransformChangedEnabled(true)
, inDestructor(false)
+ , focusReason(Qt::OtherFocusReason)
+ , focusPolicy(Qt::NoFocus)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
@@ -3218,6 +3302,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, baselineOffset(0)
, itemNodeInstance(nullptr)
, paintNode(nullptr)
+ , szPolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Fixed)
{
}
@@ -3242,6 +3327,17 @@ void QQuickItemPrivate::init(QQuickItem *parent)
}
}
+QLayoutPolicy QQuickItemPrivate::sizePolicy() const
+{
+ return szPolicy;
+}
+
+void QQuickItemPrivate::setSizePolicy(const QLayoutPolicy::Policy& horizontalPolicy, const QLayoutPolicy::Policy& verticalPolicy)
+{
+ szPolicy.setHorizontalPolicy(horizontalPolicy);
+ szPolicy.setVerticalPolicy(verticalPolicy);
+}
+
void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
{
if (!o)
@@ -3251,34 +3347,15 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(o)) {
item->setParentItem(that);
- } else {
- if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
- if (pointerHandler->parent() != that) {
- qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
- pointerHandler->setParent(that);
- }
- QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
- } else {
- QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
- QQuickItem *item = that;
- QQuickWindow *itemWindow = that->window();
- while (!itemWindow && item && item->parentItem()) {
- item = item->parentItem();
- itemWindow = item->window();
- }
-
- if (thisWindow) {
- if (itemWindow) {
- qCDebug(lcTransient) << thisWindow << "is transient for" << itemWindow;
- thisWindow->setTransientParent(itemWindow);
- } else {
- QObject::connect(item, SIGNAL(windowChanged(QQuickWindow*)),
- thisWindow, SLOT(setTransientParent_helper(QQuickWindow*)));
- }
- }
- o->setParent(that);
- resources_append(prop, o);
+ } else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
+ if (pointerHandler->parent() != that) {
+ qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
+ pointerHandler->setParent(that);
}
+ QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
+ } else {
+ o->setParent(that);
+ resources_append(prop, o);
}
}
@@ -3354,6 +3431,22 @@ void QQuickItemPrivate::data_clear(QQmlListProperty<QObject> *property)
children_clear(&childrenProperty);
}
+void QQuickItemPrivate::data_removeLast(QQmlListProperty<QObject> *property)
+{
+ QQuickItem *item = static_cast<QQuickItem*>(property->object);
+ QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
+
+ QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
+ if (children_count(&childrenProperty) > 0) {
+ children_removeLast(&childrenProperty);
+ return;
+ }
+
+ QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
+ if (resources_count(&resourcesProperty) > 0)
+ resources_removeLast(&resourcesProperty);
+}
+
QObject *QQuickItemPrivate::resources_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3390,6 +3483,21 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
}
}
+void QQuickItemPrivate::resources_removeLast(QQmlListProperty<QObject> *prop)
+{
+ QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
+ if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
+ QList<QObject *> *resources = &quickItemPrivate->extra->resourcesList;
+ if (resources->isEmpty())
+ return;
+
+ qmlobject_disconnect(resources->last(), QObject, SIGNAL(destroyed(QObject*)),
+ quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
+ resources->removeLast();
+ }
+}
+
QQuickItem *QQuickItemPrivate::children_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3425,6 +3533,14 @@ void QQuickItemPrivate::children_clear(QQmlListProperty<QQuickItem> *prop)
p->childItems.at(0)->setParentItem(nullptr);
}
+void QQuickItemPrivate::children_removeLast(QQmlListProperty<QQuickItem> *prop)
+{
+ QQuickItem *that = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *p = QQuickItemPrivate::get(that);
+ if (!p->childItems.isEmpty())
+ p->childItems.last()->setParentItem(nullptr);
+}
+
qsizetype QQuickItemPrivate::visibleChildren_count(QQmlListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3653,10 +3769,16 @@ void QQuickItemPrivate::siblingOrderChanged()
QQmlListProperty<QObject> QQuickItemPrivate::data()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::data_append,
- QQuickItemPrivate::data_count,
- QQuickItemPrivate::data_at,
- QQuickItemPrivate::data_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::data_append;
+ result.count = QQuickItemPrivate::data_count;
+ result.at = QQuickItemPrivate::data_at;
+ result.clear = QQuickItemPrivate::data_clear;
+ result.removeLast = QQuickItemPrivate::data_removeLast;
+ return result;
}
/*!
@@ -4021,7 +4143,7 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
/*!
This event handler can be reimplemented in a subclass to receive focus-in
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
@@ -4029,8 +4151,9 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
If you do reimplement this function, you should call the base class
implementation.
*/
-void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusInEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) {
@@ -4039,17 +4162,20 @@ void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
}
}
#endif
+ d->setLastFocusChangeReason(event->reason());
}
/*!
This event handler can be reimplemented in a subclass to receive focus-out
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
*/
-void QQuickItem::focusOutEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusOutEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
+ d->setLastFocusChangeReason(event->reason());
}
/*!
@@ -4274,7 +4400,11 @@ void QQuickItem::dropEvent(QDropEvent *event)
This method will only be called if filtersChildMouseEvents() is \c true.
Return \c true if the specified \a event should not be passed on to the
- specified child \a item, and \c false otherwise.
+ specified child \a item, and \c false otherwise. If you return \c true, you
+ should also \l {QEvent::accept()}{accept} or \l {QEvent::ignore()}{ignore}
+ the \a event, to signal if event propagation should stop or continue.
+ The \a event will, however, always be sent to all childMouseEventFilters
+ up the parent chain.
\note Despite the name, this function filters all QPointerEvent instances
during delivery to all children (typically mouse, touch, and tablet
@@ -4516,7 +4646,7 @@ void QQuickItem::ensurePolished()
}
#if QT_DEPRECATED_SINCE(6, 5)
-static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
+static bool unwrapMapFromToFromItemArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
QQuickItem **itemObj, qreal *x, qreal *y, qreal *w, qreal *h, bool *isRect)
{
QV4::ExecutionEngine *v4 = args->v4engine();
@@ -4613,7 +4743,7 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect from the coordinate system of
- the root QML view.
+ the \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
@@ -4622,7 +4752,7 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
/*!
\internal
*/
-void QQuickItem::mapFromItem(QQmlV4Function *args) const
+void QQuickItem::mapFromItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4671,7 +4801,7 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect to the coordinate system of the
- root QML view.
+ \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
@@ -4680,7 +4810,7 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
/*!
\internal
*/
-void QQuickItem::mapToItem(QQmlV4Function *args) const
+void QQuickItem::mapToItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4698,7 +4828,7 @@ void QQuickItem::mapToItem(QQmlV4Function *args) const
args->setReturnValue(rv.asReturnedValue());
}
-static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
+static bool unwrapMapFromToFromGlobalArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
{
QV4::ExecutionEngine *v4 = args->v4engine();
if (args->length() != 1 && args->length() != 2) {
@@ -4756,7 +4886,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\internal
*/
-void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
+void QQuickItem::mapFromGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4786,7 +4916,7 @@ void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
/*!
\internal
*/
-void QQuickItem::mapToGlobal(QQmlV4Function *args) const
+void QQuickItem::mapToGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4860,7 +4990,6 @@ void QQuickItem::forceActiveFocus()
void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
- Q_D(QQuickItem);
setFocus(true, reason);
QQuickItem *parent = parentItem();
QQuickItem *scope = nullptr;
@@ -4872,14 +5001,6 @@ void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
}
parent = parent->parentItem();
}
- // In certain reparenting scenarios, d->focus might be true and the scope
- // might also have focus, so that setFocus() returns early without actually
- // acquiring active focus, because it thinks it already has it. In that
- // case, try to set the DeliveryAgent's active focus. (QTBUG-89736).
- if (scope && !d->activeFocus) {
- if (auto da = d->deliveryAgentPrivate())
- da->setFocusInScope(scope, this, Qt::OtherFocusReason);
- }
}
/*!
@@ -4982,12 +5103,17 @@ void QQuickItemPrivate::dumpItemTree(int indent) const
{
Q_Q(const QQuickItem);
- qDebug().nospace().noquote() << QString(indent * 4, QLatin1Char(' ')) <<
+ const auto indentStr = QString(indent * 4, QLatin1Char(' '));
+ qDebug().nospace().noquote() << indentStr <<
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
const_cast<QQuickItem *>(q);
#else
q;
#endif
+ if (extra.isAllocated()) {
+ for (const auto handler : extra->pointerHandlers)
+ qDebug().nospace().noquote() << indentStr << u" \u26ee " << handler;
+ }
for (const QQuickItem *ch : childItems) {
auto itemPriv = QQuickItemPrivate::get(ch);
itemPriv->dumpItemTree(indent + 1);
@@ -4996,10 +5122,16 @@ void QQuickItemPrivate::dumpItemTree(int indent) const
QQmlListProperty<QObject> QQuickItemPrivate::resources()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::resources_append,
- QQuickItemPrivate::resources_count,
- QQuickItemPrivate::resources_at,
- QQuickItemPrivate::resources_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::resources_append;
+ result.count = QQuickItemPrivate::resources_count;
+ result.at = QQuickItemPrivate::resources_at;
+ result.clear = QQuickItemPrivate::resources_clear;
+ result.removeLast = QQuickItemPrivate::resources_removeLast;
+ return result;
}
/*!
@@ -5021,11 +5153,16 @@ QQmlListProperty<QObject> QQuickItemPrivate::resources()
*/
QQmlListProperty<QQuickItem> QQuickItemPrivate::children()
{
- return QQmlListProperty<QQuickItem>(q_func(), nullptr, QQuickItemPrivate::children_append,
- QQuickItemPrivate::children_count,
- QQuickItemPrivate::children_at,
- QQuickItemPrivate::children_clear);
-
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QQuickItem> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::children_append;
+ result.count = QQuickItemPrivate::children_count;
+ result.at = QQuickItemPrivate::children_at;
+ result.clear = QQuickItemPrivate::children_clear;
+ result.removeLast = QQuickItemPrivate::children_removeLast;
+ return result;
}
/*!
@@ -5319,7 +5456,8 @@ bool QQuickItemPrivate::transformChanged(QQuickItem *transformedItem)
if (subtreeTransformChangedEnabled) {
// Inform the children in paint order: by the time we visit leaf items,
// they can see any consequences in their parents
- for (auto child : paintOrderChildItems())
+ const auto children = paintOrderChildItems();
+ for (QQuickItem *child : children)
childWantsIt |= QQuickItemPrivate::get(child)->transformChanged(transformedItem);
}
@@ -5337,18 +5475,35 @@ bool QQuickItemPrivate::transformChanged(QQuickItem *transformedItem)
}
// If ItemObservesViewport, clipRect() calculates the intersection with the viewport;
// so each time the item moves in the viewport, its clipnode needs to be updated.
- if (thisWantsIt && q->clip())
+ if (thisWantsIt && q->clip() && !(dirtyAttributes & QQuickItemPrivate::Clip))
dirty(QQuickItemPrivate::Clip);
return ret;
}
+/*! \internal
+ Returns the new position (proposed values for the x and y properties)
+ to which this item should be moved to compensate for the given change
+ in scale from \a startScale to \a activeScale and in rotation from
+ \a startRotation to \a activeRotation. \a centroidParentPos is the
+ point that we wish to hold in place (and then apply \a activeTranslation to),
+ in this item's parent's coordinate system. \a startPos is this item's
+ position in its parent's coordinate system when the gesture began.
+ \a activeTranslation is the amount of translation that should be added to
+ the return value, i.e. the displacement by which the centroid is expected
+ to move.
+
+ If \a activeTranslation is \c (0, 0) the centroid is to be held in place.
+ If \a activeScale is \c 1, it means scale is intended to be held constant,
+ the same as \a startScale. If \a activeRotation is \c 0, it means rotation
+ is intended to be held constant, the same as \a startRotation.
+*/
QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParentPos,
const QPointF &startPos,
- const QVector2D &activeTranslation, //[0,0] means no additional translation from startPos
+ const QVector2D &activeTranslation,
qreal startScale,
- qreal activeScale, // 1.0 means no additional scale from startScale
+ qreal activeScale,
qreal startRotation,
- qreal activeRotation) // 0.0 means no additional rotation from startRotation
+ qreal activeRotation)
{
Q_Q(QQuickItem);
QVector3D xformOrigin(q->transformOriginPoint());
@@ -5474,6 +5629,41 @@ bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
return e->isAccepted();
}
+void QQuickItemPrivate::deliverPointerEvent(QEvent *event)
+{
+ Q_Q(QQuickItem);
+ const auto eventType = event->type();
+ const bool focusAccepted = setFocusIfNeeded(eventType);
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ q->mousePressEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ q->mouseReleaseEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonDblClick:
+ q->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event));
+ break;
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+ q->wheelEvent(static_cast<QWheelEvent*>(event));
+ break;
+#endif
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ q->touchEvent(static_cast<QTouchEvent *>(event));
+ break;
+ default:
+ break;
+ }
+
+ if (focusAccepted)
+ event->accept();
+}
+
void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
{
Q_Q(QQuickItem);
@@ -5542,7 +5732,7 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
{
if (extra.isAllocated() && extra->keyHandler)
- extra->keyHandler->shortcutOverride(event);
+ extra->keyHandler->shortcutOverrideEvent(event);
else
event->ignore();
}
@@ -5945,8 +6135,8 @@ void QQuickItem::setZ(qreal v)
d->dirty(QQuickItemPrivate::ZValue);
if (d->parentItem) {
- QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
QQuickItemPrivate::get(d->parentItem)->markSortedChildrenDirty(this);
+ QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
}
emit zChanged();
@@ -6310,7 +6500,11 @@ void QQuickItem::setOpacity(qreal newOpacity)
\note This property's value is only affected by changes to this property or
the parent's \c visible property. It does not change, for example, if this
- item moves off-screen, or if the \l opacity changes to 0.
+ item moves off-screen, or if the \l opacity changes to 0. However, for
+ historical reasons, this property is true after the item's construction, even
+ if the item hasn't been added to a scene yet. Changing or reading this
+ property of an item that has not been added to a scene might not produce
+ the expected results.
\note The notification signal for this property gets emitted during destruction
of the visual parent. C++ signal handlers cannot assume that items in the
@@ -6380,9 +6574,9 @@ void QQuickItem::setVisible(bool v)
Thus, a disabled item can continue to receive hover events, even when this
property is \c false. This makes it possible to show informational feedback
(such as \l ToolTip) even when an interactive item is disabled.
- The same is also true for any \l {HoverHandlers}{QQuickHoverHandler}
+ The same is also true for any \l {HoverHandler}{HoverHandlers}
added as children of the item. A HoverHandler can, however, be
- \l{disabled}{QQuickHoverHandler::enabled} explicitly, or for example
+ \l {PointerHandler::enabled}{disabled} explicitly, or for example
be bound to the \c enabled state of the item.
\sa visible
@@ -6561,9 +6755,6 @@ QString QQuickItemPrivate::dirtyToString() const
void QQuickItemPrivate::dirty(DirtyType type)
{
Q_Q(QQuickItem);
- if (type & (TransformOrigin | Transform | BasicTransform | Position | Size))
- transformChanged(q);
-
if (!(dirtyAttributes & type) || (window && !prevDirtyItem)) {
dirtyAttributes |= type;
if (window && componentComplete) {
@@ -6571,6 +6762,8 @@ void QQuickItemPrivate::dirty(DirtyType type)
QQuickWindowPrivate::get(window)->dirtyItem(q);
}
}
+ if (type & (TransformOrigin | Transform | BasicTransform | Position | Size | Clip))
+ transformChanged(q);
}
void QQuickItemPrivate::addToDirtyList()
@@ -6666,8 +6859,18 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
- if (!subtreeTransformChangedEnabled)
- subtreeTransformChangedEnabled = true;
+ // The newly added child or any of its descendants may have
+ // ItemObservesViewport set, in which case we need to both
+ // inform the item that the transform has changed, and re-apply
+ // subtreeTransformChangedEnabled to both this item and its
+ // ancestors.
+ if (QQuickItemPrivate::get(data.item)->transformChanged(q)) {
+ if (!subtreeTransformChangedEnabled) {
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << q;
+ subtreeTransformChangedEnabled = true;
+ }
+ enableSubtreeChangeNotificationsForParentHierachy();
+ }
notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildAdded, q, data.item);
break;
}
@@ -6779,6 +6982,7 @@ void QQuickItem::setSmooth(bool smooth)
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
*/
+// TODO FOCUS: Deprecate
bool QQuickItem::activeFocusOnTab() const
{
Q_D(const QQuickItem);
@@ -6895,15 +7099,23 @@ void QQuickItem::setFlag(Flag flag, bool enabled)
else
setFlags((Flags)(d->flags & ~(quint32)flag));
- if (enabled && flag == ItemObservesViewport) {
- QQuickItem *par = parentItem();
- while (par) {
- auto parPriv = QQuickItemPrivate::get(par);
- if (!parPriv->subtreeTransformChangedEnabled)
- qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << par;
- parPriv->subtreeTransformChangedEnabled = true;
- par = par->parentItem();
- }
+ // We don't return early if the flag did not change. That's useful in case
+ // we need to intentionally trigger this parent-chain traversal again.
+ if (enabled && flag == ItemObservesViewport)
+ d->enableSubtreeChangeNotificationsForParentHierachy();
+}
+
+void QQuickItemPrivate::enableSubtreeChangeNotificationsForParentHierachy()
+{
+ Q_Q(QQuickItem);
+
+ QQuickItem *par = q->parentItem();
+ while (par) {
+ auto parPriv = QQuickItemPrivate::get(par);
+ if (!parPriv->subtreeTransformChangedEnabled)
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << par;
+ parPriv->subtreeTransformChangedEnabled = true;
+ par = par->parentItem();
}
}
@@ -6994,15 +7206,17 @@ void QQuickItem::setX(qreal v)
if (qt_is_nan(v))
return;
- const qreal oldx = d->x;
+ const qreal oldx = d->x.valueBypassingBindings();
if (oldx == v)
return;
- d->x = v;
+ d->x.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
- const qreal y = d->y, w = d->width, h = d->height;
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(v, y, w, h), QRectF(oldx, y, w, h));
}
@@ -7013,17 +7227,19 @@ void QQuickItem::setY(qreal v)
if (qt_is_nan(v))
return;
- const qreal oldy = d->y;
+ const qreal oldy = d->y.valueBypassingBindings();
if (oldy == v)
return;
- d->y = v;
+ d->y.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
// we use v instead of d->y, as that avoid a method call
// and we have v anyway in scope
- const qreal x = d->x, w = d->width, h = d->height;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(x, v, w, h), QRectF(x, oldy, w, h));
}
@@ -7033,11 +7249,12 @@ void QQuickItem::setY(qreal v)
void QQuickItem::setPosition(const QPointF &pos)
{
Q_D(QQuickItem);
- if (QPointF(d->x, d->y) == pos)
- return;
- const qreal oldx = d->x;
- const qreal oldy = d->y;
+ const qreal oldx = d->x.valueBypassingBindings();
+ const qreal oldy = d->y.valueBypassingBindings();
+
+ if (QPointF(oldx, oldy) == pos)
+ return;
/* This preserves the bindings, because that was what the code used to do
The effect of this is that you can have
@@ -7057,7 +7274,8 @@ void QQuickItem::setPosition(const QPointF &pos)
d->dirty(QQuickItemPrivate::Position);
- const qreal w = d->width, h = d->height;
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(pos.x(), pos.y(), w, h), QRectF(oldx, oldy, w, h));
}
@@ -7093,15 +7311,17 @@ void QQuickItem::setWidth(qreal w)
return;
d->widthValidFlag = true;
- const qreal oldWidth = d->width;
+ const qreal oldWidth = d->width.valueBypassingBindings();
if (oldWidth == w)
return;
- d->width = w;
+ d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y, h = d->height;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
geometryChange(QRectF(x, y, w, h), QRectF(x, y, oldWidth, h));
}
@@ -7299,15 +7519,17 @@ void QQuickItem::setHeight(qreal h)
return;
d->heightValidFlag = true;
- const qreal oldHeight = d->height;
+ const qreal oldHeight = d->height.valueBypassingBindings();
if (oldHeight == h)
return;
- d->height = h;
+ d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y, w = d->width;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
geometryChange(QRectF(x, y, w, h), QRectF(x, y, w, oldHeight));
}
@@ -7411,11 +7633,11 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
const qreal oldHeight = height;
if (!wDone) {
width = w;
- d->width = w;
+ d->width.setValueBypassingBindings(w);
}
if (!hDone) {
height = h;
- d->height = h;
+ d->height.setValueBypassingBindings(h);
}
d->dirty(QQuickItemPrivate::Size);
@@ -7471,17 +7693,19 @@ void QQuickItem::setSize(const QSizeF &size)
d->heightValidFlag = true;
d->widthValidFlag = true;
- if (d->width == size.width() && d->height == size.height())
+ const qreal oldHeight = d->height.valueBypassingBindings();
+ const qreal oldWidth = d->width.valueBypassingBindings();
+
+ if (oldWidth == size.width() && oldHeight == size.height())
return;
- const qreal oldHeight = d->height;
- const qreal oldWidth = d->width;
d->height.setValueBypassingBindings(size.height());
d->width.setValueBypassingBindings(size.width());
d->dirty(QQuickItemPrivate::Size);
- const qreal x = d->x, y = d->y;
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
geometryChange(QRectF(x, y, size.width(), size.height()), QRectF(x, y, oldWidth, oldHeight));
}
@@ -7662,15 +7886,16 @@ void QQuickItem::setFocus(bool focus)
void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
{
Q_D(QQuickItem);
- if (d->focus == focus)
+ // Need to find our nearest focus scope
+ QQuickItem *scope = parentItem();
+ while (scope && !scope->isFocusScope() && scope->parentItem())
+ scope = scope->parentItem();
+
+ if (d->focus == focus && (!focus || !scope || QQuickItemPrivate::get(scope)->subFocusItem == this))
return;
bool notifyListeners = false;
if (d->window || d->parentItem) {
- // Need to find our nearest focus scope
- QQuickItem *scope = parentItem();
- while (scope && !scope->isFocusScope() && scope->parentItem())
- scope = scope->parentItem();
if (d->window) {
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
@@ -7744,6 +7969,52 @@ QQuickItem *QQuickItem::scopedFocusItem() const
}
/*!
+ \qmlproperty enumeration QtQuick::Item::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+ \value Qt.TabFocus The item accepts focus by tabbing.
+ \value Qt.ClickFocus The item accepts focus by clicking.
+ \value Qt.StrongFocus The item accepts focus by both tabbing and clicking.
+ \value Qt.WheelFocus The item accepts focus by tabbing, clicking, and using the mouse wheel.
+ \value Qt.NoFocus The item does not accept focus.
+
+ \note This property was a member of the \l[QML]{Control} QML type in Qt 6.6 and earlier.
+*/
+/*!
+ \property QQuickItem::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+*/
+Qt::FocusPolicy QQuickItem::focusPolicy() const
+{
+ Q_D(const QQuickItem);
+ uint policy = d->focusPolicy;
+ if (activeFocusOnTab())
+ policy |= Qt::TabFocus;
+ return static_cast<Qt::FocusPolicy>(policy);
+}
+
+/*!
+ Sets the focus policy of this item to \a policy.
+
+ \sa focusPolicy()
+*/
+void QQuickItem::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ Q_D(QQuickItem);
+ if (d->focusPolicy == policy)
+ return;
+
+ d->focusPolicy = policy;
+ setActiveFocusOnTab(policy & Qt::TabFocus);
+ emit focusPolicyChanged(policy);
+}
+
+/*!
Returns \c true if this item is an ancestor of \a child (i.e., if this item
is \a child's parent, or one of \a child's parent's ancestors).
@@ -8343,7 +8614,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
Returns \c true if this item contains \a point, which is in local coordinates;
returns \c false otherwise. This is the same check that is used for
hit-testing a QEventPoint during event delivery, and is affected by
- containmentMask() if it is set.
+ \l containmentMask if it is set.
*/
/*!
Returns \c true if this item contains \a point, which is in local coordinates;
@@ -8351,7 +8622,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
This function can be overridden in order to handle point collisions in items
with custom shapes. The default implementation checks whether the point is inside
- containmentMask() if it is set, or inside the bounding box otherwise.
+ \l containmentMask() if it is set, or inside the bounding box otherwise.
\note This method is used for hit-testing each QEventPoint during event
delivery, so the implementation should be kept as lightweight as possible.
@@ -8381,10 +8652,10 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains() method. Its main use is currently to determine
+ \l contains() method. Its main use is currently to determine
whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
- By default the \l contains method will return true for any point
+ By default the \c contains() method will return true for any point
within the Item's bounding box. \c containmentMask allows for
more fine-grained control. For example, if a custom C++
QQuickItem subclass with a specialized contains() method
@@ -8495,7 +8766,7 @@ void QQuickItem::setContainmentMask(QObject *mask)
\input item.qdocinc mapping
- If \a item is 0, this maps \a point to the coordinate system of the
+ If \a item is \nullptr, this maps \a point to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8503,8 +8774,13 @@ void QQuickItem::setContainmentMask(QObject *mask)
QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) const
{
QPointF p = mapToScene(point);
- if (item)
+ if (item) {
+ const QQuickWindow *itemWindow = item->window();
+ if (itemWindow != nullptr && itemWindow != window())
+ p = itemWindow->mapFromGlobal(window()->mapToGlobal(p));
+
p = item->mapFromScene(p);
+ }
return p;
}
@@ -8553,7 +8829,7 @@ QPointF QQuickItem::mapToGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect to the coordinate system of the
+ If \a item is \nullptr, this maps \a rect to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8589,14 +8865,20 @@ QRectF QQuickItem::mapRectToScene(const QRectF &rect) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a point from the coordinate system of the
+ If \a item is \nullptr, this maps \a point from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapFromItem(const QQuickItem *item, const QPointF &point) const
{
- QPointF p = item?item->mapToScene(point):point;
+ QPointF p = point;
+ if (item) {
+ p = item->mapToScene(point);
+
+ if (item->window() != window())
+ p = window()->mapFromGlobal(item->window()->mapToGlobal(p));
+ }
return mapFromScene(p);
}
@@ -8656,7 +8938,7 @@ QPointF QQuickItem::mapFromGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect from the coordinate system of the
+ If \a item is \nullptr, this maps \a rect from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8764,8 +9046,14 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel:
- touchEvent(static_cast<QTouchEvent*>(ev));
- break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+#endif
+ d->deliverPointerEvent(ev);
+ break;
case QEvent::StyleAnimationUpdate:
if (isVisible()) {
ev->accept();
@@ -8797,20 +9085,6 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent*>(ev));
break;
- case QEvent::MouseButtonPress:
- mousePressEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonRelease:
- mouseReleaseEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonDblClick:
- mouseDoubleClickEvent(static_cast<QMouseEvent*>(ev));
- break;
-#if QT_CONFIG(wheelevent)
- case QEvent::Wheel:
- wheelEvent(static_cast<QWheelEvent*>(ev));
- break;
-#endif
#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
@@ -8870,6 +9144,16 @@ QDebug operator<<(QDebug debug,
const QRectF rect(item->position(), QSizeF(item->width(), item->height()));
debug << item->metaObject()->className() << '(' << static_cast<void *>(item);
+
+ // Deferred properties will cause recursion when calling nameForObject
+ // before the component is completed, so guard against this situation.
+ if (item->isComponentComplete()) {
+ if (QQmlContext *context = qmlContext(item)) {
+ const auto objectId = context->nameForObject(item);
+ if (!objectId.isEmpty())
+ debug << ", id=" << objectId;
+ }
+ }
if (!item->objectName().isEmpty())
debug << ", name=" << item->objectName();
debug << ", parent=" << static_cast<void *>(item->parentItem())
@@ -9030,12 +9314,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();
}
@@ -9368,14 +9652,12 @@ void QQuickItemLayer::setMipmap(bool mipmap)
Modifying this property makes most sense when the \a layer.effect is also
specified.
- \list
- \li ShaderEffectSource.RGBA8
- \li ShaderEffectSource.RGBA16F
- \li ShaderEffectSource.RGBA32F
- \li ShaderEffectSource.Alpha - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGB - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGBA - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \endlist
+ \value ShaderEffectSource.RGBA8
+ \value ShaderEffectSource.RGBA16F
+ \value ShaderEffectSource.RGBA32F
+ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
\sa {Item Layers}
*/
@@ -9496,12 +9778,10 @@ void QQuickItemLayer::setSize(const QSize &size)
Modifying this property makes most sense when the \a layer.effect is
specified.
- \list
- \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
- \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
- \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
- \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
- \endlist
+ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
+ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
\note Some OpenGL ES 2 implementations do not support the GL_REPEAT
wrap mode with non-power-of-two textures.
@@ -9531,11 +9811,9 @@ void QQuickItemLayer::setWrapMode(QQuickShaderEffectSource::WrapMode mode)
such as those specified by ShaderEffect. If no effect is specified for the layered
item, mirroring has no effect on the UI representation of the item.
- \list
- \li ShaderEffectSource.NoMirroring - No mirroring
- \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
- \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
- \endlist
+ \value ShaderEffectSource.NoMirroring No mirroring
+ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
+ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
*/
void QQuickItemLayer::setTextureMirroring(QQuickShaderEffectSource::TextureMirroring mirroring)
@@ -9721,13 +9999,20 @@ QQuickItemPrivate::ExtraData::ExtraData()
#if QT_CONFIG(accessibility)
-QAccessible::Role QQuickItemPrivate::accessibleRole() const
+QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const
{
Q_Q(const QQuickItem);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false));
- if (accessibleAttached)
- return accessibleAttached->role();
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false);
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+ return role;
+}
+QAccessible::Role QQuickItemPrivate::accessibleRole() const
+{
return QAccessible::NoRole;
}
#endif
@@ -9758,7 +10043,7 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
QObjectWrapper::markObjects(that, markStack);
}
-quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
+quint64 QQuickItemPrivate::_q_createJSWrapper(QQmlV4ExecutionEnginePtr engine)
{
return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
@@ -9802,6 +10087,9 @@ QPointF QQuickItem::mapToGlobal(qreal x, qreal y) const
QPointF QQuickItem::mapFromGlobal(qreal x, qreal y) const
{ return mapFromGlobal(QPointF(x, y)); }
+//! \internal
+QQuickItemChangeListener::~QQuickItemChangeListener() = default;
+
QT_END_NAMESPACE
#include <moc_qquickitem.cpp>
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 6a1f8db8c8..6b54b1af24 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -45,7 +45,6 @@ private:
class QCursor;
class QQuickItemLayer;
-class QQmlV4Function;
class QQuickState;
class QQuickAnchorLine;
class QQuickTransition;
@@ -102,6 +101,8 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL)
Q_PROPERTY(bool activeFocusOnTab READ activeFocusOnTab WRITE setActiveFocusOnTab NOTIFY activeFocusOnTabChanged FINAL REVISION(2, 1))
+ Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged REVISION(6, 7))
+
Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged)
@@ -120,7 +121,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_CLASSINFO("DefaultProperty", "data")
Q_CLASSINFO("ParentProperty", "parent")
- Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QV4::ExecutionEngine*)")
+ Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QQmlV4ExecutionEnginePtr)")
QML_NAMED_ELEMENT(Item)
QML_ADDED_IN_VERSION(2, 0)
@@ -270,6 +271,9 @@ public:
bool isFocusScope() const;
QQuickItem *scopedFocusItem() const;
+ Qt::FocusPolicy focusPolicy() const;
+ void setFocusPolicy(Qt::FocusPolicy policy);
+
bool isAncestorOf(const QQuickItem *child) const;
Qt::MouseButtons acceptedMouseButtons() const;
@@ -318,7 +322,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use typed overload or mapRectFromItem")
- void mapFromItem(QQmlV4Function*) const;
+ void mapFromItem(QQmlV4FunctionPtr) const;
#endif
Q_INVOKABLE QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const;
// overloads mainly exist for QML
@@ -328,7 +332,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use typed overload or mapRectToItem")
- void mapToItem(QQmlV4Function*) const;
+ void mapToItem(QQmlV4FunctionPtr) const;
#endif
Q_INVOKABLE QPointF mapToItem(const QQuickItem *item, const QPointF &point) const;
// overloads mainly exist for QML
@@ -338,7 +342,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use the typed overload")
- Q_REVISION(2, 7) void mapFromGlobal(QQmlV4Function*) const;
+ Q_REVISION(2, 7) void mapFromGlobal(QQmlV4FunctionPtr) const;
#endif
Q_REVISION(2, 7) Q_INVOKABLE QPointF mapFromGlobal(qreal x, qreal y) const;
// overload mainly exists for QML
@@ -346,7 +350,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 5)
QT_DEPRECATED_VERSION_X_6_5("Use the typed overload")
- Q_REVISION(2, 7) void mapToGlobal(QQmlV4Function*) const;
+ Q_REVISION(2, 7) void mapToGlobal(QQmlV4FunctionPtr) const;
#endif
Q_REVISION(2, 7) Q_INVOKABLE QPointF mapToGlobal(qreal x, qreal y) const;
// overload only exist for QML
@@ -383,6 +387,7 @@ Q_SIGNALS:
void stateChanged(const QString &);
void focusChanged(bool);
void activeFocusChanged(bool);
+ Q_REVISION(6, 7) void focusPolicyChanged(Qt::FocusPolicy);
Q_REVISION(2, 1) void activeFocusOnTabChanged(bool);
void parentChanged(QQuickItem *);
void transformOriginChanged(TransformOrigin);
@@ -465,7 +470,7 @@ protected:
private:
Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *))
- Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *))
+ Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr))
friend class QQuickWindowPrivate;
friend class QQuickDeliveryAgentPrivate;
@@ -473,6 +478,10 @@ private:
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
friend class QQuickAnchorChanges;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_QUICK_EXPORT QDebug operator<<(QDebug debug, QQuickItem *item);
+#endif
+
Q_DISABLE_COPY(QQuickItem)
Q_DECLARE_PRIVATE(QQuickItem)
};
@@ -504,7 +513,4 @@ QDebug Q_QUICK_EXPORT operator<<(QDebug debug,
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickItem)
-QML_DECLARE_TYPE(QQuickTransform)
-
#endif // QQUICKITEM_H
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 8fe7686a62..8c99d0656c 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -42,9 +42,15 @@
#include <QtCore/qlist.h>
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qpointer.h>
+
+#include <QtGui/private/qlayoutpolicy_p.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcHandlerParent)
+Q_DECLARE_LOGGING_CATEGORY(lcVP)
+
class QNetworkReply;
class QQuickItemKeyFilter;
class QQuickLayoutMirroringAttached;
@@ -100,21 +106,21 @@ public:
#if QT_CONFIG(quick_shadereffect)
-class Q_QUICK_PRIVATE_EXPORT QQuickItemLayer : public QObject, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickItemLayer : public QObject, public QQuickItemChangeListener
{
Q_OBJECT
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(QSize textureSize READ size WRITE setSize NOTIFY sizeChanged)
- Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged)
- Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
- Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged)
- Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged REVISION(6, 5))
- Q_PROPERTY(QQuickShaderEffectSource::WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
- Q_PROPERTY(QQuickShaderEffectSource::Format format READ format WRITE setFormat NOTIFY formatChanged)
- Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged)
- Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged)
- Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged)
- Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(QSize textureSize READ size WRITE setSize NOTIFY sizeChanged FINAL)
+ Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged FINAL)
+ Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged FINAL)
+ Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged FINAL)
+ Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged REVISION(6, 5) FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::Format format READ format WRITE setFormat NOTIFY formatChanged FINAL)
+ Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged FINAL)
+ Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged FINAL)
+ Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged FINAL)
+ Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -217,7 +223,7 @@ private:
#endif
-class Q_QUICK_PRIVATE_EXPORT QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickItemPrivate
: public QObjectPrivate
, public QQuickPaletteProviderPrivateBase<QQuickItem, QQuickItemPrivate>
{
@@ -265,21 +271,23 @@ public:
static qsizetype data_count(QQmlListProperty<QObject> *);
static QObject *data_at(QQmlListProperty<QObject> *, qsizetype);
static void data_clear(QQmlListProperty<QObject> *);
+ static void data_removeLast(QQmlListProperty<QObject> *);
// resources property
static QObject *resources_at(QQmlListProperty<QObject> *, qsizetype);
static void resources_append(QQmlListProperty<QObject> *, QObject *);
static qsizetype resources_count(QQmlListProperty<QObject> *);
static void resources_clear(QQmlListProperty<QObject> *);
+ static void resources_removeLast(QQmlListProperty<QObject> *);
// children property
static void children_append(QQmlListProperty<QQuickItem> *, QQuickItem *);
static qsizetype children_count(QQmlListProperty<QQuickItem> *);
static QQuickItem *children_at(QQmlListProperty<QQuickItem> *, qsizetype);
static void children_clear(QQmlListProperty<QQuickItem> *);
+ static void children_removeLast(QQmlListProperty<QQuickItem> *);
// visibleChildren property
- static void visibleChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *o);
static qsizetype visibleChildren_count(QQmlListProperty<QQuickItem> *prop);
static QQuickItem *visibleChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index);
@@ -290,7 +298,7 @@ public:
static void transform_clear(QQmlListProperty<QQuickTransform> *list);
void _q_resourceObjectDeleted(QObject *);
- quint64 _q_createJSWrapper(QV4::ExecutionEngine *engine);
+ quint64 _q_createJSWrapper(QQmlV4ExecutionEnginePtr engine);
enum ChangeType {
Geometry = 0x01,
@@ -338,7 +346,7 @@ public:
#endif // QT_NO_DEBUG_STREAM
};
- // call QQuickItemChangeListener PMF
+ // call QQuickItemChangeListener
template <typename Fn, typename ...Args>
void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function, Args &&...args)
{
@@ -347,20 +355,12 @@ public:
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & changeTypes)
- (change.listener->*function)(args...);
- }
- }
- // call functor
- template <typename Fn>
- void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function) {
- if (changeListeners.isEmpty())
- return;
-
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & changeTypes)
- function(change);
+ if (change.types & changeTypes) {
+ if constexpr (std::is_member_function_pointer_v<Fn>)
+ (change.listener->*function)(args...);
+ else
+ function(change, args...);
+ }
}
}
@@ -488,6 +488,7 @@ public:
// focus chain and prevents tabbing outside.
quint32 isTabFence:1;
quint32 replayingPressEvent:1;
+ // Bit 40
quint32 touchEnabled:1;
quint32 hasCursorHandler:1;
// set true when this item does not expect events via a subscene delivery agent; false otherwise
@@ -496,6 +497,9 @@ public:
// (e.g. when parent has ItemIsViewport and child has ItemObservesViewport)
quint32 subtreeTransformChangedEnabled:1;
quint32 inDestructor:1; // has entered ~QQuickItem
+ quint32 focusReason:4;
+ quint32 focusPolicy:4;
+ // Bit 53
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -561,16 +565,20 @@ public:
QPointer<QQuickItem> subFocusItem;
void updateSubFocusItem(QQuickItem *scope, bool focus);
+ bool setFocusIfNeeded(QEvent::Type);
+ Qt::FocusReason lastFocusChangeReason() const;
+ virtual bool setLastFocusChangeReason(Qt::FocusReason reason);
+
QTransform windowToItemTransform() const;
QTransform itemToWindowTransform() const;
- void itemToParentTransform(QTransform &) const;
+ void itemToParentTransform(QTransform *) const;
QTransform globalToWindowTransform() const;
QTransform windowToGlobalTransform() const;
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);
@@ -609,7 +617,10 @@ public:
virtual void implicitHeightChanged();
#if QT_CONFIG(accessibility)
+ QAccessible::Role effectiveAccessibleRole() const;
+private:
virtual QAccessible::Role accessibleRole() const;
+public:
#endif
void setImplicitAntialiasing(bool antialiasing);
@@ -645,6 +656,8 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ void deliverPointerEvent(QEvent *);
+
bool anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const;
virtual bool handlePointerEvent(QPointerEvent *, bool avoidGrabbers = false);
@@ -687,6 +700,8 @@ public:
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &);
+ void enableSubtreeChangeNotificationsForParentHierachy();
+
virtual void mirrorChange() {}
void setHasCursorInChild(bool hasCursor);
@@ -698,6 +713,10 @@ public:
virtual void updatePolish() { }
virtual void dumpItemTree(int indent) const;
+
+ QLayoutPolicy sizePolicy() const;
+ void setSizePolicy(const QLayoutPolicy::Policy &horizontalPolicy, const QLayoutPolicy::Policy &verticalPolicy);
+ QLayoutPolicy szPolicy;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItemPrivate::ExtraDataTags)
@@ -720,7 +739,7 @@ public:
virtual void inputMethodEvent(QInputMethodEvent *event, bool post);
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
#endif
- virtual void shortcutOverride(QKeyEvent *event);
+ virtual void shortcutOverrideEvent(QKeyEvent *event);
virtual void componentComplete();
bool m_processPost;
@@ -750,18 +769,18 @@ public:
bool backtabSet : 1;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickKeyNavigationAttached : public QObject, public QQuickItemKeyFilter
+class Q_QUICK_EXPORT QQuickKeyNavigationAttached : public QObject, public QQuickItemKeyFilter
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickKeyNavigationAttached)
- Q_PROPERTY(QQuickItem *left READ left WRITE setLeft NOTIFY leftChanged)
- Q_PROPERTY(QQuickItem *right READ right WRITE setRight NOTIFY rightChanged)
- Q_PROPERTY(QQuickItem *up READ up WRITE setUp NOTIFY upChanged)
- Q_PROPERTY(QQuickItem *down READ down WRITE setDown NOTIFY downChanged)
- Q_PROPERTY(QQuickItem *tab READ tab WRITE setTab NOTIFY tabChanged)
- Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged)
- Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+ Q_PROPERTY(QQuickItem *left READ left WRITE setLeft NOTIFY leftChanged FINAL)
+ Q_PROPERTY(QQuickItem *right READ right WRITE setRight NOTIFY rightChanged FINAL)
+ Q_PROPERTY(QQuickItem *up READ up WRITE setUp NOTIFY upChanged FINAL)
+ Q_PROPERTY(QQuickItem *down READ down WRITE setDown NOTIFY downChanged FINAL)
+ Q_PROPERTY(QQuickItem *tab READ tab WRITE setTab NOTIFY tabChanged FINAL)
+ Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged FINAL)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged FINAL)
QML_NAMED_ELEMENT(KeyNavigation)
QML_ADDED_IN_VERSION(2, 0)
@@ -810,8 +829,8 @@ private:
class QQuickLayoutMirroringAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged)
- Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged FINAL)
QML_NAMED_ELEMENT(LayoutMirroring)
QML_ADDED_IN_VERSION(2, 0)
@@ -840,7 +859,7 @@ private:
class QQuickEnterKeyAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(Qt::EnterKeyType type READ type WRITE setType NOTIFY typeChanged)
+ Q_PROPERTY(Qt::EnterKeyType type READ type WRITE setType NOTIFY typeChanged FINAL)
QML_NAMED_ELEMENT(EnterKey)
QML_UNCREATABLE("EnterKey is only available via attached properties")
@@ -883,14 +902,14 @@ public:
QQuickKeyEvent theKeyEvent;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
+class Q_QUICK_EXPORT QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickKeysAttached)
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
- Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo)
- Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo FINAL)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged FINAL)
QML_NAMED_ELEMENT(Keys)
QML_ADDED_IN_VERSION(2, 0)
@@ -978,7 +997,7 @@ private:
void inputMethodEvent(QInputMethodEvent *, bool post) override;
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
#endif
- void shortcutOverride(QKeyEvent *event) override;
+ void shortcutOverrideEvent(QKeyEvent *event) override;
static QByteArray keyToSignal(int key);
bool isConnected(const char *signalName) const;
@@ -1049,12 +1068,4 @@ Q_DECLARE_TYPEINFO(QQuickItemPrivate::ChangeListener, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
-#if QT_CONFIG(quick_shadereffect)
-QML_DECLARE_TYPE(QQuickItemLayer)
-#endif
-QML_DECLARE_TYPE(QQuickKeysAttached)
-QML_DECLARE_TYPE(QQuickKeyNavigationAttached)
-QML_DECLARE_TYPE(QQuickLayoutMirroringAttached)
-QML_DECLARE_TYPE(QQuickEnterKeyAttached)
-
#endif // QQUICKITEM_P_H
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 51fd2a3588..11b2642748 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -770,13 +770,13 @@ void QQuickPathAnimation::setOrientationExitDuration(int duration)
qreal QQuickPathAnimation::endRotation() const
{
Q_D(const QQuickPathAnimation);
- return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
+ return d->endRotation.isValid() ? d->endRotation.value() : qreal(0);
}
void QQuickPathAnimation::setEndRotation(qreal rotation)
{
Q_D(QQuickPathAnimation);
- if (!d->endRotation.isNull && d->endRotation == rotation)
+ if (d->endRotation.isValid() && d->endRotation == rotation)
return;
d->endRotation = rotation;
@@ -946,11 +946,11 @@ void QQuickPathAnimationUpdater::setValue(qreal v)
//shortest distance to correct orientation
qreal diff = angle - startRotation;
while (diff > 180.0) {
- startRotation.value += 360.0;
+ startRotation = startRotation.value() + 360.0;
diff -= 360.0;
}
while (diff < -180.0) {
- startRotation.value -= 360.0;
+ startRotation = startRotation.value() - 360.0;
diff += 360.0;
}
}
@@ -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/qquickitemanimation_p.h b/src/quick/items/qquickitemanimation_p.h
index 25fc83883f..e2ea93dd60 100644
--- a/src/quick/items/qquickitemanimation_p.h
+++ b/src/quick/items/qquickitemanimation_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
class QQuickParentAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickParentAnimation : public QQuickAnimationGroup
+class Q_QUICK_EXPORT QQuickParentAnimation : public QQuickAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickParentAnimation)
@@ -58,7 +58,7 @@ protected:
};
class QQuickAnchorAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickAnchorAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnchorAnimation)
@@ -95,7 +95,7 @@ protected:
class QQuickItem;
class QQuickPath;
class QQuickPathAnimationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPathAnimation : public QQuickAbstractAnimation
+class Q_QUICK_EXPORT QQuickPathAnimation : public QQuickAbstractAnimation
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickPathAnimation)
@@ -174,10 +174,4 @@ Q_SIGNALS:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickParentAnimation)
-QML_DECLARE_TYPE(QQuickAnchorAnimation)
-#if QT_CONFIG(quick_path)
-QML_DECLARE_TYPE(QQuickPathAnimation)
-#endif
-
#endif // QQUICKITEMANIMATION_H
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 57bbf77f70..aed00218e5 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -15,7 +15,7 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
+#include <QtQuick/private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -81,10 +81,10 @@ private:
#define QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING
-class QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickItemChangeListener
{
public:
- virtual ~QQuickItemChangeListener() {}
+ virtual ~QQuickItemChangeListener();
virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index bcd1afc19d..e2164129be 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -14,11 +14,13 @@
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlInfo>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qquickitem_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
const QEvent::Type Event_Grab_Completed = static_cast<QEvent::Type>(QEvent::User + 1);
@@ -30,6 +32,7 @@ public:
: cacheEntry(nullptr)
, qmlEngine(nullptr)
, texture(nullptr)
+ , devicePixelRatio(1.0)
{
}
@@ -63,6 +66,7 @@ public:
QSGLayer *texture;
QSizeF itemSize;
QSize textureSize;
+ qreal devicePixelRatio;
};
/*!
@@ -235,7 +239,9 @@ void QQuickItemGrabResult::setup()
}
QSGRenderContext *rc = QQuickWindowPrivate::get(d->window.data())->context;
+ d->devicePixelRatio = d->window->effectiveDevicePixelRatio();
d->texture = rc->sceneGraphContext()->createLayer(rc);
+ d->texture->setDevicePixelRatio(d->devicePixelRatio);
d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode());
d->itemSize = QSizeF(d->item->width(), d->item->height());
}
@@ -248,11 +254,13 @@ void QQuickItemGrabResult::render()
d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->minimumFBOSize();
- d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()),
- qMax(minSize.height(), d->textureSize.height())));
+ const QSize effectiveTextureSize = d->textureSize * d->devicePixelRatio;
+ d->texture->setSize(QSize(qMax(minSize.width(), effectiveTextureSize.width()),
+ qMax(minSize.height(), effectiveTextureSize.height())));
d->texture->scheduleUpdate();
d->texture->updateTexture();
- d->image = d->texture->toImage();
+ d->image = d->texture->toImage();
+ d->image.setDevicePixelRatio(d->devicePixelRatio);
delete d->texture;
d->texture = nullptr;
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 9cff9c4c4f..d08c43ed22 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -85,30 +85,15 @@
#include "handlers/qquicktaphandler_p.h"
#include "handlers/qquickwheelhandler_p.h"
-QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcTransient)
-QT_END_NAMESPACE
-
#include "moc_qquickitemsmodule_p.cpp"
static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent)
{
- // When setting a parent (especially during dynamic object creation) in QML,
- // also try to set up the analogous item/window relationship.
if (QQuickItem *parentItem = qmlobject_cast<QQuickItem *>(parent)) {
- QQuickItem *item = qmlobject_cast<QQuickItem *>(obj);
- if (item) {
+ if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
// An Item has another Item
item->setParentItem(parentItem);
return QQmlPrivate::Parented;
- } else if (parentItem->window()) {
- QQuickWindow *win = qmlobject_cast<QQuickWindow *>(obj);
- if (win) {
- // A Window inside an Item should be transient for that item's window
- qCDebug(lcTransient) << win << "is transient for" << parentItem->window();
- win->setTransientParent(parentItem->window());
- return QQmlPrivate::Parented;
- }
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
QQuickItemPrivate::get(parentItem)->addPointerHandler(handler);
handler->setParent(parent);
@@ -116,13 +101,7 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
}
return QQmlPrivate::IncompatibleObject;
} else if (QQuickWindow *parentWindow = qmlobject_cast<QQuickWindow *>(parent)) {
- QQuickWindow *win = qmlobject_cast<QQuickWindow *>(obj);
- if (win) {
- // A Window inside a Window should be transient for it
- qCDebug(lcTransient) << win << "is transient for" << parentWindow;
- win->setTransientParent(parentWindow);
- return QQmlPrivate::Parented;
- } else if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
+ if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
// The parent of an Item inside a Window is actually the implicit content Item
item->setParentItem(parentWindow->contentItem());
return QQmlPrivate::Parented;
@@ -160,4 +139,139 @@ void QQuickItemsModule::defineModule()
qt_quickitems_defineModule();
}
+/*!
+ \qmltype PointerEvent
+ \instantiates QPointerEvent
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointerEvent.
+
+ PointerEvent is the QML name of the QPointerEvent class.
+*/
+
+/*!
+ \qmltype PointerDevice
+ \instantiates QPointingDevice
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDevice.
+
+ PointerDevice is the QML name of the QPointingDevice class.
+ It has the same properties and enums as \l QPointingDevice.
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::deviceType
+
+ This property tells the type of device that generated a PointerEvent.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QInputDevice::DeviceType, PointerDeviceHandler::acceptedDevices
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::pointerType
+
+ This property tells what is interacting with the PointerDevice.
+
+ There is some redundancy between this property and \l deviceType.
+ For example, if a touchscreen is used, then \c deviceType is
+ \c TouchScreen and \c pointerType is \c Finger. But on a graphics
+ tablet, it's often possible for both ends of the stylus to be used,
+ and programs need to distinguish them.
+ \l PointerDeviceHandler::acceptedDevices and
+ \l PointerDeviceHandler::acceptedPointerTypes can be used in combination
+ to filter the subset of events that a particular handler should react to.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen.
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QPointingDevice::PointerType, PointerDeviceHandler::acceptedPointerTypes
+*/
+
+/*!
+ \qmlproperty int PointerDevice::maximumPoints
+
+ This property tells the maximum number of simultaneous touch points
+ (fingers) that can be detected.
+*/
+
+/*!
+ \qmlproperty int PointerDevice::buttonCount
+
+ This property tells the maximum number of on-device buttons that can be
+ detected.
+*/
+
+/*!
+ \qmltype pointingDeviceUniqueId
+ \instantiates QPointingDeviceUniqueId
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDeviceUniqueId.
+
+ pointingDeviceUniqueId is the QML name of the QPointingDeviceUniqueId class.
+*/
+
+/*!
+ \qmlproperty qint64 pointingDeviceUniqueId::numericId
+
+ This property gives the numeric ID of the \l PointerDevice, if available;
+ otherwise it is \c -1.
+*/
+
+/*!
+ \qmlproperty pointingDeviceUniqueId PointerDevice::uniqueId
+
+ This property may provide a unique ID for the device, if available. For
+ example, a graphics tablet stylus device may have a unique serial number.
+
+ \sa eventPoint, QEventPoint::uniqueId()
+*/
+
+/*!
+ \qmlsignal PointerDevice::grabChanged(QtObject grabber, enumeration transition, PointerEvent event, eventPoint point)
+
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object, which may be either an \l Item or an
+ \l {Qt Quick Input Handlers}{Input Handler}.
+
+ Valid values for \a transition are:
+
+ \value PointerDevice.GrabExclusive
+ The \a grabber has taken primary responsibility for handling the \a point.
+ \value PointerDevice.UngrabExclusive
+ The \a grabber has given up its previous exclusive grab.
+ \value PointerDevice.CancelGrabExclusive
+ The exclusive grab of \a grabber has been taken over or cancelled.
+ \value PointerDevice.GrabPassive
+ The \a grabber has acquired a passive grab, to monitor the \a point.
+ \value PointerDevice.UngrabPassive
+ The \a grabber has given up its previous passive grab.
+ \value PointerDevice.CancelGrabPassive
+ The previous passive grab has terminated abnormally.
+
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted.
+
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(),
+ QPointerEvent::removePassiveGrabber(), PointerHandler::grabChanged()
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index a149f8d098..1518a484d3 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -160,8 +160,8 @@ void QQuickItemView::setModel(const QVariant &m)
disconnect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*)));
disconnect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*)));
if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
- disconnect(delegateModel, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *)));
- disconnect(delegateModel, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *)));
+ disconnect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ disconnect(delegateModel, SIGNAL(itemReused(int,QObject*)), this, SLOT(onItemReused(int,QObject*)));
}
}
@@ -199,8 +199,8 @@ void QQuickItemView::setModel(const QVariant &m)
connect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*)));
connect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*)));
if (QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
- connect(delegateModel, SIGNAL(itemPooled(int, QObject *)), this, SLOT(onItemPooled(int, QObject *)));
- connect(delegateModel, SIGNAL(itemReused(int, QObject *)), this, SLOT(onItemReused(int, QObject *)));
+ connect(delegateModel, SIGNAL(itemPooled(int,QObject*)), this, SLOT(onItemPooled(int,QObject*)));
+ connect(delegateModel, SIGNAL(itemReused(int,QObject*)), this, SLOT(onItemReused(int,QObject*)));
}
if (isComponentComplete()) {
d->updateSectionCriteria();
@@ -211,10 +211,12 @@ void QQuickItemView::setModel(const QVariant &m)
setCurrentIndex(d->model->count() > 0 ? 0 : -1);
d->updateViewport();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition) {
d->transitioner->setPopulateTransitionEnabled(true);
d->forceLayoutPolish();
}
+#endif
}
connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)),
@@ -684,6 +686,7 @@ void QQuickItemView::setReuseItems(bool reuse)
emit reuseItemsChanged();
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *QQuickItemView::populateTransition() const
{
Q_D(const QQuickItemView);
@@ -811,6 +814,7 @@ void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
emit displacedTransitionChanged();
}
}
+#endif
void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
{
@@ -1082,8 +1086,7 @@ qreal QQuickItemViewPrivate::calculatedMaxExtent() const
void QQuickItemViewPrivate::applyDelegateChange()
{
releaseVisibleItems(QQmlDelegateModel::NotReusable);
- releaseItem(currentItem, QQmlDelegateModel::NotReusable);
- currentItem = nullptr;
+ releaseCurrentItem(QQmlDelegateModel::NotReusable);
updateSectionCriteria();
refill();
moveReason = QQuickItemViewPrivate::SetIndex;
@@ -1148,11 +1151,13 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry
// don't allow item movement transitions to trigger a re-layout and
// start new transitions
bool prevInLayout = inLayout;
+#if QT_CONFIG(quick_viewtransitions)
if (!inLayout) {
FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : nullptr;
if (actualItem && actualItem->transitionRunning())
inLayout = true;
}
+#endif
updateHighlight();
inLayout = prevInLayout;
}
@@ -1165,17 +1170,20 @@ void QQuickItemView::destroyRemoved()
{
Q_D(QQuickItemView);
+#if QT_CONFIG(quick_viewtransitions)
bool hasRemoveTransition = false;
bool hasRemoveTransitionAsTarget = false;
if (d->transitioner) {
hasRemoveTransition = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false);
hasRemoveTransitionAsTarget = d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true);
}
+#endif
for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
it != d->visibleItems.end();) {
FxViewItem *item = *it;
if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) {
+#if QT_CONFIG(quick_viewtransitions)
if (hasRemoveTransitionAsTarget) {
// don't remove from visibleItems until next layout()
d->runDelayedRemoveTransition = true;
@@ -1184,9 +1192,12 @@ void QQuickItemView::destroyRemoved()
} else {
if (hasRemoveTransition)
d->runDelayedRemoveTransition = true;
+#endif
d->releaseItem(item, d->reusableFlag);
it = d->visibleItems.erase(it);
+#if QT_CONFIG(quick_viewtransitions)
}
+#endif
} else {
++it;
}
@@ -1201,8 +1212,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
Q_D(QQuickItemView);
if (reset) {
cancelFlick();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
d->moveReason = QQuickItemViewPrivate::SetIndex;
d->regenerate();
if (d->highlight && d->currentItem) {
@@ -1212,8 +1225,10 @@ void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
}
d->moveReason = QQuickItemViewPrivate::Other;
emit countChanged();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner && d->transitioner->populateTransition)
d->forceLayoutPolish();
+#endif
} else {
if (d->inLayout) {
d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
@@ -1251,7 +1266,9 @@ void QQuickItemView::trackedPositionChanged()
return;
}
- if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
+ const bool needMoveToTrackHighlight = d->autoHighlight || d->highlightRange != NoHighlightRange;
+
+ if (d->moveReason == QQuickItemViewPrivate::SetIndex && needMoveToTrackHighlight) {
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
@@ -1449,8 +1466,10 @@ void QQuickItemView::componentComplete()
d->updateFooter();
d->updateViewport();
d->setPosition(d->contentStartOffset());
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
if (d->isValid()) {
d->refill();
@@ -1478,7 +1497,6 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
, displayMarginBeginning(0), displayMarginEnd(0)
, layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
- , moveReason(Other)
, visibleIndex(0)
, currentIndex(-1), currentItem(nullptr)
, trackedItem(nullptr), requestedIndex(-1)
@@ -1487,7 +1505,9 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, highlightRangeStart(0), highlightRangeEnd(0)
, highlightMoveDuration(150)
, headerComponent(nullptr), header(nullptr), footerComponent(nullptr), footer(nullptr)
+#if QT_CONFIG(quick_viewtransitions)
, transitioner(nullptr)
+#endif
, minExtent(0), maxExtent(0)
, ownModel(false), wrap(false)
, keyNavigationEnabled(true)
@@ -1495,7 +1515,10 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
, inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
, haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
, fillCacheBuffer(false), inRequest(false)
- , runDelayedRemoveTransition(false), delegateValidated(false), isClearing(false)
+#if QT_CONFIG(quick_viewtransitions)
+ , runDelayedRemoveTransition(false)
+#endif
+ , delegateValidated(false), isClearing(false)
{
bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
bufferPause.setLoopCount(1);
@@ -1504,9 +1527,11 @@ QQuickItemViewPrivate::QQuickItemViewPrivate()
QQuickItemViewPrivate::~QQuickItemViewPrivate()
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setChangeListener(nullptr);
delete transitioner;
+#endif
}
bool QQuickItemViewPrivate::isValid() const
@@ -1628,8 +1653,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex)
if (currentItem) {
if (currentItem->attached)
currentItem->attached->setIsCurrentItem(false);
- releaseItem(currentItem, reusableFlag);
- currentItem = nullptr;
+ releaseCurrentItem(reusableFlag);
currentIndex = modelIndex;
emit q->currentIndexChanged();
emit q->currentItemChanged();
@@ -1682,16 +1706,17 @@ void QQuickItemViewPrivate::clear(bool onDestruction)
releaseVisibleItems(QQmlInstanceModel::NotReusable);
visibleIndex = 0;
+#if QT_CONFIG(quick_viewtransitions)
for (FxViewItem *item : std::as_const(releasePendingTransition)) {
item->releaseAfterTransition = false;
releaseItem(item, QQmlInstanceModel::NotReusable);
}
releasePendingTransition.clear();
+#endif
- auto oldCurrentItem = currentItem;
- releaseItem(currentItem, QQmlDelegateModel::NotReusable);
- currentItem = nullptr;
- if (oldCurrentItem)
+ const bool hadCurrentItem = currentItem != nullptr;
+ releaseCurrentItem(QQmlDelegateModel::NotReusable);
+ if (hadCurrentItem)
emit q->currentItemChanged();
createHighlight(onDestruction);
trackedItem = nullptr;
@@ -1736,6 +1761,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
Q_Q(QQuickItemView);
if (!model || !model->isValid() || !q->isComponentComplete())
return;
+ if (q->size().isEmpty() && visibleItems.isEmpty())
+ return;
if (!model->count()) {
updateHeader();
updateFooter();
@@ -1759,7 +1786,6 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
qreal fillTo = to;
bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
- bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
if (added) {
@@ -1775,6 +1801,8 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
}
}
+ bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
+
if (added || removed) {
markExtentsDirty();
updateBeginningEnd();
@@ -1836,12 +1864,15 @@ void QQuickItemViewPrivate::layout()
clear();
setPosition(contentStartOffset());
updateViewport();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
transitioner->setPopulateTransitionEnabled(false);
+#endif
inLayout = false;
return;
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition && transitioner
&& transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
// assume that any items moving now are moving due to the remove - if they schedule
@@ -1849,6 +1880,7 @@ void QQuickItemViewPrivate::layout()
for (int i=0; i<visibleItems.size(); i++)
visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
}
+#endif
ChangeResult insertionPosChanges;
ChangeResult removalPosChanges;
@@ -1862,6 +1894,7 @@ void QQuickItemViewPrivate::layout()
}
forceLayout = false;
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
// Give the view one more chance to refill itself,
// in case its size is changed such that more delegates become visible after component completed
@@ -1871,12 +1904,15 @@ void QQuickItemViewPrivate::layout()
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
}
}
+#endif
updateSections();
layoutVisibleItems();
storeFirstVisibleItemPosition();
+#if QT_CONFIG(quick_viewtransitions)
int lastIndexInView = findLastIndexInView();
+#endif
refill();
markExtentsDirty();
updateHighlight();
@@ -1891,6 +1927,7 @@ void QQuickItemViewPrivate::layout()
updateViewport();
updateUnrequestedPositions();
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner) {
// items added in the last refill() may need to be transitioned in - e.g. a remove
// causes items to slide up into view
@@ -1931,11 +1968,14 @@ void QQuickItemViewPrivate::layout()
transitioner->setPopulateTransitionEnabled(false);
transitioner->resetTargetLists();
}
+#endif
if (!currentItem)
updateCurrent(currentIndex);
+#if QT_CONFIG(quick_viewtransitions)
runDelayedRemoveTransition = false;
+#endif
inLayout = false;
}
@@ -1991,6 +2031,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex - r.index);
}
}
+#if QT_CONFIG(quick_viewtransitions)
if (runDelayedRemoveTransition) {
QQmlChangeSet::Change removal;
for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
@@ -2004,6 +2045,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
*totalRemovalResult += removalResult;
if (!removals.isEmpty()) {
updateVisibleIndex();
@@ -2042,6 +2084,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
item->attached->emitAdd();
}
+#if QT_CONFIG(quick_viewtransitions)
// for each item that was moved directly into the view as a result of a move(),
// find the index it was moved from in order to set its initial position, so that we
// can transition it from this "original" position to its new position in the view
@@ -2057,13 +2100,16 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
}
}
}
+#endif
// reposition visibleItems.first() correctly so that the content y doesn't jump
if (removedCount != prevVisibleItemsCount)
repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstItemInView, &insertionResult, &removalResult);
// Whatever removed/moved items remain are no longer visible items.
+#if QT_CONFIG(quick_viewtransitions)
prepareRemoveTransitions(&currentChanges.removedItems);
+#endif
for (auto it = currentChanges.removedItems.begin();
it != currentChanges.removedItems.end(); ++it) {
releaseItem(it.value(), reusableFlag);
@@ -2074,10 +2120,9 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
if (currentChanges.currentRemoved && currentItem) {
if (currentItem->item && currentItem->attached)
currentItem->attached->setIsCurrentItem(false);
- auto oldCurrentItem = currentItem;
- releaseItem(currentItem, reusableFlag);
- currentItem = nullptr;
- if (oldCurrentItem)
+ const bool hadCurrentItem = currentItem != nullptr;
+ releaseCurrentItem(reusableFlag);
+ if (hadCurrentItem)
emit q->currentItemChanged();
}
if (!currentIndexCleared)
@@ -2120,10 +2165,12 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
} else if (item->index >= removal.index + removal.count) {
// after removed items
item->index -= removal.count;
+#if QT_CONFIG(quick_viewtransitions)
if (removal.isMove())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
else
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
+#endif
++it;
} else {
// removed item
@@ -2157,7 +2204,9 @@ void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Ch
}
if (removal.isMove()) {
currentChanges.removedItems.replace(removal.moveKey(item->index), item);
+#if QT_CONFIG(quick_viewtransitions)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
+#endif
} else {
// track item so it is released later
currentChanges.removedItems.insert(QQmlChangeSet::MoveKey(), item);
@@ -2211,6 +2260,7 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
}
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickItemViewPrivate::createTransitioner()
{
if (!transitioner) {
@@ -2286,6 +2336,7 @@ void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitiona
}
}
}
+#endif
/*
This may return 0 if the item is being created asynchronously.
@@ -2299,6 +2350,7 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous)
return nullptr;
+#if QT_CONFIG(quick_viewtransitions)
for (int i=0; i<releasePendingTransition.size(); i++) {
if (releasePendingTransition.at(i)->index == modelIndex
&& !releasePendingTransition.at(i)->isPendingRemoval()) {
@@ -2306,6 +2358,7 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::Inc
return releasePendingTransition.takeAt(i);
}
}
+#endif
inRequest = true;
diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h
index f74ca5177b..cecbf094cb 100644
--- a/src/quick/items/qquickitemview_p.h
+++ b/src/quick/items/qquickitemview_p.h
@@ -31,7 +31,7 @@ class QQmlChangeSet;
class QQuickItemViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
+class Q_QUICK_EXPORT QQuickItemView : public QQuickFlickable
{
Q_OBJECT
@@ -57,6 +57,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
Q_PROPERTY(QQmlComponent *footer READ footer WRITE setFooter NOTIFY footerChanged)
Q_PROPERTY(QQuickItem *footerItem READ footerItem NOTIFY footerItemChanged)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *populate READ populateTransition WRITE setPopulateTransition NOTIFY populateTransitionChanged)
Q_PROPERTY(QQuickTransition *add READ addTransition WRITE setAddTransition NOTIFY addTransitionChanged)
Q_PROPERTY(QQuickTransition *addDisplaced READ addDisplacedTransition WRITE setAddDisplacedTransition NOTIFY addDisplacedTransitionChanged)
@@ -65,6 +66,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickItemView : public QQuickFlickable
Q_PROPERTY(QQuickTransition *remove READ removeTransition WRITE setRemoveTransition NOTIFY removeTransitionChanged)
Q_PROPERTY(QQuickTransition *removeDisplaced READ removeDisplacedTransition WRITE setRemoveDisplacedTransition NOTIFY removeDisplacedTransitionChanged)
Q_PROPERTY(QQuickTransition *displaced READ displacedTransition WRITE setDisplacedTransition NOTIFY displacedTransitionChanged)
+#endif
Q_PROPERTY(QQmlComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
Q_PROPERTY(QQuickItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
@@ -144,6 +146,7 @@ public:
void setHeader(QQmlComponent *);
QQuickItem *headerItem() const;
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *populateTransition() const;
void setPopulateTransition(QQuickTransition *transition);
@@ -167,6 +170,7 @@ public:
QQuickTransition *displacedTransition() const;
void setDisplacedTransition(QQuickTransition *transition);
+#endif
QQmlComponent *highlight() const;
void setHighlight(QQmlComponent *);
@@ -233,6 +237,7 @@ Q_SIGNALS:
void headerItemChanged();
void footerItemChanged();
+#if QT_CONFIG(quick_viewtransitions)
void populateTransitionChanged();
void addTransitionChanged();
void addDisplacedTransitionChanged();
@@ -241,6 +246,7 @@ Q_SIGNALS:
void removeTransitionChanged();
void removeDisplacedTransitionChanged();
void displacedTransitionChanged();
+#endif
void highlightChanged();
void highlightItemChanged();
@@ -277,17 +283,17 @@ private:
};
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewAttached : public QObject
+class Q_QUICK_EXPORT QQuickItemViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickItemView *view READ view NOTIFY viewChanged)
- Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
- Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged)
+ Q_PROPERTY(QQuickItemView *view READ view NOTIFY viewChanged FINAL)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged FINAL)
+ Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged FINAL)
- Q_PROPERTY(QString section READ section NOTIFY sectionChanged)
- Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged)
- Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged)
+ Q_PROPERTY(QString section READ section NOTIFY sectionChanged FINAL)
+ Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged FINAL)
+ Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged FINAL)
public:
QQuickItemViewAttached(QObject *parent)
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index b978410bce..0179dc1fdd 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -21,12 +21,15 @@ QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemview_p.h"
#include "qquickitemviewfxitem_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
+#endif
#include "qquickflickable_p_p.h"
#include <QtQmlModels/private/qqmlobjectmodel_p.h>
#include <QtQmlModels/private/qqmldelegatemodel_p.h>
#include <QtQmlModels/private/qqmlchangeset_p.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -64,7 +67,12 @@ public:
};
-class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener
+class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate
+ : public QQuickFlickablePrivate
+#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemViewTransitionChangeListener
+#endif
+ , public QAnimationJobChangeListener
{
public:
Q_DECLARE_PUBLIC(QQuickItemView)
@@ -112,7 +120,6 @@ public:
};
enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
- enum MovementReason { Other, SetIndex, Mouse };
bool isValid() const;
qreal position() const;
@@ -138,6 +145,11 @@ public:
void mirrorChange() override;
FxViewItem *createItem(int modelIndex,QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested);
+ bool releaseCurrentItem(QQmlInstanceModel::ReusableFlag reusableFlag)
+ {
+ auto oldCurrentItem = std::exchange(currentItem, nullptr);
+ return releaseItem(oldCurrentItem, reusableFlag);
+ }
virtual bool releaseItem(FxViewItem *item, QQmlInstanceModel::ReusableFlag reusableFlag);
QQuickItem *createHighlightItem() const;
@@ -166,11 +178,13 @@ public:
void repositionFirstItem(FxViewItem *prevVisibleItemsFirst, qreal prevVisibleItemsFirstPos,
FxViewItem *prevFirstVisible, ChangeResult *insertionResult, ChangeResult *removalResult);
+#if QT_CONFIG(quick_viewtransitions)
void createTransitioner();
void prepareVisibleItemTransitions();
void prepareRemoveTransitions(QMultiHash<QQmlChangeSet::MoveKey, FxViewItem *> *removedItems);
bool prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds);
void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override;
+#endif
int findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector<QQmlChangeSet::Change> &changes) const;
@@ -187,7 +201,10 @@ public:
bool hasPendingChanges() const {
return currentChanges.hasPendingChanges()
|| bufferedChanges.hasPendingChanges()
- ||runDelayedRemoveTransition;
+#if QT_CONFIG(quick_viewtransitions)
+ ||runDelayedRemoveTransition
+#endif
+ ;
}
void refillOrLayout() {
@@ -224,8 +241,6 @@ public:
Qt::LayoutDirection layoutDirection;
QQuickItemView::VerticalLayoutDirection verticalLayoutDirection;
- MovementReason moveReason;
-
QList<FxViewItem *> visibleItems;
qreal firstVisibleItemPosition = 0;
void storeFirstVisibleItemPosition() {
@@ -266,8 +281,10 @@ public:
MovedItem(FxViewItem *i, QQmlChangeSet::MoveKey k)
: item(i), moveKey(k) {}
};
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner;
QVector<FxViewItem *> releasePendingTransition;
+#endif
mutable qreal minExtent;
mutable qreal maxExtent;
@@ -286,7 +303,9 @@ public:
bool highlightRangeEndValid : 1;
bool fillCacheBuffer : 1;
bool inRequest : 1;
+#if QT_CONFIG(quick_viewtransitions)
bool runDelayedRemoveTransition : 1;
+#endif
bool delegateValidated : 1;
bool isClearing : 1;
@@ -333,7 +352,9 @@ protected:
QList<FxViewItem *> *newItems, QList<MovedItem> *movingIntoView) = 0;
virtual bool needsRefillForAddedOrRemovedIndex(int) const { return false; }
+#if QT_CONFIG(quick_viewtransitions)
virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) = 0;
+#endif
virtual void initializeViewItem(FxViewItem *) {}
virtual void initializeCurrentItem() {}
diff --git a/src/quick/items/qquickitemviewfxitem.cpp b/src/quick/items/qquickitemviewfxitem.cpp
index 70e122ca60..d372fde0c8 100644
--- a/src/quick/items/qquickitemviewfxitem.cpp
+++ b/src/quick/items/qquickitemviewfxitem.cpp
@@ -10,7 +10,9 @@ QT_BEGIN_NAMESPACE
QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener* changeListener)
: item(item)
, changeListener(changeListener)
+#if QT_CONFIG(quick_viewtransitions)
, transitionableItem(nullptr)
+#endif
, ownItem(ownItem)
, releaseAfterTransition(false)
, trackGeom(false)
@@ -19,8 +21,10 @@ QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuic
QQuickItemViewFxItem::~QQuickItemViewFxItem()
{
+#if QT_CONFIG(quick_viewtransitions)
delete transitionableItem;
transitionableItem = nullptr;
+#endif
if (ownItem && item) {
trackGeometry(false);
@@ -31,28 +35,47 @@ QQuickItemViewFxItem::~QQuickItemViewFxItem()
qreal QQuickItemViewFxItem::itemX() const
{
- return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemX() :
+#endif
+ (item ? item->x() : 0);
}
qreal QQuickItemViewFxItem::itemY() const
{
- return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemY() :
+#endif
+ (item ? item->y() : 0);
}
void QQuickItemViewFxItem::moveTo(const QPointF &pos, bool immediate)
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitionableItem)
transitionableItem->moveTo(pos, immediate);
- else if (item)
+ else
+#else
+ Q_UNUSED(immediate)
+#endif
+ if (item)
item->setPosition(pos);
}
void QQuickItemViewFxItem::setVisible(bool visible)
{
- if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
+ if (!visible
+#if QT_CONFIG(quick_viewtransitions)
+ && transitionableItem && transitionableItem->transitionScheduledOrRunning()
+#endif
+ )
return;
- if (item)
+ if (item) {
QQuickItemPrivate::get(item)->setCulled(!visible);
+ QQuickItemPrivate::get(item)->isAccessible = visible;
+ }
}
void QQuickItemViewFxItem::trackGeometry(bool track)
@@ -87,6 +110,7 @@ void QQuickItemViewFxItem::setGeometry(const QRectF &geometry)
item->setSize(geometry.size());
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner::TransitionType QQuickItemViewFxItem::scheduledTransitionType() const
{
return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
@@ -126,6 +150,7 @@ void QQuickItemViewFxItem::startTransition(QQuickItemViewTransitioner *transitio
if (transitionableItem)
transitionableItem->startTransition(transitioner, index);
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemviewfxitem_p_p.h b/src/quick/items/qquickitemviewfxitem_p_p.h
index b453054a89..e6e10c4233 100644
--- a/src/quick/items/qquickitemviewfxitem_p_p.h
+++ b/src/quick/items/qquickitemviewfxitem_p_p.h
@@ -17,14 +17,18 @@
#include <QtQuick/private/qtquickglobal_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <private/qanimationjobutil_p.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(quick_itemview);
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewFxItem
+class Q_QUICK_EXPORT QQuickItemViewFxItem
{
public:
QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener *changeListener);
@@ -42,6 +46,7 @@ public:
QRectF geometry() const;
void setGeometry(const QRectF &geometry);
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
bool transitionScheduledOrRunning() const;
bool transitionRunning() const;
@@ -50,6 +55,7 @@ public:
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner);
+#endif
// these are positions and sizes along the current direction of scrolling/flicking
virtual qreal position() const = 0;
@@ -62,7 +68,9 @@ public:
SelfDeletable m_selfDeletable;
QPointer<QQuickItem> item;
QQuickItemChangeListener *changeListener;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitionableItem *transitionableItem;
+#endif
int index = -1;
bool ownItem : 1;
bool releaseAfterTransition : 1;
diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h
index 01e504a440..7a2d23c3cd 100644
--- a/src/quick/items/qquickitemviewtransition_p.h
+++ b/src/quick/items/qquickitemviewtransition_p.h
@@ -26,6 +26,8 @@ QT_REQUIRE_CONFIG(quick_viewtransitions);
#include <private/qquicktransition_p.h>
#include <private/qanimationjobutil_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickItem;
@@ -34,7 +36,7 @@ class QQuickItemViewTransitionableItem;
class QQuickItemViewTransitionJob;
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionChangeListener
+class Q_QUICK_EXPORT QQuickItemViewTransitionChangeListener
{
public:
QQuickItemViewTransitionChangeListener() {}
@@ -44,7 +46,7 @@ public:
};
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitioner
+class Q_QUICK_EXPORT QQuickItemViewTransitioner
{
public:
enum TransitionType {
@@ -104,7 +106,7 @@ private:
/*
An item that can be transitioned using QQuickViewTransitionJob.
*/
-class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionableItem
+class Q_QUICK_EXPORT QQuickItemViewTransitionableItem
{
public:
QQuickItemViewTransitionableItem(QQuickItem *i);
@@ -152,12 +154,12 @@ class QQuickViewTransitionAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
- Q_PROPERTY(QQuickItem* item READ item NOTIFY itemChanged)
- Q_PROPERTY(QPointF destination READ destination NOTIFY destinationChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(QQuickItem* item READ item NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QPointF destination READ destination NOTIFY destinationChanged FINAL)
- Q_PROPERTY(QList<int> targetIndexes READ targetIndexes NOTIFY targetIndexesChanged)
- Q_PROPERTY(QQmlListProperty<QObject> targetItems READ targetItems NOTIFY targetItemsChanged)
+ Q_PROPERTY(QList<int> targetIndexes READ targetIndexes NOTIFY targetIndexesChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QObject> targetItems READ targetItems NOTIFY targetItemsChanged FINAL)
QML_NAMED_ELEMENT(ViewTransition)
QML_ADDED_IN_VERSION(2, 0)
@@ -196,6 +198,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickViewTransitionAttached)
-
#endif // QQUICKITEMVIEWTRANSITION_P_P_H
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 7693c9d288..6d7ddfb0a2 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -3,6 +3,7 @@
#include "qquicklistview_p.h"
#include "qquickitemview_p_p.h"
+#include "qquickflickablebehavior_p.h"
#include <private/qqmlobjectmodel_p.h>
#include <QtQml/qqmlexpression.h>
@@ -14,7 +15,6 @@
#include <private/qquicksmoothedanimation_p_p.h>
#include <private/qqmlcomponent_p.h>
-#include "qplatformdefs.h"
QT_BEGIN_NAMESPACE
@@ -74,7 +74,9 @@ public:
void layoutVisibleItems(int fromModelIndex = 0) override;
bool applyInsertionChange(const QQmlChangeSet::Change &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView) override;
+#if QT_CONFIG(quick_viewtransitions)
void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult) override;
+#endif
void updateSectionCriteria() override;
void updateSections() override;
@@ -105,7 +107,7 @@ public:
void fixupPosition() override;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity) override;
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity) override;
QQuickItemViewAttached *getAttachedObject(const QObject *object) const override;
@@ -287,7 +289,8 @@ public:
: itemX() + itemWidth());
}
}
- void setPosition(qreal pos, bool immediate = false) {
+
+ void setPosition(qreal pos, bool immediate = false, bool resetInactiveAxis = true) {
// position the section immediately even if there is a transition
if (section()) {
if (view->orientation() == QQuickListView::Vertical) {
@@ -302,8 +305,9 @@ public:
section()->setX(pos);
}
}
- moveTo(pointForPosition(pos), immediate);
+ moveTo(pointForPosition(pos, resetInactiveAxis), immediate);
}
+
void setSize(qreal size) {
if (view->orientation() == QQuickListView::Vertical)
item->setHeight(size);
@@ -318,26 +322,26 @@ public:
QQuickListView *view;
private:
- QPointF pointForPosition(qreal pos) const {
+ QPointF pointForPosition(qreal pos, bool resetInactiveAxis) const {
if (view->orientation() == QQuickListView::Vertical) {
if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
if (section())
pos += section()->height();
- return QPointF(itemX(), -itemHeight() - pos);
+ return QPointF(resetInactiveAxis ? 0 : itemX(), -itemHeight() - pos);
} else {
if (section())
pos += section()->height();
- return QPointF(itemX(), pos);
+ return QPointF(resetInactiveAxis ? 0 : itemX(), pos);
}
} else {
if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
if (section())
pos += section()->width();
- return QPointF(-itemWidth() - pos, itemY());
+ return QPointF(-itemWidth() - pos, resetInactiveAxis ? 0 : itemY());
} else {
if (section())
pos += section()->width();
- return QPointF(pos, itemY());
+ return QPointF(pos, resetInactiveAxis ? 0 : itemY());
}
}
}
@@ -700,6 +704,8 @@ bool QQuickListViewPrivate::releaseItem(FxViewItem *item, QQmlInstanceModel::Reu
bool released = QQuickItemViewPrivate::releaseItem(item, reusableFlag);
if (released && it && att && att->m_sectionItem) {
+ QQuickItemPrivate::get(att->m_sectionItem)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+
// We hold no more references to this item
int i = 0;
do {
@@ -755,7 +761,9 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, incubationMode))))
break;
qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
+#endif
item->setPosition(pos, true);
if (item->item)
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
@@ -774,7 +782,9 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer << "item" << (QObject *)(item->item);
--visibleIndex;
visiblePos -= item->size() + spacing;
+#if QT_CONFIG(quick_viewtransitions)
if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
+#endif
item->setPosition(visiblePos, true);
if (item->item)
QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
@@ -787,11 +797,14 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
void QQuickListViewPrivate::removeItem(FxViewItem *item)
{
+#if QT_CONFIG(quick_viewtransitions)
if (item->transitionScheduledOrRunning()) {
qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << (QObject *)(item->item);
item->releaseAfterTransition = true;
releasePendingTransition.append(item);
- } else {
+ } else
+#endif
+ {
qCDebug(lcItemViewDelegateLifecycle) << "\treleasing stationary item" << item->index << (QObject *)(item->item);
releaseItem(item, reusableFlag);
}
@@ -864,6 +877,14 @@ void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
FxListItemSG *firstItem = static_cast<FxListItemSG *>(visibleItems.constFirst());
bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
+
+#if QT_CONFIG(quick_viewtransitions)
+ /* Set position of first item in list view when populate transition is configured, as it doesn't set
+ while adding visible item (addVisibleItem()) to the view */
+ if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true))
+ resetFirstItemPosition(isContentFlowReversed() ? -firstItem->position()-firstItem->size() : firstItem->position());
+#endif
+
firstVisibleItemPosition = firstItem->position();
qreal sum = firstItem->size();
qreal pos = firstItem->position() + firstItem->size() + spacing;
@@ -1061,19 +1082,24 @@ QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
setSectionHelper(context, sectionItem, section);
} else {
- QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
- QQmlContext *context = new QQmlContext(
- creationContext ? creationContext : qmlContext(q));
QQmlComponent* delegate = sectionCriteria->delegate();
- QQmlComponentPrivate* delegatePriv = QQmlComponentPrivate::get(delegate);
+ const bool reuseExistingContext = delegate->isBound();
+ auto delegatePriv = QQmlComponentPrivate::get(delegate);
+ QQmlPropertyCache::ConstPtr rootPropertyCache;
+
+ QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
+ auto baseContext = creationContext ? creationContext : qmlContext(q);
+ // if we need to insert a context property, we need a separate context
+ QQmlContext *context = reuseExistingContext ? baseContext : new QQmlContext(baseContext);
QObject *nobj = delegate->beginCreate(context);
if (nobj) {
if (delegatePriv->hadTopLevelRequiredProperties()) {
delegate->setInitialProperties(nobj, {{QLatin1String("section"), section}});
- } else {
+ } else if (!reuseExistingContext) {
context->setContextProperty(QLatin1String("section"), section);
}
- QQml_setParent_noEvent(context, nobj);
+ if (!reuseExistingContext)
+ QQml_setParent_noEvent(context, nobj);
sectionItem = qobject_cast<QQuickItem *>(nobj);
if (!sectionItem) {
delete nobj;
@@ -1086,12 +1112,15 @@ QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
// sections are not controlled by FxListItemSG, so apply attached properties here
QQuickItemViewAttached *attached = static_cast<QQuickItemViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(sectionItem));
attached->setView(q);
- } else {
+ } else if (!reuseExistingContext) {
delete context;
}
sectionCriteria->delegate()->completeCreate();
}
+ if (sectionItem)
+ QQuickItemPrivate::get(sectionItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+
return sectionItem;
}
@@ -1100,6 +1129,9 @@ void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
if (!item)
return;
int i = 0;
+
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+
do {
if (!sectionCache[i]) {
sectionCache[i] = item;
@@ -1439,26 +1471,26 @@ void QQuickListViewPrivate::updateFooter()
FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
if (footerPositioning == QQuickListView::OverlayFooter) {
- listItem->setPosition(isContentFlowReversed() ? -position() - footerSize() : position() + size() - footerSize());
+ listItem->setPosition(isContentFlowReversed() ? -position() - footerSize() : position() + size() - footerSize(), false, false);
} else if (visibleItems.size()) {
if (footerPositioning == QQuickListView::PullBackFooter) {
qreal viewPos = isContentFlowReversed() ? -position() : position() + size();
// using qBound() would throw an assert here, because max < min is a valid case
// here, if the list's delegates do not fill the whole view
qreal clampedPos = qMax(originPosition() - footerSize() + size(), qMin(listItem->position(), lastPosition()));
- listItem->setPosition(qBound(viewPos - footerSize(), clampedPos, viewPos));
+ listItem->setPosition(qBound(viewPos - footerSize(), clampedPos, viewPos), false, false);
} else {
qreal endPos = lastPosition();
if (findLastVisibleIndex() == model->count()-1) {
- listItem->setPosition(endPos);
+ listItem->setPosition(endPos, false, false);
} else {
qreal visiblePos = position() + q->height();
if (endPos <= visiblePos || listItem->position() < endPos)
- listItem->setPosition(endPos);
+ listItem->setPosition(endPos, false, false);
}
}
} else {
- listItem->setPosition(visiblePos);
+ listItem->setPosition(visiblePos, false, false);
}
if (created)
@@ -1505,7 +1537,7 @@ void QQuickListViewPrivate::updateHeader()
FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
if (headerPositioning == QQuickListView::OverlayHeader) {
- listItem->setPosition(isContentFlowReversed() ? -position() - size() : position());
+ listItem->setPosition(isContentFlowReversed() ? -position() - size() : position(), false, false);
} else if (visibleItems.size()) {
const bool fixingUp = (orient == QQuickListView::Vertical ? vData : hData).fixingUp;
if (headerPositioning == QQuickListView::PullBackHeader) {
@@ -1517,18 +1549,18 @@ void QQuickListViewPrivate::updateHeader()
// using qBound() would throw an assert here, because max < min is a valid case
// here, if the list's delegates do not fill the whole view
qreal clampedPos = qMax(originPosition() - headerSize(), qMin(headerPosition, lastPosition() - size()));
- listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos));
+ listItem->setPosition(qBound(viewPos - headerSize(), clampedPos, viewPos), false, false);
} else {
qreal startPos = originPosition();
if (visibleIndex == 0) {
- listItem->setPosition(startPos - headerSize());
+ listItem->setPosition(startPos - headerSize(), false, false);
} else {
if (position() <= startPos || listItem->position() > startPos - headerSize())
- listItem->setPosition(startPos - headerSize());
+ listItem->setPosition(startPos - headerSize(), false, false);
}
}
} else {
- listItem->setPosition(-headerSize());
+ listItem->setPosition(-headerSize(), false, false);
}
if (created)
@@ -1579,8 +1611,10 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometry
// position all subsequent items
if (visibleItems.size() && item == visibleItems.constFirst()->item) {
FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst());
+#if QT_CONFIG(quick_viewtransitions)
if (listItem->transitionScheduledOrRunning())
return;
+#endif
if (orient == QQuickListView::Vertical) {
const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height();
const qreal heightDiff = item->height() - oldGeometry.height();
@@ -1819,13 +1853,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
}
bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
- QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
+ QQuickTimeLineCallback::Callback fixupCallback, QEvent::Type eventType, qreal velocity)
{
data.fixingUp = false;
moveReason = Mouse;
if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
correctFlick = true;
- return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, eventType, velocity);
}
qreal maxDistance = 0;
const qreal dataValue =
@@ -1881,7 +1915,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
}
if (!hData.flicking && !vData.flicking) {
// the initial flick - estimate boundary
- qreal accel = deceleration;
+ qreal accel = eventType == QEvent::Wheel ? wheelDeceleration : deceleration;
qreal v2 = v * v;
overshootDist = 0.0;
// + averageSize/4 to encourage moving at least one item in the flick direction
@@ -1946,7 +1980,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
else if (velocity > 0 && newtarget >= minExtent)
newtarget = minExtent + overshootDist;
if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
- if (qAbs(velocity) < MinimumFlickVelocity)
+ if (qAbs(velocity) < _q_MinimumFlickVelocity)
correctFlick = false;
return false;
}
@@ -1973,7 +2007,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
void QQuickListViewPrivate::setSectionHelper(QQmlContext *context, QQuickItem *sectionItem, const QString &section)
{
- if (context->contextProperty(QLatin1String("section")).isValid())
+ if (!QQmlContextData::get(context)->isInternal() && context->contextProperty(QLatin1String("section")).isValid())
context->setContextProperty(QLatin1String("section"), section);
else
sectionItem->setProperty("section", section);
@@ -2004,6 +2038,11 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
ListView are laid out horizontally or vertically. List views are inherently
flickable because ListView inherits from \l Flickable.
+ \note ListView will only load as many delegate items as needed to fill up the view.
+ Items outside of the view will not be loaded unless a sufficient \l cacheBuffer has
+ been set. Hence, a ListView with zero width or height might not load any delegate
+ items at all.
+
\section1 Example Usage
The following example shows the definition of a simple list model defined
@@ -2155,7 +2194,7 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
of type \l [QML] {real}, so it is possible to set fractional
values like \c 0.1.
- \section1 Reusing items
+ \section1 Reusing Items
Since 5.15, ListView can be configured to recycle items instead of instantiating
from the \l delegate whenever new rows are flicked into view. This approach improves
@@ -2183,12 +2222,28 @@ QQuickItemViewAttached *QQuickListViewPrivate::getAttachedObject(const QObject *
\note While an item is in the pool, it might still be alive and respond
to connected signals and bindings.
+ \note For an item to be pooled, it needs to be completely flicked out of the bounds
+ of the view, \e including the extra margins set with \l {ListView::}{cacheBuffer}.
+ Some items will also never be pooled or reused, such as \l currentItem.
+
The following example shows a delegate that animates a spinning rectangle. When
it is pooled, the animation is temporarily paused:
\snippet qml/listview/ReusableDelegate.qml 0
\sa {QML Data Models}, GridView, PathView, {Qt Quick Examples - Views}
+
+ \section1 Variable Delegate Size and Section Labels
+
+ Variable delegate sizes might lead to resizing and skipping of any attached
+ \l {ScrollBar}. This is because ListView estimates its content size from
+ allocated items (usually only the visible items, the rest are assumed to be of
+ similar size), and variable delegate sizes prevent an accurate estimation. To
+ reduce this effect, \l {ListView::}{cacheBuffer} can be set to higher values,
+ effectively creating more items and improving the size estimate of unallocated
+ items, at the expense of additional memory usage. \l{ListView::section}{Sections}
+ have the same effect because they attach and elongate the section label to the
+ first item within the section.
*/
QQuickListView::QQuickListView(QQuickItem *parent)
: QQuickItemView(*(new QQuickListViewPrivate), parent)
@@ -2201,6 +2256,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty bool QtQuick::ListView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
@@ -2212,6 +2269,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty ListView QtQuick::ListView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate and also to the header, the footer,
@@ -2220,6 +2279,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::previousSection
+ \readonly
+
This attached property holds the section of the previous element.
It is attached to each instance of the delegate.
@@ -2229,6 +2290,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::nextSection
+ \readonly
+
This attached property holds the section of the next element.
It is attached to each instance of the delegate.
@@ -2238,6 +2301,8 @@ QQuickListView::~QQuickListView()
/*!
\qmlattachedproperty string QtQuick::ListView::section
+ \readonly
+
This attached property holds the section of this element.
It is attached to each instance of the delegate.
@@ -2346,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.
*/
/*!
@@ -2449,15 +2514,13 @@ QQuickListView::~QQuickListView()
Valid values for \c highlightRangeMode are:
- \list
- \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
- However, the highlight can move outside of the range at the ends of the list or due
- to mouse interaction.
- \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
- The current item changes if a keyboard or mouse action would cause the highlight to move
- outside of the range.
- \li ListView.NoHighlightRange - this is the default value.
- \endlist
+ \value ListView.ApplyRange the view attempts to maintain the highlight within the range.
+ However, the highlight can move outside of the range at the
+ ends of the list or due to mouse interaction.
+ \value ListView.StrictlyEnforceRange the highlight never moves outside of the range.
+ The current item changes if a keyboard or mouse action would
+ cause the highlight to move outside of the range.
+ \value ListView.NoHighlightRange this is the default value.
*/
void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
{
@@ -2504,20 +2567,12 @@ void QQuickListView::setSpacing(qreal spacing)
Possible values:
- \list
- \li ListView.Horizontal - Items are laid out horizontally
- \li ListView.Vertical (default) - Items are laid out vertically
- \endlist
-
- \table
- \row
- \li Horizontal orientation:
- \image ListViewHorizontal.png
-
- \row
- \li Vertical orientation:
- \image listview-highlight.png
- \endtable
+ \value ListView.Horizontal Items are laid out horizontally
+ \br
+ \inlineimage ListViewHorizontal.png
+ \value ListView.Vertical (default) Items are laid out vertically
+ \br
+ \inlineimage listview-highlight.png
\sa {Flickable Direction}
*/
@@ -2558,10 +2613,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items will be laid out from left to right.
- \li Qt.RightToLeft - Items will be laid out from right to left.
- \endlist
+ \value Qt.LeftToRight (default) Items will be laid out from left to right.
+ \value Qt.RightToLeft Items will be laid out from right to left.
Setting this property has no effect if the \l orientation is Qt.Vertical.
@@ -2587,10 +2640,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
Possible values:
- \list
- \li ListView.TopToBottom (default) - Items are laid out from the top of the view down to the bottom of the view.
- \li ListView.BottomToTop - Items are laid out from the bottom of the view up to the top of the view.
- \endlist
+ \value ListView.TopToBottom (default) Items are laid out from the top of the view down to the bottom of the view.
+ \value ListView.BottomToTop Items are laid out from the bottom of the view up to the top of the view.
Setting this property has no effect if the \l orientation is Qt.Horizontal.
@@ -2698,35 +2749,35 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
\c section.criteria holds the criteria for forming each section based on
\c section.property. This value can be one of:
- \list
- \li ViewSection.FullString (default) - sections are created based on the
- \c section.property value.
- \li ViewSection.FirstCharacter - sections are created based on the first
- character of the \c section.property value (for example, 'A', 'B', 'C'
- sections, etc. for an address book)
- \endlist
+ \value ViewSection.FullString (default) sections are created based on the
+ \c section.property value.
+ \value ViewSection.FirstCharacter sections are created based on the first character of
+ the \c section.property value (for example,
+ 'A', 'B', 'C' ... sections for an address book.)
A case insensitive comparison is used when determining section
boundaries.
\c section.delegate holds the delegate component for each section. The
default \l {QQuickItem::z}{stacking order} of section delegate instances
- is \c 2.
+ is \c 2. If you declare a \c required property named "section" in it,
+ that property will contain the section's title.
\c section.labelPositioning determines whether the current and/or
next section labels stick to the start/end of the view, and whether
the labels are shown inline. This value can be a combination of:
- \list
- \li ViewSection.InlineLabels - section labels are shown inline between
- the item delegates separating sections (default).
- \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
- start of the view as it is moved.
- \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
- sections) sticks to the end of the view as it is moved. \note Enabling
- \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
- section, which has performance implications, especially for slower models.
- \endlist
+ \value ViewSection.InlineLabels
+ (default) section labels are shown inline between the item delegates
+ separating sections.
+ \value ViewSection.CurrentLabelAtStart
+ the current section label sticks to the start of the view as it is moved.
+ \value ViewSection.NextLabelAtEnd
+ the next section label (beyond all visible sections) sticks to the end
+ of the view as it is moved.
+ \note Enabling \c ViewSection.NextLabelAtEnd requires the view to scan
+ ahead for the next section, which has performance implications,
+ especially for slower models.
Each item in the list has attached properties named \c ListView.section,
\c ListView.previousSection and \c ListView.nextSection.
@@ -2877,18 +2928,14 @@ void QQuickListView::setHighlightResizeDuration(int duration)
This property determines how the view scrolling will settle following a drag or flick.
The possible values are:
- \list
- \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
- \li ListView.SnapToItem - the view settles with an item aligned with the start of
- the view.
- \li ListView.SnapOneItem - the view settles no more than one item away from the first
- visible item at the time the mouse button is released. This mode is particularly
- useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
- show a stronger affinity to neighboring items when movement occurs. For example, a
- short drag that snaps back to the current item with SnapToItem might snap to a
- neighboring item with SnapOneItem.
-
- \endlist
+ \value ListView.NoSnap (default) the view stops anywhere within the visible area.
+ \value ListView.SnapToItem the view settles with an item aligned with the start of the view.
+ \value ListView.SnapOneItem the view settles no more than one item away from the first
+ visible item at the time the mouse button is released. This mode is particularly
+ useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
+ show a stronger affinity to neighboring items when movement occurs. For example, a
+ short drag that snaps back to the current item with SnapToItem might snap to a
+ neighboring item with SnapOneItem.
\c snapMode does not affect the \l currentIndex. To update the
\l currentIndex as the list is moved, set \l highlightRangeMode
@@ -3431,12 +3478,12 @@ void QQuickListView::viewportMoved(Qt::Orientations orient)
const qreal minY = minYExtent();
if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
&& minY != d->vData.flickTarget)
- d->flickY(-d->vData.smoothVelocity.value());
+ d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
} else if (d->vData.velocity < 0) {
const qreal maxY = maxYExtent();
if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
&& maxY != d->vData.flickTarget)
- d->flickY(-d->vData.smoothVelocity.value());
+ d->flickY(QEvent::TouchUpdate, -d->vData.smoothVelocity.value());
}
}
@@ -3445,12 +3492,12 @@ void QQuickListView::viewportMoved(Qt::Orientations orient)
const qreal minX = minXExtent();
if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
&& minX != d->hData.flickTarget)
- d->flickX(-d->hData.smoothVelocity.value());
+ d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
} else if (d->hData.velocity < 0) {
const qreal maxX = maxXExtent();
if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
&& maxX != d->hData.flickTarget)
- d->flickX(-d->hData.smoothVelocity.value());
+ d->flickX(QEvent::TouchUpdate, -d->hData.smoothVelocity.value());
}
}
d->inFlickCorrection = false;
@@ -3610,9 +3657,16 @@ void QQuickListViewPrivate::updateSectionCriteria()
bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
{
+ Q_Q(QQuickListView);
+#if QT_CONFIG(quick_viewtransitions)
+ Q_UNUSED(movingIntoView)
+#endif
int modelIndex = change.index;
int count = change.count;
+ if (q->size().isEmpty() && visibleItems.isEmpty())
+ return false;
+
qreal tempPos = isContentFlowReversed() ? -position()-size() : position();
int index = visibleItems.size() ? mapFromModel(modelIndex) : 0;
qreal lastVisiblePos = buffer + displayMarginEnd + tempPos + size();
@@ -3652,10 +3706,12 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
for (FxViewItem *item : std::as_const(visibleItems)) {
if (item->index != -1 && item->index >= modelIndex) {
item->index += count;
+#if QT_CONFIG(quick_viewtransitions)
if (change.isMove())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
else
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
+#endif
}
}
@@ -3690,9 +3746,11 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
insertResult->changedFirstItem = true;
if (!change.isMove()) {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
static_cast<FxListItemSG *>(item)->setPosition(pos, true);
}
insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
@@ -3721,7 +3779,9 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
FxViewItem *item = nullptr;
if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(it.index))))
item->index = it.index;
+#if QT_CONFIG(quick_viewtransitions)
bool newItem = !item;
+#endif
it.removedAtIndex = false;
if (!item)
item = createItem(it.index, QQmlIncubator::Synchronous);
@@ -3732,19 +3792,26 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
continue;
}
- visibleItems.insert(index, item);
+ if (index < visibleItems.size())
+ visibleItems.insert(index, item);
+ else // special case of appending an item to the model - as above
+ visibleItems.append(item);
if (index == 0)
insertResult->changedFirstItem = true;
if (change.isMove()) {
// we know this is a move target, since move displaced items that are
// shuffled into view due to a move would be added in refill()
+#if QT_CONFIG(quick_viewtransitions)
if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
+#endif
} else {
addedItems->append(item);
+#if QT_CONFIG(quick_viewtransitions)
if (transitioner)
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
else
+#endif
static_cast<FxListItemSG *>(item)->setPosition(pos, true);
}
insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
@@ -3758,13 +3825,17 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
FxViewItem *item = visibleItems.at(index);
if (prevItem->index != item->index - 1) {
int i = index;
+#if QT_CONFIG(quick_viewtransitions)
qreal prevPos = prevItem->position();
+#endif
while (i < visibleItems.size()) {
FxListItemSG *nvItem = static_cast<FxListItemSG *>(visibleItems.takeLast());
insertResult->sizeChangesAfterVisiblePos -= nvItem->size() + spacing;
addedItems->removeOne(nvItem);
+#if QT_CONFIG(quick_viewtransitions)
if (nvItem->transitionScheduledOrRunning())
nvItem->setPosition(prevPos + (nvItem->index - prevItem->index) * averageSize);
+#endif
removeItem(nvItem);
}
}
@@ -3776,6 +3847,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
return visibleAffected;
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
{
Q_UNUSED(insertionResult);
@@ -3809,25 +3881,22 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
}
}
}
+#endif
/*!
\qmlmethod QtQuick::ListView::positionViewAtIndex(int index, PositionMode mode)
- Positions the view such that the \a index is at the position specified by
- \a mode:
-
- \list
- \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
- \li ListView.Center - position item in the center of the view.
- \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
- \li ListView.Visible - if any part of the item is visible then take no action, otherwise
- bring the item into view.
- \li ListView.Contain - ensure the entire item is visible. If the item is larger than
- the view the item is positioned at the top (or left for horizontal orientation) of the view.
- \li ListView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ Positions the view such that the \a index is at the position specified by \a mode:
+
+ \value ListView.Beginning position item at the top (or left for horizontal orientation) of the view.
+ \value ListView.Center position item in the center of the view.
+ \value ListView.End position item at bottom (or right for horizontal orientation) of the view.
+ \value ListView.Visible if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \value ListView.Contain ensure the entire item is visible. If the item is larger than the view,
+ the item is positioned at the top (or left for horizontal orientation) of the view.
+ \value ListView.SnapPosition position the item at \l preferredHighlightBegin. This mode is only valid
+ if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled via \l snapMode.
If positioning the view at \a index would cause empty space to be displayed at
the beginning or end of the view, the view will be positioned at the boundary.
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index e4719b5d36..4651bc689f 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -23,11 +23,13 @@ QT_REQUIRE_CONFIG(quick_listview);
#include <private/qtquickglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickListView;
class QQuickListViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickViewSection : public QObject
+class Q_QUICK_EXPORT QQuickViewSection : public QObject
{
Q_OBJECT
Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
@@ -75,7 +77,7 @@ private:
class QQmlInstanceModel;
class QQuickListViewAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView
+class Q_QUICK_EXPORT QQuickListView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickListView)
@@ -169,7 +171,7 @@ protected:
qreal maxXExtent() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickListViewAttached : public QQuickItemViewAttached
+class Q_QUICK_EXPORT QQuickListViewAttached : public QQuickItemViewAttached
{
Q_OBJECT
@@ -185,7 +187,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickListView)
-QML_DECLARE_TYPE(QQuickViewSection)
-
#endif // QQUICKLISTVIEW_P_H
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index da04a2956c..496948fcb7 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -13,8 +13,6 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcTransient)
-
static const QQuickItemPrivate::ChangeTypes watchedChanges
= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
@@ -161,9 +159,7 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\section2 Loader Sizing Behavior
- If the source component is not an Item type, Loader does not
- apply any special sizing rules. When used to load visual types,
- Loader applies the following sizing rules:
+ When used to load visual types, Loader applies the following sizing rules:
\list
\li If an explicit size is not specified for the Loader, the Loader
@@ -190,6 +186,8 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\li The red rectangle will be 50x50, centered in the root item.
\endtable
+ If the source component is not an Item type, Loader does not apply any
+ special sizing rules.
\section2 Receiving Signals from Loaded Objects
@@ -673,13 +671,6 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
if (status == QQmlIncubator::Ready) {
object = incubator->object();
item = qmlobject_cast<QQuickItem*>(object);
- if (!item) {
- QQuickWindow *window = qmlobject_cast<QQuickWindow*>(object);
- if (window) {
- qCDebug(lcTransient) << window << "is transient for" << q->window();
- window->setTransientParent(q->window());
- }
- }
emit q->itemChanged();
initResize();
incubator->clear();
@@ -795,7 +786,7 @@ void QQuickLoader::componentComplete()
{
Q_D(QQuickLoader);
QQuickItem::componentComplete();
- if (active()) {
+ if (active() && (status() != Ready)) {
if (d->loadingFromSource)
d->createComponent();
d->load();
@@ -804,12 +795,15 @@ void QQuickLoader::componentComplete()
void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
{
- if (change == ItemSceneChange) {
- QQuickWindow *loadedWindow = qmlobject_cast<QQuickWindow *>(item());
- if (loadedWindow) {
- qCDebug(lcTransient) << loadedWindow << "is transient for" << value.window;
- loadedWindow->setTransientParent(value.window);
- }
+ switch (change) {
+ case ItemChildAddedChange:
+ Q_ASSERT(value.item);
+ if (value.item->flags().testFlag(QQuickItem::ItemObservesViewport))
+ // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned on
+ value.item->setFlag(QQuickItem::ItemObservesViewport);
+ break;
+ default:
+ break;
}
QQuickItem::itemChange(change, value);
}
@@ -915,12 +909,24 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid();
const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid();
- if (needToUpdateWidth && needToUpdateHeight)
+ if (needToUpdateWidth && needToUpdateHeight) {
+ /* setSize keeps bindings intact (for backwards compatibility reasons),
+ but here we actually want the loader to control the size, so any
+ prexisting bindings ought to be removed
+ */
+ auto *itemPriv = QQuickItemPrivate::get(item);
+ // takeBinding would work without the check, but this is more efficient
+ // for the common case where we don't have a binding
+ if (itemPriv->width.hasBinding())
+ itemPriv->width.takeBinding();
+ if (itemPriv->height.hasBinding())
+ itemPriv->height.takeBinding();
item->setSize(QSizeF(q->width(), q->height()));
- else if (needToUpdateWidth)
+ } else if (needToUpdateWidth) {
item->setWidth(q->width());
- else if (needToUpdateHeight)
+ } else if (needToUpdateHeight) {
item->setHeight(q->height());
+ }
if (updatingSize)
return;
diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h
index f8c65513d5..c3d2015aed 100644
--- a/src/quick/items/qquickloader_p.h
+++ b/src/quick/items/qquickloader_p.h
@@ -20,8 +20,7 @@
QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
-class QQmlV4Function;
-class Q_QUICK_PRIVATE_EXPORT QQuickLoader : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickLoader : public QQuickImplicitSizeItem
{
Q_OBJECT
@@ -90,6 +89,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLoader)
-
#endif // QQUICKLOADER_P_H
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 43257e0c84..39011f1162 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -27,7 +27,6 @@ QT_BEGIN_NAMESPACE
class QQuickLoaderPrivate;
-class QQmlV4Function;
class QQuickLoaderIncubator : public QQmlIncubator
{
public:
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index dc44deca38..7e68dd8be3 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -19,9 +19,7 @@
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
-
-Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
+DEFINE_BOOL_CONFIG_OPTION(qmlMaVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
: enabled(true), hoverEnabled(false), scrollGestureEnabled(true), hovered(false), longPress(false),
@@ -53,7 +51,7 @@ void QQuickMouseAreaPrivate::init()
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
- if (qmlVisualTouchDebugging()) {
+ if (qmlMaVisualTouchDebugging()) {
q->setFlag(QQuickItem::ItemHasContents);
}
}
@@ -743,7 +741,9 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, event->flags());
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(event->source());
+#endif
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -788,13 +788,18 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true,
false, event->flags());
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(event->source());
+#endif
me.setAccepted(d->isDoubleClickConnected());
emit this->doubleClicked(&me);
if (!me.isAccepted())
d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
if (d->pressed)
d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
+
+ // Do not call the base implementation: we don't want to call event->ignore().
+ return;
}
QQuickItem::mouseDoubleClickEvent(event);
}
@@ -902,6 +907,7 @@ void QQuickMouseArea::ungrabMouse()
emit pressedButtonsChanged();
if (d->hovered && !isUnderMouse()) {
+ qCDebug(lcHoverTrace) << "losing hover: not under the mouse";
d->hovered = false;
emit hoveredChanged();
}
@@ -966,6 +972,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
emit pressedChanged();
emit containsPressChanged();
if (d->hovered) {
+ qCDebug(lcHoverTrace) << "losing hover: button released";
d->hovered = false;
emit hoveredChanged();
}
@@ -1012,7 +1019,9 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event)
d->longPress = true;
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, d->lastFlags);
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(Qt::MouseEventSynthesizedByQt);
+#endif
me.setAccepted(d->isPressAndHoldConnected());
emit pressAndHold(&me);
if (!me.isAccepted())
@@ -1028,7 +1037,7 @@ void QQuickMouseArea::geometryChange(const QRectF &newGeometry, const QRectF &ol
Q_D(QQuickMouseArea);
QQuickItem::geometryChange(newGeometry, oldGeometry);
- if (d->lastScenePos.isNull)
+ if (!d->lastScenePos.isValid())
d->lastScenePos = mapToScene(d->lastPos);
else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
d->lastPos = mapFromScene(d->lastScenePos);
@@ -1130,8 +1139,10 @@ void QQuickMouseArea::setHoverEnabled(bool h)
\qmlproperty bool QtQuick::MouseArea::containsMouse
This property holds whether the mouse is currently inside the mouse area.
- \warning If hoverEnabled is false, containsMouse will only be valid
+ \warning If hoverEnabled is \c false, \c containsMouse will be \c true
when the mouse is pressed while the mouse cursor is inside the MouseArea.
+ But if you set \c {mouse.accepted = false} in an \c onPressed handler,
+ \c containsMouse will remain \c false because the press was rejected.
*/
bool QQuickMouseArea::hovered() const
{
@@ -1229,7 +1240,9 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
if (wasPressed != p) {
QQuickMouseEvent &me = d->quickMouseEvent;
me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress, d->lastFlags);
+#if QT_DEPRECATED_SINCE(6, 6)
me.setSource(source);
+#endif
if (p) {
d->pressed |= button;
if (!d->doubleClick)
@@ -1241,6 +1254,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
if (!me.isAccepted()) {
d->pressed = Qt::NoButton;
+ if (!hoverEnabled())
+ setHovered(false);
}
if (!oldPressed) {
@@ -1267,6 +1282,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
return me.isAccepted();
}
+ Q_UNUSED(source)
return false;
}
@@ -1438,7 +1454,7 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
Q_UNUSED(data);
Q_D(QQuickMouseArea);
- if (!qmlVisualTouchDebugging())
+ if (!qmlMaVisualTouchDebugging())
return nullptr;
QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h
index 9ce57bcf60..96e60002b5 100644
--- a/src/quick/items/qquickmousearea_p.h
+++ b/src/quick/items/qquickmousearea_p.h
@@ -26,7 +26,7 @@ class QQuickDrag;
class QQuickMouseAreaPrivate;
class QQuickWheelEvent;
// used in Qt Location
-class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickMouseArea : public QQuickItem
{
Q_OBJECT
@@ -163,6 +163,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMouseArea)
-
#endif // QQUICKMOUSEAREA_P_H
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index fc994fd18c..b0b99334a2 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -17,7 +17,7 @@
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
+DEFINE_BOOL_CONFIG_OPTION(qmlMptaVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
/*!
\qmltype TouchPoint
@@ -329,18 +329,39 @@ void QQuickTouchPoint::setUniqueId(const QPointingDeviceUniqueId &id)
If minimumTouchPoints is set to a value greater than one, this signal will not be emitted until the minimum number
of required touch points has been reached.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onPressed: (points) => console.log("pressed", points.length)
+ \endqml
*/
/*!
\qmlsignal QtQuick::MultiPointTouchArea::updated(list<TouchPoint> touchPoints)
This signal is emitted when existing touch points are updated. \a touchPoints is a list of these updated points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onUpdated: (points) => console.log("updated", points.length)
+ \endqml
*/
/*!
\qmlsignal QtQuick::MultiPointTouchArea::released(list<TouchPoint> touchPoints)
This signal is emitted when existing touch points are removed. \a touchPoints is a list of these removed points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onReleased: (points) => console.log("released", points.length)
+ \endqml
*/
/*!
@@ -356,8 +377,17 @@ void QQuickTouchPoint::setUniqueId(const QPointingDeviceUniqueId &id)
\c canceled should be handled in addition to \l released.
\a touchPoints is the list of canceled points.
+
+ \note If you use the \c touchPoints argument in your signal handler code,
+ it's best to rename it in your formal parameter to avoid confusion with the
+ \c touchPoints property (see \l{QML Coding Conventions}):
+ \qml
+ onCanceled: (points) => console.log("canceled", points.length)
+ \endqml
*/
+// TODO Qt 7: remove the notes above about the signal touchPoints arguments
+
/*!
\qmlsignal QtQuick::MultiPointTouchArea::gestureStarted(GestureEvent gesture)
@@ -407,11 +437,11 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
{
setAcceptedMouseButtons(Qt::LeftButton);
setFiltersChildMouseEvents(true);
- if (qmlVisualTouchDebugging()) {
+ if (qmlMptaVisualTouchDebugging()) {
setFlag(QQuickItem::ItemHasContents);
}
setAcceptTouchEvents(true);
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
}
@@ -596,6 +626,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints
}
if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
for (QEventPoint &p : touchPoints) {
+ QPointF oldPos = p.position();
+ auto transformBack = qScopeGuard([&] { QMutableEventPoint::setPosition(p, oldPos); });
if (touchPointsFromEvent && remap == RemapEventPoints::ToLocal)
QMutableEventPoint::setPosition(p, mapFromScene(p.scenePosition()));
QEventPoint::State touchPointState = p.state();
@@ -715,17 +747,17 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
_mouseTouchPoint = dtp;
}
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
void QQuickMultiPointTouchArea::hoverEnterEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
- setTouchEventsEnabled(true);
+ setTouchEventsEnabled(isEnabled());
+ QQuickItem::hoverEnterEvent(event);
}
void QQuickMultiPointTouchArea::hoverLeaveEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
setTouchEventsEnabled(false);
+ QQuickItem::hoverLeaveEvent(event);
}
void QQuickMultiPointTouchArea::setTouchEventsEnabled(bool enable)
@@ -739,7 +771,14 @@ void QQuickMultiPointTouchArea::setTouchEventsEnabled(bool enable)
registerTouchWindow(window(), enable);
}
-#endif // Q_OS_OSX
+
+void QQuickMultiPointTouchArea::itemChange(ItemChange change, const ItemChangeData &data)
+{
+ if (change == ItemEnabledHasChanged)
+ setAcceptHoverEvents(data.boolValue);
+ QQuickItem::itemChange(change, data);
+}
+#endif // Q_OS_MACOS
void QQuickMultiPointTouchArea::addTouchPrototype(QQuickTouchPoint *prototype)
{
@@ -1000,7 +1039,7 @@ QSGNode *QQuickMultiPointTouchArea::updatePaintNode(QSGNode *oldNode, UpdatePain
{
Q_UNUSED(data);
- if (!qmlVisualTouchDebugging())
+ if (!qmlMptaVisualTouchDebugging())
return nullptr;
QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index f92705f28e..51ddb1645c 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -30,7 +30,7 @@
QT_BEGIN_NAMESPACE
class QQuickMultiPointTouchArea;
-class Q_QUICK_PRIVATE_EXPORT QQuickTouchPoint : public QObject
+class Q_QUICK_EXPORT QQuickTouchPoint : public QObject
{
Q_OBJECT
Q_PROPERTY(int pointId READ pointId NOTIFY pointIdChanged)
@@ -152,8 +152,8 @@ private:
class QQuickGrabGestureEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT)
- Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT)
+ Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT FINAL)
+ Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT FINAL)
QML_NAMED_ELEMENT(GestureEvent)
QML_ADDED_IN_VERSION(2, 0)
QML_UNCREATABLE("GestureEvent is only available in the context of handling the gestureStarted signal from MultiPointTouchArea.")
@@ -176,12 +176,12 @@ private:
QList<QObject*> _touchPoints;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointTouchArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickMultiPointTouchArea : public QQuickItem
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(QQuickMultiPointTouchArea)
- Q_PROPERTY(QQmlListProperty<QQuickTouchPoint> touchPoints READ touchPoints)
+ Q_PROPERTY(QQmlListProperty<QQuickTouchPoint> touchPoints READ touchPoints CONSTANT)
Q_PROPERTY(int minimumTouchPoints READ minimumTouchPoints WRITE setMinimumTouchPoints NOTIFY minimumTouchPointsChanged)
Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints WRITE setMaximumTouchPoints NOTIFY maximumTouchPointsChanged)
Q_PROPERTY(bool mouseEnabled READ mouseEnabled WRITE setMouseEnabled NOTIFY mouseEnabledChanged)
@@ -219,12 +219,23 @@ public:
}
Q_SIGNALS:
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void pressed(const QList<QObject*> &touchPoints);
void updated(const QList<QObject*> &touchPoints);
void released(const QList<QObject*> &touchPoints);
void canceled(const QList<QObject*> &touchPoints);
+#else
+ void pressed(const QList<QObject*> &points);
+ void updated(const QList<QObject*> &points);
+ void released(const QList<QObject*> &points);
+ void canceled(const QList<QObject*> &points);
+#endif
void gestureStarted(QQuickGrabGestureEvent *gesture);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void touchUpdated(const QList<QObject*> &touchPoints);
+#else
+ void touchUpdated(const QList<QObject*> &points);
+#endif
void minimumTouchPointsChanged();
void maximumTouchPointsChanged();
void mouseEnabledChanged();
@@ -252,10 +263,11 @@ protected:
bool shouldFilter(QEvent *event);
void grabGesture(QPointingDevice *dev);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
void hoverEnterEvent(QHoverEvent *event) override;
void hoverLeaveEvent(QHoverEvent *event) override;
void setTouchEventsEnabled(bool enable);
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
#endif
private:
@@ -278,8 +290,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTouchPoint)
-QML_DECLARE_TYPE(QQuickGrabGestureEvent)
-QML_DECLARE_TYPE(QQuickMultiPointTouchArea)
-
#endif // QQUICKMULTIPOINTTOUCHAREA_H
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index d363fbcd31..df4ee6014d 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -8,7 +8,7 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <qmath.h>
@@ -87,7 +87,6 @@ QQuickPaintedItemPrivate::QQuickPaintedItemPrivate()
, fillColor(Qt::transparent)
, renderTarget(QQuickPaintedItem::Image)
, opaquePainting(false)
- , antialiasing(false)
, mipmap(false)
, textureProvider(nullptr)
, node(nullptr)
@@ -177,6 +176,7 @@ void QQuickPaintedItem::setOpaquePainting(bool opaque)
QQuickItem::update();
}
+//### Qt7: remove the aa functions; they shadow the QQuickItem property
/*!
Returns true if antialiased painting is enabled; otherwise, false is returned.
@@ -186,8 +186,7 @@ void QQuickPaintedItem::setOpaquePainting(bool opaque)
*/
bool QQuickPaintedItem::antialiasing() const
{
- Q_D(const QQuickPaintedItem);
- return d->antialiasing;
+ return QQuickItem::antialiasing();
}
/*!
@@ -199,13 +198,7 @@ bool QQuickPaintedItem::antialiasing() const
*/
void QQuickPaintedItem::setAntialiasing(bool enable)
{
- Q_D(QQuickPaintedItem);
-
- if (d->antialiasing == enable)
- return;
-
- d->antialiasing = enable;
- update();
+ QQuickItem::setAntialiasing(enable);
}
/*!
@@ -416,6 +409,10 @@ void QQuickPaintedItem::setContentsScale(qreal scale)
\brief The item's background fill color.
By default, the fill color is set to Qt::transparent.
+
+ Set the fill color to an invalid color (e.g. QColor()) to disable background
+ filling. This may improve performance, and is safe to do if the paint() function
+ draws to all pixels on each frame.
*/
QColor QQuickPaintedItem::fillColor() const
{
@@ -546,7 +543,7 @@ QSGNode *QQuickPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
node->setPreferredRenderTarget(d->renderTarget);
node->setFastFBOResizing(d->performanceHints & FastFBOResizing);
- node->setSmoothPainting(d->antialiasing);
+ node->setSmoothPainting(antialiasing());
node->setLinearFiltering(d->smooth);
node->setMipmapping(d->mipmap);
node->setOpaquePainting(d->opaquePainting);
diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h
index fd677f5519..2695d13da4 100644
--- a/src/quick/items/qquickpainteditem_p.h
+++ b/src/quick/items/qquickpainteditem_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickPaintedItemTextureProvider;
class QSGPainterNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickPaintedItemPrivate : public QQuickItemPrivate
+class Q_QUICK_EXPORT QQuickPaintedItemPrivate : public QQuickItemPrivate
{
public:
QQuickPaintedItemPrivate();
@@ -40,7 +40,6 @@ public:
QRect dirtyRect;
bool opaquePainting: 1;
- bool antialiasing: 1;
bool mipmap: 1;
mutable QQuickPaintedItemTextureProvider *textureProvider;
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index 07d1e61b59..79b22bd8a0 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -33,7 +33,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\internal
\class QQuickPalette
- \brief The QQuickPalette class contains color groups for each QML item state.
+ \brief Contains color groups for each QML item state.
\inmodule QtQuick
\since 6.0
@@ -48,10 +48,10 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\inherits QQuickColorGroup
\inqmlmodule QtQuick
\ingroup qtquick-visual
- \brief The QQuickPalette class contains color groups for each QML item state.
+ \brief Contains color groups for each QML item state.
- A palette consists of three color groups: Active, Disabled, and Inactive.
- Active color group is the default group, its colors are used for other groups
+ A palette consists of three color groups: \c active, \c disabled, and \c inactive.
+ The \c active color group is the default group: its colors are used for other groups
if colors of these groups aren't explicitly specified.
In the following example, color is applied for all color groups:
@@ -93,22 +93,23 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
\endcode
It is also possible to specify colors like this:
- \code
- palette {
- buttonText: "azure"
- button: "khaki"
- disabled {
- buttonText: "lavender"
- button: "coral"
- }
- }
- \endcode
- This approach is convenient when you need to specify a whole palette with all color groups.
+ \snippet qtquickcontrols-custom-palette-buttons.qml palette
+
+ This approach is especially convenient when you need to specify a whole
+ palette with all color groups; but as with the other cases above, the
+ colors that are not specified are initialized from SystemPalette, or
+ potentially the \l {Styling Qt Quick Controls}{Qt Quick Controls style},
+ if one is in use.
+
+ \note Some Controls styles use some palette colors, but many styles use
+ independent colors.
+
+ \sa Window::palette, Item::palette, Popup::palette, SystemPalette
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::active
+ \qmlproperty ColorGroup QtQuick::Palette::active
The Active group is used for windows that are in focus.
@@ -116,7 +117,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::inactive
+ \qmlproperty ColorGroup QtQuick::Palette::inactive
The Inactive group is used for windows that have no keyboard focus.
@@ -124,7 +125,7 @@ static constexpr bool is_valid(QPalette::ColorGroup cg) noexcept
*/
/*!
- \qmlproperty QQuickColorGroup QtQuick::Palette::disabled
+ \qmlproperty ColorGroup QtQuick::Palette::disabled
The Disabled group is used for elements that are disabled for some reason.
@@ -152,6 +153,24 @@ QQuickColorGroup *QQuickPalette::disabled() const
return colorGroup(QPalette::Disabled);
}
+void QQuickPalette::resetActive()
+{
+ if (colorProvider().resetColor(QPalette::Active))
+ Q_EMIT changed();
+}
+
+void QQuickPalette::resetInactive()
+{
+ if (colorProvider().resetColor(QPalette::Inactive))
+ Q_EMIT changed();
+}
+
+void QQuickPalette::resetDisabled()
+{
+ if (colorProvider().resetColor(QPalette::Disabled))
+ Q_EMIT changed();
+}
+
/*!
\internal
diff --git a/src/quick/items/qquickpalette_p.h b/src/quick/items/qquickpalette_p.h
index ba93b867cc..3e40cf9036 100644
--- a/src/quick/items/qquickpalette_p.h
+++ b/src/quick/items/qquickpalette_p.h
@@ -16,20 +16,21 @@
#include <QtQuick/private/qquickcolorgroup_p.h>
+#include <QtCore/qpointer.h>
+
#include <array>
QT_BEGIN_NAMESPACE
class QQuickAbstractPaletteProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickPalette : public QQuickColorGroup
+class Q_QUICK_EXPORT QQuickPalette : public QQuickColorGroup
{
Q_OBJECT
- Q_PROPERTY(QQuickColorGroup *active READ active WRITE setActive NOTIFY activeChanged)
- Q_PROPERTY(QQuickColorGroup *inactive READ inactive WRITE setInactive NOTIFY inactiveChanged)
- Q_PROPERTY(QQuickColorGroup *disabled READ disabled WRITE setDisabled NOTIFY disabledChanged)
-
+ Q_PROPERTY(QQuickColorGroup *active READ active WRITE setActive RESET resetActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QQuickColorGroup *inactive READ inactive WRITE setInactive RESET resetInactive NOTIFY inactiveChanged FINAL)
+ Q_PROPERTY(QQuickColorGroup *disabled READ disabled WRITE setDisabled RESET resetDisabled NOTIFY disabledChanged FINAL)
QML_NAMED_ELEMENT(Palette)
QML_ADDED_IN_VERSION(6, 0)
@@ -43,6 +44,9 @@ public:
QQuickColorGroup *active() const;
QQuickColorGroup *inactive() const;
QQuickColorGroup *disabled() const;
+ void resetActive();
+ void resetInactive();
+ void resetDisabled();
QPalette::ColorGroup currentColorGroup() const override;
void setCurrentGroup(QPalette::ColorGroup currentGroup);
@@ -71,6 +75,7 @@ private:
void setColorGroup(QPalette::ColorGroup groupTag,
const QQuickColorGroup::GroupPtr &group,
void (QQuickPalette::*notifier)());
+
QQuickColorGroup::GroupPtr colorGroup(QPalette::ColorGroup groupTag) const;
QQuickColorGroup::GroupPtr findColorGroup(QPalette::ColorGroup groupTag) const;
@@ -88,6 +93,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPalette)
-
#endif // QQUICKPALETTE_H
diff --git a/src/quick/items/qquickpalettecolorprovider.cpp b/src/quick/items/qquickpalettecolorprovider.cpp
index 2d36ff01c5..f01fa22bdd 100644
--- a/src/quick/items/qquickpalettecolorprovider.cpp
+++ b/src/quick/items/qquickpalettecolorprovider.cpp
@@ -3,6 +3,7 @@
#include "qquickpalettecolorprovider_p.h"
#include <QtQuick/private/qquickabstractpaletteprovider_p.h>
+#include <QtGui/private/qpalette_p.h>
QT_BEGIN_NAMESPACE
@@ -44,10 +45,55 @@ bool QQuickPaletteColorProvider::setColor(QPalette::ColorGroup g, QPalette::Colo
bool QQuickPaletteColorProvider::resetColor(QPalette::ColorGroup group, QPalette::ColorRole role)
{
- const auto &defaultPalette = paletteProvider()->defaultPalette() ;
- const auto &defaultColor = defaultPalette.color(adjustCg(group), role);
+ if (!m_requestedPalette.isAllocated())
+ return false;
- return setColor(group, role, defaultColor);
+ QPalette::ResolveMask unsetResolveMask = 0;
+
+ if (group == QPalette::Current)
+ group = m_requestedPalette->currentColorGroup();
+
+ if (group == QPalette::All) {
+ for (int g = QPalette::Active; g < QPalette::NColorGroups; ++g)
+ unsetResolveMask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(QPalette::ColorGroup(g), role));
+ } else {
+ unsetResolveMask = (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(group, role));
+ }
+
+ m_requestedPalette->setResolveMask(m_requestedPalette->resolveMask() & ~unsetResolveMask);
+
+ return updateInheritedPalette();
+}
+
+bool QQuickPaletteColorProvider::resetColor(QPalette::ColorGroup group)
+{
+ if (!m_requestedPalette.isAllocated())
+ return false;
+
+ QPalette::ResolveMask unsetResolveMask = 0;
+
+ auto getResolveMask = [] (QPalette::ColorGroup group) {
+ QPalette::ResolveMask mask = 0;
+ for (int roleIndex = QPalette::WindowText; roleIndex < QPalette::NColorRoles; ++roleIndex) {
+ const auto cr = QPalette::ColorRole(roleIndex);
+ mask |= (QPalette::ResolveMask(1) << QPalettePrivate::bitPosition(group, cr));
+ }
+ return mask;
+ };
+
+ if (group == QPalette::Current)
+ group = m_requestedPalette->currentColorGroup();
+
+ if (group == QPalette::All) {
+ for (int g = QPalette::Active; g < QPalette::NColorGroups; ++g)
+ unsetResolveMask |= getResolveMask(QPalette::ColorGroup(g));
+ } else {
+ unsetResolveMask = getResolveMask(group);
+ }
+
+ m_requestedPalette->setResolveMask(m_requestedPalette->resolveMask() & ~unsetResolveMask);
+
+ return updateInheritedPalette();
}
bool QQuickPaletteColorProvider::fromQPalette(QPalette p)
@@ -116,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/qquickpalettecolorprovider_p.h b/src/quick/items/qquickpalettecolorprovider_p.h
index ff715219dc..2a779f20d3 100644
--- a/src/quick/items/qquickpalettecolorprovider_p.h
+++ b/src/quick/items/qquickpalettecolorprovider_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractPaletteProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickPaletteColorProvider
+class Q_QUICK_EXPORT QQuickPaletteColorProvider
: public std::enable_shared_from_this<QQuickPaletteColorProvider>
{
public:
@@ -34,6 +34,7 @@ public:
const QColor &color(QPalette::ColorGroup group, QPalette::ColorRole role) const;
bool setColor(QPalette::ColorGroup group, QPalette::ColorRole role, QColor color);
bool resetColor(QPalette::ColorGroup group, QPalette::ColorRole role);
+ bool resetColor(QPalette::ColorGroup group);
bool fromQPalette(QPalette p);
QPalette palette() const;
diff --git a/src/quick/items/qquickpaletteproviderprivatebase_p.h b/src/quick/items/qquickpaletteproviderprivatebase_p.h
index a0e0302ea9..869e86c954 100644
--- a/src/quick/items/qquickpaletteproviderprivatebase_p.h
+++ b/src/quick/items/qquickpaletteproviderprivatebase_p.h
@@ -221,7 +221,7 @@ void QQuickPaletteProviderPrivateBase<I, Impl>::registerPalette(PalettePtr palet
// In order to avoid extra noise, we should connect
// the following signals only after everything is already setup
I::connect(paletteData(), &QQuickPalette::changed, itemWithPalette(), &I::paletteChanged);
- I::connect(paletteData(), &QQuickPalette::changed, [this]{ updateChildrenPalettes(toQPalette()); });
+ I::connect(paletteData(), &QQuickPalette::changed, itemWithPalette(), [this]{ updateChildrenPalettes(toQPalette()); });
}
template<class T> struct dependent_false : std::false_type {};
@@ -230,9 +230,9 @@ template<class Impl, class I> decltype(auto) getPrivateImpl(I &t) { return Impl:
template <class T>
decltype(auto) getPrivate(T &item)
{
- if constexpr (std::is_same_v<T, QQuickWindow>) {
+ if constexpr (std::is_base_of_v<T, QQuickWindow>) {
return getPrivateImpl<QQuickWindowPrivate>(item);
- } else if constexpr (std::is_same_v<T, QQuickItem>) {
+ } else if constexpr (std::is_base_of_v<T, QQuickItem>) {
return getPrivateImpl<QQuickItemPrivate>(item);
} else {
static_assert (dependent_false<T>::value, "Extend please.");
@@ -257,12 +257,15 @@ template<class I, class Impl>
QPalette QQuickPaletteProviderPrivateBase<I, Impl>::parentPalette(const QPalette &fallbackPalette) const
{
if constexpr (!isRootWindow<I>()) {
- for (auto parentItem = itemWithPalette()->parentItem(); parentItem;
- parentItem = parentItem->parentItem()) {
-
- // Don't allocate a new palette here. Use only if it's already pre allocated
- if (parentItem && getPrivate(*parentItem)->providesPalette()) {
- return getPrivate(*parentItem)->palette()->toQPalette();
+ // Popups should always inherit from their window, even child popups: QTBUG-115707.
+ if (!std::is_base_of_v<QQuickPopup, I>) {
+ for (auto parentItem = itemWithPalette()->parentItem(); parentItem;
+ parentItem = parentItem->parentItem()) {
+
+ // Don't allocate a new palette here. Use only if it's already pre allocated
+ if (parentItem && getPrivate(*parentItem)->providesPalette()) {
+ return getPrivate(*parentItem)->palette()->toQPalette();
+ }
}
}
@@ -279,7 +282,7 @@ const QQuickItem* rootItem(const I &item)
{
if constexpr (isRootWindow<I>()) {
return item.contentItem();
- } else if constexpr (std::is_same_v<QQuickPopup, I>) {
+ } else if constexpr (std::is_base_of_v<QQuickPopup, I>) {
return nullptr;
} else {
return &item;
@@ -342,9 +345,12 @@ void QQuickPaletteProviderPrivateBase<I, Impl>::connectItem()
if constexpr (!isRootWindow<I>()) {
// Item with palette has the same lifetime as its implementation that inherits this class
- I::connect(itemWithPalette(), &I::parentChanged , [this]() { inheritPalette(parentPalette(defaultPalette())); });
- I::connect(itemWithPalette(), &I::windowChanged , [this]() { inheritPalette(parentPalette(defaultPalette())); });
- I::connect(itemWithPalette(), &I::enabledChanged, [this]() { setCurrentColorGroup(); });
+ I::connect(itemWithPalette(), &I::parentChanged,
+ itemWithPalette(), [this]() { inheritPalette(parentPalette(defaultPalette())); });
+ I::connect(itemWithPalette(), &I::windowChanged,
+ itemWithPalette(), [this]() { inheritPalette(parentPalette(defaultPalette())); });
+ I::connect(itemWithPalette(), &I::enabledChanged,
+ itemWithPalette(), [this]() { setCurrentColorGroup(); });
}
}
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 6de81470c4..3fa4125a1d 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -10,7 +10,7 @@
#include <private/qqmlglobal_p.h>
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmlchangeset_p.h>
-#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
@@ -26,10 +26,11 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemViewDelegateLifecycle)
+#if !QT_CONFIG(quick_itemview)
+Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
+#endif
Q_LOGGING_CATEGORY(lcPathView, "qt.quick.pathview")
-static const qreal MinimumFlickVelocity = 75;
-
static QQmlOpenMetaObjectType *qPathViewAttachedType = nullptr;
QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent)
@@ -64,9 +65,9 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
, inRefill(false)
, dragMargin(0), deceleration(100)
- , maximumFlickVelocity(QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::FlickMaximumVelocity).toReal())
+ , maximumFlickVelocity(QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::FlickMaximumVelocity).toReal())
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
- , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
+ , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedCacheSize(0), requestedZ(0)
, moveReason(Other), movementDirection(QQuickPathView::Shortest), moveDirection(QQuickPathView::Shortest)
, attType(nullptr), highlightComponent(nullptr), highlightItem(nullptr)
, moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition)
@@ -75,6 +76,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, highlightRangeMode(QQuickPathView::StrictlyEnforceRange)
, highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap)
{
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickPathViewPrivate::init()
@@ -207,10 +209,7 @@ QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
void QQuickPathViewPrivate::clear()
{
- if (currentItem) {
- releaseItem(currentItem);
- currentItem = nullptr;
- }
+ releaseCurrentItem();
for (QQuickItem *p : std::as_const(items))
releaseItem(p);
@@ -231,6 +230,10 @@ void QQuickPathViewPrivate::clear()
void QQuickPathViewPrivate::updateMappedRange()
{
+ // Update the actual cache size to be at max
+ // the available non-visible items.
+ cacheSize = qMax(0, qMin(requestedCacheSize, modelCount - pathItems));
+
if (model && pathItems != -1 && pathItems < modelCount) {
mappedRange = qreal(modelCount)/pathItems;
mappedCache = qreal(cacheSize)/pathItems/2; // Half of cache at each end
@@ -265,9 +268,10 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const
// returns true if position is between lower and upper, taking into
// account the circular space.
-bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const
+bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower,
+ qreal upper, bool emptyRangeCheck) const
{
- if (qFuzzyCompare(lower, upper))
+ if (emptyRangeCheck && qFuzzyCompare(lower, upper))
return true;
if (lower > upper) {
if (position > upper && position > lower)
@@ -530,6 +534,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty PathView QtQuick::PathView::view
+ \readonly
+
This attached property holds the view that manages this delegate instance.
It is attached to each instance of the delegate.
@@ -537,6 +543,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::onPath
+ \readonly
+
This attached property holds whether the item is currently on the path.
If a pathItemCount has been set, it is possible that some items may
@@ -557,6 +565,8 @@ QQuickPathView::~QQuickPathView()
/*!
\qmlattachedproperty bool QtQuick::PathView::isCurrentItem
+ \readonly
+
This attached property is true if this delegate is the current item; otherwise false.
It is attached to each instance of the delegate.
@@ -725,14 +735,13 @@ void QQuickPathView::setCurrentIndex(int idx)
? ((idx % d->modelCount) + d->modelCount) % d->modelCount
: 0;
if (d->model && (idx != d->currentIndex || !d->currentItem)) {
- if (d->currentItem) {
+ const bool hadCurrentItem = d->currentItem != nullptr;
+ const int oldCurrentIdx = d->currentIndex;
+ if (hadCurrentItem) {
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(false);
- d->releaseItem(d->currentItem);
+ d->releaseCurrentItem();
}
- int oldCurrentIdx = d->currentIndex;
- QQuickItem *oldCurrentItem = d->currentItem;
- d->currentItem = nullptr;
d->moveReason = QQuickPathViewPrivate::SetIndex;
d->currentIndex = idx;
if (d->modelCount) {
@@ -744,7 +753,7 @@ void QQuickPathView::setCurrentIndex(int idx)
}
if (oldCurrentIdx != d->currentIndex)
emit currentIndexChanged();
- if (oldCurrentItem != d->currentItem)
+ if (hadCurrentItem)
emit currentItemChanged();
}
}
@@ -894,18 +903,16 @@ QQuickItem *QQuickPathView::highlightItem() const
Valid values for \c highlightRangeMode are:
- \list
- \li \e PathView.NoHighlightRange - no range is applied and the
- highlight will move freely within the view.
- \li \e PathView.ApplyRange - the view will attempt to maintain
- the highlight within the range, however the highlight can
- move outside of the range at the ends of the path or due to
- a mouse interaction.
- \li \e PathView.StrictlyEnforceRange - the highlight will never
- move outside of the range. This means that the current item
- will change if a keyboard or mouse action would cause the
- highlight to move outside of the range.
- \endlist
+ \value PathView.NoHighlightRange no range is applied: the highlight
+ will move freely within the view.
+ \value PathView.ApplyRange the view will attempt to maintain the highlight
+ within the range, however the highlight can move
+ outside of the range at the ends of the path or
+ due to a mouse interaction.
+ \value PathView.StrictlyEnforceRange the highlight will never move outside of the range.
+ This means that the current item will change if a
+ keyboard or mouse action would cause the highlight
+ to move outside of the range.
The default value is \e PathView.StrictlyEnforceRange.
@@ -1291,16 +1298,16 @@ void QQuickPathView::resetPathItemCount()
int QQuickPathView::cacheItemCount() const
{
Q_D(const QQuickPathView);
- return d->cacheSize;
+ return d->requestedCacheSize;
}
void QQuickPathView::setCacheItemCount(int i)
{
Q_D(QQuickPathView);
- if (i == d->cacheSize || i < 0)
+ if (i == d->requestedCacheSize || i < 0)
return;
- d->cacheSize = i;
+ d->requestedCacheSize = i;
d->updateMappedRange();
refill();
emit cacheItemCountChanged();
@@ -1312,13 +1319,11 @@ void QQuickPathView::setCacheItemCount(int i)
This property determines how the items will settle following a drag or flick.
The possible values are:
- \list
- \li PathView.NoSnap (default) - the items stop anywhere along the path.
- \li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin.
- \li PathView.SnapOneItem - the items settle no more than one item away from the item nearest
+ \value PathView.NoSnap (default) the items stop anywhere along the path.
+ \value PathView.SnapToItem the items settle with an item aligned with the \l preferredHighlightBegin.
+ \value PathView.SnapOneItem the items settle no more than one item away from the item nearest
\l preferredHighlightBegin at the time the press is released. This mode is particularly
useful for moving one page at a time.
- \endlist
\c snapMode does not affect the \l currentIndex. To update the
\l currentIndex as the view is moved, set \l highlightRangeMode
@@ -1348,12 +1353,10 @@ void QQuickPathView::setSnapMode(SnapMode mode)
This property determines the direction in which items move when setting the current index.
The possible values are:
- \list
- \li PathView.Shortest (default) - the items move in the direction that requires the least
- movement, which could be either \c Negative or \c Positive.
- \li PathView.Negative - the items move backwards towards their destination.
- \li PathView.Positive - the items move forwards towards their destination.
- \endlist
+ \value PathView.Shortest (default) the items move in the direction that requires the least
+ movement, which could be either \c Negative or \c Positive.
+ \value PathView.Negative the items move backwards towards their destination.
+ \value PathView.Positive the items move forwards towards their destination.
For example, suppose that there are 5 items in the model, and \l currentIndex is \c 0.
If currentIndex is set to \c 2,
@@ -1389,15 +1392,13 @@ void QQuickPathView::setMovementDirection(QQuickPathView::MovementDirection dir)
Positions the view such that the \a index is at the position specified by
\a mode:
- \list
- \li PathView.Beginning - position item at the beginning of the path.
- \li PathView.Center - position item in the center of the path.
- \li PathView.End - position item at the end of the path.
- \li PathView.Contain - ensure the item is positioned on the path.
- \li PathView.SnapPosition - position the item at \l preferredHighlightBegin. This mode
- is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
- via \l snapMode.
- \endlist
+ \value PathView.Beginning position item at the beginning of the path.
+ \value PathView.Center position item in the center of the path.
+ \value PathView.End position item at the end of the path.
+ \value PathView.Contain ensure the item is positioned on the path.
+ \value PathView.SnapPosition position the item at \l preferredHighlightBegin. This mode
+ is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
+ via \l snapMode.
\b Note: methods should only be called after the Component has completed. To position
the view at startup, this method should be called by Component.onCompleted. For
@@ -1510,7 +1511,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
}
/*!
- \qmlmethod Item QtQuick::QQuickPathView::itemAtIndex(int index)
+ \qmlmethod Item QtQuick::PathView::itemAtIndex(int index)
Returns the item for \a index. If there is no item for that index, for example
because it has not been created yet, or because it has been panned out of
@@ -1536,6 +1537,14 @@ QQuickItem *QQuickPathView::itemAtIndex(int index) const
return nullptr;
}
+/*!
+ \internal
+
+ Returns a point in the path, that has the closest distance from \a point.
+ A value in the range 0-1 will be written to \a nearPercent if given, which
+ represents where on the path the \a point is closest to. \c 0 means the very
+ beginning of the path, and \c 1 means the very end.
+*/
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
const auto pathLength = path->path().length();
@@ -1751,7 +1760,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *event)
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
const auto averageItemLength = path->path().length() / count;
qreal pixelVelocity = averageItemLength * velocity;
- if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
+ if (qAbs(pixelVelocity) > _q_MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
@@ -1855,7 +1864,8 @@ bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e)
const bool filtered = stealThisEvent || grabberDisabled;
if (filtered)
- pe->setAccepted(false);
+ pe->setAccepted(stealThisEvent && grabber == this && grabber->isEnabled());
+
return filtered;
} else if (d->timer.isValid()) {
d->timer.invalidate();
@@ -2011,6 +2021,7 @@ void QQuickPathView::refill()
startPos = d->highlightRangeStart;
// With no items, then "end" is just off the top so we populate via append
endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount;
+ endIdx = qMax(-1, endIdx); // endIdx shouldn't be smaller than -1
endPos = d->positionOfIndex(endIdx);
}
//Append
@@ -2018,8 +2029,8 @@ void QQuickPathView::refill()
if (idx >= d->modelCount)
idx = 0;
qreal nextPos = d->positionOfIndex(idx);
- while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache) || !d->items.size())
- && d->items.size() < count+d->cacheSize) {
+ while ((d->isInBound(nextPos, endPos, 1 + d->mappedCache, false) || !d->items.size())
+ && d->items.size() < count + d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.size();
QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1);
if (!item) {
@@ -2193,8 +2204,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
} else if (d->currentItem) {
if (QQuickPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(true);
- d->releaseItem(d->currentItem);
- d->currentItem = nullptr;
+ d->releaseCurrentItem();
}
d->currentIndex = qMin(r.index, d->modelCount - r.count - 1);
currentChanged = true;
@@ -2342,7 +2352,7 @@ void QQuickPathViewPrivate::updateCurrent()
if (currentItem) {
if (QQuickPathViewAttached *att = attached(currentItem))
att->setIsCurrentItem(false);
- releaseItem(currentItem);
+ releaseCurrentItem();
}
int oldCurrentIndex = currentIndex;
currentIndex = idx;
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index da207bbf29..187ebad65a 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -30,7 +30,7 @@ class QQmlChangeSet;
class QQuickPathViewPrivate;
class QQuickPathViewAttached;
-class Q_QUICK_PRIVATE_EXPORT QQuickPathView : public QQuickItem
+class Q_QUICK_EXPORT QQuickPathView : public QQuickItem
{
Q_OBJECT
@@ -218,9 +218,9 @@ class QQuickPathViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPathView *view READ view CONSTANT)
- Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
- Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged)
+ Q_PROPERTY(QQuickPathView *view READ view CONSTANT FINAL)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged FINAL)
+ Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged FINAL)
public:
QQuickPathViewAttached(QObject *parent);
@@ -264,6 +264,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPathView)
-
#endif // QQUICKPATHVIEW_P_H
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index 61c0b2ce62..f23b03f6c2 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -31,6 +31,8 @@ QT_REQUIRE_CONFIG(quick_pathview);
#include <private/qquicktimeline_p_p.h>
#include <private/qpodvector_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlOpenMetaObjectType;
@@ -67,13 +69,18 @@ public:
}
QQuickItem *getItem(int modelIndex, qreal z = 0, bool async=false);
+ void releaseCurrentItem()
+ {
+ auto oldCurrentItem = std::exchange(currentItem, nullptr);
+ releaseItem(oldCurrentItem);
+ }
void releaseItem(QQuickItem *item);
QQuickPathViewAttached *attached(QQuickItem *item);
QQmlOpenMetaObjectType *attachedType();
void clear();
void updateMappedRange();
qreal positionOfIndex(qreal index) const;
- bool isInBound(qreal position, qreal lower, qreal upper) const;
+ bool isInBound(qreal position, qreal lower, qreal upper, bool emptyRangeCheck = true) const;
void createHighlight();
void updateHighlight();
void setHighlightPosition(qreal pos);
@@ -138,6 +145,7 @@ public:
int pathItems;
int requestedIndex;
int cacheSize;
+ int requestedCacheSize;
qreal requestedZ;
QList<QQuickItem *> items;
QList<QQuickItem *> itemCache;
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index d547fae6c9..482941b9ba 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -247,7 +247,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
Q_D(QQuickPinchArea);
d->init();
setAcceptTouchEvents(true);
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
}
diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h
index 5e08aa3eeb..9ea16f3ea4 100644
--- a/src/quick/items/qquickpincharea_p.h
+++ b/src/quick/items/qquickpincharea_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPinch : public QObject
+class Q_QUICK_EXPORT QQuickPinch : public QObject
{
Q_OBJECT
@@ -160,24 +160,24 @@ private:
bool m_active;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPinchEvent : public QObject
+class Q_QUICK_EXPORT QQuickPinchEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QPointF center READ center)
- Q_PROPERTY(QPointF startCenter READ startCenter)
- Q_PROPERTY(QPointF previousCenter READ previousCenter)
- Q_PROPERTY(qreal scale READ scale)
- Q_PROPERTY(qreal previousScale READ previousScale)
- Q_PROPERTY(qreal angle READ angle)
- Q_PROPERTY(qreal previousAngle READ previousAngle)
- Q_PROPERTY(qreal rotation READ rotation)
- Q_PROPERTY(QPointF point1 READ point1)
- Q_PROPERTY(QPointF startPoint1 READ startPoint1)
- Q_PROPERTY(QPointF point2 READ point2)
- Q_PROPERTY(QPointF startPoint2 READ startPoint2)
- Q_PROPERTY(int pointCount READ pointCount)
- Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
+ Q_PROPERTY(QPointF center READ center FINAL)
+ Q_PROPERTY(QPointF startCenter READ startCenter FINAL)
+ Q_PROPERTY(QPointF previousCenter READ previousCenter FINAL)
+ Q_PROPERTY(qreal scale READ scale FINAL)
+ Q_PROPERTY(qreal previousScale READ previousScale FINAL)
+ Q_PROPERTY(qreal angle READ angle FINAL)
+ Q_PROPERTY(qreal previousAngle READ previousAngle FINAL)
+ Q_PROPERTY(qreal rotation READ rotation FINAL)
+ Q_PROPERTY(QPointF point1 READ point1 FINAL)
+ Q_PROPERTY(QPointF startPoint1 READ startPoint1 FINAL)
+ Q_PROPERTY(QPointF point2 READ point2 FINAL)
+ Q_PROPERTY(QPointF startPoint2 READ startPoint2 FINAL)
+ Q_PROPERTY(int pointCount READ pointCount FINAL)
+ Q_PROPERTY(bool accepted READ accepted WRITE setAccepted FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -230,9 +230,8 @@ private:
};
-class QQuickMouseEvent;
class QQuickPinchAreaPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickPinchArea : public QQuickItem
+class Q_QUICK_EXPORT QQuickPinchArea : public QQuickItem
{
Q_OBJECT
@@ -278,9 +277,5 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPinch)
-QML_DECLARE_TYPE(QQuickPinchEvent)
-QML_DECLARE_TYPE(QQuickPinchArea)
-
#endif // QQUICKPINCHAREA_H
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index 5bb1f4f6d9..ad5447df6f 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -12,7 +12,7 @@
QT_BEGIN_NAMESPACE
-static const QQuickItemPrivate::ChangeTypes watchedChanges
+static const QQuickItemPrivate::ChangeTypes positionerWatchedChanges
= QQuickItemPrivate::Geometry
| QQuickItemPrivate::SiblingOrder
| QQuickItemPrivate::Visibility
@@ -21,19 +21,21 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other)
{
QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
- otherPrivate->addItemChangeListener(this, watchedChanges);
+ otherPrivate->addItemChangeListener(this, positionerWatchedChanges);
}
void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other)
{
QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
- otherPrivate->removeItemChangeListener(this, watchedChanges);
+ otherPrivate->removeItemChangeListener(this, positionerWatchedChanges);
}
QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i)
: item(i)
+#if QT_CONFIG(quick_viewtransitions)
, transitionableItem(nullptr)
+#endif
, index(-1)
, isNew(false)
, isVisible(true)
@@ -46,27 +48,40 @@ QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i)
QQuickBasePositioner::PositionedItem::~PositionedItem()
{
+#if QT_CONFIG(quick_viewtransitions)
delete transitionableItem;
+#endif
}
qreal QQuickBasePositioner::PositionedItem::itemX() const
{
- return transitionableItem ? transitionableItem->itemX() : item->x();
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemX() :
+#endif
+ item->x();
}
qreal QQuickBasePositioner::PositionedItem::itemY() const
{
- return transitionableItem ? transitionableItem->itemY() : item->y();
+ return
+#if QT_CONFIG(quick_viewtransitions)
+ transitionableItem ? transitionableItem->itemY() :
+#endif
+ item->y();
}
void QQuickBasePositioner::PositionedItem::moveTo(const QPointF &pos)
{
+#if QT_CONFIG(quick_viewtransitions)
if (transitionableItem)
transitionableItem->moveTo(pos);
else
+#endif
item->setPosition(pos);
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickBasePositioner::PositionedItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
{
if (!transitioner)
@@ -86,6 +101,7 @@ void QQuickBasePositioner::PositionedItem::startTransition(QQuickItemViewTransit
if (transitionableItem)
transitionableItem->startTransition(transitioner, index);
}
+#endif
void QQuickBasePositioner::PositionedItem::updatePadding(qreal lp, qreal tp, qreal rp, qreal bp)
{
@@ -132,7 +148,9 @@ QQuickBasePositioner::QQuickBasePositioner(QQuickBasePositionerPrivate &dd, Posi
QQuickBasePositioner::~QQuickBasePositioner()
{
Q_D(QQuickBasePositioner);
+#if QT_CONFIG(quick_viewtransitions)
delete d->transitioner;
+#endif
for (int i = 0; i < positionedItems.count(); ++i)
d->unwatchChanges(positionedItems.at(i).item);
for (int i = 0; i < unpositionedItems.count(); ++i)
@@ -164,6 +182,7 @@ void QQuickBasePositioner::setSpacing(qreal s)
emit spacingChanged();
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *QQuickBasePositioner::populate() const
{
Q_D(const QQuickBasePositioner);
@@ -216,17 +235,24 @@ void QQuickBasePositioner::setAdd(QQuickTransition *add)
d->transitioner->addTransition = add;
emit addChanged();
}
+#endif
void QQuickBasePositioner::componentComplete()
{
+#if QT_CONFIG(quick_viewtransitions)
Q_D(QQuickBasePositioner);
+#endif
QQuickItem::componentComplete();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(true);
+#endif
positionedItems.reserve(childItems().size());
prePositioning();
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner)
d->transitioner->setPopulateTransitionEnabled(false);
+#endif
}
void QQuickBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
@@ -275,7 +301,9 @@ void QQuickBasePositioner::prePositioning()
for (int ii = 0; ii < unpositionedItems.count(); ii++)
oldItems.append(unpositionedItems[ii]);
unpositionedItems.clear();
+#if QT_CONFIG(quick_viewtransitions)
int addedIndex = -1;
+#endif
for (int ii = 0; ii < children.size(); ++ii) {
QQuickItem *child = children.at(ii);
@@ -295,6 +323,7 @@ void QQuickBasePositioner::prePositioning()
posItem.index = positionedItems.count();
positionedItems.append(posItem);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
if (addedIndex < 0)
addedIndex = posItem.index;
@@ -304,6 +333,7 @@ void QQuickBasePositioner::prePositioning()
else if (!d->transitioner->populateTransitionEnabled())
theItem->transitionNextReposition(d->transitioner, QQuickItemViewTransitioner::AddTransition, true);
}
+#endif
}
} else {
PositionedItem *item = &oldItems[wIdx];
@@ -320,11 +350,13 @@ void QQuickBasePositioner::prePositioning()
item->index = positionedItems.count();
positionedItems.append(*item);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
if (addedIndex < 0)
addedIndex = item->index;
positionedItems[positionedItems.count()-1].transitionNextReposition(d->transitioner, QQuickItemViewTransitioner::AddTransition, true);
}
+#endif
} else {
item->isNew = false;
item->index = positionedItems.count();
@@ -333,6 +365,7 @@ void QQuickBasePositioner::prePositioning()
}
}
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
for (int i=0; i<positionedItems.count(); i++) {
if (!positionedItems[i].isNew) {
@@ -346,6 +379,7 @@ void QQuickBasePositioner::prePositioning()
}
}
}
+#endif
QSizeF contentSize(0,0);
reportConflictingAnchors();
@@ -354,6 +388,7 @@ void QQuickBasePositioner::prePositioning()
updateAttachedProperties();
}
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
QRectF viewBounds(QPointF(), contentSize);
for (int i=0; i<positionedItems.count(); i++)
@@ -362,6 +397,7 @@ void QQuickBasePositioner::prePositioning()
positionedItems[i].startTransition(d->transitioner);
d->transitioner->resetTargetLists();
}
+#endif
d->doingPositioning = false;
@@ -403,13 +439,17 @@ void QQuickBasePositioner::positionItemY(qreal y, PositionedItem *target)
void QQuickBasePositioner::removePositionedItem(QPODVector<PositionedItem,8> *items, int index)
{
Q_ASSERT(index >= 0 && index < items->count());
+#if QT_CONFIG(quick_viewtransitions)
delete items->at(index).transitionableItem;
+#endif
items->remove(index);
}
void QQuickBasePositioner::clearPositionedItems(QPODVector<PositionedItem,8> *items)
{
+#if QT_CONFIG(quick_viewtransitions)
for (int i=0; i<items->count(); i++)
delete items->at(i).transitionableItem;
+#endif
items->clear();
}
@@ -1111,14 +1151,12 @@ QQuickRow::QQuickRow(QQuickItem *parent)
Possible values:
- \list
- \li Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set,
- the left anchor remains to the left of the row.
- \li Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set,
- the right anchor remains to the right of the row.
- \endlist
+ \value Qt.LeftToRight (default) Items are laid out from left to right. If the width of the row is
+ explicitly set, the left anchor remains to the left of the row.
+ \value Qt.RightToLeft Items are laid out from right to left. If the width of the row is
+ explicitly set, the right anchor remains to the right of the row.
- \sa Grid::layoutDirection, Flow::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Grid::layoutDirection, Flow::layoutDirection
*/
Qt::LayoutDirection QQuickRow::layoutDirection() const
@@ -1531,7 +1569,7 @@ void QQuickGrid::setColumnSpacing(const qreal columnSpacing)
\l Grid::flow property.
\endlist
- \sa Flow::layoutDirection, Row::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Flow::layoutDirection, Row::layoutDirection
*/
Qt::LayoutDirection QQuickGrid::layoutDirection() const
{
@@ -2031,7 +2069,7 @@ void QQuickFlow::setFlow(Flow flow)
\l Flow::flow property.
\endlist
- \sa Grid::layoutDirection, Row::layoutDirection, {Qt Quick Examples - Right to Left}
+ \sa Grid::layoutDirection, Row::layoutDirection
*/
Qt::LayoutDirection QQuickFlow::layoutDirection() const
diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h
index 42ab3bc6a7..02a368882f 100644
--- a/src/quick/items/qquickpositioners_p.h
+++ b/src/quick/items/qquickpositioners_p.h
@@ -20,7 +20,9 @@
QT_REQUIRE_CONFIG(quick_positioners);
#include "qquickimplicitsizeitem_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
+#endif
#include <private/qpodvector_p.h>
@@ -38,9 +40,9 @@ class QQuickPositionerAttached : public QObject
public:
QQuickPositionerAttached(QObject *parent);
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
- Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged)
- Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged)
+ Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
+ Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged FINAL)
+ Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged FINAL)
int index() const { return m_index; }
void setIndex(int index);
@@ -62,14 +64,16 @@ private:
bool m_isLastItem;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickBasePositioner : public QQuickImplicitSizeItem
+class Q_QUICK_EXPORT QQuickBasePositioner : public QQuickImplicitSizeItem
{
Q_OBJECT
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *populate READ populate WRITE setPopulate NOTIFY populateChanged)
Q_PROPERTY(QQuickTransition *move READ move WRITE setMove NOTIFY moveChanged)
Q_PROPERTY(QQuickTransition *add READ add WRITE setAdd NOTIFY addChanged)
+#endif
Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION(2, 6))
Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION(2, 6))
@@ -91,6 +95,7 @@ public:
qreal spacing() const;
void setSpacing(qreal);
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *populate() const;
void setPopulate(QQuickTransition *);
@@ -99,6 +104,7 @@ public:
QQuickTransition *add() const;
void setAdd(QQuickTransition *);
+#endif
static QQuickPositionerAttached *qmlAttachedProperties(QObject *obj);
@@ -164,14 +170,18 @@ protected:
void moveTo(const QPointF &pos);
+#if QT_CONFIG(quick_viewtransitions)
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner);
+#endif
void updatePadding(qreal lp, qreal tp, qreal rp, qreal bp);
QQuickItem *item;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitionableItem *transitionableItem;
+#endif
int index;
bool isNew;
bool isVisible;
@@ -197,7 +207,7 @@ private:
Q_DECLARE_PRIVATE(QQuickBasePositioner)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickColumn : public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickColumn : public QQuickBasePositioner
{
Q_OBJECT
QML_NAMED_ELEMENT(Column)
@@ -211,7 +221,7 @@ protected:
};
class QQuickRowPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRow: public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickRow: public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
@@ -238,7 +248,7 @@ private:
};
class QQuickGridPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickGrid : public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickGrid : public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
@@ -326,7 +336,7 @@ private:
};
class QQuickFlowPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickFlow: public QQuickBasePositioner
+class Q_QUICK_EXPORT QQuickFlow: public QQuickBasePositioner
{
Q_OBJECT
Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
@@ -360,14 +370,6 @@ private:
Q_DECLARE_PRIVATE(QQuickFlow)
};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickColumn)
-QML_DECLARE_TYPE(QQuickRow)
-QML_DECLARE_TYPE(QQuickGrid)
-QML_DECLARE_TYPE(QQuickFlow)
-
-QML_DECLARE_TYPE(QQuickBasePositioner)
-
#endif // QQUICKPOSITIONERS_P_H
diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h
index f91b093684..004a0f5ed1 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -54,7 +54,10 @@ public:
QQuickBasePositionerPrivate()
: spacing(0), type(QQuickBasePositioner::None)
- , transitioner(0), positioningDirty(false)
+#if QT_CONFIG(quick_viewtransitions)
+ , transitioner(0)
+#endif
+ , positioningDirty(false)
, doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight)
{
@@ -68,7 +71,9 @@ public:
qreal spacing;
QQuickBasePositioner::PositionerType type;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner;
+#endif
void watchChanges(QQuickItem *other);
void unwatchChanges(QQuickItem* other);
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 7037526264..403d0c1034 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -229,10 +229,9 @@ QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
\since 5.12
Set this property to define the direction of the gradient.
- \list
- \li Gradient.Vertical - a vertical gradient
- \li Gradient.Horizontal - a horizontal gradient
- \endlist
+
+ \value Gradient.Vertical a vertical gradient
+ \value Gradient.Horizontal a horizontal gradient
The default is Gradient.Vertical.
*/
@@ -265,6 +264,17 @@ void QQuickGradient::doUpdate()
int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
+void QQuickRectanglePrivate::maybeSetImplicitAntialiasing()
+{
+ bool implicitAA = (radius != 0);
+ if (extraRectangle.isAllocated() && !implicitAA) {
+ implicitAA = extraRectangle.value().topLeftRadius > 0.0
+ || extraRectangle.value().topRightRadius > 0.0
+ || extraRectangle.value().bottomLeftRadius > 0.0
+ || extraRectangle.value().bottomRightRadius > 0.0;
+ }
+ setImplicitAntialiasing(implicitAA);
+}
/*!
\qmltype Rectangle
\instantiates QQuickRectangle
@@ -289,7 +299,10 @@ int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
You can also create rounded rectangles using the \l radius property. Since this
introduces curved edges to the corners of a rectangle, it may be appropriate to
- set the \l Item::antialiasing property to improve its appearance.
+ set the \l Item::antialiasing property to improve its appearance. To set the
+ radii individually for different corners, you can use the properties
+ \l topLeftRadius, \l topRightRadius, \l bottomLeftRadius and
+ \l bottomRightRadius.
\section1 Example Usage
@@ -340,6 +353,7 @@ void QQuickRectangle::doUpdate()
\qmlpropertygroup QtQuick::Rectangle::border
\qmlproperty int QtQuick::Rectangle::border.width
\qmlproperty color QtQuick::Rectangle::border.color
+ \qmlproperty bool QtQuick::Rectangle::border.pixelAligned
The width and color used to draw the border of the rectangle.
@@ -349,6 +363,10 @@ void QQuickRectangle::doUpdate()
rectangle itself or its position relative to other items if anchors are used.
The border is rendered within the rectangle's boundaries.
+
+ If \c pixelAligned is \c true (the default), the rendered border width is rounded to a whole
+ number of pixels, after device pixel ratio scaling. Setting \c pixelAligned to \c false will
+ allow fractional border widths, which may be desirable when \c antialiasing is enabled.
*/
QQuickPen *QQuickRectangle::border()
{
@@ -463,9 +481,12 @@ void QQuickRectangle::resetGradient()
\qmlproperty real QtQuick::Rectangle::radius
This property holds the corner radius used to draw a rounded rectangle.
- If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
- painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
- no way to specify different radii for different corners.
+ If radius is non-zero, the rectangle will be painted as a rounded rectangle,
+ otherwise it will be painted as a normal rectangle. Individual corner radii
+ can be set as well (see below). These values will override \l radius. If
+ they are unset (by setting them to \c undefined), \l radius will be used instead.
+
+ \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius
*/
qreal QQuickRectangle::radius() const
{
@@ -480,10 +501,240 @@ void QQuickRectangle::setRadius(qreal radius)
return;
d->radius = radius;
- d->setImplicitAntialiasing(radius != 0.0);
+ d->maybeSetImplicitAntialiasing();
update();
emit radiusChanged();
+
+ if (d->extraRectangle.isAllocated()) {
+ if (d->extraRectangle->topLeftRadius < 0.)
+ emit topLeftRadiusChanged();
+ if (d->extraRectangle->topRightRadius < 0.)
+ emit topRightRadiusChanged();
+ if (d->extraRectangle->bottomLeftRadius < 0.)
+ emit bottomLeftRadiusChanged();
+ if (d->extraRectangle->bottomRightRadius < 0.)
+ emit bottomRightRadiusChanged();
+ } else {
+ emit topLeftRadiusChanged();
+ emit topRightRadiusChanged();
+ emit bottomLeftRadiusChanged();
+ emit bottomRightRadiusChanged();
+ }
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::topLeftRadius
+ This property holds the radius used to draw the top left corner.
+
+ If \l topLeftRadius is not set, \l radius will be used instead.
+ If \l topLeftRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::topLeftRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->topLeftRadius >= 0.)
+ return d->extraRectangle.value().topLeftRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setTopLeftRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().topLeftRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "topLeftRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+ d->extraRectangle.value().topLeftRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topLeftRadiusChanged();
+}
+
+void QQuickRectangle::resetTopLeftRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().topLeftRadius < 0)
+ return;
+
+ d->extraRectangle.value().topLeftRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topLeftRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::topRightRadius
+ This property holds the radius used to draw the top right corner.
+
+ If \l topRightRadius is not set, \l radius will be used instead.
+ If \l topRightRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::topRightRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->topRightRadius >= 0.)
+ return d->extraRectangle.value().topRightRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setTopRightRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().topRightRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "topRightRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+ d->extraRectangle.value().topRightRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topRightRadiusChanged();
+}
+
+void QQuickRectangle::resetTopRightRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().topRightRadius < 0)
+ return;
+
+ d->extraRectangle.value().topRightRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit topRightRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::bottomLeftRadius
+ This property holds the radius used to draw the bottom left corner.
+
+ If \l bottomLeftRadius is not set, \l radius will be used instead.
+ If \l bottomLeftRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, topRightRadius, bottomRightRadius
+*/
+qreal QQuickRectangle::bottomLeftRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomLeftRadius >= 0.)
+ return d->extraRectangle.value().bottomLeftRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setBottomLeftRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().bottomLeftRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "bottomLeftRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+
+ d->extraRectangle.value().bottomLeftRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomLeftRadiusChanged();
+}
+
+void QQuickRectangle::resetBottomLeftRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().bottomLeftRadius < 0)
+ return;
+
+ d->extraRectangle.value().bottomLeftRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomLeftRadiusChanged();
+}
+
+/*!
+ \since 6.7
+ \qmlproperty real QtQuick::Rectangle::bottomRightRadius
+ This property holds the radius used to draw the bottom right corner.
+
+ If \l bottomRightRadius is not set, \l radius will be used instead.
+ If \l bottomRightRadius is zero, the corner will be sharp.
+
+ \note This API is considered tech preview and may change or be removed in
+ future versions of Qt.
+
+ \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius
+*/
+qreal QQuickRectangle::bottomRightRadius() const
+{
+ Q_D(const QQuickRectangle);
+ if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomRightRadius >= 0.)
+ return d->extraRectangle.value().bottomRightRadius;
+ return d->radius;
+}
+
+void QQuickRectangle::setBottomRightRadius(qreal radius)
+{
+ Q_D(QQuickRectangle);
+ if (d->extraRectangle.value().bottomRightRadius == radius)
+ return;
+
+ if (radius < 0) { // use the fact that radius < 0 resets the radius.
+ qmlWarning(this) << "bottomRightRadius (" << radius << ") cannot be less than 0.";
+ return;
+ }
+
+ d->extraRectangle.value().bottomRightRadius = radius;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomRightRadiusChanged();
+}
+
+void QQuickRectangle::resetBottomRightRadius()
+{
+ Q_D(QQuickRectangle);
+ if (!d->extraRectangle.isAllocated())
+ return;
+ if (d->extraRectangle.value().bottomRightRadius < 0)
+ return;
+
+ d->extraRectangle.value().bottomRightRadius = -1.;
+ d->maybeSetImplicitAntialiasing();
+
+ update();
+ emit bottomRightRadiusChanged();
}
/*!
@@ -554,6 +805,17 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
}
rectangle->setRadius(d->radius);
+ if (d->extraRectangle.isAllocated()) {
+ rectangle->setTopLeftRadius(d->extraRectangle.value().topLeftRadius);
+ rectangle->setTopRightRadius(d->extraRectangle.value().topRightRadius);
+ rectangle->setBottomLeftRadius(d->extraRectangle.value().bottomLeftRadius);
+ rectangle->setBottomRightRadius(d->extraRectangle.value().bottomRightRadius);
+ } else {
+ rectangle->setTopLeftRadius(-1.);
+ rectangle->setTopRightRadius(-1.);
+ rectangle->setBottomLeftRadius(-1.);
+ rectangle->setBottomRightRadius(-1.);
+ }
rectangle->setAntialiasing(antialiasing());
QGradientStops stops;
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index 9e5b530382..690154cd65 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -23,13 +23,13 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickPen : public QObject
+class Q_QUICK_EXPORT QQuickPen : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged)
- Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
- Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged FINAL)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
+ Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -58,7 +58,7 @@ private:
bool m_valid : 1;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGradientStop : public QObject
+class Q_QUICK_EXPORT QQuickGradientStop : public QObject
{
Q_OBJECT
@@ -84,7 +84,7 @@ private:
QColor m_color;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGradient : public QObject
+class Q_QUICK_EXPORT QQuickGradient : public QObject
{
Q_OBJECT
@@ -125,7 +125,7 @@ private:
};
class QQuickRectanglePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
+class Q_QUICK_EXPORT QQuickRectangle : public QQuickItem
{
Q_OBJECT
@@ -133,6 +133,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
Q_PROPERTY(QJSValue gradient READ gradient WRITE setGradient RESET resetGradient)
Q_PROPERTY(QQuickPen * border READ border CONSTANT)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
+ Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged RESET resetTopLeftRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged RESET resetTopRightRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged RESET resetBottomLeftRadius REVISION(6, 7) FINAL)
+ Q_PROPERTY(qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged RESET resetBottomRightRadius REVISION(6, 7) FINAL)
QML_NAMED_ELEMENT(Rectangle)
QML_ADDED_IN_VERSION(2, 0)
public:
@@ -150,9 +154,26 @@ public:
qreal radius() const;
void setRadius(qreal radius);
+ qreal topLeftRadius() const;
+ void setTopLeftRadius(qreal radius);
+ void resetTopLeftRadius();
+ qreal topRightRadius() const;
+ void setTopRightRadius(qreal radius);
+ void resetTopRightRadius();
+ qreal bottomLeftRadius() const;
+ void setBottomLeftRadius(qreal radius);
+ void resetBottomLeftRadius();
+ qreal bottomRightRadius() const;
+ void setBottomRightRadius(qreal radius);
+ void resetBottomRightRadius();
+
Q_SIGNALS:
void colorChanged();
void radiusChanged();
+ Q_REVISION(6, 7) void topLeftRadiusChanged();
+ Q_REVISION(6, 7) void topRightRadiusChanged();
+ Q_REVISION(6, 7) void bottomLeftRadiusChanged();
+ Q_REVISION(6, 7) void bottomRightRadiusChanged();
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
@@ -167,9 +188,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPen)
-QML_DECLARE_TYPE(QQuickGradientStop)
-QML_DECLARE_TYPE(QQuickGradient)
-QML_DECLARE_TYPE(QQuickRectangle)
-
#endif // QQUICKRECTANGLE_P_H
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 64bf215bc7..435cea1e52 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -17,6 +17,7 @@
#include "qquickitem_p.h"
#include <QtCore/qmetaobject.h>
+#include <private/qlazilyallocated_p.h>
QT_BEGIN_NAMESPACE
@@ -40,7 +41,26 @@ public:
QJSValue gradient;
QQuickPen *pen;
qreal radius;
+
+ struct ExtraData {
+ ExtraData()
+ : topLeftRadius(-1.),
+ topRightRadius(-1.),
+ bottomLeftRadius(-1.),
+ bottomRightRadius(-1.)
+ {
+ }
+
+ qreal topLeftRadius;
+ qreal topRightRadius;
+ qreal bottomLeftRadius;
+ qreal bottomRightRadius;
+ };
+ QLazilyAllocated<ExtraData> extraRectangle;
+
static int doUpdateSlotIdx;
+
+ void maybeSetImplicitAntialiasing();
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 0575c5e5eb..3a92507141 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -26,7 +26,7 @@
#include <QtCore/private/qobject_p.h>
#include <QtQuick/private/qquickwindow_p.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
QT_BEGIN_NAMESPACE
@@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
Management of the graphics devices, contexts, image and texture objects is up
to the application. The device or context that will be used by Qt Quick must
- be created before calling initialize(). The creation of the the texture object
+ be created before calling initialize(). The creation of the texture object
can be deferred, see below. Qt 5.4 introduces the ability for QOpenGLContext
to adopt existing native contexts. Together with QQuickRenderControl this
makes it possible to create a QOpenGLContext that shares with an external
@@ -281,6 +281,17 @@ int QQuickRenderControl::samples() const
\note This function does not need to be, and must not be, called when using
the \c software adaptation of Qt Quick.
+ With the default Qt Quick adaptation this function creates a new \l QRhi
+ object, similarly to what would happen with an on-screen QQuickWindow when
+ QQuickRenderControl was not used. To make this new QRhi object adopt some
+ existing device or context resource (e.g. use an existing QOpenGLContext
+ instead of creating a new one), use QQuickWindow::setGraphicsDevice() as
+ mentioned above. When the application wants to make the Qt Quick rendering
+ use an already existing \l QRhi object, that is possible as well via
+ \l QQuickGraphicsDevice::fromRhi(). When such a QQuickGraphicsDevice,
+ referencing an already existing QRhi, is set, there will be no new,
+ dedicated \l QRhi object created in initialize().
+
\since 6.0
\sa QQuickRenderTarget, QQuickGraphicsDevice, QQuickGraphicsConfiguration::preferredInstanceExtensions()
@@ -357,7 +368,8 @@ bool QQuickRenderControl::sync()
return false;
}
if (!d->cb) {
- qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
+ qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided "
+ "(perhaps beginFrame() was not called or it was unsuccessful?)");
return false;
}
cd->setCustomCommandBuffer(d->cb);
@@ -549,6 +561,16 @@ bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const
return false;
}
+bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w)
+{
+ Q_Q(QQuickRenderControl);
+
+ if (window && w)
+ return q->renderWindowFor(window, nullptr) == w;
+
+ return false;
+}
+
/*!
\return the QQuickWindow this QQuickRenderControl is associated with.
@@ -565,6 +587,53 @@ QQuickWindow *QQuickRenderControl::window() const
}
/*!
+ \return the QRhi this QQuickRenderControl is associated with.
+
+ \note The QRhi exists only when initialize() has successfully completed.
+ Before that the return value is null.
+
+ \note This function is not applicable and returns null when using the
+ \c software adaptation of Qt Quick.
+
+ \since 6.6
+
+ \sa commandBuffer(), beginFrame(), endFrame()
+ */
+QRhi *QQuickRenderControl::rhi() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->rhi;
+}
+
+/*!
+ \return the current command buffer.
+
+ Once beginFrame() is called, a QRhiCommandBuffer is set up automatically.
+ That is the command buffer Qt Quick scenegraph uses, but in some cases
+ applications may also want to query it, for example to issue resource
+ updates (for example, a texture readback).
+
+ The returned command buffer reference should only be used between
+ beginFrame() and endFrame(). There are specific exceptions, for example
+ calling
+ \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on
+ the command buffer right after endFrame(), but before the next
+ beginFrame(), is valid.
+
+ \note This function is not applicable and returns null when using the
+ \c software adaptation of Qt Quick.
+
+ \since 6.6
+
+ \sa rhi(), beginFrame(), endFrame()
+ */
+QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->cb;
+}
+
+/*!
Specifies the start of a graphics frame. Calls to sync() or render() must
be enclosed by calls to beginFrame() and endFrame().
@@ -574,11 +643,11 @@ QQuickWindow *QQuickRenderControl::window() const
to the user of QQuickRenderControl to specify these points.
A typical update step, including initialization of rendering into an
- existing texture, could like like the following. The example snippet
+ existing texture, could look like the following. The example snippet
assumes Direct3D 11 but the same concepts apply other graphics APIs as
well.
- \badcode
+ \code
if (!m_quickInitialized) {
m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
@@ -600,6 +669,15 @@ QQuickWindow *QQuickRenderControl::window() const
m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
\endcode
+ \note This function does not need to be, and must not be, called when using
+ the \c software adaptation of Qt Quick.
+
+ \note Internally beginFrame() and endFrame() invoke
+ \l{QRhi::}{beginOffscreenFrame()} and \l{QRhi::}{endOffscreenFrame()},
+ respectively. This implies that there must not be a frame (neither
+ offscreen, nor swapchain-based) being recorded on the QRhi when
+ this function is called.
+
\since 6.0
\sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
@@ -607,8 +685,18 @@ QQuickWindow *QQuickRenderControl::window() const
void QQuickRenderControl::beginFrame()
{
Q_D(QQuickRenderControl);
- if (!d->rhi || d->rhi->isRecordingFrame())
+ if (!d->rhi) {
+ qWarning("QQuickRenderControl: No QRhi in beginFrame()");
+ return;
+ }
+ if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) {
+ qWarning("QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again");
return;
+ }
+ if (d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame");
+ return;
+ }
emit d->window->beforeFrameBegin();
@@ -639,6 +727,9 @@ void QQuickRenderControl::beginFrame()
scenegraph are submitted to the context or command queue, whichever is
applicable.
+ \note This function does not need to be, and must not be, called when using
+ the \c software adaptation of Qt Quick.
+
\since 6.0
\sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
@@ -646,11 +737,22 @@ void QQuickRenderControl::beginFrame()
void QQuickRenderControl::endFrame()
{
Q_D(QQuickRenderControl);
- if (!d->rhi || !d->rhi->isRecordingFrame())
+ if (!d->rhi) {
+ qWarning("QQuickRenderControl: No QRhi in endFrame()");
return;
+ }
+ if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
+ qWarning("QQuickRenderControl: endFrame() must only be called after a successful beginFrame()");
+ return;
+ }
+ if (!d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame");
+ return;
+ }
d->rhi->endOffscreenFrame();
- d->cb = nullptr;
+ // do not null out d->cb; this allows calling lastCompletedGpuTime() for example
+
d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
emit d->window->afterFrameEnd();
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 195b518dcc..1d8da84e57 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -14,6 +14,8 @@ class QQuickWindow;
class QOpenGLContext;
class QQuickRenderControlPrivate;
class QThread;
+class QRhi;
+class QRhiCommandBuffer;
class Q_QUICK_EXPORT QQuickRenderControl : public QObject
{
@@ -44,6 +46,9 @@ public:
QQuickWindow *window() const;
+ QRhi *rhi() const;
+ QRhiCommandBuffer *commandBuffer() const;
+
protected:
explicit QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject *parent = nullptr);
diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h
index 29b83827d6..5b1d7e860e 100644
--- a/src/quick/items/qquickrendercontrol_p.h
+++ b/src/quick/items/qquickrendercontrol_p.h
@@ -25,7 +25,7 @@ class QRhiCommandBuffer;
class QOffscreenSurface;
class QQuickGraphicsConfiguration;
-class Q_QUICK_PRIVATE_EXPORT QQuickRenderControlPrivate : public QObjectPrivate
+class Q_QUICK_EXPORT QQuickRenderControlPrivate : public QObjectPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickRenderControl)
@@ -44,7 +44,7 @@ public:
}
static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin);
- virtual bool isRenderWindow(const QWindow *w) { Q_UNUSED(w); return false; }
+ virtual bool isRenderWindow(const QWindow *w);
static void cleanup();
diff --git a/src/quick/items/qquickrendertarget.cpp b/src/quick/items/qquickrendertarget.cpp
index 7242a55d88..55bbf22eee 100644
--- a/src/quick/items/qquickrendertarget.cpp
+++ b/src/quick/items/qquickrendertarget.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickrendertarget_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qsgrhisupport_p.h>
@@ -25,14 +25,16 @@ QQuickRenderTargetPrivate::QQuickRenderTargetPrivate()
{
}
-QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other)
+QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other)
: ref(1),
- type(other->type),
- pixelSize(other->pixelSize),
- devicePixelRatio(other->devicePixelRatio),
- sampleCount(other->sampleCount),
- u(other->u),
- mirrorVertically(other->mirrorVertically)
+ type(other.type),
+ pixelSize(other.pixelSize),
+ devicePixelRatio(other.devicePixelRatio),
+ sampleCount(other.sampleCount),
+ u(other.u),
+ customDepthTexture(other.customDepthTexture),
+ mirrorVertically(other.mirrorVertically),
+ multisampleResolve(other.multisampleResolve)
{
}
@@ -161,6 +163,84 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
}
/*!
+ \return the currently set depth texture or, in most cases, \nullptr.
+
+ The value is only non-null when setDepthTexture() was called.
+
+ \since 6.8
+ */
+QRhiTexture *QQuickRenderTarget::depthTexture() const
+{
+ return d->customDepthTexture;
+}
+
+/*!
+ Requests using the given \a texture as the depth or depth-stencil buffer.
+ Ownership of \a texture is not taken.
+
+ The request is only taken into account when relevant. For example, calling
+ this function has no effect with fromRhiRenderTarget(), fromPaintDevice(),
+ or fromOpenGLRenderBuffer().
+
+ Normally a depth-stencil buffer is created automatically, transparently to
+ the user of QQuickRenderTarget. Therefore, there is no need to call this
+ function in most cases when working with QQuickRenderTarget. In special
+ circumstances, it can however become essential to be able to provide a
+ texture to render depth (or depth and stencil) data into, instead of letting
+ Qt Quick create its own intermediate textures or buffers. An example of this
+ is \l{https://www.khronos.org/openxr/}{OpenXR} and its extensions such as
+ \l{https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_composition_layer_depth}{XR_KHR_composition_layer_depth}.
+ In order to "submit the depth buffer" to the XR compositor, one has to, in
+ practice, retrieve an already created depth (depth-stencil) texture from
+ OpenXR (from the XrSwapchain) and use that texture as the render target for
+ depth data. That would not be possible without this function.
+
+ \note The \a texture is always expected to be a non-multisample 2D texture
+ or texture array (for multiview). If MSAA is involved, the samples are
+ resolved into \a texture at the end of the render pass, regardless of having
+ the MultisampleResolve flag set or not. MSAA is only supported for depth
+ (depth-stencil) textures when the underlying 3D API supports this, and this
+ support is not universally available. See \l{QRhi::ResolveDepthStencil}{the
+ relevant QRhi feature flag} for details. When this is not supported and
+ multisampling is requested in combination with a custom depth texture, \a
+ texture is not going to be touched during rendering and a warning is
+ printed.
+
+ \since 6.8
+
+ \note When it comes to OpenGL and OpenGL ES, using depth textures is not
+ functional on OpenGL ES 2.0 and requires at least OpenGL ES 3.0. Multisample
+ (MSAA) support is not available without at least OpenGL ES 3.1, or OpenGL
+ 3.0 on desktop.
+ */
+void QQuickRenderTarget::setDepthTexture(QRhiTexture *texture)
+{
+ if (d->customDepthTexture == texture)
+ return;
+
+ detach();
+ d->customDepthTexture = texture;
+}
+
+/*!
+ \enum QQuickRenderTarget::Flag
+ Flags for the static QQuickRenderTarget constructor functions.
+
+ \value MultisampleResolve Indicates that the \c sampleCount argument is not
+ the number of samples for the provided texture (and that the texture is
+ still a non-multisample texture), but rather the desired samples for
+ multisample antialiasing. Triggers automatically creating and managing an
+ intermediate multisample texture (or texture array) as the color buffer,
+ transparently to the application. The samples are resolved into the provided
+ texture at the end of the render pass automatically. When this flag is not
+ set, and the \c sampleCount argument is greater than 1, it implies the
+ provided texture is multisample. The flag has no effect is the
+ \c sampleCount is 1 (indicating that multisampling is not involved).
+
+ \since 6.8
+*/
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL texture object
specified by \a textureId.
@@ -171,7 +251,7 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -212,8 +292,9 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format);
- d->u.nativeTexture = { textureId, 0, uint(rhiFormat), 0 };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -228,7 +309,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
\a pixelSize specifies the size of the image, in pixels. Currently
only 2D textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native
object is a multisample texture.
@@ -252,6 +333,90 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
}
/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing an OpenGL 2D texture or texture
+ array object specified by \a textureId.
+
+ \a format specifies the native internal format of the texture. Only texture
+ formats that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a textureId is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a textureId. This is the recommended approach to
+ perform MSAA when the native OpenGL texture is not already multisample.
+
+ When \a arraySize is greater than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview},
+ \l QRhiColorAttachment::setMultiViewCount()), which can be relevant with
+ VR/AR especially. In this case \a arraySize is the number of views,
+ typically \c 2. See \l QSGMaterial::viewCount() for details on enabling
+ multiview rendering within the Qt Quick scenegraph.
+
+ A depth-stencil buffer, if applicable, is created and used automatically.
+ When the color buffer is multisample, the depth-stencil buffer will
+ automatically be multisample too. For multiview rendering, the depth-stencil
+ texture will automatically be made an array with a matching \a arraySize.
+
+ The OpenGL object name \a textureId must be a valid 2D texture name in the
+ rendering context used by the Qt Quick scenegraph. When \a arraySize is
+ greater than 1, \a textureId must be a valid 2D texture array name.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \note The implementation of this overload is not compatible with OpenGL ES
+ 2.0 or 3.0, and requires OpenGL ES 3.1 at minimum. (or OpenGL 3.0 on
+ desktop)
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromOpenGLTexture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!textureId) {
+ qWarning("QQuickRenderTarget: textureId is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { textureId, 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ }
+
+ return rt;
+}
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL renderbuffer object
specified by \a renderbufferId.
@@ -264,7 +429,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
\a pixelSize specifies the size of the image, in pixels.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample renderbuffer.
@@ -311,7 +476,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLRenderBuffer(uint renderbufferI
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -349,9 +514,9 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromD3D11(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -366,7 +531,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -385,7 +550,208 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
{
return fromD3D11Texture(texture, 0 /* DXGI_FORMAT_UNKNOWN */, pixelSize, sampleCount);
}
-#endif
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D11 texture object
+ specified by \a texture.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture and \a
+ sampleCount defines the number of samples desired. The resulting
+ QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture as its color attachment, and will resolve the samples
+ into \a texture. This is the recommended approach to perform MSAA when the
+ native texture is not already multisample.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromD3D11Texture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount, Flags flags)
+{
+ QQuickRenderTarget rt = fromD3D11Texture(texture, format, pixelSize, sampleCount);
+ QQuickRenderTargetPrivate::get(&rt)->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+ return rt;
+}
+
+/*!
+ \return a new QQuickRenderTarget referencing a D3D12 texture object
+ specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically.
+
+ \note the resulting QQuickRenderTarget does not own any native resources,
+ it merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ const QSize &pixelSize,
+ int sampleCount)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+
+ return rt;
+}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D12 2D texture or 2D
+ texture array object specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is the DXGI_FORMAT used for the render target view (RTV).
+ Often the same as \a format. Functional only when
+ \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html}{relaxed
+ format casting} is supported by the driver, the argument is ignored otherwise.
+ In practice support is expected to be always available on Windows 10 1703
+ and newer.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native D3D12 texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html}{view
+ instancing}), which can be relevant with VR/AR especially. \a arraySize is
+ the number of views, typically \c 2. See \l QSGMaterial::viewCount() for
+ details on enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ uint viewFormat,
+ const QSize &pixelSize,
+ int sampleCount,
+ int arraySize,
+ Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), resourceState, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Q_OS_WIN
/*!
\return a new QQuickRenderTarget referencing a Metal texture object
@@ -397,7 +763,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -414,7 +780,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format,
const QSize &pixelSize, int sampleCount)
{
@@ -435,9 +801,9 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -452,7 +818,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -471,6 +837,94 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
{
return fromMetalTexture(texture, 0 /* MTLPixelFormatInvalid */, pixelSize, sampleCount);
}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Metal 2D texture or 2D
+ texture array given in \a texture.
+
+ \a format specifies the MTLPixelFormat of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. Note however that the value may be ignored by Qt, when at
+ run time QRhi reports that the \l{QRhi::TextureViewFormat} feature is
+ unsupported.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native Metal texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering, which can be relevant with VR/AR
+ especially. \a arraySize is the number of views, typically \c 2. See
+ \l QSGMaterial::viewCount() for details on enabling multiview rendering within
+ the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format, uint viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
#endif
/*!
@@ -484,7 +938,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -502,8 +956,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if QT_CONFIG(vulkan) || defined(Q_QDOC)
-QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format,
- const QSize &pixelSize, int sampleCount)
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount)
{
QQuickRenderTarget rt;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
@@ -522,9 +975,9 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &flags);
- d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -532,14 +985,14 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
/*!
\overload
- \return a new QQuickRenderTarget referencing n Vulkan image object specified
+ \return a new QQuickRenderTarget referencing a Vulkan image object specified
by \a image. The image is assumed to have a format of
VK_FORMAT_R8G8B8A8_UNORM.
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -558,11 +1011,113 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
{
return fromVulkanImage(image, layout, VK_FORMAT_UNDEFINED, pixelSize, sampleCount);
}
-#endif
/*!
- \internal
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Vulkan image object
+ specified by \a image. The current \a layout of the image must be provided
+ as well. The image must be either a 2D texture or 2D texture array.
+
+ \a format specifies the VkFormat of the image. Only image formats that are
+ supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. (for example, a \a format of \c VK_FORMAT_R8G8B8A8_SRGB
+ and \a viewFormat of \c VK_FORMAT_R8G8B8A8_UNORM).
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a image is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a image. This is the recommended approach to
+ perform MSAA when the native Vulkan image is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview}),
+ which can be relevant with VR/AR especially. \a arraySize is the number of
+ views, typically \c 2. See \l QSGMaterial::viewCount() for details on
+ enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, VkFormat viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (image == VK_NULL_HANDLE) {
+ qWarning("QQuickRenderTarget: image is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(image), layout, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Vulkan
+
+/*!
+ \return a new QQuickRenderTarget referencing an existing \a renderTarget.
+
+ \a renderTarget will in most cases be a QRhiTextureRenderTarget, which
+ allows directing the Qt Quick scene's rendering into a QRhiTexture.
+
+ \note the resulting QQuickRenderTarget does not own \a renderTarget and any
+ underlying native resources, it merely contains references and the
+ associated metadata of the size and sample count. It is the caller's
+ responsibility to ensure that the referenced resources exists as long as
+ necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+*/
QQuickRenderTarget QQuickRenderTarget::fromRhiRenderTarget(QRhiRenderTarget *renderTarget)
{
QQuickRenderTarget rt;
@@ -628,7 +1183,8 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
|| d->pixelSize != other.d->pixelSize
|| d->devicePixelRatio != other.d->devicePixelRatio
|| d->sampleCount != other.d->sampleCount
- || d->mirrorVertically != other.d->mirrorVertically)
+ || d->mirrorVertically != other.d->mirrorVertically
+ || d->multisampleResolve != other.d->multisampleResolve)
{
return false;
}
@@ -638,9 +1194,21 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
break;
case QQuickRenderTargetPrivate::Type::NativeTexture:
if (d->u.nativeTexture.object != other.d->u.nativeTexture.object
- || d->u.nativeTexture.layout != other.d->u.nativeTexture.layout
+ || d->u.nativeTexture.layoutOrState != other.d->u.nativeTexture.layoutOrState
|| d->u.nativeTexture.rhiFormat != other.d->u.nativeTexture.rhiFormat
- || d->u.nativeTexture.rhiFlags != other.d->u.nativeTexture.rhiFlags)
+ || d->u.nativeTexture.rhiFormatFlags != other.d->u.nativeTexture.rhiFormatFlags
+ || d->u.nativeTexture.rhiViewFormat != other.d->u.nativeTexture.rhiViewFormat
+ || d->u.nativeTexture.rhiViewFormatFlags != other.d->u.nativeTexture.rhiViewFormatFlags)
+ return false;
+ break;
+ case QQuickRenderTargetPrivate::Type::NativeTextureArray:
+ if (d->u.nativeTextureArray.object != other.d->u.nativeTextureArray.object
+ || d->u.nativeTextureArray.layoutOrState != other.d->u.nativeTextureArray.layoutOrState
+ || d->u.nativeTextureArray.arraySize != other.d->u.nativeTextureArray.arraySize
+ || d->u.nativeTextureArray.rhiFormat != other.d->u.nativeTextureArray.rhiFormat
+ || d->u.nativeTextureArray.rhiFormatFlags != other.d->u.nativeTextureArray.rhiFormatFlags
+ || d->u.nativeTextureArray.rhiViewFormat != other.d->u.nativeTextureArray.rhiViewFormat
+ || d->u.nativeTextureArray.rhiViewFormatFlags != other.d->u.nativeTextureArray.rhiViewFormatFlags)
return false;
break;
case QQuickRenderTargetPrivate::Type::NativeRenderbuffer:
@@ -662,21 +1230,261 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
return true;
}
-static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
+static bool createRhiRenderTargetWithRenderBuffer(QRhiRenderBuffer *renderBuffer,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment(renderBuffer);
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ rtDesc.setDepthStencilBuffer(depthStencil.get());
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget with renderbuffer"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build renderbuffer-based render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+
+ return true;
+}
+
+static bool createRhiRenderTarget(QRhiTexture *texture,
const QSize &pixelSize,
int sampleCount,
+ bool multisampleResolve,
QRhi *rhi,
QQuickWindowRenderTarget *dst)
{
- std::unique_ptr<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
- if (!depthStencil->create()) {
- qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
- return false;
+ // Simple path: no user-supplied depth texture. So create our own
+ // depth-stencil buffer, using renderbuffers (so this is still GLES 2.0
+ // compatible), with MSAA support being GLES 3.0 compatible.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
}
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
rtDesc.setDepthStencilBuffer(depthStencil.get());
std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetWithDepthTexture(QRhiTexture *texture,
+ QRhiTexture *depthTexture,
+ const QSize &pixelSize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // This version takes a user-supplied depthTexture. That texture is always
+ // non-multisample. If sample count is > 1, we still need our own
+ // multisample depth-stencil buffer, and the depth(stencil) data is expected
+ // to be resolved (and written out) to depthTexture, _if_ the underlying API
+ // supports it (see QRhi's ResolveDepthStencil feature). The intermediate,
+ // multisample depth-stencil buffer must be a texture here (not
+ // renderbuffer), specifically for OpenGL ES and its related multisample
+ // extensions.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (depthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ depthTexture->pixelSize().width(),
+ depthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (depthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ QRhiTexture::Format multisampleDepthTextureFormat = depthTexture->format();
+ depthStencil.reset(rhi->newTexture(multisampleDepthTextureFormat, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil texture for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(depthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ } else {
+ rtDesc.setDepthTexture(depthTexture);
+ }
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rp.get());
@@ -685,37 +1493,222 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
return false;
}
- dst->renderTarget = rt.release();
- dst->rpDesc = rp.release();
- dst->depthStencil = depthStencil.release();
- dst->owns = true; // ownership of the native resource itself is not transferred but the QRhi objects are on us now
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetMultiView(QRhiTexture *texture,
+ QRhiTexture *maybeCustomDepthTexture,
+ const QSize &pixelSize,
+ int arraySize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // Multiview path, working with texture arrays. Optionally with a
+ // user-supplied, non-multisample depth texture (array). (same semantics
+ // then as with createRhiRenderTargetWithDepthTexture, but everything is a
+ // 2D texture array here)
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.depthStencilTexture->arraySize() == arraySize)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->arraySize() == arraySize
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (maybeCustomDepthTexture) {
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (maybeCustomDepthTexture->arraySize() != arraySize) {
+ qWarning("Custom depth texture array size (%d) does not match QQuickRenderTarget (%d)",
+ maybeCustomDepthTexture->arraySize(), arraySize);
+ return false;
+ }
+ if (maybeCustomDepthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ maybeCustomDepthTexture->pixelSize().width(),
+ maybeCustomDepthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (maybeCustomDepthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ depthStencil.reset(rhi->newTextureArray(QRhiTexture::D24S8, arraySize, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer (multiview) for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTextureArray(multisampleTextureFormat, arraySize, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer (multiview) for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ colorAttachment.setMultiViewCount(arraySize);
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (maybeCustomDepthTexture) {
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(maybeCustomDepthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ }
+ } else {
+ if (depthStencil)
+ rtDesc.setDepthTexture(depthStencil.get());
+ else if (maybeCustomDepthTexture)
+ rtDesc.setDepthTexture(maybeCustomDepthTexture);
+ }
+
+ QRhiTextureRenderTarget::Flags rtFlags;
+ if (!maybeCustomDepthTexture)
+ rtFlags |= QRhiTextureRenderTarget::DoNotStoreDepthStencilContents;
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc, rtFlags));
+ rt->setName(QByteArrayLiteral("RT for multiview QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build multiview texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ dst->rt.multiViewCount = arraySize;
return true;
}
bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst)
{
+ // dst->implicitBuffers may contain valid objects. If so, and their
+ // properties are suitable, they are expected to be reused. Once taken what
+ // we can reuse, it needs to be reset().
+
switch (type) {
case Type::Null:
- dst->renderTarget = nullptr;
- dst->paintDevice = nullptr;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
return true;
case Type::NativeTexture:
{
- const auto format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
- : QRhiTexture::Format(u.nativeTexture.rhiFormat);
- const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFlags);
- std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, sampleCount, flags));
- if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layout })) {
+ QRhiTexture::Format format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTexture.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFormatFlags);
+ std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTexture.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layoutOrState })) {
qWarning("Failed to build wrapper texture for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(texture.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture) {
+ if (!createRhiRenderTargetWithDepthTexture(texture.get(), customDepthTexture, pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ } else {
+ if (!createRhiRenderTarget(texture.get(), pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ }
+ dst->res.texture = texture.release();
+ }
+ return true;
+
+ case Type::NativeTextureArray:
+ {
+ QRhiTexture::Format format = u.nativeTextureArray.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTextureArray.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTextureArray.rhiFormatFlags);
+ const int arraySize = u.nativeTextureArray.arraySize;
+ std::unique_ptr<QRhiTexture> texture(rhi->newTextureArray(format, arraySize, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTextureArray.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTextureArray.object, u.nativeTextureArray.layoutOrState })) {
+ qWarning("Failed to build wrapper texture array for QQuickRenderTarget");
return false;
- dst->texture = texture.release();
+ }
+ if (!createRhiRenderTargetMultiView(texture.get(), customDepthTexture, pixelSize, arraySize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ dst->res.texture = texture.release();
}
return true;
@@ -726,27 +1719,59 @@ bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst
qWarning("Failed to build wrapper renderbuffer for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(renderbuffer.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with renderbuffers in QQuickRenderTarget");
+ if (!createRhiRenderTargetWithRenderBuffer(renderbuffer.get(), pixelSize, sampleCount, rhi, dst))
return false;
- dst->renderBuffer = renderbuffer.release();
+ dst->res.renderBuffer = renderbuffer.release();
}
return true;
case Type::RhiRenderTarget:
- dst->renderTarget = u.rhiRt;
- dst->rpDesc = u.rhiRt->renderPassDescriptor(); // just for QQuickWindowRenderTarget::reset()
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->rt.renderTarget = u.rhiRt;
+ dst->rt.owns = false;
+ if (dst->rt.renderTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ auto texRt = static_cast<QRhiTextureRenderTarget *>(dst->rt.renderTarget);
+ const QRhiTextureRenderTargetDescription desc = texRt->description();
+ bool first = true;
+ for (auto it = desc.cbeginColorAttachments(), end = desc.cendColorAttachments(); it != end; ++it) {
+ if (it->multiViewCount() <= 1)
+ continue;
+ if (first || dst->rt.multiViewCount == it->multiViewCount()) {
+ first = false;
+ if (it->texture() && it->texture()->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (it->texture()->arraySize() >= it->layer() + it->multiViewCount()) {
+ dst->rt.multiViewCount = it->multiViewCount();
+ } else {
+ qWarning("Invalid QQuickRenderTarget; needs at least %d elements in texture array, got %d",
+ it->layer() + it->multiViewCount(),
+ it->texture()->arraySize());
+ return false;
+ }
+ } else {
+ qWarning("Invalid QQuickRenderTarget; multiview requires a texture array");
+ return false;
+ }
+ } else {
+ qWarning("Inconsistent multiViewCount in QQuickRenderTarget (was %d, now found an attachment with %d)",
+ dst->rt.multiViewCount, it->multiViewCount());
+ return false;
+ }
+ }
+ }
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with QRhiRenderTarget in QQuickRenderTarget");
return true;
+
case Type::PaintDevice:
- dst->paintDevice = u.paintDevice;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->sw.paintDevice = u.paintDevice;
+ dst->sw.owns = false;
return true;
-
- default:
- break;
}
- return false;
+
+ Q_UNREACHABLE_RETURN(false);
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendertarget.h b/src/quick/items/qquickrendertarget.h
index ded9c80274..1b93db7695 100644
--- a/src/quick/items/qquickrendertarget.h
+++ b/src/quick/items/qquickrendertarget.h
@@ -11,7 +11,7 @@
#include <QtGui/qvulkaninstance.h>
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
Q_FORWARD_DECLARE_OBJC_CLASS(MTLTexture);
#endif
@@ -19,11 +19,17 @@ QT_BEGIN_NAMESPACE
class QQuickRenderTargetPrivate;
class QRhiRenderTarget;
+class QRhiTexture;
class QPaintDevice;
class Q_QUICK_EXPORT QQuickRenderTarget
{
public:
+ enum class Flag {
+ MultisampleResolve = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
QQuickRenderTarget();
~QQuickRenderTarget();
QQuickRenderTarget(const QQuickRenderTarget &other);
@@ -37,25 +43,36 @@ public:
bool mirrorVertically() const;
void setMirrorVertically(bool enable);
+ QRhiTexture *depthTexture() const;
+ void setDepthTexture(QRhiTexture *texture);
+
#if QT_CONFIG(opengl) || defined(Q_QDOC)
- static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromOpenGLTexture(uint textureId, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
+
static QQuickRenderTarget fromOpenGLRenderBuffer(uint renderbufferId, const QSize &pixelSize, int sampleCount = 1);
#endif
#if defined(Q_OS_WIN) || defined(Q_QDOC)
- static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromD3D11Texture(void *texture, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount, Flags flags);
+
+ static QQuickRenderTarget fromD3D12Texture(void *texture, int resourceState, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromD3D12Texture(void *texture, int resourceState, uint format, uint viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
- static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+#if QT_CONFIG(metal) || defined(Q_QDOC)
static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, uint viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
#if QT_CONFIG(vulkan) || defined(Q_QDOC)
- static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount = 1);
static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount = 1);
+ static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, VkFormat viewFormat, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags);
#endif
static QQuickRenderTarget fromRhiRenderTarget(QRhiRenderTarget *renderTarget);
@@ -74,6 +91,8 @@ private:
{ return !lhs.isEqual(rhs); }
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickRenderTarget::Flags)
+
QT_END_NAMESPACE
#endif // QQUICKRENDERTARGET_H
diff --git a/src/quick/items/qquickrendertarget_p.h b/src/quick/items/qquickrendertarget_p.h
index dd781709af..d2e9962693 100644
--- a/src/quick/items/qquickrendertarget_p.h
+++ b/src/quick/items/qquickrendertarget_p.h
@@ -22,20 +22,21 @@
QT_BEGIN_NAMESPACE
class QRhi;
-class QQuickWindowRenderTarget;
+struct QQuickWindowRenderTarget;
-class Q_QUICK_PRIVATE_EXPORT QQuickRenderTargetPrivate
+class Q_QUICK_EXPORT QQuickRenderTargetPrivate
{
public:
static QQuickRenderTargetPrivate *get(QQuickRenderTarget *rt) { return rt->d; }
static const QQuickRenderTargetPrivate *get(const QQuickRenderTarget *rt) { return rt->d; }
QQuickRenderTargetPrivate();
- QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other);
+ QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other);
bool resolve(QRhi *rhi, QQuickWindowRenderTarget *dst);
enum class Type {
Null,
NativeTexture,
+ NativeTextureArray,
NativeRenderbuffer,
RhiRenderTarget,
PaintDevice
@@ -48,18 +49,32 @@ public:
int sampleCount = 1;
struct NativeTexture {
quint64 object;
- int layout;
+ int layoutOrState;
uint rhiFormat;
- uint rhiFlags;
+ uint rhiFormatFlags;
+ uint rhiViewFormat;
+ uint rhiViewFormatFlags;
+ };
+ struct NativeTextureArray {
+ quint64 object;
+ int layoutOrState;
+ int arraySize;
+ uint rhiFormat;
+ uint rhiFormatFlags;
+ uint rhiViewFormat;
+ uint rhiViewFormatFlags;
};
union {
NativeTexture nativeTexture;
+ NativeTextureArray nativeTextureArray;
quint64 nativeRenderbufferObject;
QRhiRenderTarget *rhiRt;
QPaintDevice *paintDevice;
} u;
+ QRhiTexture *customDepthTexture = nullptr;
bool mirrorVertically = false;
+ bool multisampleResolve = false;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrepeater_p.h b/src/quick/items/qquickrepeater_p.h
index 2a3b077feb..e3519b9602 100644
--- a/src/quick/items/qquickrepeater_p.h
+++ b/src/quick/items/qquickrepeater_p.h
@@ -26,7 +26,7 @@ QT_BEGIN_NAMESPACE
class QQmlChangeSet;
class QQuickRepeaterPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRepeater : public QQuickItem
+class Q_QUICK_EXPORT QQuickRepeater : public QQuickItem
{
Q_OBJECT
@@ -79,6 +79,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRepeater)
-
#endif // QQUICKREPEATER_P_H
diff --git a/src/quick/items/qquickrhiitem.cpp b/src/quick/items/qquickrhiitem.cpp
new file mode 100644
index 0000000000..b83df7f33a
--- /dev/null
+++ b/src/quick/items/qquickrhiitem.cpp
@@ -0,0 +1,1158 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickrhiitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQuickRhiItem
+ \inmodule QtQuick
+ \since 6.7
+
+ \brief The QQuickRhiItem class is a portable alternative to
+ QQuickFramebufferObject that is not tied to OpenGL, but rather allows
+ integrating rendering with the QRhi APIs with Qt Quick.
+
+ \preliminary
+
+ \note QQuickRhiItem is in tech preview in Qt 6.7. \b {The API is under
+ development and subject to change.}
+
+ QQuickRhiItem is effectively the counterpart of \l QRhiWidget in the world of
+ Qt Quick. Both of these are meant to be subclassed, and they both enable
+ recording QRhi-based rendering that targets an offscreen color buffer. The
+ resulting 2D image is then composited with the rest of the Qt Quick scene.
+
+ \note While QQuickRhiItem is a public Qt API, the QRhi family of classes in
+ the Qt Gui module, including QShader and QShaderDescription, offer limited
+ compatibility guarantees. There are no source or binary compatibility
+ guarantees for these classes, meaning the API is only guaranteed to work
+ with the Qt version the application was developed against. Source
+ incompatible changes are however aimed to be kept at a minimum and will
+ only be made in minor releases (6.7, 6.8, and so on). \c{qquickrhiitem.h}
+ does not directly include any QRhi-related headers. To use those classes
+ when implementing a QQuickRhiItem subclass, link to
+ \c{Qt::GuiPrivate} (if using CMake), and include the appropriate headers
+ with the \c rhi prefix, for example \c{#include <rhi/qrhi.h>}.
+
+ QQuickRhiItem is a replacement for the legacy \l QQuickFramebufferObject
+ class. The latter is inherently tied to OpenGL / OpenGL ES, whereas
+ QQuickRhiItem works with the QRhi classes, allowing to run the same
+ rendering code with Vulkan, Metal, Direct 3D 11/12, and OpenGL / OpenGL ES.
+ Conceptually and functionally they are very close, and migrating from
+ QQuickFramebufferObject to QQuickRhiItem is straightforward.
+ QQuickFramebufferObject continues to be available to ensure compatibility
+ for existing application code that works directly with the OpenGL API.
+
+ \note QQuickRhiItem will not be functional when using the \c software
+ adaptation of the Qt Quick scene graph.
+
+ On most platforms, the scene graph rendering, and thus the rendering
+ performed by the QQuickRhiItem will occur on a \l {Scene Graph and
+ Rendering}{dedicated thread}. For this reason, the QQuickRhiItem class
+ enforces a strict separation between the item implementation (the
+ QQuickItem subclass) and the actual rendering logic. All item logic, such
+ as properties and UI-related helper functions exposed to QML must be
+ located in the QQuickRhiItem subclass. Everything that relates to rendering
+ must be located in the QQuickRhiItemRenderer class. To avoid race
+ conditions and read/write issues from two threads it is important that the
+ renderer and the item never read or write shared variables. Communication
+ between the item and the renderer should primarily happen via the
+ QQuickRhiItem::synchronize() function. This function will be called on the
+ render thread while the GUI thread is blocked. Using queued connections or
+ events for communication between item and renderer is also possible.
+
+ Applications must subclass both QQuickRhiItem and QQuickRhiItemRenderer.
+ The pure virtual createRenderer() function must be reimplemented to return
+ a new instance of the QQuickRhiItemRenderer subclass.
+
+ As with QRhiWidget, QQuickRhiItem automatically managed the color buffer,
+ which is a 2D texture (QRhiTexture) normally, or a QRhiRenderBuffer when
+ multisampling is in use. (some 3D APIs differentiate between textures and
+ renderbuffers, while with some others the underlying native resource is the
+ same; renderbuffers are used mainly to allow multisampling with OpenGL ES
+ 3.0)
+
+ The size of the texture will by default adapt to the size of the item (with
+ the \l{QQuickWindow::effectiveDevicePixelRatio()}{device pixel ratio} taken
+ into account). If the item size changes, the texture is recreated with the
+ correct size. If a fixed size is preferred, set \l fixedColorBufferWidth and
+ \l fixedColorBufferHeight to non-zero values.
+
+ QQuickRhiItem is a \l{QSGTextureProvider}{texture provider} and can be used
+ directly in \l {ShaderEffect}{ShaderEffects} and other classes that consume
+ texture providers.
+
+ While not a primary use case, QQuickRhiItem also allows incorporating
+ rendering code that directly uses a 3D graphics API such as Vulkan, Metal,
+ Direct 3D, or OpenGL. See \l QRhiCommandBuffer::beginExternal() for details
+ on recording native commands within a QRhi render pass, as well as
+ \l QRhiTexture::createFrom() for a way to wrap an existing native texture and
+ then use it with QRhi in a subsequent render pass. See also
+ \l QQuickGraphicsConfiguration regarding configuring the native 3D API
+ environment (e.g. device extensions) and note that the \l QQuickWindow can be
+ associated with a custom \l QVulkanInstance by calling
+ \l QWindow::setVulkanInstance() early enough.
+
+ \note QQuickRhiItem always uses the same QRhi instance the QQuickWindow
+ uses (and by extension, the same OpenGL context, Vulkan device, etc.). To
+ choose which underlying 3D graphics API is used, call
+ \l{QQuickWindow::setGraphicsApi()}{setGraphicsApi()} on the QQuickWindow
+ early enough. Changing it is not possible once the scene graph has
+ initialized, and all QQuickRhiItem instances in the scene will render using
+ the same 3D API.
+
+ \section2 A simple example
+
+ Take the following subclass of QQuickRhiItem. It is shown here in complete
+ form. It renders a single triangle with a perspective projection, where the
+ triangle is rotated based on the \c angle property of the custom item.
+ (meaning it can be driven for example with animations such as
+ \l NumberAnimation from QML)
+
+ \snippet qquickrhiitem/qquickrhiitem_intro.cpp 0
+
+ It is notable that this simple class is almost exactly the same as the code
+ shown in the \l QRhiWidget introduction. The vertex and fragment shaders are
+ the same as well. These are provided as Vulkan-style GLSL source code and
+ must be processed first by the Qt shader infrastructure first. This is
+ achieved either by running the \c qsb command-line tool manually, or by
+ using the \l{Qt Shader Tools Build System Integration}{qt_add_shaders()}
+ function in CMake. The QQuickRhiItem loads these pre-processed \c{.qsb}
+ files that are shipped with the application. See \l{Qt Shader Tools} for
+ more information about Qt's shader translation infrastructure.
+
+ \c{color.vert}
+
+ \snippet qquickrhiitem/qquickrhiitem_intro.vert 0
+
+ \c{color.frag}
+
+ \snippet qquickrhiitem/qquickrhiitem_intro.frag 0
+
+ Once exposed to QML (note the \c QML_NAMED_ELEMENT), our custom item can be
+ instantiated in any scene. (after importing the appropriate \c URI specified
+ for \l{qt6_add_qml_module}{qt_add_qml_module} in the CMake project)
+
+ \code
+ ExampleRhiItem {
+ anchors.fill: parent
+ anchors.margins: 10
+ NumberAnimation on angle { from: 0; to: 360; duration: 5000; loops: Animation.Infinite }
+ }
+ \endcode
+
+ See \l{Scene Graph - RHI Texture Item} for a more complex example.
+
+ \sa QQuickRhiItemRenderer, {Scene Graph - RHI Texture Item}, QRhi, {Scene Graph and Rendering}
+ */
+
+/*!
+ \class QQuickRhiItemRenderer
+ \inmodule QtQuick
+ \since 6.7
+
+ \brief A QQuickRhiItemRenderer implements the rendering logic of a
+ QQuickRhiItem.
+
+ \preliminary
+
+ \note QQuickRhiItem and QQuickRhiItemRenderer are in tech preview in Qt
+ 6.7. \b {The API is under development and subject to change.}
+
+ \sa QQuickRhiItem, QRhi
+ */
+
+QQuickRhiItemNode::QQuickRhiItemNode(QQuickRhiItem *item)
+ : m_item(item)
+{
+ m_window = m_item->window();
+ connect(m_window, &QQuickWindow::beforeRendering, this, &QQuickRhiItemNode::render,
+ Qt::DirectConnection);
+ connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
+ if (m_window->effectiveDevicePixelRatio() != m_dpr)
+ m_item->update();
+ }, Qt::DirectConnection);
+}
+
+QSGTexture *QQuickRhiItemNode::texture() const
+{
+ return m_sgTexture.get();
+}
+
+void QQuickRhiItemNode::resetColorBufferObjects()
+{
+ // owns either m_colorTexture or m_resolveTexture
+ m_sgTexture.reset();
+
+ m_colorTexture = nullptr;
+ m_resolveTexture = nullptr;
+
+ m_msaaColorBuffer.reset();
+}
+
+void QQuickRhiItemNode::resetRenderTargetObjects()
+{
+ m_renderTarget.reset();
+ m_renderPassDescriptor.reset();
+ m_depthStencilBuffer.reset();
+}
+
+void QQuickRhiItemNode::sync()
+{
+ if (!m_rhi) {
+ m_rhi = m_window->rhi();
+ if (!m_rhi) {
+ qWarning("No QRhi found for window %p, QQuickRhiItem will not be functional", m_window);
+ return;
+ }
+ }
+
+ m_dpr = m_window->effectiveDevicePixelRatio();
+ const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin);
+ const int maxTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
+
+ QQuickRhiItemPrivate *itemD = m_item->d_func();
+ QSize newSize = QSize(itemD->fixedTextureWidth, itemD->fixedTextureHeight);
+ if (newSize.isEmpty())
+ newSize = QSize(int(m_item->width()), int(m_item->height())) * m_dpr;
+
+ newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
+ newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
+
+ if (m_colorTexture) {
+ if (m_colorTexture->format() != itemD->rhiTextureFormat
+ || m_colorTexture->sampleCount() != itemD->samples)
+ {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+ }
+
+ if (m_msaaColorBuffer) {
+ if (m_msaaColorBuffer->backingFormat() != itemD->rhiTextureFormat
+ || m_msaaColorBuffer->sampleCount() != itemD->samples)
+ {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+ }
+
+ if (m_sgTexture && m_sgTexture->hasAlphaChannel() != itemD->blend) {
+ resetColorBufferObjects();
+ resetRenderTargetObjects();
+ }
+
+ if (!m_colorTexture && itemD->samples <= 1) {
+ if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
+ qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
+ "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
+ }
+ m_colorTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, itemD->samples,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!m_colorTexture->create()) {
+ qWarning("Failed to create backing texture for QQuickRhiItem");
+ delete m_colorTexture;
+ m_colorTexture = nullptr;
+ return;
+ }
+ }
+
+ if (itemD->samples > 1) {
+ if (!m_msaaColorBuffer) {
+ if (!m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
+ qWarning("QQuickRhiItem: Multisample renderbuffers are reported as unsupported; "
+ "sample count %d will not work as expected", itemD->samples);
+ }
+ if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
+ qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
+ "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
+ }
+ m_msaaColorBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, itemD->samples,
+ {}, itemD->rhiTextureFormat));
+ if (!m_msaaColorBuffer->create()) {
+ qWarning("Failed to create multisample color buffer for QQuickRhiItem");
+ m_msaaColorBuffer.reset();
+ return;
+ }
+ }
+ if (!m_resolveTexture) {
+ m_resolveTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, 1,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!m_resolveTexture->create()) {
+ qWarning("Failed to create resolve texture for QQuickRhiItem");
+ delete m_resolveTexture;
+ m_resolveTexture = nullptr;
+ return;
+ }
+ }
+ } else if (m_resolveTexture) {
+ m_resolveTexture->deleteLater();
+ m_resolveTexture = nullptr;
+ }
+
+ if (m_colorTexture && m_colorTexture->pixelSize() != newSize) {
+ m_colorTexture->setPixelSize(newSize);
+ if (!m_colorTexture->create())
+ qWarning("Failed to rebuild texture for QQuickRhiItem after resizing");
+ }
+
+ if (m_msaaColorBuffer && m_msaaColorBuffer->pixelSize() != newSize) {
+ m_msaaColorBuffer->setPixelSize(newSize);
+ if (!m_msaaColorBuffer->create())
+ qWarning("Failed to rebuild multisample color buffer for QQuickRhiitem after resizing");
+ }
+
+ if (m_resolveTexture && m_resolveTexture->pixelSize() != newSize) {
+ m_resolveTexture->setPixelSize(newSize);
+ if (!m_resolveTexture->create())
+ qWarning("Failed to rebuild resolve texture for QQuickRhiItem after resizing");
+ }
+
+ if (!m_sgTexture) {
+ QQuickWindow::CreateTextureOptions options;
+ if (itemD->blend)
+ options |= QQuickWindow::TextureHasAlphaChannel;
+ // the QSGTexture takes ownership of the QRhiTexture
+ m_sgTexture.reset(m_window->createTextureFromRhiTexture(m_colorTexture ? m_colorTexture : m_resolveTexture,
+ options));
+ setTexture(m_sgTexture.get());
+ }
+
+ if (itemD->autoRenderTarget) {
+ const QSize pixelSize = m_colorTexture ? m_colorTexture->pixelSize()
+ : m_msaaColorBuffer->pixelSize();
+ if (!m_depthStencilBuffer) {
+ m_depthStencilBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, itemD->samples));
+ if (!m_depthStencilBuffer->create()) {
+ qWarning("Failed to create depth-stencil buffer for QQuickRhiItem");
+ resetRenderTargetObjects();
+ return;
+ }
+ } else if (m_depthStencilBuffer->pixelSize() != pixelSize) {
+ m_depthStencilBuffer->setPixelSize(pixelSize);
+ if (!m_depthStencilBuffer->create()) {
+ qWarning("Failed to rebuild depth-stencil buffer for QQuickRhiItem with new size");
+ return;
+ }
+ }
+ if (!m_renderTarget) {
+ QRhiColorAttachment color0;
+ if (m_colorTexture)
+ color0.setTexture(m_colorTexture);
+ else
+ color0.setRenderBuffer(m_msaaColorBuffer.get());
+ if (itemD->samples > 1)
+ color0.setResolveTexture(m_resolveTexture);
+ QRhiTextureRenderTargetDescription rtDesc(color0, m_depthStencilBuffer.get());
+ m_renderTarget.reset(m_rhi->newTextureRenderTarget(rtDesc));
+ m_renderPassDescriptor.reset(m_renderTarget->newCompatibleRenderPassDescriptor());
+ m_renderTarget->setRenderPassDescriptor(m_renderPassDescriptor.get());
+ if (!m_renderTarget->create()) {
+ qWarning("Failed to create render target for QQuickRhiitem");
+ resetRenderTargetObjects();
+ return;
+ }
+ }
+ } else {
+ resetRenderTargetObjects();
+ }
+
+ if (newSize != itemD->effectiveTextureSize) {
+ itemD->effectiveTextureSize = newSize;
+ emit m_item->effectiveColorBufferSizeChanged();
+ }
+
+ QRhiCommandBuffer *cb = queryCommandBuffer();
+ if (cb)
+ m_renderer->initialize(cb);
+
+ m_renderer->synchronize(m_item);
+}
+
+QRhiCommandBuffer *QQuickRhiItemNode::queryCommandBuffer()
+{
+ QRhiSwapChain *swapchain = m_window->swapChain();
+ QSGRendererInterface *rif = m_window->rendererInterface();
+
+ // Handle both cases: on-screen QQuickWindow vs. off-screen QQuickWindow
+ // e.g. by using QQuickRenderControl to redirect into a texture.
+ QRhiCommandBuffer *cb = swapchain ? swapchain->currentFrameCommandBuffer()
+ : static_cast<QRhiCommandBuffer *>(
+ rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
+
+ if (!cb) {
+ qWarning("QQuickRhiItem: Neither swapchain nor redirected command buffer are available.");
+ return nullptr;
+ }
+
+ return cb;
+}
+
+void QQuickRhiItemNode::render()
+{
+ // called before Qt Quick starts recording its main render pass
+
+ if (!isValid() || !m_renderPending)
+ return;
+
+ QRhiCommandBuffer *cb = queryCommandBuffer();
+ if (!cb)
+ return;
+
+ m_renderPending = false;
+ m_renderer->render(cb);
+
+ markDirty(QSGNode::DirtyMaterial);
+ emit textureChanged();
+}
+
+/*!
+ Constructs a new QQuickRhiItem with the given \a parent.
+ */
+QQuickRhiItem::QQuickRhiItem(QQuickItem *parent)
+ : QQuickItem(*new QQuickRhiItemPrivate, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ Destructor.
+*/
+QQuickRhiItem::~QQuickRhiItem()
+{
+}
+
+/*!
+ \internal
+ */
+QSGNode *QQuickRhiItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ // Changing to an empty size should not involve destroying and then later
+ // recreating the node, because we do not know how expensive the user's
+ // renderer setup is. Rather, keep the node if it already exist, and clamp
+ // all accesses to width and height. Hence the unusual !oldNode condition here.
+ if (!oldNode && (width() <= 0 || height() <= 0))
+ return nullptr;
+
+ Q_D(QQuickRhiItem);
+ QQuickRhiItemNode *n = static_cast<QQuickRhiItemNode *>(oldNode);
+ if (!n) {
+ if (!d->node)
+ d->node = new QQuickRhiItemNode(this);
+ if (!d->node->hasRenderer()) {
+ QQuickRhiItemRenderer *r = createRenderer();
+ if (r) {
+ r->node = d->node;
+ d->node->setRenderer(r);
+ } else {
+ qWarning("No QQuickRhiItemRenderer was created; the item will not render");
+ delete d->node;
+ d->node = nullptr;
+ return nullptr;
+ }
+ }
+ n = d->node;
+ }
+
+ n->sync();
+
+ if (!n->isValid()) {
+ delete n;
+ d->node = nullptr;
+ return nullptr;
+ }
+
+ if (window()->rhi()->isYUpInFramebuffer()) {
+ n->setTextureCoordinatesTransform(d->mirrorVertically
+ ? QSGSimpleTextureNode::NoTransform
+ : QSGSimpleTextureNode::MirrorVertically);
+ } else {
+ n->setTextureCoordinatesTransform(d->mirrorVertically
+ ? QSGSimpleTextureNode::MirrorVertically
+ : QSGSimpleTextureNode::NoTransform);
+ }
+ n->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ n->setRect(0, 0, qMax<int>(0, width()), qMax<int>(0, height()));
+
+ n->scheduleUpdate();
+
+ return n;
+}
+
+/*!
+ \reimp
+ */
+bool QQuickRhiItem::event(QEvent *e)
+{
+ return QQuickItem::event(e);
+}
+
+/*!
+ \reimp
+ */
+void QQuickRhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+ if (newGeometry.size() != oldGeometry.size())
+ update();
+}
+
+/*!
+ \reimp
+ */
+void QQuickRhiItem::releaseResources()
+{
+ // called on the gui thread if the item is removed from scene
+
+ Q_D(QQuickRhiItem);
+ d->node = nullptr;
+}
+
+void QQuickRhiItem::invalidateSceneGraph()
+{
+ // called on the render thread when the scenegraph is invalidated
+
+ Q_D(QQuickRhiItem);
+ d->node = nullptr;
+}
+
+/*!
+ \reimp
+ */
+bool QQuickRhiItem::isTextureProvider() const
+{
+ return true;
+}
+
+/*!
+ \reimp
+ */
+QSGTextureProvider *QQuickRhiItem::textureProvider() const
+{
+ if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true
+ return QQuickItem::textureProvider();
+
+ Q_D(const QQuickRhiItem);
+ if (!d->node) // create a node to have a provider, the texture will be null but that's ok
+ d->node = new QQuickRhiItemNode(const_cast<QQuickRhiItem *>(this));
+
+ return d->node;
+}
+
+/*!
+ \property QQuickRhiItem::sampleCount
+
+ This property controls for sample count for multisample antialiasing.
+ By default the value is \c 1 which means MSAA is disabled.
+
+ Valid values are 1, 4, 8, and sometimes 16 and 32.
+ \l QRhi::supportedSampleCounts() can be used to query the supported sample
+ counts at run time, but typically applications should request 1 (no MSAA),
+ 4x (normal MSAA) or 8x (high MSAA).
+
+ \note Setting a new value implies that all QRhiGraphicsPipeline objects
+ created by the renderer must use the same sample count from then on.
+ Existing QRhiGraphicsPipeline objects created with a different sample count
+ must not be used anymore. When the value changes, all color and
+ depth-stencil buffers are destroyed and recreated automatically, and
+ \l {QQuickRhiItemRenderer::}{initialize()} is invoked again. However, when
+ isAutoRenderTargetEnabled() is \c false, it will be up to the application to
+ manage this with regards to the depth-stencil buffer or additional color
+ buffers.
+
+ Changing the sample count from the default 1 to a higher value implies that
+ \l {QQuickRhiItemRenderer::}{colorTexture()} becomes \nullptr and
+ \l {QQuickRhiItemRenderer::}{msaaColorBuffer()} starts returning a
+ valid object. Switching back to 1 (or 0), implies the opposite: in the next
+ call to initialize() msaaColorBuffer() is going to return \nullptr, whereas
+ colorTexture() becomes once again valid. In addition,
+ \l {QQuickRhiItemRenderer::}{resolveTexture()}
+ returns a valid (non-multisample) QRhiTexture whenever the sample count is
+ greater than 1 (i.e., MSAA is in use).
+
+ \sa QQuickRhiItemRenderer::msaaColorBuffer(),
+ QQuickRhiItemRenderer::resolveTexture()
+ */
+
+int QQuickRhiItem::sampleCount() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->samples;
+}
+
+void QQuickRhiItem::setSampleCount(int samples)
+{
+ Q_D(QQuickRhiItem);
+ if (d->samples == samples)
+ return;
+
+ d->samples = samples;
+ emit sampleCountChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::colorBufferFormat
+
+ This property controls the texture format for the texture used as the color
+ buffer. The default value is TextureFormat::RGBA8. QQuickRhiItem supports
+ rendering to a subset of the formats supported by \l QRhiTexture. Only
+ formats that are reported as supported from
+ \l QRhi::isTextureFormatSupported() should be specified, rendering will not be
+ functional otherwise.
+
+ \note Setting a new format when the item and its renderer are already
+ initialized and have rendered implies that all QRhiGraphicsPipeline objects
+ created by the renderer may become unusable, if the associated
+ QRhiRenderPassDescriptor is now incompatible due to the different texture
+ format. Similarly to changing
+ \l sampleCount dynamically, this means that initialize() or render()
+ implementations must then take care of releasing the existing pipelines and
+ creating new ones.
+ */
+
+QQuickRhiItem::TextureFormat QQuickRhiItem::colorBufferFormat() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->itemTextureFormat;
+}
+
+void QQuickRhiItem::setColorBufferFormat(TextureFormat format)
+{
+ Q_D(QQuickRhiItem);
+ if (d->itemTextureFormat == format)
+ return;
+
+ d->itemTextureFormat = format;
+ switch (format) {
+ case TextureFormat::RGBA8:
+ d->rhiTextureFormat = QRhiTexture::RGBA8;
+ break;
+ case TextureFormat::RGBA16F:
+ d->rhiTextureFormat = QRhiTexture::RGBA16F;
+ break;
+ case TextureFormat::RGBA32F:
+ d->rhiTextureFormat = QRhiTexture::RGBA32F;
+ break;
+ case TextureFormat::RGB10A2:
+ d->rhiTextureFormat = QRhiTexture::RGB10A2;
+ break;
+ }
+ emit colorBufferFormatChanged();
+ update();
+}
+
+/*!
+ \return the current automatic depth-stencil buffer and render target management setting.
+
+ By default this value is \c true.
+
+ \sa setAutoRenderTarget()
+ */
+bool QQuickRhiItem::isAutoRenderTargetEnabled() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->autoRenderTarget;
+}
+
+/*!
+ Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
+ is created and maintained automatically by the item. The default value is
+ \c true. Call this function early on, for example from the derived class'
+ constructor, with \a enabled set to \c false to disable this.
+
+ In automatic mode, the size and sample count of the depth-stencil buffer
+ follows the color buffer texture's settings. In non-automatic mode,
+ renderTarget() and depthStencilBuffer() always return \nullptr and it is
+ then up to the application's implementation of initialize() to take care of
+ setting up and managing these objects.
+ */
+void QQuickRhiItem::setAutoRenderTarget(bool enabled)
+{
+ Q_D(QQuickRhiItem);
+ if (d->autoRenderTarget == enabled)
+ return;
+
+ d->autoRenderTarget = enabled;
+ emit autoRenderTargetChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::mirrorVertically
+
+ This property controls if texture UVs are flipped when drawing the textured
+ quad. It has no effect on the contents of the offscreen color buffer and
+ the rendering implemented by the QQuickRhiItemRenderer.
+
+ The default value is \c false.
+ */
+
+bool QQuickRhiItem::isMirrorVerticallyEnabled() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->mirrorVertically;
+}
+
+void QQuickRhiItem::setMirrorVertically(bool enable)
+{
+ Q_D(QQuickRhiItem);
+ if (d->mirrorVertically == enable)
+ return;
+
+ d->mirrorVertically = enable;
+ emit mirrorVerticallyChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::fixedColorBufferWidth
+
+ The fixed width, in pixels, of the item's associated texture or
+ renderbuffer. Relevant when a fixed color buffer size is desired that does
+ not depend on the item's size. This size has no effect on the geometry of
+ the item (its size and placement within the scene), which means the
+ texture's content will appear stretched (scaled up) or scaled down onto the
+ item's area.
+
+ For example, setting a size that is exactly twice the item's (pixel) size
+ effectively performs 2x supersampling (rendering at twice the resolution
+ and then implicitly scaling down when texturing the quad corresponding to
+ the item in the scene).
+
+ By default the value is \c 0. A value of 0 means that texture's size
+ follows the item's size. (\c{texture size} = \c{item size} * \c{device
+ pixel ratio}).
+ */
+int QQuickRhiItem::fixedColorBufferWidth() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->fixedTextureWidth;
+}
+
+void QQuickRhiItem::setFixedColorBufferWidth(int width)
+{
+ Q_D(QQuickRhiItem);
+ if (d->fixedTextureWidth == width)
+ return;
+
+ d->fixedTextureWidth = width;
+ emit fixedColorBufferWidthChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::fixedColorBufferHeight
+
+ The fixed height, in pixels, of the item's associated texture. Relevant when
+ a fixed texture size is desired that does not depend on the item's size.
+ This size has no effect on the geometry of the item (its size and placement
+ within the scene), which means the texture's content will appear stretched
+ (scaled up) or scaled down onto the item's area.
+
+ For example, setting a size that is exactly twice the item's (pixel) size
+ effectively performs 2x supersampling (rendering at twice the resolution
+ and then implicitly scaling down when texturing the quad corresponding to
+ the item in the scene).
+
+ By default the value is \c 0. A value of 0 means that texture's size
+ follows the item's size. (\c{texture size} = \c{item size} * \c{device
+ pixel ratio}).
+ */
+
+int QQuickRhiItem::fixedColorBufferHeight() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->fixedTextureHeight;
+}
+
+void QQuickRhiItem::setFixedColorBufferHeight(int height)
+{
+ Q_D(QQuickRhiItem);
+ if (d->fixedTextureHeight == height)
+ return;
+
+ d->fixedTextureHeight = height;
+ emit fixedColorBufferHeightChanged();
+ update();
+}
+
+/*!
+ \property QQuickRhiItem::effectiveColorBufferSize
+
+ This property exposes the size, in pixels, of the underlying color buffer
+ (the QRhiTexture or QRhiRenderBuffer). It is provided for use on the GUI
+ (main) thread, in QML bindings or JavaScript.
+
+ \note QQuickRhiItemRenderer implementations, operating on the scene graph
+ render thread, should not use this property. Those should rather query the
+ size from the
+ \l{QQuickRhiItemRenderer::renderTarget()}{render target}.
+
+ \note The value becomes available asynchronously from the main thread's
+ perspective in the sense that the value changes when rendering happens on
+ the render thread. This means that this property is useful mainly in QML
+ bindings. Application code must not assume that the value is up to date
+ already when the QQuickRhiItem object is constructed.
+
+ This is a read-only property.
+ */
+
+QSize QQuickRhiItem::effectiveColorBufferSize() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->effectiveTextureSize;
+}
+
+/*!
+ \property QQuickRhiItem::alphaBlending
+
+ Controls if blending is always enabled when drawing the quad textured with
+ the content generated by the QQuickRhiItem and its renderer.
+
+ The default value is \c false. This is for performance reasons: if
+ semi-transparency is not involved, because the QQuickRhiItemRenderer clears
+ to an opaque color and never renders fragments with alpha smaller than 1,
+ then there is no point in enabling blending.
+
+ If the QQuickRhiItemRenderer subclass renders with semi-transparency involved,
+ set this property to true.
+
+ \note Under certain conditions blending is still going to happen regardless
+ of the value of this property. For example, if the item's
+ \l{QQuickItem::opacity}{opacity} (more precisely, the combined opacity
+ inherited from the parent chain) is smaller than 1, blending will be
+ automatically enabled even when this property is set to false.
+
+ \note The Qt Quick scene graph relies on and expect pre-multiplied alpha.
+ For example, if the intention is to clear the background in the renderer to
+ an alpha value of 0.5, then make sure to multiply the red, green, and blue
+ clear color values with 0.5 as well. Otherwise the blending results will be
+ incorrect.
+ */
+
+bool QQuickRhiItem::alphaBlending() const
+{
+ Q_D(const QQuickRhiItem);
+ return d->blend;
+}
+
+void QQuickRhiItem::setAlphaBlending(bool enable)
+{
+ Q_D(QQuickRhiItem);
+ if (d->blend == enable)
+ return;
+
+ d->blend = enable;
+ emit alphaBlendingChanged();
+ update();
+}
+
+/*!
+ Constructs a new renderer.
+
+ This function is called on the rendering thread during the scene graph sync
+ phase when the GUI thread is blocked.
+
+ \sa QQuickRhiItem::createRenderer()
+ */
+QQuickRhiItemRenderer::QQuickRhiItemRenderer()
+{
+}
+
+/*!
+ The Renderer is automatically deleted when the scene graph resources for
+ the QQuickRhiItem item are cleaned up.
+
+ This function is called on the rendering thread.
+
+ Under certain conditions it is normal and expected that the renderer object
+ is destroyed and then recreated. This is because the renderer's lifetime
+ effectively follows the underlying scene graph node. For example, when
+ changing the parent of a QQuickRhiItem object so that it then belongs to a
+ different \l QQuickWindow, the scene graph nodes are all dropped and
+ recreated due to the window change. This will also involve dropping and
+ creating a new QQuickRhiItemRenderer.
+
+ Unlike \l QRhiWidget, QQuickRhiItemRenderer has no need to implement
+ additional code paths for releasing (or early-relasing) graphics resources
+ created via QRhi. It is sufficient to release everything in the destructor,
+ or rely on smart pointers.
+ */
+QQuickRhiItemRenderer::~QQuickRhiItemRenderer()
+{
+}
+
+/*!
+ Call this function when the content of the offscreen color buffer should be
+ updated. (i.e. to request that render() is called again; the call will
+ happen at a later point, and note that updates are typically throttled to
+ the presentation rate)
+
+ This function can be called from render() to schedule an update.
+
+ \note This function should be used from inside the renderer. To update
+ the item on the GUI thread, use QQuickRhiItem::update().
+ */
+void QQuickRhiItemRenderer::update()
+{
+ if (node)
+ node->scheduleUpdate();
+}
+
+/*!
+ \return the current QRhi object.
+
+ Must only be called from initialize() and render().
+ */
+QRhi *QQuickRhiItemRenderer::rhi() const
+{
+ return node ? node->m_rhi : nullptr;
+}
+
+/*!
+ \return the texture serving as the color buffer for the item.
+
+ Must only be called from initialize() and render().
+
+ Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
+ always available and is managed by the QQuickRhiItem, independent of the
+ value of \l {QQuickRhiItem::}{isAutoRenderTargetEnabled}.
+
+ \note When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so
+ multisample antialiasing is enabled, the return value is \nullptr. Instead,
+ query the \l QRhiRenderBuffer by calling msaaColorBuffer().
+
+ \note The backing texture size and sample count can also be queried via the
+ QRhiRenderTarget returned from renderTarget(). This can be more convenient
+ and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
+ it works regardless of multisampling is in use or not.
+
+ \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
+ */
+QRhiTexture *QQuickRhiItemRenderer::colorTexture() const
+{
+ return node ? node->m_colorTexture : nullptr;
+}
+
+/*!
+ \return the renderbuffer serving as the multisample color buffer for the item.
+
+ Must only be called from initialize() and render().
+
+ When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so multisample
+ antialising is enabled, the returned QRhiRenderBuffer has a matching sample
+ count and serves as the color buffer. Graphics pipelines used to render
+ into this buffer must be created with the same sample count, and the
+ depth-stencil buffer's sample count must match as well. The multisample
+ content is expected to be resolved into the texture returned from
+ resolveTexture(). When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is
+ \c true, renderTarget() is set up automatically to do this, by setting up
+ msaaColorBuffer() as the
+ \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of color attachment 0
+ and resolveTexture() as its
+ \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
+
+ When MSAA is not in use, the return value is \nullptr. Use colorTexture()
+ instead then.
+
+ Depending on the underlying 3D graphics API, there may be no practical
+ difference between multisample textures and color renderbuffers with a
+ sample count larger than 1 (QRhi may just map both to the same native
+ resource type). Some older APIs however may differentiate between textures
+ and renderbuffers. In order to support OpenGL ES 3.0, where multisample
+ renderbuffers are available, but multisample textures are not, QQuickRhiItem
+ always performs MSAA by using a multisample QRhiRenderBuffer as the color
+ attachment (and never a multisample QRhiTexture).
+
+ \note The backing texture size and sample count can also be queried via the
+ QRhiRenderTarget returned from renderTarget(). This can be more convenient
+ and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
+ it works regardless of multisampling is in use or not.
+
+ \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
+ */
+QRhiRenderBuffer *QQuickRhiItemRenderer::msaaColorBuffer() const
+{
+ return node ? node->m_msaaColorBuffer.get() : nullptr;
+}
+
+/*!
+ \return the non-multisample texture to which the multisample content is resolved.
+
+ The result is \nullptr when multisample antialiasing is not enabled.
+
+ Must only be called from initialize() and render().
+
+ With MSAA enabled, this is the texture that gets used by the item's
+ underlying scene graph node when texturing a quad in the main render pass
+ of Qt Quick. However, the QQuickRhiItemRenderer's rendering must target the
+ (multisample) QRhiRenderBuffer returned from msaaColorBuffer(). When \l
+ {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, this is taken care
+ of by the QRhiRenderTarget returned from renderTarget(). Otherwise, it is
+ up to the subclass code to correctly configure a render target object with
+ both the color buffer and resolve textures.
+
+ \sa colorTexture()
+ */
+QRhiTexture *QQuickRhiItemRenderer::resolveTexture() const
+{
+ return node ? node->m_resolveTexture : nullptr;
+}
+
+/*!
+ \return the depth-stencil buffer used by the item's rendering.
+
+ Must only be called from initialize() and render().
+
+ Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
+ true. Otherwise the returned value is \nullptr and it is up the
+ reimplementation of initialize() to create and manage a depth-stencil
+ buffer and a QRhiTextureRenderTarget.
+
+ \sa colorTexture(), renderTarget()
+ */
+QRhiRenderBuffer *QQuickRhiItemRenderer::depthStencilBuffer() const
+{
+ return node ? node->m_depthStencilBuffer.get() : nullptr;
+}
+
+/*!
+ \return the render target object that must be used with
+ \l QRhiCommandBuffer::beginPass() in reimplementations of render().
+
+ Must only be called from initialize() and render().
+
+ Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
+ true. Otherwise the returned value is \nullptr and it is up the
+ reimplementation of initialize() to create and manage a depth-stencil
+ buffer and a QRhiTextureRenderTarget.
+
+ When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
+ QRhiRenderPassDescriptor is needed. This can be queried from the returned
+ QRhiTextureRenderTarget by calling
+ \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
+
+ \note The returned QRhiTextureRenderTarget always reports a
+ \l{QRhiTextureRenderTarget::}{devicePixelRatio()} of \c 1.
+ This is because only swapchains and the associated window have a concept of
+ device pixel ratio, not textures, and the render target here always refers
+ to a texture. If the on-screen scale factor is relevant for rendering,
+ query and store it via the item's
+ \c{window()->effectiveDevicePixelRatio()} in \l synchronize().
+ When doing so, always prefer using \l{QQuickWindow::}{effectiveDevicePixelRatio()}
+ over the base class' \l{QWindow::}{devicePixelRatio()}.
+
+ \sa colorTexture(), depthStencilBuffer(), QQuickWindow::effectiveDevicePixelRatio()
+ */
+QRhiRenderTarget *QQuickRhiItemRenderer::renderTarget() const
+{
+ return node ? node->m_renderTarget.get() : nullptr;
+}
+
+/*!
+ \fn QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()
+
+ Reimplement this function to create and return a new instance of a
+ QQuickRhiItemRenderer subclass.
+
+ This function will be called on the rendering thread while the GUI thread
+ is blocked.
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
+
+ Called when the item is initialized for the first time, when the
+ associated texture's size, format, or sample count changes, or when the
+ QRhi or texture change for any reason. The function is expected to
+ maintain (create if not yet created, adjust and rebuild if the size has
+ changed) the graphics resources used by the rendering code in render().
+
+ To query the QRhi, QRhiTexture, and other related objects, call rhi(),
+ colorTexture(), depthStencilBuffer(), and renderTarget().
+
+ When the item size changes, the QRhi object, the color buffer texture,
+ and the depth stencil buffer objects are all the same instances (so the
+ getters return the same pointers) as before, but the color and
+ depth/stencil buffers will likely have been rebuilt, meaning the
+ \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
+ resource may be different than in the last invocation.
+
+ Reimplementations should also be prepared that the QRhi object and the
+ color buffer texture may change between invocations of this function. For
+ example, when the item is reparented so that it belongs to a new
+ QQuickWindow, the the QRhi and all related resources managed by the
+ QQuickRhiItem will be different instances than before in the subsequent
+ call to this function. Is is then important that all existing QRhi
+ resources previously created by the subclass are destroyed because they
+ belong to the previous QRhi that should not be used anymore.
+
+ When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, which is
+ the default, a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
+ associated with the colorTexture() (or msaaColorBuffer()) and the
+ depth-stencil buffer are created and managed automatically.
+ Reimplementations of initialize() and render() can query those objects via
+ depthStencilBuffer() and renderTarget(). When \l
+ {QQuickRhiItem::}{isAutoRenderTargetEnabled} is set to \c false, these
+ objects are no longer created and managed automatically. Rather, it will be
+ up the the initialize() implementation to create buffers and set up the
+ render target as it sees fit. When manually managing additional color or
+ depth-stencil attachments for the render target, their size and sample
+ count must always follow the size and sample count of colorTexture() (or
+ msaaColorBuffer()), otherwise rendering or 3D API validation errors may
+ occur.
+
+ The subclass-created graphics resources are expected to be released in the
+ destructor implementation of the subclass.
+
+ \a cb is the QRhiCommandBuffer for the current frame. The function is
+ called with a frame being recorded, but without an active render pass. The
+ command buffer is provided primarily to allow enqueuing
+ \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
+ to render().
+
+ This function is called on the render thread, if there is one.
+
+ \sa render()
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::synchronize(QQuickRhiItem *item)
+
+ This function is called on the render thread, if there is one, while the
+ main/GUI thread is blocked. It is called from
+ \l{QQuickItem::updatePaintNode()}{the \a {item}'s synchronize step},
+ and allows reading and writing data belonging to the main and render
+ threads. Typically property values stored in the QQuickRhiItem are copied
+ into the QQuickRhiItemRenderer, so that they can be safely read afterwards
+ in render() when the render and main threads continue to work in parallel.
+
+ \sa initialize(), render()
+ */
+
+/*!
+ \fn void QQuickRhiItemRenderer::render(QRhiCommandBuffer *cb)
+
+ Called when the backing color buffer's contents needs updating.
+
+ There is always at least one call to initialize() before this function is
+ called.
+
+ To request updates, call \l QQuickItem::update() when calling from QML or
+ from C++ code on the main/GUI thread (e.g. when in a property setter), or
+ \l update() when calling from within a QQuickRhiItemRenderer callback.
+ Calling QQuickRhiItemRenderer's update() from within
+ render() will lead to triggering updates continuously.
+
+ \a cb is the QRhiCommandBuffer for the current frame. The function is
+ called with a frame being recorded, but without an active render pass.
+
+ This function is called on the render thread, if there is one.
+
+ \sa initialize(), synchronize()
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrhiitem.h b/src/quick/items/qquickrhiitem.h
new file mode 100644
index 0000000000..78bfb9fe4b
--- /dev/null
+++ b/src/quick/items/qquickrhiitem.h
@@ -0,0 +1,124 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKRHIITEM_H
+#define QQUICKRHIITEM_H
+
+#include <QtQuick/QQuickItem>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRhiItem;
+class QQuickRhiItemPrivate;
+class QQuickRhiItemNode;
+class QRhi;
+class QRhiCommandBuffer;
+class QRhiTexture;
+class QRhiRenderBuffer;
+class QRhiRenderTarget;
+
+class Q_QUICK_EXPORT QQuickRhiItemRenderer
+{
+public:
+ QQuickRhiItemRenderer();
+ virtual ~QQuickRhiItemRenderer();
+
+protected:
+ virtual void initialize(QRhiCommandBuffer *cb) = 0;
+ virtual void synchronize(QQuickRhiItem *item) = 0;
+ virtual void render(QRhiCommandBuffer *cb) = 0;
+
+ void update();
+
+ QRhi *rhi() const;
+ QRhiTexture *colorTexture() const;
+ QRhiRenderBuffer *msaaColorBuffer() const;
+ QRhiTexture *resolveTexture() const;
+ QRhiRenderBuffer *depthStencilBuffer() const;
+ QRhiRenderTarget *renderTarget() const;
+
+private:
+ QQuickRhiItemNode *node;
+ friend class QQuickRhiItem;
+ friend class QQuickRhiItemNode;
+
+ Q_DISABLE_COPY_MOVE(QQuickRhiItemRenderer)
+};
+
+class Q_QUICK_EXPORT QQuickRhiItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickRhiItem)
+
+ Q_PROPERTY(int sampleCount READ sampleCount WRITE setSampleCount NOTIFY sampleCountChanged FINAL)
+ Q_PROPERTY(TextureFormat colorBufferFormat READ colorBufferFormat WRITE setColorBufferFormat NOTIFY colorBufferFormatChanged FINAL)
+ Q_PROPERTY(bool mirrorVertically READ isMirrorVerticallyEnabled WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged FINAL)
+ Q_PROPERTY(bool alphaBlending READ alphaBlending WRITE setAlphaBlending NOTIFY alphaBlendingChanged FINAL)
+ Q_PROPERTY(int fixedColorBufferWidth READ fixedColorBufferWidth WRITE setFixedColorBufferWidth NOTIFY fixedColorBufferWidthChanged FINAL)
+ Q_PROPERTY(int fixedColorBufferHeight READ fixedColorBufferHeight WRITE setFixedColorBufferHeight NOTIFY fixedColorBufferHeightChanged FINAL)
+ Q_PROPERTY(QSize effectiveColorBufferSize READ effectiveColorBufferSize NOTIFY effectiveColorBufferSizeChanged FINAL)
+
+public:
+ enum class TextureFormat {
+ RGBA8,
+ RGBA16F,
+ RGBA32F,
+ RGB10A2
+ };
+ Q_ENUM(TextureFormat)
+
+ explicit QQuickRhiItem(QQuickItem *parent = nullptr);
+ ~QQuickRhiItem() override;
+
+ int sampleCount() const;
+ void setSampleCount(int samples);
+
+ TextureFormat colorBufferFormat() const;
+ void setColorBufferFormat(TextureFormat format);
+
+ bool isMirrorVerticallyEnabled() const;
+ void setMirrorVertically(bool enable);
+
+ bool alphaBlending() const;
+ void setAlphaBlending(bool enable);
+
+ int fixedColorBufferWidth() const;
+ void setFixedColorBufferWidth(int width);
+ int fixedColorBufferHeight() const;
+ void setFixedColorBufferHeight(int height);
+
+ QSize effectiveColorBufferSize() const;
+
+ bool isTextureProvider() const override;
+ QSGTextureProvider *textureProvider() const override;
+
+Q_SIGNALS:
+ void sampleCountChanged();
+ void colorBufferFormatChanged();
+ void autoRenderTargetChanged();
+ void mirrorVerticallyChanged();
+ void alphaBlendingChanged();
+ void fixedColorBufferWidthChanged();
+ void fixedColorBufferHeightChanged();
+ void effectiveColorBufferSizeChanged();
+
+protected:
+ virtual QQuickRhiItemRenderer *createRenderer() = 0;
+
+ bool isAutoRenderTargetEnabled() const;
+ void setAutoRenderTarget(bool enabled);
+
+ QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override;
+ bool event(QEvent *) override;
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void releaseResources() override;
+
+private Q_SLOTS:
+ void invalidateSceneGraph();
+
+ friend class QQuickRhiItemNode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKRHIITEM_H
diff --git a/src/quick/items/qquickrhiitem_p.h b/src/quick/items/qquickrhiitem_p.h
new file mode 100644
index 0000000000..e60de7e55e
--- /dev/null
+++ b/src/quick/items/qquickrhiitem_p.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKRHIITEM_P_H
+#define QQUICKRHIITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickrhiitem.h"
+#include <QtQuick/QSGTextureProvider>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/private/qquickitem_p.h>
+#include <rhi/qrhi.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRhiItemNode : public QSGTextureProvider, public QSGSimpleTextureNode
+{
+ Q_OBJECT
+
+public:
+ QQuickRhiItemNode(QQuickRhiItem *item);
+
+ QSGTexture *texture() const override;
+
+ void sync();
+ QRhiCommandBuffer *queryCommandBuffer();
+ void resetColorBufferObjects();
+ void resetRenderTargetObjects();
+
+ bool isValid() const
+ {
+ return m_rhi && m_sgTexture;
+ }
+
+ void scheduleUpdate()
+ {
+ m_renderPending = true;
+ m_window->update(); // ensure getting to beforeRendering() at some point
+ }
+
+ bool hasRenderer() const
+ {
+ return m_renderer != nullptr;
+ }
+
+ void setRenderer(QQuickRhiItemRenderer *r)
+ {
+ m_renderer.reset(r);
+ }
+
+ QQuickRhiItem *m_item;
+ QQuickWindow *m_window;
+ QSize m_pixelSize;
+ qreal m_dpr = 0.0f;
+ QRhi *m_rhi = nullptr;
+ bool m_renderPending = true;
+ std::unique_ptr<QSGTexture> m_sgTexture;
+ std::unique_ptr<QQuickRhiItemRenderer> m_renderer;
+ QRhiTexture *m_colorTexture = nullptr;
+ QRhiTexture *m_resolveTexture = nullptr;
+ std::unique_ptr<QRhiRenderBuffer> m_msaaColorBuffer;
+ std::unique_ptr<QRhiRenderBuffer> m_depthStencilBuffer;
+ std::unique_ptr<QRhiTextureRenderTarget> m_renderTarget;
+ std::unique_ptr<QRhiRenderPassDescriptor> m_renderPassDescriptor;
+
+public slots:
+ void render();
+};
+
+class QQuickRhiItemPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickRhiItem)
+
+public:
+ static QQuickRhiItemPrivate *get(QQuickRhiItem *item) { return item->d_func(); }
+
+ mutable QQuickRhiItemNode *node = nullptr;
+ QQuickRhiItem::TextureFormat itemTextureFormat = QQuickRhiItem::TextureFormat::RGBA8;
+ QRhiTexture::Format rhiTextureFormat = QRhiTexture::RGBA8;
+ int samples = 1;
+ bool autoRenderTarget = true;
+ bool mirrorVertically = false;
+ bool blend = false;
+ int fixedTextureWidth = 0;
+ int fixedTextureHeight = 0;
+ QSize effectiveTextureSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h
index e89001ceff..20edb3b0cb 100644
--- a/src/quick/items/qquickscalegrid_p_p.h
+++ b/src/quick/items/qquickscalegrid_p_p.h
@@ -20,19 +20,19 @@
#include <QtQml/qqml.h>
#include <QtCore/qobject.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickScaleGrid : public QObject
+class Q_QUICK_EXPORT QQuickScaleGrid : public QObject
{
Q_OBJECT
- Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged)
- Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged)
- Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged)
- Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged)
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged FINAL)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged FINAL)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged FINAL)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -99,6 +99,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScaleGrid)
-
#endif // QQUICKSCALEGRID_P_P_H
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index ea3ae58fd0..c8405ca9ad 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -39,6 +39,8 @@ QT_BEGIN_NAMESPACE
Note that the Screen type is not valid at Component.onCompleted, because
the Item or Window has not been displayed on a screen by this time.
+
+ \sa {Qt Quick Examples - Window and Screen}
*/
/*!
diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h
index 059c33b0b7..02d33a1523 100644
--- a/src/quick/items/qquickscreen_p.h
+++ b/src/quick/items/qquickscreen_p.h
@@ -18,6 +18,8 @@
#include <QtQml/qqml.h>
#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
@@ -26,25 +28,25 @@ class QQuickWindow;
class QScreen;
-class Q_QUICK_PRIVATE_EXPORT QQuickScreenInfo : public QObject
+class Q_QUICK_EXPORT QQuickScreenInfo : public QObject
{
Q_OBJECT
- Q_PROPERTY(QString name READ name NOTIFY nameChanged)
- Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION(2, 10))
- Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION(2, 10))
- Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION(2, 10))
- Q_PROPERTY(int width READ width NOTIFY widthChanged)
- Q_PROPERTY(int height READ height NOTIFY heightChanged)
- Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged)
- Q_PROPERTY(int desktopAvailableHeight READ desktopAvailableHeight NOTIFY desktopGeometryChanged)
- Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged)
- Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY pixelDensityChanged)
- Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged)
- Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged)
- Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
-
- Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION(2, 3))
- Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION(2, 3))
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged FINAL)
+ Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION(2, 10) FINAL)
+ Q_PROPERTY(int width READ width NOTIFY widthChanged FINAL)
+ Q_PROPERTY(int height READ height NOTIFY heightChanged FINAL)
+ Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged FINAL)
+ Q_PROPERTY(int desktopAvailableHeight READ desktopAvailableHeight NOTIFY desktopGeometryChanged FINAL)
+ Q_PROPERTY(qreal logicalPixelDensity READ logicalPixelDensity NOTIFY logicalPixelDensityChanged FINAL)
+ Q_PROPERTY(qreal pixelDensity READ pixelDensity NOTIFY pixelDensityChanged FINAL)
+ Q_PROPERTY(qreal devicePixelRatio READ devicePixelRatio NOTIFY devicePixelRatioChanged FINAL)
+ Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged FINAL)
+ Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged FINAL)
+
+ Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION(2, 3) FINAL)
+ Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION(2, 3) FINAL)
QML_NAMED_ELEMENT(ScreenInfo)
QML_ADDED_IN_VERSION(2, 3)
QML_UNCREATABLE("ScreenInfo can only be used via the attached property.")
@@ -91,7 +93,7 @@ protected:
QPointer<QScreen> m_screen;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScreenAttached : public QQuickScreenInfo
+class Q_QUICK_EXPORT QQuickScreenAttached : public QQuickScreenInfo
{
Q_OBJECT
@@ -114,7 +116,7 @@ private:
QQuickItem* m_attachee;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickScreen : public QObject
+class Q_QUICK_EXPORT QQuickScreen : public QObject
{
Q_OBJECT
QML_ATTACHED(QQuickScreenAttached)
@@ -128,6 +130,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScreenInfo)
-
#endif
diff --git a/src/quick/items/qquickselectable_p.h b/src/quick/items/qquickselectable_p.h
index 726352719f..65434be490 100644
--- a/src/quick/items/qquickselectable_p.h
+++ b/src/quick/items/qquickselectable_p.h
@@ -20,19 +20,26 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickSelectable
+class Q_QUICK_EXPORT QQuickSelectable
{
public:
+ enum class CallBackFlag {
+ CancelSelection,
+ SelectionRectangleChanged
+ };
+
virtual QQuickItem *selectionPointerHandlerTarget() const = 0;
- virtual bool startSelection(const QPointF &pos) = 0;
+ virtual bool startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers) = 0;
virtual void setSelectionStartPos(const QPointF &pos) = 0;
virtual void setSelectionEndPos(const QPointF &pos) = 0;
virtual void clearSelection() = 0;
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;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index d47203057f..7dea334785 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -486,6 +486,15 @@ QT_BEGIN_NAMESPACE
\li Samplers must use binding points starting from 1.
+ \li When Qt Quick is rendering with \c multiview enabled, e.g. because it is
+ part of a 3D scene rendering in a VR/AR environment where the left and right
+ eye content are generated in a single pass, the ShaderEffect's shaders have
+ to be written with this in mind. With a view count of 2 for example, there
+ will be \c 2 matrices (qt_Matrix is an array of mat4 with two elements). The
+ vertex shader is expected to take \c gl_ViewIndex into account. See the \c
+ Multiview section in the \l{QSB Manual} for general information on creating
+ multiview-capable shaders.
+
\endlist
\sa {Item Layers}, {QSB Manual}, {Qt Shader Tools Build System Integration}
@@ -641,11 +650,9 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
This property defines which sides of the item should be visible.
- \list
- \li ShaderEffect.NoCulling - Both sides are visible
- \li ShaderEffect.BackFaceCulling - only front side is visible
- \li ShaderEffect.FrontFaceCulling - only back side is visible
- \endlist
+ \value ShaderEffect.NoCulling Both sides are visible
+ \value ShaderEffect.BackFaceCulling only the front side is visible
+ \value ShaderEffect.FrontFaceCulling only the back side is visible
The default is NoCulling.
*/
@@ -700,11 +707,9 @@ void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
This property tells the current status of the shaders.
- \list
- \li ShaderEffect.Compiled - the shader program was successfully compiled and linked.
- \li ShaderEffect.Uncompiled - the shader program has not yet been compiled.
- \li ShaderEffect.Error - the shader program failed to compile or link.
- \endlist
+ \value ShaderEffect.Compiled the shader program was successfully compiled and linked.
+ \value ShaderEffect.Uncompiled the shader program has not yet been compiled.
+ \value ShaderEffect.Error the shader program failed to compile or link.
When setting the fragment or vertex shader source code, the status will
become Uncompiled. The first time the ShaderEffect is rendered with new
@@ -985,9 +990,11 @@ void QQuickShaderEffectPrivate::handleEvent(QEvent *event)
{
if (event->type() == QEvent::DynamicPropertyChange) {
const auto propertyName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
- const auto mappedId = findMappedShaderVariableId(propertyName);
- if (mappedId)
- propertyChanged(*mappedId);
+ for (int i = 0; i < NShader; ++i) {
+ const auto mappedId = findMappedShaderVariableId(propertyName, Shader(i));
+ if (mappedId)
+ propertyChanged(*mappedId);
+ }
}
}
@@ -1039,6 +1046,7 @@ QSGNode *QQuickShaderEffectPrivate::handleUpdatePaintNode(QSGNode *oldNode, QQui
sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
node->syncMaterial(&sd);
@@ -1121,6 +1129,7 @@ bool QQuickShaderEffectPrivate::updateUniformValue(const QByteArray &name, const
sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
sd.fragment.dirtyTextures = {};
sd.materialTypeCacheKey = q->window();
+ sd.viewCount = QQuickWindowPrivate::get(q->window())->multiViewCount();
node->syncMaterial(&sd);
@@ -1274,7 +1283,7 @@ bool QQuickShaderEffectPrivate::updateShader(Shader shaderType, const QUrl &file
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
+ v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
@@ -1442,6 +1451,17 @@ std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const Q
return {};
}
+std::optional<int> QQuickShaderEffectPrivate::findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const
+{
+ const auto &vars = m_shaders[shaderType].shaderInfo.variables;
+ for (int idx = 0; idx < vars.size(); ++idx) {
+ if (vars[idx].name == name)
+ return indexToMappedId(shaderType, idx);
+ }
+
+ return {};
+}
+
bool QQuickShaderEffectPrivate::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
{
for (int shaderType = 0; shaderType < NShader; ++shaderType) {
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index 08db23394e..cfd95864de 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QQuickShaderEffectPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
+class Q_QUICK_EXPORT QQuickShaderEffect : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QUrl fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)
diff --git a/src/quick/items/qquickshadereffect_p_p.h b/src/quick/items/qquickshadereffect_p_p.h
index 2310b6d366..e7224e50a9 100644
--- a/src/quick/items/qquickshadereffect_p_p.h
+++ b/src/quick/items/qquickshadereffect_p_p.h
@@ -92,6 +92,7 @@ private:
void disconnectSignals(Shader shaderType);
void clearMappers(Shader shaderType);
std::optional<int> findMappedShaderVariableId(const QByteArray &name) const;
+ std::optional<int> findMappedShaderVariableId(const QByteArray &name, Shader shaderType) const;
bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
bool inDestructor = false;
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index e647daef56..f8a8be1b9f 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -31,13 +31,13 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
-Q_QUICK_PRIVATE_EXPORT const char *qtPositionAttributeName();
-Q_QUICK_PRIVATE_EXPORT const char *qtTexCoordAttributeName();
+Q_QUICK_EXPORT const char *qtPositionAttributeName();
+Q_QUICK_EXPORT const char *qtTexCoordAttributeName();
class QSGGeometry;
class QRectF;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMesh : public QObject
+class Q_QUICK_EXPORT QQuickShaderEffectMesh : public QObject
{
Q_OBJECT
@@ -62,7 +62,7 @@ protected:
QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent = nullptr);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
+class Q_QUICK_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
{
Q_OBJECT
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 9df53ff9e9..f9e52a9a6f 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -231,12 +231,10 @@ QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
The default value is \c{ShaderEffectSource.ClampToEdge}.
- \list
- \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
- \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
- \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
- \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
- \endlist
+ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
+ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
\note Some OpenGL ES 2 implementations do not support the GL_REPEAT
wrap mode with non-power-of-two textures.
@@ -385,14 +383,12 @@ void QQuickShaderEffectSource::setTextureSize(const QSize &size)
Modifying this property makes most sense when the item is used as a
source texture of a \l ShaderEffect.
- \list
- \li ShaderEffectSource.RGBA8
- \li ShaderEffectSource.RGBA16F
- \li ShaderEffectSource.RGBA32F
- \li ShaderEffectSource.Alpha - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGB - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \li ShaderEffectSource.RGBA - Starting with Qt 6.0, this value is not in use and has the same effect as RGBA8 in practice.
- \endlist
+ \value ShaderEffectSource.RGBA8
+ \value ShaderEffectSource.RGBA16F
+ \value ShaderEffectSource.RGBA32F
+ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
*/
QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
@@ -524,11 +520,9 @@ void QQuickShaderEffectSource::setRecursive(bool enabled)
such as those specified by ShaderEffect. Mirroring has no effect on the UI representation of
the ShaderEffectSource item itself.
- \list
- \li ShaderEffectSource.NoMirroring - No mirroring
- \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
- \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
- \endlist
+ \value ShaderEffectSource.NoMirroring No mirroring
+ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
+ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
*/
QQuickShaderEffectSource::TextureMirroring QQuickShaderEffectSource::textureMirroring() const
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index b49df4f075..ea9a6842e1 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -39,7 +39,7 @@ class QSGSimpleRectNode;
class QQuickShaderEffectSourceTextureProvider;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, public QQuickItemChangeListener
+class Q_QUICK_EXPORT QQuickShaderEffectSource : public QQuickItem, public QQuickItemChangeListener
{
Q_OBJECT
Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index 3054b8b2a6..004944c628 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -23,7 +23,7 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include <QUrl>
#include <QVariantMap>
#include <QQmlListProperty>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include "qquickspriteengine_p.h"
#include <QDebug>
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index ee2fa34f72..3bd129cf98 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -28,13 +28,13 @@ QT_REQUIRE_CONFIG(quick_sprite);
#include <QImage>
#include <QPair>
#include <QRandomGenerator>
-#include <private/qquickpixmapcache_p.h>
+#include <private/qquickpixmap_p.h>
#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
class QQuickSprite;
-class Q_QUICK_PRIVATE_EXPORT QQuickStochasticState : public QObject //Currently for internal use only - Sprite and ParticleGroup
+class Q_QUICK_EXPORT QQuickStochasticState : public QObject //Currently for internal use only - Sprite and ParticleGroup
{
Q_OBJECT
Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
@@ -147,12 +147,12 @@ private:
bool m_randomStart = false;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject
+class Q_QUICK_EXPORT QQuickStochasticEngine : public QObject
{
Q_OBJECT
//TODO: Optimize single state case?
- Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged)
- Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states)
+ Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged FINAL)
+ Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states FINAL)
public:
explicit QQuickStochasticEngine(QObject *parent = nullptr);
QQuickStochasticEngine(const QList<QQuickStochasticState*> &states, QObject *parent = nullptr);
@@ -226,10 +226,10 @@ protected:
bool m_addAdvance;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
+class Q_QUICK_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites)
+ Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites FINAL)
public:
explicit QQuickSpriteEngine(QObject *parent = nullptr);
QQuickSpriteEngine(const QList<QQuickSprite*> &sprites, QObject *parent = nullptr);
diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h
index f4fc6def58..eb80401d6e 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -28,7 +28,7 @@ class QQuickSprite;
class QQuickSpriteEngine;
class QQuickSpriteSequencePrivate;
class QSGSpriteNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickSpriteSequence : public QQuickItem
+class Q_QUICK_EXPORT QQuickSpriteSequence : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index a6e5eed56c..35b29b9134 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -7,7 +7,10 @@
#include <private/qquickstate_p_p.h>
#include <QtQml/qqmlinfo.h>
+
#include <QtCore/qmath.h>
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
@@ -156,7 +159,7 @@ QQuickParentChange::QQuickParentChange(QObject *parent)
QQmlScriptString QQuickParentChange::x() const
{
Q_D(const QQuickParentChange);
- return d->xString.value;
+ return d->xString.value();
}
void QQuickParentChange::setX(const QQmlScriptString &x)
@@ -174,7 +177,7 @@ bool QQuickParentChange::xIsSet() const
QQmlScriptString QQuickParentChange::y() const
{
Q_D(const QQuickParentChange);
- return d->yString.value;
+ return d->yString.value();
}
void QQuickParentChange::setY(const QQmlScriptString &y)
@@ -192,7 +195,7 @@ bool QQuickParentChange::yIsSet() const
QQmlScriptString QQuickParentChange::width() const
{
Q_D(const QQuickParentChange);
- return d->widthString.value;
+ return d->widthString.value();
}
void QQuickParentChange::setWidth(const QQmlScriptString &width)
@@ -210,7 +213,7 @@ bool QQuickParentChange::widthIsSet() const
QQmlScriptString QQuickParentChange::height() const
{
Q_D(const QQuickParentChange);
- return d->heightString.value;
+ return d->heightString.value();
}
void QQuickParentChange::setHeight(const QQmlScriptString &height)
@@ -228,7 +231,7 @@ bool QQuickParentChange::heightIsSet() const
QQmlScriptString QQuickParentChange::scale() const
{
Q_D(const QQuickParentChange);
- return d->scaleString.value;
+ return d->scaleString.value();
}
void QQuickParentChange::setScale(const QQmlScriptString &scale)
@@ -246,7 +249,7 @@ bool QQuickParentChange::scaleIsSet() const
QQmlScriptString QQuickParentChange::rotation() const
{
Q_D(const QQuickParentChange);
- return d->rotationString.value;
+ return d->rotationString.value();
}
void QQuickParentChange::setRotation(const QQmlScriptString &rotation)
@@ -313,13 +316,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->xString.isValid()) {
bool ok = false;
- qreal x = d->xString.value.numberLiteral(&ok);
+ qreal x = d->xString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction xa(d->target, QLatin1String("x"), x);
actions << xa;
} else {
QQmlProperty property(d->target, QLatin1String("x"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->xString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->xString.value(), d->target, qmlContext(this));
QQuickStateAction xa;
xa.property = property;
xa.toBinding = newBinding;
@@ -331,13 +335,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->yString.isValid()) {
bool ok = false;
- qreal y = d->yString.value.numberLiteral(&ok);
+ qreal y = d->yString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ya(d->target, QLatin1String("y"), y);
actions << ya;
} else {
QQmlProperty property(d->target, QLatin1String("y"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->yString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->yString.value(), d->target, qmlContext(this));
QQuickStateAction ya;
ya.property = property;
ya.toBinding = newBinding;
@@ -349,13 +354,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->scaleString.isValid()) {
bool ok = false;
- qreal scale = d->scaleString.value.numberLiteral(&ok);
+ qreal scale = d->scaleString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction sa(d->target, QLatin1String("scale"), scale);
actions << sa;
} else {
QQmlProperty property(d->target, QLatin1String("scale"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->scaleString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->scaleString.value(), d->target, qmlContext(this));
QQuickStateAction sa;
sa.property = property;
sa.toBinding = newBinding;
@@ -367,13 +373,14 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->rotationString.isValid()) {
bool ok = false;
- qreal rotation = d->rotationString.value.numberLiteral(&ok);
+ qreal rotation = d->rotationString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation);
actions << ra;
} else {
QQmlProperty property(d->target, QLatin1String("rotation"));
- auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->rotationString.value, d->target, qmlContext(this));
+ auto newBinding = QQmlAnyBinding::createFromScriptString(
+ property, d->rotationString.value(), d->target, qmlContext(this));
QQuickStateAction ra;
ra.property = property;
ra.toBinding = newBinding;
@@ -385,7 +392,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->widthString.isValid()) {
bool ok = false;
- qreal width = d->widthString.value.numberLiteral(&ok);
+ qreal width = d->widthString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction wa(d->target, QLatin1String("width"), width);
actions << wa;
@@ -403,7 +410,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
if (d->heightString.isValid()) {
bool ok = false;
- qreal height = d->heightString.value.numberLiteral(&ok);
+ qreal height = d->heightString.value().numberLiteral(&ok);
if (ok) {
QQuickStateAction ha(d->target, QLatin1String("height"), height);
actions << ha;
@@ -1063,6 +1070,9 @@ void QQuickAnchorChanges::reverse()
stateHAnchors != QQuickAnchors::LeftAnchor &&
stateHAnchors != QQuickAnchors::RightAnchor &&
stateHAnchors != QQuickAnchors::HCenterAnchor);
+ // in case of an additive AnchorChange, we _did_ end up modifying the width
+ stateSetWidth |= ((stateHAnchors & QQuickAnchors::LeftAnchor) && (origHAnchors & QQuickAnchors::RightAnchor)) ||
+ ((stateHAnchors & QQuickAnchors::RightAnchor) && (origHAnchors & QQuickAnchors::LeftAnchor));
bool origSetWidth = (origHAnchors &&
origHAnchors != QQuickAnchors::LeftAnchor &&
origHAnchors != QQuickAnchors::RightAnchor &&
@@ -1078,12 +1088,15 @@ void QQuickAnchorChanges::reverse()
stateVAnchors != QQuickAnchors::BottomAnchor &&
stateVAnchors != QQuickAnchors::VCenterAnchor &&
stateVAnchors != QQuickAnchors::BaselineAnchor);
+ // in case of an additive AnchorChange, we _did_ end up modifying the height
+ stateSetHeight |= ((stateVAnchors & QQuickAnchors::TopAnchor) && (origVAnchors & QQuickAnchors::BottomAnchor)) ||
+ ((stateVAnchors & QQuickAnchors::BottomAnchor) && (origVAnchors & QQuickAnchors::TopAnchor));
bool origSetHeight = (origVAnchors &&
origVAnchors != QQuickAnchors::TopAnchor &&
origVAnchors != QQuickAnchors::BottomAnchor &&
origVAnchors != QQuickAnchors::VCenterAnchor &&
origVAnchors != QQuickAnchors::BaselineAnchor);
- if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) {
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !qt_is_nan(d->origHeight)) {
targetPrivate->heightValidFlag = true;
if (targetPrivate->height != d->origHeight)
targetPrivate->height.setValueBypassingBindings(d->origHeight);
@@ -1097,7 +1110,12 @@ void QQuickAnchorChanges::reverse()
const QRectF newGeometry(d->target->position(), d->target->size());
if (newGeometry != oldGeometry) {
- targetPrivate->dirty(QQuickItemPrivate::Position);
+ QQuickItemPrivate::DirtyType dirtyFlags {};
+ if (newGeometry.topLeft() != oldGeometry.topLeft())
+ dirtyFlags = QQuickItemPrivate::DirtyType(dirtyFlags | QQuickItemPrivate::Position);
+ if (newGeometry.size() != oldGeometry.size())
+ dirtyFlags = QQuickItemPrivate::DirtyType(dirtyFlags | QQuickItemPrivate::Size);
+ targetPrivate->dirty(dirtyFlags);
d->target->geometryChange(newGeometry, oldGeometry);
}
}
diff --git a/src/quick/items/qquickstateoperations_p.h b/src/quick/items/qquickstateoperations_p.h
index 54b9a8325b..170e5b3b14 100644
--- a/src/quick/items/qquickstateoperations_p.h
+++ b/src/quick/items/qquickstateoperations_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QQuickParentChangePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickParentChange : public QQuickStateOperation, public QQuickStateActionEvent
+class Q_QUICK_EXPORT QQuickParentChange : public QQuickStateOperation, public QQuickStateActionEvent
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickParentChange)
@@ -91,17 +91,17 @@ public:
class QQuickAnchorChanges;
class QQuickAnchorSetPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorSet : public QObject
+class Q_QUICK_EXPORT QQuickAnchorSet : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlScriptString left READ left WRITE setLeft RESET resetLeft)
- Q_PROPERTY(QQmlScriptString right READ right WRITE setRight RESET resetRight)
- Q_PROPERTY(QQmlScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter)
- Q_PROPERTY(QQmlScriptString top READ top WRITE setTop RESET resetTop)
- Q_PROPERTY(QQmlScriptString bottom READ bottom WRITE setBottom RESET resetBottom)
- Q_PROPERTY(QQmlScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter)
- Q_PROPERTY(QQmlScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline)
+ Q_PROPERTY(QQmlScriptString left READ left WRITE setLeft RESET resetLeft FINAL)
+ Q_PROPERTY(QQmlScriptString right READ right WRITE setRight RESET resetRight FINAL)
+ Q_PROPERTY(QQmlScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter FINAL)
+ Q_PROPERTY(QQmlScriptString top READ top WRITE setTop RESET resetTop FINAL)
+ Q_PROPERTY(QQmlScriptString bottom READ bottom WRITE setBottom RESET resetBottom FINAL)
+ Q_PROPERTY(QQmlScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter FINAL)
+ Q_PROPERTY(QQmlScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -146,7 +146,7 @@ private:
};
class QQuickAnchorChangesPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickAnchorChanges : public QQuickStateOperation, public QQuickStateActionEvent
+class Q_QUICK_EXPORT QQuickAnchorChanges : public QQuickStateOperation, public QQuickStateActionEvent
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnchorChanges)
@@ -185,9 +185,5 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickParentChange)
-QML_DECLARE_TYPE(QQuickAnchorSet)
-QML_DECLARE_TYPE(QQuickAnchorChanges)
-
#endif // QQUICKSTATEOPERATIONS_P_H
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 81eddb03ce..9499d16a61 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>
@@ -40,6 +42,16 @@
in a TableView. To create models with multiple columns, either use
\l TableModel or a C++ model that inherits QAbstractItemModel.
+ A TableView does not include headers by default. You can add headers
+ using the \l HorizontalHeaderView and \l VerticalHeaderView from
+ Qt Quick Controls.
+
+ \note TableView will only \l {isRowLoaded()}{load} as many delegate items as
+ needed to fill up the view. There is no guarantee that items outside the view
+ will be loaded, although TableView will sometimes pre-load items for
+ optimization reasons. Hence, a TableView with zero width or height might not
+ load any delegate items at all.
+
\section1 Example Usage
\section2 C++ Models
@@ -102,7 +114,7 @@
the width of the column. Otherwise, it will check if an explicit width has
been set with \l setColumnWidth(). If not, \l implicitColumnWidth() will be used.
The implicit width of a column is the same as the largest
- \l {implicit width}{QQuickItem::implicitWidth()} found among the currently loaded
+ \l {Item::implicitWidth}{implicit width} found among the currently loaded
delegate items in that column. Trying to set an explicit \c width directly on
a delegate has no effect, and will be ignored and overwritten. The same logic also
applies to row heights.
@@ -126,7 +138,7 @@
of the view, and is recalculated again if it's flicked back in. This means that if the
width depends on the \l implicitColumnWidth(), the calculation can be different each time,
depending on which row you're at when the column enters (since \l implicitColumnWidth()
- only considers the delegate items that are currently \l {loaded}{isColumnLoaded()}).
+ only considers the delegate items that are currently \l {isColumnLoaded()}{loaded}).
To avoid this, you should use a \l columnWidthProvider, or ensure that all the delegate
items in the same column have the same \c implicitWidth.
@@ -156,13 +168,13 @@
You can let the user edit table cells by providing an edit delegate. The
edit delegate will be instantiated according to the \l editTriggers, which
by default is when the user double taps on a cell, or presses e.g
- \l Qt.Key_Enter or \l Qt.Key_Return. The edit delegate is set using
+ \l Qt::Key_Enter or \l Qt::Key_Return. The edit delegate is set using
\l {TableView::editDelegate}, which is an attached property that you set
on the \l delegate. The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 0
- If the user presses Qt.Key_Enter or Qt.Key_Return while the edit delegate
+ If the user presses Qt::Key_Enter or Qt::Key_Return while the edit delegate
is active, TableView will emit the \l TableView::commit signal to the edit
delegate, so that it can write back the changed data to the model.
@@ -207,7 +219,7 @@
\snippet qml/tableview/overlay.qml 0
You could also parent the overlay directly to the cell instead of the
- \l contentItem. But doing so will be fragile since the cell is unloaded
+ \l {Flickable::}{contentItem}. But doing so will be fragile since the cell is unloaded
or reused whenever it's flicked out of the viewport.
\sa layoutChanged()
@@ -248,7 +260,7 @@
to let the user select cells.
\note By default, a cell will become
- \l {QQuickItemSelectionModel::currentIndex()}{current}, and any selections will
+ \l {ItemSelectionModel::currentIndex}{current}, and any selections will
be removed, when the user taps on it. If such default tap behavior is not wanted
(e.g if you use custom pointer handlers inside your delegate), you can set
\l pointerNavigationEnabled to \c false.
@@ -257,11 +269,17 @@
In order to support keyboard navigation, you need to assign an \l ItemSelectionModel
to the \l selectionModel property. TableView will then use this model to manipulate
- the model's \l {ItemSelectionModel::currentIndex}{currentIndex}. You can
- disable keyboard navigation fully (in case you want to implement your own key
- handlers) by setting \l keyNavigationEnabled to \c false. Below is an
- example that demonstrates how to use keyboard navigation together with
- \c current and \c selected properties:
+ the model's \l {ItemSelectionModel::currentIndex}{currentIndex}.
+
+ It's the responsibility of the delegate to render itself as
+ \l {ItemSelectionModel::currentIndex}{current}. You can do this by adding a
+ property \c {required property bool current} to it, and let the appearance
+ depend on its state. The \c current property's value is set by the TableView.
+ You can also disable keyboard navigation fully (in case you want to implement your
+ own key handlers) by setting \l keyNavigationEnabled to \c false.
+
+ The following example demonstrates how you can use keyboard navigation together
+ with \c current and \c selected properties:
\snippet qml/tableview/keyboard-navigation.qml 0
@@ -324,7 +342,7 @@
}
\endcode
- \sa mimeData(), dropMimeData(), QUndoStack, QUndoCommand, QClipboard
+ \sa QAbstractItemModel::mimeData(), QAbstractItemModel::dropMimeData(), QUndoStack, QUndoCommand, QClipboard
*/
/*!
@@ -442,11 +460,35 @@
\l {Item::}{implicitHeight}. The TableView lays out the items based on that
information. Explicit width or height settings are ignored and overwritten.
+ Inside the delegate, you can optionally add one or more of the following
+ properties. TableView modifies the values of these properties to inform the
+ delegate which state it's in. This can be used by the delegate to render
+ itself differently according on its own state.
+
+ \list
+ \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:
+ \code
+ delegate: Rectangle {
+ required property bool current
+ required property bool selected
+ border.width: current ? 1 : 0
+ color: selected ? palette.highlight : palette.base
+ }
+ \endcode
+
\note Delegates are instantiated as needed and may be destroyed at any time.
They are also reused if the \l reuseItems property is set to \c true. You
should therefore avoid storing state information in the delegates.
- \sa {Row heights and column widths}, {Reusing items}
+ \sa {Row heights and column widths}, {Reusing items}, {Required Properties}
*/
/*!
@@ -570,7 +612,7 @@
\readonly
This read-only property holds the column in the view that contains the
- item that is current. If no item is current, it will be \c -1.
+ item that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current column is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -583,7 +625,7 @@
\readonly
This read-only property holds the row in the view that contains the item
- that is current. If no item is current, it will be \c -1.
+ that is \l {Keyboard navigation}{current.} If no item is current, it will be \c -1.
\note In order for TableView to report what the current row is, you
need to assign an \l ItemSelectionModel to \l selectionModel.
@@ -656,16 +698,47 @@
\qmlproperty enumeration QtQuick::TableView::selectionBehavior
\since 6.4
- This property holds whether the user can select single cells, rows or columns.
+ This property holds whether the user can select cells, rows or columns.
- \list
- \li TableView.SelectionDisabled - the user cannot perform selections.
- \li TableView.SelectCells (default) - the user can select individual cells.
- \li TableView.SelectRows - the user can only select rows.
- \li TableView.SelectColumns - the user can only select columns.
- \endlist
+ \value TableView.SelectionDisabled
+ The user cannot perform selections
+ \value TableView.SelectCells
+ (Default value) The user can select individual cells
+ \value TableView.SelectRows
+ The user can only select rows
+ \value TableView.SelectColumns
+ The user can only select columns
- \sa {Selecting items}, selectionModel, keyNavigationEnabled
+ \sa {Selecting items}, selectionMode, selectionModel, keyNavigationEnabled
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::TableView::selectionMode
+ \since 6.6
+
+ If \l selectionBehavior is set to \c {TableView.SelectCells}, this property holds
+ whether the user can select one cell at a time, or multiple cells.
+ If \l selectionBehavior is set to \c {TableView.SelectRows}, this property holds
+ whether the user can select one row at a time, or multiple rows.
+ If \l selectionBehavior is set to \c {TableView.SelectColumns}, this property holds
+ whether the user can select one column at a time, or multiple columns.
+
+ The following modes are available:
+
+ \value TableView.SingleSelection
+ The user can select a single cell, row or column.
+ \value TableView.ContiguousSelection
+ The user can select a single contiguous block of cells.
+ An existing selection can be made bigger or smaller by holding down
+ the \c Shift modifier while selecting.
+ \value TableView.ExtendedSelection
+ (Default value) The user can select multiple individual blocks of
+ cells. An existing selection can be made bigger or smaller by
+ holding down the \c Shift modifier while selecting. A new selection
+ block can be started without clearing the current selection by
+ holding down the \c Control modifier while selecting.
+
+ \sa {Selecting items}, selectionBehavior, selectionModel, keyNavigationEnabled
*/
/*!
@@ -698,25 +771,25 @@
But the application can call \l edit() and \l closeEditor() manually.
\value TableView.SingleTapped - the user can edit a cell by single tapping it.
\value TableView.DoubleTapped - the user can edit a cell by double tapping it.
- \value TableView.SelectedTapped - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by tapping it.
+ \value TableView.SelectedTapped - the user can edit a
+ \l {QItemSelectionModel::selectedIndexes()}{selected cell} by tapping it.
\value TableView.EditKeyPressed - the user can edit the
- \l {QItemSelectionModel::currentIndex()}{current cell} by pressing one
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing one
of the edit keys. The edit keys are decided by the OS, but are normally
- \c Qt.Key_Enter and \c Qt.Key_Return.
+ \c Qt::Key_Enter and \c Qt::Key_Return.
\value TableView.AnyKeyPressed - the user can edit the
- \l {TableView::current}{current cell} by pressing any key, other
+ \l {ItemSelectionModel::currentIndex}{current cell} by pressing any key, other
than the cell navigation keys. The pressed key is also sent to the
focus object inside the \l {TableView::editDelegate}{edit delegate}.
For \c TableView.SelectedTapped, \c TableView.EditKeyPressed, and
\c TableView.AnyKeyPressed to have any effect, TableView needs to have a
\l {selectionModel}{selection model} assigned, since they depend on a
- \l {QItemSelectionModel::currentIndex()}{current index} being set. To be
+ \l {ItemSelectionModel::currentIndex}{current index} being set. To be
able to receive any key events at all, TableView will also need to have
\l QQuickItem::activeFocus.
- When editing a cell, the user can press \c Qt.Key_Tab or \c Qt.Key_Backtab
+ When editing a cell, the user can press \c Qt::Key_Tab or \c Qt::Key_Backtab
to \l {TableView::commit}{commit} the data, and move editing to the next
cell. This behavior can be disabled by setting
\l QQuickItem::activeFocusOnTab on TableView to \c false.
@@ -801,6 +874,20 @@
*/
/*!
+ \qmlmethod QtQuick::TableView::positionViewAtIndex(QModelIndex index, PositionMode mode, point offset, rect subRect)
+ \since 6.5
+
+ Positions the view such that \a index is at the position specified
+ by \a mode, \a offset and \a subRect.
+
+ Convenience method for calling
+ \code
+ positionViewAtRow(rowAtIndex(index), mode & Qt.AlignVertical_Mask, offset.y, subRect)
+ positionViewAtColumn(columnAtIndex(index), mode & Qt.AlignVertical_Mask, offset.x, subRect)
+ \endcode
+*/
+
+/*!
\qmlmethod bool QtQuick::TableView::isColumnLoaded(int column)
\since 6.2
@@ -832,14 +919,9 @@
/*!
\qmlmethod QtQuick::TableView::positionViewAtCell(int column, int row, PositionMode mode, point offset, rect subRect)
+ \deprecated
- Positions \l {Flickable::}{contentX} and \l {Flickable::}{contentY} such
- that \a row and \a column is at the position specified by \a mode, \a offset and \a subRect.
-
- Convenience for calling
- \code
- positionViewAtCell(Qt.point(column, row), mode, offset, subRect)
- \endcode
+ Use \l {positionViewAtIndex()}{positionViewAtIndex(index(row, column), ...)} instead.
*/
/*!
@@ -867,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.
@@ -879,8 +1003,27 @@
/*!
\qmlmethod Item QtQuick::TableView::itemAtCell(int column, int row)
+ \deprecated
+
+ Use \l {itemAtIndex()}{itemAtIndex(index(row, column))} instead.
+*/
+
+/*!
+ \qmlmethod Item QtQuick::TableView::itemAtIndex(QModelIndex index)
+ \since 6.5
+
+ Returns the instantiated delegate item for the cell that represents
+ \a index. If the item is not \l {isRowLoaded()}{loaded}, the value
+ will be \c null.
+
+ \note only the items that are visible in the view are normally loaded.
+ As soon as a cell is flicked out of the view, the item inside will
+ either be unloaded or placed in the recycle pool. As such, the return
+ value should never be stored.
- Convenience for calling \c{itemAtCell(Qt.point(column, row))}.
+ \note If the \l model is not a QAbstractItemModel, you can also use
+ \l {itemAtCell()}{itemAtCell(Qt.point(column, row))}. But be aware
+ that \c {point.x} maps to columns and \c {point.y} maps to rows.
*/
/*!
@@ -929,15 +1072,6 @@
Returns the width of the given \a column. If the column is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- column widths are, by using a \l columnWidthProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a columnWidthProvider set, since
- otherwise you can call that function instead (which will work, even
- for columns that are not currently visible).
- If no columnWidthProvider is set, the width of a column will be
- equal to its \l implicitColumnWidth().
-
\sa columnWidthProvider, implicitColumnWidth(), isColumnLoaded(), {Row heights and column widths}
*/
@@ -948,15 +1082,6 @@
Returns the height of the given \a row. If the row is not
loaded (and therefore not visible), the return value will be \c -1.
- \note It's the applications responsibility to store what the
- row heights are, by using a \l rowHeightProvider. Hence,
- there is no setter function. This getter function is mostly
- useful if the TableView doesn't have a rowHeightProvider set, since
- otherwise you can call that function instead (which will work, even
- for rows that are not currently visible).
- If no rowHeightProvider is set, the height of a row will be
- equal to its \l implicitRowHeight().
-
\sa rowHeightProvider, implicitRowHeight(), isRowLoaded(), {Row heights and column widths}
*/
@@ -1052,7 +1177,7 @@
\qmlmethod qreal QtQuick::TableView::explicitColumnWidth(int column)
Returns the width of the \a column set with \l setColumnWidth(). This width might
- differ from the actual width of the column, if a \l columnWidthProvider()
+ differ from the actual width of the column, if a \l columnWidthProvider
is in use. To get the actual width of a column, use \l columnWidth().
A return value equal to \c 0 means that the column has been told to hide.
@@ -1124,7 +1249,7 @@
\qmlmethod qreal QtQuick::TableView::explicitRowHeight(int row)
Returns the height of the \a row set with \l setRowHeight(). This height might
- differ from the actual height of the column, if a \l rowHeightProvider()
+ differ from the actual height of the column, if a \l rowHeightProvider
is in use. To get the actual height of a row, use \l rowHeight().
A return value equal to \c 0 means that the row has been told to hide.
@@ -1139,18 +1264,14 @@
/*!
\qmlmethod QModelIndex QtQuick::TableView::modelIndex(int row, int column)
\since 6.4
+ \deprecated
- Returns the \l QModelIndex that maps to \a column and \a row in the view.
+ Use \l {QtQuick::TableView::}{index(int row, int column)} instead.
- \a row and \a column should be the row and column in the view (table row and
- table column), and not a row and column in the model.
-
- \note Because of an API incompatible change in Qt 6.4.0 and Qt 6.4.1, the
+ \note Because of an API incompatible change between Qt 6.4.0 and Qt 6.4.2, the
order of \c row and \c column was specified in the opposite order. If you
rely on the order to be \c {modelIndex(column, row)}, you can set the
environment variable \c QT_QUICK_TABLEVIEW_COMPAT_VERSION to \c 6.4
-
- \sa rowAtIndex(), columnAtIndex()
*/
/*!
@@ -1162,19 +1283,35 @@
modelIndex(cell.y, cell.x)
\endcode
- A cell is simply a \l point that combines row and column into
+ A \a cell is simply a \l point that combines row and column into
a single type.
\note \c {point.x} will map to the column, and \c {point.y} will map to the row.
*/
/*!
+ \qmlmethod QModelIndex QtQuick::TableView::index(int row, int column)
+ \since 6.4.3
+
+ Returns the \l QModelIndex that maps to \a row and \a column in the view.
+
+ \a row and \a column should be the row and column in the view (table row and
+ table column), and not a row and column in the model. For a plain
+ TableView, this is equivalent of calling \c {model.index(row, column).}
+ But for a subclass of TableView, like TreeView, where the data model is
+ wrapped inside an internal proxy model that flattens the tree structure
+ into a table, you need to use this function to resolve the model index.
+
+ \sa rowAtIndex(), columnAtIndex()
+*/
+
+/*!
\qmlmethod int QtQuick::TableView::rowAtIndex(QModelIndex modelIndex)
\since 6.4
Returns the row in the view that maps to \a modelIndex in the model.
- \sa columnAtIndex(), modelIndex()
+ \sa columnAtIndex(), index()
*/
/*!
@@ -1183,7 +1320,7 @@
Returns the column in the view that maps to \a modelIndex in the model.
- \sa rowAtIndex(), modelIndex()
+ \sa rowAtIndex(), index()
*/
/*!
@@ -1249,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.
@@ -1291,13 +1446,13 @@
This signal is emitted by the \l {TableView::editDelegate}{edit delegate}
This attached signal is emitted when the \l {TableView::editDelegate}{edit delegate}
- is active, and the user presses \l Qt.Key_Enter or \l Qt.Key_Return. It will also
+ is active, and the user presses \l Qt::Key_Enter or \l Qt::Key_Return. It will also
be emitted if TableView has \l QQuickItem::activeFocusOnTab set, and the user
- presses Qt.Key_Tab or Qt.Key_Backtab.
+ presses Qt::Key_Tab or Qt::Key_Backtab.
This signal will \e not be emitted if editing ends because of reasons other
than the ones mentioned. This includes e.g if the user presses
- Qt.Key_Escape, taps outside the delegate, the row or column being
+ Qt::Key_Escape, taps outside the delegate, the row or column being
edited is deleted, or if the application calls \l closeEditor().
Upon receiving the signal, the edit delegate should write any modified data
@@ -1315,7 +1470,7 @@
This attached property holds the edit delegate. It's instantiated
when editing begins, and parented to the delegate it edits. It
supports the same required properties as the
- \l {\l delegate}{TableView delegate}, including \c index, \c row and \c column.
+ \l {TableView::delegate}{TableView delegate}, including \c index, \c row and \c column.
Properties of the model, like \c display and \c edit, are also available
(depending on the \l {QAbstractItemModel::roleNames()}{role names} exposed
by the model).
@@ -1330,12 +1485,12 @@
and \l closeEditor(), respectively. The \c Qt::ItemIsEditable flag will
then be ignored.
- Editing ends when the user presses \c Qt.Key_Enter or \c Qt.Key_Return
- (and also \c Qt.Key_Tab or \c Qt.Key_Backtab, if TableView has
+ Editing ends when the user presses \c Qt::Key_Enter or \c Qt::Key_Return
+ (and also \c Qt::Key_Tab or \c Qt::Key_Backtab, if TableView has
\l QQuickItem::activeFocusOnTab set). In that case, the \l TableView::commit
signal will be emitted, so that the edit delegate can respond by writing any
modified data back to the model. If editing ends because of other reasons
- (e.g if the user presses Qt.Key_Escape), the signal will not be emitted.
+ (e.g if the user presses Qt::Key_Escape), the signal will not be emitted.
In any case will \l {Component::destruction}{destruction()} be emitted in the end.
While the edit delegate is showing, the cell underneath will still be visible, and
@@ -1344,7 +1499,7 @@
of the edit delegate be a solid \l Rectangle, or hide some of the items
inside the \l {TableView::delegate}{TableView delegate.}. The latter can be done
by defining a property \c {required property bool editing} inside it, that you
- bind to the \l visible property of some of the child items.
+ bind to the \l {QQuickItem::}{visible} property of some of the child items.
The following snippet shows how to do that:
\snippet qml/tableview/editdelegate.qml 1
@@ -1369,6 +1524,52 @@ 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)
+{
+#define TV_REBUILDSTATE(STATE) \
+ case QQuickTableViewPrivate::RebuildState::STATE: \
+ dbg << QStringLiteral(#STATE); break;
+
+ switch (state) {
+ TV_REBUILDSTATE(Begin);
+ TV_REBUILDSTATE(LoadInitalTable);
+ TV_REBUILDSTATE(VerifyTable);
+ TV_REBUILDSTATE(LayoutTable);
+ TV_REBUILDSTATE(CancelOvershoot);
+ TV_REBUILDSTATE(UpdateContentSize);
+ TV_REBUILDSTATE(PreloadColumns);
+ TV_REBUILDSTATE(PreloadRows);
+ TV_REBUILDSTATE(MovePreloadedItemsToPool);
+ TV_REBUILDSTATE(Done);
+ }
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, QQuickTableViewPrivate::RebuildOptions options)
+{
+#define TV_REBUILDOPTION(OPTION) \
+ if (options & QQuickTableViewPrivate::RebuildOption::OPTION) \
+ dbg << QStringLiteral(#OPTION)
+
+ if (options == QQuickTableViewPrivate::RebuildOption::None) {
+ dbg << QStringLiteral("None");
+ } else {
+ TV_REBUILDOPTION(All);
+ TV_REBUILDOPTION(LayoutOnly);
+ TV_REBUILDOPTION(ViewportOnly);
+ TV_REBUILDOPTION(CalculateNewTopLeftRow);
+ TV_REBUILDOPTION(CalculateNewTopLeftColumn);
+ TV_REBUILDOPTION(CalculateNewContentWidth);
+ TV_REBUILDOPTION(CalculateNewContentHeight);
+ TV_REBUILDOPTION(PositionViewAtRow);
+ TV_REBUILDOPTION(PositionViewAtColumn);
+ }
+
+ return dbg;
+}
QQuickTableViewPrivate::EdgeRange::EdgeRange()
: startIndex(kEdgeIndexNotSet)
@@ -1528,10 +1729,15 @@ QQuickItem *QQuickTableViewPrivate::selectionPointerHandlerTarget() const
return const_cast<QQuickTableView *>(q_func())->contentItem();
}
-bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
+bool QQuickTableViewPrivate::startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers)
{
Q_Q(QQuickTableView);
- Q_UNUSED(pos);
+ if (!selectionModel) {
+ if (warnNoSelectionModel)
+ qmlWarning(q_func()) << "Cannot start selection: no SelectionModel assigned!";
+ warnNoSelectionModel = false;
+ return false;
+ }
if (selectionBehavior == QQuickTableView::SelectionDisabled) {
qmlWarning(q) << "Cannot start selection: TableView.selectionBehavior == TableView.SelectionDisabled";
@@ -1542,6 +1748,28 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return false;
+ // For SingleSelection and ContiguousSelection, we should only allow one
+ // selection at a time. We also clear the current selection if the mode
+ // is ExtendedSelection, but no modifier is being held.
+ if (selectionMode == QQuickTableView::SingleSelection
+ || selectionMode == QQuickTableView::ContiguousSelection
+ || modifiers == Qt::NoModifier)
+ clearSelection();
+ else if (selectionModel)
+ existingSelection = selectionModel->selection();
+
+ // If pos is on top of an unselected cell, we start a session where the user selects which
+ // cells to become selected. Otherwise, if pos is on top of an already selected cell and
+ // ctrl is being held, we start a session where the user selects which selected cells to
+ // become unselected.
+ selectionFlag = QItemSelectionModel::Select;
+ if (modifiers & Qt::ControlModifier) {
+ QPoint startCell = clampedCellAtPos(pos);
+ const QModelIndex startIndex = q->index(startCell.y(), startCell.x());
+ if (selectionModel->isSelected(startIndex))
+ selectionFlag = QItemSelectionModel::Deselect;
+ }
+
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
q->closeEditor();
@@ -1550,6 +1778,8 @@ bool QQuickTableViewPrivate::startSelection(const QPointF &pos)
void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
{
+ Q_Q(QQuickTableView);
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1562,13 +1792,26 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
if (!qaim)
return;
+ if (selectionMode == QQuickTableView::SingleSelection
+ && cellIsValid(selectionStartCell)) {
+ return;
+ }
+
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
+
+ QPoint clampedCell;
+ if (pos.x() == -1) {
+ // Special case: use current cell as start cell
+ clampedCell = q->cellAtIndex(selectionModel->currentIndex());
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (cellIsValid(clampedCell))
+ setCurrentIndex(clampedCell);
+ }
+
if (!cellIsValid(clampedCell))
return;
- setCurrentIndex(clampedCell);
-
switch (selectionBehavior) {
case QQuickTableView::SelectCells:
selectionStartCell = clampedCell;
@@ -1587,11 +1830,13 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
{
+ Q_ASSERT(selectionFlag != QItemSelectionModel::NoUpdate);
if (loadedItems.isEmpty())
return;
if (!selectionModel) {
@@ -1605,9 +1850,15 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
const QRect prevSelection = selection();
- const QPoint clampedCell = clampedCellAtPos(pos);
- if (!cellIsValid(clampedCell))
- return;
+
+ QPoint clampedCell;
+ if (selectionMode == QQuickTableView::SingleSelection) {
+ clampedCell = selectionStartCell;
+ } else {
+ clampedCell = clampedCellAtPos(pos);
+ if (!cellIsValid(clampedCell))
+ return;
+ }
setCurrentIndex(clampedCell);
@@ -1629,6 +1880,7 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
@@ -1659,42 +1911,84 @@ void QQuickTableViewPrivate::updateSelection(const QRect &oldSelection, const QR
const QRect oldRect = oldSelection.normalized();
const QRect newRect = newSelection.normalized();
+ QItemSelection select;
+ QItemSelection deselect;
+
// Select cells inside the new selection rect
{
const QModelIndex startIndex = qaim->index(newRect.y(), newRect.x());
const QModelIndex endIndex = qaim->index(newRect.y() + newRect.height(), newRect.x() + newRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ 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);
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ 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());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ 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());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ 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());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
+ 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) {
+ // Don't clear the selection that existed before the user started a new selection block
+ deselect.merge(existingSelection, QItemSelectionModel::Deselect);
+ selectionModel->select(deselect, QItemSelectionModel::Deselect);
+ selectionModel->select(select, QItemSelectionModel::Select);
+ } else if (selectionFlag == QItemSelectionModel::Deselect){
+ QItemSelection oldSelection = existingSelection;
+ oldSelection.merge(select, QItemSelectionModel::Deselect);
+ selectionModel->select(oldSelection, QItemSelectionModel::Select);
+ selectionModel->select(select, QItemSelectionModel::Deselect);
+ } else {
+ Q_UNREACHABLE();
}
}
-void QQuickTableViewPrivate::clearSelection()
+void QQuickTableViewPrivate::cancelSelectionTracking()
{
+ // Cancel any ongoing key/mouse aided selection tracking
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
+ existingSelection.clear();
+ selectionFlag = QItemSelectionModel::NoUpdate;
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::CancelSelection);
+}
- if (selectionModel)
- selectionModel->clearSelection();
+void QQuickTableViewPrivate::clearSelection()
+{
+ if (!selectionModel)
+ return;
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
+ selectionModel->clearSelection();
}
void QQuickTableViewPrivate::normalizeSelection()
@@ -1728,7 +2022,7 @@ QRectF QQuickTableViewPrivate::selectionRectangle() const
// If the corner cells of the selection are loaded, we can position the
// selection rectangle at its exact location. Otherwise we extend it out
// to the edges of the content item. This is not ideal, but the best we
- // can do while the location of the the corner cells are unknown.
+ // can do while the location of the corner cells are unknown.
// This will at least move the selection handles (and other overlay) out
// of the viewport until the affected cells are eventually loaded.
int left = 0;
@@ -1766,7 +2060,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);
@@ -1832,6 +2126,11 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
return dist;
}
+void QQuickTableViewPrivate::setCallback(std::function<void (CallBackFlag)> func)
+{
+ selectableCallbackFunction = func;
+}
+
QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
{
QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
@@ -1870,14 +2169,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
@@ -2019,7 +2318,10 @@ void QQuickTableViewPrivate::updateContentWidth()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentWidth(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentWidth(kDefaultColumnWidth);
+ else
+ q->QQuickFlickable::setContentWidth(0);
return;
}
@@ -2052,7 +2354,10 @@ void QQuickTableViewPrivate::updateContentHeight()
if (loadedItems.isEmpty()) {
QBoolBlocker fixupGuard(inUpdateContentSize, true);
- q->QQuickFlickable::setContentHeight(0);
+ if (model && model->count() > 0 && tableModel && tableModel->delegate())
+ q->QQuickFlickable::setContentHeight(kDefaultRowHeight);
+ else
+ q->QQuickFlickable::setContentHeight(0);
return;
}
@@ -2365,21 +2670,22 @@ void QQuickTableViewPrivate::forceLayout(bool immediate)
const QSize actualTableSize = calculateTableSize();
if (tableSize != actualTableSize) {
- // This can happen if the app is calling forceLayout while
- // the model is updated, but before we're notified about it.
- rebuildOptions = RebuildOption::All;
- } else {
- // Resizing a column (or row) can result in the table going from being
- // e.g completely inside the viewport to go outside. And in the latter
- // case, the user needs to be able to scroll the viewport, also if
- // flags such as Flickable.StopAtBounds is in use. So we need to
- // update contentWidth/Height to support that case.
- rebuildOptions = RebuildOption::LayoutOnly
- | RebuildOption::CalculateNewContentWidth
- | RebuildOption::CalculateNewContentHeight
- | checkForVisibilityChanges();
+ // The table size will have changed if forceLayout is called after
+ // the row count in the model has changed, but before we received
+ // a rowsInsertedCallback about it (and vice versa for columns).
+ rebuildOptions |= RebuildOption::ViewportOnly;
}
+ // Resizing a column (or row) can result in the table going from being
+ // e.g completely inside the viewport to go outside. And in the latter
+ // case, the user needs to be able to scroll the viewport, also if
+ // flags such as Flickable.StopAtBounds is in use. So we need to
+ // update contentWidth/Height to support that case.
+ rebuildOptions |= RebuildOption::LayoutOnly
+ | RebuildOption::CalculateNewContentWidth
+ | RebuildOption::CalculateNewContentHeight
+ | checkForVisibilityChanges();
+
scheduleRebuildTable(rebuildOptions);
if (immediate) {
@@ -2425,7 +2731,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) {
@@ -2661,21 +2969,6 @@ qreal QQuickTableViewPrivate::sizeHintForRow(int row) const
return rowHeight;
}
-void QQuickTableViewPrivate::updateTableSize()
-{
- // tableSize is the same as row and column count, and will always
- // be the same as the number of rows and columns in the model.
- Q_Q(QQuickTableView);
-
- const QSize prevTableSize = tableSize;
- tableSize = calculateTableSize();
-
- if (prevTableSize.width() != tableSize.width())
- emit q->columnsChanged();
- if (prevTableSize.height() != tableSize.height())
- emit q->rowsChanged();
-}
-
QSize QQuickTableViewPrivate::calculateTableSize()
{
QSize size(0, 0);
@@ -2793,7 +3086,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)
@@ -2812,7 +3105,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;
@@ -2824,7 +3117,7 @@ qreal QQuickTableViewPrivate::getColumnWidth(int column) const
columnWidth = noExplicitColumnWidth;
}
- cachedColumnWidth.startIndex = column;
+ cachedColumnWidth.startIndex = logicalColumnIndex(column);
cachedColumnWidth.size = columnWidth;
return columnWidth;
}
@@ -2839,7 +3132,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)
@@ -2858,7 +3151,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;
@@ -2870,7 +3163,7 @@ qreal QQuickTableViewPrivate::getRowHeight(int row) const
rowHeight = noExplicitRowHeight;
}
- cachedRowHeight.startIndex = row;
+ cachedRowHeight.startIndex = logicalRowIndex(row);
cachedRowHeight.size = rowHeight;
return rowHeight;
}
@@ -3206,18 +3499,8 @@ void QQuickTableViewPrivate::processRebuildTable()
Q_Q(QQuickTableView);
if (rebuildState == RebuildState::Begin) {
- if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
- qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q;
- if (rebuildOptions & RebuildOption::All)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::ViewportOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly, options:" << rebuildOptions;
- else if (rebuildOptions & RebuildOption::LayoutOnly)
- qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::LayoutOnly, options:" << rebuildOptions;
- else
- Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
- }
-
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin rebuild:" << q << "options:" << rebuildOptions;
+ tableSizeBeforeRebuild = tableSize;
edgesBeforeRebuild = loadedItems.isEmpty() ? QMargins()
: QMargins(q->leftColumn(), q->topRow(), q->rightColumn(), q->bottomRow());
}
@@ -3286,6 +3569,10 @@ void QQuickTableViewPrivate::processRebuildTable()
}
if (rebuildState == RebuildState::Done) {
+ if (tableSizeBeforeRebuild.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (tableSizeBeforeRebuild.height() != tableSize.height())
+ emit q->rowsChanged();
if (edgesBeforeRebuild.left() != q->leftColumn())
emit q->leftColumnChanged();
if (edgesBeforeRebuild.right() != q->rightColumn())
@@ -3324,7 +3611,7 @@ bool QQuickTableViewPrivate::moveToNextRebuildState()
else
rebuildState = RebuildState(int(rebuildState) + 1);
- qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ qCDebug(lcTableViewDelegateLifecycle()) << rebuildState;
return true;
}
@@ -3435,7 +3722,7 @@ void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topL
void QQuickTableViewPrivate::loadInitialTable()
{
- updateTableSize();
+ tableSize = calculateTableSize();
if (positionXAnimation.isRunning()) {
positionXAnimation.stop();
@@ -3941,10 +4228,11 @@ bool QQuickTableViewPrivate::currentInSelectionModel(const QPoint &cell) const
void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
- if (!selectionModel->hasSelection()) {
- // Ensure that we cancel any ongoing key/mouse-based selections
- // if selectionModel.clearSelection() is called.
- clearSelection();
+ if (!inSelectionModelUpdate) {
+ // The selection model was manipulated outside of TableView
+ // and SelectionRectangle. In that case we cancel any ongoing
+ // selection tracking.
+ cancelSelectionTracking();
}
const auto &selectedIndexes = selected.indexes();
@@ -3957,6 +4245,13 @@ void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelecti
void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select)
{
+ if (modelIndex.isValid() && modelIndex.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot select cells: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
+
const int cellIndex = modelIndexToCellIndex(modelIndex);
if (!loadedItems.contains(cellIndex))
return;
@@ -3965,6 +4260,24 @@ void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelI
setRequiredProperty(kRequiredProperty_selected, QVariant::fromValue(select), cellIndex, item, false);
}
+QAbstractItemModel *QQuickTableViewPrivate::selectionSourceModel()
+{
+ // TableView.selectionModel.model should always be the same as TableView.model.
+ // After all, when the user selects an index in the view, the same index should
+ // be selected in the selection model. We therefore set the model in
+ // selectionModel.model automatically.
+ // But it's not always the case that the model shown in the view is the same
+ // as TableView.model. Subclasses with a proxy model will instead show the
+ // proxy model (e.g TreeView and HeaderView). And then it's no longer clear if
+ // we should use the proxy model or the TableView.model as source model in
+ // TableView.selectionModel. It's up to the subclass. But in short, if the proxy
+ // model shares the same model items as TableView.model (just with e.g a filter
+ // applied, or sorted etc), then TableView.model should be used. If the proxy
+ // model is a completely different model that shares no model items with
+ // TableView.model, then the proxy model should be used (e.g HeaderView).
+ return qaim(modelImpl());
+}
+
QAbstractItemModel *QQuickTableViewPrivate::qaim(QVariant modelAsVariant) const
{
// If modelAsVariant wraps a qaim, return it
@@ -3992,11 +4305,12 @@ void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems()
void QQuickTableViewPrivate::currentChangedInSelectionModel(const QModelIndex &current, const QModelIndex &previous)
{
- // Warn if the source models are not the same
- const QAbstractItemModel *qaimInSelection = selectionModel ? selectionModel->model() : nullptr;
- const QAbstractItemModel *qaimInTableView = qaim(modelImpl());
- if (qaimInSelection && qaimInSelection != qaimInTableView)
- qmlWarning(q_func()) << "TableView.selectionModel.model differs from TableView.model";
+ if (current.isValid() && current.model() != selectionSourceModel()) {
+ qmlWarning(q_func())
+ << "Cannot change current index: TableView.selectionModel.model is not "
+ << "compatible with the model displayed in the view";
+ return;
+ }
updateCurrentRowAndColumn();
setCurrentOnDelegateItem(previous, false);
@@ -4060,11 +4374,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);
@@ -4081,11 +4397,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);
@@ -4159,9 +4477,6 @@ QVariant QQuickTableViewPrivate::modelImpl() const
void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
{
- if (newModel == assignedModel)
- return;
-
assignedModel = newModel;
scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
emit q_func()->modelChanged();
@@ -4169,7 +4484,7 @@ void QQuickTableViewPrivate::setModelImpl(const QVariant &newModel)
void QQuickTableViewPrivate::syncModel()
{
- if (modelVariant == assignedModel)
+ if (compareModel(modelVariant, assignedModel))
return;
if (model) {
@@ -4445,6 +4760,13 @@ void QQuickTableViewPrivate::modelResetCallback()
scheduleRebuildTable(RebuildOption::All);
}
+bool QQuickTableViewPrivate::compareModel(const QVariant& model1, const QVariant& model2) const
+{
+ return (model1 == model2 ||
+ (model1.userType() == qMetaTypeId<QJSValue>() && model2.userType() == qMetaTypeId<QJSValue>() &&
+ model1.value<QJSValue>().strictlyEquals(model2.value<QJSValue>())));
+}
+
void QQuickTableViewPrivate::positionViewAtRow(int row, Qt::Alignment alignment, qreal offset, const QRectF subRect)
{
Qt::Alignment verticalAlignment = alignment & (Qt::AlignTop | Qt::AlignVCenter | Qt::AlignBottom);
@@ -4660,10 +4982,11 @@ void QQuickTableViewPrivate::init()
positionYAnimation.setProperty(QStringLiteral("contentY"));
positionYAnimation.setEasing(QEasingCurve::OutQuart);
- auto tapHandler = new QQuickTapHandler(q->contentItem());
+ auto tapHandler = new QQuickTableViewTapHandler(q);
hoverHandler = new QQuickTableViewHoverHandler(q);
resizeHandler = new QQuickTableViewResizeHandler(q);
+
hoverHandler->setEnabled(resizableRows || resizableColumns);
resizeHandler->setEnabled(resizableRows || resizableColumns);
@@ -4724,24 +5047,33 @@ void QQuickTableViewPrivate::handleTap(const QQuickHandlerPoint &point)
if (resizeHandler->state() != QQuickTableViewResizeHandler::Listening)
return;
- QModelIndex prevIndex;
- if (selectionModel) {
- prevIndex = selectionModel->currentIndex();
- if (pointerNavigationEnabled) {
- clearSelection();
- setCurrentIndexFromTap(point.position());
- }
- }
+ const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
+ bool tappedCellIsSelected = false;
- if (editTriggers != QQuickTableView::NoEditTriggers)
- q->closeEditor();
+ if (selectionModel)
+ tappedCellIsSelected = selectionModel->isSelected(tappedIndex);
- const QModelIndex tappedIndex = q->modelIndex(q->cellAtPosition(point.position()));
if (canEdit(tappedIndex, false)) {
- if (editTriggers & QQuickTableView::SingleTapped)
+ if (editTriggers & QQuickTableView::SingleTapped) {
+ if (selectionBehavior != QQuickTableView::SelectionDisabled)
+ clearSelection();
q->edit(tappedIndex);
- else if ((editTriggers & QQuickTableView::SelectedTapped) && tappedIndex == prevIndex)
+ return;
+ } else if (editTriggers & QQuickTableView::SelectedTapped && tappedCellIsSelected) {
q->edit(tappedIndex);
+ return;
+ }
+ }
+
+ // Since the tap didn't result in selecting or editing cells, we clear
+ // the current selection and move the current index instead.
+ if (pointerNavigationEnabled) {
+ q->closeEditor();
+ if (selectionBehavior != QQuickTableView::SelectionDisabled) {
+ clearSelection();
+ cancelSelectionTracking();
+ }
+ setCurrentIndexFromTap(point.position());
}
}
@@ -4840,7 +5172,6 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
const QModelIndex currentIndex = selectionModel->currentIndex();
const QPoint currentCell = q->cellAtIndex(currentIndex);
- const bool select = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
if (!q->activeFocusOnTab()) {
switch (e->key()) {
@@ -4864,7 +5195,7 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
case Qt::Key_Backtab:
// Special case: the current index doesn't map to a cell in the view (perhaps
// because it isn't set yet). In that case, we set it to be the top-left cell.
- const QModelIndex topLeftIndex = q->modelIndex(topRow(), leftColumn());
+ const QModelIndex topLeftIndex = q->index(topRow(), leftColumn());
selectionModel->setCurrentIndex(topLeftIndex, QItemSelectionModel::NoUpdate);
return true;
}
@@ -4872,25 +5203,38 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
}
auto beginMoveCurrentIndex = [&](){
- if (!select) {
+ const bool shouldSelect = (e->modifiers() & Qt::ShiftModifier) && (e->key() != Qt::Key_Backtab);
+ const bool startNewSelection = selectionRectangle().isEmpty();
+ if (!shouldSelect) {
clearSelection();
- } else if (selectionRectangle().isEmpty()) {
+ cancelSelectionTracking();
+ } else if (startNewSelection) {
+ // Try to start a new selection if no selection exists from before.
+ // The startSelection() call is theoretically allowed to refuse, although this
+ // is less likely when starting a selection using the keyboard.
const int serializedStartIndex = modelIndexToCellIndex(selectionModel->currentIndex());
if (loadedItems.contains(serializedStartIndex)) {
const QRectF startGeometry = loadedItems.value(serializedStartIndex)->geometry();
- setSelectionStartPos(startGeometry.center());
+ if (startSelection(startGeometry.center(), Qt::ShiftModifier)) {
+ setSelectionStartPos(startGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
+ }
}
}
};
auto endMoveCurrentIndex = [&](const QPoint &cell){
- if (select) {
+ const bool isSelecting = selectionFlag != QItemSelectionModel::NoUpdate;
+ if (isSelecting) {
if (polishScheduled)
forceLayout(true);
const int serializedEndIndex = modelIndexAtCell(cell);
if (loadedItems.contains(serializedEndIndex)) {
const QRectF endGeometry = loadedItems.value(serializedEndIndex)->geometry();
setSelectionEndPos(endGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
}
}
selectionModel->setCurrentIndex(q->modelIndex(cell), QItemSelectionModel::NoUpdate);
@@ -5170,6 +5514,14 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent)
QQuickTableView::~QQuickTableView()
{
+ Q_D(QQuickTableView);
+
+ if (d->syncView) {
+ // Remove this TableView as a sync child from the syncView
+ auto syncView_d = d->syncView->d_func();
+ syncView_d->syncChildren.removeOne(this);
+ syncView_d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ }
}
void QQuickTableView::componentFinalized()
@@ -5301,12 +5653,13 @@ QVariant QQuickTableView::model() const
void QQuickTableView::setModel(const QVariant &newModel)
{
Q_D(QQuickTableView);
+ if (d->compareModel(newModel, d->assignedModel))
+ return;
closeEditor();
d->setModelImpl(newModel);
-
if (d->selectionModel)
- d->selectionModel->setModel(d->qaim(newModel));
+ d->selectionModel->setModel(d->selectionSourceModel());
}
QQmlComponent *QQuickTableView::delegate() const
@@ -5406,6 +5759,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);
@@ -5473,7 +5830,7 @@ void QQuickTableView::setSelectionModel(QItemSelectionModel *selectionModel)
d->selectionModel = selectionModel;
if (d->selectionModel) {
- d->selectionModel->setModel(d->qaim(d->modelImpl()));
+ d->selectionModel->setModel(d->selectionSourceModel());
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::selectionChanged,
d, &QQuickTableViewPrivate::selectionChangedInSelectionModel);
QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::currentChanged,
@@ -5574,7 +5931,7 @@ int QQuickTableView::currentColumn() const
void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (row < 0 || row >= rows())
+ if (row < 0 || row >= rows() || d->loadedRows.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignTop | Qt::AlignBottom).
@@ -5640,7 +5997,7 @@ void QQuickTableView::positionViewAtRow(int row, PositionMode mode, qreal offset
void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal offset, const QRectF &subRect)
{
Q_D(QQuickTableView);
- if (column < 0 || column >= columns())
+ if (column < 0 || column >= columns() || d->loadedColumns.isEmpty())
return;
// Note: PositionMode::Contain is from here on translated to (Qt::AlignLeft | Qt::AlignRight).
@@ -5705,9 +6062,35 @@ void QQuickTableView::positionViewAtColumn(int column, PositionMode mode, qreal
void QQuickTableView::positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
- positionViewAtCell(cell.x(), cell.y(), mode, offset, subRect);
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(cell.x(), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(cell.y(), verticalMode, offset.y(), subRect);
+}
+
+void QQuickTableView::positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset, const QRectF &subRect)
+{
+ PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
+ PositionMode verticalMode = mode & ~(AlignLeft | AlignRight | AlignHCenter);
+ if (!horizontalMode && !verticalMode) {
+ qmlWarning(this) << "Unsupported mode:" << int(mode);
+ return;
+ }
+
+ if (horizontalMode)
+ positionViewAtColumn(columnAtIndex(index), horizontalMode, offset.x(), subRect);
+ if (verticalMode)
+ positionViewAtRow(rowAtIndex(index), verticalMode, offset.y(), subRect);
}
+#if QT_DEPRECATED_SINCE(6, 5)
void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset, const QRectF &subRect)
{
PositionMode horizontalMode = mode & ~(AlignTop | AlignBottom | AlignVCenter);
@@ -5722,6 +6105,170 @@ void QQuickTableView::positionViewAtCell(int column, int row, PositionMode mode,
if (verticalMode)
positionViewAtRow(row, verticalMode, offset.y(), subRect);
}
+#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
{
@@ -5732,10 +6279,21 @@ QQuickItem *QQuickTableView::itemAtCell(const QPoint &cell) const
return d->loadedItems.value(modelIndex)->item;
}
+#if QT_DEPRECATED_SINCE(6, 5)
QQuickItem *QQuickTableView::itemAtCell(int column, int row) const
{
return itemAtCell(QPoint(column, row));
}
+#endif
+
+QQuickItem *QQuickTableView::itemAtIndex(const QModelIndex &index) const
+{
+ Q_D(const QQuickTableView);
+ const int serializedIndex = d->modelIndexToCellIndex(index);
+ if (!d->loadedItems.contains(serializedIndex))
+ return nullptr;
+ return d->loadedItems.value(serializedIndex)->item;
+}
#if QT_DEPRECATED_SINCE(6, 4)
QPoint QQuickTableView::cellAtPos(qreal x, qreal y, bool includeSpacing) const
@@ -5890,9 +6448,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;
@@ -5925,7 +6483,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;
@@ -5948,9 +6506,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;
@@ -5983,7 +6541,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;
@@ -5999,16 +6557,18 @@ 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)
QModelIndex QQuickTableView::modelIndex(int row, int column) const
{
static bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
@@ -6019,9 +6579,18 @@ QModelIndex QQuickTableView::modelIndex(int row, int column) const
// to continue accepting calls to modelIndex(column, row).
return modelIndex({row, column});
} else {
+ qmlWarning(this) << "modelIndex(row, column) is deprecated. "
+ "Use index(row, column) instead. For more information, see "
+ "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({column, row});
}
}
+#endif
+
+QModelIndex QQuickTableView::index(int row, int column) const
+{
+ return modelIndex({column, row});
+}
int QQuickTableView::rowAtIndex(const QModelIndex &index) const
{
@@ -6061,7 +6630,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;
@@ -6090,7 +6660,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();
@@ -6141,7 +6711,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);
@@ -6307,6 +6877,21 @@ void QQuickTableView::setSelectionBehavior(SelectionBehavior selectionBehavior)
emit selectionBehaviorChanged();
}
+QQuickTableView::SelectionMode QQuickTableView::selectionMode() const
+{
+ return d_func()->selectionMode;
+}
+
+void QQuickTableView::setSelectionMode(SelectionMode selectionMode)
+{
+ Q_D(QQuickTableView);
+ if (d->selectionMode == selectionMode)
+ return;
+
+ d->selectionMode = selectionMode;
+ emit selectionModeChanged();
+}
+
bool QQuickTableView::resizableColumns() const
{
return d_func()->resizableColumns;
@@ -6344,7 +6929,6 @@ void QQuickTableView::setResizableRows(bool enabled)
}
// ----------------------------------------------
-
QQuickTableViewHoverHandler::QQuickTableViewHoverHandler(QQuickTableView *view)
: QQuickHoverHandler(view->contentItem())
{
@@ -6395,15 +6979,38 @@ 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");
+}
+
void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
, QPointingDevice::GrabTransition transition
, QPointerEvent *ev
@@ -6428,18 +7035,14 @@ void QQuickTableViewResizeHandler::onGrabChanged(QQuickPointerHandler *grabber
}
}
-bool QQuickTableViewResizeHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
-{
- Q_UNUSED(event);
- Q_UNUSED(point);
- // 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
@@ -6504,6 +7107,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);
@@ -6514,7 +7118,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
#if QT_CONFIG(cursor)
tableViewPrivate->updateCursor();
#endif
- // fallthrough
+ Q_FALLTHROUGH();
case Dragging: {
const qreal distX = point.position().x() - m_columnStartX;
const qreal distY = point.position().y() - m_rowStartY;
@@ -6525,6 +7129,7 @@ void QQuickTableViewResizeHandler::updateDrag(QPointerEvent *event, QEventPoint
break; }
case DraggingFinished: {
tableView->setFiltersChildMouseEvents(true);
+ tableViewPrivate->setActivePointerHandler(nullptr);
#if QT_CONFIG(cursor)
tableViewPrivate->updateCursor();
#endif
@@ -6532,6 +7137,331 @@ 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) {
+ 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)
+{
+ QQuickSinglePointHandler::handleEventPoint(event, 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) {
+ if (m_scrollTimer.isActive())
+ m_scrollTimer.stop();
+ 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
+ if (m_scrollTimer.isActive())
+ m_scrollTimer.stop();
+ 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())
+{
+ setObjectName("tableViewTapHandler");
+}
+
+bool QQuickTableViewTapHandler::wantsEventPoint(const QPointerEvent *event, const QEventPoint &point)
+{
+ auto tableView = static_cast<QQuickTableView *>(parentItem()->parent());
+ auto tableViewPrivate = QQuickTableViewPrivate::get(tableView);
+ return tableViewPrivate->pointerNavigationEnabled && QQuickTapHandler::wantsEventPoint(event, point);
+}
+
QT_END_NAMESPACE
#include "moc_qquicktableview_p.cpp"
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
index 162d576320..10cc53274d 100644
--- a/src/quick/items/qquicktableview_p.h
+++ b/src/quick/items/qquicktableview_p.h
@@ -31,7 +31,7 @@ class QQuickTableViewAttached;
class QQuickTableViewPrivate;
class QItemSelectionModel;
-class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable, public QQmlFinalizerHook
+class Q_QUICK_EXPORT QQuickTableView : public QQuickFlickable, public QQmlFinalizerHook
{
Q_OBJECT
Q_INTERFACES(QQmlFinalizerHook)
@@ -64,6 +64,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable, public QQ
Q_PROPERTY(bool resizableColumns READ resizableColumns WRITE setResizableColumns NOTIFY resizableColumnsChanged REVISION(6, 5) FINAL)
Q_PROPERTY(bool resizableRows READ resizableRows WRITE setResizableRows NOTIFY resizableRowsChanged REVISION(6, 5) FINAL)
Q_PROPERTY(EditTriggers editTriggers READ editTriggers WRITE setEditTriggers NOTIFY editTriggersChanged REVISION(6, 5) FINAL)
+ Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(TableView)
QML_ADDED_IN_VERSION(2, 12)
@@ -92,6 +93,13 @@ public:
};
Q_ENUM(SelectionBehavior)
+ enum SelectionMode {
+ SingleSelection,
+ ContiguousSelection,
+ ExtendedSelection
+ };
+ Q_ENUM(SelectionMode)
+
enum EditTrigger {
NoEditTriggers = 0x0,
SingleTapped = 0x1,
@@ -162,6 +170,8 @@ public:
SelectionBehavior selectionBehavior() const;
void setSelectionBehavior(SelectionBehavior selectionBehavior);
+ SelectionMode selectionMode() const;
+ void setSelectionMode(SelectionMode selectionMode);
bool resizableColumns() const;
void setResizableColumns(bool enabled);
@@ -174,15 +184,16 @@ public:
Q_INVOKABLE void forceLayout();
Q_INVOKABLE void positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
- Q_INVOKABLE void positionViewAtCell(int column, int row, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
+ Q_INVOKABLE void positionViewAtIndex(const QModelIndex &index, PositionMode mode, const QPointF &offset = QPointF(), const QRectF &subRect = QRectF());
Q_INVOKABLE void positionViewAtRow(int row, PositionMode mode, qreal offset = 0, const QRectF &subRect = QRectF());
Q_INVOKABLE void positionViewAtColumn(int column, PositionMode mode, qreal offset = 0, const QRectF &subRect = QRectF());
Q_INVOKABLE QQuickItem *itemAtCell(const QPoint &cell) const;
- Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const;
Q_REVISION(6, 4) Q_INVOKABLE QPoint cellAtPosition(const QPointF &position, bool includeSpacing = false) const;
Q_REVISION(6, 4) Q_INVOKABLE QPoint cellAtPosition(qreal x, qreal y, bool includeSpacing = false) const;
#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
+ Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
QT_DEPRECATED_VERSION_X_6_4("Use cellAtPosition() instead")
Q_INVOKABLE QPoint cellAtPos(const QPointF &position, bool includeSpacing = false) const;
Q_INVOKABLE QPoint cellAtPos(qreal x, qreal y, bool includeSpacing = false) const;
@@ -196,8 +207,8 @@ public:
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitColumnWidth(int column) const;
Q_REVISION(6, 2) Q_INVOKABLE qreal implicitRowHeight(int row) const;
+ Q_REVISION(6, 4) Q_INVOKABLE QModelIndex index(int row, int column) const;
Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(const QPoint &cell) const;
- Q_REVISION(6, 4) Q_INVOKABLE virtual QModelIndex modelIndex(int row, int column) const;
Q_REVISION(6, 4) Q_INVOKABLE virtual QPoint cellAtIndex(const QModelIndex &index) const;
Q_REVISION(6, 4) Q_INVOKABLE int rowAtIndex(const QModelIndex &index) const;
Q_REVISION(6, 4) Q_INVOKABLE int columnAtIndex(const QModelIndex &index) const;
@@ -213,6 +224,20 @@ public:
Q_REVISION(6, 5) Q_INVOKABLE void edit(const QModelIndex &index);
Q_REVISION(6, 5) Q_INVOKABLE void closeEditor();
+ Q_REVISION(6, 5) Q_INVOKABLE QQuickItem *itemAtIndex(const QModelIndex &index) const;
+
+#if QT_DEPRECATED_SINCE(6, 5)
+ QT_DEPRECATED_VERSION_X_6_5("Use itemAtIndex(index(row, column)) instead")
+ Q_INVOKABLE QQuickItem *itemAtCell(int column, int row) const;
+ QT_DEPRECATED_VERSION_X_6_5("Use positionViewAtIndex(index(row, column)) instead")
+ 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:
@@ -243,6 +268,10 @@ Q_SIGNALS:
Q_REVISION(6, 5) void resizableRowsChanged();
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;
@@ -265,11 +294,11 @@ private:
qreal maxYExtent() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject
+class Q_QUICK_EXPORT QQuickTableViewAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged)
- Q_PROPERTY(QQmlComponent *editDelegate READ editDelegate WRITE setEditDelegate NOTIFY editDelegateChanged)
+ Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged FINAL)
+ Q_PROPERTY(QQmlComponent *editDelegate READ editDelegate WRITE setEditDelegate NOTIFY editDelegateChanged FINAL)
public:
QQuickTableViewAttached(QObject *parent)
@@ -311,6 +340,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickTableView::EditTriggers)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTableView)
-
#endif // QQUICKTABLEVIEW_P_H
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index d8c2b036ac..ea49a3309b 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -23,14 +23,18 @@
#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>
#include <QtQuick/private/qquickselectable_p.h>
#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
@@ -66,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
@@ -84,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;
@@ -99,16 +114,69 @@ 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 Q_QUICK_PRIVATE_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate, public QQuickSelectable
+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
+ */
+class QQuickTableViewTapHandler : public QQuickTapHandler
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickTableViewTapHandler(QQuickTableView *view);
+ bool wantsEventPoint(const QPointerEvent *event, const QEventPoint &point) override;
+
+ friend class QQuickTableViewPrivate;
+};
+
+class Q_QUICK_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate, public QQuickSelectable
{
public:
Q_DECLARE_PUBLIC(QQuickTableView)
@@ -224,6 +292,11 @@ public:
Done
};
+ enum class SectionState {
+ Idle = 0,
+ Moving
+ };
+
enum class RebuildOption {
None = 0,
All = 0x1,
@@ -343,6 +416,10 @@ public:
QPointer<QItemSelectionModel> selectionModel;
QQuickTableView::SelectionBehavior selectionBehavior = QQuickTableView::SelectCells;
+ QQuickTableView::SelectionMode selectionMode = QQuickTableView::ExtendedSelection;
+ QItemSelectionModel::SelectionFlag selectionFlag = QItemSelectionModel::NoUpdate;
+ std::function<void(CallBackFlag)> selectableCallbackFunction;
+ bool inSelectionModelUpdate = false;
int assignedPositionViewAtRowAfterRebuild = 0;
int assignedPositionViewAtColumnAfterRebuild = 0;
@@ -360,8 +437,10 @@ public:
QPoint selectionStartCell = {-1, -1};
QPoint selectionEndCell = {-1, -1};
+ QItemSelection existingSelection;
QMargins edgesBeforeRebuild;
+ QSize tableSizeBeforeRebuild;
int currentRow = -1;
int currentColumn = -1;
@@ -371,6 +450,8 @@ public:
QQuickTableViewHoverHandler *hoverHandler = nullptr;
QQuickTableViewResizeHandler *resizeHandler = nullptr;
+ QQuickTableViewSectionDragHandler *sectionDragHandler = nullptr;
+ QQuickTableViewPointerHandler *activePtrHandler = nullptr;
QQmlTableInstanceModel *editModel = nullptr;
QQuickItem *editItem = nullptr;
@@ -381,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();
@@ -388,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;
@@ -502,6 +593,7 @@ public:
virtual void syncModel();
virtual void syncSyncView();
virtual void syncPositionView();
+ virtual QAbstractItemModel *selectionSourceModel();
inline void syncRebuildOptions();
void connectToModel();
@@ -515,6 +607,7 @@ public:
void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
void layoutChangedCallback(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
void modelResetCallback();
+ bool compareModel(const QVariant& model1, const QVariant& model2) const;
void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset, const QRectF subRect = QRectF());
void positionViewAtColumn(int column, Qt::Alignment alignment, qreal offset, const QRectF subRect = QRectF());
@@ -531,7 +624,6 @@ public:
void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected);
void updateSelectedOnAllDelegateItems();
void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select);
- void syncSourceModelInSelectionModel();
bool currentInSelectionModel(const QPoint &cell) const;
void currentChangedInSelectionModel(const QModelIndex &current, const QModelIndex &previous);
@@ -560,18 +652,36 @@ public:
// QQuickSelectable
QQuickItem *selectionPointerHandlerTarget() const override;
- bool startSelection(const QPointF &pos) override;
+ bool startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers) override;
void setSelectionStartPos(const QPointF &pos) override;
void setSelectionEndPos(const QPointF &pos) override;
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();
QPoint clampedCellAtPos(const QPointF &pos) const;
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 517ccba845..e29495f451 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -10,10 +10,8 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qsgadaptationlayer_p.h>
-#include "qquicktextnode_p.h"
-#include "qquickimage_p_p.h"
+#include "qsginternaltextnode_p.h"
#include "qquicktextutil_p.h"
-#include "qquicktextdocument_p.h"
#include <QtQuick/private/qsgtexture_p.h>
@@ -29,14 +27,16 @@
#include <private/qtextengine_p.h>
#include <private/qquickstyledtext_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <qmath.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
+Q_STATIC_LOGGING_CATEGORY(lcText, "qt.quick.text")
+
+using namespace Qt::StringLiterals;
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
@@ -47,7 +47,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)
@@ -80,7 +80,6 @@ QQuickTextPrivate::ExtraData::ExtraData()
, doc(nullptr)
, minimumPixelSize(12)
, minimumPointSize(12)
- , nbActiveDownloads(0)
, maximumLineCount(INT_MAX)
, renderTypeQuality(QQuickText::DefaultRenderTypeQuality)
, lineHeightValid(false)
@@ -99,9 +98,6 @@ void QQuickTextPrivate::init()
QQuickTextPrivate::~QQuickTextPrivate()
{
- delete elideLayout;
- delete textLine; textLine = nullptr;
-
if (extra.isAllocated()) {
qDeleteAll(extra->imgTags);
extra->imgTags.clear();
@@ -204,7 +200,7 @@ void QQuickTextPrivate::setBottomPadding(qreal value, bool reset)
Used to decide if the Text should use antialiasing or not. Only Text
with renderType of Text.NativeRendering can disable antialiasing.
- The default is true.
+ The default is \c true.
*/
void QQuickText::q_updateLayout()
@@ -273,32 +269,115 @@ void QQuickTextPrivate::updateLayout()
q->polish();
}
+/*! \internal
+ QTextDocument::loadResource() calls this to load inline images etc.
+ But if it's a local file, don't do it: let QTextDocument::loadResource()
+ load it in the default way. QQuickPixmap is for QtQuick-specific uses.
+*/
+QVariant QQuickText::loadResource(int type, const QUrl &source)
+{
+ Q_D(QQuickText);
+ const QUrl url = d->extra->doc->baseUrl().resolved(source);
+ if (url.isLocalFile()) {
+ // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
+ const QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ if (!fi.exists())
+ qmlWarning(this) << "Cannot open: " << url.toString();
+ // let QTextDocument::loadResource() handle local file loading
+ return {};
+ }
+ // see if we already started a load job
+ for (auto it = d->extra->pixmapsInProgress.cbegin(); it != d->extra->pixmapsInProgress.cend();) {
+ auto *job = *it;
+ if (job->url() == url) {
+ if (job->isError()) {
+ qmlWarning(this) << job->error();
+ delete *it;
+ it = d->extra->pixmapsInProgress.erase(it);
+ return QImage();
+ }
+ qCDebug(lcText) << "already downloading" << url;
+ // existing job: return a null variant if it's not done yet
+ return job->isReady() ? job->image() : QVariant();
+ }
+ ++it;
+ }
+ qCDebug(lcText) << "loading" << source << "resolved" << url
+ << "type" << static_cast<QTextDocument::ResourceType>(type);
+ QQmlContext *context = qmlContext(this);
+ Q_ASSERT(context);
+ // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
+ QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
+ p->connectFinished(this, SLOT(resourceRequestFinished()));
+ d->extra->pixmapsInProgress.append(p);
+ // the new job is probably not done; return a null variant if the caller should poll again
+ return p->isReady() ? p->image() : QVariant();
+}
+
+/*! \internal
+ Handle completion of a download that QQuickText::loadResource() started.
+*/
+void QQuickText::resourceRequestFinished()
+{
+ Q_D(QQuickText);
+ bool allDone = true;
+ for (auto it = d->extra->pixmapsInProgress.cbegin(); it != d->extra->pixmapsInProgress.cend();) {
+ auto *job = *it;
+ if (job->isError()) {
+ // get QTextDocument::loadResource() to call QQuickText::loadResource() again, to return the placeholder
+ qCDebug(lcText) << "failed to load" << job->url();
+ d->extra->doc->resource(QTextDocument::ImageResource, job->url());
+ } else if (job->isReady()) {
+ // get QTextDocument::loadResource() to call QQuickText::loadResource() again, and cache the result
+ auto res = d->extra->doc->resource(QTextDocument::ImageResource, job->url());
+ // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
+ qCDebug(lcText) << (res.isValid() ? "done downloading" : "failed to load") << job->url();
+ delete *it;
+ it = d->extra->pixmapsInProgress.erase(it);
+ } else {
+ allDone = false;
+ ++it;
+ }
+ }
+ if (allDone) {
+ Q_ASSERT(d->extra->pixmapsInProgress.isEmpty());
+ d->updateLayout();
+ }
+}
+
+/*! \internal
+ Handle completion of StyledText image downloads (there's no QTextDocument instance in that case).
+*/
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();
}
}
@@ -430,8 +509,13 @@ void QQuickTextPrivate::updateSize()
layedOutTextRect = QRectF(QPointF(0,0), dsize);
size = QSizeF(extra->doc->idealWidth(),dsize.height());
- QFontMetricsF fm(font);
- updateBaseline(fm.ascent(), q->height() - size.height() - vPadding);
+
+ qreal baseline = QFontMetricsF(font).ascent();
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0)
+ baseline = firstBlock.layout()->lineAt(0).ascent();
+
+ updateBaseline(baseline, q->height() - size.height() - vPadding);
//### need to confirm cost of always setting these for richText
internalWidthUpdate = true;
@@ -468,7 +552,7 @@ void QQuickTextPrivate::updateSize()
QTextLine firstLine = firstBlock.layout()->lineAt(0);
QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
advance = QSizeF(lastLine.horizontalAdvance(),
- (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ (lastLine.y() + lastBlock.layout()->position().y() + lastLine.ascent()) - (firstLine.y() + firstBlock.layout()->position().y() + firstLine.ascent()));
} else {
advance = QSizeF();
}
@@ -591,7 +675,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);
@@ -607,7 +691,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();
}
@@ -1103,7 +1187,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();
@@ -1153,8 +1237,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (visibleCount == 1)
layout.clearLayout();
} else {
- delete elideLayout;
- elideLayout = nullptr;
+ elideLayout.reset();
}
QTextLine firstLine = visibleCount == 1 && elideLayout
@@ -1203,12 +1286,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();
@@ -1266,13 +1347,14 @@ void QQuickTextPrivate::ensureDoc()
{
if (!extra.isAllocated() || !extra->doc) {
Q_Q(QQuickText);
- extra.value().doc = new QQuickTextDocumentWithImageResources(q);
- extra->doc->setPageSize(QSizeF(0, 0));
- extra->doc->setDocumentMargin(0);
+ extra.value().doc = new QTextDocument(q);
+ auto *doc = extra->doc;
+ extra->imageHandler = new QQuickTextImageHandler(doc);
+ doc->documentLayout()->registerHandler(QTextFormat::ImageObject, extra->imageHandler);
+ doc->setPageSize(QSizeF(0, 0));
+ doc->setDocumentMargin(0);
const QQmlContext *context = qmlContext(q);
- extra->doc->setBaseUrl(context ? context->resolvedUrl(q->baseUrl()) : q->baseUrl());
- qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
- q, QQuickText, SLOT(q_updateLayout()));
+ doc->setBaseUrl(context ? context->resolvedUrl(q->baseUrl()) : q->baseUrl());
}
}
@@ -1289,10 +1371,14 @@ void QQuickTextPrivate::updateDocumentText()
#else
extra->doc->setPlainText(text);
#endif
- extra->doc->clearResources();
rightToLeftText = extra->doc->toPlainText().isRightToLeft();
}
+qreal QQuickTextPrivate::devicePixelRatio() const
+{
+ return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
+}
+
/*!
\qmltype Text
\instantiates QQuickText
@@ -1301,8 +1387,8 @@ void QQuickTextPrivate::updateDocumentText()
\inherits Item
\brief Specifies how to add formatted text to a scene.
- Text items can display both plain and rich text. For example, red text with
- a specific font and size can be defined like this:
+ Text items can display both plain and rich text. For example, you can define
+ red text with a specific font and size like this:
\qml
Text {
@@ -1313,25 +1399,47 @@ void QQuickTextPrivate::updateDocumentText()
}
\endqml
- Rich text is defined using HTML-style markup:
+ Use HTML-style markup or Markdown to define rich text:
+ \if defined(onlinedocs)
+ \tab {build-qt-app}{tab-html}{HTML-style}{checked}
+ \tab {build-qt-app}{tab-md}{Markdown}{}
+ \tabcontent {tab-html}
+ \else
+ \section1 Using HTML-style
+ \endif
\qml
Text {
text: "<b>Hello</b> <i>World!</i>"
}
\endqml
+ \if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-md}
+ \else
+ \section1 Using Markdown
+ \endif
+ \qml
+ Text {
+ text: "**Hello** *World!*"
+ }
+ \endqml
+ \if defined(onlinedocs)
+ \endtabcontent
+ \endif
\image declarative-text.png
- If height and width are not explicitly set, Text will attempt to determine how
- much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
- prefer width to height (all text will be placed on a single line).
+ If height and width are not explicitly set, Text will try to determine how
+ much room is needed and set it accordingly. Unless \l wrapMode is set, it
+ will always prefer width to height (all text will be placed on a single
+ line).
- The \l elide property can alternatively be used to fit a single line of
- plain text to a set width.
+ To fit a single line of plain text to a set width, you can use the \l elide
+ property.
- Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
- HTML img tags that load remote images, the text is reloaded.
+ Note that the \l{Supported HTML Subset} is limited. Also, if the text
+ contains HTML img tags that load remote images, the text is reloaded.
Text provides read-only text. For editable text, see \l TextEdit.
@@ -1353,13 +1461,18 @@ QQuickText::QQuickText(QQuickTextPrivate &dd, QQuickItem *parent)
QQuickText::~QQuickText()
{
+ Q_D(QQuickText);
+ if (d->extra.isAllocated()) {
+ qDeleteAll(d->extra->pixmapsInProgress);
+ d->extra->pixmapsInProgress.clear();
+ }
}
/*!
\qmlproperty bool QtQuick::Text::clip
This property holds whether the text is clipped.
- Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
+ Note that if the text does not fit in the bounding rectangle, it will be abruptly chopped.
If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
*/
@@ -1448,7 +1561,8 @@ QQuickText::~QQuickText()
Sets the family name of the font.
- The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ The family name is case insensitive and may optionally include a foundry
+ name, for example "Helvetica [Cronyx]".
If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
If the family isn't available a family will be set using the font matching algorithm.
*/
@@ -1474,17 +1588,16 @@ QQuickText::~QQuickText()
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
Text { text: "Hello"; font.weight: Font.DemiBold }
@@ -1548,13 +1661,12 @@ QQuickText::~QQuickText()
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase the normal case: no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
Text { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -1571,23 +1683,21 @@ QQuickText::~QQuickText()
\note This property only has an effect when used together with render type Text.NativeRendering.
- \list
- \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
of the glyphs. The text layout will be typographically accurate, using the same metrics
- as are used e.g. when printing.
- \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ as are used, for example, when printing.
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
but align glyphs to the pixel grid in the vertical direction. The text will appear
crisper on displays where the density is too low to give an accurate rendering
of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
layout will be scalable to higher density devices (such as printers) without impacting
details such as line breaks.
- \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
vertical directions. The text will be altered to optimize legibility on the target
device, but since the metrics will depend on the target size of the text, the positions
of glyphs, line breaks, and other typographical detail will not scale, meaning that a
text layout may look different on devices with different pixel densities.
- \endlist
\qml
Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -1613,7 +1723,7 @@ QQuickText::~QQuickText()
Sometimes, a font will apply complex rules to a set of characters in order to
display them correctly. In some writing systems, such as Brahmic scripts, this is
- required in order for the text to be legible, but in e.g. Latin script, it is merely
+ required in order for the text to be legible, but in for example Latin script, it is merely
a cosmetic feature. Setting the \c preferShaping property to false will disable all
such features when they are not required, which will improve performance in most cases.
@@ -1623,6 +1733,167 @@ QQuickText::~QQuickText()
Text { text: "Some text"; font.preferShaping: false }
\endqml
*/
+
+/*!
+ \qmlproperty object QtQuick::Text::font.variableAxes
+ \since 6.7
+
+//! [qml-font-variable-axes]
+ Applies floating point values to variable axes in variable fonts.
+
+ Variable fonts provide a way to store multiple variations (with different weights, widths
+ or styles) in the same font file. The variations are given as floating point values for
+ a pre-defined set of parameters, called "variable axes". Specific instances are typically
+ given names by the font designer, and, in Qt, these can be selected using setStyleName()
+ just like traditional sub-families.
+
+ In some cases, it is also useful to provide arbitrary values for the different axes. For
+ instance, if a font has a Regular and Bold sub-family, you may want a weight in-between these.
+ You could then manually request this by supplying a custom value for the "wght" axis in the
+ font.
+
+ \qml
+ Text {
+ text: "Foobar"
+ font.family: "MyVariableFont"
+ font.variableAxes: { "wght": (Font.Normal + Font.Bold) / 2.0 }
+ }
+ \endqml
+
+ If the "wght" axis is supported by the font and the given value is within its defined range,
+ a font corresponding to the weight 550.0 will be provided.
+
+ There are a few standard axes than many fonts provide, such as "wght" (weight), "wdth" (width),
+ "ital" (italic) and "opsz" (optical size). They each have indivdual ranges defined in the font
+ itself. For instance, "wght" may span from 100 to 900 (QFont::Thin to QFont::Black) whereas
+ "ital" can span from 0 to 1 (from not italic to fully italic).
+
+ A font may also choose to define custom axes; the only limitation is that the name has to
+ meet the requirements for a QFont::Tag (sequence of four latin-1 characters.)
+
+ By default, no variable axes are set.
+
+ \note On Windows, variable axes are not supported if the optional GDI font backend is in use.
+
+ \sa QFont::setVariableAxis()
+//! [qml-font-variable-axes]
+*/
+
+
+/*!
+ \qmlproperty object QtQuick::Text::font.features
+ \since 6.6
+
+//! [qml-font-features]
+ Applies integer values to specific OpenType features when shaping the text based on the contents
+ in \a features. This provides advanced access to the font shaping process, and can be used
+ to support font features that are otherwise not covered in the API.
+
+ The font features are represented by a map from four-letter tags to integer values. This integer
+ value passed along with the tag in most cases represents a boolean value: A zero value means the
+ feature is disabled, and a non-zero value means it is enabled. For certain font features,
+ however, it may have other interpretations. For example, when applied to the \c salt feature, the
+ value is an index that specifies the stylistic alternative to use.
+
+ For example, the \c frac font feature will convert diagonal fractions separated with a slash
+ (such as \c 1/2) with a different representation. Typically this will involve baking the full
+ fraction into a single character width (such as \c ½).
+
+ If a font supports the \c frac feature, then it can be enabled in the shaper as in the following
+ code:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font.family: "MyFractionFont"
+ font.features: { "frac": 1 }
+ }
+ \endqml
+
+ Multiple features can be assigned values in the same mapping. For instance,
+ if you would like to also disable kerning for the font, you can explicitly
+ disable this as follows:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font.family: "MyFractionFont"
+ font.features: { "frac": 1, "kern": 0 }
+ }
+ \endqml
+
+ You can also collect the font properties in an object:
+
+ \qml
+ Text {
+ text: "One divided by two is 1/2"
+ font: {
+ family: "MyFractionFont"
+ features: { "frac": 1, "kern": 0 }
+ }
+ }
+ \endqml
+
+ \note By default, Qt will enable and disable certain font features based on other font
+ properties. In particular, the \c kern feature will be enabled/disabled depending on the
+ \l font.kerning property of the QFont. In addition, all ligature features (\c liga, \c clig,
+ \c dlig, \c hlig) will be disabled if a \l font.letterSpacing is set, but only for writing
+ systems where the use of ligature is cosmetic. For writing systems where ligatures are required,
+ the features will remain in their default state. The values set using \c font.features will
+ override the default behavior. If, for instance, \c{"kern"} is set to 1, then kerning will
+ always be enabled, regardless of whether the \l font.kerning property is set to false. Similarly,
+ if it is set to \c 0, it will always be disabled.
+
+ \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);
@@ -1677,14 +1948,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;
@@ -1823,12 +2110,11 @@ void QQuickText::setLinkColor(const QColor &color)
Set an additional text style.
Supported text styles are:
- \list
- \li Text.Normal - the default
- \li Text.Outline
- \li Text.Raised
- \li Text.Sunken
- \endlist
+
+ \value Text.Normal - the default
+ \value Text.Outline
+ \value Text.Raised
+ \value Text.Sunken
\qml
Row {
@@ -2028,12 +2314,17 @@ void QQuickText::setVAlign(VAlignment align)
Set this property to wrap the text to the Text item's width. The text will only
wrap if an explicit width has been set. wrapMode can be one of:
- \list
- \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
- \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
- \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value Text.NoWrap
+ (default) no wrapping will be performed. If the text contains
+ insufficient newlines, then \l contentWidth will exceed a set width.
+ \value Text.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l contentWidth will exceed a set width.
+ \value Text.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value Text.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur
+ at the appropriate point on the line, even in the middle of a word.
*/
QQuickText::WrapMode QQuickText::wrapMode() const
{
@@ -2139,7 +2430,7 @@ void QQuickText::resetMaximumLineCount()
\l {https://guides.github.com/features/mastering-markdown/}{GitHub}
extensions for tables and task lists (since 5.14)
- If the text format is \c Text.AutoText the Text item
+ If the text format is \c Text.AutoText, the Text item
will automatically determine whether the text should be treated as
styled text. This determination is made using Qt::mightBeRichText(),
which can detect the presence of an HTML tag on the first line of text,
@@ -2184,7 +2475,6 @@ void QQuickText::resetMaximumLineCount()
\list
\li code blocks use the \l {QFontDatabase::FixedFont}{default monospace font} but without a surrounding highlight box
\li block quotes are indented, but there is no vertical line alongside the quote
- \li horizontal rules are not rendered
\endlist
*/
QQuickText::TextFormat QQuickText::textFormat() const
@@ -2230,12 +2520,11 @@ void QQuickText::setTextFormat(TextFormat format)
This property cannot be used with rich text.
Eliding can be:
- \list
- \li Text.ElideNone - the default
- \li Text.ElideLeft
- \li Text.ElideMiddle
- \li Text.ElideRight
- \endlist
+
+ \value Text.ElideNone - the default
+ \value Text.ElideLeft
+ \value Text.ElideMiddle
+ \value Text.ElideRight
If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
text. The text will only elide if \c maximumLineCount, or \c height has been set.
@@ -2269,7 +2558,7 @@ void QQuickText::setElideMode(QQuickText::TextElideMode mode)
/*!
\qmlproperty url QtQuick::Text::baseUrl
- This property specifies a base URL which is used to resolve relative URLs
+ This property specifies a base URL that is used to resolve relative URLs
within the text.
Urls are resolved to be within the same directory as the target of the base
@@ -2439,8 +2728,10 @@ void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom
}
}
} else if (!heightChanged && widthMaximum) {
- if (!qFuzzyIsNull(oldGeometry.width())) {
+ if (oldGeometry.width() > 0) {
// no change to height, width is adequate and wasn't 0 before
+ // (old width could also be negative if it was 0 and the margins
+ // were set)
goto geomChangeDone;
}
}
@@ -2485,46 +2776,48 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), d->availableHeight(), d->vAlign) + topPadding();
- QQuickTextNode *node = nullptr;
+ QSGInternalTextNode *node = nullptr;
if (!oldNode)
- node = new QQuickTextNode(this);
+ node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
else
- node = static_cast<QQuickTextNode *>(oldNode);
+ node = static_cast<QSGInternalTextNode *>(oldNode);
+
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
- node->setUseNativeRenderer(d->renderType == NativeRendering);
+ node->setTextStyle(QSGTextNode::TextStyle(d->style));
+ node->setRenderType(QSGTextNode::RenderType(d->renderType));
node->setRenderTypeQuality(d->renderTypeQuality());
- node->deleteContent();
+ node->clear();
node->setMatrix(QMatrix4x4());
- const QColor color = QColor::fromRgba(d->color);
- const QColor styleColor = QColor::fromRgba(d->styleColor);
- const QColor linkColor = QColor::fromRgba(d->linkColor);
+ node->setColor(QColor::fromRgba(d->color));
+ node->setStyleColor(QColor::fromRgba(d->styleColor));
+ node->setLinkColor(QColor::fromRgba(d->linkColor));
if (d->richText) {
+ node->setViewport(clipRect());
const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), d->availableWidth(), effectiveHAlign()) + leftPadding();
d->ensureDoc();
- node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor);
+ node->addTextDocument(QPointF(dx, dy), d->extra->doc);
} else if (d->layedOutTextRect.width() > 0) {
+ if (flags().testFlag(ItemObservesViewport))
+ node->setViewport(clipRect());
+ else
+ node->setViewport(QRectF{});
const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, d->availableWidth(), effectiveHAlign()) + leftPadding();
int unelidedLineCount = d->lineCount;
if (d->elideLayout)
unelidedLineCount -= 1;
- if (unelidedLineCount > 0) {
- node->addTextLayout(
- QPointF(dx, dy),
- &d->layout,
- color, d->style, styleColor, linkColor,
- QColor(), QColor(), -1, -1,
- 0, unelidedLineCount);
- }
+ if (unelidedLineCount > 0)
+ node->addTextLayout(QPointF(dx, dy), &d->layout, -1, -1,0, unelidedLineCount);
+
if (d->elideLayout)
- node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
+ 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());
}
}
}
@@ -2539,6 +2832,11 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
void QQuickText::updatePolish()
{
Q_D(QQuickText);
+ const bool clipNodeChanged =
+ d->componentComplete && d->clipNode() && d->clipNode()->rect() != clipRect();
+ if (clipNodeChanged)
+ d->dirty(QQuickItemPrivate::Clip);
+
// If the fonts used for rendering are different from the ones used in the GUI thread,
// it means we will get warnings and corrupted text. If this case is detected, we need
// to update the text layout before creating the scenegraph nodes.
@@ -2556,7 +2854,7 @@ void QQuickText::updatePolish()
\qmlproperty real QtQuick::Text::contentWidth
Returns the width of the text, including width past the width
- which is covered due to insufficient wrapping if WrapMode is set.
+ that is covered due to insufficient wrapping if WrapMode is set.
*/
qreal QQuickText::contentWidth() const
{
@@ -2568,7 +2866,7 @@ qreal QQuickText::contentWidth() const
\qmlproperty real QtQuick::Text::contentHeight
Returns the height of the text, including height past the height
- which is covered due to there being more text than fits in the set height.
+ that is covered due to there being more text than fits in the set height.
*/
qreal QQuickText::contentHeight() const
{
@@ -2611,11 +2909,9 @@ void QQuickText::setLineHeight(qreal lineHeight)
This property determines how the line height is specified.
The possible values are:
- \list
- \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
- line (as a multiplier). For example, set to 2 for double spacing.
- \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
- \endlist
+ \value Text.ProportionalHeight (default) sets the spacing proportional to the line
+ (as a multiplier). For example, set to 2 for double spacing.
+ \value Text.FixedHeight sets the line height to a fixed line height (in pixels).
*/
QQuickText::LineHeightMode QQuickText::lineHeightMode() const
{
@@ -2643,16 +2939,16 @@ void QQuickText::setLineHeightMode(LineHeightMode mode)
This property specifies how the font size of the displayed text is determined.
The possible values are:
- \list
- \li Text.FixedSize (default) - The size specified by \l font.pixelSize
- or \l font.pointSize is used.
- \li Text.HorizontalFit - The largest size up to the size specified that fits
- within the width of the item without wrapping is used.
- \li Text.VerticalFit - The largest size up to the size specified that fits
- the height of the item is used.
- \li Text.Fit - The largest size up to the size specified that fits within the
- width and height of the item is used.
- \endlist
+ \value Text.FixedSize
+ (default) The size specified by \l font.pixelSize or \l font.pointSize is used.
+ \value Text.HorizontalFit
+ The largest size up to the size specified that fits within the width of the item
+ without wrapping is used.
+ \value Text.VerticalFit
+ The largest size up to the size specified that fits the height of the item is used.
+ \value Text.Fit
+ The largest size up to the size specified that fits within the width and height
+ of the item is used.
The font size of fitted text has a minimum bound specified by the
minimumPointSize or minimumPixelSize property and maximum bound specified
@@ -2757,8 +3053,8 @@ void QQuickText::setMinimumPointSize(int size)
int QQuickText::resourcesLoading() const
{
Q_D(const QQuickText);
- if (d->richText && d->extra.isAllocated() && d->extra->doc)
- return d->extra->doc->resourcesLoading();
+ if (d->richText && d->extra.isAllocated())
+ return d->extra->pixmapsInProgress.size();
return 0;
}
@@ -2808,7 +3104,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());
@@ -2981,13 +3277,13 @@ void QQuickText::invalidate()
{
Q_D(QQuickText);
d->textHasChanged = true;
- d->updateLayout();
+ QMetaObject::invokeMethod(this,[&]{q_updateLayout();});
}
bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
{
// If there's a lot of text, we may need QQuickText::updatePaintNode() to call
- // QQuickTextNode::addTextLayout() again to populate a different range of lines
+ // QSGInternalTextNode::addTextLayout() again to populate a different range of lines
if (flags & QQuickItem::ItemObservesViewport) {
updateType = UpdatePaintNode;
dirty(QQuickItemPrivate::Content);
@@ -2997,6 +3293,7 @@ bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
/*!
\qmlproperty int QtQuick::Text::renderTypeQuality
+ \since 6.0
Override the default rendering type quality for this component. This is a low-level
customization which can be ignored in most cases. It currently only has an effect
@@ -3010,13 +3307,11 @@ bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
The \c renderTypeQuality may be any integer over 0, or one of the following
predefined values
- \list
- \li Text.DefaultRenderTypeQuality (default) = -1
- \li Text.LowRenderTypeQuality = 26
- \li Text.NormalRenderTypeQuality = 52
- \li Text.HighRenderTypeQuality = 104
- \li Text.VeryHighRenderTypeQuality = 208
- \endlist
+ \value Text.DefaultRenderTypeQuality -1 (default)
+ \value Text.LowRenderTypeQuality 26
+ \value Text.NormalRenderTypeQuality 52
+ \value Text.HighRenderTypeQuality 104
+ \value Text.VeryHighRenderTypeQuality 208
*/
int QQuickText::renderTypeQuality() const
{
@@ -3045,16 +3340,23 @@ void QQuickText::setRenderTypeQuality(int renderTypeQuality)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value Text.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value Text.NativeRendering Text is rendered using a platform-specific technique.
+ \value Text.CurveRendering Text is rendered using a curve rasterizer running directly on the
+ graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c Text.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c Text.QtRendering and \c Text.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickText::RenderType QQuickText::renderType() const
@@ -3310,7 +3612,7 @@ void QQuickText::resetBottomPadding()
*/
/*!
- \qmlproperty string QtQuick::Text::fontInfo.pixelSize
+ \qmlproperty int QtQuick::Text::fontInfo.pixelSize
\since 5.9
The pixel size of the font info that has been resolved for the current font
@@ -3347,7 +3649,7 @@ QJSValue QQuickText::fontInfo() const
in a text flow.
Note that the advance can be negative if the text flows from
- the right to the left.
+ right to left.
*/
QSizeF QQuickText::advance() const
{
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index e1d786d098..ed474b29c0 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTextPrivate;
class QQuickTextLine;
-class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem, public QQuickTextInterface
+class Q_QUICK_EXPORT QQuickText : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -109,7 +109,8 @@ public:
Q_ENUM(WrapMode)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
@@ -300,6 +301,8 @@ protected:
private Q_SLOTS:
void q_updateLayout();
void triggerPreprocess();
+ Q_REVISION(6, 7) QVariant loadResource(int type, const QUrl &source);
+ void resourceRequestFinished();
void imageDownloadFinished();
private:
@@ -310,16 +313,16 @@ private:
Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickText::HAlignment, QQuickText::VAlignment)
class QTextLine;
-class QQuickTextLine : public QObject
+class Q_QUICK_EXPORT QQuickTextLine : public QObject
{
Q_OBJECT
- Q_PROPERTY(int number READ number)
- Q_PROPERTY(qreal width READ width WRITE setWidth)
- Q_PROPERTY(qreal height READ height WRITE setHeight)
- Q_PROPERTY(qreal x READ x WRITE setX)
- Q_PROPERTY(qreal y READ y WRITE setY)
- Q_PROPERTY(qreal implicitWidth READ implicitWidth REVISION(2, 15))
- Q_PROPERTY(bool isLast READ isLast REVISION(2, 15))
+ Q_PROPERTY(int number READ number FINAL)
+ Q_PROPERTY(qreal width READ width WRITE setWidth FINAL)
+ Q_PROPERTY(qreal height READ height WRITE setHeight FINAL)
+ Q_PROPERTY(qreal x READ x WRITE setX FINAL)
+ Q_PROPERTY(qreal y READ y WRITE setY FINAL)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth REVISION(2, 15) FINAL)
+ Q_PROPERTY(bool isLast READ isLast REVISION(2, 15) FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
@@ -354,7 +357,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickText)
-QML_DECLARE_TYPE(QQuickTextLine)
-
#endif // QQUICKTEXT_P_H
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 9870197c31..6dba7a7d75 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -23,13 +23,13 @@
#include <QtGui/qtextlayout.h>
#include <private/qquickstyledtext_p.h>
#include <private/qlazilyallocated_p.h>
+#include <private/qquicktextdocument_p.h>
QT_BEGIN_NAMESPACE
class QTextLayout;
-class QQuickTextDocumentWithImageResources;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QQuickText)
public:
@@ -71,12 +71,12 @@ public:
bool explicitRightPadding : 1;
bool explicitBottomPadding : 1;
qreal lineHeight;
- QQuickTextDocumentWithImageResources *doc;
+ QTextDocument *doc;
+ QQuickTextImageHandler *imageHandler = nullptr;
QString activeLink;
QString hoveredLink;
int minimumPixelSize;
int minimumPointSize;
- int nbActiveDownloads;
int maximumLineCount;
int renderTypeQuality;
bool lineHeightValid : 1;
@@ -84,6 +84,7 @@ public:
QQuickText::FontSizeMode fontSizeMode;
QList<QQuickStyledTextImgTag*> imgTags;
QList<QQuickStyledTextImgTag*> visibleImgTags;
+ QList<QQuickPixmap *> pixmapsInProgress;
QUrl baseUrl;
};
QLazilyAllocated<ExtraData> extra;
@@ -94,8 +95,8 @@ public:
QFontInfo fontInfo;
QTextLayout layout;
- QTextLayout *elideLayout;
- QQuickTextLine *textLine;
+ QScopedPointer<QTextLayout> elideLayout;
+ QScopedPointer<QQuickTextLine> textLine;
qreal lineWidth;
@@ -162,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/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 929ce10136..cbc46f7728 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -42,7 +42,6 @@
const int textCursorWidth = 1;
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
// could go into QTextCursor...
static QTextLine currentTextLine(const QTextCursor &cursor)
@@ -81,6 +80,7 @@ QQuickTextControlPrivate::QQuickTextControlPrivate()
cursorRectangleChanged(false),
hoveredMarker(false),
selectByTouchDrag(false),
+ imSelectionAfterPress(false),
lastSelectionStart(-1),
lastSelectionEnd(-1)
{}
@@ -583,25 +583,7 @@ void QQuickTextControl::clear()
QQuickTextControl::QQuickTextControl(QTextDocument *doc, QObject *parent)
: QInputControl(TextEdit, *new QQuickTextControlPrivate, parent)
{
- Q_D(QQuickTextControl);
- Q_ASSERT(doc);
-
- QAbstractTextDocumentLayout *layout = doc->documentLayout();
- qmlobject_connect(layout, QAbstractTextDocumentLayout, SIGNAL(update(QRectF)), this, QQuickTextControl, SIGNAL(updateRequest()));
- qmlobject_connect(layout, QAbstractTextDocumentLayout, SIGNAL(updateBlock(QTextBlock)), this, QQuickTextControl, SIGNAL(updateRequest()));
- qmlobject_connect(doc, QTextDocument, SIGNAL(contentsChanged()), this, QQuickTextControl, SIGNAL(textChanged()));
- qmlobject_connect(doc, QTextDocument, SIGNAL(contentsChanged()), this, QQuickTextControl, SLOT(_q_updateCurrentCharFormatAndSelection()));
- qmlobject_connect(doc, QTextDocument, SIGNAL(cursorPositionChanged(QTextCursor)), this, QQuickTextControl, SLOT(_q_updateCursorPosChanged(QTextCursor)));
- connect(doc, &QTextDocument::contentsChange, this, &QQuickTextControl::contentsChange);
-
- layout->setProperty("cursorWidth", textCursorWidth);
-
- d->doc = doc;
- d->cursor = QTextCursor(doc);
- d->lastCharFormat = d->cursor.charFormat();
- doc->setPageSize(QSizeF(0, 0));
- doc->setModified(false);
- doc->setUndoRedoEnabled(true);
+ setDocument(doc);
}
QQuickTextControl::~QQuickTextControl()
@@ -614,6 +596,34 @@ QTextDocument *QQuickTextControl::document() const
return d->doc;
}
+void QQuickTextControl::setDocument(QTextDocument *doc)
+{
+ Q_D(QQuickTextControl);
+ if (!doc || d->doc == doc)
+ return;
+
+ d->doc = doc;
+ d->cursor = QTextCursor(doc);
+ d->lastCharFormat = d->cursor.charFormat();
+ doc->setPageSize(QSizeF(0, 0));
+ doc->setModified(false);
+ doc->setUndoRedoEnabled(true);
+
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+ connect(layout, &QAbstractTextDocumentLayout::update, this, &QQuickTextControl::updateRequest);
+ connect(layout, &QAbstractTextDocumentLayout::updateBlock, this, &QQuickTextControl::updateRequest);
+ connect(doc, &QTextDocument::contentsChanged, doc, [d, this]() {
+ d->_q_updateCurrentCharFormatAndSelection();
+ emit textChanged();
+ });
+ connect(doc, &QTextDocument::cursorPositionChanged, doc, [d](const QTextCursor &cursor) {
+ d->_q_updateCursorPosChanged(cursor);
+ });
+ connect(doc, &QTextDocument::contentsChange, this, &QQuickTextControl::contentsChange);
+ if (auto *qtdlayout = qobject_cast<QTextDocumentLayout *>(layout))
+ qtdlayout->setCursorWidth(textCursorWidth);
+}
+
void QQuickTextControl::updateCursorRectangle(bool force)
{
Q_D(QQuickTextControl);
@@ -992,6 +1002,7 @@ void QQuickTextControlPrivate::mousePressEvent(QMouseEvent *e, const QPointF &po
mousePressed = (interactionFlags & Qt::TextSelectableByMouse) && (e->button() & Qt::LeftButton);
mousePressPos = pos.toPoint();
+ imSelectionAfterPress = false;
if (sendMouseEventToInputContext(e, pos))
return;
@@ -1177,7 +1188,7 @@ void QQuickTextControlPrivate::mouseReleaseEvent(QMouseEvent *e, const QPointF &
q->insertFromMimeData(md);
#endif
}
- if (!isMouse && !selectByTouchDrag && interactionFlags.testFlag(Qt::TextEditable))
+ if (!isMouse && !selectByTouchDrag && !imSelectionAfterPress && interactionFlags.testFlag(Qt::TextEditable))
setCursorPosition(pos);
repaintOldAndNewSelection(oldSelection);
@@ -1322,6 +1333,8 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
for (int i = 0; i < e->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = e->attributes().at(i);
if (a.type == QInputMethodEvent::Selection) {
+ if (mousePressed)
+ imSelectionAfterPress = true;
QTextCursor oldCursor = cursor;
int blockStart = a.start + cursor.block().position();
cursor.setPosition(blockStart, QTextCursor::MoveAnchor);
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index 4994c4a51e..293409ca43 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -46,6 +46,7 @@ public:
virtual ~QQuickTextControl();
QTextDocument *document() const;
+ void setDocument(QTextDocument *doc);
void setTextCursor(const QTextCursor &cursor);
QTextCursor textCursor() const;
diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h
index 5717cf6377..f04d298e4a 100644
--- a/src/quick/items/qquicktextcontrol_p_p.h
+++ b/src/quick/items/qquicktextcontrol_p_p.h
@@ -131,6 +131,7 @@ public:
bool cursorRectangleChanged : 1;
bool hoveredMarker: 1;
bool selectByTouchDrag: 1;
+ bool imSelectionAfterPress: 1;
int lastSelectionStart;
int lastSelectionEnd;
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index ff28abe600..a1fb7adcea 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -5,15 +5,42 @@
#include "qquicktextdocument_p.h"
#include "qquicktextedit_p.h"
-#include "qquicktextedit_p_p.h"
-#include "qquicktext_p_p.h"
-#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlcontext.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQml/qqmlfile.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/private/qquickpixmap_p.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTextDoc, "qt.quick.textdocument")
+
+using namespace Qt::StringLiterals;
+
+/*!
+ \qmltype TextDocument
+ \instantiates QQuickTextDocument
+ \inqmlmodule QtQuick
+ \brief A wrapper around TextEdit's backing QTextDocument.
+ \preliminary
+
+ To load text into the document, set the \l source property. If the user then
+ modifies the text and wants to save the same document, call \l save() to save
+ it to the same source again (only if \l {QUrl::isLocalFile()}{it's a local file}).
+ Or call \l saveAs() to save it to a different file.
+
+ This class cannot be instantiated in QML, but is available from \l TextEdit::textDocument.
+
+ \note All loading and saving is done synchronously for now.
+ This may block the UI if the \l source is a slow network drive.
+ This may be improved in future versions of Qt.
+
+ \note This API is considered tech preview and may change in future versions of Qt.
+*/
+
/*!
\class QQuickTextDocument
\since 5.1
@@ -21,23 +48,12 @@ QT_BEGIN_NAMESPACE
\inmodule QtQuick
This class provides access to the QTextDocument of QQuickTextEdit elements.
- This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt.
- You are not allowed to modify the document, but it can be used to output content, for example with \l{QTextDocumentWriter}),
- or provide additional formatting, for example with \l{QSyntaxHighlighter}.
-
- The class has to be used from C++ directly, using the property of the \l TextEdit.
-
- Warning: The QTextDocument provided is used internally by \l {Qt Quick} elements to provide text manipulation primitives.
- You are not allowed to perform any modification of the internal state of the QTextDocument. If you do, the element
- in question may stop functioning or crash.
+ This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt,
+ including document modifications. It can also be used to output content,
+ for example with \l{QTextDocumentWriter}, or provide additional formatting,
+ for example with \l{QSyntaxHighlighter}.
*/
-class QQuickTextDocumentPrivate : public QObjectPrivate
-{
-public:
- QPointer<QTextDocument> document;
-};
-
/*!
Constructs a QQuickTextDocument object with
\a parent as the parent object.
@@ -47,74 +63,557 @@ QQuickTextDocument::QQuickTextDocument(QQuickItem *parent)
{
Q_D(QQuickTextDocument);
Q_ASSERT(parent);
- Q_ASSERT(qobject_cast<QQuickTextEdit*>(parent));
- d->document = QPointer<QTextDocument>(qobject_cast<QQuickTextEdit*>(parent)->d_func()->document);
+ d->editor = qobject_cast<QQuickTextEdit *>(parent);
+ Q_ASSERT(d->editor);
+ connect(textDocument(), &QTextDocument::modificationChanged,
+ this, &QQuickTextDocument::modifiedChanged);
}
/*!
- Returns a pointer to the QTextDocument object.
+ \property QQuickTextDocument::status
+ \brief the status of document loading or saving
+ \since 6.7
+ \preliminary
+
+ This property holds the status of document loading or saving. It can be one of:
+
+ \value Null No file has been loaded
+ \value Loading Reading from \l source has begun
+ \value Loaded Reading has successfully finished
+ \value Saving File writing has begun after save() or saveAs()
+ \value Saved Writing has successfully finished
+ \value ReadError An error occurred while reading from \l source
+ \value WriteError An error occurred in save() or saveAs()
+ \value NonLocalFileError saveAs() was called with a URL pointing
+ to a remote resource rather than a local file
+
+ \sa errorString, source, save(), saveAs()
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::TextDocument::status
+ \readonly
+ \since 6.7
+ \preliminary
+
+ This property holds the status of document loading or saving. It can be one of:
+
+ \value TextDocument.Null No file has been loaded
+ \value TextDocument.Loading Reading from \l source has begun
+ \value TextDocument.Loaded Reading has successfully finished
+ \value TextDocument.Saving File writing has begun after save() or saveAs()
+ \value TextDocument.Saved Writing has successfully finished
+ \value TextDocument.ReadError An error occurred while reading from \l source
+ \value TextDocument.WriteError An error occurred in save() or saveAs()
+ \value TextDocument.NonLocalFileError saveAs() was called with a URL pointing
+ to a remote resource rather than a local file
+
+ Use this status to provide an update or respond to the status change in some way.
+ For example, you could:
+
+ \list
+ \li Trigger a state change:
+ \qml
+ State {
+ name: 'loaded'
+ when: textEdit.textDocument.status == textEdit.textDocument.Loaded
+ }
+ \endqml
+
+ \li Implement an \c onStatusChanged signal handler:
+ \qml
+ TextEdit {
+ onStatusChanged: {
+ if (textDocument.status === textDocument.Loaded)
+ console.log('Loaded')
+ }
+ }
+ \endqml
+
+ \li Bind to the status value:
+
+ \snippet qml/textEditStatusSwitch.qml 0
+
+ \endlist
+
+ \sa errorString, source, save(), saveAs()
+*/
+QQuickTextDocument::Status QQuickTextDocument::status() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->status;
+}
+
+/*!
+ \property QQuickTextDocument::errorString
+ \brief a human-readable string describing the error that occurred during loading or saving, if any
+ \since 6.7
+ \preliminary
+
+ By default this string is empty.
+
+ \sa status, source, save(), saveAs()
+*/
+
+/*!
+ \qmlproperty string QtQuick::TextDocument::errorString
+ \readonly
+ \since 6.7
+ \preliminary
+
+ This property holds a human-readable string describing the error that
+ occurred during loading or saving, if any; otherwise, an empty string.
+
+ \sa status, source, save(), saveAs()
+*/
+QString QQuickTextDocument::errorString() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->errorString;
+}
+
+void QQuickTextDocumentPrivate::setStatus(QQuickTextDocument::Status s, const QString &err)
+{
+ Q_Q(QQuickTextDocument);
+ if (status == s)
+ return;
+
+ status = s;
+ emit q->statusChanged();
+
+ if (errorString == err)
+ return;
+ errorString = err;
+ emit q->errorStringChanged();
+ if (!err.isEmpty())
+ qmlWarning(q) << err;
+}
+
+/*!
+ \property QQuickTextDocument::source
+ \brief the URL from which to load document contents
+ \since 6.7
+ \preliminary
+
+ QQuickTextDocument can handle any text format supported by Qt, loaded from
+ any URL scheme supported by Qt.
+
+ The \c source property cannot be changed while the document's \l modified
+ state is \c true. If the user has modified the document contents, you
+ should prompt the user whether to \l save(), or else discard changes by
+ setting \l modified to \c false before setting the \c source property to a
+ different URL.
+
+ \sa QTextDocumentWriter::supportedDocumentFormats()
*/
-QTextDocument* QQuickTextDocument::textDocument() const
+
+/*!
+ \qmlproperty url QtQuick::TextDocument::source
+ \since 6.7
+ \preliminary
+
+ QQuickTextDocument can handle any text format supported by Qt, loaded from
+ any URL scheme supported by Qt.
+
+ The URL may be absolute, or relative to the URL of the component.
+
+ The \c source property cannot be changed while the document's \l modified
+ state is \c true. If the user has modified the document contents, you
+ should prompt the user whether to \l save(), or else discard changes by
+ setting \c {modified = false} before setting the \l source property to a
+ different URL.
+
+ \sa QTextDocumentWriter::supportedDocumentFormats()
+*/
+QUrl QQuickTextDocument::source() const
{
Q_D(const QQuickTextDocument);
- return d->document.data();
+ return d->url;
+}
+
+void QQuickTextDocument::setSource(const QUrl &url)
+{
+ Q_D(QQuickTextDocument);
+
+ if (url == d->url)
+ return;
+
+ if (isModified()) {
+ qmlWarning(this) << "Existing document modified: you should save(),"
+ "or call TextEdit.clear() before setting a different source";
+ return;
+ }
+
+ d->url = url;
+ emit sourceChanged();
+ d->load();
+}
+
+/*!
+ \property QQuickTextDocument::modified
+ \brief whether the document has been modified by the user
+ \since 6.7
+ \preliminary
+
+ This property holds whether the document has been modified by the user
+ since the last time it was loaded or saved. By default, this property is
+ \c false.
+
+ As with \l QTextDocument::modified, you can set the modified property:
+ for example, set it to \c false to allow setting the \l source property
+ to a different URL (thus discarding the user's changes).
+
+ \sa QTextDocument::modified
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TextDocument::modified
+ \since 6.7
+ \preliminary
+
+ This property holds whether the document has been modified by the user
+ since the last time it was loaded or saved. By default, this property is
+ \c false.
+
+ As with \l QTextDocument::modified, you can set the modified property:
+ for example, set it to \c false to allow setting the \l source property
+ to a different URL (thus discarding the user's changes).
+
+ \sa QTextDocument::modified
+*/
+bool QQuickTextDocument::isModified() const
+{
+ const auto *doc = textDocument();
+ return doc && doc->isModified();
+}
+
+void QQuickTextDocument::setModified(bool modified)
+{
+ if (auto *doc = textDocument())
+ doc->setModified(modified);
+}
+
+void QQuickTextDocumentPrivate::load()
+{
+ auto *doc = editor->document();
+ if (!doc) {
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("Null document object: cannot load"));
+ return;
+ }
+ const QQmlContext *context = qmlContext(editor);
+ const QUrl &resolvedUrl = context ? context->resolvedUrl(url) : url;
+ const QString filePath = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
+ QFile file(filePath);
+ if (file.exists()) {
+#if QT_CONFIG(mimetype)
+ QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filePath);
+ const bool isHtml = mimeType.inherits("text/html"_L1);
+ const bool isMarkdown = mimeType.inherits("text/markdown"_L1);
+#else
+ const bool isHtml = filePath.endsWith(".html"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".htm"_L1, Qt::CaseInsensitive);
+ const bool isMarkdown = filePath.endsWith(".md"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".markdown"_L1, Qt::CaseInsensitive);
+#endif
+ if (isHtml)
+ detectedFormat = Qt::RichText;
+ else if (isMarkdown)
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+ if (file.open(QFile::ReadOnly | QFile::Text)) {
+ 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
+#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.
+ encoding = QStringConverter::encodingForHtml(data);
+ if (encoding) {
+ QStringDecoder decoder(*encoding);
+ doc->setHtml(decoder(data));
+ } else {
+ // fall back to utf8
+ doc->setHtml(QString::fromUtf8(data));
+ }
+ } else
+#endif
+ {
+ doc->setPlainText(QString::fromUtf8(data));
+ }
+ setStatus(QQuickTextDocument::Status::Loaded, {});
+ qCDebug(lcTextDoc) << editor << "loaded" << filePath
+ << "as" << editor->textFormat() << "detected" << detectedFormat
+#if QT_CONFIG(mimetype)
+ << "(file type" << mimeType << ')'
+#endif
+ ;
+ doc->setModified(false);
+ return;
+ }
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("Failed to read: %1").arg(file.errorString()));
+ } else {
+ setStatus(QQuickTextDocument::Status::ReadError,
+ QQuickTextDocument::tr("%1 does not exist").arg(filePath));
+ }
}
-QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
-: QTextDocument(parent), outstanding(0)
+void QQuickTextDocumentPrivate::writeTo(const QUrl &fileUrl)
{
- setUndoRedoEnabled(false);
- documentLayout()->registerHandler(QTextFormat::ImageObject, this);
- connect(this, &QTextDocument::baseUrlChanged, [this]() {
- clearResources();
- markContentsDirty(0, characterCount());
- });
+ auto *doc = editor->document();
+ if (!doc)
+ return;
+
+ const QString filePath = fileUrl.toLocalFile();
+ const bool sameUrl = fileUrl == url;
+ if (!sameUrl) {
+#if QT_CONFIG(mimetype)
+ const auto type = QMimeDatabase().mimeTypeForUrl(fileUrl);
+ if (type.inherits("text/html"_L1))
+ detectedFormat = Qt::RichText;
+ else if (type.inherits("text/markdown"_L1))
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+#else
+ if (filePath.endsWith(".html"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".htm"_L1, Qt::CaseInsensitive))
+ detectedFormat = Qt::RichText;
+ else if (filePath.endsWith(".md"_L1, Qt::CaseInsensitive) ||
+ filePath.endsWith(".markdown"_L1, Qt::CaseInsensitive))
+ detectedFormat = Qt::MarkdownText;
+ else
+ detectedFormat = Qt::PlainText;
+#endif
+ }
+ QFile file(filePath);
+ if (!file.open(QFile::WriteOnly | QFile::Truncate |
+ (detectedFormat == Qt::RichText ? QFile::NotOpen : QFile::Text))) {
+ setStatus(QQuickTextDocument::Status::WriteError,
+ QQuickTextDocument::tr("Cannot save: %1").arg(file.errorString()));
+ return;
+ }
+ setStatus(QQuickTextDocument::Status::Saving, {});
+ QByteArray raw;
+
+ switch (detectedFormat) {
+#if QT_CONFIG(textmarkdownwriter)
+ case Qt::MarkdownText:
+ raw = doc->toMarkdown().toUtf8();
+ break;
+#endif
+#if QT_CONFIG(texthtmlparser)
+ case Qt::RichText:
+ if (sameUrl && encoding) {
+ QStringEncoder enc(*encoding);
+ raw = enc.encode(doc->toHtml());
+ } else {
+ // default to UTF-8 unless the user is saving the same file as previously loaded
+ raw = doc->toHtml().toUtf8();
+ }
+ break;
+#endif
+ default:
+ raw = doc->toPlainText().toUtf8();
+ break;
+ }
+
+ file.write(raw);
+ file.close();
+ setStatus(QQuickTextDocument::Status::Saved, {});
+ doc->setModified(false);
}
-QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
+QTextDocument *QQuickTextDocumentPrivate::document() const
{
- if (!m_resources.isEmpty())
- qDeleteAll(m_resources);
+ return editor->document();
}
-QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
+void QQuickTextDocumentPrivate::setDocument(QTextDocument *doc)
{
- QVariant resource = QTextDocument::loadResource(type, name);
- if (resource.isNull() && type == QTextDocument::ImageResource) {
- QQmlContext *context = qmlContext(parent());
- QUrl url = baseUrl().resolved(name);
- QQuickPixmap *p = loadPixmap(context, url);
- resource = p->image();
+ Q_Q(QQuickTextDocument);
+ QTextDocument *oldDoc = editor->document();
+ if (doc == oldDoc)
+ return;
+
+ if (oldDoc)
+ oldDoc->disconnect(q);
+ if (doc) {
+ q->connect(doc, &QTextDocument::modificationChanged,
+ q, &QQuickTextDocument::modifiedChanged);
}
+ editor->setDocument(doc);
+ emit q->textDocumentChanged();
+}
+
+/*!
+ Returns a pointer to the QTextDocument object.
+*/
+QTextDocument *QQuickTextDocument::textDocument() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->document();
+}
+
+/*!
+ \brief Sets the given \a document.
+ \since 6.7
+
+ The caller retains ownership of the document.
+*/
+void QQuickTextDocument::setTextDocument(QTextDocument *document)
+{
+ d_func()->setDocument(document);
+}
+
+/*!
+ \fn void QQuickTextDocument::textDocumentChanged()
+ \since 6.7
+
+ This signal is emitted when the underlying QTextDocument is
+ replaced with a different instance.
+
+ \sa setTextDocument()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::sourceChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::modifiedChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::statusChanged()
+*/
+
+/*!
+ \preliminary
+ \fn void QQuickTextDocument::errorStringChanged()
+*/
+
+/*!
+ \fn void QQuickTextDocument::save()
+ \since 6.7
+ \preliminary
+
+ Saves the contents to the same file and format specified by \l source.
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, saveAs()
+*/
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::save()
+ \brief Saves the contents to the same file and format specified by \l source.
+ \since 6.7
+ \preliminary
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
- return resource;
+ \sa source, saveAs()
+*/
+void QQuickTextDocument::save()
+{
+ Q_D(QQuickTextDocument);
+ d->writeTo(d->url);
}
-void QQuickTextDocumentWithImageResources::requestFinished()
+/*!
+ \fn void QQuickTextDocument::saveAs(const QUrl &url)
+ \brief Saves the contents to the file and format specified by \a url.
+ \since 6.7
+ \preliminary
+
+ The file extension in \a url specifies the file format
+ (as determined by QMimeDatabase::mimeTypeForUrl()).
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, save()
+*/
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::saveAs(url url)
+ \brief Saves the contents to the file and format specified by \a url.
+ \since 6.7
+ \preliminary
+
+ The file extension in \a url specifies the file format
+ (as determined by QMimeDatabase::mimeTypeForUrl()).
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, save()
+*/
+void QQuickTextDocument::saveAs(const QUrl &url)
{
- outstanding--;
- if (outstanding == 0) {
- markContentsDirty(0, characterCount());
- emit imagesLoaded();
+ Q_D(QQuickTextDocument);
+ if (!url.isLocalFile()) {
+ d->setStatus(QQuickTextDocument::Status::NonLocalFileError,
+ QQuickTextDocument::tr("Can only save to local files"));
+ return;
}
+ d->writeTo(url);
+
+ if (url == d->url)
+ return;
+
+ d->url = url;
+ emit sourceChanged();
}
-QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
- QTextDocument *, int, const QTextFormat &format)
+QQuickTextImageHandler::QQuickTextImageHandler(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QSizeF QQuickTextImageHandler::intrinsicSize(
+ QTextDocument *doc, int, const QTextFormat &format)
{
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 = resource(QTextDocument::ImageResource, QUrl(imageFormat.name()));
+ QVariant res = doc->resource(QTextDocument::ImageResource, QUrl(imageFormat.name()));
QImage image = res.value<QImage>();
if (image.isNull()) {
+ // autotests expect us to reserve a 16x16 space for a "broken image" icon,
+ // even though we don't actually display one
if (!hasWidth)
size.setWidth(16);
if (!hasHeight)
@@ -122,12 +621,17 @@ QSizeF QQuickTextDocumentWithImageResources::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)
@@ -141,54 +645,6 @@ QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
return QSizeF();
}
-void QQuickTextDocumentWithImageResources::drawObject(
- QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
-{
-}
-
-QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format) const
-{
- QVariant res = resource(QTextDocument::ImageResource, QUrl(format.name()));
- return res.value<QImage>();
-}
-
-QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
- QQmlContext *context, const QUrl &url)
-{
-
- QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
-
- if (iter == m_resources.end()) {
- QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
- iter = m_resources.insert(url, p);
-
- if (p->isLoading()) {
- p->connectFinished(this, SLOT(requestFinished()));
- outstanding++;
- }
- }
-
- QQuickPixmap *p = *iter;
- if (p->isError()) {
- if (!errors.contains(url)) {
- errors.insert(url);
- qmlWarning(parent()) << p->error();
- }
- }
- return p;
-}
-
-void QQuickTextDocumentWithImageResources::clearResources()
-{
- for (QQuickPixmap *pixmap : std::as_const(m_resources))
- pixmap->clear(this);
- qDeleteAll(m_resources);
- m_resources.clear();
- outstanding = 0;
-}
-
-QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
-
QT_END_NAMESPACE
#include "moc_qquicktextdocument.cpp"
diff --git a/src/quick/items/qquicktextdocument.h b/src/quick/items/qquicktextdocument.h
index b73a76ec39..bccda205bd 100644
--- a/src/quick/items/qquicktextdocument.h
+++ b/src/quick/items/qquicktextdocument.h
@@ -13,12 +13,51 @@ class QQuickTextDocumentPrivate;
class Q_QUICK_EXPORT QQuickTextDocument : public QObject
{
Q_OBJECT
- QML_ANONYMOUS
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 7))
+ Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged REVISION(6, 7))
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION(6, 7))
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged REVISION(6, 7))
+
+ QML_NAMED_ELEMENT(TextDocument)
+ QML_UNCREATABLE("TextDocument is only available as a property of TextEdit or TextArea.")
QML_ADDED_IN_VERSION(2, 0)
public:
+ enum class Status : quint8 {
+ Null = 0,
+ Loading,
+ Loaded,
+ Saving,
+ Saved,
+ ReadError,
+ WriteError,
+ NonLocalFileError,
+ };
+ Q_ENUM(Status)
+
QQuickTextDocument(QQuickItem *parent);
+
+ QUrl source() const;
+ void setSource(const QUrl &url);
+
+ bool isModified() const;
+ void setModified(bool modified);
+
QTextDocument *textDocument() const;
+ void setTextDocument(QTextDocument *document);
+
+ Q_REVISION(6, 7) Q_INVOKABLE void save();
+ Q_REVISION(6, 7) Q_INVOKABLE void saveAs(const QUrl &url);
+
+ Status status() const;
+ QString errorString() const;
+
+Q_SIGNALS:
+ Q_REVISION(6,7) void textDocumentChanged();
+ Q_REVISION(6, 7) void sourceChanged();
+ Q_REVISION(6, 7) void modifiedChanged();
+ Q_REVISION(6, 7) void statusChanged();
+ Q_REVISION(6, 7) void errorStringChanged();
private:
Q_DISABLE_COPY(QQuickTextDocument)
@@ -27,6 +66,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextDocument)
-
#endif
diff --git a/src/quick/items/qquicktextdocument_p.h b/src/quick/items/qquicktextdocument_p.h
index d7404aee3e..3e5236a55c 100644
--- a/src/quick/items/qquicktextdocument_p.h
+++ b/src/quick/items/qquicktextdocument_p.h
@@ -15,56 +15,76 @@
// We mean it.
//
-#include <QtCore/qhash.h>
-#include <QtCore/qvariant.h>
-#include <QtGui/qimage.h>
-#include <QtGui/qtextdocument.h>
+#include "qquicktextdocument.h"
+
#include <QtGui/qabstracttextdocumentlayout.h>
-#include <QtGui/qtextlayout.h>
-#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextdocumentfragment.h>
+#include <QtGui/qtextformat.h>
+#include <QtCore/qrect.h>
+#include <QtCore/private/qobject_p_p.h>
+
+#if QT_CONFIG(mimetype)
+#include <QtCore/qmimedatabase.h>
+#endif
QT_BEGIN_NAMESPACE
-class QQuickItem;
class QQuickPixmap;
-class QQmlContext;
-
-class Q_AUTOTEST_EXPORT QQuickTextDocumentWithImageResources : public QTextDocument, public QTextObjectInterface
+class QQuickTextEdit;
+
+/*! \internal
+ QTextImageHandler would attempt to resolve relative paths, and load the
+ image itself if the document returns an invalid image from loadResource().
+ We replace it with this version instead, because Qt Quick's text resources
+ are resolved against the Text item's context, and because we override
+ intrinsicSize(). drawObject() is empty because we don't need to use this
+ handler to paint images: they get put into scene graph nodes instead.
+*/
+class QQuickTextImageHandler : public QObject, public QTextObjectInterface
{
Q_OBJECT
Q_INTERFACES(QTextObjectInterface)
public:
- QQuickTextDocumentWithImageResources(QQuickItem *parent);
- virtual ~QQuickTextDocumentWithImageResources();
-
- int resourcesLoading() const { return outstanding; }
-
+ QQuickTextImageHandler(QObject *parent = nullptr);
+ ~QQuickTextImageHandler() override = default;
QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
- void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) override;
-
- QImage image(const QTextImageFormat &format) const;
-
-public Q_SLOTS:
- void clearResources();
-
-Q_SIGNALS:
- void imagesLoaded();
-
-protected:
- QVariant loadResource(int type, const QUrl &name) override;
-
- QQuickPixmap *loadPixmap(QQmlContext *context, const QUrl &name);
-
-private Q_SLOTS:
- void requestFinished();
+ void drawObject(QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &) override { }
+};
-private:
- QHash<QUrl, QQuickPixmap *> m_resources;
+class QQuickTextDocumentPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTextDocument)
+public:
+ static QQuickTextDocumentPrivate *get(QQuickTextDocument *doc) { return doc->d_func(); }
+ static const QQuickTextDocumentPrivate *get(const QQuickTextDocument *doc) { return doc->d_func(); }
+
+ void load();
+ void writeTo(const QUrl &fileUrl);
+ QTextDocument *document() const;
+ void setDocument(QTextDocument *doc);
+ void setStatus(QQuickTextDocument::Status s, const QString &err);
+
+ // so far the QQuickItem given to the QQuickTextDocument ctor is always a QQuickTextEdit
+ QQuickTextEdit *editor = nullptr;
+ QUrl url;
+ QString errorString;
+ Qt::TextFormat detectedFormat = Qt::AutoText; // url's extension, independent of TextEdit.textFormat
+ std::optional<QStringConverter::Encoding> encoding; // only relevant for HTML (Qt::RichText)
+ QQuickTextDocument::Status status = QQuickTextDocument::Status::Null;
+};
- int outstanding;
- static QSet<QUrl> errors;
+namespace QtPrivate {
+class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
+{
+public:
+ inline QTextCharFormat formatAccessor(int pos)
+ {
+ return format(pos);
+ }
};
+} // namespace QtPrivate
QT_END_NAMESPACE
-#endif // QQUICKDOCUMENT_P_H
+#endif // QQUICKTEXTDOCUMENT_P_H
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 191a33167f..03e5c14ba1 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -5,9 +5,8 @@
#include "qquicktextedit_p_p.h"
#include "qquicktextcontrol_p.h"
#include "qquicktextdocument_p.h"
-#include "qquickevents_p_p.h"
#include "qquickwindow.h"
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#include "qquicktextnodeengine_p.h"
#include <QtCore/qmath.h>
@@ -23,6 +22,11 @@
#include <private/qqmlproperty_p.h>
#include <private/qtextengine_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
+
+#if QT_CONFIG(accessibility)
+#include <private/qquickaccessibleattached_p.h>
+#endif
#include "qquicktextdocument.h"
@@ -30,8 +34,9 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcVP)
-Q_LOGGING_CATEGORY(lcTextEdit, "qt.quick.textedit")
+Q_STATIC_LOGGING_CATEGORY(lcTextEdit, "qt.quick.textedit")
+
+using namespace Qt::StringLiterals;
/*!
\qmltype TextEdit
@@ -79,7 +84,7 @@ TextEdit {
You can translate between cursor positions (characters from the start of the document) and pixel
points using positionAt() and positionToRectangle().
- \sa Text, TextInput
+ \sa Text, TextInput, TextArea, {Qt Quick Controls - Text Editor}
*/
/*!
@@ -101,22 +106,13 @@ static const int nodeBreakingSize = 300;
const int QQuickTextEditPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD;
namespace {
- class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
- {
- public:
- inline QTextCharFormat formatAccessor(int pos)
- {
- return format(pos);
- }
- };
-
class RootNode : public QSGTransformNode
{
public:
RootNode() : cursorNode(nullptr), frameDecorationsNode(nullptr)
{ }
- void resetFrameDecorations(QQuickTextNode* newNode)
+ void resetFrameDecorations(QSGInternalTextNode* newNode)
{
if (frameDecorationsNode) {
removeChildNode(frameDecorationsNode);
@@ -139,7 +135,7 @@ namespace {
}
QSGInternalRectangleNode *cursorNode;
- QQuickTextNode* frameDecorationsNode;
+ QSGInternalTextNode* frameDecorationsNode;
};
}
@@ -151,6 +147,12 @@ QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
d->init();
}
+QQuickTextEdit::~QQuickTextEdit()
+{
+ Q_D(QQuickTextEdit);
+ qDeleteAll(d->pixmapsInProgress);
+}
+
QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
: QQuickImplicitSizeItem(dd, parent)
{
@@ -211,17 +213,16 @@ QString QQuickTextEdit::text() const
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
TextEdit { text: "Hello"; font.weight: Font.DemiBold }
@@ -286,13 +287,12 @@ QString QQuickTextEdit::text() const
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -309,23 +309,21 @@ QString QQuickTextEdit::text() const
\note This property only has an effect when used together with render type TextEdit.NativeRendering.
- \list
- \li Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \li Font.PreferNoHinting - If possible, render text without hinting the outlines
- of the glyphs. The text layout will be typographically accurate, using the same metrics
- as are used e.g. when printing.
- \li Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
- but align glyphs to the pixel grid in the vertical direction. The text will appear
- crisper on displays where the density is too low to give an accurate rendering
- of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
- layout will be scalable to higher density devices (such as printers) without impacting
- details such as line breaks.
- \li Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
- vertical directions. The text will be altered to optimize legibility on the target
- device, but since the metrics will depend on the target size of the text, the positions
- of glyphs, line breaks, and other typographical detail will not scale, meaning that a
- text layout may look different on devices with different pixel densities.
- \endlist
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
+ of the glyphs. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
\qml
TextEdit { text: "Hello"; renderType: TextEdit.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -363,6 +361,34 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty object QtQuick::TextEdit::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextEdit::font.features
+ \since 6.6
+
+ \include qquicktext.cpp qml-font-features
+*/
+
+/*!
+ \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
@@ -376,7 +402,20 @@ QString QQuickTextEdit::text() const
remarkably better performance for modifying especially large rich text
content.
- \sa clear(), textFormat
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property.
+
+ If you used \l TextDocument::source to load text, you can retrieve the
+ loaded text from this property. In that case, you can then change
+ \l textFormat to do format conversions that will change the value of the
+ \c text property. For example, if \c textFormat is \c RichText or
+ \c AutoText and you load an HTML file, then set \c textFormat to
+ \c MarkdownText afterwards, the \c text property will contain the
+ conversion from HTML to Markdown.
+
+ \sa clear(), preeditText, textFormat
*/
void QQuickTextEdit::setText(const QString &text)
{
@@ -384,7 +423,6 @@ void QQuickTextEdit::setText(const QString &text)
if (QQuickTextEdit::text() == text)
return;
- d->document->clearResources();
d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
d->markdownText = d->format == MarkdownText;
if (!isComponentComplete()) {
@@ -426,6 +464,11 @@ void QQuickTextEdit::q_invalidate()
\since 5.7
This property contains partial text input from an input method.
+
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa inputMethodHints
*/
QString QQuickTextEdit::preeditText() const
{
@@ -442,6 +485,7 @@ QString QQuickTextEdit::preeditText() const
\value TextEdit.PlainText (default) all styling tags are treated as plain text
\value TextEdit.AutoText detected via the Qt::mightBeRichText() heuristic
+ or the file format of \l TextDocument::source
\value TextEdit.RichText \l {Supported HTML Subset} {a subset of HTML 4}
\value TextEdit.MarkdownText \l {https://commonmark.org/help/}{CommonMark} plus the
\l {https://guides.github.com/features/mastering-markdown/}{GitHub}
@@ -449,9 +493,12 @@ QString QQuickTextEdit::preeditText() const
The default is \c TextEdit.PlainText. If the text format is set to
\c TextEdit.AutoText, the text edit will automatically determine whether
- the text should be treated as rich text. This determination is made using
- Qt::mightBeRichText(), which can detect the presence of an HTML tag on the
- first line of text, but cannot distinguish Markdown from plain text.
+ the text should be treated as rich text. If the \l text property is set,
+ this determination is made using Qt::mightBeRichText(), which can detect
+ the presence of an HTML tag on the first line of text, but cannot
+ distinguish Markdown from plain text. If the \l TextDocument::source
+ property is set, this determination is made from the
+ \l {QMimeDatabase::mimeTypeForFile()}{mime type of the file}.
\table
\row
@@ -464,14 +511,23 @@ QString QQuickTextEdit::preeditText() const
\l {https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown}{GitHub checkbox extension}
are interactively checkable.
- \note Interactively typing markup or markdown formatting is not supported.
+ If the \l TextDocument::source property is set, changing the \c textFormat
+ property after loading has the effect of converting from the detected
+ format to the requested format. For example, you can convert between HTML
+ and Markdown. However if either of those "rich" formats is loaded and then
+ you set \c textFormat to \c PlainText, the TextEdit will show the raw
+ markup. Thus, suitable bindings (e.g. to a checkable Control) can enable
+ the user to toggle back and forth between "raw" and WYSIWYG editing.
+
+ \note Interactively typing markup or markdown formatting in WYSIWYG mode
+ is not supported; but you can switch to \c PlainText, make changes, then
+ switch back to the appropriate \c textFormat.
\note With \c Text.MarkdownText, and with the supported subset of HTML,
some decorative elements are not rendered as they would be in a web browser:
\list
\li code blocks use the \l {QFontDatabase::FixedFont}{default monospace font} but without a surrounding highlight box
\li block quotes are indented, but there is no vertical line alongside the quote
- \li horizontal rules are not rendered
\endlist
*/
QQuickTextEdit::TextFormat QQuickTextEdit::textFormat() const
@@ -486,35 +542,135 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
if (format == d->format)
return;
- const bool wasRich = d->richText;
- const bool wasMarkdown = d->markdownText;
- d->richText = format == RichText || (format == AutoText && (wasRich || Qt::mightBeRichText(text())));
- d->markdownText = format == MarkdownText;
+ auto mightBeRichText = [this]() {
+ return Qt::mightBeRichText(text());
+ };
+
+ auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
+ if (d->format == PlainText)
+ return PlainText;
+ if (d->richText) return RichText;
+ if (d->markdownText) return MarkdownText;
+ if (detectedFormat == Qt::AutoText && mightBeRichText())
+ return RichText;
+ return PlainText;
+ };
+
+ auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
+ if (format == AutoText) {
+ if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
+ return MarkdownText;
+ if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
+ return RichText;
+ return PlainText; // fallback
+ }
+ return format;
+ };
+
+ bool textCachedChanged = false;
+ bool converted = false;
if (isComponentComplete()) {
-#if QT_CONFIG(texthtmlparser)
- if (wasRich && !d->richText && !d->markdownText) {
- d->control->setPlainText(!d->textCached ? d->control->toHtml() : d->text);
- updateSize();
- } else if (!wasRich && d->richText) {
- d->control->setHtml(!d->textCached ? d->control->toPlainText() : d->text);
- updateSize();
+ Qt::TextFormat detectedFormat = Qt::AutoText; // default if we don't know
+ if (d->quickDocument) {
+ // If QQuickTextDocument is in use, content can be loaded from a file,
+ // and then mime type detection overrides mightBeRichText().
+ detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
+ }
+
+ const TextFormat sourceFormat = findSourceFormat(detectedFormat);
+ const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
+
+ d->richText = destinationFormat == RichText;
+ d->markdownText = destinationFormat == MarkdownText;
+
+ // If converting between markdown and HTML, avoid using cached text: have QTD re-generate it
+ if (format != PlainText && (sourceFormat != destinationFormat)) {
+ d->textCached = false;
+ textCachedChanged = true;
}
+
+ switch (destinationFormat) {
+ case PlainText:
+#if QT_CONFIG(texthtmlparser)
+ if (sourceFormat == RichText) {
+ // If rich or unknown text was loaded and now the user wants plain text, get the raw HTML.
+ // But if we didn't set textCached to false above, assume d->text already contains HTML.
+ // This will allow the user to see the actual HTML they loaded (rather than Qt regenerating crufty HTML).
+ d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
+ converted = true;
+ }
#endif
#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
- if (wasMarkdown && !d->markdownText && !d->richText) {
- d->control->setPlainText(!d->textCached ? d->control->toMarkdown() : d->text);
- updateSize();
- } else if (!wasMarkdown && d->markdownText) {
- d->control->setMarkdownText(!d->textCached ? d->control->toPlainText() : d->text);
- updateSize();
- }
+ if (sourceFormat == MarkdownText) {
+ // If markdown or unknown text was loaded and now the user wants plain text, get the raw Markdown.
+ // But if we didn't set textCached to false above, assume d->text already contains markdown.
+ // This will allow the user to see the actual markdown they loaded.
+ d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
+ converted = true;
+ }
+#endif
+ break;
+ case RichText:
+#if QT_CONFIG(texthtmlparser)
+ switch (sourceFormat) {
+ case MarkdownText:
+ // If markdown was loaded and now the user wants HTML, convert markdown to HTML.
+ d->control->setHtml(d->control->toHtml());
+ converted = true;
+ break;
+ case PlainText:
+ // If plain text was loaded and now the user wants HTML, interpret plain text as HTML.
+ // But if we didn't set textCached to false above, assume d->text already contains HTML.
+ d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
+ converted = true;
+ break;
+ case AutoText:
+ case RichText: // nothing to do
+ break;
+ }
+#endif
+ break;
+ case MarkdownText:
+#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
+ switch (sourceFormat) {
+ case RichText:
+ // If HTML was loaded and now the user wants markdown, convert HTML to markdown.
+ d->control->setMarkdownText(d->control->toMarkdown());
+ converted = true;
+ break;
+ case PlainText:
+ // If plain text was loaded and now the user wants markdown, interpret plain text as markdown.
+ // But if we didn't set textCached to false above, assume d->text already contains markdown.
+ d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
+ converted = true;
+ break;
+ case AutoText:
+ case MarkdownText: // nothing to do
+ break;
+ }
#endif
+ break;
+ case AutoText: // nothing to do
+ break;
+ }
+
+ if (converted)
+ updateSize();
+ } else {
+ d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
+ d->markdownText = format == MarkdownText;
}
+ qCDebug(lcTextEdit) << d->format << "->" << format
+ << "rich?" << d->richText << "md?" << d->markdownText
+ << "converted?" << converted << "cache invalidated?" << textCachedChanged;
+
d->format = format;
d->control->setAcceptRichText(d->format != PlainText);
emit textFormatChanged(d->format);
+ if (textCachedChanged)
+ emit textChanged();
}
/*!
@@ -523,16 +679,23 @@ void QQuickTextEdit::setTextFormat(TextFormat format)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value TextEdit.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value TextEdit.NativeRendering Text is rendered using a platform-specific technique.
+ \value TextEdit.CurveRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c TextEdit.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c TextEdit.QtRendering and \c TextEdit.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickTextEdit::RenderType QQuickTextEdit::renderType() const
@@ -678,19 +841,21 @@ void QQuickTextEdit::setSelectedTextColor(const QColor &color)
the left.
Valid values for \c horizontalAlignment are:
- \list
- \li TextEdit.AlignLeft (default)
- \li TextEdit.AlignRight
- \li TextEdit.AlignHCenter
- \li TextEdit.AlignJustify
- \endlist
+
+ \value TextEdit.AlignLeft
+ left alignment with ragged edges on the right (default)
+ \value TextEdit.AlignRight
+ align each line to the right with ragged edges on the left
+ \value TextEdit.AlignHCenter
+ align each line to the center
+ \value TextEdit.AlignJustify
+ align each line to both right and left, spreading out words as necessary
Valid values for \c verticalAlignment are:
- \list
- \li TextEdit.AlignTop (default)
- \li TextEdit.AlignBottom
- \li TextEdit.AlignVCenter
- \endlist
+
+ \value TextEdit.AlignTop start at the top of the item (default)
+ \value TextEdit.AlignBottom align the last line to the bottom and other lines above
+ \value TextEdit.AlignVCenter align the center vertically
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
@@ -706,11 +871,11 @@ QQuickTextEdit::HAlignment QQuickTextEdit::hAlign() const
void QQuickTextEdit::setHAlign(HAlignment align)
{
Q_D(QQuickTextEdit);
- bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
- d->hAlignImplicit = false;
- if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+
+ if (d->setHAlign(align, true) && isComponentComplete()) {
d->updateDefaultTextOption();
updateSize();
+ updateWholeDocument();
}
}
@@ -743,21 +908,34 @@ QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign() const
return effectiveAlignment;
}
-bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment alignment, bool forceAlign)
+bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align, bool forceAlign)
{
Q_Q(QQuickTextEdit);
- if (hAlign != alignment || forceAlign) {
- QQuickTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
- hAlign = alignment;
- emit q->horizontalAlignmentChanged(alignment);
- if (oldEffectiveHAlign != q->effectiveHAlign())
- emit q->effectiveHorizontalAlignmentChanged();
+ if (hAlign == align && !forceAlign)
+ return false;
+
+ const bool wasImplicit = hAlignImplicit;
+ const auto oldEffectiveHAlign = q->effectiveHAlign();
+
+ hAlignImplicit = !forceAlign;
+ if (hAlign != align) {
+ hAlign = align;
+ emit q->horizontalAlignmentChanged(align);
+ }
+
+ if (q->effectiveHAlign() != oldEffectiveHAlign) {
+ emit q->effectiveHorizontalAlignmentChanged();
return true;
}
+
+ if (forceAlign && wasImplicit) {
+ // QTBUG-120052 - when horizontal text alignment is set explicitly,
+ // we need notify any other controls that may depend on it, like QQuickPlaceholderText
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
return false;
}
-
Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) const
{
const QChar *character = text.constData();
@@ -780,20 +958,24 @@ Qt::LayoutDirection QQuickTextEditPrivate::textDirection(const QString &text) co
bool QQuickTextEditPrivate::determineHorizontalAlignment()
{
Q_Q(QQuickTextEdit);
- if (hAlignImplicit && q->isComponentComplete()) {
- Qt::LayoutDirection direction = contentDirection;
+ if (!hAlignImplicit || !q->isComponentComplete())
+ return false;
+
+ Qt::LayoutDirection direction = contentDirection;
#if QT_CONFIG(im)
- if (direction == Qt::LayoutDirectionAuto) {
- const QString preeditText = control->textCursor().block().layout()->preeditAreaText();
- direction = textDirection(preeditText);
- }
- if (direction == Qt::LayoutDirectionAuto)
- direction = qGuiApp->inputMethod()->inputDirection();
+ if (direction == Qt::LayoutDirectionAuto) {
+ QTextBlock block = control->textCursor().block();
+ if (!block.layout())
+ return false;
+ direction = textDirection(block.layout()->preeditAreaText());
+ }
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = qGuiApp->inputMethod()->inputDirection();
#endif
- return setHAlign(direction == Qt::RightToLeft ? QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft);
- }
- return false;
+ const auto implicitHAlign = direction == Qt::RightToLeft ?
+ QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
+ return setHAlign(implicitHAlign);
}
void QQuickTextEditPrivate::mirrorChange()
@@ -840,6 +1022,26 @@ Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const
}
#endif
+#if QT_CONFIG(accessibility)
+void QQuickTextEditPrivate::accessibilityActiveChanged(bool active)
+{
+ if (!active)
+ return;
+
+ Q_Q(QQuickTextEdit);
+ if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
+ qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true))) {
+ accessibleAttached->setRole(effectiveAccessibleRole());
+ accessibleAttached->set_readOnly(q->isReadOnly());
+ }
+}
+
+QAccessible::Role QQuickTextEditPrivate::accessibleRole() const
+{
+ return QAccessible::EditableText;
+}
+#endif
+
void QQuickTextEditPrivate::setTopPadding(qreal value, bool reset)
{
Q_Q(QQuickTextEdit);
@@ -930,20 +1132,26 @@ void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
moveCursorDelegate();
emit verticalAlignmentChanged(d->vAlign);
}
+
/*!
\qmlproperty enumeration QtQuick::TextEdit::wrapMode
Set this property to wrap the text to the TextEdit item's width.
The text will only wrap if an explicit width has been set.
- \list
- \li TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
- \li TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
- \li TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value TextEdit.NoWrap
+ (default) no wrapping will be performed. If the text contains insufficient newlines,
+ \l {Item::}{implicitWidth} will exceed a set width.
+ \value TextEdit.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l {Item::}{implicitWidth} will exceed a set width.
+ \value TextEdit.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value TextEdit.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate
+ point on the line, even in the middle of a word.
- The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
+ The default is \c TextEdit.NoWrap. If you set a width, consider using \c TextEdit.Wrap.
*/
QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode() const
{
@@ -1107,6 +1315,24 @@ int QQuickTextEdit::positionAt(qreal x, qreal y) const
}
/*!
+ \qmlproperty QtQuick::TextSelection QtQuick::TextEdit::cursorSelection
+ \since 6.7
+ \preliminary
+
+ This property is an object that provides properties of the text that is
+ currently selected, if any, alongside the text cursor.
+
+ \sa selectedText, selectionStart, selectionEnd
+*/
+QQuickTextSelection *QQuickTextEdit::cursorSelection() const
+{
+ Q_D(const QQuickTextEdit);
+ if (!d->cursorSelection)
+ d->cursorSelection = new QQuickTextSelection(const_cast<QQuickTextEdit *>(this));
+ return d->cursorSelection;
+}
+
+/*!
\qmlmethod QtQuick::TextEdit::moveCursorSelection(int position, SelectionMode mode)
Moves the cursor to \a position and updates the selection according to the optional \a mode
@@ -1120,13 +1346,12 @@ int QQuickTextEdit::positionAt(qreal x, qreal y) const
The selection mode specifies whether the selection is updated on a per character or a per word
basis. If not specified the selection mode will default to \c {TextEdit.SelectCharacters}.
- \list
- \li TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
- the previous cursor position) to the specified position.
- \li TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
- words between the specified position and the previous cursor position. Words partially in the
- range are included.
- \endlist
+ \value TextEdit.SelectCharacters
+ Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
+ to the specified position.
+ \value TextEdit.SelectWords
+ Sets the selectionStart and selectionEnd to include all words between the specified position
+ and the previous cursor position. Words partially in the range are included.
For example, take this sequence of calls:
@@ -1424,38 +1649,31 @@ void QQuickTextEdit::setTextMargin(qreal margin)
Flags that alter behaviour are:
- \list
- \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
- \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
- in any persistent storage like predictive user dictionary.
- \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
- when a sentence ends.
- \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
- \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
- \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
- \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
-
- \li Qt.ImhDate - The text editor functions as a date field.
- \li Qt.ImhTime - The text editor functions as a time field.
- \endlist
+ \value Qt.ImhHiddenText Characters should be hidden, as is typically used when entering passwords.
+ \value Qt.ImhSensitiveData Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+ \value Qt.ImhNoAutoUppercase The input method should not try to automatically switch to
+ upper case when a sentence ends.
+ \value Qt.ImhPreferNumbers Numbers are preferred (but not required).
+ \value Qt.ImhPreferUppercase Upper case letters are preferred (but not required).
+ \value Qt.ImhPreferLowercase Lower case letters are preferred (but not required).
+ \value Qt.ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing.
+ \value Qt.ImhDate The text editor functions as a date field.
+ \value Qt.ImhTime The text editor functions as a time field.
Flags that restrict input (exclusive flags) are:
- \list
- \li Qt.ImhDigitsOnly - Only digits are allowed.
- \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
- \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
- \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
- \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
- \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
- \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
- \endlist
+ \value Qt.ImhDigitsOnly Only digits are allowed.
+ \value Qt.ImhFormattedNumbersOnly Only number input is allowed. This includes decimal point and minus sign.
+ \value Qt.ImhUppercaseOnly Only upper case letter input is allowed.
+ \value Qt.ImhLowercaseOnly Only lower case letter input is allowed.
+ \value Qt.ImhDialableCharactersOnly Only characters suitable for phone dialing are allowed.
+ \value Qt.ImhEmailCharactersOnly Only characters suitable for email addresses are allowed.
+ \value Qt.ImhUrlCharactersOnly Only characters suitable for URLs are allowed.
Masks:
- \list
- \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
- \endlist
+ \value Qt.ImhExclusiveInputMask This mask yields nonzero if any of the exclusive flags are used.
*/
Qt::InputMethodHints QQuickTextEdit::inputMethodHints() const
@@ -1487,14 +1705,35 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
void QQuickTextEdit::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickTextEdit);
- if (!d->inLayout && ((newGeometry.width() != oldGeometry.width() && widthValid())
- || (newGeometry.height() != oldGeometry.height() && heightValid()))) {
+ if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
+ || (newGeometry.height() != oldGeometry.height()))) {
updateSize();
updateWholeDocument();
- moveCursorDelegate();
+ if (widthValid() || heightValid())
+ moveCursorDelegate();
}
QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
+}
+void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickTextEdit);
+ Q_UNUSED(value);
+ switch (change) {
+ 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.
+ updateSize();
+ updateWholeDocument();
+ }
+ break;
+
+ default:
+ break;
+ }
+ QQuickImplicitSizeItem::itemChange(change, value);
}
/*!
@@ -1509,20 +1748,17 @@ void QQuickTextEdit::componentComplete()
const QUrl url = baseUrl();
const QQmlContext *context = qmlContext(this);
d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
+ if (!d->text.isEmpty()) {
#if QT_CONFIG(texthtmlparser)
- if (d->richText)
- d->control->setHtml(d->text);
- else
+ if (d->richText)
+ d->control->setHtml(d->text);
+ else
#endif
#if QT_CONFIG(textmarkdownreader)
- if (d->markdownText)
- d->control->setMarkdownText(d->text);
- else
-#endif
- if (!d->text.isEmpty()) {
if (d->markdownText)
d->control->setMarkdownText(d->text);
else
+#endif
d->control->setPlainText(d->text);
}
@@ -1535,6 +1771,17 @@ void QQuickTextEdit::componentComplete()
if (d->cursorComponent && isCursorVisible())
QQuickTextUtil::createCursor(d);
polish();
+
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive())
+ d->accessibilityActiveChanged(true);
+#endif
+}
+
+int QQuickTextEdit::resourcesLoading() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->pixmapsInProgress.size();
}
/*!
@@ -1627,10 +1874,8 @@ void QQuickTextEdit::setSelectByMouse(bool on)
Specifies how text should be selected using a mouse.
- \list
- \li TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
- \li TextEdit.SelectWords - The selection is updated with whole words.
- \endlist
+ \value TextEdit.SelectCharacters (default) The selection is updated with individual characters.
+ \value TextEdit.SelectWords The selection is updated with whole words.
This property only applies when \l selectByMouse is true.
*/
@@ -1694,6 +1939,13 @@ void QQuickTextEdit::setReadOnly(bool r)
} else if (hasActiveFocus()) {
setCursorVisible(true);
}
+
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(this))
+ accessibleAttached->set_readOnly(r);
+ }
+#endif
}
bool QQuickTextEdit::isReadOnly() const
@@ -2055,6 +2307,87 @@ void QQuickTextEdit::triggerPreprocess()
update();
}
+/*! \internal
+ QTextDocument::loadResource() calls this to load inline images etc.
+ But if it's a local file, don't do it: let QTextDocument::loadResource()
+ load it in the default way. QQuickPixmap is for QtQuick-specific uses.
+*/
+QVariant QQuickTextEdit::loadResource(int type, const QUrl &source)
+{
+ Q_D(QQuickTextEdit);
+ const QUrl url = d->document->baseUrl().resolved(source);
+ if (url.isLocalFile()) {
+ // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
+ QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
+ if (!fi.exists())
+ qmlWarning(this) << "Cannot open: " << url.toString();
+ // let QTextDocument::loadResource() handle local file loading
+ return {};
+ }
+
+ // see if we already started a load job
+ auto existingJobIter = std::find_if(
+ d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
+ [&url](const auto *job) { return job->url() == url; } );
+ if (existingJobIter != d->pixmapsInProgress.cend()) {
+ const QQuickPixmap *job = *existingJobIter;
+ if (job->isError()) {
+ qmlWarning(this) << job->error();
+ d->pixmapsInProgress.erase(existingJobIter);
+ delete job;
+ return QImage();
+ } else {
+ qCDebug(lcTextEdit) << "already downloading" << url;
+ // existing job: return a null variant if it's not done yet
+ return job->isReady() ? job->image() : QVariant();
+ }
+ }
+
+ // not found: start a new load job
+ qCDebug(lcTextEdit) << "loading" << source << "resolved" << url
+ << "type" << static_cast<QTextDocument::ResourceType>(type);
+ QQmlContext *context = qmlContext(this);
+ Q_ASSERT(context);
+ // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
+ QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
+ p->connectFinished(this, SLOT(resourceRequestFinished()));
+ d->pixmapsInProgress.append(p);
+ // the new job is probably not done; return a null variant if the caller should poll again
+ return p->isReady() ? p->image() : QVariant();
+}
+
+/*! \internal
+ Handle completion of a download that QQuickTextEdit::loadResource() started.
+*/
+void QQuickTextEdit::resourceRequestFinished()
+{
+ Q_D(QQuickTextEdit);
+ for (auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
+ auto *job = *it;
+ if (job->isError()) {
+ // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, to return the placeholder
+ qCDebug(lcTextEdit) << "failed to load (error)" << job->url();
+ d->document->resource(QTextDocument::ImageResource, job->url());
+ // that will call QQuickTextEdit::loadResource() which will delete the job;
+ // so leave it in pixmapsInProgress for now, and stop this loop
+ break;
+ } else if (job->isReady()) {
+ // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, and cache the result
+ auto res = d->document->resource(QTextDocument::ImageResource, job->url());
+ // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
+ qCDebug(lcTextEdit) << (res.isValid() ? "done downloading" : "failed to load") << job->url() << job->rect();
+ d->pixmapsInProgress.erase(it);
+ delete job;
+ break;
+ }
+ }
+ if (d->pixmapsInProgress.isEmpty()) {
+ invalidate();
+ updateSize();
+ q_invalidate();
+ }
+}
+
typedef QQuickTextEditPrivate::Node TextNode;
using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
@@ -2063,7 +2396,7 @@ static inline bool operator<(const TextNode &n1, const TextNode &n2)
return n1.startPos() < n2.startPos();
}
-static inline void updateNodeTransform(QQuickTextNode* node, const QPointF &topLeft)
+static inline void updateNodeTransform(QSGInternalTextNode *node, const QPointF &topLeft)
{
QMatrix4x4 transformMatrix;
transformMatrix.translate(topLeft.x(), topLeft.y());
@@ -2089,6 +2422,23 @@ void QQuickTextEdit::invalidateFontCaches()
}
}
+QTextDocument *QQuickTextEdit::document() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->document;
+}
+
+void QQuickTextEdit::setDocument(QTextDocument *doc)
+{
+ Q_D(QQuickTextEdit);
+ if (d->ownsDocument)
+ delete d->document;
+ d->document = doc;
+ d->ownsDocument = false;
+ d->control->setDocument(doc);
+ q_textChanged();
+}
+
inline void resetEngine(QQuickTextNodeEngine *engine, const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor)
{
*engine = QQuickTextNodeEngine();
@@ -2114,7 +2464,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
delete oldNode;
oldNode = nullptr;
- // If we had any QQuickTextNode node references, they were deleted along with the root node
+ // If we had any QSGInternalTextNode node references, they were deleted along with the root node
// But here we must delete the Node structures in textNodeMap
d->textNodeMap.clear();
}
@@ -2123,6 +2473,10 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
RootNode *rootNode = static_cast<RootNode *>(oldNode);
TextNodeIterator nodeIterator = d->textNodeMap.begin();
+ std::optional<int> firstPosAcrossAllNodes;
+ if (nodeIterator != d->textNodeMap.end())
+ firstPosAcrossAllNodes = nodeIterator->startPos();
+
while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
++nodeIterator;
@@ -2139,7 +2493,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
firstDirtyPos = nodeIterator->startPos();
// ### this could be optimized if the first and last dirty nodes are not connected
// as the intermediate text nodes would usually only need to be transformed differently.
- QQuickTextNode *firstCleanNode = nullptr;
+ QSGInternalTextNode *firstCleanNode = nullptr;
auto it = d->textNodeMap.constEnd();
while (it != nodeIterator) {
--it;
@@ -2165,7 +2519,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
rootNode->resetFrameDecorations(d->createTextNode());
resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor);
- QQuickTextNode *node = nullptr;
+ QSGInternalTextNode *node = nullptr;
int currentNodeSize = 0;
int nodeStart = firstDirtyPos;
@@ -2184,14 +2538,17 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
d->firstBlockInViewport = -1;
d->firstBlockPastViewport = -1;
+ int frameCount = -1;
while (!frames.isEmpty()) {
QTextFrame *textFrame = frames.takeFirst();
+ ++frameCount;
+ if (frameCount > 0)
+ firstDirtyPos = 0;
+ qCDebug(lcVP) << "frame" << frameCount << textFrame
+ << "from" << positionToRectangle(textFrame->firstPosition()).topLeft()
+ << "to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
frames.append(textFrame->childFrames());
frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
-
- if (textFrame->lastPosition() < firstDirtyPos
- || textFrame->firstPosition() >= firstCleanNode.startPos())
- continue;
resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
if (textFrame->firstPosition() > textFrame->lastPosition()
@@ -2199,7 +2556,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
node = d->createTextNode();
updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
const int pos = textFrame->firstPosition() - 1;
- ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(d->document->documentLayout());
+ auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
QTextCharFormat format = a->formatAccessor(pos);
QTextBlock block = textFrame->firstCursorPosition().block();
nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
@@ -2241,7 +2598,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
inView = coveredRegion.bottom() > viewport.top();
}
- if (d->firstBlockInViewport < 0 && inView) {
+ const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
+ if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
// During backward scrolling, we need to iterate backwards from textNodeMap.begin() to fill the top of the viewport.
if (coveredRegion.top() > viewport.top() + 1) {
qCDebug(lcVP) << "checking backwards from block" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
@@ -2261,7 +2619,6 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
}
qCDebug(lcVP) << "first block in viewport" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
- d->firstBlockInViewport = block.blockNumber();
if (block.layout())
d->renderedRegion = coveredRegion;
} else {
@@ -2274,9 +2631,16 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
break; // skip rest of blocks in this frame
}
- if (inView && !block.text().isEmpty() && coveredRegion.isValid())
+ if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
d->renderedRegion = d->renderedRegion.united(coveredRegion);
+ // In case we're going to visit more (nested) frames after this, ensure that we
+ // don't omit any blocks that fit within the region that we claim as fully rendered.
+ if (!frames.isEmpty())
+ viewport = viewport.united(d->renderedRegion);
+ }
}
+ if (inView && d->firstBlockInViewport < 0)
+ d->firstBlockInViewport = block.blockNumber();
}
bool createdNodeInView = false;
@@ -2420,12 +2784,7 @@ bool QQuickTextEdit::isInputMethodComposing() const
}
QQuickTextEditPrivate::ExtraData::ExtraData()
- : padding(0)
- , topPadding(0)
- , leftPadding(0)
- , rightPadding(0)
- , bottomPadding(0)
- , explicitTopPadding(false)
+ : explicitTopPadding(false)
, explicitLeftPadding(false)
, explicitRightPadding(false)
, explicitBottomPadding(false)
@@ -2451,7 +2810,10 @@ void QQuickTextEditPrivate::init()
q->setAcceptHoverEvents(true);
- document = new QQuickTextDocumentWithImageResources(q);
+ document = new QTextDocument(q);
+ ownsDocument = true;
+ auto *imageHandler = new QQuickTextImageHandler(document);
+ document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
control = new QQuickTextControl(document, q);
control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
@@ -2472,10 +2834,9 @@ void QQuickTextEditPrivate::init()
#if QT_CONFIG(clipboard)
qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
#endif
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
- qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()), q, QQuickTextEdit, SLOT(updateSize()));
- QObject::connect(document, &QQuickTextDocumentWithImageResources::contentsChange, q, &QQuickTextEdit::q_contentsChange);
+ qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
+ qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
+ QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
@@ -2485,10 +2846,12 @@ void QQuickTextEditPrivate::init()
document->setUndoRedoEnabled(false); // flush undo buffer.
document->setUndoRedoEnabled(true);
updateDefaultTextOption();
+ document->setModified(false); // we merely changed some defaults: no edits worth saving yet
q->updateSize();
#if QT_CONFIG(cursor)
updateMouseCursorShape();
#endif
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickTextEditPrivate::resetInputMethod()
@@ -2669,9 +3032,6 @@ void QQuickTextEdit::updateSize()
return;
}
- qreal naturalWidth = d->implicitWidth - leftPadding() - rightPadding();
-
- qreal newWidth = d->document->idealWidth();
// ### assumes that if the width is set, the text will fill to edges
// ### (unless wrap is false, then clipping will occur)
if (widthValid()) {
@@ -2683,8 +3043,7 @@ void QQuickTextEdit::updateSize()
}
if (d->requireImplicitWidth) {
d->document->setTextWidth(-1);
- naturalWidth = d->document->idealWidth();
-
+ const qreal naturalWidth = d->document->idealWidth();
const bool wasInLayout = d->inLayout;
d->inLayout = true;
if (d->isImplicitResizeEnabled())
@@ -2694,19 +3053,22 @@ void QQuickTextEdit::updateSize()
return; // get this far we'll get a warning to that effect.
}
const qreal newTextWidth = width() - leftPadding() - rightPadding();
- if (d->document->textWidth() != newTextWidth) {
+ if (d->document->textWidth() != newTextWidth)
+ d->document->setTextWidth(newTextWidth);
+ } else if (d->wrapMode == NoWrap) {
+ // normally, if explicit width is not set, we should call setTextWidth(-1) here,
+ // as we don't need to fit the text to any fixed width. But because of some bug
+ // in QTextDocument it also breaks RTL text alignment, so we use "idealWidth" instead.
+ const qreal newTextWidth = d->document->idealWidth();
+ if (d->document->textWidth() != newTextWidth)
d->document->setTextWidth(newTextWidth);
- newWidth = d->document->idealWidth();
- }
- //### need to confirm cost of always setting these
- } else if (d->wrapMode == NoWrap && d->document->textWidth() != newWidth) {
- d->document->setTextWidth(newWidth); // ### Text does not align if width is not set or the idealWidth exceeds the textWidth (QTextDoc bug)
} else {
d->document->setTextWidth(-1);
}
QFontMetricsF fm(d->font);
- qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
+ const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
+ const qreal newWidth = d->document->idealWidth();
if (d->isImplicitResizeEnabled()) {
// ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
@@ -2718,12 +3080,26 @@ void QQuickTextEdit::updateSize()
d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
- setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
+
+ qreal baseline = fm.ascent();
+ QTextBlock firstBlock = d->document->firstBlock();
+ if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0) {
+ QTextLine firstLine = firstBlock.layout()->lineAt(0);
+ if (firstLine.isValid())
+ baseline = firstLine.ascent();
+ }
+
+ setBaselineOffset(baseline + d->yoff + d->textMargin);
QSizeF size(newWidth, newHeight);
if (d->contentSize != size) {
d->contentSize = size;
- emit contentSizeChanged();
+ // Note: inResize is a bitfield so QScopedValueRollback can't be used here
+ const bool wasInResize = d->inResize;
+ d->inResize = true;
+ if (!wasInResize)
+ emit contentSizeChanged();
+ d->inResize = wasInResize;
updateTotalLines();
}
}
@@ -2861,6 +3237,34 @@ void QQuickTextEditPrivate::updateDefaultTextOption()
}
}
+void QQuickTextEditPrivate::onDocumentStatusChanged()
+{
+ Q_ASSERT(quickDocument);
+ switch (quickDocument->status()) {
+ case QQuickTextDocument::Status::Loaded:
+ case QQuickTextDocument::Status::Saved:
+ switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
+ case Qt::RichText:
+ richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
+ markdownText = false;
+ break;
+ case Qt::MarkdownText:
+ richText = false;
+ markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
+ break;
+ case Qt::PlainText:
+ richText = false;
+ markdownText = false;
+ break;
+ case Qt::AutoText: // format not detected
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
void QQuickTextEdit::focusInEvent(QFocusEvent *event)
{
Q_D(QQuickTextEdit);
@@ -2905,19 +3309,21 @@ void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
}
}
-void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QQuickTextNode *node, TextNodeIterator &it, int startPos)
+void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it, int startPos)
{
engine->addToSceneGraph(node, QQuickText::Normal, QColor());
it = textNodeMap.insert(it, TextNode(startPos, node));
++it;
root->appendChildNode(node);
+ ++renderedBlockCount;
}
-QQuickTextNode *QQuickTextEditPrivate::createTextNode()
+QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
{
Q_Q(QQuickTextEdit);
- QQuickTextNode* node = new QQuickTextNode(q);
- node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering);
+ QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
+ node->setRenderType(QSGTextNode::RenderType(renderType));
+ node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
return node;
}
@@ -3047,8 +3453,9 @@ void QQuickTextEdit::remove(int start, int end)
\since 5.1
Returns the QQuickTextDocument of this TextEdit.
- It can be used to implement syntax highlighting using
- \l QSyntaxHighlighter.
+ Since Qt 6.7, it has features for loading and saving files.
+ It can also be used in C++ as a means of accessing the underlying QTextDocument
+ instance, for example to install a \l QSyntaxHighlighter.
\sa QQuickTextDocument
*/
@@ -3056,8 +3463,11 @@ void QQuickTextEdit::remove(int start, int end)
QQuickTextDocument *QQuickTextEdit::textDocument()
{
Q_D(QQuickTextEdit);
- if (!d->quickDocument)
+ if (!d->quickDocument) {
d->quickDocument = new QQuickTextDocument(this);
+ connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
+ [d]() { d->onDocumentStatusChanged(); } );
+ }
return d->quickDocument;
}
@@ -3399,4 +3809,3 @@ QQuickPre64TextEdit::QQuickPre64TextEdit(QQuickItem *parent)
QT_END_NAMESPACE
#include "moc_qquicktextedit_p.cpp"
-
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index c98d062c6b..ef09457327 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -22,11 +22,21 @@
QT_BEGIN_NAMESPACE
+class QTextDocument;
class QQuickTextDocument;
class QQuickTextEditPrivate;
+class QQuickTextSelection;
class QTextBlock;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, public QQuickTextInterface
+class QQuickTextBlockForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QTextBlock)
+ QML_EXTENDED(QQuickTextBlockForeign)
+};
+
+class Q_QUICK_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -78,6 +88,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, pub
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION(2, 6))
Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION(2, 7))
Q_PROPERTY(qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged REVISION(2, 10))
+ Q_PROPERTY(QQuickTextSelection* cursorSelection READ cursorSelection REVISION(6, 7) CONSTANT FINAL)
QML_NAMED_ELEMENT(TextEdit)
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
QML_ADDED_IN_VERSION(6, 4)
@@ -85,6 +96,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem, pub
public:
QQuickTextEdit(QQuickItem *parent=nullptr);
+ ~QQuickTextEdit() override;
enum HAlignment {
AlignLeft = Qt::AlignLeft,
@@ -124,7 +136,8 @@ public:
Q_ENUM(SelectionMode)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
@@ -208,6 +221,8 @@ public:
void componentComplete() override;
+ int resourcesLoading() const; // mainly for testing
+
/* FROM EDIT */
void setReadOnly(bool);
bool isReadOnly() const;
@@ -231,6 +246,8 @@ public:
Q_INVOKABLE void moveCursorSelection(int pos);
Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
+ QQuickTextSelection *cursorSelection() const;
+
QRectF boundingRect() const override;
QRectF clipRect() const override;
@@ -353,11 +370,15 @@ private Q_SLOTS:
void q_updateAlignment();
void updateSize();
void triggerPreprocess();
+ Q_REVISION(6, 7) QVariant loadResource(int type, const QUrl &source);
+ void resourceRequestFinished();
private:
void markDirtyNodesForRange(int start, int end, int charDelta);
void updateTotalLines();
void invalidateFontCaches();
+ QTextDocument* document() const;
+ void setDocument(QTextDocument *doc);
protected:
QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent = nullptr);
@@ -366,6 +387,7 @@ protected:
#endif
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
bool event(QEvent *) override;
void keyPressEvent(QKeyEvent *) override;
@@ -389,7 +411,7 @@ protected:
void updatePolish() override;
friend class QQuickTextUtil;
- friend class QQuickTextDocument;
+ friend class QQuickTextDocumentPrivate;
private:
Q_DISABLE_COPY(QQuickTextEdit)
@@ -411,6 +433,4 @@ Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickTextEdit::HAlignment, QQuick
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextEdit)
-
#endif // QQUICKTEXTEDIT_P_H
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index db07462a5a..da01be47c2 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -19,20 +19,30 @@
#include "qquickimplicitsizeitem_p_p.h"
#include "qquicktextutil_p.h"
+#include <QtQuick/private/qquicktextselection_p.h>
+
#include <QtQml/qqml.h>
#include <QtCore/qlist.h>
#include <private/qlazilyallocated_p.h>
+#include <private/qquicktextdocument_p.h>
+
+#if QT_CONFIG(accessibility)
+#include <QtGui/qaccessible.h>
+#endif
#include <limits>
QT_BEGIN_NAMESPACE
class QTextLayout;
-class QQuickTextDocumentWithImageResources;
+class QQuickPixmap;
class QQuickTextControl;
-class QQuickTextNode;
+class QSGInternalTextNode;
class QQuickTextNodeEngine;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
+#if QT_CONFIG(accessibility)
+ , public QAccessible::ActivationObserver
+#endif
{
public:
Q_DECLARE_PUBLIC(QQuickTextEdit)
@@ -41,21 +51,21 @@ public:
struct Node {
explicit Node(int startPos = std::numeric_limits<int>::max(),
- QQuickTextNode *node = nullptr)
- : m_startPos(startPos), m_node(node), m_dirty(false) { }
- QQuickTextNode* textNode() const { return m_node; }
+ QSGInternalTextNode *node = nullptr)
+ : m_node(node), m_startPos(startPos) { }
+ QSGInternalTextNode *textNode() const { return m_node; }
void moveStartPos(int delta) { Q_ASSERT(m_startPos + delta > 0); m_startPos += delta; }
int startPos() const { return m_startPos; }
void setDirty() { m_dirty = true; }
bool dirty() const { return m_dirty; }
private:
+ QSGInternalTextNode *m_node;
int m_startPos;
- QQuickTextNode* m_node;
- bool m_dirty;
+ bool m_dirty = false;
#ifndef QT_NO_DEBUG_STREAM
- friend QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug, const Node &);
+ friend QDebug Q_QUICK_EXPORT operator<<(QDebug, const Node &);
#endif
};
typedef QList<Node>::iterator TextNodeIterator;
@@ -63,11 +73,11 @@ public:
struct ExtraData {
ExtraData();
- qreal padding;
- qreal topPadding;
- qreal leftPadding;
- qreal rightPadding;
- qreal bottomPadding;
+ qreal padding = 0;
+ qreal topPadding = 0;
+ qreal leftPadding = 0;
+ qreal rightPadding = 0;
+ qreal bottomPadding = 0;
bool explicitTopPadding : 1;
bool explicitLeftPadding : 1;
bool explicitRightPadding : 1;
@@ -78,25 +88,22 @@ public:
QQuickTextEditPrivate()
- : color(QRgb(0xFF000000)), selectionColor(QRgb(0xFF000080)), selectedTextColor(QRgb(0xFFFFFFFF))
- , textMargin(0.0), xoff(0), yoff(0)
- , font(sourceFont), cursorComponent(nullptr), cursorItem(nullptr), document(nullptr), control(nullptr)
- , quickDocument(nullptr), lastSelectionStart(0), lastSelectionEnd(0), lineCount(0)
- , hAlign(QQuickTextEdit::AlignLeft), vAlign(QQuickTextEdit::AlignTop)
- , format(QQuickTextEdit::PlainText), wrapMode(QQuickTextEdit::NoWrap)
- , renderType(QQuickTextUtil::textRenderType<QQuickTextEdit>())
- , contentDirection(Qt::LayoutDirectionAuto)
- , mouseSelectionMode(QQuickTextEdit::SelectCharacters)
-#if QT_CONFIG(im)
- , inputMethodHints(Qt::ImhNone)
-#endif
- , updateType(UpdatePaintNode)
- , dirty(false), richText(false), cursorVisible(false), cursorPending(false)
+ : dirty(false), richText(false), cursorVisible(false), cursorPending(false)
, focusOnPress(true), persistentSelection(false), requireImplicitWidth(false)
, selectByMouse(true), canPaste(false), canPasteValid(false), hAlignImplicit(true)
, textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false)
- , hadSelection(false), markdownText(false)
+ , hadSelection(false), markdownText(false), inResize(false), ownsDocument(false)
{
+#if QT_CONFIG(accessibility)
+ QAccessible::installActivationObserver(this);
+#endif
+ }
+
+ ~QQuickTextEditPrivate()
+ {
+#if QT_CONFIG(accessibility)
+ QAccessible::removeActivationObserver(this);
+#endif
}
static QQuickTextEditPrivate *get(QQuickTextEdit *item) {
@@ -106,6 +113,7 @@ public:
void resetInputMethod();
void updateDefaultTextOption();
+ void onDocumentStatusChanged();
void relayoutDocument();
bool determineHorizontalAlignment();
bool setHAlign(QQuickTextEdit::HAlignment, bool forceAlign = false);
@@ -121,13 +129,18 @@ public:
void setNativeCursorEnabled(bool) {}
void handleFocusEvent(QFocusEvent *event);
- void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos);
- QQuickTextNode* createTextNode();
+ void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QSGInternalTextNode *, TextNodeIterator&, int startPos);
+ QSGInternalTextNode* createTextNode();
#if QT_CONFIG(im)
Qt::InputMethodHints effectiveInputMethodHints() const;
#endif
+#if QT_CONFIG(accessibility)
+ void accessibilityActiveChanged(bool active) override;
+ QAccessible::Role accessibleRole() const override;
+#endif
+
inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; }
void setTopPadding(qreal value, bool reset = false);
void setLeftPadding(qreal value, bool reset = false);
@@ -137,33 +150,36 @@ public:
bool isImplicitResizeEnabled() const;
void setImplicitResizeEnabled(bool enabled);
- QColor color;
- QColor selectionColor;
- QColor selectedTextColor;
+ QColor color = QRgb(0xFF000000);
+ QColor selectionColor = QRgb(0xFF000080);
+ QColor selectedTextColor = QRgb(0xFFFFFFFF);
QSizeF contentSize;
- qreal textMargin;
- qreal xoff;
- qreal yoff;
+ qreal textMargin = 0;
+ qreal xoff = 0;
+ qreal yoff = 0;
QString text;
QUrl baseUrl;
QFont sourceFont;
QFont font;
- QQmlComponent* cursorComponent;
- QQuickItem* cursorItem;
- QQuickTextDocumentWithImageResources *document;
- QQuickTextControl *control;
- QQuickTextDocument *quickDocument;
+ QQmlComponent* cursorComponent = nullptr;
+ QQuickItem* cursorItem = nullptr;
+ QTextDocument *document = nullptr;
+ QQuickTextControl *control = nullptr;
+ QQuickTextDocument *quickDocument = nullptr;
+ mutable QQuickTextSelection *cursorSelection = nullptr;
QList<Node> textNodeMap;
+ QList<QQuickPixmap *> pixmapsInProgress;
- int lastSelectionStart;
- int lastSelectionEnd;
- int lineCount;
- int firstBlockInViewport = -1; // only for the autotest; can be wrong after scrolling sometimes
+ int lastSelectionStart = 0;
+ int lastSelectionEnd = 0;
+ int lineCount = 0;
+ int firstBlockInViewport = -1; // can be wrong after scrolling sometimes
int firstBlockPastViewport = -1; // only for the autotest
+ int renderedBlockCount = -1; // only for the autotest
QRectF renderedRegion;
enum UpdateType {
@@ -173,17 +189,17 @@ public:
UpdateAll
};
- QQuickTextEdit::HAlignment hAlign;
- QQuickTextEdit::VAlignment vAlign;
- QQuickTextEdit::TextFormat format;
- QQuickTextEdit::WrapMode wrapMode;
- QQuickTextEdit::RenderType renderType;
- Qt::LayoutDirection contentDirection;
- QQuickTextEdit::SelectionMode mouseSelectionMode;
+ QQuickTextEdit::HAlignment hAlign = QQuickTextEdit::AlignLeft;
+ QQuickTextEdit::VAlignment vAlign = QQuickTextEdit::AlignTop;
+ QQuickTextEdit::TextFormat format = QQuickTextEdit::PlainText;
+ QQuickTextEdit::WrapMode wrapMode = QQuickTextEdit::NoWrap;
+ QQuickTextEdit::RenderType renderType = QQuickTextUtil::textRenderType<QQuickTextEdit>();
+ Qt::LayoutDirection contentDirection = Qt::LayoutDirectionAuto;
+ QQuickTextEdit::SelectionMode mouseSelectionMode = QQuickTextEdit::SelectCharacters;
#if QT_CONFIG(im)
- Qt::InputMethodHints inputMethodHints;
+ Qt::InputMethodHints inputMethodHints = Qt::ImhNone;
#endif
- UpdateType updateType;
+ UpdateType updateType = UpdatePaintNode;
bool dirty : 1;
bool richText : 1;
@@ -202,12 +218,14 @@ public:
bool selectByKeyboardSet:1;
bool hadSelection : 1;
bool markdownText : 1;
+ bool inResize : 1;
+ bool ownsDocument : 1;
static const int largeTextSizeThreshold;
};
#ifndef QT_NO_DEBUG_STREAM
-QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &);
+QDebug Q_QUICK_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &);
#endif
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 1df764f79b..0826011a54 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -13,7 +13,7 @@
#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QTextBoundaryFinder>
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#include <QtQuick/qsgsimplerectnode.h>
#include <QtGui/qstylehints.h>
@@ -90,7 +90,13 @@ void QQuickTextInput::componentComplete()
The text in the TextInput.
- \sa clear()
+ Note that some keyboards use a predictive function. In this case,
+ the text being composed by the input method is not part of this property.
+ The part of the text related to the predictions is underlined and stored in
+ the \l preeditText property. To get whole text displayed in the TextInput
+ use \l displayText property.
+
+ \sa clear(), displayText, preeditText
*/
QString QQuickTextInput::text() const
{
@@ -127,16 +133,23 @@ void QQuickTextInput::setText(const QString &s)
Override the default rendering type for this component.
Supported render types are:
- \list
- \li Text.QtRendering
- \li Text.NativeRendering
- \endlist
- Select Text.NativeRendering if you prefer text to look native on the target platform and do
+ \value TextInput.QtRendering Text is rendered using a scalable distance field for each glyph.
+ \value TextInput.NativeRendering Text is rendered using a platform-specific technique.
+ \value TextInput.CurveRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
+
+ Select \c TextInput.NativeRendering if you prefer text to look native on the target platform and do
not require advanced features such as transformation of the text. Using such features in
combination with the NativeRendering render type will lend poor and sometimes pixelated
results.
+ Both \c TextInput.QtRendering and \c TextInput.CurveRendering are hardware-accelerated techniques.
+ \c QtRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveRendering should be considered as an alternative in cases
+ where \c QtRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+
The default rendering type is determined by \l QQuickWindow::textRenderType().
*/
QQuickTextInput::RenderType QQuickTextInput::renderType() const
@@ -231,17 +244,16 @@ QString QQuickTextInputPrivate::realText() const
The requested weight of the font. The weight requested must be an integer
between 1 and 1000, or one of the predefined values:
- \list
- \li Font.Thin
- \li Font.Light
- \li Font.ExtraLight
- \li Font.Normal - the default
- \li Font.Medium
- \li Font.DemiBold
- \li Font.Bold
- \li Font.ExtraBold
- \li Font.Black
- \endlist
+
+ \value Font.Thin 100
+ \value Font.ExtraLight 200
+ \value Font.Light 300
+ \value Font.Normal 400 (default)
+ \value Font.Medium 500
+ \value Font.DemiBold 600
+ \value Font.Bold 700
+ \value Font.ExtraBold 800
+ \value Font.Black 900
\qml
TextInput { text: "Hello"; font.weight: Font.DemiBold }
@@ -305,13 +317,12 @@ QString QQuickTextInputPrivate::realText() const
Sets the capitalization for the text.
- \list
- \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
- \endlist
+ \value Font.MixedCase the normal case: no capitalization change is applied
+ \value Font.AllUppercase alters the text to be rendered in all uppercase type
+ \value Font.AllLowercase alters the text to be rendered in all lowercase type
+ \value Font.SmallCaps alters the text to be rendered in small-caps type
+ \value Font.Capitalize alters the text to be rendered with the first character of
+ each word as an uppercase character
\qml
TextInput { text: "Hello"; font.capitalization: Font.AllLowercase }
@@ -328,23 +339,21 @@ QString QQuickTextInputPrivate::realText() const
\note This property only has an effect when used together with render type TextInput.NativeRendering.
- \list
- \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
- \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting If possible, render text without hinting the outlines
of the glyphs. The text layout will be typographically accurate, using the same metrics
as are used e.g. when printing.
- \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
but align glyphs to the pixel grid in the vertical direction. The text will appear
crisper on displays where the density is too low to give an accurate rendering
of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
layout will be scalable to higher density devices (such as printers) without impacting
details such as line breaks.
- \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
vertical directions. The text will be altered to optimize legibility on the target
device, but since the metrics will depend on the target size of the text, the positions
of glyphs, line breaks, and other typographical detail will not scale, meaning that a
text layout may look different on devices with different pixel densities.
- \endlist
\qml
TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
@@ -380,6 +389,34 @@ QString QQuickTextInputPrivate::realText() const
TextInput { text: "Some text"; font.preferShaping: false }
\endqml
*/
+
+/*!
+ \qmlproperty object QtQuick::TextInput::font.variableAxes
+ \since 6.7
+
+ \include qquicktext.cpp qml-font-variable-axes
+*/
+
+/*!
+ \qmlproperty object QtQuick::TextInput::font.features
+ \since 6.6
+
+ \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);
@@ -532,9 +569,8 @@ QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
void QQuickTextInput::setHAlign(HAlignment align)
{
Q_D(QQuickTextInput);
- bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
- d->hAlignImplicit = false;
- if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+
+ if (d->setHAlign(align, true) && isComponentComplete()) {
d->updateLayout();
updateCursorRectangle();
}
@@ -569,17 +605,34 @@ QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const
return effectiveAlignment;
}
-bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign)
+bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment align, bool forceAlign)
{
Q_Q(QQuickTextInput);
- if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported
- QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
- hAlign = alignment;
- emit q->horizontalAlignmentChanged(alignment);
- if (oldEffectiveHAlign != q->effectiveHAlign())
- emit q->effectiveHorizontalAlignmentChanged();
+ if (align > QQuickTextInput::AlignHCenter)
+ return false; // justify is not supported
+
+ if (hAlign == align && !forceAlign)
+ return false;
+
+ const bool wasImplicit = hAlignImplicit;
+ const auto oldEffectiveHAlign = q->effectiveHAlign();
+
+ hAlignImplicit = !forceAlign;
+ if (hAlign != align) {
+ hAlign = align;
+ emit q->horizontalAlignmentChanged(align);
+ }
+
+ if (q->effectiveHAlign() != oldEffectiveHAlign) {
+ emit q->effectiveHorizontalAlignmentChanged();
return true;
}
+
+ if (forceAlign && wasImplicit) {
+ // QTBUG-120052 - when horizontal text alignment is set explicitly,
+ // we need notify any other controls that may depend on it, like QQuickPlaceholderText
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
return false;
}
@@ -623,16 +676,19 @@ Qt::LayoutDirection QQuickTextInputPrivate::layoutDirection() const
bool QQuickTextInputPrivate::determineHorizontalAlignment()
{
- if (hAlignImplicit) {
- // if no explicit alignment has been set, follow the natural layout direction of the text
- Qt::LayoutDirection direction = textDirection();
+ if (!hAlignImplicit)
+ return false;
+
+ // if no explicit alignment has been set, follow the natural layout direction of the text
+ Qt::LayoutDirection direction = textDirection();
#if QT_CONFIG(im)
- if (direction == Qt::LayoutDirectionAuto)
- direction = QGuiApplication::inputMethod()->inputDirection();
+ if (direction == Qt::LayoutDirectionAuto)
+ direction = QGuiApplication::inputMethod()->inputDirection();
#endif
- return setHAlign(direction == Qt::RightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
- }
- return false;
+
+ const auto implicitHAlign = direction == Qt::RightToLeft ?
+ QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft;
+ return setHAlign(implicitHAlign);
}
QQuickTextInput::VAlignment QQuickTextInput::vAlign() const
@@ -660,12 +716,17 @@ void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
Set this property to wrap the text to the TextInput item's width.
The text will only wrap if an explicit width has been set.
- \list
- \li TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
- \li TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
- \li TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \li TextInput.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
- \endlist
+ \value TextInput.NoWrap
+ (default) no wrapping will be performed. If the text contains
+ insufficient newlines, then \l contentWidth will exceed a set width.
+ \value TextInput.WordWrap
+ wrapping is done on word boundaries only. If a word is too long,
+ \l contentWidth will exceed a set width.
+ \value TextInput.WrapAnywhere
+ wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \value TextInput.Wrap
+ if possible, wrapping occurs at a word boundary; otherwise it will occur
+ at the appropriate point on the line, even in the middle of a word.
The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
*/
@@ -1199,14 +1260,13 @@ Qt::InputMethodHints QQuickTextInputPrivate::effectiveInputMethodHints() const
\qmlproperty enumeration QtQuick::TextInput::echoMode
Specifies how the text should be displayed in the TextInput.
- \list
- \li TextInput.Normal - Displays the text as it is. (Default)
- \li TextInput.Password - Displays platform-dependent password mask
- characters instead of the actual characters.
- \li TextInput.NoEcho - Displays nothing.
- \li TextInput.PasswordEchoOnEdit - Displays characters as they are entered
- while editing, otherwise identical to \c TextInput.Password.
- \endlist
+
+ \value TextInput.Normal Displays the text as it is. (Default)
+ \value TextInput.Password Displays platform-dependent password mask
+ characters instead of the actual characters.
+ \value TextInput.NoEcho Displays nothing.
+ \value TextInput.PasswordEchoOnEdit Displays characters as they are entered
+ while editing, otherwise identical to \c TextInput.Password.
*/
QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
{
@@ -1247,40 +1307,31 @@ void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
Flags that alter behaviour are:
- \list
- \li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
- This is automatically set when setting echoMode to \c TextInput.Password.
- \li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
- in any persistent storage like predictive user dictionary.
- \li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
- when a sentence ends.
- \li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
- \li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
- \li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
- \li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
-
- \li Qt.ImhDate - The text editor functions as a date field.
- \li Qt.ImhTime - The text editor functions as a time field.
- \li Qt.ImhMultiLine - The text editor doesn't close software input keyboard when Return or Enter key is pressed (since QtQuick 2.4).
- \endlist
+ \value Qt.ImhHiddenText Characters should be hidden, as is typically used when entering passwords.
+ \value Qt.ImhSensitiveData Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+ \value Qt.ImhNoAutoUppercase The input method should not try to automatically switch to
+ upper case when a sentence ends.
+ \value Qt.ImhPreferNumbers Numbers are preferred (but not required).
+ \value Qt.ImhPreferUppercase Upper case letters are preferred (but not required).
+ \value Qt.ImhPreferLowercase Lower case letters are preferred (but not required).
+ \value Qt.ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing.
+ \value Qt.ImhDate The text editor functions as a date field.
+ \value Qt.ImhTime The text editor functions as a time field.
Flags that restrict input (exclusive flags) are:
- \list
- \li Qt.ImhDigitsOnly - Only digits are allowed.
- \li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
- \li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
- \li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
- \li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
- \li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
- \li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
- \endlist
+ \value Qt.ImhDigitsOnly Only digits are allowed.
+ \value Qt.ImhFormattedNumbersOnly Only number input is allowed. This includes decimal point and minus sign.
+ \value Qt.ImhUppercaseOnly Only upper case letter input is allowed.
+ \value Qt.ImhLowercaseOnly Only lower case letter input is allowed.
+ \value Qt.ImhDialableCharactersOnly Only characters suitable for phone dialing are allowed.
+ \value Qt.ImhEmailCharactersOnly Only characters suitable for email addresses are allowed.
+ \value Qt.ImhUrlCharactersOnly Only characters suitable for URLs are allowed.
Masks:
- \list
- \li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
- \endlist
+ \value Qt.ImhExclusiveInputMask This mask yields nonzero if any of the exclusive flags are used.
*/
Qt::InputMethodHints QQuickTextInput::inputMethodHints() const
@@ -1397,7 +1448,7 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
Returns the position before the character that is nearest x.
*/
-void QQuickTextInput::positionAt(QQmlV4Function *args) const
+void QQuickTextInput::positionAt(QQmlV4FunctionPtr args) const
{
Q_D(const QQuickTextInput);
@@ -1571,7 +1622,11 @@ void QQuickTextInput::mousePressEvent(QMouseEvent *event)
}
}
- if (isMouse) {
+ if (isMouse
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ || d->selectByTouchDrag
+#endif
+ ) {
bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
int cursor = d->positionAt(event->position());
d->moveCursor(cursor, mark);
@@ -1640,9 +1695,11 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
}
}
#endif
+
// On a touchscreen or with a stylus, set cursor position and focus on release, not on press;
// if Flickable steals the grab in the meantime, the cursor won't move.
- if (!isMouse)
+ // Check d->hasSelectedText() to keep touch-and-hold word selection working.
+ if (!isMouse && !d->hasSelectedText())
d->moveCursor(d->positionAt(event->position()), false);
if (d->focusOnPress && qGuiApp->styleHints()->setFocusOnTouchRelease())
@@ -1751,6 +1808,26 @@ void QQuickTextInput::geometryChange(const QRectF &newGeometry,
QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
}
+void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickTextInput);
+ Q_UNUSED(value);
+ switch (change) {
+ 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.
+ d->updateLayout();
+ }
+ break;
+
+ default:
+ break;
+ }
+ QQuickImplicitSizeItem::itemChange(change, value);
+}
+
void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength)
{
Q_Q(QQuickTextInput);
@@ -1911,9 +1988,9 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
d->updateType = QQuickTextInputPrivate::UpdateNone;
- QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
+ QSGInternalTextNode *node = static_cast<QSGInternalTextNode *>(oldNode);
if (node == nullptr)
- node = new QQuickTextNode(this);
+ node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
d->textNode = node;
const bool showCursor = !isReadOnly() && d->cursorItem == nullptr && d->cursorVisible && d->m_blinkStatus;
@@ -1924,9 +2001,19 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
else
node->clearCursor();
} else {
- node->setUseNativeRenderer(d->renderType == NativeRendering);
- node->deleteContent();
+ node->setRenderType(QSGTextNode::RenderType(d->renderType));
+ node->clear();
node->setMatrix(QMatrix4x4());
+ node->setTextStyle(QSGInternalTextNode::Normal);
+ node->setColor(d->color);
+ node->setSelectionTextColor(d->selectedTextColor);
+ node->setSelectionColor(d->selectionColor);
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+
+ if (flags().testFlag(ItemObservesViewport))
+ node->setViewport(clipRect());
+ else
+ node->setViewport(QRectF{});
QPointF offset(leftPadding(), topPadding());
if (d->autoScroll && d->m_textLayout.lineCount() > 0) {
@@ -1942,16 +2029,14 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
|| !d->m_textLayout.preeditAreaText().isEmpty()
#endif
) {
- node->addTextLayout(offset, &d->m_textLayout, d->color,
- QQuickText::Normal, QColor(), QColor(),
- d->selectionColor, d->selectedTextColor,
+ node->addTextLayout(offset, &d->m_textLayout,
d->selectionStart(),
d->selectionEnd() - 1); // selectionEnd() returns first char after
- // selection
+ // selection
}
if (showCursor)
- node->setCursor(cursorRectangle(), d->color);
+ node->setCursor(cursorRectangle(), d->color);
d->textLayoutDirty = false;
}
@@ -1974,8 +2059,14 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
|| d->extra->enterKeyAttached->type() == Qt::EnterKeyDefault) {
QQuickItem *next = const_cast<QQuickTextInput*>(this)->nextItemInFocusChain();
- while (next && next != this && !next->activeFocusOnTab())
+ QQuickItem *originalNext = next;
+ while (next && next != this && !next->activeFocusOnTab()) {
next = next->nextItemInFocusChain();
+ if (next == originalNext) {
+ // There seems to be no suitable element in the focus chain
+ next = nullptr;
+ }
+ }
if (next) {
const auto nextYPos = next->mapToGlobal(QPoint(0, 0)).y();
const auto currentYPos = this->mapToGlobal(QPoint(0, 0)).y();
@@ -2411,7 +2502,10 @@ QString QQuickTextInput::displayText() const
This property contains partial text input from an input method.
- \sa displayText
+ To turn off partial text that results from predictions, set the \c Qt.ImhNoPredictiveText
+ flag in inputMethodHints.
+
+ \sa displayText, inputMethodHints
*/
QString QQuickTextInput::preeditText() const
{
@@ -2457,10 +2551,8 @@ void QQuickTextInput::setSelectByMouse(bool on)
Specifies how text should be selected using a mouse.
- \list
- \li TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
- \li TextInput.SelectWords - The selection is updated with whole words.
- \endlist
+ \value TextInput.SelectCharacters (default) The selection is updated with individual characters.
+ \value TextInput.SelectWords The selection is updated with whole words.
This property only applies when \l selectByMouse is true.
*/
@@ -2515,7 +2607,7 @@ bool QQuickTextInput::canPaste() const
Q_D(const QQuickTextInput);
if (!d->canPasteValid) {
if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData())
- const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText();
+ const_cast<QQuickTextInputPrivate *>(d)->canPaste = !d->m_readOnly && mimeData->hasText() && !mimeData->text().isEmpty();
const_cast<QQuickTextInputPrivate *>(d)->canPasteValid = true;
}
return d->canPaste;
@@ -2600,13 +2692,12 @@ void QQuickTextInput::moveCursorSelection(int position)
The selection mode specifies whether the selection is updated on a per character or a per word
basis. If not specified the selection mode will default to \c {TextInput.SelectCharacters}.
- \list
- \li TextInput.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
- the previous cursor position) to the specified position.
- \li TextInput.SelectWords - Sets the selectionStart and selectionEnd to include all
- words between the specified position and the previous cursor position. Words partially in the
- range are included.
- \endlist
+ \value TextInput.SelectCharacters
+ Sets either the selectionStart or selectionEnd (whichever was at the previous cursor position)
+ to the specified position.
+ \value TextInput.SelectWords
+ Sets the selectionStart and selectionEnd to include all words between the specified position
+ and the previous cursor position. Words partially in the range are included.
For example, take this sequence of calls:
@@ -2796,6 +2887,7 @@ void QQuickTextInputPrivate::init()
}
m_inputControl = new QInputControl(QInputControl::LineEdit, q);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
void QQuickTextInputPrivate::cancelInput()
@@ -3526,6 +3618,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
}
selectionChange = true;
} else {
+ selectionChange = m_selstart != m_selend;
m_selstart = m_selend = 0;
}
cursorPositionChanged = true;
@@ -3604,7 +3697,7 @@ void QQuickTextInputPrivate::selectWordAtPos(int cursor)
moveCursor(c, false);
// ## text layout should support end of words.
int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
- while (end > cursor && m_text[end-1].isSpace())
+ while (end > cursor && m_text.at(end - 1).isSpace())
--end;
moveCursor(end, true);
}
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 433eed4d78..5212e6117e 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
class QQuickTextInputPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem, public QQuickTextInterface
+class Q_QUICK_EXPORT QQuickTextInput : public QQuickImplicitSizeItem, public QQuickTextInterface
{
Q_OBJECT
Q_INTERFACES(QQuickTextInterface)
@@ -138,12 +138,13 @@ public:
Q_ENUM(CursorPosition)
enum RenderType { QtRendering,
- NativeRendering
+ NativeRendering,
+ CurveRendering
};
Q_ENUM(RenderType)
//Auxilliary functions needed to control the TextInput from QML
- Q_INVOKABLE void positionAt(QQmlV4Function *args) const;
+ Q_INVOKABLE void positionAt(QQmlV4FunctionPtr args) const;
Q_INVOKABLE QRectF positionToRectangle(int pos) const;
Q_INVOKABLE void moveCursorSelection(int pos);
Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
@@ -349,6 +350,7 @@ protected:
#endif
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
@@ -415,6 +417,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextInput)
-
#endif // QQUICKTEXTINPUT_P_H
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 01d90f6b10..6bc83d318b 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -36,10 +36,10 @@
QT_BEGIN_NAMESPACE
-class QQuickTextNode;
+class QSGInternalTextNode;
class QInputControl;
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickTextInput)
@@ -199,7 +199,7 @@ public:
QFont sourceFont;
QQuickItem *cursorItem;
- QQuickTextNode *textNode;
+ QSGInternalTextNode *textNode;
std::unique_ptr<MaskInputData[]> m_maskData;
QInputControl *m_inputControl;
diff --git a/src/quick/items/qquicktextinterface_p.h b/src/quick/items/qquicktextinterface_p.h
index 4210c6c0ad..3a8a31be10 100644
--- a/src/quick/items/qquicktextinterface_p.h
+++ b/src/quick/items/qquicktextinterface_p.h
@@ -17,7 +17,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickTextInterface
+class Q_QUICK_EXPORT QQuickTextInterface
{
public:
virtual void invalidate() = 0;
diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h
deleted file mode 100644
index 0538336311..0000000000
--- a/src/quick/items/qquicktextnode_p.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QQUICKTEXTNODE_P_H
-#define QQUICKTEXTNODE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtQuick/qsgnode.h>
-#include "qquicktext_p.h"
-#include <qglyphrun.h>
-
-#include <QtGui/qcolor.h>
-#include <QtGui/qtextlayout.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qscopedpointer.h>
-
-QT_BEGIN_NAMESPACE
-
-class QSGGlyphNode;
-class QTextBlock;
-class QColor;
-class QTextDocument;
-class QSGContext;
-class QRawFont;
-class QSGInternalRectangleNode;
-class QSGClipNode;
-class QSGTexture;
-
-class QQuickTextNodeEngine;
-
-class Q_QUICK_PRIVATE_EXPORT QQuickTextNode : public QSGTransformNode
-{
-public:
- QQuickTextNode(QQuickItem *ownerElement);
- ~QQuickTextNode();
-
- static bool isComplexRichText(QTextDocument *);
-
- void deleteContent();
- void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(),
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- const QColor &anchorColor = QColor(),
- const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
- int selectionStart = -1, int selectionEnd = -1,
- int lineStart = 0, int lineCount = -1);
- void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(),
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- const QColor &anchorColor = QColor(),
- const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
- int selectionStart = -1, int selectionEnd = -1);
-
- void setCursor(const QRectF &rect, const QColor &color);
- void clearCursor();
- QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
-
- QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
- QSGNode *parentNode = 0);
- void addImage(const QRectF &rect, const QImage &image);
- void addRectangleNode(const QRectF &rect, const QColor &color);
-
- bool useNativeRenderer() const { return m_useNativeRenderer; }
- void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; }
-
- void setRenderTypeQuality(int renderTypeQuality) { m_renderTypeQuality = renderTypeQuality; }
- int renderTypeQuality() const { return m_renderTypeQuality; }
-
- QPair<int, int> renderedLineRange() const { return { m_firstLineInViewport, m_firstLinePastViewport }; }
-
-private:
- QSGInternalRectangleNode *m_cursorNode;
- QList<QSGTexture *> m_textures;
- QQuickItem *m_ownerElement;
- bool m_useNativeRenderer;
- int m_renderTypeQuality;
- int m_firstLineInViewport = -1;
- int m_firstLinePastViewport = -1;
-
- friend class QQuickTextEdit;
- friend class QQuickTextEditPrivate;
-};
-
-QT_END_NAMESPACE
-
-#endif // QQUICKTEXTNODE_P_H
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index d2256128cf..d6d31b4621 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -13,17 +13,15 @@
#include <QtGui/qtextlist.h>
#include <private/qquicktext_p.h>
-#include <private/qquicktextdocument_p.h>
#include <private/qtextdocumentlayout_p.h>
#include <private/qtextimagehandler_p.h>
#include <private/qrawfont_p.h>
#include <private/qglyphrun_p.h>
#include <private/qquickitem_p.h>
+#include <private/qsgdistancefieldglyphnode_p.h>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcSgText)
-
QQuickTextNodeEngine::BinaryTreeNodeKey::BinaryTreeNodeKey(BinaryTreeNode *node)
: fontEngine(QRawFontPrivate::get(node->glyphRun.rawFont())->fontEngine)
, clipNode(node->clipNode)
@@ -429,15 +427,8 @@ void QQuickTextNodeEngine::addTextObject(const QTextBlock &block, const QPointF
if (format.objectType() == QTextFormat::ImageObject) {
QTextImageFormat imageFormat = format.toImageFormat();
- if (QQuickTextDocumentWithImageResources *imageDoc = qobject_cast<QQuickTextDocumentWithImageResources *>(textDocument)) {
- image = imageDoc->image(imageFormat);
-
- if (image.isNull())
- return;
- } else {
- QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
- image = imageHandler->image(textDocument, imageFormat);
- }
+ QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
+ image = imageHandler->image(textDocument, imageFormat);
}
if (image.isNull()) {
@@ -668,10 +659,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();
@@ -681,7 +676,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);
}
}
@@ -702,6 +697,9 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
BinaryTreeNode *node = m_processedNodes.data() + i;
if (node->image.isNull()) {
+ if (node->glyphRun.isEmpty())
+ continue;
+
BinaryTreeNodeKey key(node);
QList<BinaryTreeNode *> &nodes = map[key];
@@ -756,7 +754,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
}
}
-void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode,
+void QQuickTextNodeEngine::addToSceneGraph(QSGInternalTextNode *parentNode,
QQuickText::TextStyle style,
const QColor &styleColor)
{
@@ -801,7 +799,7 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode,
? m_selectedTextColor
: textDecoration.color;
- parentNode->addRectangleNode(textDecoration.rect, color);
+ parentNode->addDecorationNode(textDecoration.rect, color);
}
// Finally add the selected text on top of everything
@@ -1040,6 +1038,12 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText
line.setPosition(QPointF(0, 0));
layout.endLayout();
+ // set the color for the bullets, instead of using the previous QTextBlock's color.
+ if (charFormat.foreground().style() == Qt::NoBrush)
+ setTextColor(textColor);
+ else
+ setTextColor(charFormat.foreground().color());
+
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
for (int i=0; i<glyphRuns.size(); ++i)
addUnselectedGlyphs(glyphRuns.at(i));
@@ -1068,7 +1072,7 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText
else
setPosition(blockBoundingRect.topLeft());
- if (text.contains(QChar::ObjectReplacementCharacter)) {
+ if (text.contains(QChar::ObjectReplacementCharacter) && charFormat.objectType() != QTextFormat::NoObject) {
QTextFrame *frame = qobject_cast<QTextFrame *>(textDocument->objectForFormat(charFormat));
if (!frame || frame->frameFormat().position() == QTextFrameFormat::InFlow) {
int blockRelativePosition = textPos - block.position();
diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h
index 1fbbbc2d93..1ed98ce208 100644
--- a/src/quick/items/qquicktextnodeengine_p.h
+++ b/src/quick/items/qquicktextnodeengine_p.h
@@ -9,7 +9,7 @@
#include <QtGui/qtextdocument.h>
#include <QtGui/qtextlayout.h>
#include "qquickclipnode_p.h"
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#ifndef QQUICKTEXTNODEENGINE_P_H
#define QQUICKTEXTNODEENGINE_P_H
@@ -160,7 +160,7 @@ public:
void mergeProcessedNodes(QList<BinaryTreeNode *> *regularNodes,
QList<BinaryTreeNode *> *imageNodes);
- void addToSceneGraph(QQuickTextNode *parent,
+ void addToSceneGraph(QSGInternalTextNode *parent,
QQuickText::TextStyle style = QQuickText::Normal,
const QColor &styleColor = QColor());
@@ -191,7 +191,6 @@ public:
-
private:
struct TextDecoration
{
@@ -234,7 +233,7 @@ private:
bool m_hasSelection : 1;
bool m_hasContents : 1;
- friend class QQuickTextNode;
+ friend class QSGInternalTextNode;
};
diff --git a/src/quick/items/qquicktextutil_p.h b/src/quick/items/qquicktextutil_p.h
index 212c488c73..9f8e2ae393 100644
--- a/src/quick/items/qquicktextutil_p.h
+++ b/src/quick/items/qquicktextutil_p.h
@@ -99,6 +99,8 @@ typename T::RenderType QQuickTextUtil::textRenderType()
return T::QtRendering;
case QQuickWindow::NativeTextRendering:
return T::NativeRendering;
+ case QQuickWindow::CurveTextRendering:
+ return T::CurveRendering;
}
Q_UNREACHABLE_RETURN(T::QtRendering);
diff --git a/src/quick/items/qquicktranslate_p.h b/src/quick/items/qquicktranslate_p.h
index 3dc3310da1..496a115d3f 100644
--- a/src/quick/items/qquicktranslate_p.h
+++ b/src/quick/items/qquicktranslate_p.h
@@ -24,7 +24,7 @@
QT_BEGIN_NAMESPACE
class QQuickTranslatePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTranslate : public QQuickTransform
+class Q_QUICK_EXPORT QQuickTranslate : public QQuickTransform
{
Q_OBJECT
@@ -53,7 +53,7 @@ private:
};
class QQuickScalePrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickScale : public QQuickTransform
+class Q_QUICK_EXPORT QQuickScale : public QQuickTransform
{
Q_OBJECT
@@ -92,7 +92,7 @@ private:
};
class QQuickRotationPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickRotation : public QQuickTransform
+class Q_QUICK_EXPORT QQuickRotation : public QQuickTransform
{
Q_OBJECT
@@ -126,7 +126,7 @@ private:
};
class QQuickMatrix4x4Private;
-class Q_QUICK_PRIVATE_EXPORT QQuickMatrix4x4 : public QQuickTransform
+class Q_QUICK_EXPORT QQuickMatrix4x4 : public QQuickTransform
{
Q_OBJECT
@@ -151,6 +151,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTranslate)
-
#endif
diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp
index e73eb459d4..033884d58d 100644
--- a/src/quick/items/qquicktreeview.cpp
+++ b/src/quick/items/qquicktreeview.cpp
@@ -42,7 +42,7 @@
a set of properties that can be used to position and render each node
in the tree correctly.
- An example of a custom delegate is shown below:
+ An example of a custom delegate with an animating indicator is shown below:
\snippet qml/treeview/qml-customdelegate.qml 0
@@ -81,16 +81,26 @@
By default, TreeView \l {toggleExpanded()}{toggles} the expanded state
of a row when you double tap on it. Since this is in conflict with
- double tapping to edit a cell, TreeView sets \l editTriggers to
+ double tapping to edit a cell, TreeView sets \l {TableView::}{editTriggers} to
\c TableView.EditKeyPressed by default (which is different from TableView,
which uses \c {TableView.EditKeyPressed | TableView.DoubleTapped}.
- If you change \l editTriggers to also contain \c TableView.DoubleTapped,
+ If you change \l {TableView::}{editTriggers} to also contain \c TableView.DoubleTapped,
toggling the expanded state with a double tap will be disabled.
\note A TreeView only accepts a model that inherits \l QAbstractItemModel.
*/
/*!
+ \qmlproperty QModelIndex QtQuick::TreeView::rootIndex
+ \since 6.6
+
+ This property holds the model index of the root item in the tree.
+ By default, this is the same as the root index in the model, but you can
+ set it to be a child index instead, to show only a branch of the tree.
+ Set it to \c undefined to show the whole model.
+*/
+
+/*!
\qmlmethod int QtQuick::TreeView::depth(row)
Returns the depth (the number of parents up to the root) of the given \a row.
@@ -268,9 +278,6 @@ void QQuickTreeViewPrivate::setModelImpl(const QVariant &newModel)
{
Q_Q(QQuickTreeView);
- if (newModel == m_assignedModel)
- return;
-
m_assignedModel = newModel;
QVariant effectiveModel = m_assignedModel;
if (effectiveModel.userType() == qMetaTypeId<QJSValue>())
@@ -337,55 +344,39 @@ void QQuickTreeViewPrivate::updateSelection(const QRect &oldSelection, const QRe
{
Q_Q(QQuickTreeView);
- const QRect oldRect = oldSelection.normalized();
- const QRect newRect = newSelection.normalized();
-
if (oldSelection == newSelection)
return;
- // Select the rows inside newRect that doesn't overlap with oldRect
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) {
- if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height())
- continue;
- const QModelIndex startIndex = q->modelIndex(row, newRect.x());
- const QModelIndex endIndex = q->modelIndex(row, newRect.x() + newRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
+ QItemSelection select;
+ QItemSelection deselect;
+
+ // Because each row can have a different parent, we need to create separate QItemSelections
+ // per row. But all the cells in a given row have the same parent, so they can be combined.
+ // As a result, the final QItemSelection can end up more fragmented compared to a selection
+ // in QQuickTableView, where all cells have the same parent. In the end, if TreeView has
+ // a lot of columns and the selection mode is "SelectCells", using the mouse to adjust
+ // a selection containing a _large_ number of columns can be slow.
+ const QRect cells = newSelection.normalized();
+ for (int row = cells.y(); row <= cells.y() + cells.height(); ++row) {
+ const QModelIndex startIndex = q->index(row, cells.x());
+ const QModelIndex endIndex = q->index(row, cells.x() + cells.width());
+ select.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
}
- if (oldRect.x() != -1) {
- // Since oldRect is valid, this update is a continuation of an already existing selection!
-
- // Select the columns inside newRect that don't overlap with oldRect
- for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) {
- if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width())
- continue;
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
- selectionModel->select(q->modelIndex(row, column), QItemSelectionModel::Select);
- }
-
- // Unselect the rows inside oldRect that don't overlap with newRect
- for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) {
- if (newRect.y() <= row && row <= newRect.y() + newRect.height())
- continue;
- const QModelIndex startIndex = q->modelIndex(row, oldRect.x());
- const QModelIndex endIndex = q->modelIndex(row, oldRect.x() + oldRect.width());
- selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect);
- }
+ const QModelIndexList indexes = selectionModel->selection().indexes();
+ for (const QModelIndex &index : indexes) {
+ if (!select.contains(index) && !existingSelection.contains(index))
+ deselect.merge(QItemSelection(index, index), QItemSelectionModel::Select);
+ }
- // Unselect the columns inside oldRect that don't overlap with newRect
- for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) {
- if (newRect.x() <= column && column <= newRect.x() + newRect.width())
- continue;
- // Since we're not allowed to call select/unselect on the selectionModel with
- // indices from different parents, and since indicies from different parents are
- // expected when working with trees, we need to unselect the indices in the column
- // one by one, rather than the whole column in one go. This, however, can cause a
- // lot of selection fragments in the selectionModel, which eventually can hurt
- // performance. But large selections containing a lot of columns is not normally
- // the case for a treeview, so accept this potential corner case for now.
- for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row)
- selectionModel->select(q->modelIndex(row, column), QItemSelectionModel::Deselect);
- }
+ if (selectionFlag == QItemSelectionModel::Select) {
+ selectionModel->select(deselect, QItemSelectionModel::Deselect);
+ selectionModel->select(select, QItemSelectionModel::Select);
+ } else {
+ QItemSelection oldSelection = existingSelection;
+ oldSelection.merge(select, QItemSelectionModel::Deselect);
+ selectionModel->select(oldSelection, QItemSelectionModel::Select);
+ selectionModel->select(select, QItemSelectionModel::Deselect);
}
}
@@ -403,6 +394,8 @@ QQuickTreeView::QQuickTreeView(QQuickItem *parent)
d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
d, &QQuickTreeViewPrivate::dataChangedCallback);
+ QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
+ this, &QQuickTreeView::rootIndexChanged);
auto tapHandler = new QQuickTapHandler(this);
tapHandler->setAcceptedModifiers(Qt::NoModifier);
@@ -421,6 +414,25 @@ QQuickTreeView::~QQuickTreeView()
{
}
+QModelIndex QQuickTreeView::rootIndex() const
+{
+ return d_func()->m_treeModelToTableModel.rootIndex();
+}
+
+void QQuickTreeView::setRootIndex(const QModelIndex &index)
+{
+ Q_D(QQuickTreeView);
+ d->m_treeModelToTableModel.setRootIndex(index);
+ positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
+}
+
+void QQuickTreeView::resetRootIndex()
+{
+ Q_D(QQuickTreeView);
+ d->m_treeModelToTableModel.resetRootIndex();
+ positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
+}
+
int QQuickTreeView::depth(int row) const
{
Q_D(const QQuickTreeView);
@@ -610,6 +622,7 @@ QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const
return QPoint(tableIndex.column(), tableIndex.row());
}
+#if QT_DEPRECATED_SINCE(6, 4)
QModelIndex QQuickTreeView::modelIndex(int row, int column) const
{
static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
@@ -621,9 +634,13 @@ QModelIndex QQuickTreeView::modelIndex(int row, int column) const
// to continue accepting calls to modelIndex(column, row).
return modelIndex({row, column});
} else {
+ qmlWarning(this) << "modelIndex(row, column) is deprecated. "
+ "Use index(row, column) instead. For more information, see "
+ "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
return modelIndex({column, row});
}
}
+#endif
void QQuickTreeView::keyPressEvent(QKeyEvent *event)
{
diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h
index b499f900cd..fe5fed6b95 100644
--- a/src/quick/items/qquicktreeview_p.h
+++ b/src/quick/items/qquicktreeview_p.h
@@ -22,9 +22,10 @@ QT_BEGIN_NAMESPACE
class QQuickTreeViewPrivate;
-class Q_QUICK_PRIVATE_EXPORT QQuickTreeView : public QQuickTableView
+class Q_QUICK_EXPORT QQuickTreeView : public QQuickTableView
{
Q_OBJECT
+ Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex RESET resetRootIndex NOTIFY rootIndexChanged REVISION(6, 6) FINAL)
QML_NAMED_ELEMENT(TreeView)
QML_ADDED_IN_VERSION(6, 3)
@@ -32,6 +33,10 @@ public:
QQuickTreeView(QQuickItem *parent = nullptr);
~QQuickTreeView() override;
+ QModelIndex rootIndex() const;
+ void setRootIndex(const QModelIndex &index);
+ void resetRootIndex();
+
Q_INVOKABLE int depth(int row) const;
Q_INVOKABLE bool isExpanded(int row) const;
@@ -44,12 +49,17 @@ public:
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index);
Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override;
- Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override;
+#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("Use index(row, column) instead")
+ Q_REVISION(6, 4) Q_INVOKABLE QModelIndex modelIndex(int row, int column) const override;
+#endif
+
Q_SIGNALS:
void expanded(int row, int depth);
void collapsed(int row, bool recursively);
+ Q_REVISION(6, 6) void rootIndexChanged();
protected:
void keyPressEvent(QKeyEvent *event) override;
@@ -61,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTreeView)
-
#endif // QQUICKTREEVIEW_P_H
diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h
index c14d7c4107..bb6a8d7ce4 100644
--- a/src/quick/items/qquicktreeview_p_p.h
+++ b/src/quick/items/qquicktreeview_p_p.h
@@ -22,7 +22,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickTreeViewPrivate : public QQuickTableViewPrivate
+class Q_QUICK_EXPORT QQuickTreeViewPrivate : public QQuickTableViewPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickTreeView)
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index 7f0faa119e..cb9fab654c 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -35,7 +35,7 @@ void QQuickViewPrivate::init(QQmlEngine* e)
// The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
// wrapper so that the garbage collector can see the policy.
QV4::ExecutionEngine *v4 = engine.data()->handle();
- QV4::QObjectWrapper::wrap(v4, contentItem);
+ QV4::QObjectWrapper::ensureWrapper(v4, contentItem);
}
}
@@ -48,12 +48,11 @@ QQuickViewPrivate::~QQuickViewPrivate()
{
}
-void QQuickViewPrivate::execute()
+QQuickViewPrivate::ExecuteState QQuickViewPrivate::executeHelper()
{
- Q_Q(QQuickView);
if (!engine) {
qWarning() << "QQuickView: invalid qml engine.";
- return;
+ return Stop;
}
if (root)
@@ -62,6 +61,14 @@ void QQuickViewPrivate::execute()
delete component;
component = nullptr;
}
+ return ExecuteState::Continue;
+}
+
+void QQuickViewPrivate::execute()
+{
+ if (executeHelper() == Stop)
+ return;
+ Q_Q(QQuickView);
if (!source.isEmpty()) {
component = new QQmlComponent(engine.data(), source, q);
if (!component->isLoading()) {
@@ -73,6 +80,22 @@ void QQuickViewPrivate::execute()
}
}
+void QQuickViewPrivate::execute(QAnyStringView uri, QAnyStringView typeName)
+{
+ if (executeHelper() == Stop)
+ return;
+ Q_Q(QQuickView);
+
+ component = new QQmlComponent(engine.data(), uri, typeName, q);
+ if (!component->isLoading()) {
+ q->continueExecute();
+ } else {
+ QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
+ q, SLOT(continueExecute()));
+ }
+
+}
+
void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
const QRectF &oldGeometry)
{
@@ -129,7 +152,7 @@ QQuickView::QQuickView(QWindow *parent)
/*!
Constructs a QQuickView with the given QML \a source and \a parent.
- The default value of \a parent is 0.
+ The default value of \a parent is \c{nullptr}.
*/
QQuickView::QQuickView(const QUrl &source, QWindow *parent)
@@ -139,6 +162,19 @@ QQuickView::QQuickView(const QUrl &source, QWindow *parent)
}
/*!
+ \since 6.7
+ Constructs a QQuickView with the element specified by \a uri and \a typeName
+ and parent \a parent.
+ The default value of \a parent is \c{nullptr}.
+ \sa loadFromModule
+ */
+QQuickView::QQuickView(QAnyStringView uri, QAnyStringView typeName, QWindow *parent)
+ : QQuickView(parent)
+{
+ loadFromModule(uri, typeName);
+}
+
+/*!
Constructs a QQuickView with the given QML \a engine and \a parent.
Note: In this case, the QQuickView does not own the given \a engine object;
@@ -203,6 +239,26 @@ void QQuickView::setSource(const QUrl& url)
}
/*!
+ \since 6.7
+ Loads the QML component identified by \a uri and \a typeName. If the component
+ is backed by a QML file, \l{source} will be set accordingly. For types defined
+ in \c{C++}, \c{source} will be empty.
+
+ If any \l{source} was set before this method was called, it will be cleared.
+
+ Calling this method multiple times with the same \a uri and \a typeName will result
+ in the QML component being reinstantiated.
+
+ \sa setSource, QQmlComponent::loadFromModule, QQmlApplicationEngine::loadFromModule
+ */
+void QQuickView::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
+{
+ Q_D(QQuickView);
+ d->source = {}; // clear URL
+ d->execute(uri, typeName);
+}
+
+/*!
Sets the initial properties \a initialProperties with which the QML
component gets initialized after calling \l QQuickView::setSource().
@@ -468,6 +524,11 @@ void QQuickView::continueExecute()
return;
}
+ // If we used loadFromModule, we might not have a URL so far.
+ // Thus, query the component to retrieve the associated URL, if any
+ if (d->source.isEmpty())
+ d->source = d->component->url();
+
if (d->setRootObject(obj.get()))
Q_UNUSED(obj.release());
emit statusChanged(status());
diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h
index aeff7cd88d..92bc495a98 100644
--- a/src/quick/items/qquickview.h
+++ b/src/quick/items/qquickview.h
@@ -26,6 +26,7 @@ public:
explicit QQuickView(QWindow *parent = nullptr);
QQuickView(QQmlEngine* engine, QWindow *parent);
explicit QQuickView(const QUrl &source, QWindow *parent = nullptr);
+ explicit QQuickView(QAnyStringView uri, QAnyStringView typeName, QWindow *parent = nullptr);
QQuickView(const QUrl &source, QQuickRenderControl *renderControl);
~QQuickView() override;
@@ -52,6 +53,7 @@ public:
public Q_SLOTS:
void setSource(const QUrl&);
+ void loadFromModule(QAnyStringView uri, QAnyStringView typeName);
void setInitialProperties(const QVariantMap &initialProperties);
void setContent(const QUrl& url, QQmlComponent *component, QObject *item);
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 9e495d9139..1ad1897af3 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -35,7 +35,7 @@ class QQmlError;
class QQuickItem;
class QQmlComponent;
-class Q_QUICK_PRIVATE_EXPORT QQuickViewPrivate : public QQuickWindowPrivate,
+class Q_QUICK_EXPORT QQuickViewPrivate : public QQuickWindowPrivate,
public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickView)
@@ -46,7 +46,10 @@ public:
QQuickViewPrivate();
~QQuickViewPrivate();
+ enum ExecuteState { Continue, Stop };
+ ExecuteState executeHelper();
void execute();
+ void execute(QAnyStringView uri, QAnyStringView typeName);
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) override;
void initResize();
void updateSize();
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 86a60fd89d..1f308c286e 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -8,10 +8,12 @@
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
#include "qquickgraphicsdevice_p.h"
+#include "qquickwindowcontainer_p.h"
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgplaintexture_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
+#include <QtQuick/private/qquickpointerhandler_p_p.h>
#include <private/qsgrenderloop_p.h>
#include <private/qsgrhisupport_p.h>
#include <private/qquickrendercontrol_p.h>
@@ -36,7 +38,7 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/private/qqmlmetatype_p.h>
-#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qquickpixmap_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
@@ -48,17 +50,17 @@
#ifndef QT_NO_DEBUG_STREAM
#include <private/qdebug_p.h>
#endif
+#include <QtCore/qpointer.h>
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
+
+#include <utility>
+#include <mutex>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
-Q_DECLARE_LOGGING_CATEGORY(lcMouse)
-Q_DECLARE_LOGGING_CATEGORY(lcTouch)
-Q_DECLARE_LOGGING_CATEGORY(lcPtr)
-Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
-Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
+Q_STATIC_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
+Q_LOGGING_CATEGORY(lcQuickWindow, "qt.quick.window")
bool QQuickWindowPrivate::defaultAlphaBuffer = false;
@@ -81,8 +83,8 @@ public:
QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
if (animationDriver) {
- connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
- connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate()));
+ connect(animationDriver, &QAnimationDriver::stopped, this, &QQuickWindowIncubationController::animationStopped);
+ connect(m_renderLoop, &QSGRenderLoop::timeToIncubate, this, &QQuickWindowIncubationController::incubate);
}
}
@@ -198,6 +200,8 @@ void QQuickWindow::showEvent(QShowEvent *)
void QQuickWindow::hideEvent(QHideEvent *)
{
Q_D(QQuickWindow);
+ if (auto da = d->deliveryAgentPrivate())
+ da->handleWindowHidden(this);
if (d->windowManager)
d->windowManager->hide(this);
}
@@ -324,6 +328,22 @@ struct PolishLoopDetector
int numPolishLoopsInSequence = 0;
};
+static const QQuickItem *firstItemWithDirtyChildrenStacking(const QQuickItem *item)
+{
+ if (QQuickItemPrivate::get(item)->dirtyAttributes
+ & QQuickItemPrivate::ChildrenStackingChanged) {
+ return item;
+ }
+
+ const auto childItems = item->childItems();
+ for (const auto *childItem : childItems) {
+ if (auto *dirtyItem = firstItemWithDirtyChildrenStacking(childItem))
+ return dirtyItem;
+ }
+
+ return nullptr;
+}
+
void QQuickWindowPrivate::polishItems()
{
// An item can trigger polish on another item, or itself for that matter,
@@ -357,6 +377,11 @@ void QQuickWindowPrivate::polishItems()
deliveryAgentPrivate()->updateFocusItemTransform();
}
#endif
+
+ if (auto *dirtyItem = firstItemWithDirtyChildrenStacking(contentItem)) {
+ qCDebug(lcQuickWindow) << dirtyItem << "has dirty child stacking order";
+ updateChildWindowStackingOrder();
+ }
}
/*!
@@ -396,6 +421,7 @@ void QQuickWindow::physicalDpiChanged()
d->lastReportedItemDevicePixelRatio = newPixelRatio;
if (d->contentItem)
updatePixelRatioHelper(d->contentItem, newPixelRatio);
+ d->forcePolish();
}
void QQuickWindow::handleFontDatabaseChanged()
@@ -404,23 +430,6 @@ void QQuickWindow::handleFontDatabaseChanged()
d->pendingFontUpdate = true;
}
-void QQuickWindow::handleScreenChanged(QScreen *screen)
-{
- Q_D(QQuickWindow);
- // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
- disconnect(d->physicalDpiChangedConnection);
- if (screen) {
- physicalDpiChanged();
- // When physical DPI changes on the same screen, either the resolution or the device pixel
- // ratio changed. We must check what it is. Device pixel ratio does not have its own
- // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
- d->physicalDpiChangedConnection = connect(screen, &QScreen::physicalDotsPerInchChanged,
- this, &QQuickWindow::physicalDpiChanged);
- }
-
- d->forcePolish();
-}
-
void forcePolishHelper(QQuickItem *item)
{
if (item->flags() & QQuickItem::ItemHasContents) {
@@ -432,6 +441,13 @@ void forcePolishHelper(QQuickItem *item)
forcePolishHelper(items.at(i));
}
+void QQuickWindow::handleScreenChanged(QScreen *screen)
+{
+ Q_D(QQuickWindow);
+ Q_UNUSED(screen);
+ d->forcePolish();
+}
+
/*!
Schedules polish events on all items in the scene.
*/
@@ -454,27 +470,37 @@ void forceUpdate(QQuickItem *item)
forceUpdate(items.at(i));
}
-void QQuickWindowRenderTarget::reset(QRhi *rhi)
+void QQuickWindowRenderTarget::reset(QRhi *rhi, ResetFlags flags)
{
- if (owns) {
- if (rhi) {
- delete renderTarget;
- delete rpDesc;
- delete texture;
- delete renderBuffer;
- delete depthStencil;
- }
+ if (rhi) {
+ if (rt.owns)
+ delete rt.renderTarget;
- delete paintDevice;
+ delete res.texture;
+ delete res.renderBuffer;
+ delete res.rpDesc;
}
- renderTarget = nullptr;
- rpDesc = nullptr;
- texture = nullptr;
- renderBuffer = nullptr;
- depthStencil = nullptr;
- paintDevice = nullptr;
- owns = false;
+ rt = {};
+ res = {};
+
+ if (!flags.testFlag(ResetFlag::KeepImplicitBuffers))
+ implicitBuffers.reset(rhi);
+
+ if (sw.owns)
+ delete sw.paintDevice;
+
+ sw = {};
+}
+
+void QQuickWindowRenderTarget::ImplicitBuffers::reset(QRhi *rhi)
+{
+ if (rhi) {
+ delete depthStencil;
+ delete depthStencilTexture;
+ delete multisampleTexture;
+ }
+ *this = {};
}
void QQuickWindowPrivate::invalidateFontData(QQuickItem *item)
@@ -491,19 +517,18 @@ void QQuickWindowPrivate::invalidateFontData(QQuickItem *item)
void QQuickWindowPrivate::ensureCustomRenderTarget()
{
// resolve() can be expensive when importing an existing native texture, so
- // it is important to only do it when the QQuickRenderTarget* was really changed
+ // it is important to only do it when the QQuickRenderTarget was really changed.
if (!redirect.renderTargetDirty)
return;
redirect.renderTargetDirty = false;
- redirect.rt.reset(rhi);
-
- // a default constructed QQuickRenderTarget means no redirection
- if (customRenderTarget.isNull())
- return;
+ redirect.rt.reset(rhi, QQuickWindowRenderTarget::ResetFlag::KeepImplicitBuffers);
- QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt);
+ if (!QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt)) {
+ qWarning("Failed to set up render target redirection for QQuickWindow");
+ redirect.rt.reset(rhi);
+ }
}
void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb)
@@ -553,13 +578,7 @@ void QQuickWindowPrivate::syncSceneGraph()
animationController->afterNodeSync();
- // Copy the current state of clearing from window into renderer.
renderer->setClearColor(clearColor);
- // Cannot skip clearing the color buffer in Qt 6 anymore.
- const QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearColorBuffer
- | QSGAbstractRenderer::ClearStencilBuffer
- | QSGAbstractRenderer::ClearDepthBuffer;
- renderer->setClearMode(mode);
renderer->setVisualizationMode(visualizationMode);
@@ -584,6 +603,30 @@ void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
emit w->afterRenderPassRecording();
}
+int QQuickWindowPrivate::multiViewCount()
+{
+ if (rhi) {
+ ensureCustomRenderTarget();
+ if (redirect.rt.rt.renderTarget)
+ return redirect.rt.rt.multiViewCount;
+ }
+
+ // Note that on QRhi level 0 and 1 are often used interchangeably, as both mean
+ // no-multiview. Here in Qt Quick let's always use 1 as the default
+ // (no-multiview), so that higher layers (effects, materials) do not need to
+ // handle both 0 and 1, only 1.
+ return 1;
+}
+
+QRhiRenderTarget *QQuickWindowPrivate::activeCustomRhiRenderTarget()
+{
+ if (rhi) {
+ ensureCustomRenderTarget();
+ return redirect.rt.rt.renderTarget;
+ }
+ return nullptr;
+}
+
void QQuickWindowPrivate::renderSceneGraph()
{
Q_Q(QQuickWindow);
@@ -597,8 +640,8 @@ void QQuickWindowPrivate::renderSceneGraph()
QRhiRenderTarget *rt;
QRhiRenderPassDescriptor *rp;
QRhiCommandBuffer *cb;
- if (redirect.rt.renderTarget) {
- rt = redirect.rt.renderTarget;
+ if (redirect.rt.rt.renderTarget) {
+ rt = redirect.rt.rt.renderTarget;
rp = rt->renderPassDescriptor();
if (!rp) {
qWarning("Custom render target is set but no renderpass descriptor has been provided.");
@@ -619,8 +662,9 @@ void QQuickWindowPrivate::renderSceneGraph()
cb = swapchain->currentFrameCommandBuffer();
}
sgRenderTarget = QSGRenderTarget(rt, rp, cb);
+ sgRenderTarget.multiViewCount = multiViewCount();
} else {
- sgRenderTarget = QSGRenderTarget(redirect.rt.paintDevice);
+ sgRenderTarget = QSGRenderTarget(redirect.rt.sw.paintDevice);
}
context->beginNextFrame(renderer,
@@ -633,19 +677,12 @@ void QQuickWindowPrivate::renderSceneGraph()
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
- QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
- bool flipY = rhi ? !rhi->isYUpInNDC() : false;
- if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically())
- flipY = !flipY;
- if (flipY)
- matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
-
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
QSize pixelSize;
- if (redirect.rt.renderTarget)
- pixelSize = redirect.rt.renderTarget->pixelSize();
- else if (redirect.rt.paintDevice)
- pixelSize = QSize(redirect.rt.paintDevice->width(), redirect.rt.paintDevice->height());
+ if (redirect.rt.rt.renderTarget)
+ pixelSize = redirect.rt.rt.renderTarget->pixelSize();
+ else if (redirect.rt.sw.paintDevice)
+ pixelSize = QSize(redirect.rt.sw.paintDevice->width(), redirect.rt.sw.paintDevice->height());
else if (rhi)
pixelSize = swapchain->currentPixelSize();
else // software or other backend
@@ -654,7 +691,16 @@ void QQuickWindowPrivate::renderSceneGraph()
renderer->setDevicePixelRatio(devicePixelRatio);
renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize));
renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize));
- renderer->setProjectionMatrixToRect(QRectF(QPointF(0, 0), pixelSize / devicePixelRatio), matrixFlags);
+
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
+ bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically())
+ flipY = !flipY;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+
+ const QRectF rect(QPointF(0, 0), pixelSize / devicePixelRatio);
+ renderer->setProjectionMatrixToRect(rect, matrixFlags, rhi && !rhi->isYUpInNDC());
context->renderNextFrame(renderer);
@@ -682,7 +728,6 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, clearColor(Qt::white)
, persistentGraphics(true)
, persistentSceneGraph(true)
- , componentCompleted(true)
, inDestructor(false)
, incubationController(nullptr)
, hasActiveSwapchain(false)
@@ -701,6 +746,24 @@ QQuickWindowPrivate::~QQuickWindowPrivate()
deliveryAgent = nullptr;
}
+void QQuickWindowPrivate::setPalette(QQuickPalette* palette)
+{
+ if (windowPaletteRef == palette)
+ return;
+
+ if (windowPaletteRef)
+ disconnect(windowPaletteRef, &QQuickPalette::changed, this, &QQuickWindowPrivate::updateWindowPalette);
+ windowPaletteRef = palette;
+ updateWindowPalette();
+ if (windowPaletteRef)
+ connect(windowPaletteRef, &QQuickPalette::changed, this, &QQuickWindowPrivate::updateWindowPalette);
+}
+
+void QQuickWindowPrivate::updateWindowPalette()
+{
+ QQuickPaletteProviderPrivateBase::setPalette(windowPaletteRef);
+}
+
void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette)
{
Q_Q(QQuickWindow);
@@ -744,12 +807,8 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
q,
&QQuickWindow::handleFontDatabaseChanged);
- if (QScreen *screen = q->screen()) {
+ if (q->screen()) {
lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio();
- // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
- // and connects to the new screen
- physicalDpiChangedConnection = QObject::connect(screen, &QScreen::physicalDotsPerInchChanged,
- q, &QQuickWindow::physicalDpiChanged);
}
QSGContext *sg;
@@ -772,15 +831,14 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
animationController.reset(new QQuickAnimatorController(q));
- QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
- QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
- QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::initialized, q, &QQuickWindow::sceneGraphInitialized, Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::invalidated, q, &QQuickWindow::sceneGraphInvalidated, Qt::DirectConnection);
+ QObject::connect(context, &QSGRenderContext::invalidated, q, &QQuickWindow::cleanupSceneGraph, Qt::DirectConnection);
- QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged()));
- QObject::connect(q, SIGNAL(screenChanged(QScreen*)), q, SLOT(handleScreenChanged(QScreen*)));
- QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
- q, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
- QObject::connect(q, SIGNAL(frameSwapped()), q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
+ QObject::connect(q, &QQuickWindow::focusObjectChanged, q, &QQuickWindow::activeFocusItemChanged);
+ QObject::connect(q, &QQuickWindow::screenChanged, q, &QQuickWindow::handleScreenChanged);
+ QObject::connect(qApp, &QGuiApplication::applicationStateChanged, q, &QQuickWindow::handleApplicationStateChanged);
+ QObject::connect(q, &QQuickWindow::frameSwapped, q, &QQuickWindow::runJobsAfterSwap, Qt::DirectConnection);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->addWindow(q);
@@ -861,13 +919,15 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
window for use with \c {QtQuick} graphical types.
- A Window can be declared inside an Item or inside another Window; in that
+ A Window can be declared inside an Item or inside another Window, in which
case the inner Window will automatically become "transient for" the outer
- Window: that is, most platforms will show it centered upon the outer window
- by default, and there may be other platform-dependent behaviors, depending
- also on the \l flags. If the nested window is intended to be a dialog in
- your application, you should also set \l flags to Qt.Dialog, because some
- window managers will not provide the centering behavior without that flag.
+ Window, with the outer Window as its \l transientParent. Most platforms will
+ show the Window centered upon the outer window in this case, and there may be
+ other platform-dependent behaviors, depending also on the \l flags. If the nested
+ window is intended to be a dialog in your application, you should also set \l flags
+ to \c Qt.Dialog, because some window managers will not provide the centering behavior
+ without that flag.
+
You can also declare multiple windows inside a top-level \l QtObject, in which
case the windows will have no transient relationship.
@@ -891,6 +951,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.
*/
/*!
@@ -1121,18 +1197,15 @@ QQuickWindow::~QQuickWindow()
delete root;
d->deliveryAgent = nullptr; // avoid forwarding events there during destruction
- d->renderJobMutex.lock();
- qDeleteAll(d->beforeSynchronizingJobs);
- d->beforeSynchronizingJobs.clear();
- qDeleteAll(d->afterSynchronizingJobs);
- d->afterSynchronizingJobs.clear();
- qDeleteAll(d->beforeRenderingJobs);
- d->beforeRenderingJobs.clear();
- qDeleteAll(d->afterRenderingJobs);
- d->afterRenderingJobs.clear();
- qDeleteAll(d->afterSwapJobs);
- d->afterSwapJobs.clear();
- d->renderJobMutex.unlock();
+
+ {
+ const std::lock_guard locker(d->renderJobMutex);
+ qDeleteAll(std::exchange(d->beforeSynchronizingJobs, {}));
+ qDeleteAll(std::exchange(d->afterSynchronizingJobs, {}));
+ qDeleteAll(std::exchange(d->beforeRenderingJobs, {}));
+ qDeleteAll(std::exchange(d->afterRenderingJobs, {}));;
+ qDeleteAll(std::exchange(d->afterSwapJobs, {}));
+ }
// It is important that the pixmap cache is cleaned up during shutdown.
// Besides playing nice, this also solves a practical problem that
@@ -1336,6 +1409,41 @@ QObject *QQuickWindow::focusObject() const
return const_cast<QQuickWindow*>(this);
}
+/*!
+ \internal
+
+ Clears all exclusive and passive grabs for the points in \a pointerEvent.
+
+ We never allow any kind of grab to persist after release, unless we're waiting
+ for a synth event from QtGui (as with most tablet events), so for points that
+ are fully released, the grab is cleared.
+
+ Called when QQuickWindow::event dispatches events, or when the QQuickOverlay
+ has filtered an event so that it bypasses normal delivery.
+*/
+void QQuickWindowPrivate::clearGrabbers(QPointerEvent *pointerEvent)
+{
+ if (pointerEvent->isEndEvent()
+ && !(QQuickDeliveryAgentPrivate::isTabletEvent(pointerEvent)
+ && (qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents)
+ || QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) {
+ if (pointerEvent->isSinglePointEvent()) {
+ if (static_cast<QSinglePointEvent *>(pointerEvent)->buttons() == Qt::NoButton) {
+ auto &firstPt = pointerEvent->point(0);
+ pointerEvent->setExclusiveGrabber(firstPt, nullptr);
+ pointerEvent->clearPassiveGrabbers(firstPt);
+ }
+ } else {
+ for (auto &point : pointerEvent->points()) {
+ if (point.state() == QEventPoint::State::Released) {
+ pointerEvent->setExclusiveGrabber(point, nullptr);
+ pointerEvent->clearPassiveGrabbers(point);
+ }
+ }
+ }
+ }
+}
+
/*! \reimp */
bool QQuickWindow::event(QEvent *event)
{
@@ -1478,26 +1586,7 @@ bool QQuickWindow::event(QEvent *event)
// or fix QTBUG-90851 so that the event always has points?
bool ret = (da && da->event(event));
- // failsafe: never allow any kind of grab to persist after release,
- // unless we're waiting for a synth event from QtGui (as with most tablet events)
- if (pe->isEndEvent() && !(QQuickDeliveryAgentPrivate::isTabletEvent(pe) &&
- (qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents) ||
- QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) {
- if (pe->isSinglePointEvent()) {
- if (static_cast<QSinglePointEvent *>(pe)->buttons() == Qt::NoButton) {
- auto &firstPt = pe->point(0);
- pe->setExclusiveGrabber(firstPt, nullptr);
- pe->clearPassiveGrabbers(firstPt);
- }
- } else {
- for (auto &point : pe->points()) {
- if (point.state() == QEventPoint::State::Released) {
- pe->setExclusiveGrabber(point, nullptr);
- pe->clearPassiveGrabbers(point);
- }
- }
- }
- }
+ d->clearGrabbers(pe);
if (ret)
return true;
@@ -1553,6 +1642,27 @@ bool QQuickWindow::event(QEvent *event)
d->inheritPalette(QGuiApplication::palette());
if (d->contentItem)
QCoreApplication::sendEvent(d->contentItem, event);
+ break;
+ case QEvent::DevicePixelRatioChange:
+ physicalDpiChanged();
+ break;
+ case QEvent::ChildWindowAdded: {
+ auto *childEvent = static_cast<QChildWindowEvent*>(event);
+ auto *childWindow = childEvent->child();
+ qCDebug(lcQuickWindow) << "Child window" << childWindow << "added to" << this;
+ if (childWindow->handle()) {
+ // The reparenting has already resulted in the native window
+ // being added to its parent, on top of all other windows. We need
+ // to do a synchronous re-stacking of the windows here, to avoid
+ // leaving the window in the wrong position while waiting for the
+ // asynchronous callback to QQuickWindow::polishItems().
+ d->updateChildWindowStackingOrder();
+ } else {
+ qCDebug(lcQuickWindow) << "No platform window yet."
+ << "Deferring child window stacking until surface creation";
+ }
+ break;
+ }
default:
break;
}
@@ -1568,6 +1678,35 @@ bool QQuickWindow::event(QEvent *event)
return QWindow::event(event);
}
+void QQuickWindowPrivate::updateChildWindowStackingOrder(QQuickItem *item)
+{
+ Q_Q(QQuickWindow);
+
+ if (!item) {
+ qCDebug(lcQuickWindow) << "Updating child window stacking order for" << q;
+ item = contentItem;
+ }
+ auto *itemPrivate = QQuickItemPrivate::get(item);
+ const auto paintOrderChildItems = itemPrivate->paintOrderChildItems();
+ for (auto *child : paintOrderChildItems) {
+ if (auto *windowContainer = qobject_cast<QQuickWindowContainer*>(child)) {
+ auto *window = windowContainer->containedWindow();
+ if (!window) {
+ qCDebug(lcQuickWindow) << windowContainer << "has no contained window yet";
+ continue;
+ }
+ if (window->parent() != q) {
+ qCDebug(lcQuickWindow) << window << "is not yet child of this window";
+ continue;
+ }
+ qCDebug(lcQuickWindow) << "Raising" << window << "owned by" << windowContainer;
+ window->raise();
+ }
+
+ updateChildWindowStackingOrder(child);
+ }
+}
+
/*! \reimp */
void QQuickWindow::keyPressEvent(QKeyEvent *e)
{
@@ -1664,11 +1803,14 @@ void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *root
if (!rootItem)
rootItem = contentItem;
auto cursorItemAndHandler = findCursorItemAndHandler(rootItem, scenePos);
- if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
+ if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second ||
+ (cursorItemAndHandler.second && QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty)) {
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(q);
QWindow *window = renderWindow ? renderWindow : q;
cursorItem = cursorItemAndHandler.first;
cursorHandler = cursorItemAndHandler.second;
+ if (cursorHandler)
+ QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty = false;
if (cursorItem) {
const auto cursor = QQuickItemPrivate::get(cursorItem)->effectiveCursor(cursorHandler);
qCDebug(lcHoverTrace) << "setting cursor" << cursor << "from" << cursorHandler << "or" << cursorItem;
@@ -1722,6 +1864,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
@@ -1751,10 +1928,6 @@ void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObje
if (!o)
return;
QQuickWindow *that = static_cast<QQuickWindow *>(property->object);
- if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(o)) {
- qCDebug(lcTransient) << window << "is transient for" << that;
- window->setTransientParent(that);
- }
QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(that->contentItem())->data();
itemProperty.append(&itemProperty, o);
}
@@ -1814,9 +1987,7 @@ void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName,
void QQuickWindowPrivate::cleanupNodes()
{
- for (int ii = 0; ii < cleanupNodeList.size(); ++ii)
- delete cleanupNodeList.at(ii);
- cleanupNodeList.clear();
+ qDeleteAll(std::exchange(cleanupNodeList, {}));
}
void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
@@ -1963,19 +2134,14 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
itemPriv->itemNode()->setMatrix(matrix);
}
- bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
- ((item->clip() == false) != (itemPriv->clipNode() == nullptr));
- int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
- bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
- ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
-
+ const bool clipEffectivelyChanged = dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window);
if (clipEffectivelyChanged) {
- QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
- (QSGNode *) itemPriv->itemNode();
+ QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *)itemPriv->opacityNode()
+ : (QSGNode *)itemPriv->itemNode();
QSGNode *child = itemPriv->rootNode();
- if (item->clip()) {
- Q_ASSERT(itemPriv->clipNode() == nullptr);
+ if (bool initializeClipNode = item->clip() && itemPriv->clipNode() == nullptr;
+ initializeClipNode) {
QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect());
itemPriv->extra.value().clipNode = clip;
clip->update();
@@ -1989,9 +2155,14 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
parent->appendChildNode(clip);
}
- } else {
+ } else if (bool updateClipNode = item->clip() && itemPriv->clipNode() != nullptr;
+ updateClipNode) {
+ QQuickDefaultClipNode *clip = itemPriv->clipNode();
+ clip->setClipRect(item->clipRect());
+ clip->update();
+ } else if (bool removeClipNode = !item->clip() && itemPriv->clipNode() != nullptr;
+ removeClipNode) {
QQuickDefaultClipNode *clip = itemPriv->clipNode();
- Q_ASSERT(clip);
parent->removeChildNode(clip);
if (child) {
clip->removeChildNode(child);
@@ -2005,6 +2176,10 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
}
}
+ const int effectRefCount = itemPriv->extra.isAllocated() ? itemPriv->extra->effectRefCount : 0;
+ const bool effectRefEffectivelyChanged =
+ (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window))
+ && ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
if (effectRefEffectivelyChanged) {
if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
itemPriv->childContainerNode()->removeAllChildNodes();
@@ -2216,14 +2391,6 @@ void QQuickWindow::cleanupSceneGraph()
d->runAndClearJobs(&d->afterSwapJobs);
}
-void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
-{
- qCDebug(lcTransient) << this << "is transient for" << window;
- setTransientParent(window);
- disconnect(sender(), SIGNAL(windowChanged(QQuickWindow*)),
- this, SLOT(setTransientParent_helper(QQuickWindow*)));
-}
-
QOpenGLContext *QQuickWindowPrivate::openglContext()
{
#if QT_CONFIG(opengl)
@@ -2258,7 +2425,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::frameSwapped()
+ \qmlsignal QtQuick::Window::frameSwapped()
This signal is emitted when a frame has been queued for presenting. With
vertical synchronization enabled the signal is emitted at most once per
@@ -2274,7 +2441,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphInitialized()
+ \qmlsignal QtQuick::Window::sceneGraphInitialized()
\internal
*/
@@ -2296,7 +2463,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphInvalidated()
+ \qmlsignal QtQuick::Window::sceneGraphInvalidated()
\internal
*/
@@ -2316,7 +2483,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphError(SceneGraphError error, QString message)
+ \qmlsignal QtQuick::Window::sceneGraphError(SceneGraphError error, QString message)
This signal is emitted when an \a error occurred during scene graph initialization.
@@ -2373,7 +2540,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::closing(CloseEvent close)
+ \qmlsignal QtQuick::Window::closing(CloseEvent close)
\since 5.1
This signal is emitted when the user tries to close the window.
@@ -2653,9 +2820,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
text. Using such features in combination with the NativeTextRendering
render type will lend poor and sometimes pixelated results.
- \value QtTextRendering Use Qt's own rasterization algorithm.
+ Both \c QtTextRendering and \c CurveTextRendering are hardware-accelerated techniques.
+ \c QtTextRendering is the faster of the two, but uses more memory and will exhibit rendering
+ artifacts at large sizes. \c CurveTextRendering should be considered as an alternative in cases
+ where \c QtTextRendering does not give good visual results or where reducing graphics memory
+ consumption is a priority.
+ \value QtTextRendering Use Qt's own rasterization algorithm.
\value NativeTextRendering Use the operating system's native rasterizer for text.
+ \value CurveTextRendering Text is rendered using a curve rasterizer running directly on
+ the graphics hardware. (Introduced in Qt 6.7.0.)
*/
/*!
@@ -2685,7 +2859,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeSynchronizing()
+ \qmlsignal QtQuick::Window::beforeSynchronizing()
\internal
*/
@@ -2712,7 +2886,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterSynchronizing()
+ \qmlsignal QtQuick::Window::afterSynchronizing()
\internal
\since 5.3
*/
@@ -2732,23 +2906,23 @@ QQmlIncubationController *QQuickWindow::incubationController() const
to this signal is still important if the recording of copy type of commands
is desired since those cannot be enqueued within a render pass.
- When using OpenGL, the QOpenGLContext used for rendering by the scene graph
- will be bound at this point.
-
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
- states and leaving these enabled or set to non-default values when returning
- from the connected slot can interfere with the scene graph's rendering.
+ \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when
+ returning from the connected slot can interfere with the scene graph's
+ rendering. The QOpenGLContext used for rendering by the scene graph will be
+ bound when the signal is emitted.
- \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
- {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+ \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph -
+ OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan
+ Under QML}, {Scene Graph - Direct3D 11 Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeRendering()
+ \qmlsignal QtQuick::Window::beforeRendering()
\internal
*/
@@ -2767,23 +2941,23 @@ QQmlIncubationController *QQuickWindow::incubationController() const
and afterRenderPassRecording(), that is typically used to achieve under- or
overlaying of the custom rendering.
- When using OpenGL, the QOpenGLContext used for rendering by the scene graph
- will be bound at this point.
-
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
- states and leaving these enabled or set to non-default values when returning
- from the connected slot can interfere with the scene graph's rendering.
+ \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when
+ returning from the connected slot can interfere with the scene graph's
+ rendering. The QOpenGLContext used for rendering by the scene graph will be
+ bound when the signal is emitted.
- \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
- {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+ \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph -
+ OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan
+ Under QML}, {Scene Graph - Direct3D 11 Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterRendering()
+ \qmlsignal QtQuick::Window::afterRendering()
\internal
*/
@@ -2813,10 +2987,12 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\sa rendererInterface()
\since 5.14
+
+ \sa {Scene Graph - RHI Under QML}
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeRenderPassRecording()
+ \qmlsignal QtQuick::Window::beforeRenderPassRecording()
\internal
\since 5.14
*/
@@ -2846,6 +3022,8 @@ QQmlIncubationController *QQuickWindow::incubationController() const
\sa rendererInterface()
\since 5.14
+
+ \sa {Scene Graph - RHI Under QML}
*/
/*!
@@ -2871,7 +3049,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::beforeFrameBegin()
+ \qmlsignal QtQuick::Window::beforeFrameBegin()
\internal
*/
@@ -2896,12 +3074,12 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterFrameEnd()
+ \qmlsignal QtQuick::Window::afterFrameEnd()
\internal
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterRenderPassRecording()
+ \qmlsignal QtQuick::Window::afterRenderPassRecording()
\internal
\since 5.14
*/
@@ -2921,7 +3099,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::afterAnimating()
+ \qmlsignal QtQuick::Window::afterAnimating()
This signal is emitted on the GUI thread before requesting the render thread to
perform the synchronization of the scene graph.
@@ -2955,7 +3133,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
*/
/*!
- \qmlsignal QtQuick.Window::Window::sceneGraphAboutToStop()
+ \qmlsignal QtQuick::Window::sceneGraphAboutToStop()
\internal
\since 5.3
*/
@@ -2989,9 +3167,12 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
textures will in most cases be faster to render. When this flag is not set,
the texture will have an alpha channel based on the image's format.
- When \a options contains TextureHasMipmaps, the engine will create a
- texture which can use mipmap filtering. Mipmapped textures can not be in
- an atlas.
+ When \a options contains TextureHasMipmaps, the engine will create a texture
+ which can use mipmap filtering. Mipmapped textures can not be in an atlas.
+
+ Setting TextureHasAlphaChannel in \a options serves no purpose for this
+ function since assuming an alpha channel and blending is the default. To opt
+ out, set TextureIsOpaque.
When the scene graph uses OpenGL, the returned texture will be using \c
GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. With
@@ -3002,12 +3183,12 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
initialized.
\warning The returned texture is not memory managed by the scene graph and
- must be explicitly deleted by the caller on the rendering thread.
- This is achieved by deleting the texture from a QSGNode destructor
- or by using deleteLater() in the case where the texture already has affinity
- to the rendering thread.
+ must be explicitly deleted by the caller on the rendering thread. This is
+ achieved by deleting the texture from a QSGNode destructor or by using
+ deleteLater() in the case where the texture already has affinity to the
+ rendering thread.
- This function can be called from any thread.
+ This function can be called from both the main and the render thread.
\sa sceneGraphInitialized(), QSGTexture
*/
@@ -3024,8 +3205,58 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
return d->context->createTexture(image, flags);
}
+/*!
+ Creates a new QSGTexture from the supplied \a texture.
+
+ Use \a options to customize the texture attributes. Only the
+ TextureHasAlphaChannel flag is taken into account by this function. When
+ set, the resulting QSGTexture is always treated by the scene graph renderer
+ as needing blending. For textures that are fully opaque, not setting the
+ flag can save the cost of performing alpha blending during rendering. The
+ flag has no direct correspondence to the \l{QRhiTexture::format()}{format}
+ of the QRhiTexture, i.e. not setting the flag while having a texture format
+ such as the commonly used \l QRhiTexture::RGBA8 is perfectly normal.
+
+ Mipmapping is not controlled by \a options since \a texture is already
+ created and has the presence or lack of mipmaps baked in.
+
+ The returned QSGTexture owns the QRhiTexture, meaning \a texture is
+ destroyed together with the returned QSGTexture.
+
+ If \a texture owns its underlying native graphics resources (OpenGL texture
+ object, Vulkan image, etc.), that depends on how the QRhiTexture was created
+ (\l{QRhiTexture::create()} or \l{QRhiTexture::createFrom()}), and that is
+ not controlled or changed by this function.
+
+ \note This is only functional when the scene graph has already initialized
+ and is using the default, \l{QRhi}-based \l{Scene Graph
+ Adaptations}{adaptation}. The return value is \nullptr otherwise.
+
+ \note This function can only be called on the scene graph render thread.
+
+ \since 6.6
+
+ \sa createTextureFromImage(), sceneGraphInitialized(), QSGTexture
+ */
+QSGTexture *QQuickWindow::createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options) const
+{
+ Q_D(const QQuickWindow);
+ if (!d->rhi)
+ return nullptr;
+
+ QSGPlainTexture *t = new QSGPlainTexture;
+ t->setOwnsTexture(true);
+ t->setTexture(texture);
+ t->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel);
+ t->setTextureSize(texture->pixelSize());
+ return t;
+}
+
+// Legacy, private alternative to createTextureFromRhiTexture() that internally
+// creates a QRhiTexture wrapping the existing native graphics resource.
+// New code should prefer using the public API.
QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
@@ -3035,7 +3266,7 @@ QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeOb
return nullptr;
QSGPlainTexture *texture = new QSGPlainTexture;
- texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayout, nativeFormat,
+ texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayoutOrState, nativeFormat,
size, options, flags);
texture->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel);
// note that the QRhiTexture does not (and cannot) own the native object
@@ -3050,6 +3281,10 @@ QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeOb
The background color for the window.
Setting this property is more efficient than using a separate Rectangle.
+
+ \note If you set the color to \c "transparent" or to a color with alpha translucency,
+ you should also set suitable \l flags such as \c {flags: Qt.FramelessWindowHint}.
+ Otherwise, window translucency may not be enabled consistently on all platforms.
*/
/*!
@@ -3136,7 +3371,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
buffering of resources, such as buffers, is up to the graphics API client
to manage. Most commonly, a uniform buffer where the data changes between
frames cannot simply change its contents when submitting a frame, given
- that that frame may still be active ("in flight") when starting to record
+ that the frame may still be active ("in flight") when starting to record
the next frame. To avoid stalling the pipeline, one way is to have multiple
buffers (and memory allocations) under the hood, thus realizing at least a
double buffered scheme for such resources.
@@ -3302,10 +3537,12 @@ void QQuickWindow::endExternalCommands()
whether it's a dialog, popup, or a regular window, and whether it should
have a title bar, etc.
- The flags which you read from this property might differ from the ones
+ The flags that you read from this property might differ from the ones
that you set if the requested flags could not be fulfilled.
- \sa Qt::WindowFlags
+ \snippet qml/splashWindow.qml entire
+
+ \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen}
*/
/*!
@@ -3336,6 +3573,11 @@ void QQuickWindow::endExternalCommands()
The (x,y) position is relative to the \l Screen if there is only one,
or to the virtual desktop (arrangement of multiple screens).
+ \note Not all windowing systems support setting or querying top level
+ window positions. On such a system, programmatically moving windows
+ may not have any effect, and artificial values may be returned for
+ the current positions, such as \c QPoint(0, 0).
+
\qml
Window { x: 100; y: 100; width: 100; height: 100 }
\endqml
@@ -3372,10 +3614,13 @@ void QQuickWindow::endExternalCommands()
Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}.
+ The default value is \c false, unless overridden by setting \l visibility.
+
\sa visibility
*/
/*!
+ \keyword qml-window-visibility-prop
\qmlproperty QWindow::Visibility Window::visibility
The screen-occupation state of the window.
@@ -3389,15 +3634,18 @@ void QQuickWindow::endExternalCommands()
visibility property you will always get the actual state, never
\c AutomaticVisibility.
- When a window is not visible its visibility is Hidden, and setting
+ When a window is not visible, its visibility is \c Hidden, and setting
visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
- \sa visible
+ \snippet qml/windowVisibility.qml entire
+
+ \sa visible, {Qt Quick Examples - Window and Screen}
\since 5.1
*/
/*!
\qmlattachedproperty QWindow::Visibility Window::visibility
+ \readonly
\since 5.4
This attached property holds whether the window is currently shown
@@ -3405,7 +3653,7 @@ void QQuickWindow::endExternalCommands()
hidden. The \c Window attached property can be attached to any Item. If the
item is not shown in any window, the value will be \l {QWindow::}{Hidden}.
- \sa visible, visibility
+ \sa visible, {qml-window-visibility-prop}{visibility}
*/
/*!
@@ -3486,33 +3734,32 @@ void QQuickWindow::endExternalCommands()
shown, that minimizing the parent window will also minimize the transient
window, and so on; however results vary somewhat from platform to platform.
- Normally if you declare a Window inside an Item or inside another Window,
- this relationship is deduced automatically. In that case, if you declare
- this window's \l visible property \c true, it will not actually be shown
- until the \c transientParent window is shown.
-
- However if you set this property, then Qt Quick will no longer wait until
- the \c transientParent window is shown before showing this window. If you
- want to to be able to show a transient window independently of the "parent"
- Item or Window within which it was declared, you can remove that
- relationship by setting \c transientParent to \c null:
-
- \qml
- import QtQuick.Window 2.13
-
- Window {
- // visible is false by default
- Window {
- transientParent: null
- visible: true
- }
- }
- \endqml
+ Declaring a Window inside an Item or another Window, either via the
+ \l{Window::data}{default property} or a dedicated property, will automatically
+ set up a transient parent relationship to the containing window,
+ unless the \l transientParent property is explicitly set. This applies
+ when creating Window items via \l [QML] {QtQml::Qt::createComponent()}
+ {Qt.createComponent} or \l [QML] {QtQml::Qt::createQmlObject()}
+ {Qt.createQmlObject} as well, as long as an Item or Window is passed
+ as the \c parent argument.
+
+ A Window with a transient parent will not be shown until its transient
+ parent is shown, even if the \l visible property is \c true. This also
+ applies for the automatic transient parent relationship described above.
+ In particular, if the Window's containing element is an Item, the window
+ will not be shown until the containing item is added to a scene, via its
+ \l{Concepts - Visual Parent in Qt Quick}{visual parent hierarchy}. Setting
+ the \l transientParent to \c null will override this behavior:
+
+ \snippet qml/nestedWindowTransientParent.qml 0
+ \snippet qml/nestedWindowTransientParent.qml 1
In order to cause the window to be centered above its transient parent by
default, depending on the window manager, it may also be necessary to set
the \l Window::flags property with a suitable \l Qt::WindowType (such as
\c Qt::Dialog).
+
+ \sa QtQuick::Window::parent
*/
/*!
@@ -3553,6 +3800,9 @@ void QQuickWindow::endExternalCommands()
The active status of the window.
+ \snippet qml/windowPalette.qml declaration-and-color
+ \snippet qml/windowPalette.qml closing-brace
+
\sa requestActivate()
*/
@@ -3566,14 +3816,7 @@ void QQuickWindow::endExternalCommands()
Here is an example which changes a label to show the active state of the
window in which it is shown:
- \qml
- import QtQuick 2.4
- import QtQuick.Window 2.2
-
- Text {
- text: Window.active ? "active" : "inactive"
- }
- \endqml
+ \snippet qml/windowActiveAttached.qml entire
*/
/*!
@@ -3838,6 +4081,48 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
}
/*!
+ \return the QRhi object used by this window for rendering.
+
+ Available only when the window is using Qt's 3D API and shading language
+ abstractions, meaning the result is always null when using the \c software
+ adaptation.
+
+ The result is valid only when rendering has been initialized, which is
+ indicated by the emission of the sceneGraphInitialized() signal. Before
+ that point, the returned value is null. With a regular, on-screen
+ QQuickWindow scenegraph initialization typically happens when the native
+ window gets exposed (shown) the first time. When using QQuickRenderControl,
+ initialization is done in the explicit
+ \l{QQuickRenderControl::initialize()}{initialize()} call.
+
+ In practice this function is a shortcut to querying the QRhi via the
+ QSGRendererInterface.
+
+ \since 6.6
+ */
+QRhi *QQuickWindow::rhi() const
+{
+ Q_D(const QQuickWindow);
+ return d->rhi;
+}
+
+/*!
+ \return the QRhiSwapChain used by this window, if there is one.
+
+ \note Only on-screen windows backed by one of the standard render loops
+ (such as, \c basic or \c threaded) will have a swapchain. Otherwise the
+ returned value is null. For example, the result is always null when the
+ window is used with QQuickRenderControl.
+
+ \since 6.6
+ */
+QRhiSwapChain *QQuickWindow::swapChain() const
+{
+ Q_D(const QQuickWindow);
+ return d->swapchain;
+}
+
+/*!
Requests the specified graphics \a api.
When the built-in, default graphics adaptation is used, \a api specifies
@@ -4114,6 +4399,18 @@ QQuickGraphicsConfiguration QQuickWindow::graphicsConfiguration() const
}
/*!
+ Creates a text node. When the scenegraph is not initialized, the return value is null.
+
+ \since 6.7
+ \sa QSGTextNode
+ */
+QSGTextNode *QQuickWindow::createTextNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createTextNode(d->context) : nullptr;
+}
+
+/*!
Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
@@ -4191,10 +4488,11 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
palette which serves as a default for all application windows. You can also set the default palette
for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML.
- ApplicationWindow propagates explicit palette properties to child controls. If you change a specific
- property on the window's palette, that property propagates to all child controls in the window,
+ Window propagates explicit palette properties to child items and controls,
overriding any system defaults for that property.
+ \snippet qml/windowPalette.qml entire
+
\sa Item::palette, Popup::palette, ColorGroup, SystemPalette
//! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
*/
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 3f3c82a4b8..17c4bb6fd4 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -33,6 +33,10 @@ class QQuickPalette;
class QQuickRenderTarget;
class QQuickGraphicsDevice;
class QQuickGraphicsConfiguration;
+class QRhi;
+class QRhiSwapChain;
+class QRhiTexture;
+class QSGTextNode;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -78,7 +82,8 @@ public:
enum TextRenderType {
QtTextRendering,
- NativeTextRendering
+ NativeTextRendering,
+ CurveTextRendering
};
Q_ENUM(TextRenderType)
@@ -115,6 +120,7 @@ public:
// Scene graph specific functions
QSGTexture *createTextureFromImage(const QImage &image) const;
QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options) const;
+ QSGTexture *createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options = {}) const;
void setColor(const QColor &color);
QColor color() const;
@@ -151,10 +157,14 @@ public:
QSGRectangleNode *createRectangleNode() const;
QSGImageNode *createImageNode() const;
QSGNinePatchNode *createNinePatchNode() const;
+ QSGTextNode *createTextNode() const;
static TextRenderType textRenderType();
static void setTextRenderType(TextRenderType renderType);
+ QRhi *rhi() const;
+ QRhiSwapChain *swapChain() const;
+
Q_SIGNALS:
void frameSwapped();
void sceneGraphInitialized();
@@ -220,7 +230,6 @@ private Q_SLOTS:
void cleanupSceneGraph();
void physicalDpiChanged();
void handleScreenChanged(QScreen *screen);
- void setTransientParent_helper(QQuickWindow *window);
void runJobsAfterSwap();
void handleApplicationStateChanged(Qt::ApplicationState state);
void handleFontDatabaseChanged();
@@ -234,6 +243,7 @@ private:
#endif
friend class QQuickItem;
+ friend class QQuickItemPrivate;
friend class QQuickWidget;
friend class QQuickRenderControl;
friend class QQuickAnimatorController;
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 57020eaa3e..8ba4e56515 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -56,8 +56,10 @@ class QRhiRenderBuffer;
class QRhiRenderPassDescriptor;
class QRhiTexture;
+Q_DECLARE_LOGGING_CATEGORY(lcQuickWindow)
+
//Make it easy to identify and customize the root item if needed
-class Q_QUICK_PRIVATE_EXPORT QQuickRootItem : public QQuickItem
+class Q_QUICK_EXPORT QQuickRootItem : public QQuickItem
{
Q_OBJECT
QML_ANONYMOUS
@@ -70,20 +72,39 @@ public Q_SLOTS:
void setHeight(int h) {QQuickItem::setHeight(qreal(h));}
};
-class QQuickWindowRenderTarget
+struct QQuickWindowRenderTarget
{
-public:
- void reset(QRhi *rhi);
- QRhiRenderTarget *renderTarget = nullptr;
- QRhiRenderPassDescriptor *rpDesc = nullptr;
- QRhiTexture *texture = nullptr;
- QRhiRenderBuffer *renderBuffer = nullptr;
- QRhiRenderBuffer *depthStencil = nullptr;
- QPaintDevice *paintDevice = nullptr;
- bool owns = false;
+ enum class ResetFlag {
+ KeepImplicitBuffers = 0x01
+ };
+ Q_DECLARE_FLAGS(ResetFlags, ResetFlag)
+ void reset(QRhi *rhi, ResetFlags flags = {});
+
+ struct {
+ QRhiRenderTarget *renderTarget = nullptr;
+ bool owns = false;
+ int multiViewCount = 1;
+ } rt;
+ struct {
+ QRhiTexture *texture = nullptr;
+ QRhiRenderBuffer *renderBuffer = nullptr;
+ QRhiRenderPassDescriptor *rpDesc = nullptr;
+ } res;
+ struct ImplicitBuffers {
+ QRhiRenderBuffer *depthStencil = nullptr;
+ QRhiTexture *depthStencilTexture = nullptr;
+ QRhiTexture *multisampleTexture = nullptr;
+ void reset(QRhi *rhi);
+ } implicitBuffers;
+ struct {
+ QPaintDevice *paintDevice = nullptr;
+ bool owns = false;
+ } sw;
};
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowPrivate
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowRenderTarget::ResetFlags)
+
+class Q_QUICK_EXPORT QQuickWindowPrivate
: public QWindowPrivate
, public QQuickPaletteProviderPrivateBase<QQuickWindow, QQuickWindowPrivate>
{
@@ -101,6 +122,8 @@ public:
QQuickWindowPrivate();
~QQuickWindowPrivate() override;
+ void setPalette(QQuickPalette *p) override;
+ void updateWindowPalette();
void updateChildrenPalettes(const QPalette &parentPalette) override;
void init(QQuickWindow *, QQuickRenderControl *control = nullptr);
@@ -123,6 +146,7 @@ public:
#endif
void clearFocusObject() override;
+ void setFocusToTarget(FocusTarget, Qt::FocusReason) override;
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
@@ -146,17 +170,17 @@ public:
Q_DECLARE_FLAGS(TextureFromNativeTextureFlags, TextureFromNativeTextureFlag)
QSGTexture *createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
uint nativeFormat,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
TextureFromNativeTextureFlags flags = {}) const;
QSGTexture *createTextureFromNativeTexture(quint64 nativeObjectHandle,
- int nativeLayout,
+ int nativeLayoutOrState,
const QSize &size,
QQuickWindow::CreateTextureOptions options,
TextureFromNativeTextureFlags flags = {}) const {
- return createTextureFromNativeTexture(nativeObjectHandle, nativeLayout, 0, size, options, flags);
+ return createTextureFromNativeTexture(nativeObjectHandle, nativeLayoutOrState, 0, size, options, flags);
}
QQuickItem::UpdatePaintNodeData updatePaintNodeData;
@@ -179,6 +203,13 @@ public:
void fireFrameSwapped() { Q_EMIT q_func()->frameSwapped(); }
void fireAboutToStop() { Q_EMIT q_func()->sceneGraphAboutToStop(); }
+ void clearGrabbers(QPointerEvent *event);
+
+ void updateChildWindowStackingOrder(QQuickItem *item = nullptr);
+
+ int multiViewCount();
+ QRhiRenderTarget *activeCustomRhiRenderTarget();
+
QSGRenderContext *context;
QSGRenderer *renderer;
QByteArray visualizationMode; // Default renderer supports "clip", "overdraw", "changes", "batches" and blank.
@@ -191,7 +222,6 @@ public:
uint persistentGraphics : 1;
uint persistentSceneGraph : 1;
- uint componentCompleted : 1;
uint inDestructor : 1;
// Storage for setRenderTarget(QQuickRenderTarget).
@@ -258,6 +288,7 @@ public:
uint updatesEnabled : 1;
bool pendingFontUpdate = false;
bool windowEventDispatch = false;
+ QPointer<QQuickPalette> windowPaletteRef;
private:
static void cleanupNodesOnShutdown(QQuickItem *);
diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h
index 8f11e6f4df..6cb0dd6433 100644
--- a/src/quick/items/qquickwindowattached_p.h
+++ b/src/quick/items/qquickwindowattached_p.h
@@ -24,17 +24,17 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickWindow;
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowAttached : public QObject
+class Q_QUICK_EXPORT QQuickWindowAttached : public QObject
{
Q_OBJECT
- Q_PROPERTY(QWindow::Visibility visibility READ visibility NOTIFY visibilityChanged)
- Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
- Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged)
- Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY contentItemChanged)
- Q_PROPERTY(int width READ width NOTIFY widthChanged)
- Q_PROPERTY(int height READ height NOTIFY heightChanged)
- Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged)
+ Q_PROPERTY(QWindow::Visibility visibility READ visibility NOTIFY visibilityChanged FINAL)
+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged FINAL)
+ Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged FINAL)
+ Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY contentItemChanged FINAL)
+ Q_PROPERTY(int width READ width NOTIFY widthChanged FINAL)
+ Q_PROPERTY(int height READ height NOTIFY heightChanged FINAL)
+ Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
diff --git a/src/quick/items/qquickwindowcontainer.cpp b/src/quick/items/qquickwindowcontainer.cpp
new file mode 100644
index 0000000000..cf4209a36b
--- /dev/null
+++ b/src/quick/items/qquickwindowcontainer.cpp
@@ -0,0 +1,590 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickwindowcontainer_p.h"
+
+#include <QtQuick/qquickrendercontrol.h>
+
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickrectangle_p.h>
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuick/private/qquickimplicitsizeitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_STATIC_LOGGING_CATEGORY(lcWindowContainer, "qt.quick.window.container")
+
+using namespace Qt::StringLiterals;
+
+/*!
+ \qmltype WindowContainer
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \inherits Item
+ \since 6.8
+
+ \brief Allows embedding arbitrary QWindows into a Qt Quick scene.
+
+ The window will become a child of the item's window,
+ with its position, size, z-order, etc. managed by the item.
+
+ Sibling items with a higher z-order than the window container
+ will not automatically overlap the embedded window, as the
+ window lives on top of the Qt Quick scene. To work around this,
+ place the sibling items inside their own dedicated child window:
+
+ \code
+ Item {
+ id: someItem
+ WindowContainer {
+ window: foreignWindow
+ }
+ WindowContainer {
+ window: Window {
+ Item {
+ id: siblingItem
+ }
+ }
+ }
+ }
+ \endcode
+
+ Similarly, child Items of the window container will not automatically
+ overlap the embedded window. To work around this, place the child
+ item inside a dedicated child window.
+
+ \code
+ Item {
+ id: someItem
+ WindowContainer {
+ id: windowContainer
+ window: foreignWindow
+ WindowContainer {
+ window: Window {
+ Item {
+ id: childItem
+ }
+ }
+ }
+ }
+ }
+ \endcode
+
+ \note The window container does not interoperate with QQuickWidget,
+ QQuickWindow::setRenderTarget(), QQuickRenderControl, or similar
+ functionality.
+
+ \sa {QtQuick::Window::parent}
+*/
+
+/*!
+ \qmlproperty QWindow QtQuick::WindowContainer::window
+
+ This property holds the window to embed.
+*/
+
+class QQuickWindowContainerPrivate : public QQuickImplicitSizeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickWindowContainer)
+protected:
+ bool transformChanged(QQuickItem *transformedItem) override;
+
+public:
+ QWindow *window = nullptr;
+ QQuickWindowContainer::ContainerMode containerMode;
+};
+
+/*!
+ \internal
+
+ Creates a new window container.
+
+ The container mode determines who has the last word in what the state
+ of the contained window should be. If the window container is explicitly
+ requested by the user via WindowContainer, the properties are set on the
+ item, and the embedded window should match that. If the window container
+ is implicitly created by setting a visual parent on a Window, the properties
+ are set on the Window, and the window container should respect that.
+*/
+QQuickWindowContainer::QQuickWindowContainer(QQuickItem *parent, ContainerMode containerMode)
+ : QQuickImplicitSizeItem(*(new QQuickWindowContainerPrivate), parent)
+{
+ Q_D(QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer).verbosity(1) << "Creating window container"
+ << this << "with parent" << parent << "and" << containerMode;
+
+ d->containerMode = containerMode;
+
+ setFlag(QQuickItem::ItemObservesViewport); // For clipping
+
+ connect(this, &QQuickItem::windowChanged,
+ this, &QQuickWindowContainer::parentWindowChanged);
+
+ if (lcWindowContainer().isDebugEnabled()) {
+ auto *debugRectangle = new QQuickRectangle(this);
+ debugRectangle->setColor(QColor(255, 0, 255, 20));
+ auto *border = debugRectangle->border();
+ border->setColor(Qt::magenta);
+ border->setWidth(1.0);
+ QQuickItemPrivate *rectPrivate = QQuickItemPrivate::get(debugRectangle);
+ rectPrivate->anchors()->setFill(this);
+ }
+}
+
+QQuickWindowContainer::~QQuickWindowContainer()
+{
+ Q_D(const QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Destructing window container" << this;
+
+ disconnect(this);
+ if (d->window) {
+ auto ownership = QJSEngine::objectOwnership(d->window);
+ qCDebug(lcWindowContainer) << "Contained window" << d->window
+ << "has" << (ownership == QQmlEngine::JavaScriptOwnership ?
+ "JavaScript" : "C++") << "ownership";
+ if (ownership == QQmlEngine::JavaScriptOwnership) {
+ delete d->window;
+ } else {
+ d->window->destroy();
+ d->window->setParent(nullptr);
+ }
+ }
+}
+
+void QQuickWindowContainer::releaseResources()
+{
+ Q_D(const QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Destroying" << d->window
+ << "with platform window" << (d->window ? d->window->handle() : nullptr);
+ if (d->window)
+ d->window->destroy();
+}
+
+void QQuickWindowContainer::classBegin()
+{
+ qCDebug(lcWindowContainer) << "Class begin for" << this;
+
+ QQuickImplicitSizeItem::classBegin();
+}
+
+void QQuickWindowContainer::componentComplete()
+{
+ Q_D(const QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer) << "Component completed for" << this;
+ QQuickImplicitSizeItem::componentComplete();
+
+ if (d->window)
+ initializeContainedWindow();
+}
+
+QWindow *QQuickWindowContainer::containedWindow() const
+{
+ Q_D(const QQuickWindowContainer);
+ return d->window;
+}
+
+void QQuickWindowContainer::setContainedWindow(QWindow *window)
+{
+ qCDebug(lcWindowContainer) << "Setting contained window for" << this << "to" << window;
+
+ Q_D(QQuickWindowContainer);
+
+ if (window == d->window)
+ return;
+
+ if (auto *previousWindow = d->window) {
+ qCDebug(lcWindowContainer) << "Decoupling container from" << d->window;
+ previousWindow->disconnect(this);
+ previousWindow->removeEventFilter(this);
+ previousWindow->setParent(nullptr);
+ }
+
+ d->window = window;
+
+ if (d->window) {
+ if (d->containerMode == ItemControlsWindow) {
+ if (auto *quickWindow = qobject_cast<QQuickWindowQmlImpl*>(d->window)) {
+ // Make sure the Window reflects the window container as its visual parent
+ quickWindow->setVisualParent(this);
+ }
+ }
+
+ // When the window controls the container, we need to reflect any changes
+ // in the window back to the container, so they stay in sync. And when the
+ // container controls the window, we still want to reflect width/height as
+ // new implicit size, and override any other changes with the item state.
+ connect(d->window, &QWindow::xChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::yChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::widthChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::heightChanged, this, &QQuickWindowContainer::windowUpdated);
+ connect(d->window, &QWindow::visibleChanged, this, &QQuickWindowContainer::windowUpdated);
+
+ connect(d->window, &QObject::destroyed, this, &QQuickWindowContainer::windowDestroyed);
+
+ d->window->installEventFilter(this);
+
+ if (d->componentComplete)
+ initializeContainedWindow();
+ } else {
+ // Reset state based on not having a window
+ syncWindowToItem();
+ }
+
+ emit containedWindowChanged(d->window);
+}
+
+void QQuickWindowContainer::initializeContainedWindow()
+{
+ Q_D(const QQuickWindowContainer);
+ Q_ASSERT(d->componentComplete);
+ Q_ASSERT(d->window);
+
+ qCDebug(lcWindowContainer) << "Doing initial sync between" << d->window << "and" << this;
+
+ syncWindowToItem();
+ polish();
+}
+
+static QTransform sanitizeTransform(const QTransform &transform)
+{
+ if (transform.isRotating()) {
+ // FIXME: Can we keep more here?
+ return QTransform::fromTranslate(transform.dx(), transform.dy());
+ }
+
+ return transform;
+}
+
+void QQuickWindowContainer::syncWindowToItem()
+{
+ Q_D(const QQuickWindowContainer);
+
+ const auto windowGeometry = d->window ? d->window->geometry() : QRect();
+
+ qCDebug(lcWindowContainer) << "Syncing window state from" << d->window
+ << "with geometry" << windowGeometry << "to" << this
+ << "with mode" << d->containerMode;
+
+ const auto transform = sanitizeTransform(d->windowToItemTransform());
+
+ // The window might have a larger size than the item's natural
+ // size, if there's a scale applied somewhere in the hierarchy.
+ auto itemSize = d->window ? transform.mapRect(windowGeometry).size()
+ : QSize();
+
+ if (d->containerMode == WindowControlsItem) {
+ // When the Window controls the window container the position is
+ // set up front, when creating the window container, and from that
+ // point on set exclusively via the window container, so we skip
+ // setting the position here, and only set the size.
+ setSize(itemSize);
+ setVisible(d->window ? d->window->isVisible() : false);
+ } else {
+ // Position defined by item, so don't sync from window
+ // Visible defined by item, so don't sync from window
+ setImplicitWidth(itemSize.width());
+ setImplicitHeight(itemSize.height());
+ }
+}
+
+/*!
+ \internal
+
+ updatePolish() should perform any layout as required for this item.
+
+ For us, that means propagating the item's state to the window.
+*/
+void QQuickWindowContainer::updatePolish()
+{
+ Q_D(QQuickWindowContainer);
+
+ qCDebug(lcWindowContainer) << "Propagating" << this << "state"
+ << "to" << d->window;
+
+ auto *parentWindow = window();
+
+ // FIXME: If we are part of a QQuickWidget, we have a QQuickRenderControl,
+ // and should look up the parent window via that, and apply the offset we
+ // get to the item transform below. But at the moment it's not possible
+ // to observe changes to the offset, which is critical to support this
+ // for child windows.
+
+ if (!d->window || !parentWindow)
+ return;
+
+ if (d->window->parent() != parentWindow) {
+ qCDebug(lcWindowContainer) << "Updating window parent to" << parentWindow;
+ d->window->setParent(parentWindow);
+ }
+
+ auto transform = sanitizeTransform(d->itemToWindowTransform());
+
+ // Find the window's geometry, based on the item's bounding rect,
+ // mapped to the scene. The mapping includes any x/y position set
+ // on the item itself, as well as any transforms applied to the item
+ // or its ancestor (scale, translation).
+ const QRectF itemSceneRect = transform.mapRect(boundingRect());
+ // FIXME: Rounding to a QRect here means we'll have some jitter or off
+ // placement when the underlying item is not on a integer coordinate.
+ QRect windowGeometry = itemSceneRect.toRect();
+ if (windowGeometry != d->window->geometry()) {
+ QRectF itemRect(position(), size());
+ qCDebug(lcWindowContainer) << "Updating window geometry to" << windowGeometry
+ << "based on item rect" << itemRect << "and scene rect" << itemSceneRect;
+ d->window->setGeometry(windowGeometry);
+ }
+
+ // Clip the container to its own and ancestor clip rects, by setting
+ // a mask on the window. This does not necessarily clip native windows,
+ // as QWindow::setMask() is not guaranteed to visually clip the window,
+ // only to mask input, but in most cases we should be good. For the
+ // cases where this fails, we can potentially use an intermediate window
+ // as parent of the contained window, if the platform allows clipping
+ // child windows to parent window geometry. We do not want to resize the
+ // contained window, as that will just fill the content into a smaller
+ // area.
+ const auto clipMask = [&]{
+ if (clipRect() == boundingRect())
+ return QRect();
+
+ // The clip rect has all the relevant transforms applied to it,
+ // except for the item's own scale. As the mask is in window
+ // local coordinates in the possibly scaled window, we need
+ // to apply the scale manually.
+ auto scaleTransform = QTransform::fromScale(transform.m11(), transform.m22());
+ auto rect = scaleTransform.mapRect(clipRect()).toRect();
+
+ // An empty clip rect means clip away everything, while for a
+ // window, an empty mask means mask nothing. Fake the former
+ // by setting a mask outside of the window's bounds. We have
+ // to do this check after rounding the clip rect to a QRect.
+ // FIXME: Verify this works on all platforms
+ if (rect.isEmpty())
+ return QRect(-1, -1, 1, 1);
+
+ return rect;
+ }();
+
+ if (clipMask != d->window->mask().boundingRect()) {
+ qCDebug(lcWindowContainer) << "Updating window clip mask to" << clipMask
+ << "based on clip rect" << clipRect();
+ d->window->setMask(clipMask);
+ }
+
+ // FIXME: Opacity support. Need to calculate effective opacity ourselves,
+ // and there doesn't seem to be any existing observer for opacity changes.
+ // Not all platforms implement opacity for child windows yet.
+
+ // FIXME: If a scale is applied to the item or its parents, we end up
+ // with a bigger item, and window, but we don't translate the scale to
+ // an increase device-pixel-ratio of the window. As a result, the window
+ // will likely just render more content, instead of the same content at
+ // a potentially higher density.
+
+ if (d->window->isVisible() != isVisible()) {
+ qCDebug(lcWindowContainer) << "Updating window visibility"
+ << "based on item visible" << isVisible();
+ d->window->setVisible(isVisible());
+ }
+}
+
+/*!
+ \internal
+
+ QQuickItem::clipRect() doesn't take ItemClipsChildrenToShape into
+ account, so a parent item that has clip:false, but ItemIsViewport
+ will still result in affecting the clip.
+
+ We want to stay consistent with the clipping in the scene graph,
+ which is based on QQuickItem::clip(), so we override the clipRect
+ to take ItemClipsChildrenToShape into account.
+*/
+QRectF QQuickWindowContainer::clipRect() const
+{
+ QRectF rect = boundingRect();
+
+ for (auto *viewport = viewportItem(); viewport; viewport = viewport->viewportItem()) {
+ if (viewport == this)
+ break;
+
+ if (viewport->flags().testFlag(QQuickItem::ItemClipsChildrenToShape)) {
+ // FIXME: This fails to take into account viewports that override clipRect()
+ const auto mappedViewportRect = mapRectFromItem(viewport, viewport->boundingRect());
+ rect = mappedViewportRect.intersected(rect);
+ }
+
+ if (viewport->viewportItem() == viewport)
+ break; // Content item returns itself as viewport
+ }
+
+ return rect;
+}
+
+// ----------------------- Window updates -----------------------
+
+/*!
+ \internal
+
+ Called when the contained QWindow is changed.
+
+ Depending on the sync mode we need to reflect these changes
+ to the item, or override them by applying the item state.
+*/
+void QQuickWindowContainer::windowUpdated()
+{
+ Q_D(const QQuickWindowContainer);
+
+ if (lcWindowContainer().isDebugEnabled()) {
+ auto metaMethod = sender()->metaObject()->method(senderSignalIndex());
+ auto signalName = QString::fromUtf8(metaMethod.name());
+ qCDebug(lcWindowContainer).noquote() << d->window << signalName;
+ }
+
+ syncWindowToItem();
+
+ if (d->containerMode == ItemControlsWindow) {
+ qCDebug(lcWindowContainer) << "Overriding window state by polishing";
+ // Ideally we'd always call ensurePolished() here, to synchronously
+ // override the window state ASAP, rather than wait for polish to
+ // trigger it asynchronously, but due to QWindowPrivate::setVisible
+ // emitting visibleChanged before updating the platform window, we
+ // end up applying our override temporarily, only to have QWindowPrivate
+ // follow up with the original change to the platform window.
+ if (d->window->isVisible() != isVisible())
+ polish();
+ else
+ ensurePolished();
+ }
+}
+
+bool QQuickWindowContainer::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(const QQuickWindowContainer);
+ Q_ASSERT(object == d->window);
+
+ if (event->type() == QEvent::PlatformSurface) {
+ auto type = static_cast<QPlatformSurfaceEvent*>(event)->surfaceEventType();
+ if (type == QPlatformSurfaceEvent::SurfaceCreated) {
+ qCDebug(lcWindowContainer) << "Surface created for" << object;
+ syncWindowToItem();
+ // The surface creation has already resulted in the native window
+ // being added to its parent, on top of all other windows. We need
+ // to do a synchronous re-stacking of the windows here, to avoid
+ // leaving the window in the wrong position while waiting for the
+ // asynchronous callback to QQuickWindow::polishItems().
+ if (auto *quickWindow = qobject_cast<QQuickWindow*>(window()))
+ QQuickWindowPrivate::get(quickWindow)->updateChildWindowStackingOrder();
+ }
+ }
+
+ return QQuickImplicitSizeItem::eventFilter(object, event);
+}
+
+void QQuickWindowContainer::windowDestroyed()
+{
+ Q_D(QQuickWindowContainer);
+ qCDebug(lcWindowContainer) << "Window" << (void*)d->window << "destroyed";
+
+ d->window->removeEventFilter(this);
+ d->window = nullptr;
+
+ syncWindowToItem(); // Reset state based on not having a window
+ emit containedWindowChanged(d->window);
+}
+
+// ----------------------- Item updates -----------------------
+
+/*!
+ \internal
+
+ Called when the item's geometry has changed
+*/
+void QQuickWindowContainer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ qCDebug(lcWindowContainer) << this << "geometry changed from"
+ << oldGeometry << "to" << newGeometry;
+
+ QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
+ if (newGeometry.isValid())
+ polish();
+}
+
+/*!
+ \internal
+
+ Called when the item's (effective) state has changed
+*/
+void QQuickWindowContainer::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ switch (change) {
+ case ItemVisibleHasChanged:
+ qCDebug(lcWindowContainer) << "Visible changed for" << this << "to" << isVisible();
+ polish();
+ break;
+ default:
+ break;
+ }
+
+ QQuickImplicitSizeItem::itemChange(change, data);
+}
+
+/*!
+ \internal
+
+ Called when the window container item is moved to another window
+*/
+void QQuickWindowContainer::parentWindowChanged(QQuickWindow *parentWindow)
+{
+ qCDebug(lcWindowContainer) << this << "parent window changed to" << parentWindow;
+
+ Q_D(QQuickWindowContainer);
+
+ if (!parentWindow) {
+ // We have been removed from the window we were part of,
+ // possibly because the window is going away. We need to
+ // make sure the contained window is no longer a child of
+ // former window, as otherwise it will be wiped out along
+ // with it. We can't wait for updatePolish() to do that
+ // as polish has no effect when an item is not part of a
+ // window.
+ if (d->window) {
+ // The window should already be destroyed from the
+ // call to releaseResources(), which is part of the
+ // removal of an item from a scene, but just in case
+ // we do it here as well.
+ d->window->destroy();
+
+ d->window->setParent(nullptr);
+ }
+ } else {
+ polish();
+ }
+}
+
+bool QQuickWindowContainerPrivate::transformChanged(QQuickItem *transformedItem)
+{
+ Q_Q(QQuickWindowContainer);
+
+ if (this->window) {
+ auto *transformedItemPrivate = QQuickItemPrivate::get(transformedItem);
+ qCDebug(lcWindowContainer) << "Transform changed for" << transformedItem
+ << "with dirty state" << transformedItemPrivate->dirtyToString();
+
+ if (transformedItemPrivate->dirtyAttributes
+ & QQuickItemPrivate::BasicTransform) {
+ // For some reason scale transforms, which result in the window
+ // being resized, end up with the window lagging a frame or two
+ // behind the item. Polish synchronously instead, to mitigate
+ // this, even if it may result in the opposite situation.
+ q->ensurePolished();
+ } else {
+ q->polish();
+ }
+ }
+
+ return QQuickItemPrivate::transformChanged(transformedItem);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowcontainer_p.h b/src/quick/items/qquickwindowcontainer_p.h
new file mode 100644
index 0000000000..5da0c6ba27
--- /dev/null
+++ b/src/quick/items/qquickwindowcontainer_p.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKWINDOWCONTAINER_P_H
+#define QQUICKWINDOWCONTAINER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+
+#include <QtCore/private/qobject_p.h>
+
+#include <QtQuick/private/qquickimplicitsizeitem_p.h>
+#include <QtQuick/qquickwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickWindowContainerPrivate;
+class Q_QUICK_EXPORT QQuickWindowContainer : public QQuickImplicitSizeItem
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(WindowContainer)
+ Q_PROPERTY(QWindow *window READ containedWindow WRITE setContainedWindow
+ NOTIFY containedWindowChanged DESIGNABLE false FINAL)
+
+ QML_ADDED_IN_VERSION(6, 7)
+
+public:
+ enum ContainerMode {
+ WindowControlsItem,
+ ItemControlsWindow
+ };
+
+ explicit QQuickWindowContainer(QQuickItem *parent = nullptr,
+ ContainerMode containerMode = ItemControlsWindow);
+ ~QQuickWindowContainer();
+
+ QWindow *containedWindow() const;
+ void setContainedWindow(QWindow *window);
+ Q_SIGNAL void containedWindowChanged(QWindow *window);
+
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+
+ void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override;
+
+ void updatePolish() override;
+
+ bool eventFilter(QObject *object, QEvent *event) override;
+
+ QRectF clipRect() const override;
+
+ void releaseResources() override;
+
+private:
+ Q_DECLARE_PRIVATE(QQuickWindowContainer)
+ friend class QQuickWindowQmlImpl;
+
+ void initializeContainedWindow();
+ void windowUpdated();
+ void syncWindowToItem();
+ void parentWindowChanged(QQuickWindow *window);
+ void windowDestroyed();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKWINDOWCONTAINER_P_H
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index 8a815569fe..ddcb588c04 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -20,7 +20,9 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcTransient)
+using namespace Qt::StringLiterals;
+
+Q_STATIC_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
QQuickWindowQmlImplPrivate::QQuickWindowQmlImplPrivate() = default;
@@ -29,30 +31,42 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent)
{
}
-void QQuickWindowQmlImpl::setVisible(bool visible)
+QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent)
+ : QQuickWindow(dd, parent)
{
- Q_D(QQuickWindowQmlImpl);
- d->visible = visible;
- if (d->complete && (!transientParent() || transientParentVisible()))
- QQuickWindow::setVisible(visible);
-}
+ connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged);
+ 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);
-void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
-{
- Q_D(QQuickWindowQmlImpl);
- d->visibility = visibility;
- if (d->complete)
- QQuickWindow::setVisibility(visibility);
+ // We shadow the x and y properties, so that we can re-map them in case
+ // we have an Item as our visual parent, which will result in creating an
+ // implicit window container that we control. Ensure that signals still work,
+ // and that they reflect the mapped values if a window container is used.
+ QObject::connect(this, &QWindow::xChanged, this, [this] { emit xChanged(x()); });
+ QObject::connect(this, &QWindow::yChanged, this, [this] { emit yChanged(y()); });
}
-QQuickWindowAttached *QQuickWindowQmlImpl::qmlAttachedProperties(QObject *object)
+QQuickWindowQmlImpl::~QQuickWindowQmlImpl()
{
- return new QQuickWindowAttached(object);
+ // Destroy the window while we are still alive, so that any signals
+ // emitted by the destruction can be delivered properly.
+ destroy();
}
void QQuickWindowQmlImpl::classBegin()
{
Q_D(QQuickWindowQmlImpl);
+ qCDebug(lcQuickWindow) << "Class begin for" << this;
+ d->componentComplete = false;
+
QQmlEngine* e = qmlEngine(this);
QQmlEngine::setContextForObject(contentItem(), e->rootContext());
@@ -66,89 +80,453 @@ void QQuickWindowQmlImpl::classBegin()
// The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
// wrapper so that the garbage collector can see the policy.
QV4::ExecutionEngine *v4 = e->handle();
- QV4::QObjectWrapper::wrap(v4, d->contentItem);
+ QV4::QObjectWrapper::ensureWrapper(v4, d->contentItem);
}
}
void QQuickWindowQmlImpl::componentComplete()
{
Q_D(QQuickWindowQmlImpl);
- d->complete = true;
- QQuickItem *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent());
- const bool transientParentAlreadySet = QQuickWindowPrivate::get(this)->transientParentPropertySet;
- if (!transientParentAlreadySet && itemParent && !itemParent->window()) {
- qCDebug(lcTransient) << "window" << title() << "has invisible Item parent" << itemParent << "transientParent"
- << transientParent() << "declared visibility" << d->visibility << "; delaying show";
- connect(itemParent, &QQuickItem::windowChanged, this,
- &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection);
- } else if (transientParent() && !transientParent()->isVisible()) {
- connect(transientParent(), &QQuickWindow::visibleChanged, this,
- &QQuickWindowQmlImpl::setWindowVisibility, Qt::QueuedConnection);
- } else {
- setWindowVisibility();
- }
+ qCDebug(lcQuickWindow) << "Component completed for" << this;
+ d->componentComplete = true;
+
+ applyVisualParent();
+
+ // Apply automatic transient parent if needed, and opt in to future
+ // parent change events, so we can keep the transient parent in sync.
+ updateTransientParent();
+ d->receiveParentEvents = true;
+
+ applyWindowVisibility();
+
+ // If the transient parent changes, and we've deferred making
+ // the window visible, we need to re-evaluate our decision.
+ connect(this, &QWindow::transientParentChanged,
+ this, &QQuickWindowQmlImpl::applyWindowVisibility);
}
-QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent)
- : QQuickWindow(dd, parent)
+void QQuickWindowQmlImpl::setVisible(bool visible)
{
- // These two signals are called during QWindow's dtor, thus they have to be queued connections
- // or else our slots will be called instantly when our destructor has already run but our
- // connections haven't been removed yet.
- connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged,
- Qt::QueuedConnection);
- connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged,
- Qt::QueuedConnection);
+ Q_D(QQuickWindowQmlImpl);
+ d->visible = visible;
+ d->visibleExplicitlySet = true;
+ if (d->componentComplete)
+ applyWindowVisibility();
+}
- connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged);
+void QQuickWindowQmlImpl::setVisibility(Visibility visibility)
+{
+ Q_D(QQuickWindowQmlImpl);
+ d->visibility = visibility;
+ d->visibilityExplicitlySet = true;
+ if (d->componentComplete)
+ applyWindowVisibility();
+}
+
+bool QQuickWindowQmlImpl::event(QEvent *event)
+{
+ Q_D(QQuickWindowQmlImpl);
+
+ if (event->type() == QEvent::ParentWindowChange) {
+ qCDebug(lcQuickWindow) << "Parent of" << this << "changed to" << parent();
+ if (d->visualParent) {
+ // If the window parent changes, and we've deferred making
+ // the window visible, we need to re-evaluate our decision.
+ applyWindowVisibility();
+ } else {
+ QObject::disconnect(d->itemParentWindowChangeListener);
+ updateTransientParent();
+ }
+ }
+ return QQuickWindow::event(event);
}
-void QQuickWindowQmlImpl::setWindowVisibility()
+/*
+ Update the transient parent of the window based on its
+ QObject parent (Item or Window), unless the user has
+ set an explicit transient parent.
+*/
+void QQuickWindowQmlImpl::updateTransientParent()
{
Q_D(QQuickWindowQmlImpl);
- if (transientParent() && !transientParentVisible())
+
+ // We defer updating the transient parent until the component
+ // has been fully completed, and we know whether an explicit
+ // transient parent has been set.
+ if (!d->componentComplete)
return;
- if (QQuickItem *senderItem = qmlobject_cast<QQuickItem *>(sender())) {
- disconnect(senderItem, &QQuickItem::windowChanged, this, &QQuickWindowQmlImpl::setWindowVisibility);
- } else if (sender()) {
- disconnect(transientParent(), &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::setWindowVisibility);
+ // If an explicit transient parent has been set,
+ // we don't want to apply our magic.
+ if (d->transientParentPropertySet)
+ return;
+
+ // Nor if we have a visual parent that makes this a true child window
+ if (d->visualParent)
+ return;
+
+ auto *objectParent = QObject::parent();
+ qCDebug(lcTransient) << "Applying transient parent magic to"
+ << this << "based on object parent" << objectParent << "🪄";
+
+ QWindow *transientParent = nullptr;
+ if (auto *windowParent = qmlobject_cast<QWindow *>(objectParent)) {
+ transientParent = windowParent;
+ } else if (auto *itemParent = qmlobject_cast<QQuickItem *>(objectParent)) {
+ if (!d->itemParentWindowChangeListener) {
+ d->itemParentWindowChangeListener = connect(
+ itemParent, &QQuickItem::windowChanged,
+ this, &QQuickWindowQmlImpl::updateTransientParent);
+ }
+ transientParent = itemParent->window();
}
- // We have deferred window creation until we have the full picture of what
- // the user wanted in terms of window state, geometry, visibility, etc.
+ if (!transientParent) {
+ qCDebug(lcTransient) << "No transient parent resolved from object parent";
+ return;
+ }
- if ((d->visibility == Hidden && d->visible) || (d->visibility > AutomaticVisibility && !d->visible)) {
- QQmlData *data = QQmlData::get(this);
- Q_ASSERT(data && data->context);
+ qCDebug(lcTransient) << "Setting" << transientParent << "as transient parent of" << this;
+ setTransientParent(transientParent);
- QQmlError error;
- error.setObject(this);
+ // We want to keep applying the automatic transient parent
+ d->transientParentPropertySet = false;
+}
- QQmlRefPointer<QQmlContextData> urlContext = data->context;
- while (urlContext && urlContext->url().isEmpty())
- urlContext = urlContext->parent();
- error.setUrl(urlContext ? urlContext->url() : QUrl());
+void QQuickWindowQmlImpl::applyWindowVisibility()
+{
+ Q_D(QQuickWindowQmlImpl);
- QString objectId = data->context->findObjectId(this);
- if (!objectId.isEmpty())
- error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl",
- "Conflicting properties 'visible' and 'visibility' for Window '%1'").arg(objectId));
- else
- error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl",
- "Conflicting properties 'visible' and 'visibility'"));
+ Q_ASSERT(d->componentComplete);
- QQmlEnginePrivate::get(data->context->engine())->warning(error);
+ const bool visible = d->visibilityExplicitlySet
+ ? d->visibility != Hidden : d->visible;
+
+ qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this;
+
+ if (visible) {
+ if (d->visualParent) {
+ // Even though we're complete, and have a visual parent set,
+ // we may not be part of a window yet, or we may have been
+ // removed from a window that's going away. Showing this window
+ // now would make it a top level, which is not what we want.
+ if (!QWindow::parent()) {
+ qCDebug(lcQuickWindow) << "Waiting for visual parent to reparent us into a window";
+ // We apply the visibility again on ParentWindowChange
+ return;
+ }
+ } else {
+ // Handle deferred visibility due to possible transient parent
+ auto *itemParent = qmlobject_cast<QQuickItem *>(QObject::parent());
+ if (!d->transientParentPropertySet && itemParent && !itemParent->window()) {
+ qCDebug(lcTransient) << "Waiting for parent" << itemParent << "to resolve"
+ << "its window. Deferring visibility";
+ return;
+ }
+
+ const QWindow *transientParent = QWindow::transientParent();
+ if (transientParent && !transientParentVisible()) {
+ // Defer visibility of this window until the transient parent has
+ // been made visible, or we've get a new transient parent.
+ qCDebug(lcTransient) << "Transient parent" << transientParent
+ << "not visible yet. Deferring visibility";
+
+ // QWindowPrivate::setVisible emits visibleChanged _before_ actually
+ // propagating the visibility to the platform window, so we can't use
+ // a direct connection here, as that would result in showing this
+ // window before the transient parent.
+ connect(transientParent, &QQuickWindow::visibleChanged, this,
+ &QQuickWindowQmlImpl::applyWindowVisibility,
+ Qt::ConnectionType(Qt::QueuedConnection | Qt::SingleShotConnection));
+ return;
+ }
+ }
+ }
+
+ 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) {
- setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags()));
- setVisible(d->visible);
+ // 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 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);
+ }
+}
+
+bool QQuickWindowQmlImpl::transientParentVisible()
+{
+ Q_ASSERT(transientParent());
+ if (!transientParent()->isVisible()) {
+ // handle case where transient parent is offscreen window
+ QWindow *rw = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow*>(transientParent()));
+ return rw && rw->isVisible();
+ }
+ return true;
+}
+
+// -------------------------- Visual Parent ---------------------------
+
+/*!
+ \qmlproperty var QtQuick::Window::parent
+ \since 6.7
+ \internal
+
+ This property holds the visual parent of the window.
+
+ The visual parent can be either another Window, or an Item.
+
+ A window with a visual parent will result in the window becoming a child
+ window of its visual parent, either directly if the visual parent is another
+ Window, or indirectly via the visual parent Item's window.
+
+ Just like QtQuick::Item::parent, the window will be positioned relative to
+ its visual parent.
+
+ The stacking order between sibling Windows follows the document order,
+ just like Items, but can be customized via the Window's \l{QtQuick::Window::z}
+ {z-order} property.
+
+ Setting a visual parent on a Window will take precedence over the
+ \l{QtQuick::Window::transientParent}{transient parent}.
+
+ \sa{Concepts - Visual Parent in Qt Quick}, transientParent
+*/
+
+void QQuickWindowQmlImpl::setVisualParent(QObject *visualParent)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (visualParent == d->visualParent)
+ return;
+
+ qCDebug(lcQuickWindow) << "Setting visual parent of" << this << "to" << visualParent;
+
+ if (d->visualParent) {
+ // Disconnect from deferred window listener
+ d->visualParent->disconnect(this);
+ }
+
+ d->visualParent = visualParent;
+
+ if (d->componentComplete)
+ applyVisualParent();
+
+ emit visualParentChanged(d->visualParent);
+}
+
+void QQuickWindowQmlImpl::applyVisualParent()
+{
+ Q_D(QQuickWindowQmlImpl);
+ Q_ASSERT(d->componentComplete);
+
+ qCDebug(lcQuickWindow) << "Applying" << this << "visual parent" << d->visualParent;
+
+ if (!d->visualParent) {
+ if (d->windowContainer) {
+ d->windowContainer->setContainedWindow(nullptr);
+ delete std::exchange(d->windowContainer, nullptr);
+ }
+ QQuickWindow::setParent(nullptr);
+ return;
+ }
+
+ QQuickItem *parentItem = nullptr;
+ if ((parentItem = qobject_cast<QQuickItem*>(d->visualParent)))
+ ; // All good, can use directly
+ else if (auto *parentWindow = qobject_cast<QWindow*>(d->visualParent)) {
+ if (auto *parentQuickWindow = qobject_cast<QQuickWindow*>(parentWindow)) {
+ parentItem = parentQuickWindow->contentItem();
+ } else {
+ qmlWarning(this) << "Parenting into non-Quick window. "
+ << "Stacking, position, and destruction must be handled manually";
+ QQuickWindow::setParent(parentWindow); // Try our best
+ return;
+ }
+ }
+
+ if (!parentItem) {
+ qmlWarning(this) << "Unsupported visual parent type"
+ << d->visualParent->metaObject()->className();
+ return;
+ }
+
+ if (!parentItem->window()) {
+ qCDebug(lcQuickWindow) << "No window yet. Deferring.";
+ connect(parentItem, &QQuickItem::windowChanged, this, [this]{
+ qCDebug(lcQuickWindow) << "Got window. Applying deferred visual parent item.";
+ applyVisualParent();
+ }, Qt::SingleShotConnection);
+ return;
+ }
+
+ if (qobject_cast<QQuickWindowContainer*>(d->visualParent)) {
+ qCDebug(lcQuickWindow) << "Visual parent is window container, everything is in order";
+ return;
+ }
+
+ if (!d->windowContainer) {
+ d->windowContainer = new QQuickWindowContainer(parentItem,
+ QQuickWindowContainer::WindowControlsItem);
+ d->windowContainer->setObjectName(objectName() + "Container"_L1);
+
+ auto *objectParent = this->QObject::parent();
+ if (objectParent == parentItem) {
+ // We want to reflect the QML document order of sibling windows in the
+ // resulting stacking order of the windows. We can do so by carefully
+ // using the the information we have about the child object order.
+
+ // We know that the window's object child index is correct in relation
+ // to the other child windows of the parent. Since the window container
+ // is going to represent the window from now on, make the window container
+ // take the window's place in the parent's child object list.
+ auto &objectChildren = QObjectPrivate::get(objectParent)->children;
+ auto windowIndex = objectChildren.indexOf(this);
+ auto containerIndex = objectChildren.indexOf(d->windowContainer);
+ objectChildren.move(containerIndex, windowIndex);
+ containerIndex = windowIndex;
+
+ // The parent's item children are unfortunately managed separately from
+ // the object children. But thanks to the logic above we can use the now
+ // correct object order of the window container in the object children list
+ // to also ensure a correct stacking order between the sibling child items.
+ for (int i = containerIndex + 1; i < objectChildren.size(); ++i) {
+ if (auto *childItem = qobject_cast<QQuickItem*>(objectChildren.at(i))) {
+ qCDebug(lcQuickWindow) << "Stacking" << d->windowContainer
+ << "below" << childItem;
+ d->windowContainer->stackBefore(childItem);
+ break;
+ }
+ }
+ } else {
+ // Having another visual parent than the direct object parent will
+ // mess up the stacking order. This is also the case for normal items.
+ qCDebug(lcQuickWindow) << "Visual parent is not object parent."
+ << "Can not reflect document order as stacking order.";
+ }
+
+ QQmlEngine::setContextForObject(d->windowContainer, qmlContext(this));
+
+ d->windowContainer->classBegin();
+ d->windowContainer->setContainedWindow(this);
+ // Once the window has a window container, all x/y/z changes of
+ // the window will go through the container, and ensure the
+ // correct mapping. But any changes that happened prior to
+ // this have not been mapped yet, so do that now.
+ d->windowContainer->setPosition(position());
+ d->windowContainer->setZ(d->z);
+ d->windowContainer->componentComplete();
+
+ QObject::connect(d->windowContainer, &QQuickItem::zChanged,
+ this, &QQuickWindowQmlImpl::zChanged);
} else {
- setVisibility(d->visibility);
+ d->windowContainer->setParentItem(parentItem);
}
}
+QObject *QQuickWindowQmlImpl::visualParent() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ return d->visualParent;
+}
+
+// We shadow the x and y properties of the Window, so that in case
+// the window has an Item as its visual parent we can re-map the
+// coordinates via the corresponding window container. We need to
+// do this also for the signal emissions, as otherwise the Window's
+// change signals will reflect different values than what we report
+// via the accessors. It would be nicer if this logic was contained
+// in the window container, for example via meta object property
+// interception, but that does not allow intercepting signal emissions.
+
+void QQuickWindowQmlImpl::setX(int x)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setX(x);
+ else
+ QQuickWindow::setX(x);
+}
+
+int QQuickWindowQmlImpl::x() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->x();
+ else
+ return QQuickWindow::x();
+}
+
+void QQuickWindowQmlImpl::setY(int y)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setY(y);
+ else
+ QQuickWindow::setY(y);
+}
+
+int QQuickWindowQmlImpl::y() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->y();
+ else
+ return QQuickWindow::y();
+}
+
+/*!
+ \qmlproperty real QtQuick::Window::z
+ \internal
+
+ Sets the stacking order of sibling windows.
+
+ By default the stacking order is 0.
+
+ Windows with a higher stacking value are drawn on top of windows with a
+ lower stacking order. Windows with the same stacking value are drawn
+ bottom up in the order they appear in the QML document.
+
+ \note This property only has an effect for child windows.
+
+ \sa QtQuick::Item::z
+*/
+
+void QQuickWindowQmlImpl::setZ(qreal z)
+{
+ Q_D(QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ d->windowContainer->setZ(z);
+ else
+ d->z = z;
+}
+
+qreal QQuickWindowQmlImpl::z() const
+{
+ Q_D(const QQuickWindowQmlImpl);
+ if (Q_UNLIKELY(d->windowContainer && d->windowContainer->window()))
+ return d->windowContainer->z();
+ else
+ return d->z;
+}
+
+// --------------------------------------------------------------------
+
QObject *QQuickWindowQmlImpl::screen() const
{
return new QQuickScreenInfo(const_cast<QQuickWindowQmlImpl *>(this), QWindow::screen());
@@ -160,15 +538,9 @@ void QQuickWindowQmlImpl::setScreen(QObject *screen)
QWindow::setScreen(screenWrapper ? screenWrapper->wrappedScreen() : nullptr);
}
-bool QQuickWindowQmlImpl::transientParentVisible()
+QQuickWindowAttached *QQuickWindowQmlImpl::qmlAttachedProperties(QObject *object)
{
- Q_ASSERT(transientParent());
- if (!transientParent()->isVisible()) {
- // handle case where transient parent is offscreen window
- QWindow *rw = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow*>(transientParent()));
- return rw && rw->isVisible();
- }
- return true;
+ return new QQuickWindowAttached(object);
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index c64ed537f7..709e373d0b 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -32,7 +32,7 @@ struct QWindowForeign
QML_ADDED_IN_VERSION(2, 1)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus
+class Q_QUICK_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -47,6 +47,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q
public:
QQuickWindowQmlImpl(QWindow *parent = nullptr);
+ ~QQuickWindowQmlImpl();
void setVisible(bool visible);
void setVisibility(QWindow::Visibility visibility);
@@ -54,6 +55,18 @@ public:
QObject *screen() const;
void setScreen(QObject *screen);
+ QObject *visualParent() const;
+ void setVisualParent(QObject *parent);
+ void visualParentChanged(QObject *) {};
+
+ void setX(int arg);
+ int x() const;
+ void setY(int arg);
+ int y() const;
+ void setZ(qreal arg);
+ qreal z() const;
+ void zChanged() {};
+
static QQuickWindowAttached *qmlAttachedProperties(QObject *object);
Q_SIGNALS:
@@ -61,17 +74,24 @@ Q_SIGNALS:
void visibilityChanged(QWindow::Visibility visibility);
Q_REVISION(2, 3) void screenChanged();
+ void xChanged(int arg);
+ void yChanged(int arg);
+
protected:
void classBegin() override;
void componentComplete() override;
+ bool event(QEvent *) override;
+
QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow *parent);
private Q_SLOTS:
- void setWindowVisibility();
+ Q_REVISION(6, 7) void applyWindowVisibility();
+ Q_REVISION(6, 7) void updateTransientParent();
private:
bool transientParentVisible();
+ void applyVisualParent();
private:
Q_DISABLE_COPY(QQuickWindowQmlImpl)
@@ -80,6 +100,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWindowQmlImpl)
-
#endif
diff --git a/src/quick/items/qquickwindowmodule_p_p.h b/src/quick/items/qquickwindowmodule_p_p.h
index 3057cfa2c2..227b8aa01e 100644
--- a/src/quick/items/qquickwindowmodule_p_p.h
+++ b/src/quick/items/qquickwindowmodule_p_p.h
@@ -16,18 +16,29 @@
#include "qquickwindow_p.h"
#include <QtQml/private/qv4persistent_p.h>
+#include "qquickwindowcontainer_p.h"
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImplPrivate : public QQuickWindowPrivate
+class Q_QUICK_EXPORT QQuickWindowQmlImplPrivate : public QQuickWindowPrivate
{
public:
QQuickWindowQmlImplPrivate();
- bool complete = false;
+ bool componentComplete = true;
+
bool visible = false;
+ bool visibleExplicitlySet = false;
QQuickWindow::Visibility visibility = QQuickWindow::AutomaticVisibility;
+ bool visibilityExplicitlySet = false;
+
QV4::PersistentValue rootItemMarker;
+
+ QMetaObject::Connection itemParentWindowChangeListener;
+
+ QObject *visualParent = nullptr;
+ QPointer<QQuickWindowContainer> windowContainer;
+ qreal z = 0.0;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qsginternaltextnode.cpp
index 171cbdee8b..32fde98b9f 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qsginternaltextnode.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qquicktextnode_p.h"
+#include "qsginternaltextnode_p.h"
#include "qquicktextnodeengine_p.h"
@@ -9,13 +9,12 @@
#include <private/qsgdistancefieldglyphnode_p.h>
#include <private/qquickclipnode_p.h>
#include <private/qquickitem_p.h>
-#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qquicktextdocument_p.h>
#include <QtCore/qpoint.h>
#include <qtextdocument.h>
#include <qtextlayout.h>
#include <qabstracttextdocumentlayout.h>
-#include <qxmlstream.h>
#include <private/qquickstyledtext_p.h>
#include <private/qquicktext_p_p.h>
#include <private/qfont_p.h>
@@ -26,57 +25,47 @@
QT_BEGIN_NAMESPACE
-namespace {
-
- class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
- {
- public:
- inline QTextCharFormat formatAccessor(int pos)
- {
- return format(pos);
- }
- };
-
-}
-
-Q_DECLARE_LOGGING_CATEGORY(lcVP)
-
/*!
- Creates an empty QQuickTextNode
+ Creates an empty QSGInternalTextNode
*/
-QQuickTextNode::QQuickTextNode(QQuickItem *ownerElement)
- : m_cursorNode(nullptr), m_ownerElement(ownerElement), m_useNativeRenderer(false), m_renderTypeQuality(-1)
+QSGInternalTextNode::QSGInternalTextNode(QSGRenderContext *renderContext)
+ : m_renderContext(renderContext)
{
#ifdef QSG_RUNTIME_DESCRIPTION
qsgnode_set_description(this, QLatin1String("text"));
#endif
+
+ static_assert(int(QSGTextNode::Normal) == int(QQuickText::Normal));
+ static_assert(int(QSGTextNode::Outline) == int(QQuickText::Outline));
+ static_assert(int(QSGTextNode::Raised) == int(QQuickText::Raised));
+ static_assert(int(QSGTextNode::Sunken) == int(QQuickText::Sunken));
+
+ static_assert(int(QSGTextNode::QtRendering) == int(QQuickText::QtRendering));
+ static_assert(int(QSGTextNode::NativeRendering) == int(QQuickText::NativeRendering));
+ static_assert(int(QSGTextNode::CurveRendering) == int(QQuickText::CurveRendering));
}
-QQuickTextNode::~QQuickTextNode()
+QSGInternalTextNode::~QSGInternalTextNode()
{
qDeleteAll(m_textures);
}
-QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QQuickText::TextStyle style, const QColor &styleColor,
- QSGNode *parentNode)
+QSGGlyphNode *QSGInternalTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QQuickText::TextStyle style, const QColor &styleColor,
+ QSGNode *parentNode)
{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
QRawFont font = glyphs.rawFont();
- bool preferNativeGlyphNode = m_useNativeRenderer;
- if (!preferNativeGlyphNode) {
- QRawFontPrivate *fontPriv = QRawFontPrivate::get(font);
- if (fontPriv->fontEngine->hasUnreliableGlyphOutline()) {
- preferNativeGlyphNode = true;
- } else {
- QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
- preferNativeGlyphNode = !fe->isSmoothlyScalable;
- }
- }
- QSGGlyphNode *node = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode, m_renderTypeQuality);
+ QSGTextNode::RenderType preferredRenderType = m_renderType;
+ if (m_renderType != NativeRendering) {
+ if (const QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine)
+ if (fe->hasUnreliableGlyphOutline() || !fe->isSmoothlyScalable)
+ preferredRenderType = QSGTextNode::NativeRendering;
+ }
- node->setOwnerElement(m_ownerElement);
+ QSGGlyphNode *node = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
+ preferredRenderType,
+ m_renderTypeQuality);
node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
node->setStyle(style);
node->setStyleColor(styleColor);
@@ -97,8 +86,9 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun
parentNode->appendChildNode(node);
if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
- QSGGlyphNode *fillNode = sg->sceneGraphContext()->createGlyphNode(sg, preferNativeGlyphNode, m_renderTypeQuality);
- fillNode->setOwnerElement(m_ownerElement);
+ QSGGlyphNode *fillNode = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
+ preferredRenderType,
+ m_renderTypeQuality);
fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
fillNode->setStyle(QQuickText::Normal);
fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
@@ -115,17 +105,16 @@ QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun
return node;
}
-void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
+void QSGInternalTextNode::setCursor(const QRectF &rect, const QColor &color)
{
if (m_cursorNode != nullptr)
delete m_cursorNode;
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, color);
+ m_cursorNode = m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color);
appendChildNode(m_cursorNode);
}
-void QQuickTextNode::clearCursor()
+void QSGInternalTextNode::clearCursor()
{
if (m_cursorNode)
removeChildNode(m_cursorNode);
@@ -133,42 +122,38 @@ void QQuickTextNode::clearCursor()
m_cursorNode = nullptr;
}
-void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
+void QSGInternalTextNode::addDecorationNode(const QRectF &rect, const QColor &color)
{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- appendChildNode(sg->sceneGraphContext()->createInternalRectangleNode(rect, color));
+ addRectangleNode(rect, color);
}
+void QSGInternalTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
+{
+ appendChildNode(m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color));
+}
-void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
+void QSGInternalTextNode::addImage(const QRectF &rect, const QImage &image)
{
- QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode(sg);
- QSGTexture *texture = sg->createTexture(image);
- if (m_ownerElement->smooth())
- texture->setFiltering(QSGTexture::Linear);
+ QSGInternalImageNode *node = m_renderContext->sceneGraphContext()->createInternalImageNode(m_renderContext);
+ QSGTexture *texture = m_renderContext->createTexture(image);
+ texture->setFiltering(m_filtering);
m_textures.append(texture);
node->setTargetRect(rect);
node->setInnerTargetRect(rect);
node->setTexture(texture);
- if (m_ownerElement->smooth())
- node->setFiltering(QSGTexture::Linear);
+ node->setFiltering(m_filtering);
appendChildNode(node);
node->update();
}
-void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument,
- const QColor &textColor,
- QQuickText::TextStyle style, const QColor &styleColor,
- const QColor &anchorColor,
- const QColor &selectionColor, const QColor &selectedTextColor,
- int selectionStart, int selectionEnd)
+void QSGInternalTextNode::doAddTextDocument(QPointF position, QTextDocument *textDocument,
+ int selectionStart, int selectionEnd)
{
QQuickTextNodeEngine engine;
- engine.setTextColor(textColor);
- engine.setSelectedTextColor(selectedTextColor);
- engine.setSelectionColor(selectionColor);
- engine.setAnchorColor(anchorColor);
+ engine.setTextColor(m_color);
+ engine.setSelectedTextColor(m_selectionTextColor);
+ engine.setSelectionColor(m_selectionColor);
+ engine.setAnchorColor(m_linkColor);
engine.setPosition(position);
QList<QTextFrame *> frames;
@@ -182,7 +167,7 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex
if (textFrame->firstPosition() > textFrame->lastPosition()
&& textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
const int pos = textFrame->firstPosition() - 1;
- ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(textDocument->documentLayout());
+ auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(textDocument->documentLayout());
QTextCharFormat format = a->formatAccessor(pos);
QRectF rect = a->frameBoundingRect(textFrame);
@@ -197,29 +182,26 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex
Q_ASSERT(!engine.currentLine().isValid());
QTextBlock block = it.currentBlock();
- engine.addTextBlock(textDocument, block, position, textColor, anchorColor, selectionStart, selectionEnd,
+ engine.addTextBlock(textDocument, block, position, m_color, m_linkColor, selectionStart, selectionEnd,
(textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ?
- m_ownerElement->clipRect() : QRectF()));
+ m_viewport : QRectF()));
++it;
}
}
}
- engine.addToSceneGraph(this, style, styleColor);
+ engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
}
-void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
- QQuickText::TextStyle style, const QColor &styleColor,
- const QColor &anchorColor,
- const QColor &selectionColor, const QColor &selectedTextColor,
- int selectionStart, int selectionEnd,
- int lineStart, int lineCount)
+void QSGInternalTextNode::doAddTextLayout(QPointF position, QTextLayout *textLayout,
+ int selectionStart, int selectionEnd,
+ int lineStart, int lineCount)
{
QQuickTextNodeEngine engine;
- engine.setTextColor(color);
- engine.setSelectedTextColor(selectedTextColor);
- engine.setSelectionColor(selectionColor);
- engine.setAnchorColor(anchorColor);
+ engine.setTextColor(m_color);
+ engine.setSelectedTextColor(m_selectionTextColor);
+ engine.setSelectionColor(m_selectionColor);
+ engine.setAnchorColor(m_linkColor);
engine.setPosition(position);
#if QT_CONFIG(im)
@@ -230,12 +212,6 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay
QVarLengthArray<QTextLayout::FormatRange> colorChanges;
engine.mergeFormats(textLayout, &colorChanges);
- // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport.
- QRectF viewport;
- if (m_ownerElement->flags().testFlag(QQuickItem::ItemObservesViewport)) {
- viewport = m_ownerElement->clipRect();
- qCDebug(lcVP) << "text viewport" << viewport;
- }
lineCount = lineCount >= 0
? qMin(lineStart + lineCount, textLayout->lineCount())
: textLayout->lineCount();
@@ -255,8 +231,9 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay
end += preeditLength;
}
#endif
- if (viewport.isNull() || (line.y() + line.height() > viewport.top() && line.y() < viewport.bottom())) {
- if (!inViewport && !viewport.isNull()) {
+ // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport.
+ if (m_viewport.isNull() || (line.y() + line.height() > m_viewport.top() && line.y() < m_viewport.bottom())) {
+ if (!inViewport && !m_viewport.isNull()) {
m_firstLineInViewport = i;
qCDebug(lcVP) << "first line in viewport" << i << "@" << line.y();
}
@@ -264,17 +241,17 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay
engine.setCurrentLine(line);
engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
} else if (inViewport) {
- Q_ASSERT(!viewport.isNull());
+ Q_ASSERT(!m_viewport.isNull());
m_firstLinePastViewport = i;
qCDebug(lcVP) << "first omitted line past bottom of viewport" << i << "@" << line.y();
break; // went past the bottom of the viewport, so we're done
}
}
- engine.addToSceneGraph(this, style, styleColor);
+ engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
}
-void QQuickTextNode::deleteContent()
+void QSGInternalTextNode::clear()
{
while (firstChild() != nullptr)
delete firstChild();
diff --git a/src/quick/items/qsginternaltextnode_p.h b/src/quick/items/qsginternaltextnode_p.h
new file mode 100644
index 0000000000..0d29657912
--- /dev/null
+++ b/src/quick/items/qsginternaltextnode_p.h
@@ -0,0 +1,199 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSGINTERNALTEXTNODE_P_H
+#define QSGINTERNALTEXTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgtextnode.h"
+#include "qquicktext_p.h"
+#include <qglyphrun.h>
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qtextlayout.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGGlyphNode;
+class QTextBlock;
+class QColor;
+class QTextDocument;
+class QSGContext;
+class QRawFont;
+class QSGInternalRectangleNode;
+class QSGClipNode;
+class QSGTexture;
+class QSGRenderContext;
+
+class QQuickTextNodeEngine;
+
+class Q_QUICK_EXPORT QSGInternalTextNode : public QSGTextNode
+{
+public:
+ QSGInternalTextNode(QSGRenderContext *renderContext);
+ ~QSGInternalTextNode();
+
+ static bool isComplexRichText(QTextDocument *);
+
+ void setColor(QColor color) override
+ {
+ m_color = color;
+ }
+
+ QColor color() const override
+ {
+ return m_color;
+ }
+
+ void setTextStyle(TextStyle textStyle) override
+ {
+ m_textStyle = textStyle;
+ }
+
+ TextStyle textStyle() override
+ {
+ return m_textStyle;
+ }
+
+ void setStyleColor(QColor styleColor) override
+ {
+ m_styleColor = styleColor;
+ }
+
+ QColor styleColor() const override
+ {
+ return m_styleColor;
+ }
+
+ void setLinkColor(QColor linkColor) override
+ {
+ m_linkColor = linkColor;
+ }
+
+ QColor linkColor() const override
+ {
+ return m_linkColor;
+ }
+
+ void setSelectionColor(QColor selectionColor) override
+ {
+ m_selectionColor = selectionColor;
+ }
+
+ QColor selectionColor() const override
+ {
+ return m_selectionColor;
+ }
+
+ void setSelectionTextColor(QColor selectionTextColor) override
+ {
+ m_selectionTextColor = selectionTextColor;
+ }
+
+ QColor selectionTextColor() const override
+ {
+ return m_selectionTextColor;
+ }
+
+ void setRenderTypeQuality(int renderTypeQuality) override
+ {
+ m_renderTypeQuality = renderTypeQuality;
+ }
+ int renderTypeQuality() const override
+ {
+ return m_renderTypeQuality;
+ }
+
+ void setRenderType(RenderType renderType) override
+ {
+ m_renderType = renderType;
+ }
+
+ RenderType renderType() const override
+ {
+ return m_renderType;
+ }
+
+ void setFiltering(QSGTexture::Filtering filtering) override
+ {
+ m_filtering = filtering;
+ }
+
+ QSGTexture::Filtering filtering() const override
+ {
+ return m_filtering;
+ }
+
+ void setViewport(const QRectF &viewport) override
+ {
+ m_viewport = viewport;
+ }
+
+ QRectF viewport() const override
+ {
+ return m_viewport;
+ }
+
+ void setCursor(const QRectF &rect, const QColor &color);
+ void clearCursor();
+
+ void addRectangleNode(const QRectF &rect, const QColor &color);
+ virtual void addDecorationNode(const QRectF &rect, const QColor &color);
+ void addImage(const QRectF &rect, const QImage &image);
+ void clear() override;
+ QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
+ QSGNode *parentNode = 0);
+
+ QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
+ QPair<int, int> renderedLineRange() const { return { m_firstLineInViewport, m_firstLinePastViewport }; }
+
+protected:
+ void doAddTextLayout(QPointF position,
+ QTextLayout *textLayout,
+ int selectionStart,
+ int selectionEnd,
+ int lineStart,
+ int lineCount) override;
+
+ void doAddTextDocument(QPointF position,
+ QTextDocument *textDocument,
+ int selectionStart,
+ int selectionEnd) override;
+
+private:
+ QSGInternalRectangleNode *m_cursorNode = nullptr;
+ QList<QSGTexture *> m_textures;
+ QSGRenderContext *m_renderContext = nullptr;
+ RenderType m_renderType = QtRendering;
+ TextStyle m_textStyle = Normal;
+ QRectF m_viewport;
+ QColor m_color = QColor(0, 0, 0);
+ QColor m_styleColor = QColor(0, 0, 0);
+ QColor m_linkColor = QColor(0, 0, 255);
+ QColor m_selectionColor = QColor(0, 0, 128);
+ QColor m_selectionTextColor = QColor(255, 255, 255);
+ QSGTexture::Filtering m_filtering = QSGTexture::Nearest;
+ int m_renderTypeQuality = -1;
+ int m_firstLineInViewport = -1;
+ int m_firstLinePastViewport = -1;
+
+ friend class QQuickTextEdit;
+ friend class QQuickTextEditPrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGINTERNALTEXTNODE_P_H