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
qqstylekitdelegate.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
6
7#include <QtQuick/private/qquickrectangle_p.h>
8#include <QtQuick/private/qquickimplicitsizeitem_p_p.h>
9#include <QtQuickControls2Impl/private/qquickcolorimage_p.h>
10
12
13/*!
14 \qmltype StyledItem
15 \inqmlmodule Qt.labs.StyleKit
16 \inherits Item
17 \brief Renders a DelegateStyle.
18
19 StyledItem renders a \l DelegateStyle. It reads properties such as
20 \l {DelegateStyle::color}{color},
21 \l {DelegateStyle::border}{border},
22 \l {DelegateStyle::gradient}{gradient}, and
23 \l {DelegateStyle::image}{image}, and creates the corresponding
24 visual elements internally.
25
26 If the \l {DelegateStyle::delegate}{delegate} set on a DelegateStyle
27 is \c null (the default), StyledItem will automatically be used for rendering.
28
29 A custom \l {DelegateStyle::delegate}{delegate} can be set to
30 any Item. But combined with a StyledItem, you can retain
31 the default rendering while adding overlay, underlay, or shader effects.
32 The following snippet shows how to draw an extra Item on top of the
33 default delegate:
34
35 \snippet StyledItemOverlay.qml overlay
36
37 \labs
38
39 For more examples of overlays, underlays and shader effects, see the
40 \l{StyleKit Example}.
41
42 \sa DelegateStyle, {DelegateStyle::delegate}{delegate}, {DelegateStyle::data}{data}
43*/
44
45/*!
46 \qmlproperty DelegateStyle StyledItem::delegateStyle
47
48 The \l DelegateStyle that this item renders.
49
50 This property is \l {Required Properties}{required}. When
51 StyledItem is the root item of a
52 \l {DelegateStyle::delegate}{delegate}, it is set automatically.
53 But when used as a child inside a custom delegate, it must be set
54 explicitly.
55
56 The following snippet uses a custom delegate that draws a star
57 underneath the default slider handle. Since the root item is not
58 a StyledItem, it declares a \l {Required Properties}{required}
59 property \c {delegateStyle} (which is assigned automatically), and
60 forwards it to the child StyledItem:
61
62 \snippet StyledItemUnderlay.qml underlay
63
64 \sa DelegateStyle, {DelegateStyle::delegate}{delegate}, {DelegateStyle::data}{data}
65*/
66
68 : QQuickImplicitSizeItem(*new QQuickImplicitSizeItemPrivate, parent)
69{
70}
71
72QQStyleKitDelegateProperties *QQStyleKitDelegate::delegateStyle() const
73{
74 return m_delegateProperties;
75}
76
77void QQStyleKitDelegate::setDelegateStyle(QQStyleKitDelegateProperties *delegateStyle)
78{
79 if (m_delegateProperties == delegateStyle)
80 return;
81
82 if (m_delegateProperties)
83 disconnect(m_delegateProperties, nullptr, this, nullptr);
84
85 m_delegateProperties = delegateStyle;
86
87 if (!qmlEngine(this)) {
88 qmlWarning(this) << "Unable to draw delegate: no QQmlEngine found";
89 return;
90 }
91
92 maybeCreateColor();
93 maybeCreateGradient();
94 maybeCreateImage();
95 updateImplicitSize();
96
97 connect(m_delegateProperties, &QQStyleKitDelegateProperties::implicitWidthChanged, this, &QQStyleKitDelegate::updateImplicitSize);
98 connect(m_delegateProperties, &QQStyleKitDelegateProperties::implicitHeightChanged, this, &QQStyleKitDelegate::updateImplicitSize);
99
100 emit delegateStyleChanged();
101}
102
103void QQStyleKitDelegate::updateImplicitSize()
104{
105 if (!m_delegateProperties)
106 return;
107
108 /* The implicit size is determined by the following priority:
109 * 1. Explicit implicit size set on QQStyleKitDelegateProperties
110 * 2. Implicit size of the image (if present)
111 * 3. Zero
112 * The implicit size is read-only because it's calculated in C++ from internal
113 * child items that are intentionally not exposed to QML. */
114 const qreal impWidthInStyle = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->implicitWidth());
115 const qreal impHeightInStyle = m_delegateProperties->implicitHeight();
116 setImplicitWidth(impWidthInStyle > 0 || !m_imageOverlay ? impWidthInStyle : m_imageOverlay->implicitWidth());
117 setImplicitHeight(impHeightInStyle > 0 || !m_imageOverlay ? impHeightInStyle : m_imageOverlay->implicitHeight());
118}
119
120void QQStyleKitDelegate::maybeCreateColor()
121{
122 if (m_colorOverlay)
123 return;
124 if (!m_delegateProperties)
125 return;
126 if (m_delegateProperties->color().alpha() == 0
127 && (m_delegateProperties->border()->color().alpha() == 0
128 || m_delegateProperties->border()->width() == 0)) {
129 // Lazy-create the color rectangle later, if/when needed
130 connect(m_delegateProperties, &QQStyleKitDelegateProperties::colorChanged,
131 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
132 connect(m_delegateProperties->border(), &QQStyleKitBorderProperties::colorChanged,
133 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
134 connect(m_delegateProperties->border(), &QQStyleKitBorderProperties::widthChanged,
135 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
136 return;
137 }
138
139 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::colorChanged,
140 this, &QQStyleKitDelegate::maybeCreateColor);
141 disconnect(m_delegateProperties->border(), &QQStyleKitBorderProperties::colorChanged,
142 this, &QQStyleKitDelegate::maybeCreateColor);
143 disconnect(m_delegateProperties->border(), &QQStyleKitBorderProperties::widthChanged,
144 this, &QQStyleKitDelegate::maybeCreateColor);
145
146 QQmlEngine *engine = qmlEngine(this);
147 Q_ASSERT(engine);
148 static QQmlComponent *component = nullptr;
149 if (!component || component->engine() != engine) {
150 delete component;
151 component = new QQmlComponent(engine);
152 const QString qmlCode = QString::fromUtf8(R"(
153 import QtQuick
154 Rectangle {
155 z: -3
156 width: parent.width
157 height: parent.height
158 color: delegateStyle.color
159 opacity: delegateStyle.opacity
160 topLeftRadius: delegateStyle.topLeftRadius
161 topRightRadius: delegateStyle.topRightRadius
162 bottomLeftRadius: delegateStyle.bottomLeftRadius
163 bottomRightRadius: delegateStyle.bottomRightRadius
164 border.width: delegateStyle.border.width
165 border.color: delegateStyle.border.color
166 }
167 )");
168 component->setData(qmlCode.toUtf8(), QUrl());
169 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
170 }
171
172 QQmlContext *ctx = QQmlEngine::contextForObject(this);
173 m_colorOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
174 Q_ASSERT(m_colorOverlay);
175 m_colorOverlay->setParent(this);
176 m_colorOverlay->setParentItem(this);
177 component->completeCreate();
178}
179
180void QQStyleKitDelegate::maybeCreateGradient()
181{
182 /* Unlike a Rectangle, a StyledItem draws both the color and the gradient at
183 * the same time. This allows a style to define them independently. That way you can
184 * define a common semi-transparent grayscale gradient once for a delegate in the style
185 * (e.g for control.background.gradient), and then tint it with different colors for
186 * different controls, states, or themes (e.g for button.hovered.background.color). */
187 if (m_gradientOverlay)
188 return;
189 if (!m_delegateProperties)
190 return;
191 if (!m_delegateProperties->gradient()) {
192 connect(m_delegateProperties, &QQStyleKitDelegateProperties::gradientChanged,
193 this, &QQStyleKitDelegate::maybeCreateGradient, Qt::UniqueConnection);
194 return;
195 }
196
197 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::gradientChanged,
198 this, &QQStyleKitDelegate::maybeCreateGradient);
199
200 QQmlEngine *engine = qmlEngine(this);
201 Q_ASSERT(engine);
202 static QQmlComponent *component = nullptr;
203 if (!component || component->engine() != engine) {
204 delete component;
205 component = new QQmlComponent(engine);
206 const QString qmlCode = QString::fromUtf8(R"(
207 import QtQuick
208 Rectangle {
209 z: -2
210 width: parent.width
211 height: parent.height
212 color: "transparent"
213 gradient: delegateStyle.gradient
214 topLeftRadius: delegateStyle.topLeftRadius
215 topRightRadius: delegateStyle.topRightRadius
216 bottomLeftRadius: delegateStyle.bottomLeftRadius
217 bottomRightRadius: delegateStyle.bottomRightRadius
218 border.width: delegateStyle.border.width
219 border.color: delegateStyle.border.color
220 }
221 )");
222 component->setData(qmlCode.toUtf8(), QUrl());
223 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
224 }
225
226 QQmlContext *ctx = QQmlEngine::contextForObject(this);
227 m_gradientOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
228 Q_ASSERT(m_gradientOverlay);
229 m_gradientOverlay->setParent(this);
230 m_gradientOverlay->setParentItem(this);
231 component->completeCreate();
232}
233
234void QQStyleKitDelegate::maybeCreateImage()
235{
236 if (m_imageOverlay)
237 return;
238 if (!m_delegateProperties)
239 return;
240 if (m_delegateProperties->image()->source().isEmpty()
241 || m_delegateProperties->image()->color().alpha() == 0) {
242 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
243 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
244 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
245 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
246 return;
247 }
248
249 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
250 this, &QQStyleKitDelegate::maybeCreateImage);
251 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
252 this, &QQStyleKitDelegate::maybeCreateImage);
253
254 QQmlEngine *engine = qmlEngine(this);
255 Q_ASSERT(engine);
256 static QQmlComponent *component = nullptr;
257 if (!component || component->engine() != engine) {
258 delete component;
259 component = new QQmlComponent(engine);
260 const QString qmlCode = QString::fromUtf8(R"(
261 import QtQuick.Controls.impl
262 ColorImage {
263 z: -1
264 width: parent.width
265 height: parent.height
266 color: delegateStyle.image.color
267 source: delegateStyle.image.source
268 fillMode: delegateStyle.image.fillMode
269 }
270 )");
271 component->setData(qmlCode.toUtf8(), QUrl());
272 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
273 }
274
275 QQmlContext *ctx = QQmlEngine::contextForObject(this);
276 m_imageOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
277 m_imageOverlay->setParent(this);
278 m_imageOverlay->setParentItem(this);
279 component->completeCreate();
280
281 updateImplicitSize();
282 connect(m_imageOverlay, &QQuickItem::implicitWidthChanged, this, &QQStyleKitDelegate::updateImplicitSize);
283 connect(m_imageOverlay, &QQuickItem::implicitHeightChanged, this, &QQStyleKitDelegate::updateImplicitSize);
284}
285
286QT_END_NAMESPACE
287
288#include "moc_qqstylekitdelegate_p.cpp"
void setDelegateStyle(QQStyleKitDelegateProperties *delegateStyle)
QQStyleKitDelegateProperties * delegateStyle() const
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
Combined button and popup list for selecting options.