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 Quick Controls 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::widthChanged, this, &QQStyleKitDelegate::updateImplicitSize);
98 connect(m_delegateProperties, &QQStyleKitDelegateProperties::heightChanged, this, &QQStyleKitDelegate::updateImplicitSize);
99
100 emit delegateStyleChanged();
101}
102
103void QQStyleKitDelegate::updateImplicitSize()
104{
105 if (!m_delegateProperties)
106 return;
107
108 /* The implicit size of this item is determined by the following priority:
109 * 1. The width/height set on DelegateStyle (clamped to minimumWidth/minimumHeight)
110 * 2. The implicit size of the image overlay (if present)
111 * 3. Zero */
112 const qreal widthInStyle = qMax(m_delegateProperties->minimumWidth(), m_delegateProperties->width());
113 const qreal heightInStyle = qMax(m_delegateProperties->minimumHeight(), m_delegateProperties->height());
114 setImplicitWidth(widthInStyle > 0 || !m_imageOverlay ? widthInStyle : m_imageOverlay->implicitWidth());
115 setImplicitHeight(heightInStyle > 0 || !m_imageOverlay ? heightInStyle : m_imageOverlay->implicitHeight());
116}
117
118void QQStyleKitDelegate::maybeCreateColor()
119{
120 if (m_colorOverlay)
121 return;
122 if (!m_delegateProperties)
123 return;
124 if (m_delegateProperties->color().alpha() == 0
125 && (m_delegateProperties->border()->color().alpha() == 0
126 || m_delegateProperties->border()->width() == 0)) {
127 // Lazy-create the color rectangle later, if/when needed
128 connect(m_delegateProperties, &QQStyleKitDelegateProperties::colorChanged,
129 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
130 connect(m_delegateProperties->border(), &QQStyleKitBorderProperties::colorChanged,
131 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
132 connect(m_delegateProperties->border(), &QQStyleKitBorderProperties::widthChanged,
133 this, &QQStyleKitDelegate::maybeCreateColor, Qt::UniqueConnection);
134 return;
135 }
136
137 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::colorChanged,
138 this, &QQStyleKitDelegate::maybeCreateColor);
139 disconnect(m_delegateProperties->border(), &QQStyleKitBorderProperties::colorChanged,
140 this, &QQStyleKitDelegate::maybeCreateColor);
141 disconnect(m_delegateProperties->border(), &QQStyleKitBorderProperties::widthChanged,
142 this, &QQStyleKitDelegate::maybeCreateColor);
143
144 QQmlEngine *engine = qmlEngine(this);
145 Q_ASSERT(engine);
146 static QQmlComponent *component = nullptr;
147 if (!component || component->engine() != engine) {
148 delete component;
149 component = new QQmlComponent(engine);
150 const auto qmlCode = R"(
151 import QtQuick
152 Rectangle {
153 z: -3
154 width: parent.width
155 height: parent.height
156 color: delegateStyle.color
157 opacity: delegateStyle.opacity
158 topLeftRadius: delegateStyle.topLeftRadius
159 topRightRadius: delegateStyle.topRightRadius
160 bottomLeftRadius: delegateStyle.bottomLeftRadius
161 bottomRightRadius: delegateStyle.bottomRightRadius
162 border.width: delegateStyle.border.width
163 border.color: delegateStyle.border.color
164 }
165 )"_ba;
166 component->setData(qmlCode, QUrl());
167 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
168 }
169
170 QQmlContext *ctx = QQmlEngine::contextForObject(this);
171 m_colorOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
172 Q_ASSERT(m_colorOverlay);
173 m_colorOverlay->setParent(this);
174 m_colorOverlay->setParentItem(this);
175 component->completeCreate();
176}
177
178void QQStyleKitDelegate::maybeCreateGradient()
179{
180 /* Unlike a Rectangle, a StyledItem draws both the color and the gradient at
181 * the same time. This allows a style to define them independently. That way you can
182 * define a common semi-transparent grayscale gradient once for a delegate in the style
183 * (e.g for control.background.gradient), and then tint it with different colors for
184 * different controls, states, or themes (e.g for button.hovered.background.color). */
185 if (m_gradientOverlay)
186 return;
187 if (!m_delegateProperties)
188 return;
189 if (!m_delegateProperties->gradient()) {
190 connect(m_delegateProperties, &QQStyleKitDelegateProperties::gradientChanged,
191 this, &QQStyleKitDelegate::maybeCreateGradient, Qt::UniqueConnection);
192 return;
193 }
194
195 disconnect(m_delegateProperties, &QQStyleKitDelegateProperties::gradientChanged,
196 this, &QQStyleKitDelegate::maybeCreateGradient);
197
198 QQmlEngine *engine = qmlEngine(this);
199 Q_ASSERT(engine);
200 static QQmlComponent *component = nullptr;
201 if (!component || component->engine() != engine) {
202 delete component;
203 component = new QQmlComponent(engine);
204 const auto qmlCode = R"(
205 import QtQuick
206 Rectangle {
207 z: -2
208 width: parent.width
209 height: parent.height
210 color: "transparent"
211 visible: delegateStyle.gradient != null
212 gradient: delegateStyle.gradient
213 topLeftRadius: delegateStyle.topLeftRadius
214 topRightRadius: delegateStyle.topRightRadius
215 bottomLeftRadius: delegateStyle.bottomLeftRadius
216 bottomRightRadius: delegateStyle.bottomRightRadius
217 border.width: delegateStyle.border.width
218 border.color: delegateStyle.border.color
219 }
220 )"_ba;
221 component->setData(qmlCode, QUrl());
222 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
223 }
224
225 QQmlContext *ctx = QQmlEngine::contextForObject(this);
226 m_gradientOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
227 Q_ASSERT(m_gradientOverlay);
228 m_gradientOverlay->setParent(this);
229 m_gradientOverlay->setParentItem(this);
230 component->completeCreate();
231}
232
233void QQStyleKitDelegate::maybeCreateImage()
234{
235 if (m_imageOverlay)
236 return;
237 if (!m_delegateProperties)
238 return;
239 if (m_delegateProperties->image()->source().isEmpty()
240 || m_delegateProperties->image()->color().alpha() == 0) {
241 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
242 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
243 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
244 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
245 return;
246 }
247
248 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
249 this, &QQStyleKitDelegate::maybeCreateImage);
250 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
251 this, &QQStyleKitDelegate::maybeCreateImage);
252
253 QQmlEngine *engine = qmlEngine(this);
254 Q_ASSERT(engine);
255 static QQmlComponent *component = nullptr;
256 if (!component || component->engine() != engine) {
257 delete component;
258 component = new QQmlComponent(engine);
259 const auto qmlCode = R"(
260 import QtQuick.Controls.impl
261 ColorImage {
262 z: -1
263 width: parent.width
264 height: parent.height
265 color: delegateStyle.image.color
266 source: delegateStyle.image.source
267 fillMode: delegateStyle.image.fillMode
268 }
269 )"_ba;
270 component->setData(qmlCode, QUrl());
271 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
272 }
273
274 QQmlContext *ctx = QQmlEngine::contextForObject(this);
275 m_imageOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
276 m_imageOverlay->setParent(this);
277 m_imageOverlay->setParentItem(this);
278 component->completeCreate();
279
280 updateImplicitSize();
281 connect(m_imageOverlay, &QQuickItem::implicitWidthChanged, this, &QQStyleKitDelegate::updateImplicitSize);
282 connect(m_imageOverlay, &QQuickItem::implicitHeightChanged, this, &QQStyleKitDelegate::updateImplicitSize);
283}
284
285QT_END_NAMESPACE
286
287#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.