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
70{
71 return m_delegateComponent == s_defaultDelegateComponent;
72}
73
74void QQStyleKitDelegateContainer::updateImplicitSize()
75{
76 if (m_inGeometryChange) {
77 /* The delegate instance is expected (but not restricted) to bind its size to the parent
78 * container since the final size of the control (and its delegates) is set from the
79 * application. However, some delegates also change their implicit size when resized.
80 * Shape, for example, recomputes its implicit size from the bounding rect of its
81 * ShapePaths, which may in turn depend on the Shape's own size. And a change to the
82 * implicit size can cause the layout that the control is in to relayout (and resize)
83 * the control once more, which causes a binding loop. To prevent this, we ignore changes
84 * to the delegate's implicit size while we're resizing it. */
85 return;
86 }
87
88 qreal implicitWidth = 0;
89 qreal implicitHeight = 0;
90
91 if (m_delegateInstance) {
92 implicitWidth = m_delegateInstance->implicitWidth();
93 implicitHeight = m_delegateInstance->implicitHeight();
94 } else if (m_delegateProperties) {
95 /* The delegate instance may not exist yet (e.g. because it is hidden),
96 * but the container should still report the style's implicit size so
97 * that it reserves the correct space in a layout. */
98 implicitWidth = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->implicitWidth());
99 implicitHeight = m_delegateProperties->implicitHeight();
100 }
101
102 setImplicitWidth(implicitWidth);
103 setImplicitHeight(implicitHeight);
104}
105
106void QQStyleKitDelegateContainer::maybeCreateDelegate()
107{
108 if (m_delegateInstance)
109 return;
110 if (!m_delegateProperties)
111 return;
112
113 if (!m_delegateProperties->visible()) {
114 connect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
115 this, &QQStyleKitDelegateContainer::maybeCreateDelegate, Qt::UniqueConnection);
116 return;
117 }
118
119 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
120 this, &QQStyleKitDelegateContainer::maybeCreateDelegate);
121
122 const bool wasUsingDefaultDelegate = usingDefaultDelegate();
123
124 if (QQmlComponent *delegateComponent = m_delegateProperties->delegate()) {
125 m_delegateComponent = delegateComponent;
126 } else {
127 if (!s_defaultDelegateComponent || s_defaultDelegateComponent->engine() != qmlEngine(this)) {
128 delete s_defaultDelegateComponent;
129 QQmlEngine *engine = qmlEngine(this);
130 s_defaultDelegateComponent = new QQmlComponent(engine);
131 const QString qmlCode = QString::fromUtf8(R"(
132 import QtQuick
133 import Qt.labs.StyleKit
134 StyledItem {
135 width: parent.width
136 height: parent.height
137 }
138 )");
139 s_defaultDelegateComponent->setData(qmlCode.toUtf8(), QUrl());
140 Q_ASSERT_X(!s_defaultDelegateComponent->isError(), __FUNCTION__,
141 s_defaultDelegateComponent->errorString().toUtf8().constData());
142 }
143 m_delegateComponent = s_defaultDelegateComponent;
144 }
145
146 QQmlContext *ctx = QQmlEngine::contextForObject(this);
147 m_delegateInstance = qobject_cast<QQuickItem*>(m_delegateComponent->beginCreate(ctx));
148 Q_ASSERT(m_delegateInstance);
149 m_delegateInstance->setParent(this);
150 m_delegateInstance->setParentItem(this);
151 m_delegateInstance->setProperty("control", QVariant::fromValue(m_control.get()));
152 m_delegateInstance->setProperty("delegateStyle", QVariant::fromValue(m_delegateProperties.get()));
153 m_delegateComponent->completeCreate();
154
155 updateImplicitSize();
156 connect(m_delegateInstance, &QQuickItem::implicitWidthChanged, this, &QQStyleKitDelegateContainer::updateImplicitSize);
157 connect(m_delegateInstance, &QQuickItem::implicitHeightChanged, this, &QQStyleKitDelegateContainer::updateImplicitSize);
158
159 if (usingDefaultDelegate() != wasUsingDefaultDelegate)
160 emit usingDefaultDelegateChanged();
161}
162
163void QQStyleKitDelegateContainer::maybeCreateShadow()
164{
165 if (m_shadowInstance)
166 return;
167 if (!m_delegateProperties)
168 return;
169
170 if (!m_delegateProperties->visible()) {
171 connect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
172 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
173 return;
174 }
175 if (!m_delegateProperties->shadow()->visible()) {
176 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::visibleChanged,
177 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
178 return;
179 }
180 if (m_delegateProperties->shadow()->color().alpha() == 0) {
181 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::colorChanged,
182 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
183 return;
184 }
185 if (m_delegateProperties->shadow()->opacity() == 0) {
186 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::opacityChanged,
187 this, &QQStyleKitDelegateContainer::maybeCreateShadow, Qt::UniqueConnection);
188 return;
189 }
190
191 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::visibleChanged,
192 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
193 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::visibleChanged,
194 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
195 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::colorChanged,
196 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
197 disconnect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::opacityChanged,
198 this, &QQStyleKitDelegateContainer::maybeCreateShadow);
199
200 if (QQmlComponent *shadowComponent = m_delegateProperties->shadow()->delegate()) {
201 m_shadowComponent = shadowComponent;
202 } else {
203 if (!s_defaultShadowComponent || s_defaultShadowComponent->engine() != qmlEngine(this)) {
204 delete s_defaultShadowComponent;
205 QQmlEngine *engine = qmlEngine(this);
206 s_defaultShadowComponent = new QQmlComponent(engine);
207 const QString qmlCode = QString::fromUtf8(R"(
208 import Qt.labs.StyleKit.impl
209 Shadow {}
210 )");
211 s_defaultShadowComponent->setData(qmlCode.toUtf8(), QUrl());
212 Q_ASSERT_X(!s_defaultShadowComponent->isError(), __FUNCTION__,
213 s_defaultShadowComponent->errorString().toUtf8().constData());
214 }
215 m_shadowComponent = s_defaultShadowComponent;
216 }
217
218 QQmlContext *ctx = QQmlEngine::contextForObject(this);
219 m_shadowInstance = qobject_cast<QQuickItem*>(m_shadowComponent->beginCreate(ctx));
220 Q_ASSERT(m_shadowInstance);
221 m_shadowInstance->setParent(this);
222 m_shadowInstance->setParentItem(this);
223 m_shadowInstance->setZ(-1);
224 m_shadowInstance->setProperty("control", QVariant::fromValue(m_control.get()));
225 m_shadowInstance->setProperty("delegateStyle", QVariant::fromValue(m_delegateProperties.get()));
226 m_shadowComponent->completeCreate();
227}
228
230{
231 QQuickItem::componentComplete();
232 Q_ASSERT(m_delegateProperties);
233 Q_ASSERT(m_control);
234
235 maybeCreateDelegate();
236 connect(m_delegateProperties, &QQStyleKitDelegateProperties::delegateChanged, this, [this]{
237 if (!m_delegateInstance) {
238 maybeCreateDelegate();
239 } else {
240 const QQmlComponent *newDelegateComp = m_delegateProperties->delegate();
241 if (m_delegateComponent == newDelegateComp)
242 return;
243 if (!newDelegateComp && m_delegateComponent == s_defaultDelegateComponent) {
244 /* If newDelegateComp is nullptr, it means that we should use the default
245 * delegate instead, which we already do. */
246 return;
247 }
248
249 delete m_delegateInstance;
250 maybeCreateDelegate();
251 emit delegateInstanceChanged();
252 }
253 });
254
255 maybeCreateShadow();
256 connect(m_delegateProperties->shadow(), &QQStyleKitShadowProperties::delegateChanged, this, [this]{
257 if (!m_shadowInstance) {
258 maybeCreateShadow();
259 } else {
260 const QQmlComponent *newShadowComp = m_delegateProperties->shadow()->delegate();
261 if (m_shadowComponent == newShadowComp)
262 return;
263 if (!newShadowComp && m_shadowComponent == s_defaultShadowComponent) {
264 /* If newShadowComp is nullptr, it means that we should use the default
265 * delegate instead, which we already do. */
266 return;
268
269 delete m_shadowInstance;
270 maybeCreateShadow();
271 }
272 });
273
274 updateImplicitSize();
275}
276
277void QQStyleKitDelegateContainer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
278{
279 QScopedValueRollback implicitSizeUpdateGuard(m_inGeometryChange, true);
280 QQuickItem::geometryChange(newGeometry, oldGeometry);
281}
282
283QT_END_NAMESPACE
284
285#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.