1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQUICKITEMCHANGELISTENER_P_H
5#define QQUICKITEMCHANGELISTENER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/qxptype_traits.h>
19#include <QtQml/private/qqmldata_p.h>
20#include <QtQuick/qquickitem.h>
21#include <QtQuick/private/qtquickglobal_p.h>
22
23QT_BEGIN_NAMESPACE
24
25class QRectF;
26class QQuickItem;
27class QQuickAnchorsPrivate;
28
29class QQuickGeometryChange
30{
31public:
32 enum Kind: int {
33 Nothing = 0x00,
34 X = 0x01,
35 Y = 0x02,
36 Width = 0x04,
37 Height = 0x08,
38
39 Size = Width | Height,
40 All = X | Y | Size
41 };
42
43 QQuickGeometryChange(int change = Nothing)
44 : kind(change)
45 {}
46
47 bool noChange() const { return kind == Nothing; }
48 bool anyChange() const { return !noChange(); }
49
50 bool xChange() const { return kind & X; }
51 bool yChange() const { return kind & Y; }
52 bool widthChange() const { return kind & Width; }
53 bool heightChange() const { return kind & Height; }
54
55 bool positionChange() const { return xChange() || yChange(); }
56 bool sizeChange() const { return widthChange() || heightChange(); }
57
58 bool horizontalChange() const { return xChange() || widthChange(); }
59 bool verticalChange() const { return yChange() || heightChange(); }
60
61 void setXChange(bool enabled) { set(bits: X, enabled); }
62 void setYChange(bool enabled) { set(bits: Y, enabled); }
63 void setWidthChange(bool enabled) { set(bits: Width, enabled); }
64 void setHeightChange(bool enabled) { set(bits: Height, enabled); }
65 void setSizeChange(bool enabled) { set(bits: Size, enabled); }
66 void setAllChanged(bool enabled) { set(bits: All, enabled); }
67 void setHorizontalChange(bool enabled) { set(bits: X | Width, enabled); }
68 void setVerticalChange(bool enabled) { set(bits: Y | Height, enabled); }
69
70 void set(int bits, bool enabled)
71 {
72 if (enabled) {
73 kind |= bits;
74 } else {
75 kind &= ~bits;
76 }
77 }
78
79 bool matches(QQuickGeometryChange other) const { return kind & other.kind; }
80
81private:
82 int kind;
83};
84
85#define QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING
86
87class Q_QUICK_EXPORT QQuickItemChangeListener
88{
89public:
90 virtual ~QQuickItemChangeListener();
91
92 virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {}
93 virtual void itemSiblingOrderChanged(QQuickItem *) {}
94 virtual void itemVisibilityChanged(QQuickItem *) {}
95 virtual void itemEnabledChanged(QQuickItem *) {}
96 virtual void itemOpacityChanged(QQuickItem *) {}
97 virtual void itemDestroyed(QQuickItem *) {}
98 virtual void itemChildAdded(QQuickItem *, QQuickItem * /* child */ ) {}
99 virtual void itemChildRemoved(QQuickItem *, QQuickItem * /* child */ ) {}
100 virtual void itemParentChanged(QQuickItem *, QQuickItem * /* parent */ ) {}
101 virtual void itemRotationChanged(QQuickItem *) {}
102 virtual void itemImplicitWidthChanged(QQuickItem *) {}
103 virtual void itemImplicitHeightChanged(QQuickItem *) {}
104 virtual void itemFocusChanged(QQuickItem *, Qt::FocusReason /* reason */) {}
105 virtual void itemScaleChanged(QQuickItem *) {}
106 virtual void itemTransformChanged(QQuickItem *, QQuickItem * /* transformedItem */) {}
107
108 virtual QQuickAnchorsPrivate *anchorPrivate() { return nullptr; }
109 virtual bool baseDeleted(const QObject *caller) const
110 {
111 if (auto ptr = dynamic_cast<const QObjectPrivate *>(this))
112 return ptr->q_ptr != caller && QQmlData::wasDeleted(priv: ptr);
113 return false;
114 }
115 virtual QString debugName() const {
116 return QStringLiteral("QQuickItemChangeListener(0x%1)").arg(a: quintptr(this), fieldWidth: 0, base: 16);
117 }
118 virtual void addSourceItem(QQuickItem *) {}
119 virtual void removeSourceItem(QQuickItem *) {}
120};
121
122/*!
123 \internal
124
125 Helper class using CRTP to add tracking of listeners to a
126 QQuickItemChangeListener implementation. Instantiated with the
127 class that implements the QQuickItemChangeListener interface, like
128
129 \code
130 class QQuickThingPrivate : public QQuickItemPrivate,
131 public QSafeQuickItemChangeListener<QQuickThingPrivate>
132 \endcode
133
134 When destroyed, QSafeQuickItemChangeListener will emit warning messages
135 in \c{-developer-build} configurations when listeners are still registered.
136 As the listener is at this point partially destroyed, any call to the
137 listener interface might result in undefined behavior, such as use-after-free.
138
139 Notifications to registered listeners will in addition check if the listener
140 is already in the destructor, and warn if that's the case. The \c{baseDeleted}
141 override checks for an \c inDestructor or \c wasDeleted data member of the
142 class implementing the QSafeQuickItemChangeListener interface.
143*/
144template<typename Self>
145class QSafeQuickItemChangeListener : public QQuickItemChangeListener
146{
147public:
148 ~QSafeQuickItemChangeListener() override
149 {
150#ifdef QT_BUILD_INTERNAL
151 for (const auto &sourceItem : std::as_const(t&: m_sourceItems)) {
152 if (sourceItem)
153 qCritical() << "Change Listener is still registered with item" << sourceItem;
154 }
155#endif
156 }
157
158 template <typename T>
159 using InDestructorTest = decltype(T::inDestructor);
160 template <typename T>
161 using WasDeletedTest = decltype(T::wasDeleted);
162
163 bool baseDeleted(const QObject *caller) const override
164 {
165 Q_UNUSED(caller);
166 const Self *self = static_cast<const Self *>(this);
167 if constexpr (qxp::is_detected_v<InDestructorTest, Self>) {
168 bool same = false;
169 if constexpr (std::is_convertible_v<Self *, QObjectPrivate *>)
170 same = self->q_ptr == caller;
171 return !same && self->inDestructor;
172 } else if constexpr (qxp::is_detected_v<WasDeletedTest, Self>) {
173 bool same = false;
174 if constexpr (std::is_convertible_v<Self *, QObjectPrivate *>)
175 same = self->q_ptr == caller;
176 return !same && self->wasDeleted;
177 } else if constexpr (std::is_convertible_v<Self *, QQuickItem *>) {
178 return self != caller && QQmlData::wasDeleted(self);
179 } else if constexpr (std::is_convertible_v<Self *, QObject *>) {
180 return self != caller && QQmlData::wasDeleted(self);
181 } else {
182 static_assert(QtPrivate::type_dependent_false<Self>(),
183 "Don't know where destruction state is stored");
184 }
185 }
186
187 QString debugName() const override
188 {
189 QString result;
190 QDebug dbg(&result);
191 dbg.nospace().noquote();
192 const Self *self = static_cast<const Self *>(this);
193 if constexpr (std::is_convertible_v<Self *, QObject *>) {
194 dbg << self;
195 } else if constexpr (std::is_convertible_v<Self *, QObjectPrivate *>) {
196 dbg << self->q_ptr << "::d_ptr";
197 } else {
198 dbg << QMetaType::fromType<Self>().name() << '('
199 << reinterpret_cast<const void *>(self) << ')';
200 }
201 return result;
202 }
203
204#ifdef QT_BUILD_INTERNAL
205 void addSourceItem(QQuickItem *item) override {
206 m_sourceItems << item;
207 }
208 void removeSourceItem(QQuickItem *item) override {
209 m_sourceItems.removeAll(t: item);
210 }
211
212 QList<QPointer<QQuickItem>> m_sourceItems;
213#endif
214};
215
216QT_END_NAMESPACE
217
218#endif // QQUICKITEMCHANGELISTENER_P_H
219

source code of qtdeclarative/src/quick/items/qquickitemchangelistener_p.h