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
qqstylekitdebug.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
9
11
12const QQStyleKitPropertyGroup *QQStyleKitDebug::groupBeingRead = nullptr;
13QPointer<QQuickItem> QQStyleKitDebug::m_item;
14QString QQStyleKitDebug::m_filter;
15int QQStyleKitDebug::m_outputCount = 0;
16
17static const QChar kDot = '.'_L1;
18
19template <typename EnumType>
20QString QQStyleKitDebug::enumToString(EnumType enumValue)
21{
22 auto propertyMetaEnum = QMetaEnum::fromType<EnumType>();
23 return QString::fromUtf8(propertyMetaEnum.valueToKeys(quint64(enumValue)));
24}
25
26QString QQStyleKitDebug::objectName(const QObject *obj) {
27 QString str = QString::fromLatin1(obj->metaObject()->className());
28 int idx = str.indexOf("_QMLTYPE"_L1);
29 if (idx != -1)
30 str = str.left(idx);
31 else {
32 const QString prefix("QQStyleKit"_L1);
33 if (str.startsWith(prefix))
34 str = str.mid(prefix.length());
35 }
36 const QString name = obj->objectName();
37 if (!name.isEmpty())
38 str = str + "("_L1 + name + ")"_L1;
39 return str;
40}
41
42QString QQStyleKitDebug::stateToString(const QQSK::State state)
43{
44 const QStringList list = enumToString(state).split('|'_L1);
45 return "["_L1 + list.join(','_L1) + "]"_L1;
46}
47
48QString QQStyleKitDebug::styleReaderToString(const QQStyleKitReader *reader)
49{
50 return "StyleKitReader"_L1 + stateToString(reader->controlState());
51}
52
53QString QQStyleKitDebug::propertyPath(const QQStyleKitPropertyGroup *group, const PropertyPathId property)
54{
55 const QString path = group->pathToString();
56 QString propertyName = enumToString(property.property());
57 propertyName[0] = propertyName[0].toLower();
58 if (path.isEmpty())
59 return propertyName;
60 return path + kDot + propertyName;
61}
62
63QString QQStyleKitDebug::controlToString(const QQStyleKitControlProperties *control)
64{
65 const QObject *parentObj = control->parent();
66 if (!parentObj)
67 return "<no parent>"_L1;
68 auto *controls = qobject_cast<const QQStyleKitControls *>(parentObj);
69 if (!controls) {
70 return "<"_L1 + QString::fromUtf8(parentObj->metaObject()->className()) + ">"_L1;
71 }
72
73 const int startIndex = QQStyleKitControlProperties::staticMetaObject.propertyOffset();
74 const int endIndex = QQStyleKitControlProperties::staticMetaObject.propertyCount();
75
76 const QMetaObject* parentMeta = parentObj->metaObject();
77 for (int i = startIndex; i < endIndex; ++i) {
78 const QMetaProperty prop = parentMeta->property(i);
79 const QMetaObject* typeMeta = QMetaType::fromName(prop.typeName()).metaObject();
80 if (!typeMeta || !typeMeta->inherits(&QQStyleKitControl::staticMetaObject))
81 continue;
82
83 QObject *propObj = prop.read(parentObj).value<QObject *>();
84 if (propObj == control)
85 return QString::fromUtf8(prop.name());
86 }
87 return "<unknown control: no property found>"_L1;
88}
89
90QString QQStyleKitDebug::objectPath(const QQStyleKitControlProperties *properties, QObject *from)
91{
92 QString path;
93 const QObject *obj = properties;
94
95 while (obj) {
96 if (!path.isEmpty())
97 path.prepend(kDot);
98
99 if (auto *theme = qobject_cast<const QQStyleKitCustomTheme *>(obj)) {
100 path.prepend(theme->name() + kDot);
101 } else if (auto *theme = qobject_cast<const QQStyleKitTheme *>(obj)) {
102 // Note: only one theme is instantiated at a time
103 if (auto style = theme->style())
104 path.prepend(style->themeName());
105 else
106 path.prepend(objectName(obj));
107 } else if (auto *control = qobject_cast<const QQStyleKitControl *>(obj)) {
108 path.prepend(controlToString(control));
109 } else if (auto *reader = qobject_cast<const QQStyleKitReader *>(obj)) {
110 path.prepend(styleReaderToString(reader));
111 } else {
112 path.prepend(objectName(obj));
113 }
114
115 if (obj == from)
116 break;
117
118 obj = obj->parent();
119 }
120
121 return path;
122}
123
124void QQStyleKitDebug::notifyPropertyRead(
125 const PropertyPathId property,
126 const QQStyleKitControlProperties *storage,
127 const QQSK::State state,
128 const QVariant &value)
129{
130 Q_ASSERT(enabled());
131
133 if (reader->subclass() == QQSK::Subclass::QQStyleKitState) {
134 /* The reader is in the UnfiedStyle, and not in the users application (which can happen
135 * when e.g resolving local bindings between properties in the style). Those are not
136 * interesting to print out when inspecting control-to-style mappings. Ignore. */
137 return;
138 }
139
140 if (!insideControl(reader)) {
141 // We should only debug reads that targets m_item. So return.
142 return;
143 }
144
145 const QString _readerPath = objectPath(reader, m_item);
146 const QString _readPropertyPath = propertyPath(QQStyleKitDebug::groupBeingRead, property);
147 const QString queriedPath = _readerPath + kDot +_readPropertyPath;
148
149 QString storagePath;
150 if (storage->subclass() == QQSK::Subclass::QQStyleKitReader) {
151 /* We read an interpolated value stored directly in the reader itself. While this
152 * can be interesting to print out whe debugging the styling engine itself, it
153 * comes across as noise when inspecting control-to-style mappings. Ignore. */
154#if 0
155 storagePath = "[local storage] "_L1;
156#else
157 return;
158#endif
159 } else {
160 const QString _controlPathInStyle = objectPath(storage, storage->style());
161 const QString _statePath = stateToString(state);
162 storagePath = _controlPathInStyle + _statePath;
163 }
164
165 QString valueString = value.toString();
166 if (!value.isValid()) // value was set, but probably to undefined
167 valueString = "<undefined>"_L1;
168 else if (valueString.isEmpty())
169 valueString = "<object>"_L1;
170
171 const QString output = queriedPath + " -> "_L1 + storagePath + " = "_L1 + valueString;
172
173 if (!QRegularExpression(m_filter).match(output).hasMatch())
174 return;
175
176 qDebug().nospace().noquote() << m_outputCount++ << " | [read] "_L1 << output;
177}
178
179void QQStyleKitDebug::notifyPropertyWrite(
180 const QQStyleKitPropertyGroup *group,
181 const QQSK::Property property,
182 const QQStyleKitControlProperties *storage,
183 const QQSK::State state,
184 const PropertyStorageId key,
185 const QVariant &value)
186{
187#if 1
188 Q_UNUSED(group);
189 Q_UNUSED(property);
190 Q_UNUSED(storage);
191 Q_UNUSED(state);
192 Q_UNUSED(key);
193 Q_UNUSED(value);
194#else
195 /* Note: in order to catch _all_ writes, we cannot depend on enabling writes from
196 * QML using a property, as that would resolve to 'true' too late. */
197 QString storagePath;
198 if (storage->subclass() == QQSK::Subclass::QQStyleKitReader) {
199 storagePath = "[local storage]"_L1;
200 } else {
201 const QString _controlPathInStyle = objectPath(storage, storage->style());
202 const QString _statePath = stateToString(state);
203 storagePath = _controlPathInStyle + _statePath;
204 }
205
206 QString valueString = value.toString();
207 if (!value.isValid()) // value was set, but probably to undefined
208 valueString = "<undefined>"_L1;
209 else if (valueString.isEmpty())
210 valueString = "<object>"_L1;
211
212 const QString path = propertyPath(group, property);
213 const QString output = storagePath + kDot + path + " (storage key:"_L1 + QString::number(key) + ") = "_L1 + valueString;
214
215 qDebug().nospace().noquote() << m_outputCount++ << " | [write] "_L1 << output;
216#endif
217}
218
219void QQStyleKitDebug::notifyPropertyNotResolved(const PropertyPathId property)
220{
222 if (!insideControl(reader)) {
223 // We should only debug reads that targets m_item. So return.
224 return;
225 }
226
227 const QString _readerPath = objectPath(reader, m_item);
228 const QString _propertyPath = propertyPath(QQStyleKitDebug::groupBeingRead, property);
229 const QString queriedPath = _readerPath + kDot +_propertyPath;
230 const QString output = queriedPath + " -> <property not set>"_L1;
231
232 if (!QRegularExpression(m_filter).match(output).hasMatch())
233 return;
234
235 qDebug().nospace().noquote() << m_outputCount++ << " | [read] "_L1 << output;
236}
237
238void QQStyleKitDebug::trace(
239 const PropertyPathId property,
240 const QQStyleKitControlProperties *storage,
241 const QQSK::State state,
242 const PropertyStorageId key)
243{
244#if 1
245 Q_UNUSED(property);
246 Q_UNUSED(storage);
247 Q_UNUSED(state);
248 Q_UNUSED(key);
249#else
250 const QQStyleKitControlProperties *reader = QQStyleKitDebug::groupBeingRead->controlProperties();
251 if (reader->subclass() == QQSK::Subclass::QQStyleKitState) {
252 /* The reader is in the UnfiedStyle, and not in the users application (which can happen
253 * when e.g resolving local bindings between properties in the style). Those are not
254 * interesting to print out when inspecting control-to-style mappings. Ignore. */
255 return;
256 }
257
258 if (!insideControl(reader)) {
259 // We should only debug reads that targets m_item. So return.
260 return;
261 }
262
263 const QString _readerPath = objectPath(reader, m_item);
264 const QString _readPropertyPath = propertyPath(QQStyleKitDebug::groupBeingRead, property);
265 const QString queriedPath = _readerPath + kDot +_readPropertyPath;
266
267 QString storagePath;
268 if (storage->subclass() == QQSK::Subclass::QQStyleKitReader) {
269 /* We read an interpolated value stored directly in the reader itself. While this
270 * can be interesting to print out whe debugging the styling engine itself, it
271 * comes across as noise when inspecting control-to-style mappings. Ignore. */
272#if 0
273 storagePath = "[local storage]"_L1;
274#else
275 return;
276#endif
277 } else {
278 const QString _controlPathInStyle = objectPath(storage, storage->style());
279 const QString _statePath = stateToString(state);
280 storagePath = _controlPathInStyle + _statePath;
281 }
282
283 const QString output = queriedPath + ", checking "_L1 + storagePath + " (storage key:"_L1 + QString::number(key)+ ")"_L1;
284
285 if (!QRegularExpression(m_filter).match(output).hasMatch())
286 return;
287
288 qDebug().nospace().noquote() << m_outputCount++ << " | [trace] "_L1 << output;
289#endif
290}
291
292QQuickItem *QQStyleKitDebug::control() const
293{
294 return m_item;
295}
296
297void QQStyleKitDebug::setControl(QQuickItem *item)
298{
299 if (m_item == item)
300 return;
301
302 m_item = item;
303 emit controlChanged();
304}
305
307{
308 return m_filter;
309}
310
311void QQStyleKitDebug::setFilter(const QString &filter)
312{
313 if (m_filter == filter)
314 return;
315
316 m_filter = filter;
317 emit filterChanged();
318}
319
320bool QQStyleKitDebug::insideControl(const QObject *child)
321{
322 if (!m_item)
323 return false;
324 const QObject *obj = child;
325 do {
326 if (obj == m_item)
327 return true;
328 obj = obj->parent();
329 } while (obj);
330 return false;
331}
332
333QT_END_NAMESPACE
334
335#include "moc_qqstylekitdebug_p.cpp"
QString filter() const
void setFilter(const QString &filter)
void setControl(QQuickItem *item)
QQStyleKitControlProperties * controlProperties() const
Combined button and popup list for selecting options.
static const QChar kDot