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::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 = qMax(m_delegateProperties->minimumHeight(), 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 auto qmlCode = 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 )"_ba;
168 component->setData(qmlCode, 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 auto qmlCode = R"(
207 import QtQuick
208 Rectangle {
209 z: -2
210 width: parent.width
211 height: parent.height
212 color: "transparent"
213 visible: delegateStyle.gradient != null
214 gradient: delegateStyle.gradient
215 topLeftRadius: delegateStyle.topLeftRadius
216 topRightRadius: delegateStyle.topRightRadius
217 bottomLeftRadius: delegateStyle.bottomLeftRadius
218 bottomRightRadius: delegateStyle.bottomRightRadius
219 border.width: delegateStyle.border.width
220 border.color: delegateStyle.border.color
221 }
222 )"_ba;
223 component->setData(qmlCode, QUrl());
224 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
225 }
226
227 QQmlContext *ctx = QQmlEngine::contextForObject(this);
228 m_gradientOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
229 Q_ASSERT(m_gradientOverlay);
230 m_gradientOverlay->setParent(this);
231 m_gradientOverlay->setParentItem(this);
232 component->completeCreate();
233}
234
235void QQStyleKitDelegate::maybeCreateImage()
236{
237 if (m_imageOverlay)
238 return;
239 if (!m_delegateProperties)
240 return;
241 if (m_delegateProperties->image()->source().isEmpty()
242 || m_delegateProperties->image()->color().alpha() == 0) {
243 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
244 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
245 connect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
246 this, &QQStyleKitDelegate::maybeCreateImage, Qt::UniqueConnection);
247 return;
248 }
249
250 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::sourceChanged,
251 this, &QQStyleKitDelegate::maybeCreateImage);
252 disconnect(m_delegateProperties->image(), &QQStyleKitImageProperties::colorChanged,
253 this, &QQStyleKitDelegate::maybeCreateImage);
254
255 QQmlEngine *engine = qmlEngine(this);
256 Q_ASSERT(engine);
257 static QQmlComponent *component = nullptr;
258 if (!component || component->engine() != engine) {
259 delete component;
260 component = new QQmlComponent(engine);
261 const auto qmlCode = R"(
262 import QtQuick.Controls.impl
263 ColorImage {
264 z: -1
265 width: parent.width
266 height: parent.height
267 color: delegateStyle.image.color
268 source: delegateStyle.image.source
269 fillMode: delegateStyle.image.fillMode
270 }
271 )"_ba;
272 component->setData(qmlCode, QUrl());
273 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
274 }
275
276 QQmlContext *ctx = QQmlEngine::contextForObject(this);
277 m_imageOverlay = qobject_cast<QQuickItem*>(component->beginCreate(ctx));
278 m_imageOverlay->setParent(this);
279 m_imageOverlay->setParentItem(this);
280 component->completeCreate();
281
282 updateImplicitSize();
283 connect(m_imageOverlay, &QQuickItem::implicitWidthChanged, this, &QQStyleKitDelegate::updateImplicitSize);
284 connect(m_imageOverlay, &QQuickItem::implicitHeightChanged, this, &QQStyleKitDelegate::updateImplicitSize);
285}
286
287QT_END_NAMESPACE
288
289#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.