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
qqstylekitcontrolproperties.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
12// ************* QQStyleKitPropertyGroup ****************
13
14QHash<PropertyPathId_t, QString> QQStyleKitPropertyGroup::s_pathStrings;
15
16QQStyleKitPropertyGroup::QQStyleKitPropertyGroup(QQSK::PropertyGroup, QObject *parent)
17 : QObject(parent)
18{
19}
20
21PropertyPathId QQStyleKitPropertyGroup::propertyPathId(QQSK::Property property, PropertyPathId::Flag flag) const
22{
23 if (flag == PropertyPathId::Flag::IncludeSubtype) {
24 if (m_pathFlags.testFlag(QQSK::PropertyPathFlag::DelegateSubtype1))
25 return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype1);
26 else if (m_pathFlags.testFlag(QQSK::PropertyPathFlag::DelegateSubtype2))
27 return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype2);
28 }
29 return PropertyPathId(property, m_groupSpace.start, QQSK::PropertyGroup::DelegateSubtype0);
30}
31
33{
34 /* Start from the root of the path and build the path down to this group. This
35 * mirrors how the groups were originally created and avoids rounding issues
36 * that can arise if attempting to reconstruct the path “backwards”.
37 * Note: For each group, m_groupSpace.start is stored relative to the root,
38 * while m_groupSpace.size is relative to the parent group. However, when
39 * calculating the group index, the group-space start must be computed
40 * relative to the parent group.
41 * We cache the requested paths, as the same paths are typically requested
42 * repeatedly. The number of possible paths (and thus leaf groups) is well below
43 * 100, and in practice the cache usually ends up with fewer than 20 entries. */
44 if (s_pathStrings.contains(m_groupSpace.start))
45 return s_pathStrings[m_groupSpace.start];
46
47 constexpr PropertyPathId_t rootGroupsSize = nestedGroupsStartSize / nestedGroupCount;
48 const auto metaEnum = QMetaEnum::fromType<QQSK::PropertyGroup>();
49
50 PropertyPathId_t nestedGroupStart = m_groupSpace.start;
51 PropertyPathId_t nestedGroupSize = rootGroupsSize;
52 PropertyPathId_t nestedGroupIndex = nestedGroupStart / nestedGroupSize;
53 auto groupType = QQSK::PropertyGroup(nestedGroupIndex);
54 if (groupType == QQSK::PropertyGroup::Control)
55 return {};
56
57 QString groupName = QString::fromLatin1(metaEnum.valueToKey(static_cast<int>(groupType)));
58 groupName[0] = groupName[0].toLower();
59 QString pathString = groupName;
60
61 while (true) {
62 nestedGroupStart -= nestedGroupIndex * nestedGroupSize;
63 nestedGroupSize /= nestedGroupCount;
64 nestedGroupIndex = nestedGroupStart / nestedGroupSize;
65 groupType = QQSK::PropertyGroup(nestedGroupIndex);
66 if (groupType == QQSK::PropertyGroup::Control)
67 break;
68
69 QString groupName = QString::fromLatin1(metaEnum.valueToKey(static_cast<int>(groupType)));
70 groupName[0] = groupName[0].toLower();
71 pathString += '.'_L1 + groupName;
72 }
73
74 s_pathStrings.insert(m_groupSpace.start, pathString);
75 return pathString;
76}
77
79{
81 Q_ASSERT(qobject_cast<const QQStyleKitControlProperties *>(this));
82 auto *self = const_cast<QQStyleKitPropertyGroup *>(this);
83 return static_cast<QQStyleKitControlProperties *>(self);
84 }
85 Q_ASSERT(qobject_cast<const QQStyleKitControlProperties *>(parent()));
86 return static_cast<QQStyleKitControlProperties *>(parent());
87}
88
89template<typename T>
90T *QQStyleKitPropertyGroup::lazyCreateGroup(T * const &ptr, QQSK::PropertyGroup group) const
91{
92 T *nestedGroup = QQSK::lazyCreate(ptr, controlProperties(), group);
93
94 // Nested groups inherit path flags from their parents
95 nestedGroup->m_pathFlags = m_pathFlags;
96
97 if (group == QQSK::PropertyGroup::DelegateSubtype1) {
98 /* Subtypes, like states, are not part of a property's path ID—they belong to the
99 * storage ID instead. They are therefore prefixed later, during lookup, when
100 * propagation determines which value to read.
101 * For now, we simply record which subtype this group (and any nested groups) is
102 * associated with. The subtype will then be taken into account later when reading
103 * properties from the group. Setting aside space for the sub types was already
104 * taken care of during the construction of the root QQStyleKitControlProperties. */
105 nestedGroup->m_pathFlags.setFlag(QQSK::PropertyPathFlag::DelegateSubtype1);
106 nestedGroup->m_groupSpace = m_groupSpace;
107 } else if (group == QQSK::PropertyGroup::DelegateSubtype2) {
108 nestedGroup->m_pathFlags.setFlag(QQSK::PropertyPathFlag::DelegateSubtype2);
109 nestedGroup->m_groupSpace = m_groupSpace;
110 } else {
111 /* Calculate the available property ID space for the nested group. This is done by
112 * dividing the available space inside _this_ group on the number of potential groups
113 * that _this_ group can potentially contain. */
114 const PropertyPathId_t nestedGroupIndex = PropertyPathId_t(group);
115 const PropertyPathId_t nestedGroupSize = m_groupSpace.size / nestedGroupCount;
116 nestedGroup->m_groupSpace.size = nestedGroupSize;
117 nestedGroup->m_groupSpace.start = m_groupSpace.start + (nestedGroupIndex * nestedGroupSize);
118 /* Ensure that we haven’t exhausted the available PropertyPathId space. There must be
119 * enough room remaining to assign IDs for all properties defined in QQSK::Property.
120 * If this assertion triggers, consider switching to a wider PropertyPathId_t type or
121 * optimizing how the space is allocated. For example, certain nested paths (such as
122 * control.handle.indicator) can never occur, yet we currently reserve INNER_GROUP_COUNT
123 * for every nesting level, which is wasteful. */
124 Q_ASSERT(nestedGroupSize >= PropertyPathId_t(QQSK::Property::COUNT));
125 }
126 return nestedGroup;
127}
128
129/* This macro will check if the caller has the same group path as \a GROUP_PATH.
130 * This is needed since a QQSK::Property (e.g Color) can sometimes be a
131 * property in several different subclasses of QQStyleKitPropertyGroup.
132 * For example, both control.background.color and control.indicator.color has a
133 * color property. But the group path differs, so they are in reality two completely
134 * different properties. And in that case, when the former changes value, we want to
135 * emit changes globally only to that property, and not the latter.
136 * The caller of this macro will therefore need to go through all the usages of its
137 * subclass in the API, to figure out which group itself is an instance of. For the
138 * one that is a match, the macro will go through all readers and emit the same
139 * signal for them. */
140#define CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(CONTROL_PROPERTIES, GROUP_PATH) if
141 (this == CONTROL_PROPERTIES -> GROUP_PATH ) {
142 for (QQStyleKitReader *reader : QQStyleKitReader::s_allReaders) {
143 const auto baseTypes = QQStyleKitPropertyResolver::baseTypesForType(reader->type());
144 if (reader->type() != controlType && !baseTypes.contains(controlType))
145 continue;
146 reader->clearLocalStorage();
147 ((reader-> GROUP_PATH ->*changedSignals)(), ...);
148 }
149 return; \
150}
151
152template<typename SUBCLASS>
153void QQStyleKitPropertyGroup::handleStylePropertyChanged(void (SUBCLASS::*changedSignal)()) {
154 handleStylePropertiesChanged<SUBCLASS>(changedSignal);
155}
156
157template <typename SUBCLASS, typename... CHANGED_SIGNALS>
158void QQStyleKitPropertyGroup::handleStylePropertiesChanged(CHANGED_SIGNALS... changedSignals)
159{
160 /* This function will check which subclass of QQStyleKitProperties this
161 * group is (nested) inside. Based on that, it will decide if the signals
162 * should be emitted locally or not, and if the changed properties affects
163 * all existing QQStyleKitReaders, and therefore will need to be
164 * emitted 'globally'. Note that it only makes sense to call this function
165 * for changed properties that are available from a QQStyleKitReader.
166 * Properities only available from e.g QQStyleKitControl (such as
167 * variations), are anyway not readable from a QQStyleKitReader. */
168 static_assert(std::is_base_of<QQStyleKitPropertyGroup, SUBCLASS>::value,
169 "SUBCLASS must inherit QQStyleKitPropertyGroup");
170
171 auto *group = static_cast<SUBCLASS *>(this);
172 const QQSK::Subclass objectWrittenTo = controlProperties()->subclass();
173
174 if (objectWrittenTo == QQSK::Subclass::QQStyleKitState) {
175 ((group->*changedSignals)(), ...);
176
177 if (shouldEmitGlobally()) {
179 const QQStyleKitExtendableControlType type = control->controlType();
180 group->emitGlobally(type, changedSignals...);
181 }
182 return;
183 }
184
185 if (objectWrittenTo == QQSK::Subclass::QQStyleKitReader) {
186 /* Unless the StyleReader has told us not to emit any signals (because it's only
187 * syncing it's own local storage with old values before starting a transition), we
188 * emit the signal like normal. This will cause the control to repaint (perhaps
189 * using a transition). */
190 if (shouldEmitLocally())
191 ((group->*changedSignals)(), ...);
192 return;
193 }
194
195 Q_UNREACHABLE();
196}
197
199{
200 /* This function will emit changed signals for all style properties in the
201 * StyleKit API (for a single QQStyleKitReader), which is needed after
202 * doing a style-, or theme change. */
203 const int startIndex = QQStyleKitPropertyGroup::staticMetaObject.propertyOffset();
204 const QMetaObject* meta = metaObject();
205 for (int i = startIndex; i < meta->propertyCount(); ++i) {
206 const QMetaProperty prop = meta->property(i);
207 const QMetaObject* metaObject = QMetaType::fromName(prop.typeName()).metaObject();
208 if (metaObject) {
209 if (metaObject->inherits(&QQStyleKitDelegateProperties::staticMetaObject)) {
210 /* Skip recursing into QQStyleKitDelegateProperties, because those are lazy
211 * created when read, and reading them from here would accidentally
212 * create them. */
213 continue;
214 }
215 if (metaObject->inherits(&QQStyleKitPropertyGroup::staticMetaObject)) {
216 // The property is of type QQStyleKitPropertyGroup, so recurse into it
217 QObject *childObj = qvariant_cast<QObject *>(property(prop.name()));
218 if (auto *child = qobject_cast<QQStyleKitPropertyGroup *>(childObj))
219 child->emitChangedForAllStylePropertiesRecursive();
220 continue;
221 }
222 }
223
224 // Emit the changed signal for the property
225 Q_ASSERT(prop.hasNotifySignal());
226 QMetaMethod notify = prop.notifySignal();
227 notify.invoke(this, Qt::DirectConnection);
228 }
229}
230
231bool QQStyleKitPropertyGroup::shouldEmitLocally()
232{
233 return !controlProperties()->asQQStyleKitReader()->dontEmitChangedSignals();
234}
235
236bool QQStyleKitPropertyGroup::shouldEmitGlobally()
237{
239 if (!parentStyle)
240 return false;
241
242 if (parentStyle->loaded() && !parentStyle->m_isUpdatingPalette) {
243 /* When a property has changed in the 'global' QQStyleKitStyle itself, it can
244 * potentially affect all control instances. We therefore need to go through all
245 * QQStyleKitReaders and inform that their own local property that matches the
246 * 'global' property needs to be re-read. We emit the signals directly, omitting any
247 * applied transitions in the QQStyleKitReaders, to optimize for speed. The exception
248 * is if we're just updating the palette in the Style to match the palette in the current
249 * control / QQStyleKitReader. Such a change will only affect a single control. */
250 return parentStyle == QQStyleKitStyle::current();
251 }
252 return false;
253}
254
255// ************* QQStyleKitImageProperties ****************
256
257QQStyleKitImageProperties::QQStyleKitImageProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
258 : QQStyleKitPropertyGroup(group, parent)
259{
260}
261
262template <typename... CHANGED_SIGNALS>
264 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
265{
266 // Go through all instances of QQStyleKitImageProperties
268 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->image());
269 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->image());
270 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->image());
271 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->image());
272}
273
275{
276 return styleProperty<QUrl>(QQSK::Property::Source);
277}
278
279void QQStyleKitImageProperties::setSource(const QUrl &source)
280{
281 if (setStyleProperty(QQSK::Property::Source, source))
282 handleStylePropertyChanged(&QQStyleKitImageProperties::sourceChanged);
283}
284
286{
287 return styleProperty<QColor>(QQSK::Property::Color);
288}
289
290void QQStyleKitImageProperties::setColor(const QColor &color)
291{
292 if (setStyleProperty(QQSK::Property::Color, color))
293 handleStylePropertyChanged(&QQStyleKitImageProperties::colorChanged);
294}
295
297{
298 return styleProperty<QQuickImage::FillMode>(QQSK::Property::FillMode);
299}
300
301void QQStyleKitImageProperties::setFillMode(QQuickImage::FillMode fillMode)
302{
303 if (setStyleProperty(QQSK::Property::FillMode, fillMode))
304 handleStylePropertyChanged(&QQStyleKitImageProperties::fillModeChanged);
305}
306
307// ************* QQStyleKitBorderProperties ****************
308
309QQStyleKitBorderProperties::QQStyleKitBorderProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
310 : QQStyleKitPropertyGroup(group, parent)
311{
312}
313
314template <typename... CHANGED_SIGNALS>
316 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
317{
318 // Go through all instances of QQStyleKitBorderProperties
320 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->border());
321 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->border());
322 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->border());
323 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->border());
324}
325
327{
328 return styleProperty<qreal>(QQSK::Property::Width);
329}
330
332{
333 if (setStyleProperty(QQSK::Property::Width, width))
334 handleStylePropertyChanged(&QQStyleKitBorderProperties::widthChanged);
335}
336
338{
339 return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
340}
341
342void QQStyleKitBorderProperties::setColor(const QColor &color)
343{
344 if (setStyleProperty(QQSK::Property::Color, color))
345 handleStylePropertyChanged(&QQStyleKitBorderProperties::colorChanged);
346}
347
348// ************* QQStyleKitShadowProperties ****************
349
350QQStyleKitShadowProperties::QQStyleKitShadowProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
351 : QQStyleKitPropertyGroup(group, parent)
352{
353}
354
355template <typename... CHANGED_SIGNALS>
356void QQStyleKitShadowProperties::emitGlobally(
357 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
358{
359 // Go through all instances of QQStyleKitShadowProperties
360 const QQStyleKitControlProperties *cp = controlProperties();
361 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, background()->shadow());
362 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, handle()->shadow());
363 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->shadow());
364 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground()->shadow());
365}
366
367QColor QQStyleKitShadowProperties::color() const
368{
369 return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
370}
371
372void QQStyleKitShadowProperties::setColor(QColor color)
373{
374 if (setStyleProperty(QQSK::Property::Color, color))
375 handleStylePropertyChanged(&QQStyleKitShadowProperties::colorChanged);
376}
377
378qreal QQStyleKitShadowProperties::opacity() const
379{
380 return styleProperty<qreal>(QQSK::Property::Opacity, 1.0);
381}
382
383void QQStyleKitShadowProperties::setOpacity(qreal opacity)
384{
385 if (setStyleProperty(QQSK::Property::Opacity, opacity))
386 handleStylePropertyChanged(&QQStyleKitShadowProperties::opacityChanged);
387}
388
389qreal QQStyleKitShadowProperties::scale() const
390{
391 return styleProperty<qreal>(QQSK::Property::Scale, 1.0);
392}
393
394void QQStyleKitShadowProperties::setScale(qreal scale)
395{
396 if (setStyleProperty(QQSK::Property::Scale, scale))
397 handleStylePropertyChanged(&QQStyleKitShadowProperties::scaleChanged);
398}
399
400qreal QQStyleKitShadowProperties::verticalOffset() const
401{
402 return styleProperty<qreal>(QQSK::Property::VOffset);
403}
404
405void QQStyleKitShadowProperties::setVerticalOffset(qreal verticalOffset)
406{
407 if (setStyleProperty(QQSK::Property::VOffset, verticalOffset))
408 handleStylePropertyChanged(&QQStyleKitShadowProperties::verticalOffsetChanged);
409}
410
411qreal QQStyleKitShadowProperties::horizontalOffset() const
412{
413 return styleProperty<qreal>(QQSK::Property::HOffset);
414}
415
416void QQStyleKitShadowProperties::setHorizontalOffset(qreal horizontalOffset)
417{
418 if (setStyleProperty(QQSK::Property::HOffset, horizontalOffset))
419 handleStylePropertyChanged(&QQStyleKitShadowProperties::horizontalOffsetChanged);
420}
421
422qreal QQStyleKitShadowProperties::blur() const
423{
424 return styleProperty<qreal>(QQSK::Property::Blur, 10.0);
425}
426
427void QQStyleKitShadowProperties::setBlur(qreal blur)
428{
429 if (setStyleProperty(QQSK::Property::Blur, blur))
430 handleStylePropertyChanged(&QQStyleKitShadowProperties::blurChanged);
431}
432
433bool QQStyleKitShadowProperties::visible() const
434{
435 return styleProperty<bool>(QQSK::Property::Visible, true);
436}
437
438void QQStyleKitShadowProperties::setVisible(bool visible)
439{
440 if (setStyleProperty(QQSK::Property::Visible, visible))
441 handleStylePropertyChanged(&QQStyleKitShadowProperties::visibleChanged);
442}
443
444QQmlComponent *QQStyleKitShadowProperties::delegate() const
445{
446 return styleProperty<QQmlComponent *>(QQSK::Property::Delegate);
447}
448
449void QQStyleKitShadowProperties::setDelegate(QQmlComponent *delegate)
450{
451 if (setStyleProperty(QQSK::Property::Delegate, delegate))
452 handleStylePropertyChanged(&QQStyleKitShadowProperties::delegateChanged);
453}
454
455// ************* QQStyleKitDelegateProperties ****************
456
457QQStyleKitDelegateProperties::QQStyleKitDelegateProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
458 : QQStyleKitPropertyGroup(group, parent)
459{
460}
461
462template <typename... CHANGED_SIGNALS>
463void QQStyleKitDelegateProperties::emitGlobally(
464 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
465{
466 // Go through all instances of QQStyleKitDelegateProperties
467 const QQStyleKitControlProperties *cp = controlProperties();
471 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->foreground());
472}
473
474qreal QQStyleKitDelegateProperties::radius() const
475{
476 return styleProperty<qreal>(QQSK::Property::Radius);
477}
478
479void QQStyleKitDelegateProperties::setRadius(qreal radius)
480{
481 if (setStyleProperty(QQSK::Property::Radius, radius))
482 handleStylePropertiesChanged<QQStyleKitDelegateProperties>(
483 &QQStyleKitDelegateProperties::radiusChanged,
484 &QQStyleKitDelegateProperties::topLeftRadiusChanged,
485 &QQStyleKitDelegateProperties::topRightRadiusChanged,
486 &QQStyleKitDelegateProperties::bottomLeftRadiusChanged,
487 &QQStyleKitDelegateProperties::bottomRightRadiusChanged);
488}
489
490qreal QQStyleKitDelegateProperties::topLeftRadius() const
491{
492 return styleProperty<qreal>(QQSK::Property::TopLeftRadius, QQSK::Property::Radius);
493}
494
495void QQStyleKitDelegateProperties::setTopLeftRadius(qreal radius)
496{
497 if (setStyleProperty(QQSK::Property::TopLeftRadius, radius))
498 handleStylePropertyChanged(&QQStyleKitDelegateProperties::topLeftRadiusChanged);
499}
500
501qreal QQStyleKitDelegateProperties::topRightRadius() const
502{
503 return styleProperty<qreal>(QQSK::Property::TopRightRadius, QQSK::Property::Radius);
504}
505
506void QQStyleKitDelegateProperties::setTopRightRadius(qreal radius)
507{
508 if (setStyleProperty(QQSK::Property::TopRightRadius, radius))
509 handleStylePropertyChanged(&QQStyleKitDelegateProperties::topRightRadiusChanged);
510}
511
512qreal QQStyleKitDelegateProperties::bottomLeftRadius() const
513{
514 return styleProperty<qreal>(QQSK::Property::BottomLeftRadius, QQSK::Property::Radius);
515}
516
517void QQStyleKitDelegateProperties::setBottomLeftRadius(qreal radius)
518{
519 if (setStyleProperty(QQSK::Property::BottomLeftRadius, radius))
520 handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomLeftRadiusChanged);
521}
522
523qreal QQStyleKitDelegateProperties::bottomRightRadius() const
524{
525 return styleProperty<qreal>(QQSK::Property::BottomRightRadius, QQSK::Property::Radius);
526}
527
528void QQStyleKitDelegateProperties::setBottomRightRadius(qreal radius)
529{
530 if (setStyleProperty(QQSK::Property::BottomRightRadius, radius))
531 handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomRightRadiusChanged);
532}
533
534qreal QQStyleKitDelegateProperties::scale() const
535{
536 return styleProperty<qreal>(QQSK::Property::Scale, 1.0);
537}
538
539void QQStyleKitDelegateProperties::setScale(qreal scale)
540{
541 if (setStyleProperty(QQSK::Property::Scale, scale))
542 handleStylePropertyChanged(&QQStyleKitDelegateProperties::scaleChanged);
543}
544
545qreal QQStyleKitDelegateProperties::rotation() const
546{
547 return styleProperty<qreal>(QQSK::Property::Rotation);
548}
549
550void QQStyleKitDelegateProperties::setRotation(qreal rotation)
551{
552 if (setStyleProperty(QQSK::Property::Rotation, rotation))
553 handleStylePropertyChanged(&QQStyleKitDelegateProperties::rotationChanged);
554}
555
556qreal QQStyleKitDelegateProperties::implicitWidth() const
557{
558 return styleProperty<qreal>(QQSK::Property::ImplicitWidth);
559}
560
561void QQStyleKitDelegateProperties::setImplicitWidth(qreal width)
562{
563 if (setStyleProperty(QQSK::Property::ImplicitWidth, width))
564 handleStylePropertyChanged(&QQStyleKitDelegateProperties::implicitWidthChanged);
565}
566
567qreal QQStyleKitDelegateProperties::implicitHeight() const
568{
569 return styleProperty<qreal>(QQSK::Property::ImplicitHeight);
570}
571
572void QQStyleKitDelegateProperties::setImplicitHeight(qreal height)
573{
574 if (setStyleProperty(QQSK::Property::ImplicitHeight, height))
575 handleStylePropertyChanged(&QQStyleKitDelegateProperties::implicitHeightChanged);
576}
577
578qreal QQStyleKitDelegateProperties::minimumWidth() const
579{
580 return styleProperty<qreal>(QQSK::Property::MinimumWidth);
581}
582
583void QQStyleKitDelegateProperties::setMinimumWidth(qreal width)
584{
585 if (setStyleProperty(QQSK::Property::MinimumWidth, width))
586 handleStylePropertyChanged(&QQStyleKitDelegateProperties::minimumWidthChanged);
587}
588
589qreal QQStyleKitDelegateProperties::margins() const
590{
591 return styleProperty<qreal>(QQSK::Property::Margins);
592}
593
594void QQStyleKitDelegateProperties::setMargins(qreal margins)
595{
596 if (setStyleProperty(QQSK::Property::Margins, margins))
597 handleStylePropertiesChanged<QQStyleKitDelegateProperties>(
598 &QQStyleKitDelegateProperties::marginsChanged,
599 &QQStyleKitDelegateProperties::leftMarginChanged,
600 &QQStyleKitDelegateProperties::rightMarginChanged,
601 &QQStyleKitDelegateProperties::topMarginChanged,
602 &QQStyleKitDelegateProperties::bottomMarginChanged);
603}
604
605qreal QQStyleKitDelegateProperties::leftMargin() const
606{
607 return styleProperty<qreal>(QQSK::Property::LeftMargin, QQSK::Property::Margins);
608}
609
610void QQStyleKitDelegateProperties::setLeftMargin(qreal margin)
611{
612 if (setStyleProperty(QQSK::Property::LeftMargin, margin))
613 handleStylePropertyChanged(&QQStyleKitDelegateProperties::leftMarginChanged);
614}
615
616qreal QQStyleKitDelegateProperties::rightMargin() const
617{
618 return styleProperty<qreal>(QQSK::Property::RightMargin, QQSK::Property::Margins);
619}
620
621void QQStyleKitDelegateProperties::setRightMargin(qreal margin)
622{
623 if (setStyleProperty(QQSK::Property::RightMargin, margin))
624 handleStylePropertyChanged(&QQStyleKitDelegateProperties::rightMarginChanged);
625}
626
627qreal QQStyleKitDelegateProperties::topMargin() const
628{
629 return styleProperty<qreal>(QQSK::Property::TopMargin, QQSK::Property::Margins);
630}
631
632void QQStyleKitDelegateProperties::setTopMargin(qreal margin)
633{
634 if (setStyleProperty(QQSK::Property::TopMargin, margin))
635 handleStylePropertyChanged(&QQStyleKitDelegateProperties::topMarginChanged);
636}
637
638qreal QQStyleKitDelegateProperties::bottomMargin() const
639{
640 return styleProperty<qreal>(QQSK::Property::BottomMargin, QQSK::Property::Margins);
641}
642
643void QQStyleKitDelegateProperties::setBottomMargin(qreal margin)
644{
645 if (setStyleProperty(QQSK::Property::BottomMargin, margin))
646 handleStylePropertyChanged(&QQStyleKitDelegateProperties::bottomMarginChanged);
647}
648
649Qt::Alignment QQStyleKitDelegateProperties::alignment() const
650{
651 return styleProperty<Qt::Alignment>(QQSK::Property::Alignment, Qt::AlignLeft | Qt::AlignVCenter);
652}
653
654void QQStyleKitDelegateProperties::setAlignment(Qt::Alignment alignment)
655{
656 if (setStyleProperty(QQSK::Property::Alignment, alignment))
657 handleStylePropertyChanged(&QQStyleKitDelegateProperties::alignmentChanged);
658}
659
660qreal QQStyleKitDelegateProperties::opacity() const
661{
662 return styleProperty<qreal>(QQSK::Property::Opacity, 1.0);
663}
664
665void QQStyleKitDelegateProperties::setOpacity(qreal opacity)
666{
667 if (setStyleProperty(QQSK::Property::Opacity, opacity))
668 handleStylePropertyChanged(&QQStyleKitDelegateProperties::opacityChanged);
669}
670
671QColor QQStyleKitDelegateProperties::color() const
672{
673 return styleProperty<QColor>(QQSK::Property::Color, Qt::transparent);
674}
675
676void QQStyleKitDelegateProperties::setColor(const QColor &color)
677{
678 if (setStyleProperty(QQSK::Property::Color, color))
679 handleStylePropertyChanged(&QQStyleKitDelegateProperties::colorChanged);
680}
681
682bool QQStyleKitDelegateProperties::visible() const
683{
684 return styleProperty<bool>(QQSK::Property::Visible, true);
685}
686
687void QQStyleKitDelegateProperties::setVisible(bool visible)
688{
689 if (setStyleProperty(QQSK::Property::Visible, visible))
690 handleStylePropertyChanged(&QQStyleKitDelegateProperties::visibleChanged);
691}
692
693bool QQStyleKitDelegateProperties::clip() const
694{
695 return styleProperty<bool>(QQSK::Property::Clip, false);
696}
697
698void QQStyleKitDelegateProperties::setClip(bool clip)
699{
700 if (setStyleProperty(QQSK::Property::Clip, clip))
701 handleStylePropertyChanged(&QQStyleKitDelegateProperties::clipChanged);
702}
703
704QQuickGradient *QQStyleKitDelegateProperties::gradient() const
705{
706 return styleProperty<QQuickGradient *>(QQSK::Property::Gradient);
707}
708
709void QQStyleKitDelegateProperties::setGradient(QQuickGradient *gradient)
710{
711 if (setStyleProperty(QQSK::Property::Gradient, gradient))
712 handleStylePropertyChanged(&QQStyleKitDelegateProperties::gradientChanged);
713}
714
715QObject *QQStyleKitDelegateProperties::data() const
716{
717 return styleProperty<QObject *>(QQSK::Property::Data);
718}
719
720void QQStyleKitDelegateProperties::setData(QObject *data)
721{
722 if (setStyleProperty(QQSK::Property::Data, data))
723 handleStylePropertyChanged(&QQStyleKitDelegateProperties::dataChanged);
724}
725
726QQmlComponent *QQStyleKitDelegateProperties::delegate() const
727{
728 return styleProperty<QQmlComponent *>(QQSK::Property::Delegate);
729}
730
731void QQStyleKitDelegateProperties::setDelegate(QQmlComponent *delegate)
732{
733 if (setStyleProperty(QQSK::Property::Delegate, delegate))
734 handleStylePropertyChanged(&QQStyleKitDelegateProperties::delegateChanged);
735}
736
737QQStyleKitBorderProperties *QQStyleKitDelegateProperties::border() const
738{
739 return lazyCreateGroup(m_border, QQSK::PropertyGroup::Border);
740}
741
742QQStyleKitShadowProperties *QQStyleKitDelegateProperties::shadow() const
743{
744 return lazyCreateGroup(m_shadow, QQSK::PropertyGroup::Shadow);
745}
746
747QQStyleKitImageProperties *QQStyleKitDelegateProperties::image() const
748{
749 return lazyCreateGroup(m_image, QQSK::PropertyGroup::Image);
750}
751
752// ************* QQStyleKitHandleProperties ****************
753
754QQStyleKitHandleProperties::QQStyleKitHandleProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
755 : QQStyleKitDelegateProperties(group, parent)
756{
757}
758
759QQStyleKitDelegateProperties *QQStyleKitHandleProperties::first() const
760{
761 return lazyCreateGroup(m_first, QQSK::PropertyGroup::DelegateSubtype1);
762}
763
764QQStyleKitDelegateProperties *QQStyleKitHandleProperties::second() const
765{
766 return lazyCreateGroup(m_second, QQSK::PropertyGroup::DelegateSubtype2);
767}
768
769// ************* QQStyleKitIndicatorProperties ****************
770
771QQStyleKitIndicatorProperties::QQStyleKitIndicatorProperties(
772 QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
773 : QQStyleKitDelegateProperties(group, parent)
774{
775}
776
777template <typename... CHANGED_SIGNALS>
779 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
780{
781 // Go through all instances of QQStyleKitIndicatorProperties
782 const QQStyleKitControlProperties *cp = controlProperties();
783 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->up());
784 CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(cp, indicator()->down());
785}
786
787QQStyleKitDelegateProperties *QQStyleKitIndicatorProperties::foreground() const
788{
789 return lazyCreateGroup(m_foreground, QQSK::PropertyGroup::Foreground);
790}
791
792// ************* QQStyleKitIndicatorWithSubTypes ****************
793
794QQStyleKitIndicatorWithSubTypes::QQStyleKitIndicatorWithSubTypes(
795 QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
796 : QQStyleKitDelegateProperties(group, parent)
797{
798}
799
800template <typename... CHANGED_SIGNALS>
802 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
803{
804 // Go through all instances of QQStyleKitIndicatorWithSubTypes
805 const QQStyleKitControlProperties *cp = controlProperties();
807}
808
809QQStyleKitDelegateProperties *QQStyleKitIndicatorWithSubTypes::foreground() const
810{
811 return lazyCreateGroup(m_foreground, QQSK::PropertyGroup::Foreground);
812}
813
815{
816 return lazyCreateGroup(m_up, QQSK::PropertyGroup::DelegateSubtype1);
817}
818
820{
821 return lazyCreateGroup(m_down, QQSK::PropertyGroup::DelegateSubtype2);
822}
823
824// ************* QQStyleKitTextProperties ****************
825QQStyleKitTextProperties::QQStyleKitTextProperties(QQSK::PropertyGroup group, QQStyleKitControlProperties *parent)
826 : QQStyleKitPropertyGroup(group, parent)
827{
828}
829
830template <typename... CHANGED_SIGNALS>
832 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
833{
836}
837
839{
840 return styleProperty<QColor>(QQSK::Property::Color);
841}
842
843void QQStyleKitTextProperties::setColor(const QColor &color)
844{
845 if (setStyleProperty(QQSK::Property::Color, color))
846 handleStylePropertyChanged(&QQStyleKitTextProperties::colorChanged);
847}
848
850{
851 return styleProperty<Qt::Alignment>(QQSK::Property::Alignment);
852}
853
854void QQStyleKitTextProperties::setAlignment(Qt::Alignment alignment)
855{
856 if (setStyleProperty(QQSK::Property::Alignment, alignment))
857 handleStylePropertyChanged(&QQStyleKitTextProperties::alignmentChanged);
858}
859
861{
862 return styleProperty<bool>(QQSK::Property::Bold, false);
863}
864
866{
867 if (setStyleProperty(QQSK::Property::Bold, bold))
868 handleStylePropertyChanged(&QQStyleKitTextProperties::boldChanged);
869}
870
872{
873 return styleProperty<bool>(QQSK::Property::Italic, false);
874}
875
877{
878 if (setStyleProperty(QQSK::Property::Italic, italic))
879 handleStylePropertyChanged(&QQStyleKitTextProperties::italicChanged);
880}
881
883{
884 return styleProperty<qreal>(QQSK::Property::PointSize);
885}
886
888{
889 if (setStyleProperty(QQSK::Property::PointSize, pointSize))
890 handleStylePropertyChanged(&QQStyleKitTextProperties::pointSizeChanged);
891}
892
894{
895 return styleProperty<qreal>(QQSK::Property::Padding);
896}
897
908
910{
911 return styleProperty<qreal>(QQSK::Property::LeftPadding, QQSK::Property::Padding);
912}
913
915{
916 if (setStyleProperty(QQSK::Property::LeftPadding, padding))
917 handleStylePropertyChanged(&QQStyleKitTextProperties::leftPaddingChanged);
918}
919
921{
922 return styleProperty<qreal>(QQSK::Property::RightPadding, QQSK::Property::Padding);
923}
924
926{
927 if (setStyleProperty(QQSK::Property::RightPadding, padding))
928 handleStylePropertyChanged(&QQStyleKitTextProperties::rightPaddingChanged);
929}
930
932{
933 return styleProperty<qreal>(QQSK::Property::TopPadding, QQSK::Property::Padding);
934}
935
937{
938 if (setStyleProperty(QQSK::Property::TopPadding, padding))
939 handleStylePropertyChanged(&QQStyleKitTextProperties::topPaddingChanged);
940}
941
943{
944 return styleProperty<qreal>(QQSK::Property::BottomPadding, QQSK::Property::Padding);
945}
946
948{
949 if (setStyleProperty(QQSK::Property::BottomPadding, padding))
950 handleStylePropertyChanged(&QQStyleKitTextProperties::bottomPaddingChanged);
951}
952
953// ************* QQStyleKitControlProperties ****************
954
955QQStyleKitControlProperties::QQStyleKitControlProperties(QQSK::PropertyGroup group, QObject *parent)
956 : QQStyleKitPropertyGroup(group, parent)
957{
958 /* Calculate the free space storage ID space that can accommodate all unique style
959 * properties that may be applied to a control. Since we'll prepend different states
960 * and subtypes during the property propagation lookup phase later, we need to reserve
961 * ID space for them both already now. More docs about the property space is written in
962 * the implementation of PropertyPathId. */
963 m_groupSpace.size = nestedGroupsStartSize;
964 m_groupSpace.start = 0;
965
966 if (group == QQSK::PropertyGroup::GlobalFlag) {
967 /* A property path may include pseudo-groups that offers a convenient API for
968 * reading properties with specific options applied. The 'global' group is one such
969 * pseudo-group. When it is prefixed to a property path, it indicates that the property
970 * should be read directly from the style, bypassing any active transitions that might
971 * otherwise affect its value.
972 * Note: The global group should be ignored when computing a PropertyPathId_t, as it
973 * only affect _where_ the property should be read from, not its ID. */
974 m_pathFlags.setFlag(QQSK::PropertyPathFlag::Global);
975 }
976}
977
979{
980 if (subclass() == QQSK::Subclass::QQStyleKitState) {
981 /* A QQStyleKitControlState (and its subclasses) should always be a (grand)child of a
982 * QQStyleKitStyle. And it belongs to that style, even it that style is not the
983 * currently active application style. This is opposed to a QQStyleKitReader,
984 * that normally belongs / communicates with the currently active style.
985 * NOTE: a style can also be a fallback style for another style (which can be recursive,
986 * meaning that a fallback style can also have its own fallback style, and so on). But
987 * this function will return the nearest style, and not the root style */
988 QObject *obj = parent();
989 while (obj && !obj->metaObject()->inherits(&QQStyleKitStyle::staticMetaObject))
990 obj = obj->parent();
991 return obj ? static_cast<QQStyleKitStyle *>(obj) : nullptr;
992 }
993
994 /* A style reader belongs to the currently active application style. We could in theory
995 * support being able to point a QQStyleKitReader to any style, which would basically
996 * mean that you could mix controls from several styles inside the same application. But
997 * there is currently no API (or use-case?) in Controls that lets you to do that, so its
998 * disabled for now. */
1000}
1001
1003{
1004 /* QQStyleKitControlProperties is subclassed by several different classes in this
1005 * framework. As such, it's basically just an interface because it only declares the
1006 * different properties that can be read or written to in a QQStyleKitStyle, such as
1007 * hovered.background.color or pressed.indicator.foreground.color. It says nothing
1008 * about how those properties are stored, instead that is up to each individual
1009 * subclass to decide */
1010 if (metaObject()->inherits(&QQStyleKitReader::staticMetaObject))
1011 return QQSK::Subclass::QQStyleKitReader;
1012 if (metaObject()->inherits(&QQStyleKitControlState::staticMetaObject))
1013 return QQSK::Subclass::QQStyleKitState;
1014 Q_UNREACHABLE();
1015}
1016
1018{
1019 Q_ASSERT(subclass() == QQSK::Subclass::QQStyleKitReader);
1020 return static_cast<QQStyleKitReader *>(const_cast<QQStyleKitControlProperties *>(this));
1021}
1022
1024{
1025 Q_ASSERT(subclass() == QQSK::Subclass::QQStyleKitState);
1026 Q_ASSERT(metaObject()->inherits(&QQStyleKitControlState::staticMetaObject));
1027 return static_cast<QQStyleKitControlState *>(const_cast<QQStyleKitControlProperties *>(this));
1028}
1029
1030void QQStyleKitControlProperties::forEachUsedDelegate(
1031 std::function<void (QQStyleKitDelegateProperties *, QQSK::Delegate, const QString &)> f)
1032{
1033 // If adding more delegates here, remember to keep StyleKitAnimation.qml in sync
1034 if (m_background)
1035 f(m_background, QQSK::Delegate::Background, "background"_L1);
1036
1037 if (m_indicator) {
1038 f(m_indicator, QQSK::Delegate::Indicator, "indicator"_L1);
1039 if (m_indicator->m_foreground)
1040 f(m_indicator->m_foreground, QQSK::Delegate::IndicatorForeground, "indicator.foreground"_L1);
1041 if (m_indicator->m_up) {
1042 f(m_indicator->m_up, QQSK::Delegate::IndicatorUp, "indicator.up"_L1);
1043 if (m_indicator->m_up->m_foreground)
1044 f(m_indicator->m_up->m_foreground, QQSK::Delegate::IndicatorUpForeground, "indicator.up.foreground"_L1);
1045 }
1046 if (m_indicator->m_down) {
1047 f(m_indicator->m_down, QQSK::Delegate::IndicatorDown, "indicator.down"_L1);
1048 if (m_indicator->m_down->m_foreground)
1049 f(m_indicator->m_down->m_foreground, QQSK::Delegate::IndicatorDownForeground, "indicator.down.foreground"_L1);
1050 }
1051 }
1052
1053 if (m_handle) {
1054 f(m_handle, QQSK::Delegate::Handle, "handle"_L1);
1055 if (m_handle->m_first)
1056 f(m_handle->m_first, QQSK::Delegate::HandleFirst, "handle.first"_L1);
1057 if (m_handle->m_second)
1058 f(m_handle->m_second, QQSK::Delegate::HandleSecond, "handle.second"_L1);
1059 }
1060}
1061
1063{
1064 /* This brute-force function will emit update signals for _all_ style properties
1065 * in the QQStyleKitStyle API. Doing so is typically needed after a style-, or theme
1066 * change, as we don't know which properties are affected by such a big change. */
1067 emit leftPaddingChanged();
1068 emit rightPaddingChanged();
1069 emit topPaddingChanged();
1070 emit bottomPaddingChanged();
1071 emit spacingChanged();
1072 emit transitionChanged();
1073 emit textChanged();
1074
1075 forEachUsedDelegate([](QQStyleKitDelegateProperties *delegate, QQSK::Delegate, const QString &){
1076 delegate->emitChangedForAllStylePropertiesRecursive();
1077 });
1078}
1079
1080template <typename... CHANGED_SIGNALS>
1082 QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
1083{
1084 for (QQStyleKitReader *reader : QQStyleKitReader::s_allReaders) {
1085 if (reader->type() != controlType)
1086 continue;
1087 ((reader->*changedSignals)(), ...);
1088 }
1089}
1090
1092{
1093 return styleProperty<qreal>(QQSK::Property::Spacing);
1094}
1095
1097{
1098 if (setStyleProperty(QQSK::Property::Spacing, spacing))
1099 handleStylePropertyChanged(&QQStyleKitControlProperties::spacingChanged);
1100}
1101
1103{
1104 return styleProperty<qreal>(QQSK::Property::Padding);
1105}
1106
1117
1119{
1120 return styleProperty<qreal>(QQSK::Property::LeftPadding, QQSK::Property::Padding);
1121}
1122
1124{
1125 if (setStyleProperty(QQSK::Property::LeftPadding, leftPadding))
1126 handleStylePropertyChanged(&QQStyleKitControlProperties::leftPaddingChanged);
1127}
1128
1130{
1131 return styleProperty<qreal>(QQSK::Property::RightPadding, QQSK::Property::Padding);
1132}
1133
1135{
1136 if (setStyleProperty(QQSK::Property::RightPadding, rightPadding))
1137 handleStylePropertyChanged(&QQStyleKitControlProperties::rightPaddingChanged);
1138}
1139
1141{
1142 return styleProperty<qreal>(QQSK::Property::TopPadding, QQSK::Property::Padding);
1143}
1144
1146{
1147 if (setStyleProperty(QQSK::Property::TopPadding, topPadding))
1148 handleStylePropertyChanged(&QQStyleKitControlProperties::topPaddingChanged);
1149}
1150
1152{
1153 return styleProperty<qreal>(QQSK::Property::BottomPadding, QQSK::Property::Padding);
1154}
1155
1157{
1158 if (setStyleProperty(QQSK::Property::BottomPadding, bottomPadding))
1159 handleStylePropertyChanged(&QQStyleKitControlProperties::bottomPaddingChanged);
1160}
1161
1163{
1164 return styleProperty<QQuickTransition *>(QQSK::Property::Transition);
1165}
1166
1167void QQStyleKitControlProperties::setTransition(QQuickTransition *transition)
1168{
1169 if (setStyleProperty(QQSK::Property::Transition, transition))
1170 handleStylePropertyChanged(&QQStyleKitControlProperties::transitionChanged);
1171}
1172
1174{
1175 return lazyCreateGroup(m_text, QQSK::PropertyGroup::Text);
1176}
1177
1178QQStyleKitDelegateProperties *QQStyleKitControlProperties::background() const
1179{
1180 return lazyCreateGroup(m_background, QQSK::PropertyGroup::Background);
1181}
1182
1184{
1185 return lazyCreateGroup(m_handle, QQSK::PropertyGroup::Handle);
1186}
1187
1189{
1190 return lazyCreateGroup(m_indicator, QQSK::PropertyGroup::Indicator);
1191}
1192
1193QT_END_NAMESPACE
1194
1195#include "moc_qqstylekitcontrolproperties_p.cpp"
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
QQStyleKitTextProperties * text() const
QQStyleKitIndicatorWithSubTypes * indicator() const
void setTransition(QQuickTransition *transition)
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
QQStyleKitDelegateProperties * background() const
QQStyleKitReader * asQQStyleKitReader() const
QQStyleKitHandleProperties * handle() const
QQStyleKitControlState * asQQStyleKitState() const
QQStyleKitControl * control() const
QQStyleKitDelegateProperties * first() const
QQStyleKitDelegateProperties * second() const
QQuickImage::FillMode fillMode() const
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
QQStyleKitDelegateProperties * foreground() const
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
QQStyleKitDelegateProperties * foreground() const
QQStyleKitIndicatorProperties * down() const
QQStyleKitIndicatorProperties * up() const
T * lazyCreateGroup(T *const &ptr, QQSK::PropertyGroup group) const
void handleStylePropertyChanged(void(SUBCLASS::*changedSignal)())
void handleStylePropertiesChanged(CHANGED_SIGNALS... changedSignals)
QQStyleKitControlProperties * controlProperties() const
static QQStyleKitStyle * current()
void setRightPadding(qreal rightPadding)
void setBottomPadding(qreal bottomPadding)
void emitGlobally(QQStyleKitExtendableControlType controlType, CHANGED_SIGNALS... changedSignals) const
Combined button and popup list for selecting options.
#define CONDITIONALLY_EMIT_SIGNALS_GLOBALLY_FOR(CONTROL_PROPERTIES, GROUP_PATH)