Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqstylekitdelegatecontainer.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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#include <QtQuick/private/qquickitem_p.h>
5
7#include "../qqstylekitcontrolproperties_p.h"
8
10
11QQmlComponent* QQStyleKitDelegateContainer::s_defaultDelegateComponent = nullptr;
12QQmlComponent* QQStyleKitDelegateContainer::s_defaultShadowComponent = nullptr;
13
14QQStyleKitDelegateContainer::QQStyleKitDelegateContainer(QQuickItem *parent)
15 : QQuickItem(parent)
16{
17}
18
20{
21 // Disconnect before QQuickItem::~QQuickItem() reparents children, which emits
22 // signals. At that point our vtable would already be replaced with QQuickItem's,
23 // causing assertObjectType to fail.
24 if (m_delegateInstance)
25 disconnect(m_delegateInstance, nullptr, this, nullptr);
26}
27
28QQStyleKitDelegateProperties *QQStyleKitDelegateContainer::delegateStyle() const
29{
30 return m_delegateProperties;
31}
32
33void QQStyleKitDelegateContainer::setDelegateStyle(QQStyleKitDelegateProperties *delegateProperties)
34{
35 if (m_delegateProperties == delegateProperties)
36 return;
37
38 if (m_delegateProperties) {
39 /* We don't expect m_delegateProperties to change after componentCompleted(), since it's bound
40 * to a controls StyleKitReader, which is not supposed to change. So, considering that there
41 * might be hundreds of delegate instances in an application, we try to save some connections
42 * this way. But note, this is only an optimization, not a technical limitation. */
43 qmlWarning(this) << "Changing delegateStyle on StyleKitContainer is not supported.";
44 return;
45 }
46
47 m_delegateProperties = delegateProperties;
48 emit delegateStyleChanged();
49}
50
52{
53 return m_control;
54}
55
57{
58 if (m_control == control)
59 return;
60 m_control = control;
61 emit quickControlChanged();
62}
63
65{
66 return m_delegateInstance;
67}
68
69void QQStyleKitDelegateContainer::updateImplicitSize()
70{
71 if (m_inGeometryChange) {
72 /* The delegate instance is expected (but not restricted) to bind its size to the parent
73 * container since the final size of the control (and its delegates) is set from the
74 * application. However, some delegates also change their implicit size when resized.
75 * Shape, for example, recomputes its implicit size from the bounding rect of its
76 * ShapePaths, which may in turn depend on the Shape's own size. And a change to the
77 * implicit size can cause the layout that the control is in to relayout (and resize)
78 * the control once more, which causes a binding loop. To prevent this, we ignore changes
79 * to the delegate's implicit size while we're resizing it. */
80 return;
81 }
82
83 qreal implicitWidth = 0;
84 qreal implicitHeight = 0;
85
86 if (m_delegateInstance) {
87 implicitWidth = m_delegateInstance->implicitWidth();
88 implicitHeight = m_delegateInstance->implicitHeight();
89 } else if (m_delegateProperties) {
90 /* The delegate instance may not exist yet (e.g. because it is hidden),
91 * but the container should still report the style's implicit size so
92 * that it reserves the correct space in a layout. */
93 implicitWidth = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->width());
94 implicitHeight = qMax(m_delegateProperties->minimumHeight(), m_delegateProperties->height());
95 }
96
97 setImplicitWidth(implicitWidth);
98 setImplicitHeight(implicitHeight);
99}
100
101void QQStyleKitDelegateContainer::maybeCreateDelegate()
102{
103 if (m_delegateInstance)
104 return;
105 if (!m_delegateProperties)
106 return;
107
108 if (!m_delegateProperties->visible()) {
109 connect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
110 this, &QQStyleKitDelegateContainer::maybeCreateDelegate, Qt::UniqueConnection);
111 return;
112 }
113
114 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
115 this, &QQStyleKitDelegateContainer::maybeCreateDelegate);
116
117 if (QQmlComponent *delegateComponent = m_delegateProperties->delegate()) {
118 m_delegateComponent = delegateComponent;
119 } else {
120 if (!s_defaultDelegateComponent || s_defaultDelegateComponent->engine() != qmlEngine(this)) {
121 delete s_defaultDelegateComponent;
122 QQmlEngine *engine = qmlEngine(this);
123 s_defaultDelegateComponent = new QQmlComponent(engine);
124 const QString qmlCode = QString::fromUtf8(R"(
125 import Qt.labs.StyleKit
126 StyledItem {
127 visible: delegateStyle.visible
128 width: parent.width
129 height: parent.height
130 }
131 )");
132 s_defaultDelegateComponent->setData(qmlCode.toUtf8(), QUrl());
133 Q_ASSERT_X(!s_defaultDelegateComponent->isError(), __FUNCTION__,
134 s_defaultDelegateComponent->errorString().toUtf8().constData());
135 }
136 m_delegateComponent = s_defaultDelegateComponent;
137 }
138
139 QQmlContext *ctx = QQmlEngine::contextForObject(this);
140 m_delegateInstance = qobject_cast<QQuickItem*>(m_delegateComponent->beginCreate(ctx));
141 Q_ASSERT(m_delegateInstance);
142 m_delegateInstance->setParent(this);
143 m_delegateInstance->setParentItem(this);
144 m_delegateInstance->setProperty("control", QVariant::fromValue(m_control.get()));
145 m_delegateInstance->setProperty("delegateStyle", QVariant::fromValue(m_delegateProperties.get()));
146 m_delegateComponent->completeCreate();
147
148 updateImplicitSize();
149 connect(m_delegateInstance, &QQuickItem::implicitWidthChanged, this, &QQStyleKitDelegateContainer::updateImplicitSize);
150 connect(m_delegateInstance, &QQuickItem::implicitHeightChanged, this, &QQStyleKitDelegateContainer::updateImplicitSize);
151
152}
153
154void QQStyleKitDelegateContainer::maybeCreateShadow()
155{
156 if (m_shadowInstance)
157 return;
158 if (!m_delegateProperties)
159 return;
160
161 if (!m_delegateProperties->visible()) {
162 connect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
163 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
164 return;
165 }
166 if (!m_delegateProperties->shadow()->visible()) {
167 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::visibleChanged,
168 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
169 return;
170 }
171 if (m_delegateProperties->shadow()->color().alpha() == 0) {
172 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::colorChanged,
173 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
174 return;
175 }
176 if (m_delegateProperties->shadow()->opacity() == 0) {
177 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::opacityChanged,
178 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
179 return;
180 }
181
182 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
183 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
184 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::visibleChanged,
185 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
186 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::colorChanged,
187 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
188 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::opacityChanged,
189 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
190
191 if (QQmlComponent *shadowComponent = m_delegateProperties->shadow()->delegate()) {
192 m_shadowComponent = shadowComponent;
193 } else {
194 if (!s_defaultShadowComponent || s_defaultShadowComponent->engine() != qmlEngine(this)) {
195 delete s_defaultShadowComponent;
196 QQmlEngine *engine = qmlEngine(this);
197 s_defaultShadowComponent = new QQmlComponent(engine);
198 const QString qmlCode = QString::fromUtf8(R"(
199 import Qt.labs.StyleKit.impl
200 Shadow {}
201 )");
202 s_defaultShadowComponent->setData(qmlCode.toUtf8(), QUrl());
203 Q_ASSERT_X(!s_defaultShadowComponent->isError(), __FUNCTION__,
204 s_defaultShadowComponent->errorString().toUtf8().constData());
205 }
206 m_shadowComponent = s_defaultShadowComponent;
207 }
208
209 QQmlContext *ctx = QQmlEngine::contextForObject(this);
210 m_shadowInstance = qobject_cast<QQuickItem*>(m_shadowComponent->beginCreate(ctx));
211 Q_ASSERT(m_shadowInstance);
212 m_shadowInstance->setParent(this);
213 m_shadowInstance->setParentItem(this);
214 m_shadowInstance->setZ(-1);
215 m_shadowInstance->setProperty("control", QVariant::fromValue(m_control.get()));
216 m_shadowInstance->setProperty("delegateStyle", QVariant::fromValue(m_delegateProperties.get()));
217 m_shadowComponent->completeCreate();
218}
219
221{
222 QQuickItem::componentComplete();
223 Q_ASSERT(m_delegateProperties);
224 Q_ASSERT(m_control);
225
226 maybeCreateDelegate();
227 connect(m_delegateProperties, &QQStyleKitDelegateProperties::delegateChanged, this, [this]{
228 if (!m_delegateInstance) {
229 maybeCreateDelegate();
230 } else {
231 const QQmlComponent *newDelegateComp = m_delegateProperties->delegate();
232 if (m_delegateComponent == newDelegateComp)
233 return;
234 if (!newDelegateComp && m_delegateComponent == s_defaultDelegateComponent) {
235 /* If newDelegateComp is nullptr, it means that we should use the default
236 * delegate instead, which we already do. */
237 return;
238 }
239
240 delete m_delegateInstance;
241 maybeCreateDelegate();
242 emit delegateInstanceChanged();
243 }
244 });
245
246 maybeCreateShadow();
247 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::delegateChanged, this, [this]{
248 if (!m_shadowInstance) {
249 maybeCreateShadow();
250 } else {
251 const QQmlComponent *newShadowComp = m_delegateProperties->shadow()->delegate();
252 if (m_shadowComponent == newShadowComp)
253 return;
254 if (!newShadowComp && m_shadowComponent == s_defaultShadowComponent) {
255 /* If newShadowComp is nullptr, it means that we should use the default
256 * delegate instead, which we already do. */
257 return;
258 }
259
260 delete m_shadowInstance;
261 maybeCreateShadow();
262 }
263 });
264
265 /* rotation and scale are applied here rather than as QML bindings
266 * in each QML file that uses a container, simply to avoid repeating the same
267 * bindings in all container instances. But note, the properties we set from
268 * c++ cannot then be bound to another value from QML since the c++ code would
269 * ignore and overwrite such bindings. For this reason, properties like 'visible'
270 * still needs to be set from QML since a container should sometimes be hidden
271 * based on the state of the control (e.g a menu arrow should only be visible
272 * when the menu has children etc). */
273 setRotation(m_delegateProperties->rotation());
274 setScale(m_delegateProperties->scale());
275
276 connect(m_delegateProperties, &QQStyleKitDelegateProperties::rotationChanged, this, [this]() {
277 setRotation(m_delegateProperties->rotation());
278 });
279 connect(m_delegateProperties, &QQStyleKitDelegateProperties::scaleChanged, this, [this]() {
280 setScale(m_delegateProperties->scale());
281 });
282
283 updateImplicitSize();
284}
285
286void QQStyleKitDelegateContainer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
287{
288 QScopedValueRollback implicitSizeUpdateGuard(m_inGeometryChange, true);
289 QQuickItem::geometryChange(newGeometry, oldGeometry);
290}
291
292QT_END_NAMESPACE
293
294#include "moc_qqstylekitdelegatecontainer_p.cpp"
QQStyleKitDelegateProperties * delegateStyle() const
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void setDelegateStyle(QQStyleKitDelegateProperties *delegateProperties)
Combined button and popup list for selecting options.